2 * Copyright (c) 1999-2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: Initialization code for HFS and HFS Plus volumes.
28 Copyright: � 1984-1999 by Apple Computer, Inc., all rights reserved.
32 #include <sys/param.h>
33 #include <sys/types.h>
36 #include <sys/errno.h>
38 #include <sys/sysctl.h>
39 #include <sys/vmmeter.h>
52 #include <TargetConditionals.h>
56 // CoreServices is not available, so...
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 */
74 kTextEncodingMacUnicode
= 0x7E,
77 #else // !TARGET_OS_IPHONE
79 #include <CoreServices/CoreServices.h>
81 #endif // !TARGET_OS_IPHONE
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.
88 #define COMMON_DIGEST_FOR_OPENSSL
89 #include <CommonCrypto/CommonDigest.h>
91 #include <libkern/OSByteOrder.h>
93 #include <CoreFoundation/CFString.h>
94 #include <CoreFoundation/CFStringEncodingExt.h>
95 #include <IOKit/IOKitLib.h>
96 #include <IOKit/storage/IOMedia.h>
98 #include <TargetConditionals.h>
100 extern Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
103 #include <hfs/hfs_format.h>
104 #include <hfs/hfs_mount.h>
105 #include "hfs_endian.h"
107 #include "newfs_hfs.h"
109 #ifndef NEWFS_HFS_DEBUG
111 # define NEWFS_HFS_DEBUG 1
113 # define NEWFS_HFS_DEBUG 0
117 #define HFS_BOOT_DATA "/usr/share/misc/hfsbootdata"
119 #define HFS_JOURNAL_FILE ".journal"
120 #define HFS_JOURNAL_INFO ".journal_info_block"
122 #define kJournalFileType 0x6a726e6c /* 'jrnl' */
125 typedef HFSMasterDirectoryBlock HFS_MDB
;
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
;
141 struct filefork gDTDBFork
, gSystemFork
, gReadMeFork
;
143 static void WriteVH
__P((const DriveInfo
*driveInfo
, HFSPlusVolumeHeader
*hp
));
144 static void InitVH
__P((hfsparams_t
*defaults
, UInt64 sectors
,
145 HFSPlusVolumeHeader
*header
));
147 static int AllocateExtent(UInt8
*buffer
, UInt32 startBlock
, UInt32 blockCount
);
148 static int MarkExtentUsed(const DriveInfo
*, HFSPlusVolumeHeader
*, UInt32
, UInt32
);
150 static void WriteExtentsFile
__P((const DriveInfo
*dip
, UInt64 startingSector
,
151 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp
, void *buffer
,
152 UInt32
*bytesUsed
, UInt32
*mapNodes
));
154 static void WriteAttributesFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
155 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp
, void *buffer
,
156 UInt32
*bytesUsed
, UInt32
*mapNodes
);
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
,
164 static void InitCatalogRoot_HFSPlus
__P((const hfsparams_t
*dp
, const HFSPlusVolumeHeader
*header
, void * buffer
));
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
));
172 static UInt32
GetDefaultEncoding();
174 static UInt32 UTCToLocal
__P((UInt32 utcTime
));
176 static int ConvertUTF8toUnicode
__P((const UInt8
* source
, size_t bufsize
,
177 UniChar
* unibuf
, UInt16
*charcount
));
179 #define VOLUMEUUIDVALUESIZE 2
180 typedef union VolumeUUID
{
181 UInt32 value
[VOLUMEUUIDVALUESIZE
];
187 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
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)))
193 #define BYTESTOBLKS(bytes,blks) DivideAndRoundUp((bytes),(blks))
195 #define ROUNDUP(x, u) (((x) % (u) == 0) ? (x) : ((x)/(u) + 1) * (u))
197 #if TARGET_OS_EMBEDDED
198 #define ENCODING_TO_BIT(e) \
201 #define ENCODING_TO_BIT(e) \
203 ((e) == kCFStringEncodingMacUkrainian ? 48 : \
204 ((e) == kCFStringEncodingMacFarsi ? 49 : 0)))
209 struct cp_root_xattr
{
213 u_int8_t reserved3
[16];
214 } __attribute__((aligned(2), packed
));
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.
224 createExtents(HFSPlusForkData
*file
,
230 if (NEWFS_HFS_DEBUG
== 0) {
232 * The common case, for non-debug.
234 file
->extents
[0].startBlock
= startBlock
;
235 file
->extents
[0].blockCount
= file
->totalBlocks
;
237 UInt32 blocksLeft
, blocksTotal
= 0, blockStep
;
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
;
247 if (file
->totalBlocks
< numExtents
)
248 numExtents
= file
->totalBlocks
;
250 blocksLeft
= file
->totalBlocks
;
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.)
260 * If there are more requested extents than blocks, the division
261 * works out to zero... so we limit blockStep to minBlocks.
264 blockStep
= blocksLeft
/ numExtents
;
267 * To allow invalid extent lengths, set minBlocks to 1, and
268 * comment out the next two if statements.
270 if ((blockStep
% minBlocks
) != 0)
271 blockStep
= (blockStep
/ minBlocks
) * minBlocks
;
273 blockStep
= minBlocks
;
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.
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
);
288 * Now, at this point, start handing out blocks to each extent.
289 * First to the 8 extents in the fork descriptor.
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
;
300 * Then, if there are any left, to the overflow extents.
302 while (blocksLeft
> 0) {
303 struct ExtentRecord tmp
;
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
);
317 blocksTotal
+= bcount
;
318 overflowExtents
= realloc(overflowExtents
, (numOverflowExtents
+1) * sizeof(*overflowExtents
));
319 overflowExtents
[numOverflowExtents
++] = tmp
;
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.
339 err
= wipefs_alloc(fd
, 0/*sectorSize*/, &handle
);
341 err
= wipefs_wipe(handle
);
343 wipefs_free(&handle
);
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.
356 make_hfsplus(const DriveInfo
*driveInfo
, hfsparams_t
*defaults
)
359 UInt32 sectorsPerBlock
;
361 UInt32 sectorsPerNode
;
364 UInt32 endOfAttributes
;
365 UInt32 startOfAllocation
;
367 void *nodeBuffer
= NULL
;
368 HFSPlusVolumeHeader
*header
= NULL
;
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
376 (void) dowipefs(driveInfo
->fd
);
378 /* --- Create an HFS Plus header: */
380 header
= (HFSPlusVolumeHeader
*)malloc((size_t)kBytesPerSector
);
384 /* VH Initialized in native byte order */
385 InitVH(defaults
, driveInfo
->totalSectors
, header
);
387 sectorsPerBlock
= header
->blockSize
/ kBytesPerSector
;
390 /*--- ZERO OUT BEGINNING OF DISK: */
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.
398 /* Zero out first 1M (to be safe) for volume header */
399 WriteBuffer(driveInfo
, 0, 1024*1024, NULL
);
401 if (NEWFS_HFS_DEBUG
) {
403 * Mark each file extent as used individually, rather than doing it all at once.
404 * Also zero out the entire file.
408 WriteBuffer(driveInfo, \
409 header->f.extents[0].startBlock * sectorsPerBlock, \
410 header->f.totalBlocks * header->blockSize, \
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); \
421 /* Zero out from start of allocation file to end of attribute file;
422 * will include allocation bitmap, journal, extents btree, and
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
);
431 bytesToZero
= (UInt64
) header
->catalogFile
.totalBlocks
* header
->blockSize
;
432 sector
= header
->catalogFile
.extents
[0].startBlock
* sectorsPerBlock
;
433 WriteBuffer(driveInfo
, sector
, bytesToZero
, NULL
);
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.
441 temp
= Largest( defaults
->catalogNodeSize
* 2,
442 (defaults
->attributesNodeSize
* 2),
444 (header
->catalogFile
.extents
[0].startBlock
+ header
->catalogFile
.totalBlocks
+ 7) / 8 );
446 * If size is not a mutiple of 512, round up to nearest sector
448 if ( (temp
& 0x01FF) != 0 )
449 temp
= (temp
+ kBytesPerSector
) & 0xFFFFFE00;
451 nodeBuffer
= valloc((size_t)temp
);
452 if (nodeBuffer
== NULL
)
457 /*--- WRITE ALLOCATION BITMAP BITS TO DISK: */
460 * XXX - this doesn't work well with using arbitrary extents.
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.
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.
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__
);
482 } else if (header
->blockSize
== 1024) {
483 if (MarkExtentUsed(driveInfo
, header
, 0, 2) == -1) {
484 errx(1, "Overlapped extent at <0, 2> (%d)", __LINE__
);
487 if (MarkExtentUsed(driveInfo
, header
, 0, 1) == -1) {
488 errx(1, "Overlapped extent at <0, 1> (%d)", __LINE__
);
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__
);
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
,
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
);
513 * Write alternate Volume Header bitmap bit to allocations file at
514 * 2nd to last sector on HFS+ volume
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);
521 * If the blockSize is 512 bytes, then the last 1kbyte has to be marked
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);
531 /*--- WRITE FILE EXTENTS B-TREE TO DISK: */
533 btNodeSize
= defaults
->extentsNodeSize
;
534 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
536 sector
= header
->extentsFile
.extents
[0].startBlock
* sectorsPerBlock
;
537 WriteExtentsFile(driveInfo
, sector
, defaults
, NULL
, nodeBuffer
, &bytesUsed
, &mapNodes
);
540 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
541 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
546 /*--- WRITE FILE ATTRIBUTES B-TREE TO DISK: */
547 if (defaults
->attributesInitialSize
) {
549 btNodeSize
= defaults
->attributesNodeSize
;
550 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
552 sector
= header
->attributesFile
.extents
[0].startBlock
* sectorsPerBlock
;
553 WriteAttributesFile(driveInfo
, sector
, defaults
, NULL
, nodeBuffer
, &bytesUsed
, &mapNodes
);
555 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
556 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
560 /*--- WRITE CATALOG B-TREE TO DISK: */
562 btNodeSize
= defaults
->catalogNodeSize
;
563 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
565 sector
= header
->catalogFile
.extents
[0].startBlock
* sectorsPerBlock
;
566 WriteCatalogFile(driveInfo
, sector
, defaults
, header
, nodeBuffer
, &bytesUsed
, &mapNodes
);
569 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
570 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
573 /*--- JOURNALING SETUP */
574 if (defaults
->journaledHFS
) {
575 sector
= header
->journalInfoBlock
* sectorsPerBlock
;
576 if (NEWFS_HFS_DEBUG
) {
578 * For debug build, the journal may be located somewhere other
579 * than right after the journalInfoBlock.
581 if (MarkExtentUsed(driveInfo
, header
, header
->journalInfoBlock
, 1) == -1) {
582 errx(1, "Extent overlap for journalInfoBlock <%u, 1>", header
->journalInfoBlock
);
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
);
593 if (WriteJournalInfo(driveInfo
, sector
, defaults
, header
, nodeBuffer
) != 0) {
594 err(EINVAL
, "Failed to create the journal");
598 /*--- WRITE VOLUME HEADER TO DISK: */
600 /* write header last in case we fail along the way */
602 /* Writes both copies of the volume header */
603 WriteVH (driveInfo
, header
);
604 /* VH is now big-endian */
615 * Writes the Volume Header (VH) to disk.
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.
621 WriteVH (const DriveInfo
*driveInfo
, HFSPlusVolumeHeader
*hp
)
625 WriteBuffer(driveInfo
, 2, kBytesPerSector
, hp
);
626 WriteBuffer(driveInfo
, driveInfo
->totalSectors
- 2, kBytesPerSector
, hp
);
633 * Initialize a Volume Header record.
636 InitVH(hfsparams_t
*defaults
, UInt64 sectors
, HFSPlusVolumeHeader
*hp
)
642 UInt16 burnedBlocksBeforeVH
= 0;
643 UInt16 burnedBlocksAfterAltVH
= 0;
645 UInt32 allocateBlock
;
646 VolumeUUID newVolumeUUID
;
647 VolumeUUID
* finderInfoUUIDPtr
;
648 UInt64 hotFileBandSize
;
652 * 2 MB is the minimum size for the new behavior with
653 * space after the attr b-tree, and hotfile stuff.
655 #define MINVOLSIZE_WITHSPACE 2097152
657 bzero(hp
, kBytesPerSector
);
659 blockSize
= defaults
->blockSize
;
660 blockCount
= sectors
/ (blockSize
>> kLog2SectorSize
);
663 * HFSPlusVolumeHeader is located at sector 2, so we may need
664 * to invalidate blocks before HFSPlusVolumeHeader.
666 if ( blockSize
== 512 ) {
667 burnedBlocksBeforeVH
= 2; /* 2 before VH */
668 burnedBlocksAfterAltVH
= 1; /* 1 after altVH */
669 } else if ( blockSize
== 1024 ) {
670 burnedBlocksBeforeVH
= 1;
672 nextBlock
= burnedBlocksBeforeVH
+ 1; /* +1 for VH itself */
673 if (defaults
->fsStartBlock
) {
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 */
679 bitmapBlocks
= defaults
->allocationClumpSize
/ blockSize
;
681 /* note: add 2 for the Alternate VH, and VH */
682 blocksUsed
= 2 + burnedBlocksBeforeVH
+ burnedBlocksAfterAltVH
+ bitmapBlocks
;
684 if (defaults
->flags
& kMakeCaseSensitive
) {
685 hp
->signature
= kHFSXSigWord
;
686 hp
->version
= kHFSXVersion
;
688 hp
->signature
= kHFSPlusSigWord
;
689 hp
->version
= kHFSPlusVersion
;
691 hp
->attributes
= kHFSVolumeUnmountedMask
| kHFSUnusedNodeFixMask
;
692 if (defaults
->flags
& kMakeContentProtect
) {
693 hp
->attributes
|= kHFSContentProtectionMask
;
695 hp
->lastMountedVersion
= kHFSPlusMountVersion
;
697 /* NOTE: create date is in local time, not GMT! */
698 hp
->createDate
= UTCToLocal(defaults
->createDate
);
699 hp
->modifyDate
= defaults
->createDate
;
701 hp
->checkedDate
= defaults
->createDate
;
703 // hp->fileCount = 0;
704 // hp->folderCount = 0;
706 hp
->blockSize
= blockSize
;
707 hp
->totalBlocks
= blockCount
;
708 hp
->freeBlocks
= blockCount
; /* will be adjusted at the end */
710 volsize
= (UInt64
) blockCount
* (UInt64
) blockSize
;
712 hp
->rsrcClumpSize
= defaults
->rsrcClumpSize
;
713 hp
->dataClumpSize
= defaults
->dataClumpSize
;
714 hp
->nextCatalogID
= defaults
->nextFreeFileID
;
715 hp
->encodingsBitmap
= 1;
717 /* set up allocation bitmap file */
718 hp
->allocationFile
.clumpSize
= defaults
->allocationClumpSize
;
719 hp
->allocationFile
.logicalSize
= defaults
->allocationClumpSize
;
720 hp
->allocationFile
.totalBlocks
= bitmapBlocks
;
722 if (NEWFS_HFS_DEBUG
&& defaults
->allocationStartBlock
)
723 allocateBlock
= defaults
->allocationStartBlock
;
725 allocateBlock
= nextBlock
;
726 nextBlock
+= bitmapBlocks
;
729 createExtents(&hp
->allocationFile
, kHFSAllocationFileID
, allocateBlock
, defaults
->allocationExtsCount
, 1);
731 // This works because the files are contiguous for now
733 printf ("allocationFile: (%10u, %10u)\n", hp
->allocationFile
.extents
[0].startBlock
, hp
->allocationFile
.totalBlocks
);
735 /* set up journal files */
736 if (defaults
->journaledHFS
) {
739 hp
->attributes
|= kHFSVolumeJournaledMask
;
740 hp
->nextCatalogID
+= 2;
743 * Allocate 1 block for the journalInfoBlock. The
744 * journal file size is passed in hfsparams_t.
746 if (NEWFS_HFS_DEBUG
&& defaults
->journalInfoBlock
)
747 hp
->journalInfoBlock
= defaults
->journalInfoBlock
;
749 hp
->journalInfoBlock
= nextBlock
++;
750 if (NEWFS_HFS_DEBUG
&& defaults
->journalBlock
)
751 journalBlock
= defaults
->journalBlock
;
753 journalBlock
= hp
->journalInfoBlock
+ 1;
754 nextBlock
+= ((defaults
->journalSize
+blockSize
-1) / blockSize
);
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
));
761 /* XXX What if journal is on a different device? */
762 blocksUsed
+= 1 + ((defaults
->journalSize
+blockSize
-1) / blockSize
);
764 hp
->journalInfoBlock
= 0;
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
;
774 allocateBlock
= nextBlock
;
775 nextBlock
+= hp
->extentsFile
.totalBlocks
;
777 createExtents(&hp
->extentsFile
, kHFSExtentsFileID
, allocateBlock
, defaults
->extentsExtsCount
, (defaults
->journaledHFS
&& defaults
->extentsNodeSize
> hp
->blockSize
) ? defaults
->extentsNodeSize
/ hp
->blockSize
: 1);
779 blocksUsed
+= hp
->extentsFile
.totalBlocks
;
782 printf ("extentsFile : (%10u, %10u)\n", hp
->extentsFile
.extents
[0].startBlock
, hp
->extentsFile
.totalBlocks
);
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
;
792 allocateBlock
= nextBlock
;
793 nextBlock
+= hp
->attributesFile
.totalBlocks
;
795 createExtents(&hp
->attributesFile
, kHFSAttributesFileID
, allocateBlock
, defaults
->attributesExtsCount
, (defaults
->journaledHFS
&& defaults
->attributesNodeSize
> hp
->blockSize
) ? defaults
->attributesNodeSize
/ hp
->blockSize
: 1);
796 blocksUsed
+= hp
->attributesFile
.totalBlocks
;
798 if (NEWFS_HFS_DEBUG
) {
799 printf ("attributesFile: (%10u, %10u)\n", hp
->attributesFile
.extents
[0].startBlock
, hp
->attributesFile
.totalBlocks
);
802 * Leave some room for the Attributes B-tree to grow, if the volsize >= 2MB
804 if (volsize
>= MINVOLSIZE_WITHSPACE
&& defaults
->attributesStartBlock
== 0) {
805 nextBlock
+= 10 * (hp
->attributesFile
.clumpSize
/ blockSize
);
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
;
816 allocateBlock
= nextBlock
;
817 nextBlock
+= hp
->catalogFile
.totalBlocks
;
819 createExtents(&hp
->catalogFile
, kHFSCatalogFileID
, allocateBlock
, defaults
->catalogExtsCount
, (defaults
->journaledHFS
&& defaults
->catalogNodeSize
> hp
->blockSize
) ? defaults
->catalogNodeSize
/ hp
->blockSize
: 1);
820 blocksUsed
+= hp
->catalogFile
.totalBlocks
;
823 printf ("catalogFile : (%10u, %10u)\n\n", hp
->catalogFile
.extents
[0].startBlock
, hp
->catalogFile
.totalBlocks
);
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");
831 * Add some room for the catalog file to grow...
833 nextBlock
+= 10 * (hp
->catalogFile
.clumpSize
/ hp
->blockSize
);
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.
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
;
851 if (NEWFS_HFS_DEBUG
&& defaults
->nextAllocBlock
)
852 hp
->nextAllocation
= defaults
->nextAllocBlock
;
854 hp
->nextAllocation
= nextBlock
;
856 /* Adjust free blocks to reflect everything we have allocated. */
857 hp
->freeBlocks
-= blocksUsed
;
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
);
869 * Mark the given extent as in-use in the given bitmap buffer.
871 static int AllocateExtent(UInt8
*buffer
, UInt32 startBlock
, UInt32 blockCount
)
875 /* Point to start of extent in bitmap buffer */
876 p
= buffer
+ (startBlock
/ 8);
879 * Important to remember: block 0 is (1 << 7);
880 * block 7 is (1 << 0).
882 /* Partial byte at start of extent */
886 unsigned int lShift
= 0;
887 unsigned int startBit
= startBlock
& 7;
890 * Is startBlock + blockCount entirely in
893 if (blockCount
< (8 - startBit
)) {
894 lShift
= 8 - (startBit
+ blockCount
);
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
);
903 * We have either set <lShift> or <startBlock & 7> bits.
905 blockCount
-= 8 - (lShift
+ startBit
);
906 // blockCount -= lShift ? blockCount : (8 - startBit);
907 // blockCount -= __builtin_popcount(mask);
910 /* Fill in whole bytes */
913 if (NEWFS_HFS_DEBUG
) {
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.
920 for (indx
= 0; indx
< blockCount
/ 8; indx
++) {
922 fprintf(stderr
, "%s(%d): Expected 0 at %zu, got 0x%x\n", __FUNCTION__
, __LINE__
, indx
, p
[indx
]);
928 memset(p
, 0xFF, blockCount
/ 8);
934 /* Partial byte at end of extent */
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
);
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
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.
959 MarkExtentUsed(const DriveInfo
*driveInfo
,
960 HFSPlusVolumeHeader
*header
,
964 size_t bufSize
= driveInfo
->physSectorSize
;
966 uint32_t blocksLeft
= blockCount
;
967 uint32_t curBlock
= startBlock
;
968 static const int kBitsPerByte
= 8;
971 buf
= valloc(bufSize
);
976 * We loop through physSectorSize blocks.
977 * This allows us to set as many bits as we need.
979 while (blocksLeft
> 0) {
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
984 memset(buf
, 0, sizeof(buf
));
985 secNum
= curBlock
/ (bufSize
* kBitsPerByte
);
986 blockOffset
= curBlock
% (bufSize
* kBitsPerByte
);
987 numBlocks
= MIN((bufSize
* kBitsPerByte
) - blockOffset
, blocksLeft
);
990 * Okay, now we've got the block number to read,
991 * the offset into the block, and the number of blocks
994 * First we read in the buffer. To do that, we need to
995 * know where to read.
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.
1009 offset
= (header
->allocationFile
.extents
[0].startBlock
* header
->blockSize
) +
1012 nbytes
= pread(driveInfo
->fd
, buf
, bufSize
, offset
);
1014 if (nbytes
< (ssize_t
)bufSize
) {
1016 err(1, "%s::pread(%d, %p, %zu, %lld)", __FUNCTION__
, driveInfo
->fd
, buf
, bufSize
, offset
);
1020 if (AllocateExtent(buf
, blockOffset
, numBlocks
) == -1) {
1021 warnx("In-use allocation block in <%u, %u>", blockOffset
, numBlocks
);
1024 nwritten
= pwrite(driveInfo
->fd
, buf
, bufSize
, offset
);
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.
1030 if (nwritten
!= (ssize_t
)bufSize
)
1033 // And go get the next set, if needed
1034 blocksLeft
-= numBlocks
;
1035 curBlock
+= numBlocks
;
1048 * Initializes and writes out the extents b-tree file.
1050 * Byte swapping is performed in place. The buffer should not be
1051 * accessed through direct casting once it leaves this function.
1054 WriteExtentsFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1055 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp __unused
, void *buffer
,
1056 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1058 BTNodeDescriptor
*ndp
;
1061 UInt32 nodeBitsInHeader
;
1068 fileSize
= dp
->extentsInitialSize
;
1069 nodeSize
= dp
->extentsNodeSize
;
1071 bzero(buffer
, nodeSize
);
1074 /* FILL IN THE NODE DESCRIPTOR: */
1075 ndp
= (BTNodeDescriptor
*)buffer
;
1076 ndp
->kind
= kBTHeaderNode
;
1077 ndp
->numRecords
= SWAP_BE16 (3);
1078 offset
= sizeof(BTNodeDescriptor
);
1080 SETOFFSET(buffer
, nodeSize
, offset
, 1);
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
);
1092 bthp
->treeDepth
= 0;
1094 bthp
->firstLeafNode
= 0;
1095 bthp
->lastLeafNode
= 0;
1096 bthp
->leafRecords
= 0;
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
);
1104 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1105 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusExtentKeyMaximumLength
);
1106 offset
+= sizeof(BTHeaderRec
);
1108 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1110 offset
+= kBTreeHeaderUserBytes
;
1112 SETOFFSET(buffer
, nodeSize
, offset
, 3);
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
)) );
1122 if (SWAP_BE32 (bthp
->totalNodes
) > nodeBitsInHeader
) {
1123 UInt32 nodeBitsInMapNode
;
1125 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1126 nodeBitsInMapNode
= 8 * (nodeSize
1127 - sizeof(BTNodeDescriptor
)
1128 - (2 * sizeof(SInt16
))
1130 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1131 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1132 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
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.
1140 bmp
= ((UInt8
*)buffer
+ offset
);
1141 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
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;
1148 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1150 if (NEWFS_HFS_DEBUG
&& numOverflowExtents
) {
1151 void *node2
= (uint8_t*)buffer
+ nodeSize
;
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;
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;
1166 if (numOverflowExtents
> 1) {
1167 qsort_b(overflowExtents
, numOverflowExtents
, sizeof(*overflowExtents
), keyCompare
);
1169 bzero(node2
, nodeSize
);
1170 ndp
= (BTNodeDescriptor
*)node2
;
1171 ndp
->kind
= kBTLeafNode
;
1172 ndp
->numRecords
= SWAP_BE16(numOverflowExtents
);
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
);
1181 SETOFFSET(node2
, nodeSize
, offset
, numOverflowExtents
+ 1);
1184 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1186 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1191 * WriteAttributesFile
1193 * Initializes and writes out the attributes b-tree file.
1195 * Byte swapping is performed in place. The buffer should not be
1196 * accessed through direct casting once it leaves this function.
1199 WriteAttributesFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1200 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp __unused
, void *buffer
,
1201 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1203 BTNodeDescriptor
*ndp
;
1206 UInt32 nodeBitsInHeader
;
1211 int set_cp_level
= 0;
1214 fileSize
= dp
->attributesInitialSize
;
1215 nodeSize
= dp
->attributesNodeSize
;
1219 * If user specified content protection and a protection level,
1220 * then verify the protection level is sane.
1222 if ((dp
->flags
& kMakeContentProtect
) && (dp
->protectlevel
!= 0)) {
1223 if ((dp
->protectlevel
>= 2 ) && (dp
->protectlevel
<= 4)) {
1230 bzero(buffer
, nodeSize
);
1233 /* FILL IN THE NODE DESCRIPTOR: */
1234 ndp
= (BTNodeDescriptor
*)buffer
;
1235 ndp
->kind
= kBTHeaderNode
;
1236 ndp
->numRecords
= SWAP_BE16 (3);
1237 offset
= sizeof(BTNodeDescriptor
);
1239 SETOFFSET(buffer
, nodeSize
, offset
, 1);
1242 /* FILL IN THE HEADER RECORD: */
1243 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
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);
1252 bthp
->treeDepth
= 0;
1254 bthp
->firstLeafNode
= 0;
1255 bthp
->lastLeafNode
= 0;
1256 bthp
->leafRecords
= 0;
1259 bthp
->nodeSize
= SWAP_BE16 (nodeSize
);
1260 bthp
->totalNodes
= SWAP_BE32 (fileSize
/ nodeSize
);
1262 /* Add 1 node for the first record */
1263 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - 2);
1266 /* Take the header into account */
1267 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - 1);
1269 bthp
->clumpSize
= SWAP_BE32 (dp
->attributesClumpSize
);
1271 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
| kBTVariableIndexKeysMask
);
1272 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusAttrKeyMaximumLength
);
1274 offset
+= sizeof(BTHeaderRec
);
1276 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1278 offset
+= kBTreeHeaderUserBytes
;
1280 SETOFFSET(buffer
, nodeSize
, offset
, 3);
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
;
1292 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1293 nodeBitsInMapNode
= 8 * (nodeSize
1294 - sizeof(BTNodeDescriptor
)
1295 - (2 * sizeof(SInt16
))
1297 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1298 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1299 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
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.
1307 bmp
= ((UInt8
*)buffer
+ offset
);
1308 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
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;
1315 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1319 /* Stuff in the EA on the root folder */
1320 void *node2
= (uint8_t*)buffer
+ nodeSize
;
1322 struct cp_root_xattr ea
;
1324 uint8_t canonicalName
[256];
1327 HFSPlusAttrData
*attrData
;
1328 HFSPlusAttrKey
*attrKey
;
1329 bzero(node2
, nodeSize
);
1330 ndp
= (BTNodeDescriptor
*)node2
;
1332 ndp
->kind
= kBTLeafNode
;
1333 ndp
->numRecords
= SWAP_BE16(1);
1336 offset
= sizeof(BTNodeDescriptor
);
1337 SETOFFSET(node2
, nodeSize
, offset
, 1);
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
));
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
);
1352 /* If the offset is odd, move up to the next even value */
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
);
1365 SETOFFSET (node2
, nodeSize
, offset
, 2);
1370 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1371 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1374 #if !TARGET_OS_EMBEDDED
1376 get_dev_uuid(const char *disk_name
, char *dev_uuid_str
, int dev_uuid_len
)
1378 io_service_t service
;
1379 CFStringRef uuid_str
;
1382 if (strncmp(disk_name
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0) {
1383 disk_name
+= strlen(_PATH_DEV
);
1386 dev_uuid_str
[0] = '\0';
1388 service
= IOServiceGetMatchingService(kIOMasterPortDefault
, IOBSDNameMatching(kIOMasterPortDefault
, 0, disk_name
));
1389 if (service
!= IO_OBJECT_NULL
) {
1390 uuid_str
= IORegistryEntryCreateCFProperty(service
, CFSTR(kIOMediaUUIDKey
), kCFAllocatorDefault
, 0);
1392 if (CFStringGetFileSystemRepresentation(uuid_str
, dev_uuid_str
, dev_uuid_len
) != 0) {
1395 CFRelease(uuid_str
);
1397 IOObjectRelease(service
);
1404 clear_journal_dev(const char *dev_name
)
1408 fd
= open(dev_name
, O_RDWR
);
1410 printf("Failed to open the journal device %s (%s)\n", dev_name
, strerror(errno
));
1419 #endif /* !TARGET_OS_EMBEDDED */
1423 WriteJournalInfo(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1424 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
,
1427 JournalInfoBlock
*jibp
= buffer
;
1428 UInt32 journalBlock
;
1430 memset(buffer
, 0xdb, driveInfo
->physSectorSize
);
1431 memset(jibp
, 0, sizeof(JournalInfoBlock
));
1433 #if !TARGET_OS_EMBEDDED
1434 if (dp
->journalDevice
) {
1437 if (get_dev_uuid(dp
->journalDevice
, uuid_str
, sizeof(uuid_str
)) == 0) {
1438 strlcpy((char *)&jibp
->reserved
[0], uuid_str
, sizeof(jibp
->reserved
));
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) {
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
));
1454 jibp
->flags
= kJIJournalInFSMask
;
1455 #if !TARGET_OS_EMBEDDED
1458 jibp
->flags
|= kJIJournalNeedInitMask
;
1459 if (NEWFS_HFS_DEBUG
&& dp
->journalBlock
)
1460 journalBlock
= dp
->journalBlock
;
1462 journalBlock
= header
->journalInfoBlock
+ 1;
1463 jibp
->offset
= ((UInt64
) journalBlock
) * header
->blockSize
;
1464 jibp
->size
= dp
->journalSize
;
1466 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1467 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1468 jibp
->size
= SWAP_BE64(jibp
->size
);
1470 WriteBuffer(driveInfo
, startingSector
, driveInfo
->physSectorSize
, buffer
);
1472 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1473 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1474 jibp
->size
= SWAP_BE64(jibp
->size
);
1476 if (jibp
->flags
& kJIJournalInFSMask
) {
1478 * Zero out the on-disk content of the journal file.
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.
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.
1491 WriteBuffer(driveInfo
, jibp
->offset
/ kBytesPerSector
, jibp
->size
, NULL
);
1501 * This routine initializes a Catalog B-Tree.
1503 * Note: Since large volumes can have bigger b-trees they
1504 * might need to have map nodes setup.
1507 WriteCatalogFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1508 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
, void *buffer
,
1509 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1511 BTNodeDescriptor
*ndp
;
1514 UInt32 nodeBitsInHeader
;
1521 fileSize
= dp
->catalogInitialSize
;
1522 nodeSize
= dp
->catalogNodeSize
;
1524 bzero(buffer
, nodeSize
);
1527 /* FILL IN THE NODE DESCRIPTOR: */
1528 ndp
= (BTNodeDescriptor
*)buffer
;
1529 ndp
->kind
= kBTHeaderNode
;
1530 ndp
->numRecords
= SWAP_BE16 (3);
1531 offset
= sizeof(BTNodeDescriptor
);
1533 SETOFFSET(buffer
, nodeSize
, offset
, 1);
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
);
1549 bthp
->attributes
|= SWAP_BE32 (kBTVariableIndexKeysMask
+ kBTBigKeysMask
);
1550 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMaximumLength
);
1551 if (dp
->flags
& kMakeCaseSensitive
)
1552 bthp
->keyCompareType
= kHFSBinaryCompare
;
1554 bthp
->keyCompareType
= kHFSCaseFolding
;
1556 offset
+= sizeof(BTHeaderRec
);
1558 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1560 offset
+= kBTreeHeaderUserBytes
;
1562 SETOFFSET(buffer
, nodeSize
, offset
, 3);
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
)) );
1571 if (SWAP_BE32 (bthp
->totalNodes
) > nodeBitsInHeader
) {
1572 UInt32 nodeBitsInMapNode
;
1574 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1575 nodeBitsInMapNode
= 8 * (nodeSize
1576 - sizeof(BTNodeDescriptor
)
1577 - (2 * sizeof(SInt16
))
1579 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1580 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1581 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
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.
1588 bmp
= ((UInt8
*)buffer
+ offset
);
1589 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
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;
1596 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1598 InitCatalogRoot_HFSPlus(dp
, header
, buffer
+ nodeSize
);
1600 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1602 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1607 InitCatalogRoot_HFSPlus(const hfsparams_t
*dp
, const HFSPlusVolumeHeader
*header
, void * buffer
)
1609 BTNodeDescriptor
*ndp
;
1610 HFSPlusCatalogKey
*ckp
;
1611 HFSPlusCatalogKey
*tkp
;
1612 HFSPlusCatalogFolder
*cdp
;
1613 HFSPlusCatalogFile
*cfp
;
1614 HFSPlusCatalogThread
*ctp
;
1617 size_t unicodeBytes
;
1618 UInt8 canonicalName
[kHFSPlusMaxFileNameBytes
]; // UTF8 character may convert to three bytes, plus a NUL
1623 nodeSize
= dp
->catalogNodeSize
;
1624 bzero(buffer
, nodeSize
);
1627 * All nodes have a node descriptor...
1629 ndp
= (BTNodeDescriptor
*)buffer
;
1630 ndp
->kind
= kBTLeafNode
;
1632 ndp
->numRecords
= SWAP_BE16 (dp
->journaledHFS
? 6 : 2);
1633 offset
= sizeof(BTNodeDescriptor
);
1634 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1637 * First record is always the root directory...
1639 ckp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
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
));
1645 if (!cfOK
|| ConvertUTF8toUnicode(canonicalName
, sizeof(ckp
->nodeName
.unicode
),
1646 ckp
->nodeName
.unicode
, &ckp
->nodeName
.length
)) {
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
);
1657 ckp
->nodeName
.length
= SWAP_BE16 (ckp
->nodeName
.length
);
1659 unicodeBytes
= sizeof(UniChar
) * SWAP_BE16 (ckp
->nodeName
.length
);
1661 ckp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
);
1662 ckp
->parentID
= SWAP_BE32 (kHFSRootParentID
);
1663 offset
+= SWAP_BE16 (ckp
->keyLength
) + 2;
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
);
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
);
1681 offset
+= sizeof(HFSPlusCatalogFolder
);
1682 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1685 * Second record is always the root directory thread...
1687 tkp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1688 tkp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
);
1689 tkp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1690 // tkp->nodeName.length = 0;
1692 offset
+= SWAP_BE16 (tkp
->keyLength
) + 2;
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
) );
1701 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1704 * Add records for ".journal" and ".journal_info_block" files:
1706 if (dp
->journaledHFS
) {
1707 struct HFSUniStr255
*nodename1
, *nodename2
;
1708 size_t uBytes1
, uBytes2
;
1709 UInt32 journalBlock
;
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;
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;
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
);
1738 if (NEWFS_HFS_DEBUG
&& dp
->journalBlock
)
1739 journalBlock
= dp
->journalBlock
;
1741 journalBlock
= header
->journalInfoBlock
+ 1;
1742 cfp
->dataFork
.extents
[0].startBlock
= SWAP_BE32 (journalBlock
);
1743 cfp
->dataFork
.extents
[0].blockCount
= cfp
->dataFork
.totalBlocks
;
1745 offset
+= sizeof(HFSPlusCatalogFile
);
1746 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1747 nodename1
= &ckp
->nodeName
;
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;
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;
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);
1776 cfp
->dataFork
.extents
[0].startBlock
= SWAP_BE32 (header
->journalInfoBlock
);
1777 cfp
->dataFork
.extents
[0].blockCount
= cfp
->dataFork
.totalBlocks
;
1779 offset
+= sizeof(HFSPlusCatalogFile
);
1780 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1781 nodename2
= &ckp
->nodeName
;
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;
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
);
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;
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
);
1818 * Initializes a B-tree map node and writes it out to disk.
1821 WriteMapNodes(const DriveInfo
*driveInfo
, UInt64 diskStart
, UInt32 firstMapNode
,
1822 UInt32 mapNodes
, UInt16 btNodeSize
, void *buffer
)
1824 UInt32 sectorsPerNode
;
1825 UInt32 mapRecordBytes
;
1827 BTNodeDescriptor
*nd
= (BTNodeDescriptor
*)buffer
;
1829 bzero(buffer
, btNodeSize
);
1831 nd
->kind
= kBTMapNode
;
1832 nd
->numRecords
= SWAP_BE16 (1);
1834 /* note: must belong word aligned (hence the extra -2) */
1835 mapRecordBytes
= btNodeSize
- sizeof(BTNodeDescriptor
) - 2*sizeof(SInt16
) - 2;
1837 SETOFFSET(buffer
, btNodeSize
, sizeof(BTNodeDescriptor
), 1);
1838 SETOFFSET(buffer
, btNodeSize
, sizeof(BTNodeDescriptor
) + mapRecordBytes
, 2);
1840 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
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!
1847 for (i
= 0; i
< mapNodes
; i
++) {
1848 if ((i
+ 1) < mapNodes
)
1849 nd
->fLink
= SWAP_BE32 (++firstMapNode
); /* point to next map node */
1851 nd
->fLink
= 0; /* this is the last map node */
1853 WriteBuffer(driveInfo
, diskStart
, btNodeSize
, buffer
);
1855 diskStart
+= sectorsPerNode
;
1860 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1861 * NOTE: IF buffer IS NULL, THIS FUNCTION WILL WRITE ZERO'S.
1863 * startingSector is in terms of 512-byte sectors.
1864 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1867 WriteBuffer(const DriveInfo
*driveInfo
, UInt64 startingSector
, UInt64 byteCount
,
1871 off_t physSector
= 0;
1872 off_t byteOffsetInPhysSector
;
1873 UInt32 numBytesToIO
;
1874 UInt32 numPhysSectorsToIO
;
1875 UInt32 tempbufSizeInPhysSectors
;
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 */
1883 if (0 == byteCount
) {
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
);
1894 tempbufSizeInPhysSectors
= MIN ( tempbufSizeInPhysSectors
, (4 * 1024 * 1024) / physSectorSize
);
1895 tempbufSize
= tempbufSizeInPhysSectors
* physSectorSize
;
1897 if ((tempbuf
= valloc(tempbufSize
)) == NULL
) {
1898 /* try allocation of smallest allowed size: one
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.
1904 tempbufSizeInPhysSectors
= 1;
1905 tempbufSize
= physSectorSize
;
1906 if ((tempbuf
= valloc(tempbufSize
)) == NULL
) {
1912 /*@@@@@@@@@@ io @@@@@@@@@@*/
1913 sector
= driveInfo
->sectorOffset
+ startingSector
;
1914 physSector
= sector
/ sectorSizeRatio
;
1915 byteOffsetInPhysSector
= (sector
% sectorSizeRatio
) * kBytesPerSector
;
1917 while (byteCount
> 0) {
1918 numPhysSectorsToIO
= MIN ( (byteCount
- 1 + physSectorSize
) / physSectorSize
,
1919 tempbufSizeInPhysSectors
);
1920 numBytesToIO
= MIN(byteCount
, (unsigned)((numPhysSectorsToIO
* physSectorSize
) - byteOffsetInPhysSector
));
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) {
1930 if (NULL
!= buffer
) {
1931 memcpy(tempbuf
+ byteOffsetInPhysSector
, buffer
, numBytesToIO
);
1934 bzero(tempbuf
+ byteOffsetInPhysSector
, numBytesToIO
);
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
));
1943 byteOffsetInPhysSector
= 0;
1944 byteCount
-= numBytesToIO
;
1945 physSector
+= numPhysSectorsToIO
;
1946 if (NULL
!= buffer
) {
1947 buffer
+= numBytesToIO
;
1960 else if (2 == status
) {
1961 err(1, "read (sector %llu)", physSector
);
1963 else if (3 == status
) {
1964 err(1, "write (sector %llu)", physSector
);
1971 static UInt32
Largest( UInt32 a
, UInt32 b
, UInt32 c
, UInt32 d
)
1980 /* return max(a,c) */
1988 * UTCToLocal - convert from Mac OS GMT time to Mac OS local time
1990 static UInt32
UTCToLocal(UInt32 utcTime
)
1992 UInt32 localTime
= utcTime
;
1993 struct timezone timeZone
;
1994 struct timeval timeVal
;
1996 if (localTime
!= 0) {
1998 /* HFS volumes need timezone info to convert local to GMT */
1999 (void)gettimeofday( &timeVal
, &timeZone
);
2002 localTime
-= (timeZone
.tz_minuteswest
* 60);
2003 if (timeZone
.tz_dsttime
)
2011 ConvertUTF8toUnicode(const UInt8
* source
, size_t bufsize
, UniChar
* unibuf
,
2020 targetEnd
= (UniChar
*)((UInt8
*)unibuf
+ bufsize
);
2022 while ((byte
= *source
++)) {
2024 /* check for single-byte ascii */
2026 if (byte
== ':') /* ':' is mapped to '/' */
2029 *target
++ = SWAP_BE16 (byte
);
2032 UInt8 seq
= (byte
>> 4);
2035 case 0xc: /* double-byte sequence (1100 and 1101) */
2037 ch
= (byte
& 0x1F) << 6; /* get 5 bits */
2038 if (((byte
= *source
++) >> 6) != 2)
2042 case 0xe: /* triple-byte sequence (1110) */
2043 ch
= (byte
& 0x0F) << 6; /* get 4 bits */
2044 if (((byte
= *source
++) >> 6) != 2)
2046 ch
+= (byte
& 0x3F); ch
<<= 6; /* get 6 bits */
2047 if (((byte
= *source
++) >> 6) != 2)
2052 return (EINVAL
); /* malformed sequence */
2055 ch
+= (byte
& 0x3F); /* get last 6 bits */
2057 if (target
>= targetEnd
)
2060 *target
++ = SWAP_BE16 (ch
);
2064 *charcount
= target
- unibuf
;
2069 /* Generate Volume UUID - similar to code existing in hfs_util */
2070 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2072 char randomInputBuffer
[26];
2073 unsigned char digest
[20];
2078 char sysctlstring
[128];
2080 double sysloadavg
[3];
2081 struct vmtotal sysvmtotal
;
2084 /* Initialize the SHA-1 context for processing: */
2085 SHA1_Init(&context
);
2087 /* Now process successive bits of "random" input to seed the process: */
2089 /* The current system's uptime: */
2091 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2093 /* The kernel's boot time: */
2095 mib
[1] = KERN_BOOTTIME
;
2096 datalen
= sizeof(sysdata
);
2097 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2098 SHA1_Update(&context
, &sysdata
, datalen
);
2100 /* The system's host id: */
2102 mib
[1] = KERN_HOSTID
;
2103 datalen
= sizeof(sysdata
);
2104 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2105 SHA1_Update(&context
, &sysdata
, datalen
);
2107 /* The system's host name: */
2109 mib
[1] = KERN_HOSTNAME
;
2110 datalen
= sizeof(sysctlstring
);
2111 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2112 SHA1_Update(&context
, sysctlstring
, datalen
);
2114 /* The running kernel's OS release string: */
2116 mib
[1] = KERN_OSRELEASE
;
2117 datalen
= sizeof(sysctlstring
);
2118 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2119 SHA1_Update(&context
, sysctlstring
, datalen
);
2121 /* The running kernel's version string: */
2123 mib
[1] = KERN_VERSION
;
2124 datalen
= sizeof(sysctlstring
);
2125 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2126 SHA1_Update(&context
, sysctlstring
, datalen
);
2128 /* The system's load average: */
2129 datalen
= sizeof(sysloadavg
);
2130 getloadavg(sysloadavg
, 3);
2131 SHA1_Update(&context
, &sysloadavg
, datalen
);
2133 /* The system's VM statistics: */
2136 datalen
= sizeof(sysvmtotal
);
2137 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2138 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2140 /* The current GMT (26 ASCII characters): */
2142 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2143 SHA1_Update(&context
, randomInputBuffer
, 26);
2145 /* Pad the accumulated input and extract the final digest hash: */
2146 SHA1_Final(digest
, &context
);
2148 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2149 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));