1#include <stdio.h> 2#include <stdlib.h> 3#include <unistd.h> 4#include <string.h> 5#include <fcntl.h> 6#include <err.h> 7#include <errno.h> 8#include <sys/stat.h> 9#include <sys/disk.h> 10#include <sys/sysctl.h> 11#include <hfs/hfs_mount.h> 12#include <Block.h> 13#include "hfsmeta.h" 14#include "Data.h" 15 16/* 17 * Open the source device. In addition to opening the device, 18 * this also attempts to flush the journal, and then sets up a 19 * DeviceInfo_t object that will be used when doing the actual 20 * reading. 21 */ 22__private_extern__ 23DeviceInfo_t * 24OpenDevice(const char *devname, int flushJournal) 25{ 26 DeviceInfo_t *retval = NULL; 27 int fd; 28 DeviceInfo_t dev = { 0 }; 29 struct stat sb; 30 struct vfsconf vfc; 31 32 if (stat(devname, &sb) == -1) { 33 err(kBadExit, "cannot open device %s", devname); 34 } 35 /* 36 * Attempt to flush the journal if requested. If it fails, we just warn, but don't abort. 37 */ 38 if (flushJournal && getvfsbyname("hfs", &vfc) == 0) { 39 int rv; 40 int mib[4]; 41 char block_device[MAXPATHLEN+1]; 42 int jfd; 43 44 /* 45 * The journal replay code, sadly, requires a block device. 46 * So we need to go from the raw device to block device, if 47 * necessary. 48 */ 49 if (strncmp(devname, "/dev/rdisk", 10) == 0) { 50 snprintf(block_device, sizeof(block_device), "/dev/%s", devname+6); 51 } else { 52 snprintf(block_device, sizeof(block_device), "%s", devname); 53 } 54 jfd = open(block_device, O_RDWR); 55 if (jfd == -1) { 56 warn("Cannot open block device %s for read-write", block_device); 57 } else { 58 mib[0] = CTL_VFS; 59 mib[1] = vfc.vfc_typenum; 60 mib[2] = HFS_REPLAY_JOURNAL; 61 mib[3] = jfd; 62 if (debug) 63 fprintf(stderr, "about to replay journal\n"); 64 rv = sysctl(mib, 4, NULL, NULL, NULL, 0); 65 if (rv == -1) { 66 warn("cannot replay journal"); 67 } 68 /* This is probably not necessary, but we couldn't prove it. */ 69 (void)fcntl(jfd, F_FULLFSYNC, 0); 70 close(jfd); 71 } 72 } 73 /* 74 * We only allow a character device (e.g., /dev/rdisk1s2) 75 * If we're given a non-character device, we'll try to turn 76 * into a character device assuming a name pattern of /dev/rdisk* 77 */ 78 if ((sb.st_mode & S_IFMT) == S_IFCHR) { 79 dev.devname = strdup(devname); 80 } else if (strncmp(devname, "/dev/disk", 9) == 0) { 81 // Turn "/dev/diskFoo" into "/dev/rdiskFoo" 82 char tmpname[strlen(devname) + 2]; 83 (void)snprintf(tmpname, sizeof(tmpname), "/dev/rdisk%s", devname + sizeof("/dev/disk") - 1); 84 if (stat(tmpname, &sb) == -1) { 85 err(kBadExit, "cannot open raw device %s", tmpname); 86 } 87 if ((sb.st_mode & S_IFMT) != S_IFCHR) { 88 errx(kBadExit, "raw device %s is not a raw device", tmpname); 89 } 90 dev.devname = strdup(tmpname); 91 } else { 92 errx(kBadExit, "device name `%s' does not fit pattern", devname); 93 } 94 // Only use an exclusive open if we're not debugging. 95 fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_EXLOCK)); 96 if (fd == -1) { 97 err(kBadExit, "cannot open raw device %s", dev.devname); 98 } 99 // Get the block size and counts for the device. 100 if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev.blockSize) == -1) { 101 dev.blockSize = 512; // Sane default, I hope 102 } 103 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &dev.blockCount) == -1) { 104 err(kBadExit, "cannot get size of device %s", dev.devname); 105 } 106 107 dev.size = dev.blockCount * dev.blockSize; 108 dev.fd = fd; 109 110 retval = malloc(sizeof(*retval)); 111 if (retval == NULL) { 112 err(kBadExit, "cannot allocate device info structure"); 113 } 114 *retval = dev; 115 return retval; 116} 117 118/* 119 * Get the header and alternate header for a device. 120 */ 121__private_extern__ 122VolumeDescriptor_t * 123VolumeInfo(DeviceInfo_t *devp) 124{ 125 uint8_t buffer[devp->blockSize]; 126 VolumeDescriptor_t *vdp = NULL, vd = { 0 }; 127 ssize_t rv; 128 129 vd.priOffset = 1024; // primary volume header is at 1024 bytes 130 vd.altOffset = devp->size - 1024; // alternate header is 1024 bytes from the end 131 132 rv = GetBlock(devp, vd.priOffset, buffer); 133 if (rv == -1) { 134 err(kBadExit, "cannot get primary volume header for device %s", devp->devname); 135 } 136 vd.priHeader = *(HFSPlusVolumeHeader*)buffer; 137 138 rv = GetBlock(devp, vd.altOffset, buffer); 139 if (rv == -1) { 140 err(kBadExit, "cannot get alternate volume header for device %s", devp->devname); 141 } 142 vd.altHeader = *(HFSPlusVolumeHeader*)buffer; 143 144 vdp = malloc(sizeof(*vdp)); 145 *vdp = vd; 146 147 return vdp; 148} 149 150/* 151 * Compare the primary and alternate volume headers. 152 * We only care about the "important" bits (namely, the 153 * portions related to extents). 154 */ 155__private_extern__ 156int 157CompareVolumeHeaders(VolumeDescriptor_t *vdp) 158{ 159 int retval = -1; 160 161#define CMP_FILE(v, f) memcmp(&(v)->priHeader.f, &(v)->altHeader.f, sizeof(v->priHeader.f)) 162 163 if (vdp && 164 vdp->priHeader.journalInfoBlock == vdp->altHeader.journalInfoBlock && 165 CMP_FILE(vdp, allocationFile) == 0 && 166 CMP_FILE(vdp, extentsFile) == 0 && 167 CMP_FILE(vdp, catalogFile) == 0 && 168 CMP_FILE(vdp, attributesFile) == 0 && 169 CMP_FILE(vdp, startupFile) == 0) 170 retval = 0; 171#undef CMP_FILE 172 return retval; 173} 174 175/* 176 * Only two (currently) types of signatures are valid: H+ and HX. 177 */ 178static int 179IsValidSigWord(uint16_t word) { 180 if (word == kHFSPlusSigWord || 181 word == kHFSXSigWord) 182 return 1; 183 return 0; 184} 185 186/* 187 * Add the volume headers to the in-core volume information list. 188 */ 189__private_extern__ 190int 191AddHeaders(VolumeObjects_t *vop, int roundBlock) 192{ 193 int retval = 1; 194 HFSPlusVolumeHeader *hp; 195 uint8_t buffer[vop->devp->blockSize]; 196 ssize_t rv; 197 198 hp = &vop->vdp->priHeader; 199 200 if (IsValidSigWord(S16(hp->signature)) == 0) { 201 warnx("primary volume header signature = %x, invalid", S16(hp->signature)); 202 retval = 0; 203 } 204 if (roundBlock) { 205 AddExtent(vop, 1024 / vop->devp->blockSize, vop->devp->blockSize); 206 } else { 207 AddExtent(vop, 1024, 512); 208 } 209 210 hp = &vop->vdp->altHeader; 211 212 if (IsValidSigWord(S16(hp->signature)) == 0) { 213 warnx("alternate volume header signature = %x, invalid", S16(hp->signature)); 214 retval = 0; 215 } 216 if (roundBlock) { 217 AddExtent(vop, (vop->vdp->altOffset / vop->devp->blockSize) * vop->devp->blockSize, vop->devp->blockSize); 218 } else { 219 AddExtent(vop, vop->vdp->altOffset, 512); 220 } 221 222done: 223 return retval; 224} 225 226/* 227 * Add the journal information to the in-core volume list. 228 * This means the journal info block, the journal itself, and 229 * the contents of the same as described by the alternate volume 230 * header (if it's different from the primary volume header). 231 */ 232__private_extern__ 233void 234AddJournal(VolumeObjects_t *vop) 235{ 236 DeviceInfo_t *devp = vop->devp; 237 uint8_t buffer[devp->blockSize]; 238 ssize_t rv; 239 HFSPlusVolumeHeader *php, *ahp; 240 JournalInfoBlock *jib; 241 242 php = &vop->vdp->priHeader; 243 ahp = &vop->vdp->altHeader; 244 245 if (php->journalInfoBlock) { 246 off_t jOffset = (off_t)S32(php->journalInfoBlock) * S32(php->blockSize); 247 rv = GetBlock(devp, jOffset, buffer); 248 if (rv == -1) { 249 err(kBadExit, "cannot get primary header's copy of journal info block"); 250 } 251 AddExtent(vop, jOffset, sizeof(buffer)); 252 jib = (JournalInfoBlock*)buffer; 253 if (S32(jib->flags) & kJIJournalInFSMask) { 254 AddExtent(vop, S64(jib->offset), S64(jib->size)); 255 } 256 } 257 258 if (ahp->journalInfoBlock && 259 ahp->journalInfoBlock != php->journalInfoBlock) { 260 off_t jOffset = (off_t)S32(ahp->journalInfoBlock) * S32(ahp->blockSize); 261 rv = GetBlock(devp, jOffset, buffer); 262 if (rv == -1) { 263 err(kBadExit, "cannot get alternate header's copy of journal info block"); 264 } 265 AddExtent(vop, jOffset, sizeof(buffer)); 266 jib = (JournalInfoBlock*)buffer; 267 if (S32(jib->flags) & kJIJournalInFSMask) { 268 AddExtent(vop, S64(jib->offset), S64(jib->size)); 269 } 270 } 271 272} 273 274/* 275 * Add the extents for the special files in the volume header. Compare 276 * them with the alternate volume header's versions, and if they're different, 277 * add that as well. 278 */ 279__private_extern__ 280void 281AddFileExtents(VolumeObjects_t *vop) 282{ 283 int useAlt = 0; 284#define ADDEXTS(vop, file, fid) \ 285 do { \ 286 off_t pSize = S32(vop->vdp->priHeader.blockSize); \ 287 off_t aSize = S32(vop->vdp->altHeader.blockSize); \ 288 int i; \ 289 if (debug) printf("Adding " #file " extents\n"); \ 290 for (i = 0; i < kHFSPlusExtentDensity; i++) { \ 291 HFSPlusExtentDescriptor *ep = &vop->vdp->priHeader. file .extents[i]; \ 292 HFSPlusExtentDescriptor *ap = &vop->vdp->altHeader. file .extents[i]; \ 293 if (debug) printf("\tExtent <%u, %u>\n", S32(ep->startBlock), S32(ep->blockCount)); \ 294 if (ep->startBlock && ep->blockCount) { \ 295 AddExtentForFile(vop, S32(ep->startBlock) * pSize, S32(ep->blockCount) * pSize, fid); \ 296 if (memcmp(ep, ap, sizeof(*ep)) != 0) { \ 297 AddExtentForFile(vop, S32(ap->startBlock) * aSize, S32(ap->blockCount) * aSize, fid); \ 298 useAlt = 1; \ 299 } \ 300 } \ 301 } \ 302 } while (0) 303 304 ADDEXTS(vop, allocationFile, kHFSAllocationFileID); 305 ADDEXTS(vop, extentsFile, kHFSExtentsFileID); 306 ADDEXTS(vop, catalogFile, kHFSCatalogFileID); 307 ADDEXTS(vop, attributesFile, kHFSAttributesFileID); 308 ADDEXTS(vop, startupFile, kHFSStartupFileID); 309 310#undef ADDEXTS 311 312 ScanExtents(vop, 0); 313 if (useAlt) 314 ScanExtents(vop, useAlt); 315 316 return; 317} 318 319static int 320ScanCatalogNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) 321{ 322 BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; 323 uint16_t *indices = (uint16_t*)(buffer + nodeSize); 324 size_t counter; 325 off_t blockSize = S32(vop->vdp->priHeader.blockSize); 326 int retval = 0; 327 328 if (ndp->kind != kBTLeafNode) // Skip if it's not a leaf node 329 return 0; 330 331 if (debug) 332 fprintf(stderr, "%s: scanning catalog node\n", __FUNCTION__); 333 334 for (counter = 1; counter <= S16(ndp->numRecords); counter++) { 335 // Need to get past the end of the key 336 uint16_t recOffset = S16(indices[-counter]); 337 HFSPlusCatalogKey *keyp = (HFSPlusCatalogKey*)(buffer + recOffset); 338 size_t keyLength = S16(keyp->keyLength); 339 // Add two because the keyLength field is not included. 340 HFSPlusCatalogFile *fp = (HFSPlusCatalogFile*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); 341 342 if (S16(fp->recordType) != kHFSPlusFileRecord) { 343 if (debug) 344 fprintf(stderr, "%s: skipping node record %zu because it is type %#x, at offset %u keyLength %zu\n", __FUNCTION__, counter, S16(fp->recordType), recOffset, keyLength); 345 continue; 346 } 347 348 if (debug) 349 fprintf(stderr, "%s: node record %zu, file id = %u\n", __FUNCTION__, counter, S32(fp->fileID)); 350 if (S32(fp->userInfo.fdType) == kSymLinkFileType && 351 S32(fp->userInfo.fdCreator) == kSymLinkCreator) { 352 unsigned int fid = S32(fp->fileID); 353 HFSPlusExtentDescriptor *extPtr = fp->dataFork.extents; 354 int i; 355 356 for (i = 0; i < 8; i++) { 357 if (extPtr[i].startBlock && 358 extPtr[i].blockCount) { 359 off_t start = blockSize * S32(extPtr[i].startBlock); 360 off_t length = blockSize * S32(extPtr[i].blockCount); 361 retval = handler(fid, start, length); 362 if (retval != 0) 363 return retval; 364 } else { 365 break; 366 } 367 } 368 } 369 } 370 return retval; 371} 372 373static int 374ScanAttrNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) 375{ 376 BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; 377 uint16_t *indices = (uint16_t*)(buffer + nodeSize); 378 size_t counter; 379 off_t blockSize = S32(vop->vdp->priHeader.blockSize); 380 int retval = 0; 381 382 if (ndp->kind != kBTLeafNode) 383 return 0; // Skip if it's not a leaf node 384 385 /* 386 * Look for records of type kHFSPlusForkData and kHFSPlusAttrExtents 387 */ 388 for (counter = 1; counter <= S16(ndp->numRecords); counter++) { 389 // Need to get past the end of the key 390 unsigned int fid; 391 HFSPlusAttrKey *keyp = (HFSPlusAttrKey*)(buffer + S16(indices[-counter])); 392 size_t keyLength = S16(keyp->keyLength); 393 // Add two because the keyLength field is not included. 394 HFSPlusAttrRecord *ap = (HFSPlusAttrRecord*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); 395 HFSPlusExtentDescriptor *theExtents = NULL; 396 switch (S32(ap->recordType)) { 397 case kHFSPlusAttrForkData: 398 theExtents = ap->forkData.theFork.extents; 399 break; 400 case kHFSPlusAttrExtents: 401 theExtents = ap->overflowExtents.extents; 402 break; 403 default: 404 break; 405 } 406 if (theExtents != NULL) { 407 HFSPlusExtentDescriptor *extPtr = theExtents; 408 int i; 409 fid = S32(keyp->fileID); 410 411 for (i = 0; i < 8; i++) { 412 if (extPtr[i].startBlock && 413 extPtr[i].blockCount) { 414 off_t start = blockSize * S32(extPtr[i].startBlock); 415 off_t length = blockSize * S32(extPtr[i].blockCount); 416 retval = handler(fid, start, length); 417 if (retval != 0) 418 return retval; 419 } else { 420 break; 421 } 422 } 423 } 424 } 425 return retval; 426} 427 428 429/* 430 * Given a VolumeObject_t, search for the other metadata that 431 * aren't described by the system files, but rather in the 432 * system files. This includes symbolic links, and large EA 433 * extents. We can do this at one of two times -- while copying 434 * the data, or while setting up the list of extents. The 435 * former is going to be more efficient, but the latter will 436 * mean the estimates and continuation will be less likely to 437 * be wrong as we add extents to the list. 438 */ 439__private_extern__ 440int 441FindOtherMetadata(VolumeObjects_t *vop, extent_handler_t handler) 442{ 443 size_t catNodeSize = 0, attrNodeSize = 0; 444 off_t node0_location = 0; 445 uint8_t *tBuffer; 446 BTHeaderRec *hdp; 447 BTNodeDescriptor *ndp; 448 int retval = 0; 449 450 tBuffer = calloc(1, vop->devp->blockSize); 451 if (tBuffer == NULL) { 452 warn("Could not allocate memory to collect extra metadata"); 453 goto done; 454 } 455 /* 456 * First, do the catalog file 457 */ 458 if (vop->vdp->priHeader.catalogFile.logicalSize) { 459 460 node0_location = S32(vop->vdp->priHeader.catalogFile.extents[0].startBlock); 461 node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); 462 if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { 463 warn("Could not read catalog header node"); 464 } else { 465 ndp = (BTNodeDescriptor*)tBuffer; 466 hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); 467 468 if (ndp->kind != kBTHeaderNode) { 469 warnx("Did not read header node for catalog as expected"); 470 } else { 471 catNodeSize = S16(hdp->nodeSize); 472 } 473 } 474 } 475 /* 476 * Now, the attributes file. 477 */ 478 if (vop->vdp->priHeader.attributesFile.logicalSize) { 479 480 node0_location = S32(vop->vdp->priHeader.attributesFile.extents[0].startBlock); 481 node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); 482 if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { 483 warn("Could not read attributes file header node"); 484 } else { 485 ndp = (BTNodeDescriptor*)tBuffer; 486 hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); 487 488 if (ndp->kind != kBTHeaderNode) { 489 warnx("Did not read header node for attributes file as expected"); 490 } else { 491 attrNodeSize = S16(hdp->nodeSize); 492 } 493 } 494 } 495 if (debug) 496 fprintf(stderr, "Catalog node size = %zu, attributes node size = %zu\n", catNodeSize, attrNodeSize); 497 498 /* 499 * We start reading the extents now. 500 * 501 * This is a lot of duplicated code, unfortunately. 502 */ 503 ExtentList_t *exts; 504 for (exts = vop->list; 505 exts; 506 exts = exts->next) { 507 size_t indx; 508 509 for (indx = 0; indx < exts->count; indx++) { 510 off_t start = exts->extents[indx].base; 511 off_t len = exts->extents[indx].length; 512 off_t nread = 0; 513 if (exts->extents[indx].fid == 0) { 514 continue; // Unknown file, skip 515 } else { 516 if (debug) fprintf(stderr, "%s: fid = %u, start = %llu, len = %llu\n", __FUNCTION__, exts->extents[indx].fid, start, len); 517 while (nread < len) { 518 size_t bufSize; 519 uint8_t *buffer; 520 bufSize = MIN(len - nread, 1024 * 1024); // Read 1mbyte max 521 buffer = calloc(1, bufSize); 522 if (buffer == NULL) { 523 warn("Cannot allocate %zu bytes for buffer, skipping node scan", bufSize); 524 } else { 525 ssize_t t = UnalignedRead(vop->devp, buffer, bufSize, start + nread); 526 if (t != bufSize) { 527 warn("Attempted to read %zu bytes, only read %zd, skipping node scan", bufSize, t); 528 } else { 529 uint8_t *curPtr = buffer, *endPtr = (buffer + bufSize); 530 size_t nodeSize = 0; 531 int (*func)(VolumeObjects_t *, uint8_t *, size_t, extent_handler_t) = NULL; 532 if (exts->extents[indx].fid == kHFSCatalogFileID) { 533 func = ScanCatalogNode; 534 nodeSize = catNodeSize; 535 } else if (exts->extents[indx].fid == kHFSAttributesFileID) { 536 func = ScanAttrNode; 537 nodeSize = attrNodeSize; 538 } 539 if (func) { 540 while (curPtr < endPtr && retval == 0) { 541 retval = (*func)(vop, curPtr, nodeSize, handler); 542 curPtr += nodeSize; 543 } 544 } 545 } 546 free(buffer); 547 } 548 if (retval != 0) 549 goto done; 550 nread += bufSize; 551 } 552 } 553 } 554 } 555 556done: 557 if (tBuffer) 558 free(tBuffer); 559 return retval; 560} 561 562/* 563 * Perform a (potentially) unaligned read from a given input device. 564 */ 565__private_extern__ 566ssize_t 567UnalignedRead(DeviceInfo_t *devp, void *buffer, size_t size, off_t offset) 568{ 569 ssize_t nread = -1; 570 size_t readSize = ((size + devp->blockSize - 1) / devp->blockSize) * devp->blockSize; 571 off_t baseOffset = (offset / devp->blockSize) * devp->blockSize; 572 size_t off = offset - baseOffset; 573 char *tmpbuf = NULL; 574 575 if ((baseOffset == offset) && (readSize == size)) { 576 /* 577 * The read is already properly aligned, so call pread. 578 */ 579 return pread(devp->fd, buffer, size, offset); 580 } 581 582 tmpbuf = malloc(readSize); 583 if (!tmpbuf) { 584 goto done; 585 } 586 587 nread = pread(devp->fd, tmpbuf, readSize, baseOffset); 588 if (nread == -1) { 589 goto done; 590 } 591 592 nread -= off; 593 if (nread > (ssize_t)size) { 594 nread = size; 595 } 596 memcpy(buffer, tmpbuf + off, nread); 597 598done: 599 free(tmpbuf); 600 return nread; 601} 602 603__private_extern__ 604void 605ReleaseDeviceInfo(DeviceInfo_t *devp) 606{ 607 if (devp) { 608 if (devp->fd != -1) { 609 close(devp->fd); 610 } 611 if (devp->devname) 612 free(devp->devname); 613 free(devp); 614 } 615 return; 616} 617 618__private_extern__ 619void 620ReleaseVolumeDescriptor(VolumeDescriptor_t *vdp) 621{ 622 if (vdp) 623 free(vdp); // No contained pointers! 624 return; 625} 626 627__private_extern__ 628void 629ReleaseVolumeObjects(VolumeObjects_t *vop) 630{ 631 if (vop) { 632 if (vop->devp) { 633 ReleaseDeviceInfo(vop->devp); 634 } 635 if (vop->vdp) { 636 ReleaseVolumeDescriptor(vop->vdp); 637 } 638 ExtentList_t *extList; 639 for (extList = vop->list; 640 extList; 641 ) { 642 ExtentList_t *next = extList->next; 643 free(extList); 644 extList = next; 645 } 646 free(vop); 647 } 648} 649