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