]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/util.c
hfs-407.30.1.tar.gz
[apple/hfs.git] / CopyHFSMeta / util.c
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__
23 DeviceInfo_t *
24 OpenDevice(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 // Open with a shared lock if we're not debugging, since the hfs estimator api is invoked from the booted system
95 fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_SHLOCK));
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__
122 VolumeDescriptor_t *
123 VolumeInfo(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__
156 int
157 CompareVolumeHeaders(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 */
178 static int
179 IsValidSigWord(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__
190 int
191 AddHeaders(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
222 done:
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__
233 void
234 AddJournal(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__
280 void
281 AddFileExtents(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
319 static int
320 ScanCatalogNode(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
373 static int
374 ScanAttrNode(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__
440 int
441 FindOtherMetadata(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
556 done:
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__
566 ssize_t
567 UnalignedRead(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
598 done:
599 free(tmpbuf);
600 return nread;
601 }
602
603 __private_extern__
604 void
605 ReleaseDeviceInfo(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__
619 void
620 ReleaseVolumeDescriptor(VolumeDescriptor_t *vdp)
621 {
622 if (vdp)
623 free(vdp); // No contained pointers!
624 return;
625 }
626
627 __private_extern__
628 void
629 ReleaseVolumeObjects(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 }