]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/DeviceWrapper.c
hfs-407.1.3.tar.gz
[apple/hfs.git] / CopyHFSMeta / DeviceWrapper.c
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
21 struct DeviceWrapperContext {
22 char *pathname;
23 size_t blockSize;
24 off_t devSize;
25 int fd;
26 };
27
28 static int
29 noClean(struct IOWrapper *ctx)
30 {
31 // Conceivably, we could erase the entire device
32 return 0;
33 }
34
35 static ssize_t
36 doRead(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
44 static ssize_t
45 writeExtent(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 }
76 done:
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 */
85 static off_t
86 GetProgress(struct IOWrapper *context)
87 {
88 return 0;
89 }
90 static void
91 SetProgress(struct IOWrapper *context, off_t progr)
92 {
93 return;
94 }
95
96 struct IOWrapper *
97 InitDeviceWrapper(const char *path, DeviceInfo_t *devp)
98 {
99 struct DeviceWrapperContext ctx = { .fd = -1 };
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.fd = open(rawname, O_RDWR);
123 if (ctx.fd == -1) {
124 warn("Cannot open device %s for reading and writing", rawname);
125 goto done;
126 }
127
128 if (ioctl(ctx.fd, DKIOCGETBLOCKSIZE, &ctx.blockSize) == -1) {
129 ctx.blockSize = 512; // A reasonable default
130 }
131 if (ioctl(ctx.fd, DKIOCGETBLOCKCOUNT, &blockCount) == -1) {
132 warn("Cannot block count for device %s", rawname);
133 goto done;
134 }
135 ctx.devSize = ctx.blockSize * blockCount;
136
137 if (ctx.devSize != devp->size) {
138 warnx("Device %s is not the same size (%lld) as source device (%lld)", rawname, ctx.devSize, devp->size);
139 goto done;
140 }
141
142 ctx.pathname = strdup(rawname);
143 retctx = malloc(sizeof(ctx));
144 if (retctx == NULL) {
145 warn("Cannot allocate space for device context");
146 goto done;
147 }
148 *retctx = ctx;
149 retval = malloc(sizeof(*retval));
150 if (retval == NULL) {
151 warn("Cannot allocate space for device wrapper");
152 goto done;
153 }
154 retval->context = retctx;
155 retval->reader = &doRead;
156 retval->writer = &writeExtent;
157 retval->getprog = &GetProgress;
158 retval->setprog = &SetProgress;
159 retval->cleanup = &noClean;
160
161 done:
162 if (!retval) {
163 free(ctx.pathname);
164 free(retctx);
165 if (ctx.fd >= 0)
166 close(ctx.fd);
167 }
168
169 return retval;
170 }