2 * Copyright (c) 1999-2011 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>
53 * CommonCrypto is meant to be a more stable API than OpenSSL.
54 * Defining COMMON_DIGEST_FOR_OPENSSL gives API-compatibility
55 * with OpenSSL, so we don't have to change the code.
57 #define COMMON_DIGEST_FOR_OPENSSL
58 #include <CommonCrypto/CommonDigest.h>
60 #include <libkern/OSByteOrder.h>
62 #include <CoreFoundation/CFString.h>
63 #include <CoreFoundation/CFStringEncodingExt.h>
64 #include <IOKit/IOKitLib.h>
65 #include <IOKit/storage/IOMedia.h>
67 #include <TargetConditionals.h>
69 extern Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
72 #include <hfs/hfs_format.h>
73 #include <hfs/hfs_mount.h>
74 #include "hfs_endian.h"
76 #include "newfs_hfs.h"
78 #ifndef NEWFS_HFS_DEBUG
80 # define NEWFS_HFS_DEBUG 1
82 # define NEWFS_HFS_DEBUG 0
86 #define HFS_BOOT_DATA "/usr/share/misc/hfsbootdata"
88 #define HFS_JOURNAL_FILE ".journal"
89 #define HFS_JOURNAL_INFO ".journal_info_block"
91 #define kJournalFileType 0x6a726e6c /* 'jrnl' */
94 typedef HFSMasterDirectoryBlock HFS_MDB
;
103 struct ExtentRecord
{
104 HFSPlusExtentKey key
;
105 HFSPlusExtentRecord record
;
106 } __attribute__((aligned(2), packed
));
107 static size_t numOverflowExtents
= 0;
108 static struct ExtentRecord
*overflowExtents
= NULL
;
110 struct filefork gDTDBFork
, gSystemFork
, gReadMeFork
;
112 static void WriteVH
__P((const DriveInfo
*driveInfo
, HFSPlusVolumeHeader
*hp
));
113 static void InitVH
__P((hfsparams_t
*defaults
, UInt64 sectors
,
114 HFSPlusVolumeHeader
*header
));
116 static int AllocateExtent(UInt8
*buffer
, UInt32 startBlock
, UInt32 blockCount
);
117 static int MarkExtentUsed(const DriveInfo
*, HFSPlusVolumeHeader
*, UInt32
, UInt32
);
119 static void WriteExtentsFile
__P((const DriveInfo
*dip
, UInt64 startingSector
,
120 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp
, void *buffer
,
121 UInt32
*bytesUsed
, UInt32
*mapNodes
));
123 static void WriteAttributesFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
124 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp
, void *buffer
,
125 UInt32
*bytesUsed
, UInt32
*mapNodes
);
127 static void WriteCatalogFile
__P((const DriveInfo
*dip
, UInt64 startingSector
,
128 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
, void *buffer
,
129 UInt32
*bytesUsed
, UInt32
*mapNodes
));
130 static int WriteJournalInfo(const DriveInfo
*driveInfo
, UInt64 startingSector
,
131 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
,
133 static void InitCatalogRoot_HFSPlus
__P((const hfsparams_t
*dp
, const HFSPlusVolumeHeader
*header
, void * buffer
));
135 static void WriteMapNodes
__P((const DriveInfo
*driveInfo
, UInt64 diskStart
,
136 UInt32 firstMapNode
, UInt32 mapNodes
, UInt16 btNodeSize
, void *buffer
));
137 static void WriteBuffer
__P((const DriveInfo
*driveInfo
, UInt64 startingSector
,
138 UInt64 byteCount
, const void *buffer
));
139 static UInt32 Largest
__P((UInt32 a
, UInt32 b
, UInt32 c
, UInt32 d
));
141 static UInt32
GetDefaultEncoding();
143 static UInt32 UTCToLocal
__P((UInt32 utcTime
));
145 static int ConvertUTF8toUnicode
__P((const UInt8
* source
, size_t bufsize
,
146 UniChar
* unibuf
, UInt16
*charcount
));
148 static int getencodinghint(unsigned char *name
);
150 #define VOLUMEUUIDVALUESIZE 2
151 typedef union VolumeUUID
{
152 UInt32 value
[VOLUMEUUIDVALUESIZE
];
158 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
160 void SETOFFSET (void *buffer
, UInt16 btNodeSize
, SInt16 recOffset
, SInt16 vecOffset
);
161 #define SETOFFSET(buf,ndsiz,offset,rec) \
162 (*(SInt16 *)((UInt8 *)(buf) + (ndsiz) + (-2 * (rec))) = (SWAP_BE16 (offset)))
164 #define BYTESTOBLKS(bytes,blks) DivideAndRoundUp((bytes),(blks))
166 #define ROUNDUP(x, u) (((x) % (u) == 0) ? (x) : ((x)/(u) + 1) * (u))
168 #if TARGET_OS_EMBEDDED
169 #define ENCODING_TO_BIT(e) \
172 #define ENCODING_TO_BIT(e) \
174 ((e) == kCFStringEncodingMacUkrainian ? 48 : \
175 ((e) == kCFStringEncodingMacFarsi ? 49 : 0)))
180 struct cp_root_xattr
{
184 u_int8_t reserved3
[16];
185 } __attribute__((aligned(2), packed
));
189 * Create a series of (sequential!) extents for the
190 * requested file. It tries to create the requested
191 * number, but may be stymied by the file size, and
192 * the number of minimum blocks.
195 createExtents(HFSPlusForkData
*file
,
201 if (NEWFS_HFS_DEBUG
== 0) {
203 * The common case, for non-debug.
205 file
->extents
[0].startBlock
= startBlock
;
206 file
->extents
[0].blockCount
= file
->totalBlocks
;
208 UInt32 blocksLeft
, blocksTotal
= 0, blockStep
;
212 if (numExtents
== 1) {
213 // The common case, no need to do any math
214 file
->extents
[0].startBlock
= startBlock
;
215 file
->extents
[0].blockCount
= file
->totalBlocks
;
218 if (file
->totalBlocks
< numExtents
)
219 numExtents
= file
->totalBlocks
;
221 blocksLeft
= file
->totalBlocks
;
224 * The intent here is to split the number of blocks into the
225 * requested number of extents. So first we determine how
226 * many blocks should go in each extent -- that's blockStep.
227 * If we have been giving minBlocks, we need to make sure it's
228 * a multiple of that. (In general, the values are going to be
229 * 1 or 2 for minBlocks.)
231 * If there are more requested extents than blocks, the division
232 * works out to zero... so we limit blockStep to minBlocks.
235 blockStep
= blocksLeft
/ numExtents
;
238 * To allow invalid extent lengths, set minBlocks to 1, and
239 * comment out the next two if statements.
241 if ((blockStep
% minBlocks
) != 0)
242 blockStep
= (blockStep
/ minBlocks
) * minBlocks
;
244 blockStep
= minBlocks
;
247 * Now, after that, we may still not have the right number, since
248 * the math may not work out properly. So we can work around that
249 * by making the first extent have all the spares.
251 if ((blockStep
* numExtents
) < blocksLeft
) {
252 // Need to adjust the first one.
253 firstAdjust
= blocksLeft
- (blockStep
* numExtents
);
254 if ((firstAdjust
% minBlocks
) != 0)
255 firstAdjust
= ROUNDUP(firstAdjust
, minBlocks
);
259 * Now, at this point, start handing out blocks to each extent.
260 * First to the 8 extents in the fork descriptor.
262 for (i
= 0; i
< 8 && blocksLeft
> 0; i
++) {
263 int n
= MIN(blockStep
+ firstAdjust
, blocksLeft
);
264 file
->extents
[i
].startBlock
= startBlock
+ blocksTotal
;
265 file
->extents
[i
].blockCount
= n
;
271 * Then, if there are any left, to the overflow extents.
273 while (blocksLeft
> 0) {
274 struct ExtentRecord tmp
;
276 memset(&tmp
, 0, sizeof(tmp
));
277 tmp
.key
.keyLength
= SWAP_BE16(sizeof(HFSPlusExtentKey
) - sizeof(uint16_t));
278 tmp
.key
.forkType
= 0;
279 tmp
.key
.fileID
= SWAP_BE32(fileID
);
280 tmp
.key
.startBlock
= SWAP_BE32(blocksTotal
);
281 for (i
= 0; i
< 8 && blocksLeft
> 0; i
++) {
282 int n
= MIN(blockStep
, blocksLeft
);
283 tmp
.record
[i
].startBlock
= SWAP_BE32(blocksTotal
+ bcount
+ startBlock
);
284 tmp
.record
[i
].blockCount
= SWAP_BE32(n
);
288 blocksTotal
+= bcount
;
289 overflowExtents
= realloc(overflowExtents
, (numOverflowExtents
+1) * sizeof(*overflowExtents
));
290 overflowExtents
[numOverflowExtents
++] = tmp
;
297 * wipefs() in -lutil knows about multiple filesystem formats.
298 * This replaces the code:
299 * WriteBuffer(driveInfo, 0, diskBlocksUsed * kBytesPerSector, NULL);
300 * WriteBuffer(driveInfo, driveInfo->totalSectors - 8, 4 * 1024, NULL);
301 * which was used to erase the beginning and end of the filesystem.
310 err
= wipefs_alloc(fd
, 0/*sectorSize*/, &handle
);
312 err
= wipefs_wipe(handle
);
314 wipefs_free(&handle
);
322 * This routine writes an initial HFS Plus volume structure onto a volume.
323 * It is assumed that the disk has already been formatted and verified.
327 make_hfsplus(const DriveInfo
*driveInfo
, hfsparams_t
*defaults
)
330 UInt32 sectorsPerBlock
;
332 UInt32 sectorsPerNode
;
335 UInt32 endOfAttributes
;
336 UInt32 startOfAllocation
;
338 void *nodeBuffer
= NULL
;
339 HFSPlusVolumeHeader
*header
= NULL
;
342 /* Use wipefs() API to clear old metadata from the device.
343 * This should be done before we start writing anything on the
344 * device as wipefs will internally call ioctl(DKIOCDISCARD) on the
347 (void) dowipefs(driveInfo
->fd
);
349 /* --- Create an HFS Plus header: */
351 header
= (HFSPlusVolumeHeader
*)malloc((size_t)kBytesPerSector
);
355 defaults
->encodingHint
= getencodinghint(defaults
->volumeName
);
357 /* VH Initialized in native byte order */
358 InitVH(defaults
, driveInfo
->totalSectors
, header
);
360 sectorsPerBlock
= header
->blockSize
/ kBytesPerSector
;
363 /*--- ZERO OUT BEGINNING OF DISK: */
365 * Clear out the space to be occupied by the bitmap and B-Trees.
366 * The first chunk is the boot sectors, volume header, allocation bitmap,
367 * journal, Extents B-tree, and Attributes B-tree (if any).
368 * The second chunk is the Catalog B-tree.
371 /* Zero out first 1M (to be safe) for volume header */
372 WriteBuffer(driveInfo
, 0, 1024*1024, NULL
);
374 if (NEWFS_HFS_DEBUG
) {
376 * Mark each file extent as used individually, rather than doing it all at once.
377 * Also zero out the entire file.
381 WriteBuffer(driveInfo, \
382 header->f.extents[0].startBlock * sectorsPerBlock, \
383 header->f.totalBlocks * header->blockSize, \
385 if (MarkExtentUsed(driveInfo, header, header->f.extents[0].startBlock, header->f.totalBlocks) == -1) { \
386 errx(1, #f " extent overlap <%u, %u>", header->f.extents[0].startBlock, header->f.totalBlocks); \
394 /* Zero out from start of allocation file to end of attribute file;
395 * will include allocation bitmap, journal, extents btree, and
398 sector
= header
->allocationFile
.extents
[0].startBlock
* sectorsPerBlock
;
399 endOfAttributes
= header
->attributesFile
.extents
[0].startBlock
+ header
->attributesFile
.totalBlocks
;
400 startOfAllocation
= header
->allocationFile
.extents
[0].startBlock
;
401 bytesToZero
= (UInt64
) (endOfAttributes
- startOfAllocation
+ 1) * header
->blockSize
;
402 WriteBuffer(driveInfo
, sector
, bytesToZero
, NULL
);
404 bytesToZero
= (UInt64
) header
->catalogFile
.totalBlocks
* header
->blockSize
;
405 sector
= header
->catalogFile
.extents
[0].startBlock
* sectorsPerBlock
;
406 WriteBuffer(driveInfo
, sector
, bytesToZero
, NULL
);
409 * Allocate a buffer for the rest of our IO.
410 * Note that in some cases we may need to initialize an EA, so we
411 * need to use the attribute B-Tree node size in this calculation.
414 temp
= Largest( defaults
->catalogNodeSize
* 2,
415 (defaults
->attributesNodeSize
* 2),
417 (header
->catalogFile
.extents
[0].startBlock
+ header
->catalogFile
.totalBlocks
+ 7) / 8 );
419 * If size is not a mutiple of 512, round up to nearest sector
421 if ( (temp
& 0x01FF) != 0 )
422 temp
= (temp
+ kBytesPerSector
) & 0xFFFFFE00;
424 nodeBuffer
= valloc((size_t)temp
);
425 if (nodeBuffer
== NULL
)
430 /*--- WRITE ALLOCATION BITMAP BITS TO DISK: */
433 * XXX - this doesn't work well with using arbitrary extents.
435 * To do this, we need to find the appropriate area in the file, and
436 * pass that in to AllocateExtent, which is just a bitmap manipulation
437 * routine. Then we need to write it out at the right place. Note that
438 * we may have to read it in first, as well, which may mean zeroing out
439 * the entirety of the allocation file first.
442 * New function to mark extent as used.
443 * Function should figure out which block(s) for an extent.
444 * Read it in. Mark the bits used. Return.
445 * For now, it can assume the allocation extents are contiguous, but
446 * should be extensible to not do that.
448 sector
= header
->allocationFile
.extents
[0].startBlock
* sectorsPerBlock
;
449 bzero(nodeBuffer
, temp
);
450 /* Mark volume header as allocated */
451 if (header
->blockSize
== 512) {
452 if (MarkExtentUsed(driveInfo
, header
, 0, 4) == -1) {
453 errx(1, "Overlapped extent at <0, 4> (%d)", __LINE__
);
455 } else if (header
->blockSize
== 1024) {
456 if (MarkExtentUsed(driveInfo
, header
, 0, 2) == -1) {
457 errx(1, "Overlapped extent at <0, 2> (%d)", __LINE__
);
460 if (MarkExtentUsed(driveInfo
, header
, 0, 1) == -1) {
461 errx(1, "Overlapped extent at <0, 1> (%d)", __LINE__
);
464 if (NEWFS_HFS_DEBUG
== 0) {
465 /* Mark area from bitmap to end of attributes as allocated */
466 if (MarkExtentUsed(driveInfo
, header
, startOfAllocation
, (endOfAttributes
- startOfAllocation
)) == -1) {
467 errx(1, "Overlapped extent at <%u, %u> (%d)\n", startOfAllocation
, endOfAttributes
- startOfAllocation
, __LINE__
);
471 /* Mark catalog btree blocks as allocated */
472 if (NEWFS_HFS_DEBUG
) {
473 /* Erase the catalog file first */
474 WriteBuffer(driveInfo
,
475 header
->catalogFile
.extents
[0].startBlock
* sectorsPerBlock
,
476 header
->catalogFile
.totalBlocks
* header
->blockSize
,
479 if (MarkExtentUsed(driveInfo
, header
,
480 header
->catalogFile
.extents
[0].startBlock
,
481 header
->catalogFile
.totalBlocks
) == -1) {
482 errx(1, "Overlapped catalog extent at <%u, %u>\n", header
->catalogFile
.extents
[0].startBlock
, header
->catalogFile
.totalBlocks
);
486 * Write alternate Volume Header bitmap bit to allocations file at
487 * 2nd to last sector on HFS+ volume
489 if (MarkExtentUsed(driveInfo
, header
, header
->totalBlocks
- 1, 1) == -1) {
490 errx(1, "Overlapped extent for header at <%u, %u>\n", header
->totalBlocks
- 1, 1);
494 * If the blockSize is 512 bytes, then the last 1kbyte has to be marked
497 if ( header
->blockSize
== 512 ) {
498 if (MarkExtentUsed(driveInfo
, header
, header
->totalBlocks
- 2, 1) == -1) {
499 errx(1, "Overlapped extent for AVH at <%u, %u>\n", header
->totalBlocks
- 2, 1);
504 /*--- WRITE FILE EXTENTS B-TREE TO DISK: */
506 btNodeSize
= defaults
->extentsNodeSize
;
507 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
509 sector
= header
->extentsFile
.extents
[0].startBlock
* sectorsPerBlock
;
510 WriteExtentsFile(driveInfo
, sector
, defaults
, NULL
, nodeBuffer
, &bytesUsed
, &mapNodes
);
513 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
514 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
519 /*--- WRITE FILE ATTRIBUTES B-TREE TO DISK: */
520 if (defaults
->attributesClumpSize
) {
522 btNodeSize
= defaults
->attributesNodeSize
;
523 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
525 sector
= header
->attributesFile
.extents
[0].startBlock
* sectorsPerBlock
;
526 WriteAttributesFile(driveInfo
, sector
, defaults
, NULL
, nodeBuffer
, &bytesUsed
, &mapNodes
);
528 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
529 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
533 /*--- WRITE CATALOG B-TREE TO DISK: */
535 btNodeSize
= defaults
->catalogNodeSize
;
536 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
538 sector
= header
->catalogFile
.extents
[0].startBlock
* sectorsPerBlock
;
539 WriteCatalogFile(driveInfo
, sector
, defaults
, header
, nodeBuffer
, &bytesUsed
, &mapNodes
);
542 WriteMapNodes(driveInfo
, (sector
+ bytesUsed
/kBytesPerSector
),
543 bytesUsed
/btNodeSize
, mapNodes
, btNodeSize
, nodeBuffer
);
546 /*--- JOURNALING SETUP */
547 if (defaults
->journaledHFS
) {
548 sector
= header
->journalInfoBlock
* sectorsPerBlock
;
549 if (NEWFS_HFS_DEBUG
) {
551 * For debug build, the journal may be located somewhere other
552 * than right after the journalInfoBlock.
554 if (MarkExtentUsed(driveInfo
, header
, header
->journalInfoBlock
, 1) == -1) {
555 errx(1, "Extent overlap for journalInfoBlock <%u, 1>", header
->journalInfoBlock
);
558 if (!defaults
->journalDevice
) {
559 UInt32 jStart
= defaults
->journalBlock
? defaults
->journalBlock
: (header
->journalInfoBlock
+ 1);
560 UInt32 jCount
= (UInt32
)(defaults
->journalSize
/ header
->blockSize
);
561 if (MarkExtentUsed(driveInfo
, header
, jStart
, jCount
) == -1) {
562 errx(1, "Extent overlap for journal <%u, %u>", jStart
, jCount
);
566 if (WriteJournalInfo(driveInfo
, sector
, defaults
, header
, nodeBuffer
) != 0) {
567 err(EINVAL
, "Failed to create the journal");
571 /*--- WRITE VOLUME HEADER TO DISK: */
573 /* write header last in case we fail along the way */
575 /* Writes both copies of the volume header */
576 WriteVH (driveInfo
, header
);
577 /* VH is now big-endian */
588 * Writes the Volume Header (VH) to disk.
590 * The VH is byte-swapped if necessary to big endian. Since this
591 * is always the last operation, there's no point in unswapping it.
594 WriteVH (const DriveInfo
*driveInfo
, HFSPlusVolumeHeader
*hp
)
598 WriteBuffer(driveInfo
, 2, kBytesPerSector
, hp
);
599 WriteBuffer(driveInfo
, driveInfo
->totalSectors
- 2, kBytesPerSector
, hp
);
606 * Initialize a Volume Header record.
609 InitVH(hfsparams_t
*defaults
, UInt64 sectors
, HFSPlusVolumeHeader
*hp
)
615 UInt16 burnedBlocksBeforeVH
= 0;
616 UInt16 burnedBlocksAfterAltVH
= 0;
618 UInt32 allocateBlock
;
619 VolumeUUID newVolumeUUID
;
620 VolumeUUID
* finderInfoUUIDPtr
;
621 UInt64 hotFileBandSize
;
625 * 2 MB is the minimum size for the new behavior with
626 * space after the attr b-tree, and hotfile stuff.
628 #define MINVOLSIZE_WITHSPACE 2097152
630 bzero(hp
, kBytesPerSector
);
632 blockSize
= defaults
->blockSize
;
633 blockCount
= sectors
/ (blockSize
>> kLog2SectorSize
);
636 * HFSPlusVolumeHeader is located at sector 2, so we may need
637 * to invalidate blocks before HFSPlusVolumeHeader.
639 if ( blockSize
== 512 ) {
640 burnedBlocksBeforeVH
= 2; /* 2 before VH */
641 burnedBlocksAfterAltVH
= 1; /* 1 after altVH */
642 } else if ( blockSize
== 1024 ) {
643 burnedBlocksBeforeVH
= 1;
645 nextBlock
= burnedBlocksBeforeVH
+ 1; /* +1 for VH itself */
646 if (defaults
->fsStartBlock
) {
648 printf ("Laying down metadata starting at allocation block=%u (totalBlocks=%u)\n", (unsigned int)defaults
->fsStartBlock
, (unsigned int)blockCount
);
649 nextBlock
+= defaults
->fsStartBlock
; /* lay down file system after this allocation block */
652 bitmapBlocks
= defaults
->allocationClumpSize
/ blockSize
;
654 /* note: add 2 for the Alternate VH, and VH */
655 blocksUsed
= 2 + burnedBlocksBeforeVH
+ burnedBlocksAfterAltVH
+ bitmapBlocks
;
657 if (defaults
->flags
& kMakeCaseSensitive
) {
658 hp
->signature
= kHFSXSigWord
;
659 hp
->version
= kHFSXVersion
;
661 hp
->signature
= kHFSPlusSigWord
;
662 hp
->version
= kHFSPlusVersion
;
664 hp
->attributes
= kHFSVolumeUnmountedMask
| kHFSUnusedNodeFixMask
;
665 if (defaults
->flags
& kMakeContentProtect
) {
666 hp
->attributes
|= kHFSContentProtectionMask
;
668 hp
->lastMountedVersion
= kHFSPlusMountVersion
;
670 /* NOTE: create date is in local time, not GMT! */
671 hp
->createDate
= UTCToLocal(defaults
->createDate
);
672 hp
->modifyDate
= defaults
->createDate
;
674 hp
->checkedDate
= defaults
->createDate
;
676 // hp->fileCount = 0;
677 // hp->folderCount = 0;
679 hp
->blockSize
= blockSize
;
680 hp
->totalBlocks
= blockCount
;
681 hp
->freeBlocks
= blockCount
; /* will be adjusted at the end */
683 volsize
= (UInt64
) blockCount
* (UInt64
) blockSize
;
685 hp
->rsrcClumpSize
= defaults
->rsrcClumpSize
;
686 hp
->dataClumpSize
= defaults
->dataClumpSize
;
687 hp
->nextCatalogID
= defaults
->nextFreeFileID
;
688 hp
->encodingsBitmap
= 1 | (1 << ENCODING_TO_BIT(defaults
->encodingHint
));
690 /* set up allocation bitmap file */
691 hp
->allocationFile
.clumpSize
= defaults
->allocationClumpSize
;
692 hp
->allocationFile
.logicalSize
= defaults
->allocationClumpSize
;
693 hp
->allocationFile
.totalBlocks
= bitmapBlocks
;
695 if (NEWFS_HFS_DEBUG
&& defaults
->allocationStartBlock
)
696 allocateBlock
= defaults
->allocationStartBlock
;
698 allocateBlock
= nextBlock
;
699 nextBlock
+= bitmapBlocks
;
702 createExtents(&hp
->allocationFile
, kHFSAllocationFileID
, allocateBlock
, defaults
->allocationExtsCount
, 1);
704 // This works because the files are contiguous for now
706 printf ("allocationFile: (%10u, %10u)\n", hp
->allocationFile
.extents
[0].startBlock
, hp
->allocationFile
.totalBlocks
);
708 /* set up journal files */
709 if (defaults
->journaledHFS
) {
712 hp
->attributes
|= kHFSVolumeJournaledMask
;
713 hp
->nextCatalogID
+= 2;
716 * Allocate 1 block for the journalInfoBlock. The
717 * journal file size is passed in hfsparams_t.
719 if (NEWFS_HFS_DEBUG
&& defaults
->journalInfoBlock
)
720 hp
->journalInfoBlock
= defaults
->journalInfoBlock
;
722 hp
->journalInfoBlock
= nextBlock
++;
723 if (NEWFS_HFS_DEBUG
&& defaults
->journalBlock
)
724 journalBlock
= defaults
->journalBlock
;
726 journalBlock
= hp
->journalInfoBlock
+ 1;
727 nextBlock
+= ((defaults
->journalSize
+blockSize
-1) / blockSize
);
730 if (NEWFS_HFS_DEBUG
) {
731 printf ("journalInfo : (%10u, %10u)\n", (u_int32_t
)hp
->journalInfoBlock
, 1);
732 printf ("journal : (%10u, %10u)\n", (u_int32_t
)journalBlock
, (u_int32_t
)((defaults
->journalSize
+ (blockSize
-1)) / blockSize
));
734 /* XXX What if journal is on a different device? */
735 blocksUsed
+= 1 + ((defaults
->journalSize
+blockSize
-1) / blockSize
);
737 hp
->journalInfoBlock
= 0;
740 /* set up extents b-tree file */
741 hp
->extentsFile
.clumpSize
= defaults
->extentsClumpSize
;
742 hp
->extentsFile
.logicalSize
= defaults
->extentsClumpSize
;
743 hp
->extentsFile
.totalBlocks
= defaults
->extentsClumpSize
/ blockSize
;
744 if (NEWFS_HFS_DEBUG
&& defaults
->extentsStartBlock
)
745 allocateBlock
= defaults
->extentsStartBlock
;
747 allocateBlock
= nextBlock
;
748 nextBlock
+= hp
->extentsFile
.totalBlocks
;
750 createExtents(&hp
->extentsFile
, kHFSExtentsFileID
, allocateBlock
, defaults
->extentsExtsCount
, (defaults
->journaledHFS
&& defaults
->extentsNodeSize
> hp
->blockSize
) ? defaults
->extentsNodeSize
/ hp
->blockSize
: 1);
752 blocksUsed
+= hp
->extentsFile
.totalBlocks
;
755 printf ("extentsFile : (%10u, %10u)\n", hp
->extentsFile
.extents
[0].startBlock
, hp
->extentsFile
.totalBlocks
);
757 /* set up attributes b-tree file */
758 if (defaults
->attributesClumpSize
) {
759 hp
->attributesFile
.clumpSize
= defaults
->attributesClumpSize
;
760 hp
->attributesFile
.logicalSize
= defaults
->attributesClumpSize
;
761 hp
->attributesFile
.totalBlocks
= defaults
->attributesClumpSize
/ blockSize
;
762 if (NEWFS_HFS_DEBUG
&& defaults
->attributesStartBlock
)
763 allocateBlock
= defaults
->attributesStartBlock
;
765 allocateBlock
= nextBlock
;
766 nextBlock
+= hp
->attributesFile
.totalBlocks
;
768 createExtents(&hp
->attributesFile
, kHFSAttributesFileID
, allocateBlock
, defaults
->attributesExtsCount
, (defaults
->journaledHFS
&& defaults
->attributesNodeSize
> hp
->blockSize
) ? defaults
->attributesNodeSize
/ hp
->blockSize
: 1);
769 blocksUsed
+= hp
->attributesFile
.totalBlocks
;
771 if (NEWFS_HFS_DEBUG
) {
772 printf ("attributesFile: (%10u, %10u)\n", hp
->attributesFile
.extents
[0].startBlock
, hp
->attributesFile
.totalBlocks
);
775 * Leave some room for the Attributes B-tree to grow, if the volsize >= 2MB
777 if (volsize
>= MINVOLSIZE_WITHSPACE
&& defaults
->attributesStartBlock
== 0) {
778 nextBlock
+= 10 * (hp
->attributesFile
.clumpSize
/ blockSize
);
782 /* set up catalog b-tree file */
783 hp
->catalogFile
.clumpSize
= defaults
->catalogClumpSize
;
784 hp
->catalogFile
.logicalSize
= defaults
->catalogClumpSize
;
785 hp
->catalogFile
.totalBlocks
= defaults
->catalogClumpSize
/ blockSize
;
786 if (NEWFS_HFS_DEBUG
&& defaults
->catalogStartBlock
)
787 allocateBlock
= defaults
->catalogStartBlock
;
789 allocateBlock
= nextBlock
;
790 nextBlock
+= hp
->catalogFile
.totalBlocks
;
792 createExtents(&hp
->catalogFile
, kHFSCatalogFileID
, allocateBlock
, defaults
->catalogExtsCount
, (defaults
->journaledHFS
&& defaults
->catalogNodeSize
> hp
->blockSize
) ? defaults
->catalogNodeSize
/ hp
->blockSize
: 1);
793 blocksUsed
+= hp
->catalogFile
.totalBlocks
;
796 printf ("catalogFile : (%10u, %10u)\n\n", hp
->catalogFile
.extents
[0].startBlock
, hp
->catalogFile
.totalBlocks
);
798 if ((numOverflowExtents
* sizeof(struct ExtentRecord
)) >
799 (defaults
->extentsNodeSize
- sizeof(BTNodeDescriptor
) - (sizeof(uint16_t) * numOverflowExtents
))) {
800 errx(1, "Too many overflow extent records to fit into a single extent node");
804 * Add some room for the catalog file to grow...
806 nextBlock
+= 10 * (hp
->catalogFile
.clumpSize
/ hp
->blockSize
);
809 * Add some room for the hot file band. This uses the same 5MB per GB
810 * as the kernel. The kernel only uses hotfiles if the volume is larger
811 * than 10GBytes, so do the same here.
813 #define METADATAZONE_MINIMUM_VOLSIZE (10ULL * 1024ULL * 1024ULL * 1024ULL)
814 #define HOTBAND_MINIMUM_SIZE (10*1024*1024)
815 #define HOTBAND_MAXIMUM_SIZE (512*1024*1024)
816 if (volsize
>= METADATAZONE_MINIMUM_VOLSIZE
) {
817 hotFileBandSize
= (UInt64
) blockCount
* blockSize
/ 1024 * 5;
818 if (hotFileBandSize
> HOTBAND_MAXIMUM_SIZE
)
819 hotFileBandSize
= HOTBAND_MAXIMUM_SIZE
;
820 else if (hotFileBandSize
< HOTBAND_MINIMUM_SIZE
)
821 hotFileBandSize
= HOTBAND_MINIMUM_SIZE
;
822 nextBlock
+= hotFileBandSize
/ blockSize
;
824 if (NEWFS_HFS_DEBUG
&& defaults
->nextAllocBlock
)
825 hp
->nextAllocation
= defaults
->nextAllocBlock
;
827 hp
->nextAllocation
= nextBlock
;
829 /* Adjust free blocks to reflect everything we have allocated. */
830 hp
->freeBlocks
-= blocksUsed
;
832 /* Generate and write UUID for the HFS+ disk */
833 GenerateVolumeUUID(&newVolumeUUID
);
834 finderInfoUUIDPtr
= (VolumeUUID
*)(&hp
->finderInfo
[24]);
835 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(newVolumeUUID
.v
.high
);
836 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(newVolumeUUID
.v
.low
);
842 * Mark the given extent as in-use in the given bitmap buffer.
844 static int AllocateExtent(UInt8
*buffer
, UInt32 startBlock
, UInt32 blockCount
)
848 /* Point to start of extent in bitmap buffer */
849 p
= buffer
+ (startBlock
/ 8);
852 * Important to remember: block 0 is (1 << 7);
853 * block 7 is (1 << 0).
855 /* Partial byte at start of extent */
859 unsigned int lShift
= 0;
860 unsigned int startBit
= startBlock
& 7;
863 * Is startBlock + blockCount entirely in
866 if (blockCount
< (8 - startBit
)) {
867 lShift
= 8 - (startBit
+ blockCount
);
869 mask
= (0xff >> startBit
) & (0xff << lShift
);
870 if (NEWFS_HFS_DEBUG
&& (*p
& mask
)) {
871 fprintf(stderr
, "%s(%d): expected 0, got %x\n", __FUNCTION__
, __LINE__
, *p
& mask
);
876 * We have either set <lShift> or <startBlock & 7> bits.
878 blockCount
-= 8 - (lShift
+ startBit
);
879 // blockCount -= lShift ? blockCount : (8 - startBit);
880 // blockCount -= __builtin_popcount(mask);
883 /* Fill in whole bytes */
886 if (NEWFS_HFS_DEBUG
) {
888 * Put this in ifdef because it'll slow things down.
889 * For non-debug case, we shouldn't have to worry about
890 * an overlap, anyway.
893 for (indx
= 0; indx
< blockCount
/ 8; indx
++) {
895 fprintf(stderr
, "%s(%d): Expected 0 at %zu, got 0x%x\n", __FUNCTION__
, __LINE__
, indx
, p
[indx
]);
901 memset(p
, 0xFF, blockCount
/ 8);
907 /* Partial byte at end of extent */
910 UInt8 mask
= 0xff << (8 - blockCount
);
911 if (NEWFS_HFS_DEBUG
&& (*p
& mask
)) {
912 fprintf(stderr
, "%s(%d): Expected 0, got %x\n", __FUNCTION__
, __LINE__
, *p
& mask
);
921 * Mark an extent as being used.
922 * This involves finding out where the allocations file is,
923 * where in the allocations file the extent starts, and how
926 * One downside to this implementation is that this does
927 * more I/O than the old mechanism, a cost to the flexibility.
928 * May have to consider doing caching of some sort.
932 MarkExtentUsed(const DriveInfo
*driveInfo
,
933 HFSPlusVolumeHeader
*header
,
937 size_t bufSize
= driveInfo
->physSectorSize
;
938 uint8_t buf
[bufSize
];
939 uint32_t blocksLeft
= blockCount
;
940 uint32_t curBlock
= startBlock
;
941 static const int kBitsPerByte
= 8;
944 * We loop through physSectorSize blocks.
945 * This allows us to set as many bits as we need.
947 while (blocksLeft
> 0) {
949 uint32_t numBlocks
; // The number of blocks to mark as used in this pass.
950 uint32_t blockOffset
; // This is the block number of the current range, which starts at curBlock
952 memset(buf
, 0, sizeof(buf
));
953 secNum
= curBlock
/ (bufSize
* kBitsPerByte
);
954 blockOffset
= curBlock
% (bufSize
* kBitsPerByte
);
955 numBlocks
= MIN((bufSize
* kBitsPerByte
) - blockOffset
, blocksLeft
);
958 * Okay, now we've got the block number to read,
959 * the offset into the block, and the number of blocks
962 * First we read in the buffer. To do that, we need to
963 * know where to read.
971 * This needs to be changed if/when we support non-contiguous multiple
972 * extents. At that point, it'll probably have to be a function to search
973 * for the requested offset. (How many times must MapFileC be written?)
974 * For now, though, the offset is the physical sector offset from the
975 * start of the allocations file.
977 offset
= (header
->allocationFile
.extents
[0].startBlock
* header
->blockSize
) +
980 nbytes
= pread(driveInfo
->fd
, buf
, bufSize
, offset
);
982 if (nbytes
< (ssize_t
)bufSize
) {
984 err(1, "%s::pread(%d, %p, %zu, %lld)", __FUNCTION__
, driveInfo
->fd
, buf
, bufSize
, offset
);
988 if (AllocateExtent(buf
, blockOffset
, numBlocks
) == -1) {
989 warnx("In-use allocation block in <%u, %u>", blockOffset
, numBlocks
);
992 nwritten
= pwrite(driveInfo
->fd
, buf
, bufSize
, offset
);
994 * Normally I'd check for nwritten to be less than bufSize, but since bufSize is
995 * the physical sector size, we shouldn't be able to get less. So that most likely
996 * means a return value of 0 or -1, neither of which I could do anything about.
998 if (nwritten
!= (ssize_t
)bufSize
)
1001 // And go get the next set, if needed
1002 blocksLeft
-= numBlocks
;
1003 curBlock
+= numBlocks
;
1011 * Initializes and writes out the extents b-tree file.
1013 * Byte swapping is performed in place. The buffer should not be
1014 * accessed through direct casting once it leaves this function.
1017 WriteExtentsFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1018 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp __unused
, void *buffer
,
1019 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1021 BTNodeDescriptor
*ndp
;
1024 UInt32 nodeBitsInHeader
;
1031 fileSize
= dp
->extentsClumpSize
;
1032 nodeSize
= dp
->extentsNodeSize
;
1034 bzero(buffer
, nodeSize
);
1037 /* FILL IN THE NODE DESCRIPTOR: */
1038 ndp
= (BTNodeDescriptor
*)buffer
;
1039 ndp
->kind
= kBTHeaderNode
;
1040 ndp
->numRecords
= SWAP_BE16 (3);
1041 offset
= sizeof(BTNodeDescriptor
);
1043 SETOFFSET(buffer
, nodeSize
, offset
, 1);
1046 /* FILL IN THE HEADER RECORD: */
1047 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1048 if (numOverflowExtents
) {
1049 bthp
->treeDepth
= SWAP_BE16(1);
1050 bthp
->rootNode
= SWAP_BE32(1);
1051 bthp
->firstLeafNode
= SWAP_BE32(1);
1052 bthp
->lastLeafNode
= SWAP_BE32(1);
1053 bthp
->leafRecords
= SWAP_BE32(numOverflowExtents
);
1055 bthp
->treeDepth
= 0;
1057 bthp
->firstLeafNode
= 0;
1058 bthp
->lastLeafNode
= 0;
1059 bthp
->leafRecords
= 0;
1062 bthp
->nodeSize
= SWAP_BE16 (nodeSize
);
1063 bthp
->totalNodes
= SWAP_BE32 (fileSize
/ nodeSize
);
1064 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - (numOverflowExtents
? 2 : 1)); /* header */
1065 bthp
->clumpSize
= SWAP_BE32 (fileSize
);
1067 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1068 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusExtentKeyMaximumLength
);
1069 offset
+= sizeof(BTHeaderRec
);
1071 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1073 offset
+= kBTreeHeaderUserBytes
;
1075 SETOFFSET(buffer
, nodeSize
, offset
, 3);
1078 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1079 nodeBitsInHeader
= 8 * (nodeSize
1080 - sizeof(BTNodeDescriptor
)
1081 - sizeof(BTHeaderRec
)
1082 - kBTreeHeaderUserBytes
1083 - (4 * sizeof(SInt16
)) );
1085 if (SWAP_BE32 (bthp
->totalNodes
) > nodeBitsInHeader
) {
1086 UInt32 nodeBitsInMapNode
;
1088 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1089 nodeBitsInMapNode
= 8 * (nodeSize
1090 - sizeof(BTNodeDescriptor
)
1091 - (2 * sizeof(SInt16
))
1093 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1094 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1095 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
1100 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1101 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1103 bmp
= ((UInt8
*)buffer
+ offset
);
1104 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
1106 /* Working a byte at a time is endian safe */
1107 while (temp
>= 8) { *bmp
= 0xFF; temp
-= 8; bmp
++; }
1108 *bmp
= ~(0xFF >> temp
);
1109 offset
+= nodeBitsInHeader
/8;
1111 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1113 if (NEWFS_HFS_DEBUG
&& numOverflowExtents
) {
1114 void *node2
= (uint8_t*)buffer
+ nodeSize
;
1116 int (^keyCompare
)(const void *l
, const void *r
) = ^(const void *l
, const void *r
) {
1117 const struct ExtentRecord
*left
= (const struct ExtentRecord
*)l
;
1118 const struct ExtentRecord
*right
= (const struct ExtentRecord
*)r
;
1119 if (SWAP_BE32(left
->key
.fileID
) != SWAP_BE32(right
->key
.fileID
)) {
1120 return (SWAP_BE32(left
->key
.fileID
) > SWAP_BE32(right
->key
.fileID
)) ? 1 : -1;
1122 // forkType will always be 0 for us
1123 if (SWAP_BE32(left
->key
.startBlock
) != SWAP_BE32(right
->key
.startBlock
)) {
1124 return (SWAP_BE32(left
->key
.startBlock
) > SWAP_BE32(right
->key
.startBlock
)) ? 1 : -1;
1129 if (numOverflowExtents
> 1) {
1130 qsort_b(overflowExtents
, numOverflowExtents
, sizeof(*overflowExtents
), keyCompare
);
1132 bzero(node2
, nodeSize
);
1133 ndp
= (BTNodeDescriptor
*)node2
;
1134 ndp
->kind
= kBTLeafNode
;
1135 ndp
->numRecords
= SWAP_BE16(numOverflowExtents
);
1138 offset
= sizeof(BTNodeDescriptor
);
1139 for (i
= 0; i
< numOverflowExtents
; i
++) {
1140 SETOFFSET(node2
, nodeSize
, offset
, 1 + i
);
1141 memcpy(node2
+ offset
, &overflowExtents
[i
], sizeof(*overflowExtents
));
1142 offset
+= sizeof(*overflowExtents
);
1144 SETOFFSET(node2
, nodeSize
, offset
, numOverflowExtents
+ 1);
1147 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1149 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1154 * WriteAttributesFile
1156 * Initializes and writes out the attributes b-tree file.
1158 * Byte swapping is performed in place. The buffer should not be
1159 * accessed through direct casting once it leaves this function.
1162 WriteAttributesFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1163 const hfsparams_t
*dp
, HFSExtentDescriptor
*bbextp __unused
, void *buffer
,
1164 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1166 BTNodeDescriptor
*ndp
;
1169 UInt32 nodeBitsInHeader
;
1174 int set_cp_level
= 0;
1177 fileSize
= dp
->attributesClumpSize
;
1178 nodeSize
= dp
->attributesNodeSize
;
1182 * If user specified content protection and a protection level,
1183 * then verify the protection level is sane.
1185 if ((dp
->flags
& kMakeContentProtect
) && (dp
->protectlevel
!= 0)) {
1186 if ((dp
->protectlevel
>= 2 ) && (dp
->protectlevel
<= 4)) {
1193 bzero(buffer
, nodeSize
);
1196 /* FILL IN THE NODE DESCRIPTOR: */
1197 ndp
= (BTNodeDescriptor
*)buffer
;
1198 ndp
->kind
= kBTHeaderNode
;
1199 ndp
->numRecords
= SWAP_BE16 (3);
1200 offset
= sizeof(BTNodeDescriptor
);
1202 SETOFFSET(buffer
, nodeSize
, offset
, 1);
1205 /* FILL IN THE HEADER RECORD: */
1206 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1208 bthp
->treeDepth
= SWAP_BE16(1);
1209 bthp
->rootNode
= SWAP_BE32(1);
1210 bthp
->firstLeafNode
= SWAP_BE32(1);
1211 bthp
->lastLeafNode
= SWAP_BE32(1);
1212 bthp
->leafRecords
= SWAP_BE32(1);
1215 bthp
->treeDepth
= 0;
1217 bthp
->firstLeafNode
= 0;
1218 bthp
->lastLeafNode
= 0;
1219 bthp
->leafRecords
= 0;
1222 bthp
->nodeSize
= SWAP_BE16 (nodeSize
);
1223 bthp
->totalNodes
= SWAP_BE32 (fileSize
/ nodeSize
);
1225 /* Add 1 node for the first record */
1226 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - 2);
1229 /* Take the header into account */
1230 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - 1);
1232 bthp
->clumpSize
= SWAP_BE32 (fileSize
);
1234 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
| kBTVariableIndexKeysMask
);
1235 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusAttrKeyMaximumLength
);
1237 offset
+= sizeof(BTHeaderRec
);
1239 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1241 offset
+= kBTreeHeaderUserBytes
;
1243 SETOFFSET(buffer
, nodeSize
, offset
, 3);
1246 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1247 nodeBitsInHeader
= 8 * (nodeSize
1248 - sizeof(BTNodeDescriptor
)
1249 - sizeof(BTHeaderRec
)
1250 - kBTreeHeaderUserBytes
1251 - (4 * sizeof(SInt16
)) );
1252 if (SWAP_BE32 (bthp
->totalNodes
) > nodeBitsInHeader
) {
1253 UInt32 nodeBitsInMapNode
;
1255 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1256 nodeBitsInMapNode
= 8 * (nodeSize
1257 - sizeof(BTNodeDescriptor
)
1258 - (2 * sizeof(SInt16
))
1260 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1261 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1262 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
1267 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1268 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1270 bmp
= ((UInt8
*)buffer
+ offset
);
1271 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
1273 /* Working a byte at a time is endian safe */
1274 while (temp
>= 8) { *bmp
= 0xFF; temp
-= 8; bmp
++; }
1275 *bmp
= ~(0xFF >> temp
);
1276 offset
+= nodeBitsInHeader
/8;
1278 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1282 /* Stuff in the EA on the root folder */
1283 void *node2
= (uint8_t*)buffer
+ nodeSize
;
1285 struct cp_root_xattr ea
;
1287 uint8_t canonicalName
[256];
1290 HFSPlusAttrData
*attrData
;
1291 HFSPlusAttrKey
*attrKey
;
1292 bzero(node2
, nodeSize
);
1293 ndp
= (BTNodeDescriptor
*)node2
;
1295 ndp
->kind
= kBTLeafNode
;
1296 ndp
->numRecords
= SWAP_BE16(1);
1299 offset
= sizeof(BTNodeDescriptor
);
1300 SETOFFSET(node2
, nodeSize
, offset
, 1);
1302 attrKey
= (HFSPlusAttrKey
*)((uint8_t*)node2
+ offset
);
1303 attrKey
->fileID
= SWAP_BE32(1);
1304 attrKey
->startBlock
= 0;
1305 attrKey
->keyLength
= SWAP_BE16(sizeof(*attrKey
) - sizeof(attrKey
->keyLength
));
1307 cfstr
= CFStringCreateWithCString(kCFAllocatorDefault
, "com.apple.system.cprotect", kCFStringEncodingUTF8
);
1308 if (_CFStringGetFileSystemRepresentation(cfstr
, canonicalName
, sizeof(canonicalName
)) &&
1309 ConvertUTF8toUnicode(canonicalName
,
1310 sizeof(attrKey
->attrName
),
1311 attrKey
->attrName
, &attrKey
->attrNameLen
) == 0) {
1312 attrKey
->attrNameLen
= SWAP_BE16(attrKey
->attrNameLen
);
1313 offset
+= sizeof(*attrKey
);
1315 /* If the offset is odd, move up to the next even value */
1320 attrData
= (HFSPlusAttrData
*)((uint8_t*)node2
+ offset
);
1321 bzero(&ea
, sizeof(ea
));
1322 ea
.vers
= OSSwapHostToLittleInt16(dp
->protectlevel
); //(leave in LittleEndian)
1323 attrData
->recordType
= SWAP_BE32(kHFSPlusAttrInlineData
);
1324 attrData
->attrSize
= SWAP_BE32(sizeof(ea
));
1325 memcpy(attrData
->attrData
, &ea
, sizeof(ea
));
1326 offset
+= sizeof (HFSPlusAttrData
) + sizeof(ea
) - sizeof(attrData
->attrData
);
1328 SETOFFSET (node2
, nodeSize
, offset
, 2);
1333 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1334 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1337 #if !TARGET_OS_EMBEDDED
1339 get_dev_uuid(const char *disk_name
, char *dev_uuid_str
, int dev_uuid_len
)
1341 io_service_t service
;
1342 CFStringRef uuid_str
;
1345 if (strncmp(disk_name
, _PATH_DEV
, strlen(_PATH_DEV
)) == 0) {
1346 disk_name
+= strlen(_PATH_DEV
);
1349 dev_uuid_str
[0] = '\0';
1351 service
= IOServiceGetMatchingService(kIOMasterPortDefault
, IOBSDNameMatching(kIOMasterPortDefault
, 0, disk_name
));
1352 if (service
!= IO_OBJECT_NULL
) {
1353 uuid_str
= IORegistryEntryCreateCFProperty(service
, CFSTR(kIOMediaUUIDKey
), kCFAllocatorDefault
, 0);
1355 if (CFStringGetFileSystemRepresentation(uuid_str
, dev_uuid_str
, dev_uuid_len
) != 0) {
1358 CFRelease(uuid_str
);
1360 IOObjectRelease(service
);
1367 clear_journal_dev(const char *dev_name
)
1371 fd
= open(dev_name
, O_RDWR
);
1373 printf("Failed to open the journal device %s (%s)\n", dev_name
, strerror(errno
));
1382 #endif /* !TARGET_OS_EMBEDDED */
1386 WriteJournalInfo(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1387 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
,
1390 JournalInfoBlock
*jibp
= buffer
;
1391 UInt32 journalBlock
;
1393 memset(buffer
, 0xdb, driveInfo
->physSectorSize
);
1394 memset(jibp
, 0, sizeof(JournalInfoBlock
));
1396 #if !TARGET_OS_EMBEDDED
1397 if (dp
->journalDevice
) {
1400 if (get_dev_uuid(dp
->journalDevice
, uuid_str
, sizeof(uuid_str
)) == 0) {
1401 strlcpy((char *)&jibp
->reserved
[0], uuid_str
, sizeof(jibp
->reserved
));
1403 // we also need to blast out some zeros to the journal device
1404 // in case it had a file system on it previously. that way
1405 // it's "initialized" in the sense that the previous contents
1406 // won't get mounted accidently. if this fails we'll bail out.
1407 if (clear_journal_dev(dp
->journalDevice
) != 0) {
1411 printf("FAILED to get the device uuid for device %s\n", dp
->journalDevice
);
1412 strlcpy((char *)&jibp
->reserved
[0], "NO-DEV-UUID", sizeof(jibp
->reserved
));
1417 jibp
->flags
= kJIJournalInFSMask
;
1418 #if !TARGET_OS_EMBEDDED
1421 jibp
->flags
|= kJIJournalNeedInitMask
;
1422 if (NEWFS_HFS_DEBUG
&& dp
->journalBlock
)
1423 journalBlock
= dp
->journalBlock
;
1425 journalBlock
= header
->journalInfoBlock
+ 1;
1426 jibp
->offset
= ((UInt64
) journalBlock
) * header
->blockSize
;
1427 jibp
->size
= dp
->journalSize
;
1429 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1430 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1431 jibp
->size
= SWAP_BE64(jibp
->size
);
1433 WriteBuffer(driveInfo
, startingSector
, driveInfo
->physSectorSize
, buffer
);
1435 jibp
->flags
= SWAP_BE32(jibp
->flags
);
1436 jibp
->offset
= SWAP_BE64(jibp
->offset
);
1437 jibp
->size
= SWAP_BE64(jibp
->size
);
1446 * This routine initializes a Catalog B-Tree.
1448 * Note: Since large volumes can have bigger b-trees they
1449 * might need to have map nodes setup.
1452 WriteCatalogFile(const DriveInfo
*driveInfo
, UInt64 startingSector
,
1453 const hfsparams_t
*dp
, HFSPlusVolumeHeader
*header
, void *buffer
,
1454 UInt32
*bytesUsed
, UInt32
*mapNodes
)
1456 BTNodeDescriptor
*ndp
;
1459 UInt32 nodeBitsInHeader
;
1466 fileSize
= dp
->catalogClumpSize
;
1467 nodeSize
= dp
->catalogNodeSize
;
1469 bzero(buffer
, nodeSize
);
1472 /* FILL IN THE NODE DESCRIPTOR: */
1473 ndp
= (BTNodeDescriptor
*)buffer
;
1474 ndp
->kind
= kBTHeaderNode
;
1475 ndp
->numRecords
= SWAP_BE16 (3);
1476 offset
= sizeof(BTNodeDescriptor
);
1478 SETOFFSET(buffer
, nodeSize
, offset
, 1);
1481 /* FILL IN THE HEADER RECORD: */
1482 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1483 bthp
->treeDepth
= SWAP_BE16 (1);
1484 bthp
->rootNode
= SWAP_BE32 (1);
1485 bthp
->firstLeafNode
= SWAP_BE32 (1);
1486 bthp
->lastLeafNode
= SWAP_BE32 (1);
1487 bthp
->leafRecords
= SWAP_BE32 (dp
->journaledHFS
? 6 : 2);
1488 bthp
->nodeSize
= SWAP_BE16 (nodeSize
);
1489 bthp
->totalNodes
= SWAP_BE32 (fileSize
/ nodeSize
);
1490 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->totalNodes
) - 2); /* header and root */
1491 bthp
->clumpSize
= SWAP_BE32 (fileSize
);
1494 bthp
->attributes
|= SWAP_BE32 (kBTVariableIndexKeysMask
+ kBTBigKeysMask
);
1495 bthp
->maxKeyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMaximumLength
);
1496 if (dp
->flags
& kMakeCaseSensitive
)
1497 bthp
->keyCompareType
= kHFSBinaryCompare
;
1499 bthp
->keyCompareType
= kHFSCaseFolding
;
1501 offset
+= sizeof(BTHeaderRec
);
1503 SETOFFSET(buffer
, nodeSize
, offset
, 2);
1505 offset
+= kBTreeHeaderUserBytes
;
1507 SETOFFSET(buffer
, nodeSize
, offset
, 3);
1509 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
1510 nodeBitsInHeader
= 8 * (nodeSize
1511 - sizeof(BTNodeDescriptor
)
1512 - sizeof(BTHeaderRec
)
1513 - kBTreeHeaderUserBytes
1514 - (4 * sizeof(SInt16
)) );
1516 if (SWAP_BE32 (bthp
->totalNodes
) > nodeBitsInHeader
) {
1517 UInt32 nodeBitsInMapNode
;
1519 ndp
->fLink
= SWAP_BE32 (SWAP_BE32 (bthp
->lastLeafNode
) + 1);
1520 nodeBitsInMapNode
= 8 * (nodeSize
1521 - sizeof(BTNodeDescriptor
)
1522 - (2 * sizeof(SInt16
))
1524 *mapNodes
= (SWAP_BE32 (bthp
->totalNodes
) - nodeBitsInHeader
+
1525 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
1526 bthp
->freeNodes
= SWAP_BE32 (SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
);
1530 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
1531 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
1533 bmp
= ((UInt8
*)buffer
+ offset
);
1534 temp
= SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
);
1536 /* Working a byte at a time is endian safe */
1537 while (temp
>= 8) { *bmp
= 0xFF; temp
-= 8; bmp
++; }
1538 *bmp
= ~(0xFF >> temp
);
1539 offset
+= nodeBitsInHeader
/8;
1541 SETOFFSET(buffer
, nodeSize
, offset
, 4);
1543 InitCatalogRoot_HFSPlus(dp
, header
, buffer
+ nodeSize
);
1545 *bytesUsed
= (SWAP_BE32 (bthp
->totalNodes
) - SWAP_BE32 (bthp
->freeNodes
) - *mapNodes
) * nodeSize
;
1547 WriteBuffer(driveInfo
, startingSector
, *bytesUsed
, buffer
);
1552 InitCatalogRoot_HFSPlus(const hfsparams_t
*dp
, const HFSPlusVolumeHeader
*header
, void * buffer
)
1554 BTNodeDescriptor
*ndp
;
1555 HFSPlusCatalogKey
*ckp
;
1556 HFSPlusCatalogKey
*tkp
;
1557 HFSPlusCatalogFolder
*cdp
;
1558 HFSPlusCatalogFile
*cfp
;
1559 HFSPlusCatalogThread
*ctp
;
1562 size_t unicodeBytes
;
1563 UInt8 canonicalName
[256];
1568 nodeSize
= dp
->catalogNodeSize
;
1569 bzero(buffer
, nodeSize
);
1572 * All nodes have a node descriptor...
1574 ndp
= (BTNodeDescriptor
*)buffer
;
1575 ndp
->kind
= kBTLeafNode
;
1577 ndp
->numRecords
= SWAP_BE16 (dp
->journaledHFS
? 6 : 2);
1578 offset
= sizeof(BTNodeDescriptor
);
1579 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1582 * First record is always the root directory...
1584 ckp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1586 /* Use CFString functions to get a HFSPlus Canonical name */
1587 cfstr
= CFStringCreateWithCString(kCFAllocatorDefault
, (char *)dp
->volumeName
, kCFStringEncodingUTF8
);
1588 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, canonicalName
, sizeof(canonicalName
));
1590 if (!cfOK
|| ConvertUTF8toUnicode(canonicalName
, sizeof(ckp
->nodeName
.unicode
),
1591 ckp
->nodeName
.unicode
, &ckp
->nodeName
.length
)) {
1593 /* On conversion errors "untitled" is used as a fallback. */
1594 (void) ConvertUTF8toUnicode((UInt8
*)kDefaultVolumeNameStr
,
1595 sizeof(ckp
->nodeName
.unicode
),
1596 ckp
->nodeName
.unicode
,
1597 &ckp
->nodeName
.length
);
1598 warnx("invalid HFS+ name: \"%s\", using \"%s\" instead",
1599 dp
->volumeName
, kDefaultVolumeNameStr
);
1602 ckp
->nodeName
.length
= SWAP_BE16 (ckp
->nodeName
.length
);
1604 unicodeBytes
= sizeof(UniChar
) * SWAP_BE16 (ckp
->nodeName
.length
);
1606 ckp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
+ unicodeBytes
);
1607 ckp
->parentID
= SWAP_BE32 (kHFSRootParentID
);
1608 offset
+= SWAP_BE16 (ckp
->keyLength
) + 2;
1610 cdp
= (HFSPlusCatalogFolder
*)((UInt8
*)buffer
+ offset
);
1611 cdp
->recordType
= SWAP_BE16 (kHFSPlusFolderRecord
);
1612 /* folder count is only supported on HFSX volumes */
1613 if (dp
->flags
& kMakeCaseSensitive
) {
1614 cdp
->flags
= SWAP_BE16 (kHFSHasFolderCountMask
);
1616 cdp
->valence
= SWAP_BE32 (dp
->journaledHFS
? 2 : 0);
1617 cdp
->folderID
= SWAP_BE32 (kHFSRootFolderID
);
1618 cdp
->createDate
= SWAP_BE32 (dp
->createDate
);
1619 cdp
->contentModDate
= SWAP_BE32 (dp
->createDate
);
1620 cdp
->textEncoding
= SWAP_BE32 (dp
->encodingHint
);
1621 if (dp
->flags
& kUseAccessPerms
) {
1622 cdp
->bsdInfo
.ownerID
= SWAP_BE32 (dp
->owner
);
1623 cdp
->bsdInfo
.groupID
= SWAP_BE32 (dp
->group
);
1624 cdp
->bsdInfo
.fileMode
= SWAP_BE16 (dp
->mask
| S_IFDIR
);
1626 offset
+= sizeof(HFSPlusCatalogFolder
);
1627 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1630 * Second record is always the root directory thread...
1632 tkp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1633 tkp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
);
1634 tkp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1635 // tkp->nodeName.length = 0;
1637 offset
+= SWAP_BE16 (tkp
->keyLength
) + 2;
1639 ctp
= (HFSPlusCatalogThread
*)((UInt8
*)buffer
+ offset
);
1640 ctp
->recordType
= SWAP_BE16 (kHFSPlusFolderThreadRecord
);
1641 ctp
->parentID
= SWAP_BE32 (kHFSRootParentID
);
1642 bcopy(&ckp
->nodeName
, &ctp
->nodeName
, sizeof(UInt16
) + unicodeBytes
);
1643 offset
+= (sizeof(HFSPlusCatalogThread
)
1644 - (sizeof(ctp
->nodeName
.unicode
) - unicodeBytes
) );
1646 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1649 * Add records for ".journal" and ".journal_info_block" files:
1651 if (dp
->journaledHFS
) {
1652 struct HFSUniStr255
*nodename1
, *nodename2
;
1653 size_t uBytes1
, uBytes2
;
1654 UInt32 journalBlock
;
1656 /* File record #1 */
1657 ckp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1658 (void) ConvertUTF8toUnicode((UInt8
*)HFS_JOURNAL_FILE
, sizeof(ckp
->nodeName
.unicode
),
1659 ckp
->nodeName
.unicode
, &ckp
->nodeName
.length
);
1660 ckp
->nodeName
.length
= SWAP_BE16 (ckp
->nodeName
.length
);
1661 uBytes1
= sizeof(UniChar
) * SWAP_BE16 (ckp
->nodeName
.length
);
1662 ckp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
+ uBytes1
);
1663 ckp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1664 offset
+= SWAP_BE16 (ckp
->keyLength
) + 2;
1666 cfp
= (HFSPlusCatalogFile
*)((UInt8
*)buffer
+ offset
);
1667 cfp
->recordType
= SWAP_BE16 (kHFSPlusFileRecord
);
1668 cfp
->flags
= SWAP_BE16 (kHFSThreadExistsMask
);
1669 cfp
->fileID
= SWAP_BE32 (dp
->nextFreeFileID
);
1670 cfp
->createDate
= SWAP_BE32 (dp
->createDate
+ 1);
1671 cfp
->contentModDate
= SWAP_BE32 (dp
->createDate
+ 1);
1672 cfp
->textEncoding
= 0;
1674 cfp
->bsdInfo
.fileMode
= SWAP_BE16 (S_IFREG
);
1675 cfp
->bsdInfo
.ownerFlags
= (uint8_t) SWAP_BE16 (((uint16_t)UF_NODUMP
));
1676 cfp
->bsdInfo
.special
.linkCount
= SWAP_BE32(1);
1677 cfp
->userInfo
.fdType
= SWAP_BE32 (kJournalFileType
);
1678 cfp
->userInfo
.fdCreator
= SWAP_BE32 (kHFSPlusCreator
);
1679 cfp
->userInfo
.fdFlags
= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1680 cfp
->dataFork
.logicalSize
= SWAP_BE64 (dp
->journalSize
);
1681 cfp
->dataFork
.totalBlocks
= SWAP_BE32 ((dp
->journalSize
+dp
->blockSize
-1) / dp
->blockSize
);
1683 if (NEWFS_HFS_DEBUG
&& dp
->journalBlock
)
1684 journalBlock
= dp
->journalBlock
;
1686 journalBlock
= header
->journalInfoBlock
+ 1;
1687 cfp
->dataFork
.extents
[0].startBlock
= SWAP_BE32 (journalBlock
);
1688 cfp
->dataFork
.extents
[0].blockCount
= cfp
->dataFork
.totalBlocks
;
1690 offset
+= sizeof(HFSPlusCatalogFile
);
1691 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1692 nodename1
= &ckp
->nodeName
;
1694 /* File record #2 */
1695 ckp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1696 (void) ConvertUTF8toUnicode((UInt8
*)HFS_JOURNAL_INFO
, sizeof(ckp
->nodeName
.unicode
),
1697 ckp
->nodeName
.unicode
, &ckp
->nodeName
.length
);
1698 ckp
->nodeName
.length
= SWAP_BE16 (ckp
->nodeName
.length
);
1699 uBytes2
= sizeof(UniChar
) * SWAP_BE16 (ckp
->nodeName
.length
);
1700 ckp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
+ uBytes2
);
1701 ckp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1702 offset
+= SWAP_BE16 (ckp
->keyLength
) + 2;
1704 cfp
= (HFSPlusCatalogFile
*)((UInt8
*)buffer
+ offset
);
1705 cfp
->recordType
= SWAP_BE16 (kHFSPlusFileRecord
);
1706 cfp
->flags
= SWAP_BE16 (kHFSThreadExistsMask
);
1707 cfp
->fileID
= SWAP_BE32 (dp
->nextFreeFileID
+ 1);
1708 cfp
->createDate
= SWAP_BE32 (dp
->createDate
);
1709 cfp
->contentModDate
= SWAP_BE32 (dp
->createDate
);
1710 cfp
->textEncoding
= 0;
1712 cfp
->bsdInfo
.fileMode
= SWAP_BE16 (S_IFREG
);
1713 cfp
->bsdInfo
.ownerFlags
= (uint8_t) SWAP_BE16 (((uint16_t)UF_NODUMP
));
1714 cfp
->bsdInfo
.special
.linkCount
= SWAP_BE32(1);
1715 cfp
->userInfo
.fdType
= SWAP_BE32 (kJournalFileType
);
1716 cfp
->userInfo
.fdCreator
= SWAP_BE32 (kHFSPlusCreator
);
1717 cfp
->userInfo
.fdFlags
= SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1718 cfp
->dataFork
.logicalSize
= SWAP_BE64(dp
->blockSize
);;
1719 cfp
->dataFork
.totalBlocks
= SWAP_BE32(1);
1721 cfp
->dataFork
.extents
[0].startBlock
= SWAP_BE32 (header
->journalInfoBlock
);
1722 cfp
->dataFork
.extents
[0].blockCount
= cfp
->dataFork
.totalBlocks
;
1724 offset
+= sizeof(HFSPlusCatalogFile
);
1725 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1726 nodename2
= &ckp
->nodeName
;
1728 /* Thread record for file #1 */
1729 tkp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1730 tkp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
);
1731 tkp
->parentID
= SWAP_BE32 (dp
->nextFreeFileID
);
1732 tkp
->nodeName
.length
= 0;
1733 offset
+= SWAP_BE16 (tkp
->keyLength
) + 2;
1735 ctp
= (HFSPlusCatalogThread
*)((UInt8
*)buffer
+ offset
);
1736 ctp
->recordType
= SWAP_BE16 (kHFSPlusFileThreadRecord
);
1737 ctp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1738 bcopy(nodename1
, &ctp
->nodeName
, sizeof(UInt16
) + uBytes1
);
1739 offset
+= (sizeof(HFSPlusCatalogThread
)
1740 - (sizeof(ctp
->nodeName
.unicode
) - uBytes1
) );
1741 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1743 /* Thread record for file #2 */
1744 tkp
= (HFSPlusCatalogKey
*)((UInt8
*)buffer
+ offset
);
1745 tkp
->keyLength
= SWAP_BE16 (kHFSPlusCatalogKeyMinimumLength
);
1746 tkp
->parentID
= SWAP_BE32 (dp
->nextFreeFileID
+ 1);
1747 tkp
->nodeName
.length
= 0;
1748 offset
+= SWAP_BE16 (tkp
->keyLength
) + 2;
1750 ctp
= (HFSPlusCatalogThread
*)((UInt8
*)buffer
+ offset
);
1751 ctp
->recordType
= SWAP_BE16 (kHFSPlusFileThreadRecord
);
1752 ctp
->parentID
= SWAP_BE32 (kHFSRootFolderID
);
1753 bcopy(nodename2
, &ctp
->nodeName
, sizeof(UInt16
) + uBytes2
);
1754 offset
+= (sizeof(HFSPlusCatalogThread
)
1755 - (sizeof(ctp
->nodeName
.unicode
) - uBytes2
) );
1756 SETOFFSET(buffer
, nodeSize
, offset
, ++index
);
1763 * Initializes a B-tree map node and writes it out to disk.
1766 WriteMapNodes(const DriveInfo
*driveInfo
, UInt64 diskStart
, UInt32 firstMapNode
,
1767 UInt32 mapNodes
, UInt16 btNodeSize
, void *buffer
)
1769 UInt32 sectorsPerNode
;
1770 UInt32 mapRecordBytes
;
1772 BTNodeDescriptor
*nd
= (BTNodeDescriptor
*)buffer
;
1774 bzero(buffer
, btNodeSize
);
1776 nd
->kind
= kBTMapNode
;
1777 nd
->numRecords
= SWAP_BE16 (1);
1779 /* note: must belong word aligned (hence the extra -2) */
1780 mapRecordBytes
= btNodeSize
- sizeof(BTNodeDescriptor
) - 2*sizeof(SInt16
) - 2;
1782 SETOFFSET(buffer
, btNodeSize
, sizeof(BTNodeDescriptor
), 1);
1783 SETOFFSET(buffer
, btNodeSize
, sizeof(BTNodeDescriptor
) + mapRecordBytes
, 2);
1785 sectorsPerNode
= btNodeSize
/kBytesPerSector
;
1788 * Note - worst case (32MB alloc blk) will have
1789 * only 18 map nodes. So don't bother optimizing
1790 * this section to do multiblock writes!
1792 for (i
= 0; i
< mapNodes
; i
++) {
1793 if ((i
+ 1) < mapNodes
)
1794 nd
->fLink
= SWAP_BE32 (++firstMapNode
); /* point to next map node */
1796 nd
->fLink
= 0; /* this is the last map node */
1798 WriteBuffer(driveInfo
, diskStart
, btNodeSize
, buffer
);
1800 diskStart
+= sectorsPerNode
;
1805 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1806 * NOTE: IF buffer IS NULL, THIS FUNCTION WILL WRITE ZERO'S.
1808 * startingSector is in terms of 512-byte sectors.
1809 * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
1812 WriteBuffer(const DriveInfo
*driveInfo
, UInt64 startingSector
, UInt64 byteCount
,
1816 off_t physSector
= 0;
1817 off_t byteOffsetInPhysSector
;
1818 UInt32 numBytesToIO
;
1819 UInt32 numPhysSectorsToIO
;
1820 UInt32 tempbufSizeInPhysSectors
;
1822 UInt32 fd
= driveInfo
->fd
;
1823 UInt32 physSectorSize
= driveInfo
->physSectorSize
;
1824 void *tempbuf
= NULL
;
1825 int sectorSizeRatio
= driveInfo
->physSectorSize
/ kBytesPerSector
;
1826 int status
= 0; /* 0: no error; 1: alloc; 2: read; 3: write */
1828 if (0 == byteCount
) {
1832 /*@@@@@@@@@@ buffer allocation @@@@@@@@@@*/
1833 /* try a buffer size for optimal IO, __UP TO 4MB__. if that
1834 fails, then try with the minimum allowed buffer size, which
1835 is equal to physSectorSize */
1836 tempbufSizeInPhysSectors
= MIN ( (byteCount
- 1 + physSectorSize
) / physSectorSize
,
1837 driveInfo
->physSectorsPerIO
);
1839 tempbufSizeInPhysSectors
= MIN ( tempbufSizeInPhysSectors
, (4 * 1024 * 1024) / physSectorSize
);
1840 tempbufSize
= tempbufSizeInPhysSectors
* physSectorSize
;
1842 if ((tempbuf
= valloc(tempbufSize
)) == NULL
) {
1843 /* try allocation of smallest allowed size: one
1845 NOTE: the previous valloc tempbufSize might have
1846 already been one physical sector. we don't want to
1847 check if that was the case, so just try again.
1849 tempbufSizeInPhysSectors
= 1;
1850 tempbufSize
= physSectorSize
;
1851 if ((tempbuf
= valloc(tempbufSize
)) == NULL
) {
1857 /*@@@@@@@@@@ io @@@@@@@@@@*/
1858 sector
= driveInfo
->sectorOffset
+ startingSector
;
1859 physSector
= sector
/ sectorSizeRatio
;
1860 byteOffsetInPhysSector
= (sector
% sectorSizeRatio
) * kBytesPerSector
;
1862 while (byteCount
> 0) {
1863 numPhysSectorsToIO
= MIN ( (byteCount
- 1 + physSectorSize
) / physSectorSize
,
1864 tempbufSizeInPhysSectors
);
1865 numBytesToIO
= MIN(byteCount
, (unsigned)((numPhysSectorsToIO
* physSectorSize
) - byteOffsetInPhysSector
));
1867 /* if IO does not align with physical sector boundaries */
1868 if ((0 != byteOffsetInPhysSector
) || ((numBytesToIO
% physSectorSize
) != 0)) {
1869 if (pread(fd
, tempbuf
, numPhysSectorsToIO
* physSectorSize
, physSector
* physSectorSize
) < 0) {
1875 if (NULL
!= buffer
) {
1876 memcpy(tempbuf
+ byteOffsetInPhysSector
, buffer
, numBytesToIO
);
1879 bzero(tempbuf
+ byteOffsetInPhysSector
, numBytesToIO
);
1882 if (pwrite(fd
, tempbuf
, numPhysSectorsToIO
* physSectorSize
, physSector
* physSectorSize
) < 0) {
1883 warn("%s: pwrite(%d, %p, %zu, %lld)", __FUNCTION__
, fd
, tempbuf
, (size_t)(numPhysSectorsToIO
* physSectorSize
), (long long)(physSector
* physSectorSize
));
1888 byteOffsetInPhysSector
= 0;
1889 byteCount
-= numBytesToIO
;
1890 physSector
+= numPhysSectorsToIO
;
1891 if (NULL
!= buffer
) {
1892 buffer
+= numBytesToIO
;
1905 else if (2 == status
) {
1906 err(1, "read (sector %llu)", physSector
);
1908 else if (3 == status
) {
1909 err(1, "write (sector %llu)", physSector
);
1916 static UInt32
Largest( UInt32 a
, UInt32 b
, UInt32 c
, UInt32 d
)
1925 /* return max(a,c) */
1933 * UTCToLocal - convert from Mac OS GMT time to Mac OS local time
1935 static UInt32
UTCToLocal(UInt32 utcTime
)
1937 UInt32 localTime
= utcTime
;
1938 struct timezone timeZone
;
1939 struct timeval timeVal
;
1941 if (localTime
!= 0) {
1943 /* HFS volumes need timezone info to convert local to GMT */
1944 (void)gettimeofday( &timeVal
, &timeZone
);
1947 localTime
-= (timeZone
.tz_minuteswest
* 60);
1948 if (timeZone
.tz_dsttime
)
1955 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
1958 GetDefaultEncoding()
1960 struct passwd
*passwdp
;
1962 if ((passwdp
= getpwuid(0))) { // root account
1963 char buffer
[MAXPATHLEN
+ 1];
1966 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
1967 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
1969 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
1972 readSize
= read(fd
, buffer
, MAXPATHLEN
);
1973 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
1975 return strtol(buffer
, NULL
, 0);
1983 ConvertUTF8toUnicode(const UInt8
* source
, size_t bufsize
, UniChar
* unibuf
,
1992 targetEnd
= (UniChar
*)((UInt8
*)unibuf
+ bufsize
);
1994 while ((byte
= *source
++)) {
1996 /* check for single-byte ascii */
1998 if (byte
== ':') /* ':' is mapped to '/' */
2001 *target
++ = SWAP_BE16 (byte
);
2004 UInt8 seq
= (byte
>> 4);
2007 case 0xc: /* double-byte sequence (1100 and 1101) */
2009 ch
= (byte
& 0x1F) << 6; /* get 5 bits */
2010 if (((byte
= *source
++) >> 6) != 2)
2014 case 0xe: /* triple-byte sequence (1110) */
2015 ch
= (byte
& 0x0F) << 6; /* get 4 bits */
2016 if (((byte
= *source
++) >> 6) != 2)
2018 ch
+= (byte
& 0x3F); ch
<<= 6; /* get 6 bits */
2019 if (((byte
= *source
++) >> 6) != 2)
2024 return (EINVAL
); /* malformed sequence */
2027 ch
+= (byte
& 0x3F); /* get last 6 bits */
2029 if (target
>= targetEnd
)
2032 *target
++ = SWAP_BE16 (ch
);
2036 *charcount
= target
- unibuf
;
2042 * Derive the encoding hint for the given name.
2045 getencodinghint(unsigned char *name
)
2048 size_t buflen
= sizeof(int);
2052 if (getvfsbyname("hfs", &vfc
) < 0)
2056 mib
[1] = vfc
.vfc_typenum
;
2057 mib
[2] = HFS_ENCODINGHINT
;
2059 if (sysctl(mib
, 3, &hint
, &buflen
, name
, strlen((char *)name
) + 1) < 0)
2063 hint
= GetDefaultEncoding();
2068 /* Generate Volume UUID - similar to code existing in hfs_util */
2069 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2071 char randomInputBuffer
[26];
2072 unsigned char digest
[20];
2077 char sysctlstring
[128];
2079 double sysloadavg
[3];
2080 struct vmtotal sysvmtotal
;
2083 /* Initialize the SHA-1 context for processing: */
2084 SHA1_Init(&context
);
2086 /* Now process successive bits of "random" input to seed the process: */
2088 /* The current system's uptime: */
2090 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2092 /* The kernel's boot time: */
2094 mib
[1] = KERN_BOOTTIME
;
2095 datalen
= sizeof(sysdata
);
2096 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2097 SHA1_Update(&context
, &sysdata
, datalen
);
2099 /* The system's host id: */
2101 mib
[1] = KERN_HOSTID
;
2102 datalen
= sizeof(sysdata
);
2103 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2104 SHA1_Update(&context
, &sysdata
, datalen
);
2106 /* The system's host name: */
2108 mib
[1] = KERN_HOSTNAME
;
2109 datalen
= sizeof(sysctlstring
);
2110 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2111 SHA1_Update(&context
, sysctlstring
, datalen
);
2113 /* The running kernel's OS release string: */
2115 mib
[1] = KERN_OSRELEASE
;
2116 datalen
= sizeof(sysctlstring
);
2117 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2118 SHA1_Update(&context
, sysctlstring
, datalen
);
2120 /* The running kernel's version string: */
2122 mib
[1] = KERN_VERSION
;
2123 datalen
= sizeof(sysctlstring
);
2124 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2125 SHA1_Update(&context
, sysctlstring
, datalen
);
2127 /* The system's load average: */
2128 datalen
= sizeof(sysloadavg
);
2129 getloadavg(sysloadavg
, 3);
2130 SHA1_Update(&context
, &sysloadavg
, datalen
);
2132 /* The system's VM statistics: */
2135 datalen
= sizeof(sysvmtotal
);
2136 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2137 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2139 /* The current GMT (26 ASCII characters): */
2141 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2142 SHA1_Update(&context
, randomInputBuffer
, 26);
2144 /* Pad the accumulated input and extract the final digest hash: */
2145 SHA1_Final(digest
, &context
);
2147 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2148 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));