X-Git-Url: https://git.saurik.com/apple/hfs.git/blobdiff_plain/ab40a2da39ee6a5080bc926494d42a74970ff044..a56bdb9dd68c843a17a0658b5250e0cc67c22fe8:/CopyHFSMeta/DeviceWrapper.c diff --git a/CopyHFSMeta/DeviceWrapper.c b/CopyHFSMeta/DeviceWrapper.c new file mode 100644 index 0000000..27c9116 --- /dev/null +++ b/CopyHFSMeta/DeviceWrapper.c @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}