]> git.saurik.com Git - apple/hfs.git/blobdiff - CopyHFSMeta/DeviceWrapper.c
hfs-183.1.tar.gz
[apple/hfs.git] / CopyHFSMeta / DeviceWrapper.c
diff --git a/CopyHFSMeta/DeviceWrapper.c b/CopyHFSMeta/DeviceWrapper.c
new file mode 100644 (file)
index 0000000..27c9116
--- /dev/null
@@ -0,0 +1,152 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <sys/disk.h>
+
+#include "hfsmeta.h"
+
+/*
+ * Functions to wrap around a device.
+ */
+#define MIN(a, b) \
+       ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
+               __a < __b ? __a : __b; })
+
+struct DeviceWrapperContext {
+       char *pathname;
+       size_t blockSize;
+       off_t devSize;
+       int fd;
+};
+
+static int
+noClean(struct IOWrapper *ctx)
+{
+       // Conceivably, we could erase the entire device
+       return 0;
+}
+
+static ssize_t
+doRead(struct IOWrapper *ctx, off_t start, void *buffer, off_t len)
+{
+       // For now, just do a pread
+       struct DeviceWrapperContext *dctx = (struct DeviceWrapperContext*)ctx->context;
+
+       return pread(dctx->fd, buffer, (size_t)len, start);
+}
+
+static ssize_t
+writeExtent(struct IOWrapper *context, DeviceInfo_t *devp, off_t start, off_t len, void (^bp)(off_t))
+{
+       const size_t bufSize = 1024 * 1024;
+       struct DeviceWrapperContext *ctx = (struct DeviceWrapperContext*)context->context;
+       uint8_t buffer[bufSize];
+       off_t total = 0;
+
+       if (debug) printf("Writing extent <%lld, %lld> to device %s", start, len, ctx->pathname);
+
+       while (total < len) {
+               ssize_t nread;
+               size_t amt = MIN(bufSize, len - total);
+               nread = pread(devp->fd, buffer, amt, start + total);
+               if (nread == -1) {
+                       warn("Cannot read from device at offset %lld", start + total);
+                       return -1;
+               }
+               (void)pwrite(ctx->fd, (char*)buffer + total, amt, start + total);
+               bp(amt);
+               total += amt;
+       }
+       return 0;
+}
+
+/*
+ * Device files can't have progress information stored, so we don't do anything.
+ */
+static off_t
+GetProgress(struct IOWrapper *context)
+{
+       return 0;
+}
+static void
+SetProgress(struct IOWrapper *context, off_t progr)
+{
+       return;
+}
+
+struct IOWrapper *
+InitDeviceWrapper(const char *path, DeviceInfo_t *devp)
+{
+       struct DeviceWrapperContext ctx = { 0 };
+       struct DeviceWrapperContext *retctx = NULL;
+       IOWrapper_t *retval = NULL;
+       struct stat sb;
+       uint64_t blockCount;
+       char rawname[strlen(path) + 2]; // /dev/disk5 -> /dev/rdisk5
+
+       if (strncmp(path, "/dev/disk", 9) == 0) {
+               // Need to make it into a raw device name
+               sprintf(rawname, "/dev/rdisk%s", path + 9);
+       } else {
+               strcpy(rawname, path);
+       }
+
+       if (lstat(rawname, &sb) == -1) {
+               warn("cannot examine raw device %s", rawname);
+               goto done;
+       }
+       if ((sb.st_mode & S_IFMT) != S_IFCHR) {
+               warnx("device %s is not a raw device", rawname);
+               goto done;
+       }
+
+       ctx.pathname = strdup(rawname);
+
+       ctx.fd = open(rawname, O_RDWR);
+       if (ctx.fd == -1) {
+               warn("Cannot open device %s for reading and writing", rawname);
+               goto done;
+       }
+
+       if (ioctl(ctx.fd, DKIOCGETBLOCKSIZE, &ctx.blockSize) == -1) {
+               ctx.blockSize = 512;    // A reasonable default
+       }
+       if (ioctl(ctx.fd, DKIOCGETBLOCKCOUNT, &blockCount) == -1) {
+               warn("Cannot block count for device %s", rawname);
+               goto done;
+       }
+       ctx.devSize = ctx.blockSize * blockCount;
+
+       if (ctx.devSize != devp->size) {
+               warnx("Device %s is not the same size (%lld) as source device (%lld)", rawname, ctx.devSize, devp->size);
+               goto done;
+       }
+
+       ctx.pathname = strdup(rawname);
+       retctx = malloc(sizeof(ctx));
+       if (retctx == NULL) {
+               warn("Cannot allocate space for device context");
+               goto done;
+       }
+       *retctx = ctx;
+       retval = malloc(sizeof(*retval));
+       if (retval == NULL) {
+               warn("Cannot allocate space for device wrapper");
+               goto done;
+       }
+       retval->context = retctx;
+       retval->reader = &doRead;
+       retval->writer = &writeExtent;
+       retval->getprog = &GetProgress;
+       retval->setprog = &SetProgress;
+       retval->cleanup = &noClean;
+
+done:
+       return retval;
+}