1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <unistd.h> 5#include <fcntl.h> 6#include <err.h> 7#include <errno.h> 8#include <sys/stat.h> 9#include <sys/fcntl.h> 10#include <sys/disk.h> 11 12#include "hfsmeta.h" 13 14/* 15 * Functions to wrap around a device. 16 */ 17#define MIN(a, b) \ 18 ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \ 19 __a < __b ? __a : __b; }) 20 21struct DeviceWrapperContext { 22 char *pathname; 23 size_t blockSize; 24 off_t devSize; 25 int fd; 26}; 27 28static int 29noClean(struct IOWrapper *ctx) 30{ 31 // Conceivably, we could erase the entire device 32 return 0; 33} 34 35static ssize_t 36doRead(struct IOWrapper *ctx, off_t start, void *buffer, off_t len) 37{ 38 // For now, just do a pread 39 struct DeviceWrapperContext *dctx = (struct DeviceWrapperContext*)ctx->context; 40 41 return pread(dctx->fd, buffer, (size_t)len, start); 42} 43 44static ssize_t 45writeExtent(struct IOWrapper *context, DeviceInfo_t *devp, off_t start, off_t len, void (^bp)(off_t)) 46{ 47 const size_t bufSize = 1024 * 1024; 48 struct DeviceWrapperContext *ctx = (struct DeviceWrapperContext*)context->context; 49 uint8_t *buffer = NULL; 50 ssize_t retval = 0; 51 off_t total = 0; 52 53 if (debug) printf("Writing extent <%lld, %lld> to device %s", start, len, ctx->pathname); 54 55 buffer = malloc(bufSize); 56 if (buffer == NULL) { 57 warn("%s(%s): Could not allocate %zu bytes for buffer", __FILE__, __FUNCTION__, bufSize); 58 retval = -1; 59 goto done; 60 } 61 62 while (total < len) { 63 ssize_t nread; 64 size_t amt = MIN(bufSize, len - total); 65 // XXX - currently, DeviceWrapepr isn't used, but it needs to deal wit unaligned I/O when it is. 66 nread = pread(devp->fd, buffer, amt, start + total); 67 if (nread == -1) { 68 warn("Cannot read from device at offset %lld", start + total); 69 retval = -1; 70 goto done; 71 } 72 (void)pwrite(ctx->fd, (char*)buffer, nread, start + total); 73 bp(nread); 74 total += nread; 75 } 76done: 77 if (buffer) 78 free(buffer); 79 return retval; 80} 81 82/* 83 * Device files can't have progress information stored, so we don't do anything. 84 */ 85static off_t 86GetProgress(struct IOWrapper *context) 87{ 88 return 0; 89} 90static void 91SetProgress(struct IOWrapper *context, off_t progr) 92{ 93 return; 94} 95 96struct IOWrapper * 97InitDeviceWrapper(const char *path, DeviceInfo_t *devp) 98{ 99 struct DeviceWrapperContext ctx = { 0 }; 100 struct DeviceWrapperContext *retctx = NULL; 101 IOWrapper_t *retval = NULL; 102 struct stat sb; 103 uint64_t blockCount; 104 char rawname[strlen(path) + 2]; // /dev/disk5 -> /dev/rdisk5 105 106 if (strncmp(path, "/dev/disk", 9) == 0) { 107 // Need to make it into a raw device name 108 sprintf(rawname, "/dev/rdisk%s", path + 9); 109 } else { 110 strcpy(rawname, path); 111 } 112 113 if (lstat(rawname, &sb) == -1) { 114 warn("cannot examine raw device %s", rawname); 115 goto done; 116 } 117 if ((sb.st_mode & S_IFMT) != S_IFCHR) { 118 warnx("device %s is not a raw device", rawname); 119 goto done; 120 } 121 122 ctx.pathname = strdup(rawname); 123 124 ctx.fd = open(rawname, O_RDWR); 125 if (ctx.fd == -1) { 126 warn("Cannot open device %s for reading and writing", rawname); 127 goto done; 128 } 129 130 if (ioctl(ctx.fd, DKIOCGETBLOCKSIZE, &ctx.blockSize) == -1) { 131 ctx.blockSize = 512; // A reasonable default 132 } 133 if (ioctl(ctx.fd, DKIOCGETBLOCKCOUNT, &blockCount) == -1) { 134 warn("Cannot block count for device %s", rawname); 135 goto done; 136 } 137 ctx.devSize = ctx.blockSize * blockCount; 138 139 if (ctx.devSize != devp->size) { 140 warnx("Device %s is not the same size (%lld) as source device (%lld)", rawname, ctx.devSize, devp->size); 141 goto done; 142 } 143 144 ctx.pathname = strdup(rawname); 145 retctx = malloc(sizeof(ctx)); 146 if (retctx == NULL) { 147 warn("Cannot allocate space for device context"); 148 goto done; 149 } 150 *retctx = ctx; 151 retval = malloc(sizeof(*retval)); 152 if (retval == NULL) { 153 warn("Cannot allocate space for device wrapper"); 154 goto done; 155 } 156 retval->context = retctx; 157 retval->reader = &doRead; 158 retval->writer = &writeExtent; 159 retval->getprog = &GetProgress; 160 retval->setprog = &SetProgress; 161 retval->cleanup = &noClean; 162 163done: 164 return retval; 165} 166