--- /dev/null
+#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;
+}