From 41dcebd9dbc8544645cb2f11dbddca553bffd7dd Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 24 Oct 2014 18:05:37 +0000 Subject: [PATCH] hfs-285.tar.gz --- CopyHFSMeta/CopyHFSMeta.entitlements | 8 + CopyHFSMeta/Data.h | 37 +- CopyHFSMeta/ScanExtents.c | 3 +- CopyHFSMeta/SparseBundle.c | 43 +- CopyHFSMeta/hfsmeta.h | 2 + CopyHFSMeta/main.c | 318 +----------- CopyHFSMeta/misc.c | 18 +- CopyHFSMeta/util.c | 648 +++++++++++++++++++++++++ fsck_hfs/cache.c | 5 +- fsck_hfs/dfalib/SControl.c | 16 +- fsck_hfs/dfalib/SUtils.c | 3 +- fsck_hfs/dfalib/fsck_journal.c | 26 + fsck_hfs/fsck_hfs.entitlements | 8 + fsck_hfs/fsck_hfs.h | 2 + fsck_hfs/utilities.c | 9 +- fstyp_hfs/fstyp_hfs.entitlements | 8 + hfs.xcodeproj/project.pbxproj | 159 ++++++ hfs_util/hfs_util.entitlements | 8 + hfs_util/hfsutil_jnl.c | 24 +- hfs_util/hfsutil_main.c | 8 +- libhfs_metadata/iterate_hfs_metadata.c | 113 +++++ libhfs_metadata/iterate_hfs_metadata.h | 11 + mount_hfs/mount_hfs.c | 102 ++-- mount_hfs/mount_hfs.entitlements | 8 + newfs_hfs/makehfs.c | 22 +- newfs_hfs/newfs_hfs.c | 17 +- newfs_hfs/newfs_hfs.entitlements | 8 + newfs_hfs/newfs_hfs.h | 11 +- 28 files changed, 1248 insertions(+), 397 deletions(-) create mode 100644 CopyHFSMeta/CopyHFSMeta.entitlements create mode 100644 CopyHFSMeta/util.c create mode 100644 fsck_hfs/fsck_hfs.entitlements create mode 100644 fstyp_hfs/fstyp_hfs.entitlements create mode 100644 hfs_util/hfs_util.entitlements create mode 100644 libhfs_metadata/iterate_hfs_metadata.c create mode 100644 libhfs_metadata/iterate_hfs_metadata.h create mode 100644 mount_hfs/mount_hfs.entitlements create mode 100644 newfs_hfs/newfs_hfs.entitlements diff --git a/CopyHFSMeta/CopyHFSMeta.entitlements b/CopyHFSMeta/CopyHFSMeta.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/CopyHFSMeta/CopyHFSMeta.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/CopyHFSMeta/Data.h b/CopyHFSMeta/Data.h index 6f183bf..a6dc8c6 100644 --- a/CopyHFSMeta/Data.h +++ b/CopyHFSMeta/Data.h @@ -1,6 +1,27 @@ #ifndef _DATA_H # define _DATA_H +# include +/* + * Exit status values. We use some errno values because + * they are convenient. + * kGoodExit: we finished copying, and no problems. + * kNoSpaceExit: Not enough space for the skeleton copy. + * kCopyIOExit: An I/O error occurred. This may not be fatal, + * as it may just mean the source device went away. We + * can continue later, perhaps. + * kIntrExit: The copying was interrupted. As above, this may + * not be fatal. + * kBadExit: Any other problem. + */ +enum { + kGoodExit = 0, + kNoSpaceExit = ENOSPC, + kCopyIOExit = EIO, + kIntrExit = EINTR, + kBadExit = 1, +}; + /* * Data-manipulating functions and structures, used to * create the skeleton copy. @@ -16,6 +37,7 @@ struct IOWrapper; struct Extents { off_t base; off_t length; + unsigned int fid; // Optional, may not be set }; typedef struct Extents Extents_t; @@ -31,7 +53,6 @@ struct ExtentList { struct ExtentList *next; }; typedef struct ExtentList ExtentList_t; - /* * The in-core description of the volume: an input source, * a description of the volume, the linked list of extents, @@ -47,11 +68,25 @@ struct VolumeObjects { }; typedef struct VolumeObjects VolumeObjects_t; +typedef int (^extent_handler_t)(int fid, off_t start, off_t len); + extern VolumeObjects_t *InitVolumeObject(struct DeviceInfo *devp, struct VolumeDescriptor *vdp); extern int AddExtent(VolumeObjects_t *vop, off_t start, off_t length); +extern int AddExtentForFile(VolumeObjects_t *vop, off_t start, off_t length, unsigned int fid); extern void PrintVolumeObject(VolumeObjects_t*); extern int CopyObjectsToDest(VolumeObjects_t*, struct IOWrapper *wrapper, off_t skip); extern void WriteGatheredData(const char *, VolumeObjects_t*); +extern struct DeviceInfo *OpenDevice(const char *, int); +extern struct VolumeDescriptor *VolumeInfo(struct DeviceInfo *); +extern int AddHeaders(VolumeObjects_t *, int); +extern void AddJournal(VolumeObjects_t *); +extern void AddFileExtents(VolumeObjects_t *); +extern int FindOtherMetadata(VolumeObjects_t *, extent_handler_t); +extern int CompareVolumeHeaders(struct VolumeDescriptor*); + +void ReleaseDeviceInfo(struct DeviceInfo *); +void ReleaseVolumeDescriptor(struct VolumeDescriptor*); +void ReleaseVolumeObjects(VolumeObjects_t *); #endif /* _DATA_H */ diff --git a/CopyHFSMeta/ScanExtents.c b/CopyHFSMeta/ScanExtents.c index 5910e4a..470a699 100644 --- a/CopyHFSMeta/ScanExtents.c +++ b/CopyHFSMeta/ScanExtents.c @@ -166,7 +166,7 @@ ScanNode(VolumeObjects_t *vop, uint8_t *nodePtr, size_t nodeSize, off_t blockSiz off_t start = S32((*datap)[i].startBlock) * (off_t)blockSize; off_t len = S32((*datap)[i].blockCount) * (off_t)blockSize; if (start && len) - AddExtent(vop, start, len); + AddExtentForFile(vop, start, len, S32(keyp->fileID)); } } } @@ -179,6 +179,7 @@ ScanNode(VolumeObjects_t *vop, uint8_t *nodePtr, size_t nodeSize, off_t blockSiz * looking for system-file extents (those with a CNID < 16). If useAltHdr * is set, it'll use the extents overflow descriptor in the alternate header. */ +__private_extern__ int ScanExtents(VolumeObjects_t *vop, int useAltHdr) { diff --git a/CopyHFSMeta/SparseBundle.c b/CopyHFSMeta/SparseBundle.c index a7c5668..455903e 100644 --- a/CopyHFSMeta/SparseBundle.c +++ b/CopyHFSMeta/SparseBundle.c @@ -23,9 +23,11 @@ * no encryption. */ -#define MIN(a, b) \ +#ifndef MIN +# define MIN(a, b) \ ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \ __a < __b ? __a : __b; }) +#endif /* * Context for the sparse bundle routines. The path name, @@ -61,45 +63,6 @@ static const char *bundlePrototype = "\n" "\n"; -/* - * Perform a (potentially) unaligned read from a given input device. - */ -static ssize_t -UnalignedRead(DeviceInfo_t *devp, void *buffer, size_t size, off_t offset) -{ - ssize_t nread = -1; - size_t readSize = ((size + devp->blockSize - 1) / devp->blockSize) * devp->blockSize; - off_t baseOffset = (offset / devp->blockSize) * devp->blockSize; - size_t off = offset - baseOffset; - char *tmpbuf = NULL; - - if ((baseOffset == offset) && (readSize == size)) { - /* - * The read is already properly aligned, so call pread. - */ - return pread(devp->fd, buffer, size, offset); - } - - tmpbuf = malloc(readSize); - if (!tmpbuf) { - goto done; - } - - nread = pread(devp->fd, tmpbuf, readSize, baseOffset); - if (nread == -1) { - goto done; - } - - nread -= off; - if (nread > (ssize_t)size) { - nread = size; - } - memcpy(buffer, tmpbuf + off, nread); - -done: - free(tmpbuf); - return nread; -} /* * Read from a sparse bundle. If the band file doesn't exist, or is shorter than diff --git a/CopyHFSMeta/hfsmeta.h b/CopyHFSMeta/hfsmeta.h index 52d9540..7485c49 100644 --- a/CopyHFSMeta/hfsmeta.h +++ b/CopyHFSMeta/hfsmeta.h @@ -63,6 +63,8 @@ struct IOWrapper { }; typedef struct IOWrapper IOWrapper_t; +extern ssize_t UnalignedRead(DeviceInfo_t *, void *, size_t, off_t); + extern int debug, verbose, printProgress; #endif /* _HFS_META_H */ diff --git a/CopyHFSMeta/main.c b/CopyHFSMeta/main.c index 2c5b69b..d562d7f 100644 --- a/CopyHFSMeta/main.c +++ b/CopyHFSMeta/main.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "hfsmeta.h" #include "Data.h" #include "Sparse.h" @@ -27,166 +28,16 @@ int verbose; int debug; int printProgress; -/* - * Exit status values. We use some errno values because - * they are convenient. - * kGoodExit: we finished copying, and no problems. - * kNoSpaceExit: Not enough space for the skeleton copy. - * kCopyIOExit: An I/O error occurred. This may not be fatal, - * as it may just mean the source device went away. We - * can continue later, perhaps. - * kIntrExit: The copying was interrupted. As above, this may - * not be fatal. - * kBadExit: Any other problem. - */ -enum { - kGoodExit = 0, - kNoSpaceExit = ENOSPC, - kCopyIOExit = EIO, - kIntrExit = EINTR, - kBadExit = 1, -}; - -/* - * Open the source device. In addition to opening the device, - * this also attempts to flush the journal, and then sets up a - * DeviceInfo_t object that will be used when doing the actual - * reading. - */ -static DeviceInfo_t * -OpenDevice(const char *devname) -{ - char *rawname; - DeviceInfo_t *retval = NULL; - int fd; - DeviceInfo_t dev = { 0 }; - struct stat sb; - struct vfsconf vfc; - - if (stat(devname, &sb) == -1) { - err(kBadExit, "cannot open device %s", devname); - } - /* - * Attempt to flush the journal. If it fails, we just warn, but don't abort. - */ - if (getvfsbyname("hfs", &vfc) == 0) { - int rv; - int mib[4]; - char block_device[MAXPATHLEN+1]; - int jfd; - - /* - * The journal replay code, sadly, requires a block device. - * So we need to go from the raw device to block device, if - * necessary. - */ - if (strncmp(devname, "/dev/rdisk", 10) == 0) { - snprintf(block_device, sizeof(block_device), "/dev/%s", devname+6); - } else { - snprintf(block_device, sizeof(block_device), "%s", devname); - } - jfd = open(block_device, O_RDWR); - if (jfd == -1) { - warn("Cannot open block device %s for read-write", block_device); - } else { - mib[0] = CTL_VFS; - mib[1] = vfc.vfc_typenum; - mib[2] = HFS_REPLAY_JOURNAL; - mib[3] = jfd; - if (debug) - fprintf(stderr, "about to replay journal\n"); - rv = sysctl(mib, 4, NULL, NULL, NULL, 0); - if (rv == -1) { - warn("cannot replay journal"); - } - /* This is probably not necessary, but we couldn't prove it. */ - (void)fcntl(jfd, F_FULLFSYNC, 0); - close(jfd); - } - } - /* - * We only allow a character device (e.g., /dev/rdisk1s2) - * If we're given a non-character device, we'll try to turn - * into a character device assuming a name pattern of /dev/rdisk* - */ - if ((sb.st_mode & S_IFMT) == S_IFCHR) { - dev.devname = strdup(devname); - } else if (strncmp(devname, "/dev/disk", 9) == 0) { - // Turn "/dev/diskFoo" into "/dev/rdiskFoo" - char tmpname[strlen(devname) + 2]; - (void)snprintf(tmpname, sizeof(tmpname), "/dev/rdisk%s", devname + sizeof("/dev/disk") - 1); - if (stat(tmpname, &sb) == -1) { - err(kBadExit, "cannot open raw device %s", tmpname); - } - if ((sb.st_mode & S_IFMT) != S_IFCHR) { - errx(kBadExit, "raw device %s is not a raw device", tmpname); - } - dev.devname = strdup(tmpname); - } else { - errx(kBadExit, "device name `%s' does not fit pattern", devname); - } - // Only use an exclusive open if we're not debugging. - fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_EXLOCK)); - if (fd == -1) { - err(kBadExit, "cannot open raw device %s", dev.devname); - } - // Get the block size and counts for the device. - if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev.blockSize) == -1) { - dev.blockSize = 512; // Sane default, I hope - } - if (ioctl(fd, DKIOCGETBLOCKCOUNT, &dev.blockCount) == -1) { - err(kBadExit, "cannot get size of device %s", dev.devname); - } - - dev.size = dev.blockCount * dev.blockSize; - dev.fd = fd; - - retval = malloc(sizeof(*retval)); - if (retval == NULL) { - err(kBadExit, "cannot allocate device info structure"); - } - *retval = dev; - return retval; -} - -/* - * Get the header and alternate header for a device. - */ -VolumeDescriptor_t * -VolumeInfo(DeviceInfo_t *devp) -{ - uint8_t buffer[devp->blockSize]; - VolumeDescriptor_t *vdp = NULL, vd = { 0 }; - ssize_t rv; - - vd.priOffset = 1024; // primary volume header is at 1024 bytes - vd.altOffset = devp->size - 1024; // alternate header is 1024 bytes from the end - - rv = GetBlock(devp, vd.priOffset, buffer); - if (rv == -1) { - err(kBadExit, "cannot get primary volume header for device %s", devp->devname); - } - vd.priHeader = *(HFSPlusVolumeHeader*)buffer; - - rv = GetBlock(devp, vd.altOffset, buffer); - if (rv == -1) { - err(kBadExit, "cannot get alternate volume header for device %s", devp->devname); - } - vd.altHeader = *(HFSPlusVolumeHeader*)buffer; - - vdp = malloc(sizeof(*vdp)); - *vdp = vd; - - return vdp; -} /* * Compare two volume headers to see if they're the same. Some fields * we may not care about, so we only compare specific fields. Note that * since we're looking for equality, we don't need to byte swap. + * (The function CompareVolumeHeaders() will compare the two volume + * headers to see if the extents they describe are the same.) */ -int -CompareVolumeHeaders(HFSPlusVolumeHeader *left, HFSPlusVolumeHeader *right) +static int +CheckVolumeHeaders(HFSPlusVolumeHeader *left, HFSPlusVolumeHeader *right) { if (left->signature != right->signature || left->version != right->version || @@ -200,144 +51,11 @@ CompareVolumeHeaders(HFSPlusVolumeHeader *left, HFSPlusVolumeHeader *right) return 1; } -/* - * Only two (currently) types of signatures are valid: H+ and HX. - */ -static int -IsValidSigWord(uint16_t word) { - if (word == kHFSPlusSigWord || - word == kHFSXSigWord) - return 1; - return 0; -} - -/* - * Add the volume headers to the in-core volume information list. - */ -int -AddHeaders(VolumeObjects_t *vop) -{ - int retval = 1; - HFSPlusVolumeHeader *hp; - uint8_t buffer[vop->devp->blockSize]; - ssize_t rv; - - hp = &vop->vdp->priHeader; - - if (IsValidSigWord(S16(hp->signature)) == 0) { - warnx("primary volume header signature = %x, invalid", S16(hp->signature)); - retval = 0; - } - AddExtent(vop, 1024, 512); - - hp = &vop->vdp->altHeader; - - if (IsValidSigWord(S16(hp->signature)) == 0) { - warnx("alternate volume header signature = %x, invalid", S16(hp->signature)); - retval = 0; - } - AddExtent(vop, vop->vdp->altOffset, 512); - -done: - return retval; -} - -/* - * Add the journal information to the in-core volume list. - * This means the journal info block, the journal itself, and - * the contents of the same as described by the alternate volume - * header (if it's different from the primary volume header). - */ -void -AddJournal(VolumeObjects_t *vop) -{ - DeviceInfo_t *devp = vop->devp; - uint8_t buffer[devp->blockSize]; - ssize_t rv; - HFSPlusVolumeHeader *php, *ahp; - JournalInfoBlock *jib; - - php = &vop->vdp->priHeader; - ahp = &vop->vdp->altHeader; - - if (php->journalInfoBlock) { - off_t jOffset = (off_t)S32(php->journalInfoBlock) * S32(php->blockSize); - rv = GetBlock(devp, jOffset, buffer); - if (rv == -1) { - err(kBadExit, "cannot get primary header's copy of journal info block"); - } - AddExtent(vop, jOffset, sizeof(buffer)); - jib = (JournalInfoBlock*)buffer; - if (S32(jib->flags) & kJIJournalInFSMask) { - AddExtent(vop, S64(jib->offset), S64(jib->size)); - } - } - - if (ahp->journalInfoBlock && - ahp->journalInfoBlock != php->journalInfoBlock) { - off_t jOffset = (off_t)S32(ahp->journalInfoBlock) * S32(ahp->blockSize); - rv = GetBlock(devp, jOffset, buffer); - if (rv == -1) { - err(kBadExit, "cannot get alternate header's copy of journal info block"); - } - AddExtent(vop, jOffset, sizeof(buffer)); - jib = (JournalInfoBlock*)buffer; - if (S32(jib->flags) & kJIJournalInFSMask) { - AddExtent(vop, S64(jib->offset), S64(jib->size)); - } - } - -} - -/* - * Add the extents for the special files in the volume header. Compare - * them with the alternate volume header's versions, and if they're different, - * add that as well. - */ -void -AddFileExtents(VolumeObjects_t *vop) -{ - int useAlt = 0; -#define ADDEXTS(vop, file) \ - do { \ - off_t pSize = S32(vop->vdp->priHeader.blockSize); \ - off_t aSize = S32(vop->vdp->altHeader.blockSize); \ - int i; \ - if (debug) printf("Adding " #file " extents\n"); \ - for (i = 0; i < kHFSPlusExtentDensity; i++) { \ - HFSPlusExtentDescriptor *ep = &vop->vdp->priHeader. file .extents[i]; \ - HFSPlusExtentDescriptor *ap = &vop->vdp->altHeader. file .extents[i]; \ - if (debug) printf("\tExtent <%u, %u>\n", S32(ep->startBlock), S32(ep->blockCount)); \ - if (ep->startBlock && ep->blockCount) { \ - AddExtent(vop, S32(ep->startBlock) * pSize, S32(ep->blockCount) * pSize); \ - if (memcmp(ep, ap, sizeof(*ep)) != 0) { \ - AddExtent(vop, S32(ap->startBlock) * aSize, S32(ap->blockCount) * aSize); \ - useAlt = 1; \ - } \ - } \ - } \ - } while (0) - - ADDEXTS(vop, allocationFile); - ADDEXTS(vop, extentsFile); - ADDEXTS(vop, catalogFile); - ADDEXTS(vop, attributesFile); - ADDEXTS(vop, startupFile); - -#undef ADDEXTS - - ScanExtents(vop, 0); - if (useAlt) - ScanExtents(vop, useAlt); - - return; -} - static void usage(const char *progname) { - errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-r ] ", progname); + errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r ] ", progname); } @@ -356,9 +74,11 @@ main(int ac, char **av) char *gather = NULL; int force = 0; int retval = kGoodExit; + int find_all_metadata = 0; - while ((ch = getopt(ac, av, "fvdg:Spr:")) != -1) { + while ((ch = getopt(ac, av, "fvdg:Spr:CA")) != -1) { switch (ch) { + case 'A': find_all_metadata = 1; break; case 'v': verbose++; break; case 'd': debug++; verbose++; break; case 'S': printEstimate = 1; break; @@ -381,7 +101,7 @@ main(int ac, char **av) dst = av[1]; // Start by opening the input device - devp = OpenDevice(src); + devp = OpenDevice(src, 1); if (devp == NULL) { errx(kBadExit, "cannot get device information for %s", src); } @@ -393,13 +113,25 @@ main(int ac, char **av) vop = InitVolumeObject(devp, vdp); // Add the volume headers - if (AddHeaders(vop) == 0) { + if (AddHeaders(vop, 0) == 0) { errx(kBadExit, "Invalid volume header(s) for %s", src); } // Add the journal and file extents AddJournal(vop); AddFileExtents(vop); + /* + * find_all_metadata requires scanning through + * the catalog and attributes files, looking for + * other bits treated as metadata. + */ + if (find_all_metadata) + FindOtherMetadata(vop, ^(int fid, off_t start, off_t len) { + AddExtentForFile(vop, start, len, fid); +// fprintf(stderr, "AddExtentForFile(%p, %llu, %llu, %u)\n", vop, start, len, fid); + return 0; + }); + if (debug) PrintVolumeObject(vop); @@ -446,11 +178,11 @@ main(int ac, char **av) HFSPlusVolumeHeader priHeader, altHeader; if (wrapper->reader(wrapper, 1024, &priHeader, sizeof(priHeader)) != -1) { - if (CompareVolumeHeaders(&priHeader, &vop->vdp->priHeader) == 0) { + if (CheckVolumeHeaders(&priHeader, &vop->vdp->priHeader) == 0) { restart = 0; } else { if (wrapper->reader(wrapper, vop->vdp->altOffset, &altHeader, sizeof(altHeader)) != -1) { - if (CompareVolumeHeaders(&altHeader, &vop->vdp->altHeader) == 0) { + if (CheckVolumeHeaders(&altHeader, &vop->vdp->altHeader) == 0) { restart = 0; } } diff --git a/CopyHFSMeta/misc.c b/CopyHFSMeta/misc.c index c0c870f..f79b8d2 100644 --- a/CopyHFSMeta/misc.c +++ b/CopyHFSMeta/misc.c @@ -18,6 +18,7 @@ /* * Get a block from a given input device. */ +__private_extern__ ssize_t GetBlock(DeviceInfo_t *devp, off_t offset, uint8_t *buffer) { @@ -40,6 +41,7 @@ done: /* * Initialize a VolumeObject. Simple function. */ +__private_extern__ VolumeObjects_t * InitVolumeObject(struct DeviceInfo *devp, struct VolumeDescriptor *vdp) { @@ -67,19 +69,28 @@ done: * number of objects we allocate, while still trying to keep * the waste memory allocation low. */ +__private_extern__ int AddExtent(VolumeObjects_t *vdp, off_t start, off_t length) +{ + return AddExtentForFile(vdp, start, length, 0); +} + +__private_extern__ +int +AddExtentForFile(VolumeObjects_t *vdp, off_t start, off_t length, unsigned int fid) { int retval = 0; size_t indx; ExtentList_t **ep = &vdp->list; - if (debug) printf("AddExtent(%p, %lld, %lld)\n", vdp, start, length); + if (debug) printf("AddExtent(%p, %lld, %lld) (file id %u)\n", vdp, start, length, fid); while (*ep) { if ((*ep)->count < kExtentCount) { indx = (*ep)->count; (*ep)->extents[indx].base = start; (*ep)->extents[indx].length = length; + (*ep)->extents[indx].fid = fid; (*ep)->count++; break; } else { @@ -94,6 +105,7 @@ AddExtent(VolumeObjects_t *vdp, off_t start, off_t length) (*ep)->count = 1; (*ep)->extents[0].base = start; (*ep)->extents[0].length = length; + (*ep)->extents[0].fid = fid; (*ep)->next = NULL; } vdp->count++; @@ -104,6 +116,7 @@ done: } // Debugging function +__private_extern__ void PrintVolumeObject(VolumeObjects_t *vop) { @@ -124,7 +137,7 @@ PrintVolumeObject(VolumeObjects_t *vop) exts = exts->next) { int indx; for (indx = 0; indx < exts->count; indx++) { - printf("\t\t<%lld, %lld>\n", exts->extents[indx].base, exts->extents[indx].length); + printf("\t\t<%lld, %lld> (file %u)\n", exts->extents[indx].base, exts->extents[indx].length, exts->extents[indx].fid); } } return; @@ -136,6 +149,7 @@ PrintVolumeObject(VolumeObjects_t *vop) * track of progress, and also takes an amount to skip (which happens if it's * resuming an earlier, interrupted copy). */ +__private_extern__ int CopyObjectsToDest(VolumeObjects_t *vop, struct IOWrapper *wrapper, off_t skip) { diff --git a/CopyHFSMeta/util.c b/CopyHFSMeta/util.c new file mode 100644 index 0000000..d90f7de --- /dev/null +++ b/CopyHFSMeta/util.c @@ -0,0 +1,648 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hfsmeta.h" +#include "Data.h" + +/* + * Open the source device. In addition to opening the device, + * this also attempts to flush the journal, and then sets up a + * DeviceInfo_t object that will be used when doing the actual + * reading. + */ +__private_extern__ +DeviceInfo_t * +OpenDevice(const char *devname, int flushJournal) +{ + DeviceInfo_t *retval = NULL; + int fd; + DeviceInfo_t dev = { 0 }; + struct stat sb; + struct vfsconf vfc; + + if (stat(devname, &sb) == -1) { + err(kBadExit, "cannot open device %s", devname); + } + /* + * Attempt to flush the journal if requested. If it fails, we just warn, but don't abort. + */ + if (flushJournal && getvfsbyname("hfs", &vfc) == 0) { + int rv; + int mib[4]; + char block_device[MAXPATHLEN+1]; + int jfd; + + /* + * The journal replay code, sadly, requires a block device. + * So we need to go from the raw device to block device, if + * necessary. + */ + if (strncmp(devname, "/dev/rdisk", 10) == 0) { + snprintf(block_device, sizeof(block_device), "/dev/%s", devname+6); + } else { + snprintf(block_device, sizeof(block_device), "%s", devname); + } + jfd = open(block_device, O_RDWR); + if (jfd == -1) { + warn("Cannot open block device %s for read-write", block_device); + } else { + mib[0] = CTL_VFS; + mib[1] = vfc.vfc_typenum; + mib[2] = HFS_REPLAY_JOURNAL; + mib[3] = jfd; + if (debug) + fprintf(stderr, "about to replay journal\n"); + rv = sysctl(mib, 4, NULL, NULL, NULL, 0); + if (rv == -1) { + warn("cannot replay journal"); + } + /* This is probably not necessary, but we couldn't prove it. */ + (void)fcntl(jfd, F_FULLFSYNC, 0); + close(jfd); + } + } + /* + * We only allow a character device (e.g., /dev/rdisk1s2) + * If we're given a non-character device, we'll try to turn + * into a character device assuming a name pattern of /dev/rdisk* + */ + if ((sb.st_mode & S_IFMT) == S_IFCHR) { + dev.devname = strdup(devname); + } else if (strncmp(devname, "/dev/disk", 9) == 0) { + // Turn "/dev/diskFoo" into "/dev/rdiskFoo" + char tmpname[strlen(devname) + 2]; + (void)snprintf(tmpname, sizeof(tmpname), "/dev/rdisk%s", devname + sizeof("/dev/disk") - 1); + if (stat(tmpname, &sb) == -1) { + err(kBadExit, "cannot open raw device %s", tmpname); + } + if ((sb.st_mode & S_IFMT) != S_IFCHR) { + errx(kBadExit, "raw device %s is not a raw device", tmpname); + } + dev.devname = strdup(tmpname); + } else { + errx(kBadExit, "device name `%s' does not fit pattern", devname); + } + // Only use an exclusive open if we're not debugging. + fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_EXLOCK)); + if (fd == -1) { + err(kBadExit, "cannot open raw device %s", dev.devname); + } + // Get the block size and counts for the device. + if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev.blockSize) == -1) { + dev.blockSize = 512; // Sane default, I hope + } + if (ioctl(fd, DKIOCGETBLOCKCOUNT, &dev.blockCount) == -1) { + err(kBadExit, "cannot get size of device %s", dev.devname); + } + + dev.size = dev.blockCount * dev.blockSize; + dev.fd = fd; + + retval = malloc(sizeof(*retval)); + if (retval == NULL) { + err(kBadExit, "cannot allocate device info structure"); + } + *retval = dev; + return retval; +} + +/* + * Get the header and alternate header for a device. + */ +__private_extern__ +VolumeDescriptor_t * +VolumeInfo(DeviceInfo_t *devp) +{ + uint8_t buffer[devp->blockSize]; + VolumeDescriptor_t *vdp = NULL, vd = { 0 }; + ssize_t rv; + + vd.priOffset = 1024; // primary volume header is at 1024 bytes + vd.altOffset = devp->size - 1024; // alternate header is 1024 bytes from the end + + rv = GetBlock(devp, vd.priOffset, buffer); + if (rv == -1) { + err(kBadExit, "cannot get primary volume header for device %s", devp->devname); + } + vd.priHeader = *(HFSPlusVolumeHeader*)buffer; + + rv = GetBlock(devp, vd.altOffset, buffer); + if (rv == -1) { + err(kBadExit, "cannot get alternate volume header for device %s", devp->devname); + } + vd.altHeader = *(HFSPlusVolumeHeader*)buffer; + + vdp = malloc(sizeof(*vdp)); + *vdp = vd; + + return vdp; +} + +/* + * Compare the primary and alternate volume headers. + * We only care about the "important" bits (namely, the + * portions related to extents). + */ +__private_extern__ +int +CompareVolumeHeaders(VolumeDescriptor_t *vdp) +{ + int retval = -1; + +#define CMP_FILE(v, f) memcmp(&(v)->priHeader.f, &(v)->altHeader.f, sizeof(v->priHeader.f)) + + if (vdp && + vdp->priHeader.journalInfoBlock == vdp->altHeader.journalInfoBlock && + CMP_FILE(vdp, allocationFile) == 0 && + CMP_FILE(vdp, extentsFile) == 0 && + CMP_FILE(vdp, catalogFile) == 0 && + CMP_FILE(vdp, attributesFile) == 0 && + CMP_FILE(vdp, startupFile) == 0) + retval = 0; +#undef CMP_FILE + return retval; +} + +/* + * Only two (currently) types of signatures are valid: H+ and HX. + */ +static int +IsValidSigWord(uint16_t word) { + if (word == kHFSPlusSigWord || + word == kHFSXSigWord) + return 1; + return 0; +} + +/* + * Add the volume headers to the in-core volume information list. + */ +__private_extern__ +int +AddHeaders(VolumeObjects_t *vop, int roundBlock) +{ + int retval = 1; + HFSPlusVolumeHeader *hp; + uint8_t buffer[vop->devp->blockSize]; + ssize_t rv; + + hp = &vop->vdp->priHeader; + + if (IsValidSigWord(S16(hp->signature)) == 0) { + warnx("primary volume header signature = %x, invalid", S16(hp->signature)); + retval = 0; + } + if (roundBlock) { + AddExtent(vop, 1024 / vop->devp->blockSize, vop->devp->blockSize); + } else { + AddExtent(vop, 1024, 512); + } + + hp = &vop->vdp->altHeader; + + if (IsValidSigWord(S16(hp->signature)) == 0) { + warnx("alternate volume header signature = %x, invalid", S16(hp->signature)); + retval = 0; + } + if (roundBlock) { + AddExtent(vop, (vop->vdp->altOffset / vop->devp->blockSize) * vop->devp->blockSize, vop->devp->blockSize); + } else { + AddExtent(vop, vop->vdp->altOffset, 512); + } + +done: + return retval; +} + +/* + * Add the journal information to the in-core volume list. + * This means the journal info block, the journal itself, and + * the contents of the same as described by the alternate volume + * header (if it's different from the primary volume header). + */ +__private_extern__ +void +AddJournal(VolumeObjects_t *vop) +{ + DeviceInfo_t *devp = vop->devp; + uint8_t buffer[devp->blockSize]; + ssize_t rv; + HFSPlusVolumeHeader *php, *ahp; + JournalInfoBlock *jib; + + php = &vop->vdp->priHeader; + ahp = &vop->vdp->altHeader; + + if (php->journalInfoBlock) { + off_t jOffset = (off_t)S32(php->journalInfoBlock) * S32(php->blockSize); + rv = GetBlock(devp, jOffset, buffer); + if (rv == -1) { + err(kBadExit, "cannot get primary header's copy of journal info block"); + } + AddExtent(vop, jOffset, sizeof(buffer)); + jib = (JournalInfoBlock*)buffer; + if (S32(jib->flags) & kJIJournalInFSMask) { + AddExtent(vop, S64(jib->offset), S64(jib->size)); + } + } + + if (ahp->journalInfoBlock && + ahp->journalInfoBlock != php->journalInfoBlock) { + off_t jOffset = (off_t)S32(ahp->journalInfoBlock) * S32(ahp->blockSize); + rv = GetBlock(devp, jOffset, buffer); + if (rv == -1) { + err(kBadExit, "cannot get alternate header's copy of journal info block"); + } + AddExtent(vop, jOffset, sizeof(buffer)); + jib = (JournalInfoBlock*)buffer; + if (S32(jib->flags) & kJIJournalInFSMask) { + AddExtent(vop, S64(jib->offset), S64(jib->size)); + } + } + +} + +/* + * Add the extents for the special files in the volume header. Compare + * them with the alternate volume header's versions, and if they're different, + * add that as well. + */ +__private_extern__ +void +AddFileExtents(VolumeObjects_t *vop) +{ + int useAlt = 0; +#define ADDEXTS(vop, file, fid) \ + do { \ + off_t pSize = S32(vop->vdp->priHeader.blockSize); \ + off_t aSize = S32(vop->vdp->altHeader.blockSize); \ + int i; \ + if (debug) printf("Adding " #file " extents\n"); \ + for (i = 0; i < kHFSPlusExtentDensity; i++) { \ + HFSPlusExtentDescriptor *ep = &vop->vdp->priHeader. file .extents[i]; \ + HFSPlusExtentDescriptor *ap = &vop->vdp->altHeader. file .extents[i]; \ + if (debug) printf("\tExtent <%u, %u>\n", S32(ep->startBlock), S32(ep->blockCount)); \ + if (ep->startBlock && ep->blockCount) { \ + AddExtentForFile(vop, S32(ep->startBlock) * pSize, S32(ep->blockCount) * pSize, fid); \ + if (memcmp(ep, ap, sizeof(*ep)) != 0) { \ + AddExtentForFile(vop, S32(ap->startBlock) * aSize, S32(ap->blockCount) * aSize, fid); \ + useAlt = 1; \ + } \ + } \ + } \ + } while (0) + + ADDEXTS(vop, allocationFile, kHFSAllocationFileID); + ADDEXTS(vop, extentsFile, kHFSExtentsFileID); + ADDEXTS(vop, catalogFile, kHFSCatalogFileID); + ADDEXTS(vop, attributesFile, kHFSAttributesFileID); + ADDEXTS(vop, startupFile, kHFSStartupFileID); + +#undef ADDEXTS + + ScanExtents(vop, 0); + if (useAlt) + ScanExtents(vop, useAlt); + + return; +} + +static int +ScanCatalogNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) +{ + BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; + uint16_t *indices = (uint16_t*)(buffer + nodeSize); + size_t counter; + off_t blockSize = S32(vop->vdp->priHeader.blockSize); + int retval = 0; + + if (ndp->kind != kBTLeafNode) // Skip if it's not a leaf node + return 0; + + if (debug) + fprintf(stderr, "%s: scanning catalog node\n", __FUNCTION__); + + for (counter = 1; counter <= S16(ndp->numRecords); counter++) { + // Need to get past the end of the key + uint16_t recOffset = S16(indices[-counter]); + HFSPlusCatalogKey *keyp = (HFSPlusCatalogKey*)(buffer + recOffset); + size_t keyLength = S16(keyp->keyLength); + // Add two because the keyLength field is not included. + HFSPlusCatalogFile *fp = (HFSPlusCatalogFile*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); + + if (S16(fp->recordType) != kHFSPlusFileRecord) { + if (debug) + 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); + continue; + } + + if (debug) + fprintf(stderr, "%s: node record %zu, file id = %u\n", __FUNCTION__, counter, S32(fp->fileID)); + if (S32(fp->userInfo.fdType) == kSymLinkFileType && + S32(fp->userInfo.fdCreator) == kSymLinkCreator) { + unsigned int fid = S32(fp->fileID); + HFSPlusExtentDescriptor *extPtr = fp->dataFork.extents; + int i; + + for (i = 0; i < 8; i++) { + if (extPtr[i].startBlock && + extPtr[i].blockCount) { + off_t start = blockSize * S32(extPtr[i].startBlock); + off_t length = blockSize * S32(extPtr[i].blockCount); + retval = handler(fid, start, length); + if (retval != 0) + return retval; + } else { + break; + } + } + } + } + return retval; +} + +static int +ScanAttrNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) +{ + BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; + uint16_t *indices = (uint16_t*)(buffer + nodeSize); + size_t counter; + off_t blockSize = S32(vop->vdp->priHeader.blockSize); + int retval = 0; + + if (ndp->kind != kBTLeafNode) + return 0; // Skip if it's not a leaf node + + /* + * Look for records of type kHFSPlusForkData and kHFSPlusAttrExtents + */ + for (counter = 1; counter <= S16(ndp->numRecords); counter++) { + // Need to get past the end of the key + unsigned int fid; + HFSPlusAttrKey *keyp = (HFSPlusAttrKey*)(buffer + S16(indices[-counter])); + size_t keyLength = S16(keyp->keyLength); + // Add two because the keyLength field is not included. + HFSPlusAttrRecord *ap = (HFSPlusAttrRecord*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); + HFSPlusExtentDescriptor *theExtents = NULL; + switch (S32(ap->recordType)) { + case kHFSPlusAttrForkData: + theExtents = ap->forkData.theFork.extents; + break; + case kHFSPlusAttrExtents: + theExtents = ap->overflowExtents.extents; + break; + default: + break; + } + if (theExtents != NULL) { + HFSPlusExtentDescriptor *extPtr = theExtents; + int i; + fid = S32(keyp->fileID); + + for (i = 0; i < 8; i++) { + if (extPtr[i].startBlock && + extPtr[i].blockCount) { + off_t start = blockSize * S32(extPtr[i].startBlock); + off_t length = blockSize * S32(extPtr[i].blockCount); + retval = handler(fid, start, length); + if (retval != 0) + return retval; + } else { + break; + } + } + } + } + return retval; +} + + +/* + * Given a VolumeObject_t, search for the other metadata that + * aren't described by the system files, but rather in the + * system files. This includes symbolic links, and large EA + * extents. We can do this at one of two times -- while copying + * the data, or while setting up the list of extents. The + * former is going to be more efficient, but the latter will + * mean the estimates and continuation will be less likely to + * be wrong as we add extents to the list. + */ +__private_extern__ +int +FindOtherMetadata(VolumeObjects_t *vop, extent_handler_t handler) +{ + size_t catNodeSize = 0, attrNodeSize = 0; + off_t node0_location = 0; + uint8_t *tBuffer; + BTHeaderRec *hdp; + BTNodeDescriptor *ndp; + int retval = 0; + + tBuffer = calloc(1, vop->devp->blockSize); + if (tBuffer == NULL) { + warn("Could not allocate memory to collect extra metadata"); + goto done; + } + /* + * First, do the catalog file + */ + if (vop->vdp->priHeader.catalogFile.logicalSize) { + + node0_location = S32(vop->vdp->priHeader.catalogFile.extents[0].startBlock); + node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); + if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { + warn("Could not read catalog header node"); + } else { + ndp = (BTNodeDescriptor*)tBuffer; + hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); + + if (ndp->kind != kBTHeaderNode) { + warnx("Did not read header node for catalog as expected"); + } else { + catNodeSize = S16(hdp->nodeSize); + } + } + } + /* + * Now, the attributes file. + */ + if (vop->vdp->priHeader.attributesFile.logicalSize) { + + node0_location = S32(vop->vdp->priHeader.attributesFile.extents[0].startBlock); + node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); + if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { + warn("Could not read attributes file header node"); + } else { + ndp = (BTNodeDescriptor*)tBuffer; + hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); + + if (ndp->kind != kBTHeaderNode) { + warnx("Did not read header node for attributes file as expected"); + } else { + attrNodeSize = S16(hdp->nodeSize); + } + } + } + if (debug) + fprintf(stderr, "Catalog node size = %zu, attributes node size = %zu\n", catNodeSize, attrNodeSize); + + /* + * We start reading the extents now. + * + * This is a lot of duplicated code, unfortunately. + */ + ExtentList_t *exts; + for (exts = vop->list; + exts; + exts = exts->next) { + size_t indx; + + for (indx = 0; indx < exts->count; indx++) { + off_t start = exts->extents[indx].base; + off_t len = exts->extents[indx].length; + off_t nread = 0; + if (exts->extents[indx].fid == 0) { + continue; // Unknown file, skip + } else { + if (debug) fprintf(stderr, "%s: fid = %u, start = %llu, len = %llu\n", __FUNCTION__, exts->extents[indx].fid, start, len); + while (nread < len) { + size_t bufSize; + uint8_t *buffer; + bufSize = MIN(len - nread, 1024 * 1024); // Read 1mbyte max + buffer = calloc(1, bufSize); + if (buffer == NULL) { + warn("Cannot allocate %zu bytes for buffer, skipping node scan", bufSize); + } else { + ssize_t t = UnalignedRead(vop->devp, buffer, bufSize, start + nread); + if (t != bufSize) { + warn("Attempted to read %zu bytes, only read %zd, skipping node scan", bufSize, t); + } else { + uint8_t *curPtr = buffer, *endPtr = (buffer + bufSize); + size_t nodeSize = 0; + int (*func)(VolumeObjects_t *, uint8_t *, size_t, extent_handler_t) = NULL; + if (exts->extents[indx].fid == kHFSCatalogFileID) { + func = ScanCatalogNode; + nodeSize = catNodeSize; + } else if (exts->extents[indx].fid == kHFSAttributesFileID) { + func = ScanAttrNode; + nodeSize = attrNodeSize; + } + if (func) { + while (curPtr < endPtr && retval == 0) { + retval = (*func)(vop, curPtr, nodeSize, handler); + curPtr += nodeSize; + } + } + } + free(buffer); + } + if (retval != 0) + goto done; + nread += bufSize; + } + } + } + } + +done: + if (tBuffer) + free(tBuffer); + return retval; +} + +/* + * Perform a (potentially) unaligned read from a given input device. + */ +__private_extern__ +ssize_t +UnalignedRead(DeviceInfo_t *devp, void *buffer, size_t size, off_t offset) +{ + ssize_t nread = -1; + size_t readSize = ((size + devp->blockSize - 1) / devp->blockSize) * devp->blockSize; + off_t baseOffset = (offset / devp->blockSize) * devp->blockSize; + size_t off = offset - baseOffset; + char *tmpbuf = NULL; + + if ((baseOffset == offset) && (readSize == size)) { + /* + * The read is already properly aligned, so call pread. + */ + return pread(devp->fd, buffer, size, offset); + } + + tmpbuf = malloc(readSize); + if (!tmpbuf) { + goto done; + } + + nread = pread(devp->fd, tmpbuf, readSize, baseOffset); + if (nread == -1) { + goto done; + } + + nread -= off; + if (nread > (ssize_t)size) { + nread = size; + } + memcpy(buffer, tmpbuf + off, nread); + +done: + free(tmpbuf); + return nread; +} + +__private_extern__ +void +ReleaseDeviceInfo(DeviceInfo_t *devp) +{ + if (devp) { + if (devp->fd != -1) { + close(devp->fd); + } + if (devp->devname) + free(devp->devname); + free(devp); + } + return; +} + +__private_extern__ +void +ReleaseVolumeDescriptor(VolumeDescriptor_t *vdp) +{ + if (vdp) + free(vdp); // No contained pointers! + return; +} + +__private_extern__ +void +ReleaseVolumeObjects(VolumeObjects_t *vop) +{ + if (vop) { + if (vop->devp) { + ReleaseDeviceInfo(vop->devp); + } + if (vop->vdp) { + ReleaseVolumeDescriptor(vop->vdp); + } + ExtentList_t *extList; + for (extList = vop->list; + extList; + ) { + ExtentList_t *next = extList->next; + free(extList); + extList = next; + } + free(vop); + } +} diff --git a/fsck_hfs/cache.c b/fsck_hfs/cache.c index fdd5649..a5ce2b6 100644 --- a/fsck_hfs/cache.c +++ b/fsck_hfs/cache.c @@ -341,7 +341,7 @@ int CacheRead (Cache_t *cache, uint64_t off, uint32_t len, Buf_t **bufp) while (searchBuf != NULL) { if ((searchBuf->Offset >= off) && (searchBuf->Offset < off + len)) { #if CACHE_DEBUG - printf ("ERROR: CacheRead: Deadlock\n"); + printf ("ERROR: CacheRead: Deadlock (searchBuff = <%llu, %u>, off = %llu, off+len = %llu)\n", searchBuf->Offset, searchBuf->Length, off, off+len); #endif return (EDEADLK); } @@ -372,6 +372,9 @@ int CacheRead (Cache_t *cache, uint64_t off, uint32_t len, Buf_t **bufp) buf->Flags |= BUF_SPAN; } /* Fetch the first cache block */ +#if CACHE_DEBUG + printf("%s(%d): Looking up cache block %llu for offset %llu, cache blockSize %u\n", __FUNCTION__, __LINE__, cblk, off, cache->BlockSize); +#endif error = CacheLookup (cache, cblk, &tag); if (error != EOK) { #if CACHE_DEBUG diff --git a/fsck_hfs/dfalib/SControl.c b/fsck_hfs/dfalib/SControl.c index 5c654b9..5ee6c40 100644 --- a/fsck_hfs/dfalib/SControl.c +++ b/fsck_hfs/dfalib/SControl.c @@ -494,7 +494,21 @@ termScav: if ( fsckGetVerbosity(dataArea.context) >= kDebugLog && (err != noErr || dataArea.RepLevel != repairLevelNoProblemsFound) ) PrintVolumeObject(); - + if (err != 0 && embedded == 1) { + Buf_t *buf = NULL; + off_t offset = 1024; + uint32_t len = 512; // the size of an HFS+ volume header + int rv = CacheRead(&fscache, offset, len, &buf); + if (rv == 0) { + fprintf(stderr, "Offset %llu length %u:\n", offset, len); + DumpData(buf->Buffer, len); + CacheRelease(&fscache, buf, 0); + } else { + fprintf(stderr, "%s(%d): rv = %d\n", __FUNCTION__, __LINE__, rv); + } + fflush(stderr); + } + // If we have write access on volume and we are allowed to write, // mark the volume clean/dirty if ((fsWriteRef != -1) && (dataArea.canWrite != 0)) { diff --git a/fsck_hfs/dfalib/SUtils.c b/fsck_hfs/dfalib/SUtils.c index 6404891..44b9847 100644 --- a/fsck_hfs/dfalib/SUtils.c +++ b/fsck_hfs/dfalib/SUtils.c @@ -1596,7 +1596,7 @@ static void CompareVolHeaderBTreeSizes( SGlobPtr GPtr, enum { WIDTH = 16, }; -static void +void DumpData(const void *data, size_t len) { unsigned char *base = (unsigned char*)data; @@ -1707,6 +1707,7 @@ done: } else { uint8_t *ptr = (uint8_t*)theBlockDesc.buffer; DumpData(ptr, theBlockDesc.blockSize); + ReleaseVolumeBlock(myVOPtr->vcbPtr, &theBlockDesc, kReleaseBlock); } } return retval; diff --git a/fsck_hfs/dfalib/fsck_journal.c b/fsck_hfs/dfalib/fsck_journal.c index fa93f99..6c5b0e4 100644 --- a/fsck_hfs/dfalib/fsck_journal.c +++ b/fsck_hfs/dfalib/fsck_journal.c @@ -46,6 +46,8 @@ #include "../fsck_hfs.h" #include "fsck_journal.h" +#define DEBUG_JOURNAL 0 + extern char debug; #include @@ -191,8 +193,10 @@ getJournalTransaction(JournalIOInfo_t *jinfo, swapper_t *swap) * Either there really are no blocks, or this is not a valid * transaction. Either way, there's nothing for us to do here. */ +#if DEBUG_JOURNAL if (debug) fplog(stderr, "%s(%d): hdr->num_blocks == 0\n", __FUNCTION__, __LINE__); +#endif return NULL; } /* @@ -214,10 +218,12 @@ getJournalTransaction(JournalIOInfo_t *jinfo, swapper_t *swap) } if (swap->swap32(hdr->bytes_used) < sizeof(block)) { +#if DEBUG_JOURNAL if (debug) { fplog(stderr, "%s(%d): hdr has bytes_used (%u) less than sizeof block (%zd)\n", __FUNCTION__, __LINE__, swap->swap32(hdr->bytes_used), sizeof(block)); } +#endif return NULL; } @@ -267,8 +273,10 @@ replayTransaction(block_list_header *txn, size_t blSize, size_t blkSize, swapper uint8_t *dataPtr = ((uint8_t*)txn) + blSize; int retval = -1; for (i = 1; i < swap->swap32(txn->num_blocks); i++) { +#if DEBUG_JOURNAL if (debug) plog("\tBlock %d: blkNum %llu, size %u, data offset = %zd\n", i, swap->swap64(txn->binfo[i].bnum), swap->swap32(txn->binfo[i].bsize), dataPtr - (uint8_t*)txn); +#endif /* * XXX * Check with security types on these checks. Need to ensure @@ -290,6 +298,7 @@ replayTransaction(block_list_header *txn, size_t blSize, size_t blkSize, swapper plog("\tData end out of range for block_list_header\n"); return retval; } +#if DEBUG_JOURNAL // Just for debugging if (debug) { if (swap->swap64(txn->binfo[i].bnum) == 2) { @@ -297,10 +306,13 @@ replayTransaction(block_list_header *txn, size_t blSize, size_t blkSize, swapper plog("vp->signature = %#x, version = %#x\n", vp->signature, vp->version); } } +#endif // It's in the spec, and I saw it come up once on a live volume. if (swap->swap64(txn->binfo[i].bnum) == ~(uint64_t)0) { +#if DEBUG_JOURNAL if (debug) plog("\tSkipping this block due to magic skip number\n"); +#endif } else { // Should we set retval to -2 here? if (writer) { @@ -451,8 +463,10 @@ journal_open(int jfd, JournalIOInfo_t jinfo = { 0 }; +#if DEBUG_JOURNAL if (debug) plog("Journal start sequence number = %u\n", jnlSwap->swap32(jhdr.sequence_num)); +#endif /* * Now set up the JournalIOInfo object with the file descriptor, @@ -496,34 +510,44 @@ journal_open(int jfd, plog("Journal sequence number is 0, is going into the end okay?\n"); } into_the_weeds = 1; +#if DEBUG_JOURNAL if (debug) plog("Attempting to read past stated end of journal\n"); +#endif state = "tentative "; jinfo.end = (jinfo.base + startOffset - jinfo.bSize); continue; } +#if DEBUG_JOURNAL if (debug) plog("Before getting %stransaction: jinfo.current = %llu\n", state, jinfo.current); +#endif /* * Note that getJournalTransaction verifies the checksum on the block_list_header, so * if it's bad, it'll return NULL. */ txn = getJournalTransaction(&jinfo, jnlSwap); if (txn == NULL) { +#if DEBUG_JOURNAL if (debug) plog("txn is NULL, jinfo.current = %llu\n", jinfo.current); +#endif if (into_the_weeds) { +#if DEBUG_JOURNAL if (debug) plog("\tBut we do not care, since it is past the end of the journal\n"); +#endif } else { bad_journal = 1; } break; } +#if DEBUG_JOURNAL if (debug) { plog("After getting %stransaction: jinfo.current = %llu\n", state, jinfo.current); plog("%stxn = { %u max_blocks, %u num_blocks, %u bytes_used, binfo[0].next = %u }\n", state, jnlSwap->swap32(txn->max_blocks), jnlSwap->swap32(txn->num_blocks), jnlSwap->swap32(txn->bytes_used), jnlSwap->swap32(txn->binfo[0].next)); } +#endif if (into_the_weeds) { /* * This seems to be what the kernel was checking: if the @@ -538,8 +562,10 @@ journal_open(int jfd, jnlSwap->swap32(txn->binfo[0].next) != last_sequence_number && jnlSwap->swap32(txn->binfo[0].next) != (last_sequence_number + 1)) { // Probably not a valid transaction +#if DEBUG_JOURNAL if (debug) plog("\tTentative txn sequence %u is not expected %u, stopping journal replay\n", jnlSwap->swap32(txn->binfo[0].next), last_sequence_number + 1); +#endif break; } } diff --git a/fsck_hfs/fsck_hfs.entitlements b/fsck_hfs/fsck_hfs.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/fsck_hfs/fsck_hfs.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/fsck_hfs/fsck_hfs.h b/fsck_hfs/fsck_hfs.h index cdac5db..c3ccea1 100644 --- a/fsck_hfs/fsck_hfs.h +++ b/fsck_hfs/fsck_hfs.h @@ -71,3 +71,5 @@ int reply __P((char *question)); void start_progress(void); void draw_progress(int); void end_progress(void); +void DumpData(const void *, size_t); + diff --git a/fsck_hfs/utilities.c b/fsck_hfs/utilities.c index 683a506..e42777c 100644 --- a/fsck_hfs/utilities.c +++ b/fsck_hfs/utilities.c @@ -445,7 +445,8 @@ shutdown_logging(void) /* Log fsck_hfs check completion time */ t = time(NULL); if (in_mem_log) { - print_to_mem(DO_STR, IN_MEM_LOG, "fsck_hfs completed at %s\n", ctime(&t), NULL); + va_list empty_list = {0}; + print_to_mem(DO_STR, IN_MEM_LOG, "fsck_hfs completed at %s\n", ctime(&t), empty_list); } else { fprintf(log_file, "%s: fsck_hfs completed at %s\n", cdevname ? cdevname : "UNKNOWN-DEV", ctime(&t)); } @@ -630,7 +631,8 @@ setup_logging(void) cur_in_mem_out = in_mem_out; t = time(NULL); - print_to_mem(DO_STR, IN_MEM_LOG, "\nfsck_hfs started at %s", ctime(&t), NULL); + va_list empty_list = {0}; + print_to_mem(DO_STR, IN_MEM_LOG, "\nfsck_hfs started at %s", ctime(&t), empty_list); if (live_fsck && log_file) { pthread_cond_init(&mem_buf_cond, NULL); @@ -794,7 +796,8 @@ static int need_prefix=1; LOG_PREFIX; \ fprintf(log_file, fmt, str); \ } else { \ - print_to_mem(DO_STR, IN_MEM_LOG, fmt, str, NULL); \ + va_list empty_list = {0}; \ + print_to_mem(DO_STR, IN_MEM_LOG, fmt, str, empty_list); \ } diff --git a/fstyp_hfs/fstyp_hfs.entitlements b/fstyp_hfs/fstyp_hfs.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/fstyp_hfs/fstyp_hfs.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/hfs.xcodeproj/project.pbxproj b/hfs.xcodeproj/project.pbxproj index 18a12b3..17c6702 100644 --- a/hfs.xcodeproj/project.pbxproj +++ b/hfs.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ buildPhases = ( ); dependencies = ( + 8657285C18319A93007F580F /* PBXTargetDependency */, 4DBD523F1548A499007AA736 /* PBXTargetDependency */, 4DBD52411548A49A007AA736 /* PBXTargetDependency */, 4DBD52431548A49D007AA736 /* PBXTargetDependency */, @@ -114,6 +115,13 @@ 4DFD9538153746210039B6BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4DFD9536153746210039B6BA /* InfoPlist.strings */; }; 4DFD953C15377BD80039B6BA /* fsck_keys.h in Copy fsck_keys.h */ = {isa = PBXBuildFile; fileRef = 4DFD9449153600060039B6BA /* fsck_keys.h */; }; 7279A68D1593AA5C00192947 /* fsck_journal.c in Sources */ = {isa = PBXBuildFile; fileRef = 7279A68B1593AA5C00192947 /* fsck_journal.c */; }; + 862C904C1834311200BAD882 /* iterate_hfs_metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 862C904B1834311200BAD882 /* iterate_hfs_metadata.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 863D03971820761900A4F0C4 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 863D03961820761900A4F0C4 /* util.c */; }; + 8654E4C01832A68400808937 /* ScanExtents.c in Sources */ = {isa = PBXBuildFile; fileRef = FDD9FA4F14A1343D0043D4A9 /* ScanExtents.c */; }; + 86CBF382183186FB00A64A93 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 863D03961820761900A4F0C4 /* util.c */; }; + 86CBF3831831876200A64A93 /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FDD9FA4E14A1343D0043D4A9 /* misc.c */; }; + 86CBF3861831880F00A64A93 /* iterate_hfs_metadata.c in Sources */ = {isa = PBXBuildFile; fileRef = 86CBF3851831880F00A64A93 /* iterate_hfs_metadata.c */; }; + 86CBF3871831884600A64A93 /* Data.h in Headers */ = {isa = PBXBuildFile; fileRef = FDD9FA4714A1343D0043D4A9 /* Data.h */; }; C1B6FA0810CC0A0A00778D48 /* hfsutil_jnl.c in Sources */ = {isa = PBXBuildFile; fileRef = C1B6FA0610CC0A0A00778D48 /* hfsutil_jnl.c */; }; C1B6FA0910CC0A0A00778D48 /* hfsutil_main.c in Sources */ = {isa = PBXBuildFile; fileRef = C1B6FA0710CC0A0A00778D48 /* hfsutil_main.c */; }; C1B6FA3010CC0B9500778D48 /* hfs.util.8 in Copy man8 */ = {isa = PBXBuildFile; fileRef = C1B6FA2F10CC0B8A00778D48 /* hfs.util.8 */; }; @@ -207,6 +215,13 @@ remoteGlobalIDString = 4DFD94BC15373C2C0039B6BA; remoteInfo = fsck_makestrings; }; + 8657285B18319A93007F580F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 86CBF37E183186C300A64A93; + remoteInfo = hfs_metadata; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -289,6 +304,12 @@ 4D0E89A71534FF48004CD678 /* mount_hfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mount_hfs.c; sourceTree = ""; }; 4D0E89A81534FF48004CD678 /* optical.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = optical.c; sourceTree = ""; }; 4D0E89A91534FF48004CD678 /* optical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = optical.h; sourceTree = ""; }; + 4D6E7827191D3E7E004E3F93 /* fsck_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = fsck_hfs.entitlements; sourceTree = ""; }; + 4D6E7828191D3F26004E3F93 /* newfs_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = newfs_hfs.entitlements; path = newfs_hfs/newfs_hfs.entitlements; sourceTree = SOURCE_ROOT; }; + 4D6E7829191D3F41004E3F93 /* mount_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = mount_hfs.entitlements; sourceTree = ""; }; + 4D7C8964192141CA002013C9 /* hfs_util.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = hfs_util.entitlements; sourceTree = ""; }; + 4D7C8965192141DB002013C9 /* CopyHFSMeta.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CopyHFSMeta.entitlements; sourceTree = ""; }; + 4D7C8966192141ED002013C9 /* fstyp_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = fstyp_hfs.entitlements; sourceTree = ""; }; 4DE6C7461535012200C11066 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 4DE6C74A1535018100C11066 /* libutil.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libutil.dylib; path = /usr/lib/libutil.dylib; sourceTree = ""; }; 4DE6C75B153504C100C11066 /* newfs_hfs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = newfs_hfs; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -361,6 +382,10 @@ 4DFD953D15377C7D0039B6BA /* hfs.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = hfs.xcconfig; sourceTree = ""; }; 7279A68B1593AA5C00192947 /* fsck_journal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fsck_journal.c; path = dfalib/fsck_journal.c; sourceTree = ""; }; 7279A68C1593AA5C00192947 /* fsck_journal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fsck_journal.h; path = dfalib/fsck_journal.h; sourceTree = ""; }; + 862C904B1834311200BAD882 /* iterate_hfs_metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iterate_hfs_metadata.h; path = libhfs_metadata/iterate_hfs_metadata.h; sourceTree = SOURCE_ROOT; }; + 863D03961820761900A4F0C4 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = ""; }; + 86CBF37F183186C300A64A93 /* libhfs_metadata.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libhfs_metadata.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 86CBF3851831880F00A64A93 /* iterate_hfs_metadata.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = iterate_hfs_metadata.c; path = libhfs_metadata/iterate_hfs_metadata.c; sourceTree = SOURCE_ROOT; }; C1B6FA0610CC0A0A00778D48 /* hfsutil_jnl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hfsutil_jnl.c; sourceTree = ""; }; C1B6FA0710CC0A0A00778D48 /* hfsutil_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hfsutil_main.c; sourceTree = ""; }; C1B6FA2210CC0AF400778D48 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; @@ -434,6 +459,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 86CBF37C183186C300A64A93 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -459,6 +491,7 @@ children = ( 4DFD953D15377C7D0039B6BA /* hfs.xcconfig */, FDD9FA4614A1343D0043D4A9 /* CopyHFSMeta */, + 86CBF384183187D500A64A93 /* libhfs_metadata */, FDD9FA3F14A1335D0043D4A9 /* hfs_util */, 4D0E899E1534FE65004CD678 /* mount_hfs */, 4DE6C75D153504C100C11066 /* newfs_hfs */, @@ -476,6 +509,7 @@ children = ( 4D07DCC31538EF92002B57CB /* fstyp_hfs.c */, 4D07DCC21538EF92002B57CB /* fstyp_hfs.8 */, + 4D7C8966192141ED002013C9 /* fstyp_hfs.entitlements */, ); path = fstyp_hfs; sourceTree = ""; @@ -488,6 +522,7 @@ 4D0E89A71534FF48004CD678 /* mount_hfs.c */, 4D0E89A81534FF48004CD678 /* optical.c */, 4D0E89A61534FF48004CD678 /* mount_hfs.8 */, + 4D6E7829191D3F41004E3F93 /* mount_hfs.entitlements */, ); path = mount_hfs; sourceTree = ""; @@ -501,6 +536,7 @@ 4DE6C7661535050700C11066 /* makehfs.c */, 4DE6C7641535050700C11066 /* hfs_endian.c */, 4DE6C7671535050700C11066 /* newfs_hfs.8 */, + 4D6E7828191D3F26004E3F93 /* newfs_hfs.entitlements */, ); path = newfs_hfs; sourceTree = ""; @@ -527,6 +563,7 @@ 4DFD9418153600060039B6BA /* dfalib */, 4DFD9440153600060039B6BA /* docs */, 4DFD9446153600060039B6BA /* fsck_hfs.8 */, + 4D6E7827191D3E7E004E3F93 /* fsck_hfs.entitlements */, ); path = fsck_hfs; sourceTree = ""; @@ -595,6 +632,16 @@ path = fs; sourceTree = ""; }; + 86CBF384183187D500A64A93 /* libhfs_metadata */ = { + isa = PBXGroup; + children = ( + 86CBF3851831880F00A64A93 /* iterate_hfs_metadata.c */, + 862C904B1834311200BAD882 /* iterate_hfs_metadata.h */, + ); + name = libhfs_metadata; + path = CopyHFSMeta; + sourceTree = ""; + }; C1B6FD2C10CC0DB200778D48 /* Products */ = { isa = PBXGroup; children = ( @@ -607,6 +654,7 @@ 4DFD94E615373C2C0039B6BA /* fsck_makestrings */, 4DFD95121537402A0039B6BA /* hfs.fs */, 4D07DCB81538EF3A002B57CB /* fstyp_hfs */, + 86CBF37F183186C300A64A93 /* libhfs_metadata.a */, ); name = Products; sourceTree = ""; @@ -617,6 +665,7 @@ C1B6FA2F10CC0B8A00778D48 /* hfs.util.8 */, C1B6FA0610CC0A0A00778D48 /* hfsutil_jnl.c */, C1B6FA0710CC0A0A00778D48 /* hfsutil_main.c */, + 4D7C8964192141CA002013C9 /* hfs_util.entitlements */, ); path = hfs_util; sourceTree = ""; @@ -635,6 +684,7 @@ FDD9FA4614A1343D0043D4A9 /* CopyHFSMeta */ = { isa = PBXGroup; children = ( + 863D03961820761900A4F0C4 /* util.c */, FDD9FA4714A1343D0043D4A9 /* Data.h */, FDD9FA4814A1343D0043D4A9 /* DeviceWrapper.c */, FDD9FA4914A1343D0043D4A9 /* dump.c */, @@ -645,12 +695,25 @@ FDD9FA4F14A1343D0043D4A9 /* ScanExtents.c */, FDD9FA5014A1343D0043D4A9 /* Sparse.h */, FDD9FA5114A1343D0043D4A9 /* SparseBundle.c */, + 4D7C8965192141DB002013C9 /* CopyHFSMeta.entitlements */, ); path = CopyHFSMeta; sourceTree = ""; }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 86CBF37D183186C300A64A93 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 862C904C1834311200BAD882 /* iterate_hfs_metadata.h in Headers */, + 86CBF3871831884600A64A93 /* Data.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ 4D07DCB71538EF3A002B57CB /* fstyp_hfs */ = { isa = PBXNativeTarget; @@ -772,6 +835,23 @@ productReference = 4DFD95121537402A0039B6BA /* hfs.fs */; productType = "com.apple.product-type.bundle"; }; + 86CBF37E183186C300A64A93 /* hfs_metadata */ = { + isa = PBXNativeTarget; + buildConfigurationList = 86CBF381183186C300A64A93 /* Build configuration list for PBXNativeTarget "hfs_metadata" */; + buildPhases = ( + 86CBF37B183186C300A64A93 /* Sources */, + 86CBF37C183186C300A64A93 /* Frameworks */, + 86CBF37D183186C300A64A93 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = hfs_metadata; + productName = hfs_metadata; + productReference = 86CBF37F183186C300A64A93 /* libhfs_metadata.a */; + productType = "com.apple.product-type.library.static"; + }; 8DD76FA90486AB0100D96B5E /* hfs.util */ = { isa = PBXNativeTarget; buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "hfs.util" */; @@ -840,6 +920,7 @@ 4DFD93F31535FF510039B6BA /* fsck_hfs */, 4D07DCB71538EF3A002B57CB /* fstyp_hfs */, 4DFD94BC15373C2C0039B6BA /* fsck_makestrings */, + 86CBF37E183186C300A64A93 /* hfs_metadata */, ); }; /* End PBXProject section */ @@ -1032,6 +1113,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 86CBF37B183186C300A64A93 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 86CBF382183186FB00A64A93 /* util.c in Sources */, + 86CBF3831831876200A64A93 /* misc.c in Sources */, + 8654E4C01832A68400808937 /* ScanExtents.c in Sources */, + 86CBF3861831880F00A64A93 /* iterate_hfs_metadata.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8DD76FAB0486AB0100D96B5E /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1050,6 +1142,7 @@ FDD9FA5814A1343D0043D4A9 /* ScanExtents.c in Sources */, FDD9FA5314A1343D0043D4A9 /* dump.c in Sources */, FDD9FA5914A1343D0043D4A9 /* SparseBundle.c in Sources */, + 863D03971820761900A4F0C4 /* util.c in Sources */, FDD9FA5214A1343D0043D4A9 /* DeviceWrapper.c in Sources */, FDD9FA5414A1343D0043D4A9 /* Gather.c in Sources */, ); @@ -1113,6 +1206,11 @@ target = 4DFD94BC15373C2C0039B6BA /* fsck_makestrings */; targetProxy = 4DBD52521548A4D4007AA736 /* PBXContainerItemProxy */; }; + 8657285C18319A93007F580F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 86CBF37E183186C300A64A93 /* hfs_metadata */; + targetProxy = 8657285B18319A93007F580F /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1130,6 +1228,8 @@ 1DEB928708733DD80010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = hfs_util/hfs_util.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; INSTALL_PATH = $FS_BUNDLE_BIN_PATH; PRODUCT_NAME = hfs.util; }; @@ -1145,6 +1245,8 @@ 4D07DCBF1538EF3A002B57CB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = fstyp_hfs/fstyp_hfs.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; INSTALL_PATH = /sbin; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1153,6 +1255,8 @@ 4D0E89A41534FE65004CD678 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = mount_hfs/mount_hfs.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; EXCLUDED_SOURCE_FILE_NAMES = ""; "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*]" = optical.c; "EXCLUDED_SOURCE_FILE_NAMES[sdk=iphonesimulator*]" = optical.c; @@ -1185,6 +1289,8 @@ 4DE6C763153504C100C11066 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = newfs_hfs/newfs_hfs.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; INSTALL_PATH = $FS_BUNDLE_BIN_PATH; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1193,6 +1299,8 @@ 4DFD93FB1535FF510039B6BA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = fsck_hfs/fsck_hfs.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; GCC_PREPROCESSOR_DEFINITIONS = ( "BSD=1", "CONFIG_HFS_TRIM=1", @@ -1206,6 +1314,8 @@ 4DFD94AE153649070039B6BA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = newfs_hfs/newfs_hfs.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; GCC_PREPROCESSOR_DEFINITIONS = "DEBUG_BUILD=1"; PRODUCT_NAME = newfs_hfs_debug; }; @@ -1235,9 +1345,50 @@ }; name = Release; }; + 86CBF380183186C300A64A93 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEPLOYMENT_POSTPROCESSING = YES; + ENABLE_NS_ASSERTIONS = NO; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + GENERATE_MASTER_OBJECT_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + STRIPFLAGS = ""; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = "non-global"; + }; + name = Release; + }; FDD9FA3414A132BF0043D4A9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = CopyHFSMeta/CopyHFSMeta.entitlements; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "-"; INSTALL_PATH = $FS_BUNDLE_BIN_PATH; PRODUCT_NAME = "$(TARGET_NAME)"; }; @@ -1342,6 +1493,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 86CBF381183186C300A64A93 /* Build configuration list for PBXNativeTarget "hfs_metadata" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 86CBF380183186C300A64A93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; FDD9FA3514A132BF0043D4A9 /* Build configuration list for PBXNativeTarget "CopyHFSMeta" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/hfs_util/hfs_util.entitlements b/hfs_util/hfs_util.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/hfs_util/hfs_util.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/hfs_util/hfsutil_jnl.c b/hfs_util/hfsutil_jnl.c index aab17de..663a43f 100644 --- a/hfs_util/hfsutil_jnl.c +++ b/hfs_util/hfsutil_jnl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 Apple Inc. All rights reserved. + * Copyright (c) 1999-2014 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -46,6 +46,9 @@ #include +#include +#include + #include #include #include @@ -411,7 +414,15 @@ DoMakeJournaled(char *volname, int jsize) { } // printf("Embedded offset == 0x%llx\n", embedded_offset); - fd = open(journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000); +#if TARGET_OS_EMBEDDED + /* + * Must use open_dprotected_np to create a class D file. This will + * be the same as standard open(2) on systems that do not support content protection + */ + fd = open_dprotected_np (journal_fname, O_CREAT|O_TRUNC|O_RDWR, PROTECTION_CLASS_D, 0, 000); +#else + fd = open (journal_fname, O_CREAT|O_TRUNC|O_RDWR, 000); +#endif if (fd < 0) { fprintf(stderr, "Can't create journal file on volume %s (%s)\n", volname, strerror(errno)); @@ -500,7 +511,16 @@ retry: jib.offset = (off_t)((unsigned int)jstart_block) * (off_t)((unsigned int)block_size); jib.size = (off_t)((unsigned int)journal_size); +#if TARGET_OS_EMBEDDED + /* + * Use open_dprotected_np to create JIB as a class D file. This will + * behave the same as a standard open(2) on systems that do not support content protection + */ + fd = open_dprotected_np(jib_fname, O_CREAT|O_TRUNC|O_RDWR, PROTECTION_CLASS_D, 0, 000); +#else fd = open(jib_fname, O_CREAT|O_TRUNC|O_RDWR, 000); +#endif + if (fd < 0) { fprintf(stderr, "Could not create journal info block file on volume %s (%s)\n", volname, strerror(errno)); diff --git a/hfs_util/hfsutil_main.c b/hfs_util/hfsutil_main.c index 8bb4e84..bb84345 100644 --- a/hfs_util/hfsutil_main.c +++ b/hfs_util/hfsutil_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2009 Apple Inc. All rights reserved. + * Copyright (c) 1999-2014 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -125,6 +125,8 @@ /* **************************************** L O C A L S ******************************************* */ +#define kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* 255 unicode characters, plus 1 NUL byte */ + #define HFS_BLOCK_SIZE 512 char gHFS_FS_NAME[] = "hfs"; @@ -741,7 +743,7 @@ DoProbe(char *rawDeviceNamePtr, char *blockDeviceNamePtr) char * bufPtr; HFSMasterDirectoryBlock * mdbPtr; HFSPlusVolumeHeader * volHdrPtr; - u_char volnameUTF8[NAME_MAX+1]; + u_char volnameUTF8[kHFSPlusMaxFileNameBytes]; /* * Determine if there is a volume already mounted from this device. If @@ -1949,7 +1951,7 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned c } swapped->unicode[i] = 0; cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, swapped->unicode, swapped->length); - (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX, kCFStringEncodingUTF8); + (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX * 3 + 1, kCFStringEncodingUTF8); CFRelease(cfstr); free(swapped); } diff --git a/libhfs_metadata/iterate_hfs_metadata.c b/libhfs_metadata/iterate_hfs_metadata.c new file mode 100644 index 0000000..b2aa4ae --- /dev/null +++ b/libhfs_metadata/iterate_hfs_metadata.c @@ -0,0 +1,113 @@ +#include +#include +#include + +#include "iterate_hfs_metadata.h" +#include "../CopyHFSMeta/Data.h" +#include "../CopyHFSMeta/hfsmeta.h" + +/* + * These variables are used by the CopyHFSMeta routines, so we have to define them + */ + +__private_extern__ int debug = 0; +__private_extern__ int verbose = 0; +__private_extern__ int printProgress = 0; + +/* + * This is essentially the guts of CopyHFSMeta, only without + * the ability to write out to a sparse bundle. It also always + * wants to get the "other" metadata (symlinks and large EA extents). + * + * For each extent it finds, it calls the passed-in function pointer, + * with the start and length. + * + * It will go through and get the symlink and EA extents first, and + * then go through the rest of the extents. It does not attempt any + * sorting past that. + */ +int +iterate_hfs_metadata(char *device, int (*handle_extent)(int fd, off_t start, off_t length, void *ctx), void *context_ptr) +{ + struct DeviceInfo *devp = NULL; + struct VolumeDescriptor *vdp = NULL; + VolumeObjects_t *vop = NULL; + int retval = 0; + + devp = OpenDevice(device, 0); + if (devp == NULL) { + retval = errno; + goto done; + } + + vdp = VolumeInfo(devp); + if (vdp == NULL) { + retval = errno; + goto done; + } + + if (CompareVolumeHeaders(vdp) == -1) { + retval = EINVAL; + goto done; + } + + vop = InitVolumeObject(devp, vdp); + if (vop == NULL) { + retval = errno; + goto done; + } + + /* + * These guys don't have an error. Probably + * a mistake. + */ + AddHeaders(vop, 1); + AddJournal(vop); + AddFileExtents(vop); + + /* + * By this point, vop has all of the system metadata. + * The only metadata we don't have would be symlinks (from + * the catalog file), and extended attribute fork extents + * (from the attributes file). We get those with + * FindOtherMetadata(). + */ + retval = FindOtherMetadata(vop, ^(int fid __unused, off_t start, off_t len) { + return (*handle_extent)(devp->fd, start, len, context_ptr); + }); + + if (retval != 0) + goto done; + + /* + * Now, we've handled all of the other metadata, so we need + * to go through the rest of our metadata, which is in vop. + */ + ExtentList_t *extList; + for (extList = vop->list; + extList; + extList = extList->next) { + size_t index; + + for (index = 0; index < extList->count; index++) { + retval = (*handle_extent)(devp->fd, extList->extents[index].base, extList->extents[index].length, context_ptr); + if (retval != 0) + goto done; + } + } + +done: + if (vop) { + ReleaseVolumeObjects(vop); + } else { + if (vdp) { + ReleaseVolumeDescriptor(vdp); + } + if (devp) { + ReleaseDeviceInfo(devp); + } + } + + return retval; + +} diff --git a/libhfs_metadata/iterate_hfs_metadata.h b/libhfs_metadata/iterate_hfs_metadata.h new file mode 100644 index 0000000..86e9bb8 --- /dev/null +++ b/libhfs_metadata/iterate_hfs_metadata.h @@ -0,0 +1,11 @@ +#ifndef _hfs_iterate_hfs_metadata_h +#define _hfs_iterate_hfs_metadata_h + +/* + * Given a device name, a function pointer, and + * a context pointer, call the function pointer for + * each metadata extent in the HFS+ filesystem. + */ +extern int iterate_hfs_metadata(char *, int (*)(int, off_t, off_t, void*), void *); + +#endif diff --git a/mount_hfs/mount_hfs.c b/mount_hfs/mount_hfs.c index 8f086e8..a737aaf 100644 --- a/mount_hfs/mount_hfs.c +++ b/mount_hfs/mount_hfs.c @@ -61,6 +61,37 @@ #include +/* + * Replay the journal. We don't care if there are problems. + */ +static void +replay_journal(const char *device) +{ + struct vfsconf vfc; + int mib[4]; + int fd = -1; + + fd = open(device, O_RDWR); + if (fd == -1) { + warn("Could not open block device %s for writing", device); + goto done; + } + if (getvfsbyname("hfs", &vfc) != 0) { + warn("Could not get hfs vfs information"); + goto done; + } + mib[0] = CTL_VFS; + mib[1] = vfc.vfc_typenum; + mib[2] = HFS_REPLAY_JOURNAL; + mib[3] = fd; + (void)sysctl(mib, 4, NULL, NULL, NULL, 0); + +done: + if (fd != -1) + close(fd); + return; +} + struct mntopt mopts[] = { MOPT_STDOPTS, MOPT_IGNORE_OWNERSHIP, @@ -423,6 +454,8 @@ main(argc, argv) u_int32_t localCreateTime; struct hfs_mnt_encoding *encp; + int do_rekey = 0; + int tmp_mntflags = 0; #if TARGET_OS_EMBEDDED mntflags = MNT_NOATIME; #else @@ -615,45 +648,46 @@ main(argc, argv) #endif #if !TARGET_OS_EMBEDDED - /* - * We shouldn't really be calling up to other layers, but - * an exception was made in this case to fix the situation - * where HFS was writable on optical media. - */ - - if ((_optical_is_writable(dev) & _OPTICAL_WRITABLE_PACKET)) { - mntflags |= MNT_RDONLY; - } + /* + * We shouldn't really be calling up to other layers, but + * an exception was made in this case to fix the situation + * where HFS was writable on optical media. + */ + + if ((_optical_is_writable(dev) & _OPTICAL_WRITABLE_PACKET)) { + mntflags |= MNT_RDONLY; + } #endif - if (is_hfs_std) - mntflags |= MNT_RDONLY; - - if ((mntflags & MNT_RDONLY) == 0) { - /* - * get the volume's create date so we can synchronize - * it with the root directory create date - */ - localCreateTime = getVolumeCreateDate(dev); - } - else { - localCreateTime = 0; - } - + if (is_hfs_std) + mntflags |= MNT_RDONLY; + + if ((mntflags & MNT_RDONLY) == 0) { + /* + * get the volume's create date so we can synchronize + * it with the root directory create date + */ + localCreateTime = getVolumeCreateDate(dev); + } + else { + localCreateTime = 0; + } + if ((mountStatus = mount(HFS_MOUNT_TYPE, dir, mntflags, &args)) < 0) { #if DEBUG - printf("mount_hfs: error on mount(): error = %d.\n", mountStatus); + printf("mount_hfs: error on mount(): error = %d.\n", mountStatus); #endif - err(1, NULL); - }; - - /* - * synchronize the root directory's create date - * with the volume's create date - */ - if (localCreateTime) - syncCreateDate(dir, localCreateTime); - + err(1, NULL); + }; + + /* + * synchronize the root directory's create date + * with the volume's create date + */ + if (localCreateTime) + syncCreateDate(dir, localCreateTime); + + exit(0); } diff --git a/mount_hfs/mount_hfs.entitlements b/mount_hfs/mount_hfs.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/mount_hfs/mount_hfs.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/newfs_hfs/makehfs.c b/newfs_hfs/makehfs.c index 27b922a..0fdaba9 100644 --- a/newfs_hfs/makehfs.c +++ b/newfs_hfs/makehfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Apple Inc. All rights reserved. + * Copyright (c) 1999-2014 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -1436,6 +1436,24 @@ WriteJournalInfo(const DriveInfo *driveInfo, UInt64 startingSector, jibp->offset = SWAP_BE64(jibp->offset); jibp->size = SWAP_BE64(jibp->size); + if (jibp->flags & kJIJournalInFSMask) { + /* + * Zero out the on-disk content of the journal file. + * + * This is a really ugly hack. Right now, all of the logic in the code + * that calls us (make_hfsplus), uses the value 'sectorsPerBlock' but it + * is really hardcoded to assume the sector size is 512 bytes. The code + * in WriteBuffer will massage the I/O to use the actual physical sector + * size. Since WriteBuffer takes a sector # relative to 512 byte sectors, + * We need to convert the journal offset in bytes to value that represents + * its start LBA in 512 byte sectors. + * + * Note further that we swapped to big endian prior to the WriteBuffer call, + * but we have swapped back to native after the call. + */ + WriteBuffer(driveInfo, jibp->offset / kBytesPerSector, jibp->size, NULL); + } + return 0; } @@ -1560,7 +1578,7 @@ InitCatalogRoot_HFSPlus(const hfsparams_t *dp, const HFSPlusVolumeHeader *header UInt16 nodeSize; SInt16 offset; size_t unicodeBytes; - UInt8 canonicalName[256]; + UInt8 canonicalName[kHFSPlusMaxFileNameBytes]; // UTF8 character may convert to three bytes, plus a NUL CFStringRef cfstr; Boolean cfOK; int index = 0; diff --git a/newfs_hfs/newfs_hfs.c b/newfs_hfs/newfs_hfs.c index 682ed8d..bd7b076 100644 --- a/newfs_hfs/newfs_hfs.c +++ b/newfs_hfs/newfs_hfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Apple Inc. All rights reserved. + * Copyright (c) 1999-2014 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -93,7 +93,7 @@ static int bad_disk_size (u_int64_t numsectors, u_int64_t sectorsize); char *progname; -char gVolumeName[kHFSPlusMaxFileNameChars + 1] = {kDefaultVolumeNameStr}; +char *gVolumeName = kDefaultVolumeNameStr; char rawdevice[MAXPATHLEN]; char blkdevice[MAXPATHLEN]; uint32_t gBlockSize = 0; @@ -316,13 +316,9 @@ main(argc, argv) break; case 'v': - n = strlen(optarg); - if ((size_t)n > (sizeof(gVolumeName) - 1)) - fatal("\"%s\" is too long (%d byte maximum)", - optarg, sizeof(gVolumeName) - 1); - if (n == 0) - fatal("name required with -v option"); - strlcpy(gVolumeName, optarg, sizeof(gVolumeName)); + if ((gVolumeName = strdup(optarg)) == NULL) { + fatal("Could not copy volume name %s", optarg); + } break; case '?': @@ -1086,8 +1082,7 @@ static void hfsplus_params (const DriveInfo* dip, hfsparams_t *defaults) } } - strncpy((char *)defaults->volumeName, gVolumeName, sizeof(defaults->volumeName) - 1); - defaults->volumeName[sizeof(defaults->volumeName) - 1] = '\0'; + defaults->volumeName = (unsigned char*)gVolumeName; if (rsrclumpblks == 0) { if (gBlockSize > DFL_BLKSIZE) diff --git a/newfs_hfs/newfs_hfs.entitlements b/newfs_hfs/newfs_hfs.entitlements new file mode 100644 index 0000000..1f58459 --- /dev/null +++ b/newfs_hfs/newfs_hfs.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.private.security.disk-device-access + + + diff --git a/newfs_hfs/newfs_hfs.h b/newfs_hfs/newfs_hfs.h index 4f0c521..79d9624 100644 --- a/newfs_hfs/newfs_hfs.h +++ b/newfs_hfs/newfs_hfs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2014 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -174,6 +174,13 @@ enum { */ #define MAC_GMT_FACTOR 2082844800UL +/* + * Maximum number of bytes in an HFS+ filename. + * It's 255 characters; a UTF16 character may translate + * to 3 bytes. Plus one for NUL. + */ + +#define kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* sectorSize = kBytesPerSector = 512 sectorOffset and totalSectors are in terms of 512-byte sector size. @@ -231,7 +238,7 @@ struct hfsparams { uint32_t createDate; /* in UTC */ uint32_t hfsAlignment; - unsigned char volumeName[kHFSPlusMaxFileNameChars + 1]; /* in UTF-8 */ + unsigned char *volumeName; /* In UTF8, but we need to allocate space for it. */ uint32_t encodingHint; uint32_t journaledHFS; uint32_t journalSize; -- 2.45.2