--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
#ifndef _DATA_H
# define _DATA_H
+# include <errno.h>
+/*
+ * 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.
struct Extents {
off_t base;
off_t length;
+ unsigned int fid; // Optional, may not be set
};
typedef struct Extents Extents_t;
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,
};
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 */
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));
}
}
}
* 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)
{
* 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,
"</dict>\n"
"</plist>\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
};
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 */
#include <sys/disk.h>
#include <sys/sysctl.h>
#include <hfs/hfs_mount.h>
+#include <Block.h>
#include "hfsmeta.h"
#include "Data.h"
#include "Sparse.h"
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 ||
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 <bytes>] <src device> <destination>", progname);
+ errx(kBadExit, "usage: %s [-vdpS] [-g gatherFile] [-C] [-r <bytes>] <src device> <destination>", progname);
}
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;
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);
}
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);
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;
}
}
/*
* Get a block from a given input device.
*/
+__private_extern__
ssize_t
GetBlock(DeviceInfo_t *devp, off_t offset, uint8_t *buffer)
{
/*
* Initialize a VolumeObject. Simple function.
*/
+__private_extern__
VolumeObjects_t *
InitVolumeObject(struct DeviceInfo *devp, struct VolumeDescriptor *vdp)
{
* 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 {
(*ep)->count = 1;
(*ep)->extents[0].base = start;
(*ep)->extents[0].length = length;
+ (*ep)->extents[0].fid = fid;
(*ep)->next = NULL;
}
vdp->count++;
}
// Debugging function
+__private_extern__
void
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;
* 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)
{
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+#include <sys/sysctl.h>
+#include <hfs/hfs_mount.h>
+#include <Block.h>
+#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);
+ }
+}
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);
}
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
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)) {
enum { WIDTH = 16, };
-static void
+void
DumpData(const void *data, size_t len)
{
unsigned char *base = (unsigned char*)data;
} else {
uint8_t *ptr = (uint8_t*)theBlockDesc.buffer;
DumpData(ptr, theBlockDesc.blockSize);
+ ReleaseVolumeBlock(myVOPtr->vcbPtr, &theBlockDesc, kReleaseBlock);
}
}
return retval;
#include "../fsck_hfs.h"
#include "fsck_journal.h"
+#define DEBUG_JOURNAL 0
+
extern char debug;
#include <hfs/hfs_format.h>
* 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;
}
/*
}
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;
}
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
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) {
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) {
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,
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
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;
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
void start_progress(void);
void draw_progress(int);
void end_progress(void);
+void DumpData(const void *, size_t);
+
/* 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));
}
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);
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); \
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
buildPhases = (
);
dependencies = (
+ 8657285C18319A93007F580F /* PBXTargetDependency */,
4DBD523F1548A499007AA736 /* PBXTargetDependency */,
4DBD52411548A49A007AA736 /* PBXTargetDependency */,
4DBD52431548A49D007AA736 /* PBXTargetDependency */,
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 */; };
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 */
4D0E89A71534FF48004CD678 /* mount_hfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mount_hfs.c; sourceTree = "<group>"; };
4D0E89A81534FF48004CD678 /* optical.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = optical.c; sourceTree = "<group>"; };
4D0E89A91534FF48004CD678 /* optical.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = optical.h; sourceTree = "<group>"; };
+ 4D6E7827191D3E7E004E3F93 /* fsck_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = fsck_hfs.entitlements; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ 4D7C8964192141CA002013C9 /* hfs_util.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = hfs_util.entitlements; sourceTree = "<group>"; };
+ 4D7C8965192141DB002013C9 /* CopyHFSMeta.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = CopyHFSMeta.entitlements; sourceTree = "<group>"; };
+ 4D7C8966192141ED002013C9 /* fstyp_hfs.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = fstyp_hfs.entitlements; sourceTree = "<group>"; };
4DE6C7461535012200C11066 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; };
4DE6C74A1535018100C11066 /* libutil.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libutil.dylib; path = /usr/lib/libutil.dylib; sourceTree = "<absolute>"; };
4DE6C75B153504C100C11066 /* newfs_hfs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = newfs_hfs; sourceTree = BUILT_PRODUCTS_DIR; };
4DFD953D15377C7D0039B6BA /* hfs.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = hfs.xcconfig; sourceTree = "<group>"; };
7279A68B1593AA5C00192947 /* fsck_journal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fsck_journal.c; path = dfalib/fsck_journal.c; sourceTree = "<group>"; };
7279A68C1593AA5C00192947 /* fsck_journal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fsck_journal.h; path = dfalib/fsck_journal.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
+ 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 = "<group>"; };
C1B6FA0710CC0A0A00778D48 /* hfsutil_main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hfsutil_main.c; sourceTree = "<group>"; };
C1B6FA2210CC0AF400778D48 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 86CBF37C183186C300A64A93 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8DD76FAD0486AB0100D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
children = (
4DFD953D15377C7D0039B6BA /* hfs.xcconfig */,
FDD9FA4614A1343D0043D4A9 /* CopyHFSMeta */,
+ 86CBF384183187D500A64A93 /* libhfs_metadata */,
FDD9FA3F14A1335D0043D4A9 /* hfs_util */,
4D0E899E1534FE65004CD678 /* mount_hfs */,
4DE6C75D153504C100C11066 /* newfs_hfs */,
children = (
4D07DCC31538EF92002B57CB /* fstyp_hfs.c */,
4D07DCC21538EF92002B57CB /* fstyp_hfs.8 */,
+ 4D7C8966192141ED002013C9 /* fstyp_hfs.entitlements */,
);
path = fstyp_hfs;
sourceTree = "<group>";
4D0E89A71534FF48004CD678 /* mount_hfs.c */,
4D0E89A81534FF48004CD678 /* optical.c */,
4D0E89A61534FF48004CD678 /* mount_hfs.8 */,
+ 4D6E7829191D3F41004E3F93 /* mount_hfs.entitlements */,
);
path = mount_hfs;
sourceTree = "<group>";
4DE6C7661535050700C11066 /* makehfs.c */,
4DE6C7641535050700C11066 /* hfs_endian.c */,
4DE6C7671535050700C11066 /* newfs_hfs.8 */,
+ 4D6E7828191D3F26004E3F93 /* newfs_hfs.entitlements */,
);
path = newfs_hfs;
sourceTree = "<group>";
4DFD9418153600060039B6BA /* dfalib */,
4DFD9440153600060039B6BA /* docs */,
4DFD9446153600060039B6BA /* fsck_hfs.8 */,
+ 4D6E7827191D3E7E004E3F93 /* fsck_hfs.entitlements */,
);
path = fsck_hfs;
sourceTree = "<group>";
path = fs;
sourceTree = "<group>";
};
+ 86CBF384183187D500A64A93 /* libhfs_metadata */ = {
+ isa = PBXGroup;
+ children = (
+ 86CBF3851831880F00A64A93 /* iterate_hfs_metadata.c */,
+ 862C904B1834311200BAD882 /* iterate_hfs_metadata.h */,
+ );
+ name = libhfs_metadata;
+ path = CopyHFSMeta;
+ sourceTree = "<group>";
+ };
C1B6FD2C10CC0DB200778D48 /* Products */ = {
isa = PBXGroup;
children = (
4DFD94E615373C2C0039B6BA /* fsck_makestrings */,
4DFD95121537402A0039B6BA /* hfs.fs */,
4D07DCB81538EF3A002B57CB /* fstyp_hfs */,
+ 86CBF37F183186C300A64A93 /* libhfs_metadata.a */,
);
name = Products;
sourceTree = "<group>";
C1B6FA2F10CC0B8A00778D48 /* hfs.util.8 */,
C1B6FA0610CC0A0A00778D48 /* hfsutil_jnl.c */,
C1B6FA0710CC0A0A00778D48 /* hfsutil_main.c */,
+ 4D7C8964192141CA002013C9 /* hfs_util.entitlements */,
);
path = hfs_util;
sourceTree = "<group>";
FDD9FA4614A1343D0043D4A9 /* CopyHFSMeta */ = {
isa = PBXGroup;
children = (
+ 863D03961820761900A4F0C4 /* util.c */,
FDD9FA4714A1343D0043D4A9 /* Data.h */,
FDD9FA4814A1343D0043D4A9 /* DeviceWrapper.c */,
FDD9FA4914A1343D0043D4A9 /* dump.c */,
FDD9FA4F14A1343D0043D4A9 /* ScanExtents.c */,
FDD9FA5014A1343D0043D4A9 /* Sparse.h */,
FDD9FA5114A1343D0043D4A9 /* SparseBundle.c */,
+ 4D7C8965192141DB002013C9 /* CopyHFSMeta.entitlements */,
);
path = CopyHFSMeta;
sourceTree = "<group>";
};
/* 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;
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" */;
4DFD93F31535FF510039B6BA /* fsck_hfs */,
4D07DCB71538EF3A002B57CB /* fstyp_hfs */,
4DFD94BC15373C2C0039B6BA /* fsck_makestrings */,
+ 86CBF37E183186C300A64A93 /* hfs_metadata */,
);
};
/* End PBXProject section */
);
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;
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 */,
);
target = 4DFD94BC15373C2C0039B6BA /* fsck_makestrings */;
targetProxy = 4DBD52521548A4D4007AA736 /* PBXContainerItemProxy */;
};
+ 8657285C18319A93007F580F /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 86CBF37E183186C300A64A93 /* hfs_metadata */;
+ targetProxy = 8657285B18319A93007F580F /* PBXContainerItemProxy */;
+ };
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
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;
};
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)";
};
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;
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)";
};
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",
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;
};
};
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)";
};
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 = (
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
/*
- * Copyright (c) 1999-2012 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <System/hfs/hfs_fsctl.h>
+#include <System/sys/content_protection.h>
+#include <TargetConditionals.h>
+
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
}
// 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));
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));
/*
- * Copyright (c) 1999-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
/* **************************************** 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";
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
}
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);
}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#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;
+
+}
--- /dev/null
+#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
#include <mntopts.h>
+/*
+ * 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,
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
#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);
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
/*
- * Copyright (c) 1999-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
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;
}
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;
/*
- * Copyright (c) 1999-2011 Apple Inc. All rights reserved.
+ * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
char *progname;
-char gVolumeName[kHFSPlusMaxFileNameChars + 1] = {kDefaultVolumeNameStr};
+char *gVolumeName = kDefaultVolumeNameStr;
char rawdevice[MAXPATHLEN];
char blkdevice[MAXPATHLEN];
uint32_t gBlockSize = 0;
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 '?':
}
}
- 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)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.private.security.disk-device-access</key>
+ <true/>
+</dict>
+</plist>
/*
- * Copyright (c) 1999-2011 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 1999-2014 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#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.
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;