]> git.saurik.com Git - hfs.git/blobdiff - CopyHFSMeta/misc.c
hfs-183.1.tar.gz
[hfs.git] / CopyHFSMeta / misc.c
diff --git a/CopyHFSMeta/misc.c b/CopyHFSMeta/misc.c
new file mode 100644 (file)
index 0000000..c0c870f
--- /dev/null
@@ -0,0 +1,218 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+
+#include "hfsmeta.h"
+
+#define MIN(a, b) \
+       ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
+               __a < __b ? __a : __b; })
+
+/*
+ * Get a block from a given input device.
+ */
+ssize_t
+GetBlock(DeviceInfo_t *devp, off_t offset, uint8_t *buffer)
+{
+       ssize_t retval = -1;
+       off_t baseOffset = (offset / devp->blockSize) * devp->blockSize;
+
+       retval = pread(devp->fd, buffer, devp->blockSize, baseOffset);
+       if (retval != devp->blockSize) {
+               warn("GetBlock: pread returned %zd", retval);
+       }
+       if (offset != baseOffset) {
+               size_t off = offset % devp->blockSize;
+               memmove(buffer, buffer + off, devp->blockSize - off);
+       }
+       retval = 0;
+done:
+       return retval;
+}
+
+/*
+ * Initialize a VolumeObject.  Simple function.
+ */
+VolumeObjects_t *
+InitVolumeObject(struct DeviceInfo *devp, struct VolumeDescriptor *vdp)
+{
+       VolumeObjects_t *retval = NULL;
+
+       retval = malloc(sizeof(*retval));
+       if (retval) {
+               retval->devp = devp;
+               retval->vdp = vdp;
+               retval->count = 0;
+               retval->byteCount = 0;
+               retval->list = NULL;
+       }
+
+done:
+       return retval;
+}
+
+/*
+ * Add an extent (<start, length> pair) to a volume list.
+ * Note that this doesn't try to see if an extent is already
+ * in the list; the presumption is that an fsck_hfs run will
+ * note overlapping extents in that case.  It adds the extents
+ * in groups of kExtentCount; the goal here is to minimize the
+ * number of objects we allocate, while still trying to keep
+ * the waste memory allocation low.
+ */
+int
+AddExtent(VolumeObjects_t *vdp, off_t start, off_t length)
+{
+       int retval = 0;
+       size_t indx;
+       ExtentList_t **ep = &vdp->list;
+
+       if (debug) printf("AddExtent(%p, %lld, %lld)\n", vdp, start, length);
+       while (*ep) {
+               if ((*ep)->count < kExtentCount) {
+                       indx = (*ep)->count;
+                       (*ep)->extents[indx].base = start;
+                       (*ep)->extents[indx].length = length;
+                       (*ep)->count++;
+                       break;
+               } else {
+                       ep = &(*ep)->next;
+               }
+       }
+       if (*ep == NULL) {
+               *ep = malloc(sizeof(ExtentList_t));
+               if (*ep == NULL) {
+                       err(1, "cannot allocate a new ExtentList object");
+               }
+               (*ep)->count = 1;
+               (*ep)->extents[0].base = start;
+               (*ep)->extents[0].length = length;
+               (*ep)->next = NULL;
+       }
+       vdp->count++;
+       vdp->byteCount += length;
+
+done:
+       return retval;
+}
+
+// Debugging function
+void
+PrintVolumeObject(VolumeObjects_t *vop)
+{
+       ExtentList_t *exts;
+
+       printf("Volume Information\n");
+       if (vop->devp) {
+               printf("\tDevice %s\n", vop->devp->devname);
+               printf("\t\tSize %lld\n", vop->devp->size);
+               printf("\t\tBlock size %d\n", vop->devp->blockSize);
+               printf("\t\tBlock Count %lld\n", vop->devp->blockCount);
+       }
+       printf("\tObject count %zu\n", vop->count);
+       printf("\tByte count %lld\n", vop->byteCount);
+       printf("\tExtent list:\n");
+       for (exts = vop->list;
+            exts;
+            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);
+               }
+       }
+       return;
+}
+
+/*
+ * The main routine:  given a Volume descriptor, copy the metadata from it
+ * to the given destination object (a device or sparse bundle).  It keeps
+ * track of progress, and also takes an amount to skip (which happens if it's
+ * resuming an earlier, interrupted copy).
+ */
+int
+CopyObjectsToDest(VolumeObjects_t *vop, struct IOWrapper *wrapper, off_t skip)
+{
+       ExtentList_t *exts;
+       off_t total = 0;
+
+       if (skip == 0) {
+               wrapper->cleanup(wrapper);
+       }
+       for (exts = vop->list;
+            exts;
+            exts = exts->next) {
+               int indx;
+               for (indx = 0; indx < exts->count; indx++) {
+                       off_t start = exts->extents[indx].base;
+                       off_t len = exts->extents[indx].length;
+                       if (skip < len) {
+                               __block off_t totalWritten;
+                               void (^bp)(off_t);
+
+                               if (skip) {
+                                       off_t amt = MIN(skip, len);
+                                       len -= amt;
+                                       start += amt;
+                                       total += amt;
+                                       skip -= amt;
+                                       wrapper->setprog(wrapper, total);
+                                       if (debug)
+                                               printf("* * * Wrote %lld of %lld\n", total, vop->byteCount);
+                                       else
+                                               printf("%d%%\n", (int)((total * 100) / vop->byteCount));
+                                       fflush(stdout);
+                               }
+                               totalWritten = total;
+                               if (printProgress) {
+                                       bp = ^(off_t amt) {
+                                               totalWritten += amt;
+                                               wrapper->setprog(wrapper, totalWritten);
+                                               if (debug)
+                                                       printf("* * Wrote %lld of %lld (%d%%)\n", totalWritten, vop->byteCount, (int)((totalWritten * 100) / vop->byteCount));
+                                               else
+                                                       printf("%d%%\n", (int)((totalWritten * 100) / vop->byteCount));
+                                               fflush(stdout);
+                                               return;
+                                       };
+                               } else {
+                                       bp = ^(off_t amt) {
+                                               totalWritten += amt;
+                                               return;
+                                       };
+                               }
+                               if (wrapper->writer(wrapper, vop->devp, start, len, bp) == -1) {
+                                       int t = errno;
+                                       if (verbose)
+                                               warnx("Writing extent <%lld, %lld> failed", start, len);
+                                       errno = t;
+                                       return -1;
+                               }
+                               total = totalWritten;
+                       } else {
+                               skip -= len;
+                               total += len;
+                               if (printProgress) {
+                                       wrapper->setprog(wrapper, total);
+                                       if (debug)
+                                               printf("Wrote %lld of %lld\n", total, vop->byteCount);
+                                       else
+                                               printf("%d%%\n", (int)((total * 100) / vop->byteCount));
+                                       fflush(stdout);
+                               }
+                       }
+               }
+       }
+
+       if (total == vop->byteCount) {
+               wrapper->setprog(wrapper, 0);   // remove progress
+       }
+
+       return 0;
+}