]> git.saurik.com Git - apple/hfs.git/blob - newfs_hfs/makehfs.c
hfs-407.200.4.tar.gz
[apple/hfs.git] / newfs_hfs / makehfs.c
1 /*
2 * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 File: makehfs.c
25
26 Contains: Initialization code for HFS and HFS Plus volumes.
27
28 Copyright: � 1984-1999 by Apple Computer, Inc., all rights reserved.
29
30 */
31
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <err.h>
36 #include <sys/errno.h>
37 #include <sys/stat.h>
38 #include <sys/sysctl.h>
39 #include <sys/vmmeter.h>
40
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <paths.h>
45 #include <pwd.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <wipefs.h>
51
52 #include <TargetConditionals.h>
53
54 #if TARGET_OS_IPHONE
55
56 // CoreServices is not available, so...
57
58 /*
59 * Mac OS Finder flags
60 */
61 enum {
62 kHasBeenInited = 0x0100, /* Files only */
63 /* Clear if the file contains desktop database */
64 /* bit 0x0200 was the letter bit for AOCE, but is now reserved for future use */
65 kHasCustomIcon = 0x0400, /* Files and folders */
66 kIsStationery = 0x0800, /* Files only */
67 kNameLocked = 0x1000, /* Files and folders */
68 kHasBundle = 0x2000, /* Files only */
69 kIsInvisible = 0x4000, /* Files and folders */
70 kIsAlias = 0x8000 /* Files only */
71 };
72
73 enum {
74 kTextEncodingMacUnicode = 0x7E,
75 };
76
77 #else // !TARGET_OS_IPHONE
78
79 #include <CoreServices/CoreServices.h>
80
81 #endif // !TARGET_OS_IPHONE
82
83 /*
84 * CommonCrypto is meant to be a more stable API than OpenSSL.
85 * Defining COMMON_DIGEST_FOR_OPENSSL gives API-compatibility
86 * with OpenSSL, so we don't have to change the code.
87 */
88 #define COMMON_DIGEST_FOR_OPENSSL
89 #include <CommonCrypto/CommonDigest.h>
90
91 #include <libkern/OSByteOrder.h>
92
93 #include <CoreFoundation/CFString.h>
94 #include <CoreFoundation/CFStringEncodingExt.h>
95 #include <IOKit/IOKitLib.h>
96 #include <IOKit/storage/IOMedia.h>
97
98 #include <TargetConditionals.h>
99
100 extern Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, UInt8 *buffer, CFIndex maxBufLen);
101
102
103 #include <hfs/hfs_format.h>
104 #include <hfs/hfs_mount.h>
105 #include "hfs_endian.h"
106
107 #include "newfs_hfs.h"
108
109 #ifndef NEWFS_HFS_DEBUG
110 # ifdef DEBUG_BUILD
111 # define NEWFS_HFS_DEBUG 1
112 # else
113 # define NEWFS_HFS_DEBUG 0
114 # endif
115 #endif
116
117 #define HFS_BOOT_DATA "/usr/share/misc/hfsbootdata"
118
119 #define HFS_JOURNAL_FILE ".journal"
120 #define HFS_JOURNAL_INFO ".journal_info_block"
121
122 #define kJournalFileType 0x6a726e6c /* 'jrnl' */
123
124
125 typedef HFSMasterDirectoryBlock HFS_MDB;
126
127 struct filefork {
128 UInt16 startBlock;
129 UInt16 blockCount;
130 UInt32 logicalSize;
131 UInt32 physicalSize;
132 };
133
134 struct ExtentRecord {
135 HFSPlusExtentKey key;
136 HFSPlusExtentRecord record;
137 } __attribute__((aligned(2), packed));
138 static size_t numOverflowExtents = 0;
139 static struct ExtentRecord *overflowExtents = NULL;
140
141 struct filefork gDTDBFork, gSystemFork, gReadMeFork;
142
143 static void WriteVH __P((const DriveInfo *driveInfo, HFSPlusVolumeHeader *hp));
144 static void InitVH __P((hfsparams_t *defaults, UInt64 sectors,
145 HFSPlusVolumeHeader *header));
146
147 static int AllocateExtent(UInt8 *buffer, UInt32 startBlock, UInt32 blockCount);
148 static int MarkExtentUsed(const DriveInfo *, HFSPlusVolumeHeader *, UInt32, UInt32);
149
150 static void WriteExtentsFile __P((const DriveInfo *dip, UInt64 startingSector,
151 const hfsparams_t *dp, HFSExtentDescriptor *bbextp, void *buffer,
152 UInt32 *bytesUsed, UInt32 *mapNodes));
153
154 static void WriteAttributesFile(const DriveInfo *driveInfo, UInt64 startingSector,
155 const hfsparams_t *dp, HFSExtentDescriptor *bbextp, void *buffer,
156 UInt32 *bytesUsed, UInt32 *mapNodes);
157
158 static void WriteCatalogFile __P((const DriveInfo *dip, UInt64 startingSector,
159 const hfsparams_t *dp, HFSPlusVolumeHeader *header, void *buffer,
160 UInt32 *bytesUsed, UInt32 *mapNodes));
161 static int WriteJournalInfo(const DriveInfo *driveInfo, UInt64 startingSector,
162 const hfsparams_t *dp, HFSPlusVolumeHeader *header,
163 void *buffer);
164 static void InitCatalogRoot_HFSPlus __P((const hfsparams_t *dp, const HFSPlusVolumeHeader *header, void * buffer));
165
166 static void WriteMapNodes __P((const DriveInfo *driveInfo, UInt64 diskStart,
167 UInt32 firstMapNode, UInt32 mapNodes, UInt16 btNodeSize, void *buffer));
168 static void WriteBuffer __P((const DriveInfo *driveInfo, UInt64 startingSector,
169 UInt64 byteCount, const void *buffer));
170 static UInt32 Largest __P((UInt32 a, UInt32 b, UInt32 c, UInt32 d ));
171
172 static UInt32 GetDefaultEncoding();
173
174 static UInt32 UTCToLocal __P((UInt32 utcTime));
175
176 static int ConvertUTF8toUnicode __P((const UInt8* source, size_t bufsize,
177 UniChar* unibuf, UInt16 *charcount));
178
179 #define VOLUMEUUIDVALUESIZE 2
180 typedef union VolumeUUID {
181 UInt32 value[VOLUMEUUIDVALUESIZE];
182 struct {
183 UInt32 high;
184 UInt32 low;
185 } v;
186 } VolumeUUID;
187 void GenerateVolumeUUID(VolumeUUID *newVolumeID);
188
189 void SETOFFSET (void *buffer, UInt16 btNodeSize, SInt16 recOffset, SInt16 vecOffset);
190 #define SETOFFSET(buf,ndsiz,offset,rec) \
191 (*(SInt16 *)((UInt8 *)(buf) + (ndsiz) + (-2 * (rec))) = (SWAP_BE16 (offset)))
192
193 #define BYTESTOBLKS(bytes,blks) DivideAndRoundUp((bytes),(blks))
194
195 #define ROUNDUP(x, u) (((x) % (u) == 0) ? (x) : ((x)/(u) + 1) * (u))
196
197 #if TARGET_OS_EMBEDDED
198 #define ENCODING_TO_BIT(e) \
199 ((e) < 48 ? (e) : 0)
200 #else
201 #define ENCODING_TO_BIT(e) \
202 ((e) < 48 ? (e) : \
203 ((e) == kCFStringEncodingMacUkrainian ? 48 : \
204 ((e) == kCFStringEncodingMacFarsi ? 49 : 0)))
205 #endif
206
207
208 #ifdef DEBUG_BUILD
209 struct cp_root_xattr {
210 u_int16_t vers;
211 u_int16_t reserved1;
212 u_int64_t reserved2;
213 u_int8_t reserved3[16];
214 } __attribute__((aligned(2), packed));
215 #endif
216
217 /*
218 * Create a series of (sequential!) extents for the
219 * requested file. It tries to create the requested
220 * number, but may be stymied by the file size, and
221 * the number of minimum blocks.
222 */
223 static void
224 createExtents(HFSPlusForkData *file,
225 UInt32 fileID,
226 UInt32 startBlock,
227 size_t numExtents,
228 int minBlocks)
229 {
230 if (NEWFS_HFS_DEBUG == 0) {
231 /*
232 * The common case, for non-debug.
233 */
234 file->extents[0].startBlock = startBlock;
235 file->extents[0].blockCount = file->totalBlocks;
236 } else {
237 UInt32 blocksLeft, blocksTotal = 0, blockStep;
238 int i;
239 int firstAdjust = 0;
240
241 if (numExtents == 1) {
242 // The common case, no need to do any math
243 file->extents[0].startBlock = startBlock;
244 file->extents[0].blockCount = file->totalBlocks;
245 return;
246 }
247 if (file->totalBlocks < numExtents)
248 numExtents = file->totalBlocks;
249
250 blocksLeft = file->totalBlocks;
251
252 /*
253 * The intent here is to split the number of blocks into the
254 * requested number of extents. So first we determine how
255 * many blocks should go in each extent -- that's blockStep.
256 * If we have been giving minBlocks, we need to make sure it's
257 * a multiple of that. (In general, the values are going to be
258 * 1 or 2 for minBlocks.)
259 *
260 * If there are more requested extents than blocks, the division
261 * works out to zero... so we limit blockStep to minBlocks.
262 *
263 */
264 blockStep = blocksLeft / numExtents;
265
266 /*
267 * To allow invalid extent lengths, set minBlocks to 1, and
268 * comment out the next two if statements.
269 */
270 if ((blockStep % minBlocks) != 0)
271 blockStep = (blockStep / minBlocks) * minBlocks;
272 if (blockStep == 0)
273 blockStep = minBlocks;
274
275 /*
276 * Now, after that, we may still not have the right number, since
277 * the math may not work out properly. So we can work around that
278 * by making the first extent have all the spares.
279 */
280 if ((blockStep * numExtents) < blocksLeft) {
281 // Need to adjust the first one.
282 firstAdjust = blocksLeft - (blockStep * numExtents);
283 if ((firstAdjust % minBlocks) != 0)
284 firstAdjust = ROUNDUP(firstAdjust, minBlocks);
285 }
286
287 /*
288 * Now, at this point, start handing out blocks to each extent.
289 * First to the 8 extents in the fork descriptor.
290 */
291 for (i = 0; i < 8 && blocksLeft > 0; i++) {
292 int n = MIN(blockStep + firstAdjust, blocksLeft);
293 file->extents[i].startBlock = startBlock + blocksTotal;
294 file->extents[i].blockCount = n;
295 blocksLeft -= n;
296 blocksTotal += n;
297 firstAdjust = 0;
298 }
299 /*
300 * Then, if there are any left, to the overflow extents.
301 */
302 while (blocksLeft > 0) {
303 struct ExtentRecord tmp;
304 UInt32 bcount = 0;
305 memset(&tmp, 0, sizeof(tmp));
306 tmp.key.keyLength = SWAP_BE16(sizeof(HFSPlusExtentKey) - sizeof(uint16_t));
307 tmp.key.forkType = 0;
308 tmp.key.fileID = SWAP_BE32(fileID);
309 tmp.key.startBlock = SWAP_BE32(blocksTotal);
310 for (i = 0; i < 8 && blocksLeft > 0; i++) {
311 int n = MIN(blockStep, blocksLeft);
312 tmp.record[i].startBlock = SWAP_BE32(blocksTotal + bcount + startBlock);
313 tmp.record[i].blockCount = SWAP_BE32(n);
314 bcount += n;
315 blocksLeft -= n;
316 }
317 blocksTotal += bcount;
318 overflowExtents = realloc(overflowExtents, (numOverflowExtents+1) * sizeof(*overflowExtents));
319 overflowExtents[numOverflowExtents++] = tmp;
320 }
321 }
322 return;
323 }
324
325 /*
326 * wipefs() in -lutil knows about multiple filesystem formats.
327 * This replaces the code:
328 * WriteBuffer(driveInfo, 0, diskBlocksUsed * kBytesPerSector, NULL);
329 * WriteBuffer(driveInfo, driveInfo->totalSectors - 8, 4 * 1024, NULL);
330 * which was used to erase the beginning and end of the filesystem.
331 *
332 */
333 static int
334 dowipefs(int fd)
335 {
336 int err;
337 wipefs_ctx handle;
338
339 err = wipefs_alloc(fd, 0/*sectorSize*/, &handle);
340 if (err == 0) {
341 err = wipefs_wipe(handle);
342 }
343 wipefs_free(&handle);
344 return err;
345 }
346
347
348 /*
349 * make_hfsplus
350 *
351 * This routine writes an initial HFS Plus volume structure onto a volume.
352 * It is assumed that the disk has already been formatted and verified.
353 *
354 */
355 int
356 make_hfsplus(const DriveInfo *driveInfo, hfsparams_t *defaults)
357 {
358 UInt16 btNodeSize;
359 UInt32 sectorsPerBlock;
360 UInt32 mapNodes;
361 UInt32 sectorsPerNode;
362 UInt32 temp;
363 UInt32 bytesUsed;
364 UInt32 endOfAttributes;
365 UInt32 startOfAllocation;
366 UInt64 bytesToZero;
367 void *nodeBuffer = NULL;
368 HFSPlusVolumeHeader *header = NULL;
369 UInt64 sector;
370
371 /* Use wipefs() API to clear old metadata from the device.
372 * This should be done before we start writing anything on the
373 * device as wipefs will internally call ioctl(DKIOCDISCARD) on the
374 * entire device.
375 */
376 (void) dowipefs(driveInfo->fd);
377
378 /* --- Create an HFS Plus header: */
379
380 header = (HFSPlusVolumeHeader*)malloc((size_t)kBytesPerSector);
381 if (header == NULL)
382 err(1, NULL);
383
384 /* VH Initialized in native byte order */
385 InitVH(defaults, driveInfo->totalSectors, header);
386
387 sectorsPerBlock = header->blockSize / kBytesPerSector;
388
389
390 /*--- ZERO OUT BEGINNING OF DISK: */
391 /*
392 * Clear out the space to be occupied by the bitmap and B-Trees.
393 * The first chunk is the boot sectors, volume header, allocation bitmap,
394 * journal, Extents B-tree, and Attributes B-tree (if any).
395 * The second chunk is the Catalog B-tree.
396 */
397
398 /* Zero out first 1M (to be safe) for volume header */
399 WriteBuffer(driveInfo, 0, 1024*1024, NULL);
400
401 if (NEWFS_HFS_DEBUG) {
402 /*
403 * Mark each file extent as used individually, rather than doing it all at once.
404 * Also zero out the entire file.
405 */
406 # define MFU(f) \
407 do { \
408 WriteBuffer(driveInfo, \
409 header->f.extents[0].startBlock * sectorsPerBlock, \
410 header->f.totalBlocks * header->blockSize, \
411 NULL); \
412 if (MarkExtentUsed(driveInfo, header, header->f.extents[0].startBlock, header->f.totalBlocks) == -1) { \
413 errx(1, #f " extent overlap <%u, %u>", header->f.extents[0].startBlock, header->f.totalBlocks); \
414 } \
415 } while (0)
416 MFU(allocationFile);
417 MFU(attributesFile);
418 MFU(extentsFile);
419 # undef MFU
420 } else {
421 /* Zero out from start of allocation file to end of attribute file;
422 * will include allocation bitmap, journal, extents btree, and
423 * attribute btree
424 */
425 sector = header->allocationFile.extents[0].startBlock * sectorsPerBlock;
426 endOfAttributes = header->attributesFile.extents[0].startBlock + header->attributesFile.totalBlocks;
427 startOfAllocation = header->allocationFile.extents[0].startBlock;
428 bytesToZero = (UInt64) (endOfAttributes - startOfAllocation + 1) * header->blockSize;
429 WriteBuffer(driveInfo, sector, bytesToZero, NULL);
430
431 bytesToZero = (UInt64) header->catalogFile.totalBlocks * header->blockSize;
432 sector = header->catalogFile.extents[0].startBlock * sectorsPerBlock;
433 WriteBuffer(driveInfo, sector, bytesToZero, NULL);
434 }
435 /*
436 * Allocate a buffer for the rest of our IO.
437 * Note that in some cases we may need to initialize an EA, so we
438 * need to use the attribute B-Tree node size in this calculation.
439 */
440
441 temp = Largest( defaults->catalogNodeSize * 2,
442 (defaults->attributesNodeSize * 2),
443 header->blockSize,
444 (header->catalogFile.extents[0].startBlock + header->catalogFile.totalBlocks + 7) / 8 );
445 /*
446 * If size is not a mutiple of 512, round up to nearest sector
447 */
448 if ( (temp & 0x01FF) != 0 )
449 temp = (temp + kBytesPerSector) & 0xFFFFFE00;
450
451 nodeBuffer = valloc((size_t)temp);
452 if (nodeBuffer == NULL)
453 err(1, NULL);
454
455
456
457 /*--- WRITE ALLOCATION BITMAP BITS TO DISK: */
458
459 /*
460 * XXX - this doesn't work well with using arbitrary extents.
461 *
462 * To do this, we need to find the appropriate area in the file, and
463 * pass that in to AllocateExtent, which is just a bitmap manipulation
464 * routine. Then we need to write it out at the right place. Note that
465 * we may have to read it in first, as well, which may mean zeroing out
466 * the entirety of the allocation file first.
467 *
468 * Possible solution:
469 * New function to mark extent as used.
470 * Function should figure out which block(s) for an extent.
471 * Read it in. Mark the bits used. Return.
472 * For now, it can assume the allocation extents are contiguous, but
473 * should be extensible to not do that.
474 */
475 sector = header->allocationFile.extents[0].startBlock * sectorsPerBlock;
476 bzero(nodeBuffer, temp);
477 /* Mark volume header as allocated */
478 if (header->blockSize == 512) {
479 if (MarkExtentUsed(driveInfo, header, 0, 4) == -1) {
480 errx(1, "Overlapped extent at <0, 4> (%d)", __LINE__);
481 }
482 } else if (header->blockSize == 1024) {
483 if (MarkExtentUsed(driveInfo, header, 0, 2) == -1) {
484 errx(1, "Overlapped extent at <0, 2> (%d)", __LINE__);
485 }
486 } else {
487 if (MarkExtentUsed(driveInfo, header, 0, 1) == -1) {
488 errx(1, "Overlapped extent at <0, 1> (%d)", __LINE__);
489 }
490 }
491 if (NEWFS_HFS_DEBUG == 0) {
492 /* Mark area from bitmap to end of attributes as allocated */
493 if (MarkExtentUsed(driveInfo, header, startOfAllocation, (endOfAttributes - startOfAllocation)) == -1) {
494 errx(1, "Overlapped extent at <%u, %u> (%d)\n", startOfAllocation, endOfAttributes - startOfAllocation, __LINE__);
495 }
496 }
497
498 /* Mark catalog btree blocks as allocated */
499 if (NEWFS_HFS_DEBUG) {
500 /* Erase the catalog file first */
501 WriteBuffer(driveInfo,
502 header->catalogFile.extents[0].startBlock * sectorsPerBlock,
503 header->catalogFile.totalBlocks * header->blockSize,
504 NULL);
505 }
506 if (MarkExtentUsed(driveInfo, header,
507 header->catalogFile.extents[0].startBlock,
508 header->catalogFile.totalBlocks) == -1) {
509 errx(1, "Overlapped catalog extent at <%u, %u>\n", header->catalogFile.extents[0].startBlock, header->catalogFile.totalBlocks);
510 }
511
512 /*
513 * Write alternate Volume Header bitmap bit to allocations file at
514 * 2nd to last sector on HFS+ volume
515 */
516 if (MarkExtentUsed(driveInfo, header, header->totalBlocks - 1, 1) == -1) {
517 errx(1, "Overlapped extent for header at <%u, %u>\n", header->totalBlocks - 1, 1);
518 }
519
520 /*
521 * If the blockSize is 512 bytes, then the last 1kbyte has to be marked
522 * used via two bits.
523 */
524 if ( header->blockSize == 512 ) {
525 if (MarkExtentUsed(driveInfo, header, header->totalBlocks - 2, 1) == -1) {
526 errx(1, "Overlapped extent for AVH at <%u, %u>\n", header->totalBlocks - 2, 1);
527 }
528
529 }
530
531 /*--- WRITE FILE EXTENTS B-TREE TO DISK: */
532
533 btNodeSize = defaults->extentsNodeSize;
534 sectorsPerNode = btNodeSize/kBytesPerSector;
535
536 sector = header->extentsFile.extents[0].startBlock * sectorsPerBlock;
537 WriteExtentsFile(driveInfo, sector, defaults, NULL, nodeBuffer, &bytesUsed, &mapNodes);
538
539 if (mapNodes > 0) {
540 WriteMapNodes(driveInfo, (sector + bytesUsed/kBytesPerSector),
541 bytesUsed/btNodeSize, mapNodes, btNodeSize, nodeBuffer);
542 }
543
544
545
546 /*--- WRITE FILE ATTRIBUTES B-TREE TO DISK: */
547 if (defaults->attributesInitialSize) {
548
549 btNodeSize = defaults->attributesNodeSize;
550 sectorsPerNode = btNodeSize/kBytesPerSector;
551
552 sector = header->attributesFile.extents[0].startBlock * sectorsPerBlock;
553 WriteAttributesFile(driveInfo, sector, defaults, NULL, nodeBuffer, &bytesUsed, &mapNodes);
554 if (mapNodes > 0) {
555 WriteMapNodes(driveInfo, (sector + bytesUsed/kBytesPerSector),
556 bytesUsed/btNodeSize, mapNodes, btNodeSize, nodeBuffer);
557 }
558 }
559
560 /*--- WRITE CATALOG B-TREE TO DISK: */
561
562 btNodeSize = defaults->catalogNodeSize;
563 sectorsPerNode = btNodeSize/kBytesPerSector;
564
565 sector = header->catalogFile.extents[0].startBlock * sectorsPerBlock;
566 WriteCatalogFile(driveInfo, sector, defaults, header, nodeBuffer, &bytesUsed, &mapNodes);
567
568 if (mapNodes > 0) {
569 WriteMapNodes(driveInfo, (sector + bytesUsed/kBytesPerSector),
570 bytesUsed/btNodeSize, mapNodes, btNodeSize, nodeBuffer);
571 }
572
573 /*--- JOURNALING SETUP */
574 if (defaults->journaledHFS) {
575 sector = header->journalInfoBlock * sectorsPerBlock;
576 if (NEWFS_HFS_DEBUG) {
577 /*
578 * For debug build, the journal may be located somewhere other
579 * than right after the journalInfoBlock.
580 */
581 if (MarkExtentUsed(driveInfo, header, header->journalInfoBlock, 1) == -1) {
582 errx(1, "Extent overlap for journalInfoBlock <%u, 1>", header->journalInfoBlock);
583 }
584
585 if (!defaults->journalDevice) {
586 UInt32 jStart = defaults->journalBlock ? defaults->journalBlock : (header->journalInfoBlock + 1);
587 UInt32 jCount = (UInt32)(defaults->journalSize / header->blockSize);
588 if (MarkExtentUsed(driveInfo, header, jStart, jCount) == -1) {
589 errx(1, "Extent overlap for journal <%u, %u>", jStart, jCount);
590 }
591 }
592 }
593 if (WriteJournalInfo(driveInfo, sector, defaults, header, nodeBuffer) != 0) {
594 err(EINVAL, "Failed to create the journal");
595 }
596 }
597
598 /*--- WRITE VOLUME HEADER TO DISK: */
599
600 /* write header last in case we fail along the way */
601
602 /* Writes both copies of the volume header */
603 WriteVH (driveInfo, header);
604 /* VH is now big-endian */
605
606 free(nodeBuffer);
607 free(header);
608
609 return (0);
610 }
611
612 /*
613 * WriteVH
614 *
615 * Writes the Volume Header (VH) to disk.
616 *
617 * The VH is byte-swapped if necessary to big endian. Since this
618 * is always the last operation, there's no point in unswapping it.
619 */
620 static void
621 WriteVH (const DriveInfo *driveInfo, HFSPlusVolumeHeader *hp)
622 {
623 SWAP_HFSPLUSVH (hp);
624
625 WriteBuffer(driveInfo, 2, kBytesPerSector, hp);
626 WriteBuffer(driveInfo, driveInfo->totalSectors - 2, kBytesPerSector, hp);
627 }
628
629
630 /*
631 * InitVH
632 *
633 * Initialize a Volume Header record.
634 */
635 static void
636 InitVH(hfsparams_t *defaults, UInt64 sectors, HFSPlusVolumeHeader *hp)
637 {
638 UInt32 blockSize;
639 UInt32 blockCount;
640 UInt32 blocksUsed;
641 UInt32 bitmapBlocks;
642 UInt16 burnedBlocksBeforeVH = 0;
643 UInt16 burnedBlocksAfterAltVH = 0;
644 UInt32 nextBlock;
645 UInt32 allocateBlock;
646 VolumeUUID newVolumeUUID;
647 VolumeUUID* finderInfoUUIDPtr;
648 UInt64 hotFileBandSize;
649 UInt64 volsize;
650
651 /*
652 * 2 MB is the minimum size for the new behavior with
653 * space after the attr b-tree, and hotfile stuff.
654 */
655 #define MINVOLSIZE_WITHSPACE 2097152
656
657 bzero(hp, kBytesPerSector);
658
659 blockSize = defaults->blockSize;
660 blockCount = sectors / (blockSize >> kLog2SectorSize);
661
662 /*
663 * HFSPlusVolumeHeader is located at sector 2, so we may need
664 * to invalidate blocks before HFSPlusVolumeHeader.
665 */
666 if ( blockSize == 512 ) {
667 burnedBlocksBeforeVH = 2; /* 2 before VH */
668 burnedBlocksAfterAltVH = 1; /* 1 after altVH */
669 } else if ( blockSize == 1024 ) {
670 burnedBlocksBeforeVH = 1;
671 }
672 nextBlock = burnedBlocksBeforeVH + 1; /* +1 for VH itself */
673 if (defaults->fsStartBlock) {
674 if (NEWFS_HFS_DEBUG)
675 printf ("Laying down metadata starting at allocation block=%u (totalBlocks=%u)\n", (unsigned int)defaults->fsStartBlock, (unsigned int)blockCount);
676 nextBlock += defaults->fsStartBlock; /* lay down file system after this allocation block */
677 }
678
679 bitmapBlocks = defaults->allocationClumpSize / blockSize;
680
681 /* note: add 2 for the Alternate VH, and VH */
682 blocksUsed = 2 + burnedBlocksBeforeVH + burnedBlocksAfterAltVH + bitmapBlocks;
683
684 if (defaults->flags & kMakeCaseSensitive) {
685 hp->signature = kHFSXSigWord;
686 hp->version = kHFSXVersion;
687 } else {
688 hp->signature = kHFSPlusSigWord;
689 hp->version = kHFSPlusVersion;
690 }
691 hp->attributes = kHFSVolumeUnmountedMask | kHFSUnusedNodeFixMask;
692 if (defaults->flags & kMakeContentProtect) {
693 hp->attributes |= kHFSContentProtectionMask;
694 }
695 hp->lastMountedVersion = kHFSPlusMountVersion;
696
697 /* NOTE: create date is in local time, not GMT! */
698 hp->createDate = UTCToLocal(defaults->createDate);
699 hp->modifyDate = defaults->createDate;
700 hp->backupDate = 0;
701 hp->checkedDate = defaults->createDate;
702
703 // hp->fileCount = 0;
704 // hp->folderCount = 0;
705
706 hp->blockSize = blockSize;
707 hp->totalBlocks = blockCount;
708 hp->freeBlocks = blockCount; /* will be adjusted at the end */
709
710 volsize = (UInt64) blockCount * (UInt64) blockSize;
711
712 hp->rsrcClumpSize = defaults->rsrcClumpSize;
713 hp->dataClumpSize = defaults->dataClumpSize;
714 hp->nextCatalogID = defaults->nextFreeFileID;
715 hp->encodingsBitmap = 1;
716
717 /* set up allocation bitmap file */
718 hp->allocationFile.clumpSize = defaults->allocationClumpSize;
719 hp->allocationFile.logicalSize = defaults->allocationClumpSize;
720 hp->allocationFile.totalBlocks = bitmapBlocks;
721
722 if (NEWFS_HFS_DEBUG && defaults->allocationStartBlock)
723 allocateBlock = defaults->allocationStartBlock;
724 else {
725 allocateBlock = nextBlock;
726 nextBlock += bitmapBlocks;
727 }
728
729 createExtents(&hp->allocationFile, kHFSAllocationFileID, allocateBlock, defaults->allocationExtsCount, 1);
730
731 // This works because the files are contiguous for now
732 if (NEWFS_HFS_DEBUG)
733 printf ("allocationFile: (%10u, %10u)\n", hp->allocationFile.extents[0].startBlock, hp->allocationFile.totalBlocks);
734
735 /* set up journal files */
736 if (defaults->journaledHFS) {
737 UInt32 journalBlock;
738 hp->fileCount = 2;
739 hp->attributes |= kHFSVolumeJournaledMask;
740 hp->nextCatalogID += 2;
741
742 /*
743 * Allocate 1 block for the journalInfoBlock. The
744 * journal file size is passed in hfsparams_t.
745 */
746 if (NEWFS_HFS_DEBUG && defaults->journalInfoBlock)
747 hp->journalInfoBlock = defaults->journalInfoBlock;
748 else
749 hp->journalInfoBlock = nextBlock++;
750 if (NEWFS_HFS_DEBUG && defaults->journalBlock)
751 journalBlock = defaults->journalBlock;
752 else {
753 journalBlock = hp->journalInfoBlock + 1;
754 nextBlock += ((defaults->journalSize+blockSize-1) / blockSize);
755 }
756
757 if (NEWFS_HFS_DEBUG) {
758 printf ("journalInfo : (%10u, %10u)\n", (u_int32_t)hp->journalInfoBlock, 1);
759 printf ("journal : (%10u, %10u)\n", (u_int32_t)journalBlock, (u_int32_t)((defaults->journalSize + (blockSize-1)) / blockSize));
760 }
761 /* XXX What if journal is on a different device? */
762 blocksUsed += 1 + ((defaults->journalSize+blockSize-1) / blockSize);
763 } else {
764 hp->journalInfoBlock = 0;
765 }
766
767 /* set up extents b-tree file */
768 hp->extentsFile.clumpSize = defaults->extentsClumpSize;
769 hp->extentsFile.logicalSize = defaults->extentsInitialSize;
770 hp->extentsFile.totalBlocks = defaults->extentsInitialSize / blockSize;
771 if (NEWFS_HFS_DEBUG && defaults->extentsStartBlock)
772 allocateBlock = defaults->extentsStartBlock;
773 else {
774 allocateBlock = nextBlock;
775 nextBlock += hp->extentsFile.totalBlocks;
776 }
777 createExtents(&hp->extentsFile, kHFSExtentsFileID, allocateBlock, defaults->extentsExtsCount, (defaults->journaledHFS && defaults->extentsNodeSize > hp->blockSize) ? defaults->extentsNodeSize / hp->blockSize : 1);
778
779 blocksUsed += hp->extentsFile.totalBlocks;
780
781 if (NEWFS_HFS_DEBUG)
782 printf ("extentsFile : (%10u, %10u)\n", hp->extentsFile.extents[0].startBlock, hp->extentsFile.totalBlocks);
783
784 /* set up attributes b-tree file */
785 if (defaults->attributesInitialSize) {
786 hp->attributesFile.clumpSize = defaults->attributesClumpSize;
787 hp->attributesFile.logicalSize = defaults->attributesInitialSize;
788 hp->attributesFile.totalBlocks = defaults->attributesInitialSize / blockSize;
789 if (NEWFS_HFS_DEBUG && defaults->attributesStartBlock)
790 allocateBlock = defaults->attributesStartBlock;
791 else {
792 allocateBlock = nextBlock;
793 nextBlock += hp->attributesFile.totalBlocks;
794 }
795 createExtents(&hp->attributesFile, kHFSAttributesFileID, allocateBlock, defaults->attributesExtsCount, (defaults->journaledHFS && defaults->attributesNodeSize > hp->blockSize) ? defaults->attributesNodeSize / hp->blockSize : 1);
796 blocksUsed += hp->attributesFile.totalBlocks;
797
798 if (NEWFS_HFS_DEBUG) {
799 printf ("attributesFile: (%10u, %10u)\n", hp->attributesFile.extents[0].startBlock, hp->attributesFile.totalBlocks);
800 }
801 /*
802 * Leave some room for the Attributes B-tree to grow, if the volsize >= 2MB
803 */
804 if (volsize >= MINVOLSIZE_WITHSPACE && defaults->attributesStartBlock == 0) {
805 nextBlock += 10 * (hp->attributesFile.clumpSize / blockSize);
806 }
807 }
808
809 /* set up catalog b-tree file */
810 hp->catalogFile.clumpSize = defaults->catalogClumpSize;
811 hp->catalogFile.logicalSize = defaults->catalogInitialSize;
812 hp->catalogFile.totalBlocks = defaults->catalogInitialSize / blockSize;
813 if (NEWFS_HFS_DEBUG && defaults->catalogStartBlock)
814 allocateBlock = defaults->catalogStartBlock;
815 else {
816 allocateBlock = nextBlock;
817 nextBlock += hp->catalogFile.totalBlocks;
818 }
819 createExtents(&hp->catalogFile, kHFSCatalogFileID, allocateBlock, defaults->catalogExtsCount, (defaults->journaledHFS && defaults->catalogNodeSize > hp->blockSize) ? defaults->catalogNodeSize / hp->blockSize : 1);
820 blocksUsed += hp->catalogFile.totalBlocks;
821
822 if (NEWFS_HFS_DEBUG)
823 printf ("catalogFile : (%10u, %10u)\n\n", hp->catalogFile.extents[0].startBlock, hp->catalogFile.totalBlocks);
824
825 if ((numOverflowExtents * sizeof(struct ExtentRecord)) >
826 (defaults->extentsNodeSize - sizeof(BTNodeDescriptor) - (sizeof(uint16_t) * numOverflowExtents))) {
827 errx(1, "Too many overflow extent records to fit into a single extent node");
828 }
829
830 /*
831 * Add some room for the catalog file to grow...
832 */
833 nextBlock += 10 * (hp->catalogFile.clumpSize / hp->blockSize);
834
835 /*
836 * Add some room for the hot file band. This uses the same 5MB per GB
837 * as the kernel. The kernel only uses hotfiles if the volume is larger
838 * than 10GBytes, so do the same here.
839 */
840 #define METADATAZONE_MINIMUM_VOLSIZE (10ULL * 1024ULL * 1024ULL * 1024ULL)
841 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
842 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
843 if (volsize >= METADATAZONE_MINIMUM_VOLSIZE) {
844 hotFileBandSize = (UInt64) blockCount * blockSize / 1024 * 5;
845 if (hotFileBandSize > HOTBAND_MAXIMUM_SIZE)
846 hotFileBandSize = HOTBAND_MAXIMUM_SIZE;
847 else if (hotFileBandSize < HOTBAND_MINIMUM_SIZE)
848 hotFileBandSize = HOTBAND_MINIMUM_SIZE;
849 nextBlock += hotFileBandSize / blockSize;
850 }
851 if (NEWFS_HFS_DEBUG && defaults->nextAllocBlock)
852 hp->nextAllocation = defaults->nextAllocBlock;
853 else
854 hp->nextAllocation = nextBlock;
855
856 /* Adjust free blocks to reflect everything we have allocated. */
857 hp->freeBlocks -= blocksUsed;
858
859 /* Generate and write UUID for the HFS+ disk */
860 GenerateVolumeUUID(&newVolumeUUID);
861 finderInfoUUIDPtr = (VolumeUUID *)(&hp->finderInfo[24]);
862 finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(newVolumeUUID.v.high);
863 finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(newVolumeUUID.v.low);
864 }
865
866 /*
867 * AllocateExtent
868 *
869 * Mark the given extent as in-use in the given bitmap buffer.
870 */
871 static int AllocateExtent(UInt8 *buffer, UInt32 startBlock, UInt32 blockCount)
872 {
873 UInt8 *p;
874
875 /* Point to start of extent in bitmap buffer */
876 p = buffer + (startBlock / 8);
877
878 /*
879 * Important to remember: block 0 is (1 << 7);
880 * block 7 is (1 << 0).
881 */
882 /* Partial byte at start of extent */
883 if (startBlock & 7)
884 {
885 UInt8 mask = 0xff;
886 unsigned int lShift = 0;
887 unsigned int startBit = startBlock & 7;
888
889 /*
890 * Is startBlock + blockCount entirely in
891 * p[0]?
892 */
893 if (blockCount < (8 - startBit)) {
894 lShift = 8 - (startBit + blockCount);
895 }
896 mask = (0xff >> startBit) & (0xff << lShift);
897 if (NEWFS_HFS_DEBUG && (*p & mask)) {
898 fprintf(stderr, "%s(%d): expected 0, got %x\n", __FUNCTION__, __LINE__, *p & mask);
899 return -1;
900 }
901 *(p++) |= mask;
902 /*
903 * We have either set <lShift> or <startBlock & 7> bits.
904 */
905 blockCount -= 8 - (lShift + startBit);
906 // blockCount -= lShift ? blockCount : (8 - startBit);
907 // blockCount -= __builtin_popcount(mask);
908 }
909
910 /* Fill in whole bytes */
911 if (blockCount >= 8)
912 {
913 if (NEWFS_HFS_DEBUG) {
914 /*
915 * Put this in ifdef because it'll slow things down.
916 * For non-debug case, we shouldn't have to worry about
917 * an overlap, anyway.
918 */
919 size_t indx;
920 for (indx = 0; indx < blockCount / 8; indx++) {
921 if (p[indx] != 0) {
922 fprintf(stderr, "%s(%d): Expected 0 at %zu, got 0x%x\n", __FUNCTION__, __LINE__, indx, p[indx]);
923 return -1;
924 }
925 p[indx] = 0xff;
926 }
927 } else {
928 memset(p, 0xFF, blockCount / 8);
929 }
930 p += blockCount / 8;
931 blockCount &= 7;
932 }
933
934 /* Partial byte at end of extent */
935 if (blockCount)
936 {
937 UInt8 mask = 0xff << (8 - blockCount);
938 if (NEWFS_HFS_DEBUG && (*p & mask)) {
939 fprintf(stderr, "%s(%d): Expected 0, got %x\n", __FUNCTION__, __LINE__, *p & mask);
940 return -1;
941 }
942 *(p++) |= mask;
943 }
944 return 0;
945 }
946
947 /*
948 * Mark an extent as being used.
949 * This involves finding out where the allocations file is,
950 * where in the allocations file the extent starts, and how
951 * long it runs.
952 *
953 * One downside to this implementation is that this does
954 * more I/O than the old mechanism, a cost to the flexibility.
955 * May have to consider doing caching of some sort.
956 */
957
958 static int
959 MarkExtentUsed(const DriveInfo *driveInfo,
960 HFSPlusVolumeHeader *header,
961 UInt32 startBlock,
962 UInt32 blockCount)
963 {
964 size_t bufSize = driveInfo->physSectorSize;
965 void *buf;
966 uint32_t blocksLeft = blockCount;
967 uint32_t curBlock = startBlock;
968 static const int kBitsPerByte = 8;
969 int status = -1;
970
971 buf = valloc(bufSize);
972 if (buf == NULL)
973 err(1, NULL);
974
975 /*
976 * We loop through physSectorSize blocks.
977 * This allows us to set as many bits as we need.
978 */
979 while (blocksLeft > 0) {
980 off_t secNum;
981 uint32_t numBlocks; // The number of blocks to mark as used in this pass.
982 uint32_t blockOffset; // This is the block number of the current range, which starts at curBlock
983
984 memset(buf, 0, sizeof(buf));
985 secNum = curBlock / (bufSize * kBitsPerByte);
986 blockOffset = curBlock % (bufSize * kBitsPerByte);
987 numBlocks = MIN((bufSize * kBitsPerByte) - blockOffset, blocksLeft);
988
989 /*
990 * Okay, now we've got the block number to read,
991 * the offset into the block, and the number of blocks
992 * to set.
993 *
994 * First we read in the buffer. To do that, we need to
995 * know where to read.
996 */
997 ssize_t nbytes;
998 ssize_t nwritten;
999 off_t offset;
1000
1001 /*
1002 * XXX
1003 * This needs to be changed if/when we support non-contiguous multiple
1004 * extents. At that point, it'll probably have to be a function to search
1005 * for the requested offset. (How many times must MapFileC be written?)
1006 * For now, though, the offset is the physical sector offset from the
1007 * start of the allocations file.
1008 */
1009 offset = (header->allocationFile.extents[0].startBlock * header->blockSize) +
1010 (secNum * bufSize);
1011
1012 nbytes = pread(driveInfo->fd, buf, bufSize, offset);
1013
1014 if (nbytes < (ssize_t)bufSize) {
1015 if (nbytes == -1)
1016 err(1, "%s::pread(%d, %p, %zu, %lld)", __FUNCTION__, driveInfo->fd, buf, bufSize, offset);
1017 goto exit;
1018 }
1019
1020 if (AllocateExtent(buf, blockOffset, numBlocks) == -1) {
1021 warnx("In-use allocation block in <%u, %u>", blockOffset, numBlocks);
1022 goto exit;
1023 }
1024 nwritten = pwrite(driveInfo->fd, buf, bufSize, offset);
1025 /*
1026 * Normally I'd check for nwritten to be less than bufSize, but since bufSize is
1027 * the physical sector size, we shouldn't be able to get less. So that most likely
1028 * means a return value of 0 or -1, neither of which I could do anything about.
1029 */
1030 if (nwritten != (ssize_t)bufSize)
1031 goto exit;
1032
1033 // And go get the next set, if needed
1034 blocksLeft -= numBlocks;
1035 curBlock += numBlocks;
1036 }
1037
1038 status = 0;
1039
1040 exit:
1041 free(buf);
1042
1043 return status;
1044 }
1045 /*
1046 * WriteExtentsFile
1047 *
1048 * Initializes and writes out the extents b-tree file.
1049 *
1050 * Byte swapping is performed in place. The buffer should not be
1051 * accessed through direct casting once it leaves this function.
1052 */
1053 static void
1054 WriteExtentsFile(const DriveInfo *driveInfo, UInt64 startingSector,
1055 const hfsparams_t *dp, HFSExtentDescriptor *bbextp __unused , void *buffer,
1056 UInt32 *bytesUsed, UInt32 *mapNodes)
1057 {
1058 BTNodeDescriptor *ndp;
1059 BTHeaderRec *bthp;
1060 UInt8 *bmp;
1061 UInt32 nodeBitsInHeader;
1062 UInt32 fileSize;
1063 UInt32 nodeSize;
1064 UInt32 temp;
1065 SInt16 offset;
1066
1067 *mapNodes = 0;
1068 fileSize = dp->extentsInitialSize;
1069 nodeSize = dp->extentsNodeSize;
1070
1071 bzero(buffer, nodeSize);
1072
1073
1074 /* FILL IN THE NODE DESCRIPTOR: */
1075 ndp = (BTNodeDescriptor *)buffer;
1076 ndp->kind = kBTHeaderNode;
1077 ndp->numRecords = SWAP_BE16 (3);
1078 offset = sizeof(BTNodeDescriptor);
1079
1080 SETOFFSET(buffer, nodeSize, offset, 1);
1081
1082
1083 /* FILL IN THE HEADER RECORD: */
1084 bthp = (BTHeaderRec *)((UInt8 *)buffer + offset);
1085 if (numOverflowExtents) {
1086 bthp->treeDepth = SWAP_BE16(1);
1087 bthp->rootNode = SWAP_BE32(1);
1088 bthp->firstLeafNode = SWAP_BE32(1);
1089 bthp->lastLeafNode = SWAP_BE32(1);
1090 bthp->leafRecords = SWAP_BE32(numOverflowExtents);
1091 } else {
1092 bthp->treeDepth = 0;
1093 bthp->rootNode = 0;
1094 bthp->firstLeafNode = 0;
1095 bthp->lastLeafNode = 0;
1096 bthp->leafRecords = 0;
1097 }
1098
1099 bthp->nodeSize = SWAP_BE16 (nodeSize);
1100 bthp->totalNodes = SWAP_BE32 (fileSize / nodeSize);
1101 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->totalNodes) - (numOverflowExtents ? 2 : 1)); /* header */
1102 bthp->clumpSize = SWAP_BE32 (dp->extentsClumpSize);
1103
1104 bthp->attributes |= SWAP_BE32 (kBTBigKeysMask);
1105 bthp->maxKeyLength = SWAP_BE16 (kHFSPlusExtentKeyMaximumLength);
1106 offset += sizeof(BTHeaderRec);
1107
1108 SETOFFSET(buffer, nodeSize, offset, 2);
1109
1110 offset += kBTreeHeaderUserBytes;
1111
1112 SETOFFSET(buffer, nodeSize, offset, 3);
1113
1114
1115 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1116 nodeBitsInHeader = 8 * (nodeSize
1117 - sizeof(BTNodeDescriptor)
1118 - sizeof(BTHeaderRec)
1119 - kBTreeHeaderUserBytes
1120 - (4 * sizeof(SInt16)) );
1121
1122 if (SWAP_BE32 (bthp->totalNodes) > nodeBitsInHeader) {
1123 UInt32 nodeBitsInMapNode;
1124
1125 ndp->fLink = SWAP_BE32 (SWAP_BE32 (bthp->lastLeafNode) + 1);
1126 nodeBitsInMapNode = 8 * (nodeSize
1127 - sizeof(BTNodeDescriptor)
1128 - (2 * sizeof(SInt16))
1129 - 2 );
1130 *mapNodes = (SWAP_BE32 (bthp->totalNodes) - nodeBitsInHeader +
1131 (nodeBitsInMapNode - 1)) / nodeBitsInMapNode;
1132 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->freeNodes) - *mapNodes);
1133 }
1134
1135
1136 /*
1137 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1138 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1139 */
1140 bmp = ((UInt8 *)buffer + offset);
1141 temp = SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes);
1142
1143 /* Working a byte at a time is endian safe */
1144 while (temp >= 8) { *bmp = 0xFF; temp -= 8; bmp++; }
1145 *bmp = ~(0xFF >> temp);
1146 offset += nodeBitsInHeader/8;
1147
1148 SETOFFSET(buffer, nodeSize, offset, 4);
1149
1150 if (NEWFS_HFS_DEBUG && numOverflowExtents) {
1151 void *node2 = (uint8_t*)buffer + nodeSize;
1152 size_t i;
1153 int (^keyCompare)(const void *l, const void *r) = ^(const void *l, const void *r) {
1154 const struct ExtentRecord *left = (const struct ExtentRecord*)l;
1155 const struct ExtentRecord *right = (const struct ExtentRecord*)r;
1156 if (SWAP_BE32(left->key.fileID) != SWAP_BE32(right->key.fileID)) {
1157 return (SWAP_BE32(left->key.fileID) > SWAP_BE32(right->key.fileID)) ? 1 : -1;
1158 }
1159 // forkType will always be 0 for us
1160 if (SWAP_BE32(left->key.startBlock) != SWAP_BE32(right->key.startBlock)) {
1161 return (SWAP_BE32(left->key.startBlock) > SWAP_BE32(right->key.startBlock)) ? 1 : -1;
1162 }
1163 return 0;
1164 };
1165
1166 if (numOverflowExtents > 1) {
1167 qsort_b(overflowExtents, numOverflowExtents, sizeof(*overflowExtents), keyCompare);
1168 }
1169 bzero(node2, nodeSize);
1170 ndp = (BTNodeDescriptor*)node2;
1171 ndp->kind = kBTLeafNode;
1172 ndp->numRecords = SWAP_BE16(numOverflowExtents);
1173 ndp->height = 1;
1174
1175 offset = sizeof(BTNodeDescriptor);
1176 for (i = 0; i < numOverflowExtents; i++) {
1177 SETOFFSET(node2, nodeSize, offset, 1 + i);
1178 memcpy(node2 + offset, &overflowExtents[i], sizeof(*overflowExtents));
1179 offset += sizeof(*overflowExtents);
1180 }
1181 SETOFFSET(node2, nodeSize, offset, numOverflowExtents + 1);
1182 }
1183
1184 *bytesUsed = (SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes) - *mapNodes) * nodeSize;
1185
1186 WriteBuffer(driveInfo, startingSector, *bytesUsed, buffer);
1187
1188 }
1189
1190 /*
1191 * WriteAttributesFile
1192 *
1193 * Initializes and writes out the attributes b-tree file.
1194 *
1195 * Byte swapping is performed in place. The buffer should not be
1196 * accessed through direct casting once it leaves this function.
1197 */
1198 static void
1199 WriteAttributesFile(const DriveInfo *driveInfo, UInt64 startingSector,
1200 const hfsparams_t *dp, HFSExtentDescriptor *bbextp __unused, void *buffer,
1201 UInt32 *bytesUsed, UInt32 *mapNodes)
1202 {
1203 BTNodeDescriptor *ndp;
1204 BTHeaderRec *bthp;
1205 UInt8 *bmp;
1206 UInt32 nodeBitsInHeader;
1207 UInt32 fileSize;
1208 UInt32 nodeSize;
1209 UInt32 temp;
1210 SInt16 offset;
1211 int set_cp_level = 0;
1212
1213 *mapNodes = 0;
1214 fileSize = dp->attributesInitialSize;
1215 nodeSize = dp->attributesNodeSize;
1216
1217 #ifdef DEBUG_BUILD
1218 /*
1219 * If user specified content protection and a protection level,
1220 * then verify the protection level is sane.
1221 */
1222 if ((dp->flags & kMakeContentProtect) && (dp->protectlevel != 0)) {
1223 if ((dp->protectlevel >= 2 ) && (dp->protectlevel <= 4)) {
1224 set_cp_level = 1;
1225 }
1226 }
1227 #endif
1228
1229
1230 bzero(buffer, nodeSize);
1231
1232
1233 /* FILL IN THE NODE DESCRIPTOR: */
1234 ndp = (BTNodeDescriptor *)buffer;
1235 ndp->kind = kBTHeaderNode;
1236 ndp->numRecords = SWAP_BE16 (3);
1237 offset = sizeof(BTNodeDescriptor);
1238
1239 SETOFFSET(buffer, nodeSize, offset, 1);
1240
1241
1242 /* FILL IN THE HEADER RECORD: */
1243 bthp = (BTHeaderRec *)((UInt8 *)buffer + offset);
1244 if (set_cp_level) {
1245 bthp->treeDepth = SWAP_BE16(1);
1246 bthp->rootNode = SWAP_BE32(1);
1247 bthp->firstLeafNode = SWAP_BE32(1);
1248 bthp->lastLeafNode = SWAP_BE32(1);
1249 bthp->leafRecords = SWAP_BE32(1);
1250 }
1251 else {
1252 bthp->treeDepth = 0;
1253 bthp->rootNode = 0;
1254 bthp->firstLeafNode = 0;
1255 bthp->lastLeafNode = 0;
1256 bthp->leafRecords = 0;
1257 }
1258
1259 bthp->nodeSize = SWAP_BE16 (nodeSize);
1260 bthp->totalNodes = SWAP_BE32 (fileSize / nodeSize);
1261 if (set_cp_level) {
1262 /* Add 1 node for the first record */
1263 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->totalNodes) - 2);
1264 }
1265 else {
1266 /* Take the header into account */
1267 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->totalNodes) - 1);
1268 }
1269 bthp->clumpSize = SWAP_BE32 (dp->attributesClumpSize);
1270
1271 bthp->attributes |= SWAP_BE32 (kBTBigKeysMask | kBTVariableIndexKeysMask);
1272 bthp->maxKeyLength = SWAP_BE16 (kHFSPlusAttrKeyMaximumLength);
1273
1274 offset += sizeof(BTHeaderRec);
1275
1276 SETOFFSET(buffer, nodeSize, offset, 2);
1277
1278 offset += kBTreeHeaderUserBytes;
1279
1280 SETOFFSET(buffer, nodeSize, offset, 3);
1281
1282
1283 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1284 nodeBitsInHeader = 8 * (nodeSize
1285 - sizeof(BTNodeDescriptor)
1286 - sizeof(BTHeaderRec)
1287 - kBTreeHeaderUserBytes
1288 - (4 * sizeof(SInt16)) );
1289 if (SWAP_BE32 (bthp->totalNodes) > nodeBitsInHeader) {
1290 UInt32 nodeBitsInMapNode;
1291
1292 ndp->fLink = SWAP_BE32 (SWAP_BE32 (bthp->lastLeafNode) + 1);
1293 nodeBitsInMapNode = 8 * (nodeSize
1294 - sizeof(BTNodeDescriptor)
1295 - (2 * sizeof(SInt16))
1296 - 2 );
1297 *mapNodes = (SWAP_BE32 (bthp->totalNodes) - nodeBitsInHeader +
1298 (nodeBitsInMapNode - 1)) / nodeBitsInMapNode;
1299 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->freeNodes) - *mapNodes);
1300 }
1301
1302
1303 /*
1304 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1305 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1306 */
1307 bmp = ((UInt8 *)buffer + offset);
1308 temp = SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes);
1309
1310 /* Working a byte at a time is endian safe */
1311 while (temp >= 8) { *bmp = 0xFF; temp -= 8; bmp++; }
1312 *bmp = ~(0xFF >> temp);
1313 offset += nodeBitsInHeader/8;
1314
1315 SETOFFSET(buffer, nodeSize, offset, 4);
1316
1317 #ifdef DEBUG_BUILD
1318 if (set_cp_level) {
1319 /* Stuff in the EA on the root folder */
1320 void *node2 = (uint8_t*)buffer + nodeSize;
1321
1322 struct cp_root_xattr ea;
1323
1324 uint8_t canonicalName[256];
1325 CFStringRef cfstr;
1326
1327 HFSPlusAttrData *attrData;
1328 HFSPlusAttrKey *attrKey;
1329 bzero(node2, nodeSize);
1330 ndp = (BTNodeDescriptor*)node2;
1331
1332 ndp->kind = kBTLeafNode;
1333 ndp->numRecords = SWAP_BE16(1);
1334 ndp->height = 1;
1335
1336 offset = sizeof(BTNodeDescriptor);
1337 SETOFFSET(node2, nodeSize, offset, 1);
1338
1339 attrKey = (HFSPlusAttrKey*)((uint8_t*)node2 + offset);
1340 attrKey->fileID = SWAP_BE32(1);
1341 attrKey->startBlock = 0;
1342 attrKey->keyLength = SWAP_BE16(sizeof(*attrKey) - sizeof(attrKey->keyLength));
1343
1344 cfstr = CFStringCreateWithCString(kCFAllocatorDefault, "com.apple.system.cprotect", kCFStringEncodingUTF8);
1345 if (_CFStringGetFileSystemRepresentation(cfstr, canonicalName, sizeof(canonicalName)) &&
1346 ConvertUTF8toUnicode(canonicalName,
1347 sizeof(attrKey->attrName),
1348 attrKey->attrName, &attrKey->attrNameLen) == 0) {
1349 attrKey->attrNameLen = SWAP_BE16(attrKey->attrNameLen);
1350 offset += sizeof(*attrKey);
1351
1352 /* If the offset is odd, move up to the next even value */
1353 if (offset & 1) {
1354 offset++;
1355 }
1356
1357 attrData = (HFSPlusAttrData*)((uint8_t*)node2 + offset);
1358 bzero(&ea, sizeof(ea));
1359 ea.vers = OSSwapHostToLittleInt16(dp->protectlevel); //(leave in LittleEndian)
1360 attrData->recordType = SWAP_BE32(kHFSPlusAttrInlineData);
1361 attrData->attrSize = SWAP_BE32(sizeof(ea));
1362 memcpy(attrData->attrData, &ea, sizeof(ea));
1363 offset += sizeof (HFSPlusAttrData) + sizeof(ea) - sizeof(attrData->attrData);
1364 }
1365 SETOFFSET (node2, nodeSize, offset, 2);
1366 CFRelease(cfstr);
1367 }
1368 #endif
1369
1370 *bytesUsed = (SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes) - *mapNodes) * nodeSize;
1371 WriteBuffer(driveInfo, startingSector, *bytesUsed, buffer);
1372 }
1373
1374 #if !TARGET_OS_EMBEDDED
1375 static int
1376 get_dev_uuid(const char *disk_name, char *dev_uuid_str, int dev_uuid_len)
1377 {
1378 io_service_t service;
1379 CFStringRef uuid_str;
1380 int ret = EINVAL;
1381
1382 if (strncmp(disk_name, _PATH_DEV, strlen(_PATH_DEV)) == 0) {
1383 disk_name += strlen(_PATH_DEV);
1384 }
1385
1386 dev_uuid_str[0] = '\0';
1387
1388 service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, disk_name));
1389 if (service != IO_OBJECT_NULL) {
1390 uuid_str = IORegistryEntryCreateCFProperty(service, CFSTR(kIOMediaUUIDKey), kCFAllocatorDefault, 0);
1391 if (uuid_str) {
1392 if (CFStringGetFileSystemRepresentation(uuid_str, dev_uuid_str, dev_uuid_len) != 0) {
1393 ret = 0;
1394 }
1395 CFRelease(uuid_str);
1396 }
1397 IOObjectRelease(service);
1398 }
1399
1400 return ret;
1401 }
1402
1403 static int
1404 clear_journal_dev(const char *dev_name)
1405 {
1406 int fd;
1407
1408 fd = open(dev_name, O_RDWR);
1409 if (fd < 0) {
1410 printf("Failed to open the journal device %s (%s)\n", dev_name, strerror(errno));
1411 return -1;
1412 }
1413
1414 dowipefs(fd);
1415
1416 close(fd);
1417 return 0;
1418 }
1419 #endif /* !TARGET_OS_EMBEDDED */
1420
1421
1422 static int
1423 WriteJournalInfo(const DriveInfo *driveInfo, UInt64 startingSector,
1424 const hfsparams_t *dp, HFSPlusVolumeHeader *header,
1425 void *buffer)
1426 {
1427 JournalInfoBlock *jibp = buffer;
1428 UInt32 journalBlock;
1429
1430 memset(buffer, 0xdb, driveInfo->physSectorSize);
1431 memset(jibp, 0, sizeof(JournalInfoBlock));
1432
1433 #if !TARGET_OS_EMBEDDED
1434 if (dp->journalDevice) {
1435 char uuid_str[64];
1436
1437 if (get_dev_uuid(dp->journalDevice, uuid_str, sizeof(uuid_str)) == 0) {
1438 strlcpy((char *)&jibp->reserved[0], uuid_str, sizeof(jibp->reserved));
1439
1440 // we also need to blast out some zeros to the journal device
1441 // in case it had a file system on it previously. that way
1442 // it's "initialized" in the sense that the previous contents
1443 // won't get mounted accidently. if this fails we'll bail out.
1444 if (clear_journal_dev(dp->journalDevice) != 0) {
1445 return -1;
1446 }
1447 } else {
1448 printf("FAILED to get the device uuid for device %s\n", dp->journalDevice);
1449 strlcpy((char *)&jibp->reserved[0], "NO-DEV-UUID", sizeof(jibp->reserved));
1450 return -1;
1451 }
1452 } else {
1453 #endif
1454 jibp->flags = kJIJournalInFSMask;
1455 #if !TARGET_OS_EMBEDDED
1456 }
1457 #endif
1458 jibp->flags |= kJIJournalNeedInitMask;
1459 if (NEWFS_HFS_DEBUG && dp->journalBlock)
1460 journalBlock = dp->journalBlock;
1461 else
1462 journalBlock = header->journalInfoBlock + 1;
1463 jibp->offset = ((UInt64) journalBlock) * header->blockSize;
1464 jibp->size = dp->journalSize;
1465
1466 jibp->flags = SWAP_BE32(jibp->flags);
1467 jibp->offset = SWAP_BE64(jibp->offset);
1468 jibp->size = SWAP_BE64(jibp->size);
1469
1470 WriteBuffer(driveInfo, startingSector, driveInfo->physSectorSize, buffer);
1471
1472 jibp->flags = SWAP_BE32(jibp->flags);
1473 jibp->offset = SWAP_BE64(jibp->offset);
1474 jibp->size = SWAP_BE64(jibp->size);
1475
1476 if (jibp->flags & kJIJournalInFSMask) {
1477 /*
1478 * Zero out the on-disk content of the journal file.
1479 *
1480 * This is a really ugly hack. Right now, all of the logic in the code
1481 * that calls us (make_hfsplus), uses the value 'sectorsPerBlock' but it
1482 * is really hardcoded to assume the sector size is 512 bytes. The code
1483 * in WriteBuffer will massage the I/O to use the actual physical sector
1484 * size. Since WriteBuffer takes a sector # relative to 512 byte sectors,
1485 * We need to convert the journal offset in bytes to value that represents
1486 * its start LBA in 512 byte sectors.
1487 *
1488 * Note further that we swapped to big endian prior to the WriteBuffer call,
1489 * but we have swapped back to native after the call.
1490 */
1491 WriteBuffer(driveInfo, jibp->offset / kBytesPerSector, jibp->size, NULL);
1492 }
1493
1494 return 0;
1495 }
1496
1497
1498 /*
1499 * WriteCatalogFile
1500 *
1501 * This routine initializes a Catalog B-Tree.
1502 *
1503 * Note: Since large volumes can have bigger b-trees they
1504 * might need to have map nodes setup.
1505 */
1506 static void
1507 WriteCatalogFile(const DriveInfo *driveInfo, UInt64 startingSector,
1508 const hfsparams_t *dp, HFSPlusVolumeHeader *header, void *buffer,
1509 UInt32 *bytesUsed, UInt32 *mapNodes)
1510 {
1511 BTNodeDescriptor *ndp;
1512 BTHeaderRec *bthp;
1513 UInt8 *bmp;
1514 UInt32 nodeBitsInHeader;
1515 UInt32 fileSize;
1516 UInt32 nodeSize;
1517 UInt32 temp;
1518 SInt16 offset;
1519
1520 *mapNodes = 0;
1521 fileSize = dp->catalogInitialSize;
1522 nodeSize = dp->catalogNodeSize;
1523
1524 bzero(buffer, nodeSize);
1525
1526
1527 /* FILL IN THE NODE DESCRIPTOR: */
1528 ndp = (BTNodeDescriptor *)buffer;
1529 ndp->kind = kBTHeaderNode;
1530 ndp->numRecords = SWAP_BE16 (3);
1531 offset = sizeof(BTNodeDescriptor);
1532
1533 SETOFFSET(buffer, nodeSize, offset, 1);
1534
1535
1536 /* FILL IN THE HEADER RECORD: */
1537 bthp = (BTHeaderRec *)((UInt8 *)buffer + offset);
1538 bthp->treeDepth = SWAP_BE16 (1);
1539 bthp->rootNode = SWAP_BE32 (1);
1540 bthp->firstLeafNode = SWAP_BE32 (1);
1541 bthp->lastLeafNode = SWAP_BE32 (1);
1542 bthp->leafRecords = SWAP_BE32 (dp->journaledHFS ? 6 : 2);
1543 bthp->nodeSize = SWAP_BE16 (nodeSize);
1544 bthp->totalNodes = SWAP_BE32 (fileSize / nodeSize);
1545 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->totalNodes) - 2); /* header and root */
1546 bthp->clumpSize = SWAP_BE32 (dp->catalogClumpSize);
1547
1548
1549 bthp->attributes |= SWAP_BE32 (kBTVariableIndexKeysMask + kBTBigKeysMask);
1550 bthp->maxKeyLength = SWAP_BE16 (kHFSPlusCatalogKeyMaximumLength);
1551 if (dp->flags & kMakeCaseSensitive)
1552 bthp->keyCompareType = kHFSBinaryCompare;
1553 else
1554 bthp->keyCompareType = kHFSCaseFolding;
1555
1556 offset += sizeof(BTHeaderRec);
1557
1558 SETOFFSET(buffer, nodeSize, offset, 2);
1559
1560 offset += kBTreeHeaderUserBytes;
1561
1562 SETOFFSET(buffer, nodeSize, offset, 3);
1563
1564 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1565 nodeBitsInHeader = 8 * (nodeSize
1566 - sizeof(BTNodeDescriptor)
1567 - sizeof(BTHeaderRec)
1568 - kBTreeHeaderUserBytes
1569 - (4 * sizeof(SInt16)) );
1570
1571 if (SWAP_BE32 (bthp->totalNodes) > nodeBitsInHeader) {
1572 UInt32 nodeBitsInMapNode;
1573
1574 ndp->fLink = SWAP_BE32 (SWAP_BE32 (bthp->lastLeafNode) + 1);
1575 nodeBitsInMapNode = 8 * (nodeSize
1576 - sizeof(BTNodeDescriptor)
1577 - (2 * sizeof(SInt16))
1578 - 2 );
1579 *mapNodes = (SWAP_BE32 (bthp->totalNodes) - nodeBitsInHeader +
1580 (nodeBitsInMapNode - 1)) / nodeBitsInMapNode;
1581 bthp->freeNodes = SWAP_BE32 (SWAP_BE32 (bthp->freeNodes) - *mapNodes);
1582 }
1583
1584 /*
1585 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1586 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1587 */
1588 bmp = ((UInt8 *)buffer + offset);
1589 temp = SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes);
1590
1591 /* Working a byte at a time is endian safe */
1592 while (temp >= 8) { *bmp = 0xFF; temp -= 8; bmp++; }
1593 *bmp = ~(0xFF >> temp);
1594 offset += nodeBitsInHeader/8;
1595
1596 SETOFFSET(buffer, nodeSize, offset, 4);
1597
1598 InitCatalogRoot_HFSPlus(dp, header, buffer + nodeSize);
1599
1600 *bytesUsed = (SWAP_BE32 (bthp->totalNodes) - SWAP_BE32 (bthp->freeNodes) - *mapNodes) * nodeSize;
1601
1602 WriteBuffer(driveInfo, startingSector, *bytesUsed, buffer);
1603 }
1604
1605
1606 static void
1607 InitCatalogRoot_HFSPlus(const hfsparams_t *dp, const HFSPlusVolumeHeader *header, void * buffer)
1608 {
1609 BTNodeDescriptor *ndp;
1610 HFSPlusCatalogKey *ckp;
1611 HFSPlusCatalogKey *tkp;
1612 HFSPlusCatalogFolder *cdp;
1613 HFSPlusCatalogFile *cfp;
1614 HFSPlusCatalogThread *ctp;
1615 UInt16 nodeSize;
1616 SInt16 offset;
1617 size_t unicodeBytes;
1618 UInt8 canonicalName[kHFSPlusMaxFileNameBytes]; // UTF8 character may convert to three bytes, plus a NUL
1619 CFStringRef cfstr;
1620 Boolean cfOK;
1621 int index = 0;
1622
1623 nodeSize = dp->catalogNodeSize;
1624 bzero(buffer, nodeSize);
1625
1626 /*
1627 * All nodes have a node descriptor...
1628 */
1629 ndp = (BTNodeDescriptor *)buffer;
1630 ndp->kind = kBTLeafNode;
1631 ndp->height = 1;
1632 ndp->numRecords = SWAP_BE16 (dp->journaledHFS ? 6 : 2);
1633 offset = sizeof(BTNodeDescriptor);
1634 SETOFFSET(buffer, nodeSize, offset, ++index);
1635
1636 /*
1637 * First record is always the root directory...
1638 */
1639 ckp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1640
1641 /* Use CFString functions to get a HFSPlus Canonical name */
1642 cfstr = CFStringCreateWithCString(kCFAllocatorDefault, (char *)dp->volumeName, kCFStringEncodingUTF8);
1643 cfOK = _CFStringGetFileSystemRepresentation(cfstr, canonicalName, sizeof(canonicalName));
1644
1645 if (!cfOK || ConvertUTF8toUnicode(canonicalName, sizeof(ckp->nodeName.unicode),
1646 ckp->nodeName.unicode, &ckp->nodeName.length)) {
1647
1648 /* On conversion errors "untitled" is used as a fallback. */
1649 (void) ConvertUTF8toUnicode((UInt8 *)kDefaultVolumeNameStr,
1650 sizeof(ckp->nodeName.unicode),
1651 ckp->nodeName.unicode,
1652 &ckp->nodeName.length);
1653 warnx("invalid HFS+ name: \"%s\", using \"%s\" instead",
1654 dp->volumeName, kDefaultVolumeNameStr);
1655 }
1656 CFRelease(cfstr);
1657 ckp->nodeName.length = SWAP_BE16 (ckp->nodeName.length);
1658
1659 unicodeBytes = sizeof(UniChar) * SWAP_BE16 (ckp->nodeName.length);
1660
1661 ckp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength + unicodeBytes);
1662 ckp->parentID = SWAP_BE32 (kHFSRootParentID);
1663 offset += SWAP_BE16 (ckp->keyLength) + 2;
1664
1665 cdp = (HFSPlusCatalogFolder *)((UInt8 *)buffer + offset);
1666 cdp->recordType = SWAP_BE16 (kHFSPlusFolderRecord);
1667 /* folder count is only supported on HFSX volumes */
1668 if (dp->flags & kMakeCaseSensitive) {
1669 cdp->flags = SWAP_BE16 (kHFSHasFolderCountMask);
1670 }
1671 cdp->valence = SWAP_BE32 (dp->journaledHFS ? 2 : 0);
1672 cdp->folderID = SWAP_BE32 (kHFSRootFolderID);
1673 cdp->createDate = SWAP_BE32 (dp->createDate);
1674 cdp->contentModDate = SWAP_BE32 (dp->createDate);
1675 cdp->textEncoding = SWAP_BE32 (kTextEncodingMacUnicode);
1676 if (dp->flags & kUseAccessPerms) {
1677 cdp->bsdInfo.ownerID = SWAP_BE32 (dp->owner);
1678 cdp->bsdInfo.groupID = SWAP_BE32 (dp->group);
1679 cdp->bsdInfo.fileMode = SWAP_BE16 (dp->mask | S_IFDIR);
1680 }
1681 offset += sizeof(HFSPlusCatalogFolder);
1682 SETOFFSET(buffer, nodeSize, offset, ++index);
1683
1684 /*
1685 * Second record is always the root directory thread...
1686 */
1687 tkp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1688 tkp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength);
1689 tkp->parentID = SWAP_BE32 (kHFSRootFolderID);
1690 // tkp->nodeName.length = 0;
1691
1692 offset += SWAP_BE16 (tkp->keyLength) + 2;
1693
1694 ctp = (HFSPlusCatalogThread *)((UInt8 *)buffer + offset);
1695 ctp->recordType = SWAP_BE16 (kHFSPlusFolderThreadRecord);
1696 ctp->parentID = SWAP_BE32 (kHFSRootParentID);
1697 bcopy(&ckp->nodeName, &ctp->nodeName, sizeof(UInt16) + unicodeBytes);
1698 offset += (sizeof(HFSPlusCatalogThread)
1699 - (sizeof(ctp->nodeName.unicode) - unicodeBytes) );
1700
1701 SETOFFSET(buffer, nodeSize, offset, ++index);
1702
1703 /*
1704 * Add records for ".journal" and ".journal_info_block" files:
1705 */
1706 if (dp->journaledHFS) {
1707 struct HFSUniStr255 *nodename1, *nodename2;
1708 size_t uBytes1, uBytes2;
1709 UInt32 journalBlock;
1710
1711 /* File record #1 */
1712 ckp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1713 (void) ConvertUTF8toUnicode((UInt8 *)HFS_JOURNAL_FILE, sizeof(ckp->nodeName.unicode),
1714 ckp->nodeName.unicode, &ckp->nodeName.length);
1715 ckp->nodeName.length = SWAP_BE16 (ckp->nodeName.length);
1716 uBytes1 = sizeof(UniChar) * SWAP_BE16 (ckp->nodeName.length);
1717 ckp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength + uBytes1);
1718 ckp->parentID = SWAP_BE32 (kHFSRootFolderID);
1719 offset += SWAP_BE16 (ckp->keyLength) + 2;
1720
1721 cfp = (HFSPlusCatalogFile *)((UInt8 *)buffer + offset);
1722 cfp->recordType = SWAP_BE16 (kHFSPlusFileRecord);
1723 cfp->flags = SWAP_BE16 (kHFSThreadExistsMask);
1724 cfp->fileID = SWAP_BE32 (dp->nextFreeFileID);
1725 cfp->createDate = SWAP_BE32 (dp->createDate + 1);
1726 cfp->contentModDate = SWAP_BE32 (dp->createDate + 1);
1727 cfp->textEncoding = 0;
1728
1729 cfp->bsdInfo.fileMode = SWAP_BE16 (S_IFREG);
1730 cfp->bsdInfo.ownerFlags = (uint8_t) SWAP_BE16 (((uint16_t)UF_NODUMP));
1731 cfp->bsdInfo.special.linkCount = SWAP_BE32(1);
1732 cfp->userInfo.fdType = SWAP_BE32 (kJournalFileType);
1733 cfp->userInfo.fdCreator = SWAP_BE32 (kHFSPlusCreator);
1734 cfp->userInfo.fdFlags = SWAP_BE16 (kIsInvisible + kNameLocked);
1735 cfp->dataFork.logicalSize = SWAP_BE64 (dp->journalSize);
1736 cfp->dataFork.totalBlocks = SWAP_BE32 ((dp->journalSize+dp->blockSize-1) / dp->blockSize);
1737
1738 if (NEWFS_HFS_DEBUG && dp->journalBlock)
1739 journalBlock = dp->journalBlock;
1740 else
1741 journalBlock = header->journalInfoBlock + 1;
1742 cfp->dataFork.extents[0].startBlock = SWAP_BE32 (journalBlock);
1743 cfp->dataFork.extents[0].blockCount = cfp->dataFork.totalBlocks;
1744
1745 offset += sizeof(HFSPlusCatalogFile);
1746 SETOFFSET(buffer, nodeSize, offset, ++index);
1747 nodename1 = &ckp->nodeName;
1748
1749 /* File record #2 */
1750 ckp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1751 (void) ConvertUTF8toUnicode((UInt8 *)HFS_JOURNAL_INFO, sizeof(ckp->nodeName.unicode),
1752 ckp->nodeName.unicode, &ckp->nodeName.length);
1753 ckp->nodeName.length = SWAP_BE16 (ckp->nodeName.length);
1754 uBytes2 = sizeof(UniChar) * SWAP_BE16 (ckp->nodeName.length);
1755 ckp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength + uBytes2);
1756 ckp->parentID = SWAP_BE32 (kHFSRootFolderID);
1757 offset += SWAP_BE16 (ckp->keyLength) + 2;
1758
1759 cfp = (HFSPlusCatalogFile *)((UInt8 *)buffer + offset);
1760 cfp->recordType = SWAP_BE16 (kHFSPlusFileRecord);
1761 cfp->flags = SWAP_BE16 (kHFSThreadExistsMask);
1762 cfp->fileID = SWAP_BE32 (dp->nextFreeFileID + 1);
1763 cfp->createDate = SWAP_BE32 (dp->createDate);
1764 cfp->contentModDate = SWAP_BE32 (dp->createDate);
1765 cfp->textEncoding = 0;
1766
1767 cfp->bsdInfo.fileMode = SWAP_BE16 (S_IFREG);
1768 cfp->bsdInfo.ownerFlags = (uint8_t) SWAP_BE16 (((uint16_t)UF_NODUMP));
1769 cfp->bsdInfo.special.linkCount = SWAP_BE32(1);
1770 cfp->userInfo.fdType = SWAP_BE32 (kJournalFileType);
1771 cfp->userInfo.fdCreator = SWAP_BE32 (kHFSPlusCreator);
1772 cfp->userInfo.fdFlags = SWAP_BE16 (kIsInvisible + kNameLocked);
1773 cfp->dataFork.logicalSize = SWAP_BE64(dp->blockSize);;
1774 cfp->dataFork.totalBlocks = SWAP_BE32(1);
1775
1776 cfp->dataFork.extents[0].startBlock = SWAP_BE32 (header->journalInfoBlock);
1777 cfp->dataFork.extents[0].blockCount = cfp->dataFork.totalBlocks;
1778
1779 offset += sizeof(HFSPlusCatalogFile);
1780 SETOFFSET(buffer, nodeSize, offset, ++index);
1781 nodename2 = &ckp->nodeName;
1782
1783 /* Thread record for file #1 */
1784 tkp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1785 tkp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength);
1786 tkp->parentID = SWAP_BE32 (dp->nextFreeFileID);
1787 tkp->nodeName.length = 0;
1788 offset += SWAP_BE16 (tkp->keyLength) + 2;
1789
1790 ctp = (HFSPlusCatalogThread *)((UInt8 *)buffer + offset);
1791 ctp->recordType = SWAP_BE16 (kHFSPlusFileThreadRecord);
1792 ctp->parentID = SWAP_BE32 (kHFSRootFolderID);
1793 bcopy(nodename1, &ctp->nodeName, sizeof(UInt16) + uBytes1);
1794 offset += (sizeof(HFSPlusCatalogThread)
1795 - (sizeof(ctp->nodeName.unicode) - uBytes1) );
1796 SETOFFSET(buffer, nodeSize, offset, ++index);
1797
1798 /* Thread record for file #2 */
1799 tkp = (HFSPlusCatalogKey *)((UInt8 *)buffer + offset);
1800 tkp->keyLength = SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength);
1801 tkp->parentID = SWAP_BE32 (dp->nextFreeFileID + 1);
1802 tkp->nodeName.length = 0;
1803 offset += SWAP_BE16 (tkp->keyLength) + 2;
1804
1805 ctp = (HFSPlusCatalogThread *)((UInt8 *)buffer + offset);
1806 ctp->recordType = SWAP_BE16 (kHFSPlusFileThreadRecord);
1807 ctp->parentID = SWAP_BE32 (kHFSRootFolderID);
1808 bcopy(nodename2, &ctp->nodeName, sizeof(UInt16) + uBytes2);
1809 offset += (sizeof(HFSPlusCatalogThread)
1810 - (sizeof(ctp->nodeName.unicode) - uBytes2) );
1811 SETOFFSET(buffer, nodeSize, offset, ++index);
1812 }
1813 }
1814
1815 /*
1816 * WriteMapNodes
1817 *
1818 * Initializes a B-tree map node and writes it out to disk.
1819 */
1820 static void
1821 WriteMapNodes(const DriveInfo *driveInfo, UInt64 diskStart, UInt32 firstMapNode,
1822 UInt32 mapNodes, UInt16 btNodeSize, void *buffer)
1823 {
1824 UInt32 sectorsPerNode;
1825 UInt32 mapRecordBytes;
1826 UInt16 i;
1827 BTNodeDescriptor *nd = (BTNodeDescriptor *)buffer;
1828
1829 bzero(buffer, btNodeSize);
1830
1831 nd->kind = kBTMapNode;
1832 nd->numRecords = SWAP_BE16 (1);
1833
1834 /* note: must belong word aligned (hence the extra -2) */
1835 mapRecordBytes = btNodeSize - sizeof(BTNodeDescriptor) - 2*sizeof(SInt16) - 2;
1836
1837 SETOFFSET(buffer, btNodeSize, sizeof(BTNodeDescriptor), 1);
1838 SETOFFSET(buffer, btNodeSize, sizeof(BTNodeDescriptor) + mapRecordBytes, 2);
1839
1840 sectorsPerNode = btNodeSize/kBytesPerSector;
1841
1842 /*
1843 * Note - worst case (32MB alloc blk) will have
1844 * only 18 map nodes. So don't bother optimizing
1845 * this section to do multiblock writes!
1846 */
1847 for (i = 0; i < mapNodes; i++) {
1848 if ((i + 1) < mapNodes)
1849 nd->fLink = SWAP_BE32 (++firstMapNode); /* point to next map node */
1850 else
1851 nd->fLink = 0; /* this is the last map node */
1852
1853 WriteBuffer(driveInfo, diskStart, btNodeSize, buffer);
1854
1855 diskStart += sectorsPerNode;
1856 }
1857 }
1858
1859 /*
1860 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1861 * NOTE: IF buffer IS NULL, THIS FUNCTION WILL WRITE ZERO'S.
1862 *
1863 * startingSector is in terms of 512-byte sectors.
1864 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1865 */
1866 static void
1867 WriteBuffer(const DriveInfo *driveInfo, UInt64 startingSector, UInt64 byteCount,
1868 const void *buffer)
1869 {
1870 off_t sector;
1871 off_t physSector = 0;
1872 off_t byteOffsetInPhysSector;
1873 UInt32 numBytesToIO;
1874 UInt32 numPhysSectorsToIO;
1875 UInt32 tempbufSizeInPhysSectors;
1876 UInt32 tempbufSize;
1877 UInt32 fd = driveInfo->fd;
1878 UInt32 physSectorSize = driveInfo->physSectorSize;
1879 void *tempbuf = NULL;
1880 int sectorSizeRatio = driveInfo->physSectorSize / kBytesPerSector;
1881 int status = 0; /* 0: no error; 1: alloc; 2: read; 3: write */
1882
1883 if (0 == byteCount) {
1884 goto exit;
1885 }
1886
1887 /*@@@@@@@@@@ buffer allocation @@@@@@@@@@*/
1888 /* try a buffer size for optimal IO, __UP TO 4MB__. if that
1889 fails, then try with the minimum allowed buffer size, which
1890 is equal to physSectorSize */
1891 tempbufSizeInPhysSectors = MIN ( (byteCount - 1 + physSectorSize) / physSectorSize,
1892 driveInfo->physSectorsPerIO );
1893 /* limit at 4MB */
1894 tempbufSizeInPhysSectors = MIN ( tempbufSizeInPhysSectors, (4 * 1024 * 1024) / physSectorSize );
1895 tempbufSize = tempbufSizeInPhysSectors * physSectorSize;
1896
1897 if ((tempbuf = valloc(tempbufSize)) == NULL) {
1898 /* try allocation of smallest allowed size: one
1899 physical sector.
1900 NOTE: the previous valloc tempbufSize might have
1901 already been one physical sector. we don't want to
1902 check if that was the case, so just try again.
1903 */
1904 tempbufSizeInPhysSectors = 1;
1905 tempbufSize = physSectorSize;
1906 if ((tempbuf = valloc(tempbufSize)) == NULL) {
1907 status = 1;
1908 goto exit;
1909 }
1910 }
1911
1912 /*@@@@@@@@@@ io @@@@@@@@@@*/
1913 sector = driveInfo->sectorOffset + startingSector;
1914 physSector = sector / sectorSizeRatio;
1915 byteOffsetInPhysSector = (sector % sectorSizeRatio) * kBytesPerSector;
1916
1917 while (byteCount > 0) {
1918 numPhysSectorsToIO = MIN ( (byteCount - 1 + physSectorSize) / physSectorSize,
1919 tempbufSizeInPhysSectors );
1920 numBytesToIO = MIN(byteCount, (unsigned)((numPhysSectorsToIO * physSectorSize) - byteOffsetInPhysSector));
1921
1922 /* if IO does not align with physical sector boundaries */
1923 if ((0 != byteOffsetInPhysSector) || ((numBytesToIO % physSectorSize) != 0)) {
1924 if (pread(fd, tempbuf, numPhysSectorsToIO * physSectorSize, physSector * physSectorSize) < 0) {
1925 status = 2;
1926 goto exit;
1927 }
1928 }
1929
1930 if (NULL != buffer) {
1931 memcpy(tempbuf + byteOffsetInPhysSector, buffer, numBytesToIO);
1932 }
1933 else {
1934 bzero(tempbuf + byteOffsetInPhysSector, numBytesToIO);
1935 }
1936
1937 if (pwrite(fd, tempbuf, numPhysSectorsToIO * physSectorSize, physSector * physSectorSize) < 0) {
1938 warn("%s: pwrite(%d, %p, %zu, %lld)", __FUNCTION__, fd, tempbuf, (size_t)(numPhysSectorsToIO * physSectorSize), (long long)(physSector * physSectorSize));
1939 status = 3;
1940 goto exit;
1941 }
1942
1943 byteOffsetInPhysSector = 0;
1944 byteCount -= numBytesToIO;
1945 physSector += numPhysSectorsToIO;
1946 if (NULL != buffer) {
1947 buffer += numBytesToIO;
1948 }
1949 }
1950
1951 exit:
1952 if (tempbuf) {
1953 free(tempbuf);
1954 tempbuf = NULL;
1955 }
1956
1957 if (1 == status) {
1958 err(1, NULL);
1959 }
1960 else if (2 == status) {
1961 err(1, "read (sector %llu)", physSector);
1962 }
1963 else if (3 == status) {
1964 err(1, "write (sector %llu)", physSector);
1965 }
1966
1967 return;
1968 }
1969
1970
1971 static UInt32 Largest( UInt32 a, UInt32 b, UInt32 c, UInt32 d )
1972 {
1973 /* a := max(a,b) */
1974 if (a < b)
1975 a = b;
1976 /* c := max(c,d) */
1977 if (c < d)
1978 c = d;
1979
1980 /* return max(a,c) */
1981 if (a > c)
1982 return a;
1983 else
1984 return c;
1985 }
1986
1987 /*
1988 * UTCToLocal - convert from Mac OS GMT time to Mac OS local time
1989 */
1990 static UInt32 UTCToLocal(UInt32 utcTime)
1991 {
1992 UInt32 localTime = utcTime;
1993 struct timezone timeZone;
1994 struct timeval timeVal;
1995
1996 if (localTime != 0) {
1997
1998 /* HFS volumes need timezone info to convert local to GMT */
1999 (void)gettimeofday( &timeVal, &timeZone );
2000
2001
2002 localTime -= (timeZone.tz_minuteswest * 60);
2003 if (timeZone.tz_dsttime)
2004 localTime += 3600;
2005 }
2006
2007 return (localTime);
2008 }
2009
2010 static int
2011 ConvertUTF8toUnicode(const UInt8* source, size_t bufsize, UniChar* unibuf,
2012 UInt16 *charcount)
2013 {
2014 UInt8 byte;
2015 UniChar* target;
2016 UniChar* targetEnd;
2017
2018 *charcount = 0;
2019 target = unibuf;
2020 targetEnd = (UniChar *)((UInt8 *)unibuf + bufsize);
2021
2022 while ((byte = *source++)) {
2023
2024 /* check for single-byte ascii */
2025 if (byte < 128) {
2026 if (byte == ':') /* ':' is mapped to '/' */
2027 byte = '/';
2028
2029 *target++ = SWAP_BE16 (byte);
2030 } else {
2031 UniChar ch;
2032 UInt8 seq = (byte >> 4);
2033
2034 switch (seq) {
2035 case 0xc: /* double-byte sequence (1100 and 1101) */
2036 case 0xd:
2037 ch = (byte & 0x1F) << 6; /* get 5 bits */
2038 if (((byte = *source++) >> 6) != 2)
2039 return (EINVAL);
2040 break;
2041
2042 case 0xe: /* triple-byte sequence (1110) */
2043 ch = (byte & 0x0F) << 6; /* get 4 bits */
2044 if (((byte = *source++) >> 6) != 2)
2045 return (EINVAL);
2046 ch += (byte & 0x3F); ch <<= 6; /* get 6 bits */
2047 if (((byte = *source++) >> 6) != 2)
2048 return (EINVAL);
2049 break;
2050
2051 default:
2052 return (EINVAL); /* malformed sequence */
2053 }
2054
2055 ch += (byte & 0x3F); /* get last 6 bits */
2056
2057 if (target >= targetEnd)
2058 return (ENOBUFS);
2059
2060 *target++ = SWAP_BE16 (ch);
2061 }
2062 }
2063
2064 *charcount = target - unibuf;
2065
2066 return (0);
2067 }
2068
2069 /* Generate Volume UUID - similar to code existing in hfs_util */
2070 void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
2071 SHA_CTX context;
2072 char randomInputBuffer[26];
2073 unsigned char digest[20];
2074 time_t now;
2075 clock_t uptime;
2076 int mib[2];
2077 int sysdata;
2078 char sysctlstring[128];
2079 size_t datalen;
2080 double sysloadavg[3];
2081 struct vmtotal sysvmtotal;
2082
2083 do {
2084 /* Initialize the SHA-1 context for processing: */
2085 SHA1_Init(&context);
2086
2087 /* Now process successive bits of "random" input to seed the process: */
2088
2089 /* The current system's uptime: */
2090 uptime = clock();
2091 SHA1_Update(&context, &uptime, sizeof(uptime));
2092
2093 /* The kernel's boot time: */
2094 mib[0] = CTL_KERN;
2095 mib[1] = KERN_BOOTTIME;
2096 datalen = sizeof(sysdata);
2097 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2098 SHA1_Update(&context, &sysdata, datalen);
2099
2100 /* The system's host id: */
2101 mib[0] = CTL_KERN;
2102 mib[1] = KERN_HOSTID;
2103 datalen = sizeof(sysdata);
2104 sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2105 SHA1_Update(&context, &sysdata, datalen);
2106
2107 /* The system's host name: */
2108 mib[0] = CTL_KERN;
2109 mib[1] = KERN_HOSTNAME;
2110 datalen = sizeof(sysctlstring);
2111 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2112 SHA1_Update(&context, sysctlstring, datalen);
2113
2114 /* The running kernel's OS release string: */
2115 mib[0] = CTL_KERN;
2116 mib[1] = KERN_OSRELEASE;
2117 datalen = sizeof(sysctlstring);
2118 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2119 SHA1_Update(&context, sysctlstring, datalen);
2120
2121 /* The running kernel's version string: */
2122 mib[0] = CTL_KERN;
2123 mib[1] = KERN_VERSION;
2124 datalen = sizeof(sysctlstring);
2125 sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2126 SHA1_Update(&context, sysctlstring, datalen);
2127
2128 /* The system's load average: */
2129 datalen = sizeof(sysloadavg);
2130 getloadavg(sysloadavg, 3);
2131 SHA1_Update(&context, &sysloadavg, datalen);
2132
2133 /* The system's VM statistics: */
2134 mib[0] = CTL_VM;
2135 mib[1] = VM_METER;
2136 datalen = sizeof(sysvmtotal);
2137 sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
2138 SHA1_Update(&context, &sysvmtotal, datalen);
2139
2140 /* The current GMT (26 ASCII characters): */
2141 time(&now);
2142 strncpy(randomInputBuffer, asctime(gmtime(&now)), 26); /* "Mon Mar 27 13:46:26 2000" */
2143 SHA1_Update(&context, randomInputBuffer, 26);
2144
2145 /* Pad the accumulated input and extract the final digest hash: */
2146 SHA1_Final(digest, &context);
2147
2148 memcpy(newVolumeID, digest, sizeof(*newVolumeID));
2149 } while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
2150 }
2151
2152