]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/SparseBundle.c
hfs-522.0.9.tar.gz
[apple/hfs.git] / CopyHFSMeta / SparseBundle.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 <unistd.h>
9 #include <sys/stat.h>
10 #include <sys/fcntl.h>
11 #include <removefile.h>
12
13 #include <CoreFoundation/CoreFoundation.h>
14 #include <System/sys/fsctl.h>
15
16 #include "hfsmeta.h"
17 #include "Sparse.h"
18
19 /*
20 * Routines to maniupulate a sparse bundle.
21 * N.B.: The sparse bundle format it uses is a subset of
22 * the real sparse bundle format: no partition map, and
23 * no encryption.
24 */
25
26 #ifndef MIN
27 # define MIN(a, b) \
28 ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
29 __a < __b ? __a : __b; })
30 #endif
31
32 /*
33 * Context for the sparse bundle routines. The path name,
34 * size of the band files, and cached file descriptor and
35 * band numbers, to reduce the amount of pathname lookups
36 * required.
37 */
38 struct SparseBundleContext {
39 char *pathname;
40 size_t bandSize;
41 int cfd; // Cached file descriptor
42 int cBandNum; // cached bandfile number
43 };
44
45 static const int kBandSize = 8388608;
46
47 // Prototype bundle Info.plist file
48 static const char *bundlePrototype =
49 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
50 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
51 "<plist version=\"1.0\">\n"
52 "<dict>\n"
53 "\t<key>CFBundleInfoDictionaryVersion</key>\n"
54 "\t<string>6.0</string>\n"
55 "\t<key>band-size</key>\n"
56 "\t<integer>%d</integer>\n"
57 "\t<key>bundle-backingstore-version</key>\n"
58 "\t<integer>1</integer>\n"
59 "\t<key>diskimage-bundle-type</key>\n"
60 "\t<string>com.apple.diskimage.sparsebundle</string>\n"
61 "\t<key>size</key>\n"
62 "\t<integer>%llu</integer>\n"
63 "</dict>\n"
64 "</plist>\n";
65
66
67 /*
68 * Read from a sparse bundle. If the band file doesn't exist, or is shorter than
69 * what we need to get from it, we pad out with 0's.
70 */
71 static ssize_t
72 doSparseRead(struct IOWrapper *context, off_t offset, void *buffer, off_t len)
73 {
74 struct SparseBundleContext *ctx = context->context;
75 off_t blockSize = ctx->bandSize;
76 ssize_t nread = 0;
77 ssize_t retval = -1;
78
79 while (nread < len) {
80 off_t bandNum = (offset + nread) / blockSize; // Which band file to use
81 off_t bandOffset = (offset + nread) % blockSize; // how far to go into the file
82 ssize_t amount = MIN(len - nread, blockSize - bandOffset); // How many bytes to write in this band file
83 char *bandName;
84 ssize_t n;
85 int fd;
86
87 asprintf(&bandName, "%s/bands/%llx", ctx->pathname, bandNum);
88 if (!bandName) {
89 warn("Cannot allocate memory for path '%s/bands/%llx'", ctx->pathname, bandNum);
90 retval = -1;
91 goto done;
92 }
93 fd = open(bandName, O_RDONLY);
94 if (fd == -1) {
95 if (errno == ENOENT) {
96 // Doesn't exist, so we just write zeroes
97 free(bandName);
98 memset(buffer + nread, 0, amount);
99 nread += amount;
100 continue;
101 }
102 warn("Cannot open band file %s for offset %llu", bandName, offset + nread);
103 retval = -1;
104 free(bandName);
105 goto done;
106 }
107
108 n = pread(fd, (char*)buffer + nread, amount, bandOffset);
109 if (n == -1) {
110 warn("Cannot read from band file %s for offset %llu for amount %zu", bandName, offset+nread, amount);
111 close(fd);
112 free(bandName);
113 goto done;
114 }
115 if (n < amount) { // hit EOF, pad out with zeroes
116 memset(buffer + nread + amount, 0, amount - n);
117 }
118 free(bandName);
119 close(fd);
120 nread += n;
121 }
122 retval = nread;
123 done:
124 return retval;
125
126 }
127
128 /*
129 * Write a chunk of data to a bundle.
130 */
131 static ssize_t
132 doSparseWrite(IOWrapper_t *context, off_t offset, void *buffer, off_t len)
133 {
134 struct SparseBundleContext *ctx = context->context;
135 off_t blockSize = ctx->bandSize;
136 ssize_t written = 0;
137 ssize_t retval = -1;
138
139 while (written < len) {
140 off_t bandNum = (offset + written) / blockSize; // Which band file to use
141 off_t bandOffset = (offset + written) % blockSize; // how far to go into the file
142 size_t amount = MIN(len - written, blockSize - bandOffset); // How many bytes to write in this band file
143 ssize_t nwritten;
144 int fd;
145
146 if (ctx->cfd == -1 || ctx->cBandNum != bandNum) {
147 char *bandName = NULL;
148
149 if (ctx->cfd != -1) {
150 close(ctx->cfd);
151 ctx->cfd = -1;
152 }
153 asprintf(&bandName, "%s/bands/%llx", ctx->pathname, bandNum);
154 if (!bandName) {
155 warnx("Cannot allocate memory for band %s/bands/%llx", ctx->pathname, bandNum);
156 retval = -1;
157 goto done;
158 }
159
160 fd = open(bandName, O_WRONLY | O_CREAT, 0666);
161 if (fd == -1) {
162 warn("Cannot open band file %s for offset %llu", bandName, offset + written);
163 free(bandName);
164 retval = -1;
165 goto done;
166 }
167 /*
168 * When we create a new band file, we sync the volume
169 * it's on, so that we can ensure that the band file is present
170 * on disk. (Otherwise, with a crash, we can end up with the
171 * data not where we expected.) In this case, however, we probably
172 * don't need to wait for it -- just start the sync.
173 */
174 fsync_volume_np(fd, 0);
175 fcntl(fd, F_NOCACHE, 1);
176 free(bandName);
177 bandName = NULL;
178 ctx->cfd = fd;
179 ctx->cBandNum = bandNum;
180 } else {
181 fd = ctx->cfd;
182 }
183 nwritten = pwrite(fd, (char*)buffer + written, amount, bandOffset);
184 if (nwritten == -1) {
185 warn("Cannot write to band file %s/band/%llx for offset %llu for amount %zu", ctx->pathname, bandNum, offset+written, amount);
186 close(fd);
187 ctx->cfd = -1;
188 retval = -1;
189 goto done;
190 }
191 // Sync the data out.
192 fsync(fd);
193 written += nwritten;
194 }
195 retval = written;
196 done:
197 return retval;
198
199 }
200
201 /*
202 * Write a given extent (<start, length> pair) from an input device to the
203 * sparse bundle. We also use a block to update progress.
204 */
205 static ssize_t
206 WriteExtentToSparse(struct IOWrapper * context, DeviceInfo_t *devp, off_t start, off_t len, void (^bp)(off_t))
207 {
208 const ssize_t bufSize = 1024 * 1024;
209 uint8_t *buffer = NULL;
210 ssize_t retval = 0;
211 off_t total = 0;
212
213 if (debug) printf("Writing extent <%lld, %lld>\n", start, len);
214 buffer = malloc(bufSize);
215 if (buffer == NULL) {
216 warn("%s(%s): Could not allocate %zu bytes for buffer", __FILE__, __FUNCTION__, bufSize);
217 retval = -1;
218 goto done;
219 }
220
221 while (total < len) {
222 ssize_t nread;
223 ssize_t nwritten;
224 ssize_t amt = MIN(bufSize, len - total);
225 nread = UnalignedRead(devp, buffer, amt, start + total);
226 if (nread == -1) {
227 warn("Cannot read from device at offset %lld", start + total);
228 retval = -1;
229 break;
230 }
231 if (nread < amt) {
232 warnx("Short read from source device -- got %zd, expected %zd", nread, amt);
233 }
234 nwritten = doSparseWrite(context, start + total, buffer, nread);
235 if (nwritten == -1) {
236 retval = -1;
237 break;
238 }
239 bp(nread);
240 total += nread;
241 }
242 if (debug) printf("\twrote %lld\n", total);
243 done:
244 if (buffer)
245 free(buffer);
246 return retval;
247 }
248
249 static const CFStringRef kBandSizeKey = CFSTR("band-size");
250 static const CFStringRef kDevSizeKey = CFSTR("size");
251
252 /*
253 * We need to be able to get the size of the "device" from a sparse bundle;
254 * we do this by using CF routines to parse the Info.plist file, and then
255 * get the two keys we care about: band-size (size of the band files), and
256 * size (size -- in bytes -- of the "disk").
257 */
258 static int
259 GetSizesFromPlist(const char *path, size_t *bandSize, off_t *devSize)
260 {
261 int retval = -1;
262 CFReadStreamRef inFile = NULL;
263 CFURLRef inFileURL = NULL;
264 CFStringRef cfPath = NULL;
265 CFPropertyListRef cfDict = NULL;
266 CFNumberRef cfVal = NULL;
267 int tmpInt;
268 long long tmpLL;
269
270
271 inFileURL = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), FALSE);
272 if (inFileURL == NULL) {
273 if (debug) warn("Cannot create url from pathname %s", path);
274 goto done;
275 }
276
277 inFile = CFReadStreamCreateWithFile(NULL, inFileURL);
278 if (inFile == NULL) {
279 if (debug) warn("cannot create read stream from path %s", path);
280 goto done;
281 }
282
283 if (CFReadStreamOpen(inFile) == FALSE) {
284 if (debug) warn("cannot open read stream");
285 goto done;
286 }
287
288 cfDict = CFPropertyListCreateWithStream(NULL, inFile, 0, 0, NULL, NULL);
289 if (cfDict == NULL) {
290 if (debug) warnx("cannot create propertly list from stream for path %s", path);
291 goto done;
292 }
293
294 cfVal = CFDictionaryGetValue(cfDict, kBandSizeKey);
295 if (cfVal == NULL) {
296 if (debug) warnx("cannot get bandsize key from plist");
297 goto done;
298 }
299
300 if (CFNumberGetValue(cfVal, kCFNumberIntType, &tmpInt) == false) {
301 if (debug) warnx("cannot get value from band size number");
302 goto done;
303 } else {
304 *bandSize = tmpInt;
305 }
306
307 cfVal = CFDictionaryGetValue(cfDict, kDevSizeKey);
308 if (cfVal == NULL) {
309 if (debug) warnx("cannot get dev size key from plist");
310 goto done;
311 }
312 if (CFNumberGetValue(cfVal, kCFNumberLongLongType, &tmpLL) == false) {
313 goto done;
314 } else {
315 *devSize = tmpLL;
316 }
317 retval = 0;
318
319 done:
320
321 if (cfPath)
322 CFRelease(cfPath);
323 if (inFileURL)
324 CFRelease(inFileURL);
325 if (inFile)
326 CFRelease(inFile);
327 if (cfDict)
328 CFRelease(cfDict);
329 return retval;
330 }
331
332 #define kProgressName "HC.progress.txt"
333
334 /*
335 * Get the progress state from a sparse bundle. If it's not there, then
336 * no progress.
337 */
338 static off_t
339 GetProgress(struct IOWrapper *context)
340 {
341 struct SparseBundleContext *ctx = context->context;
342 FILE *fp = NULL;
343 off_t retval = 0;
344 char progFile[strlen(ctx->pathname) + sizeof(kProgressName) + 2]; // '/' and NUL
345
346 sprintf(progFile, "%s/%s", ctx->pathname, kProgressName);
347 fp = fopen(progFile, "r");
348 if (fp == NULL) {
349 goto done;
350 }
351 if (fscanf(fp, "%llu", &retval) != 1) {
352 retval = 0;
353 }
354 fclose(fp);
355 done:
356 return retval;
357 }
358
359 /*
360 * Write the progress information out. This involves writing a file in
361 * the sparse bundle with the amount -- in bytes -- we've written so far.
362 */
363 static void
364 SetProgress(struct IOWrapper *context, off_t prog)
365 {
366 struct SparseBundleContext *ctx = context->context;
367 FILE *fp = NULL;
368 char progFile[strlen(ctx->pathname) + sizeof(kProgressName) + 2]; // '/' and NUL
369
370 sprintf(progFile, "%s/%s", ctx->pathname, kProgressName);
371 if (prog == 0) {
372 remove(progFile);
373 } else {
374 fp = fopen(progFile, "w");
375 if (fp) {
376 (void)fprintf(fp, "%llu\n", prog);
377 fclose(fp);
378 }
379 }
380 return;
381 }
382
383 /*
384 * Clean up. This is used when we have to initialize the bundle, but don't
385 * have any progress information -- in that case, we don't want to have any
386 * of the old band files laying around. We use removefile() to recursively
387 * remove them, but keep the bands directory.
388 */
389 int
390 doCleanup(struct IOWrapper *ctx)
391 {
392 struct SparseBundleContext *context = ctx->context;
393 int rv = 0;
394 char bandsDir[strlen(context->pathname) + sizeof("/bands") + 1]; // 1 for NUL
395
396 sprintf(bandsDir, "%s/bands", context->pathname);
397
398 if (debug)
399 fprintf(stderr, "Cleaning up, about to call removefile\n");
400 rv = removefile(bandsDir, NULL, REMOVEFILE_RECURSIVE | REMOVEFILE_KEEP_PARENT);
401 if (debug)
402 fprintf(stderr, "removefile returned %d\n", rv);
403
404 return (rv == 0) ? 0 : -1;
405 }
406
407 /*
408 * Initialize the IOWrapper structure for a sparse bundle. This will
409 * create the bundle directory (but not its parents!) if needed, and
410 * will populate it out. It checks to see if there is an existing bundle
411 * of the same name, and, if so, ensures that the izes are correct. Then
412 * it sets up all the function pointers.
413 */
414 struct IOWrapper *
415 InitSparseBundle(const char *path, DeviceInfo_t *devp)
416 {
417 struct SparseBundleContext ctx = { 0 };
418 IOWrapper_t *wrapper = NULL;
419 struct stat sb;
420 char *tmpname = NULL;
421 int tmplen;
422
423 if (strstr(path, ".sparsebundle") == NULL) {
424 asprintf(&ctx.pathname, "%s.sparsebundle", path);
425 } else {
426 ctx.pathname = strdup(path);
427 }
428
429 if (ctx.pathname == NULL) {
430 return NULL;
431 }
432
433 if (lstat(ctx.pathname, &sb) == -1) {
434 if (errno != ENOENT) {
435 warn("cannot check sparse bundle %s", ctx.pathname);
436 goto done;
437 }
438 if (mkdir(ctx.pathname, 0777) == -1) {
439 warn("cannot create sparse bundle %s", ctx.pathname);
440 goto done;
441 }
442 } else if ((sb.st_mode & S_IFMT) != S_IFDIR) {
443 warnx("sparse bundle object %s is not a directory", ctx.pathname);
444 goto done;
445 }
446 /* NB! All names we create inside the bundle directory are shorter
447 * then "Info.plist", we re-use the buffer allocated here for
448 * multiple file names. */
449 tmplen = asprintf(&tmpname, "%s/Info.plist", ctx.pathname);
450 if (tmpname == NULL) {
451 goto done;
452 }
453
454 if (stat(tmpname, &sb) != -1) {
455 size_t bandSize = 0;
456 off_t devSize = 0;
457 if (GetSizesFromPlist(tmpname, &bandSize, &devSize) == -1) {
458 warnx("Existing sparse bundle can't be parsed");
459 goto done;
460 }
461 if (debug)
462 printf("Existing sparse bundle size = %lld, bandsize = %zu\n", devSize, bandSize);
463
464 if (devSize != devp->size) {
465 warnx("Existing sparse bundle size (%lld) != dev size (%lld)", devSize, devp->size);
466 goto done;
467 }
468 ctx.bandSize = bandSize;
469 } else {
470 FILE *fp = fopen(tmpname, "w");
471 if (fp == NULL) {
472 warn("cannot create sparse bundle info plist %s", tmpname);
473 goto done;
474 }
475 ctx.bandSize = kBandSize;
476 if (fprintf(fp, bundlePrototype, kBandSize, devp->size) < 0) {
477 warn("failed to set bundle information in %s", tmpname);
478 fclose(fp); /* eat error return */
479 goto done;
480 }
481 if (fclose(fp) != 0) {
482 warn("failed to update bundle information in %s", tmpname);
483 goto done;
484 }
485 snprintf(tmpname, tmplen + 1, "%s/Info.bckup", ctx.pathname);
486 if ((int)strlen(tmpname) == tmplen) { /* Only update backup if we get the full path */
487 /* Failure to create a backup is not fatal */
488 fp = fopen(tmpname, "w");
489 if (fp) {
490 if ((fprintf(fp, bundlePrototype, kBandSize, devp->size) < 0) ||
491 (fclose(fp) < 0)) {
492 unlink(tmpname);
493 }
494 }
495 } else {
496 warn("Cannot create backup bundle information file in %s", tmpname);
497 }
498 if ((snprintf(tmpname, tmplen + 1, "%s/bands", ctx.pathname) > tmplen) ||
499 (mkdir(tmpname, 0777) == -1)) {
500 warn("cannot create bands directory in sparse bundle %s", ctx.pathname);
501 goto done;
502 }
503 if (snprintf(tmpname, tmplen + 1, "%s/token", ctx.pathname) <= tmplen) {
504 close(open(tmpname, O_CREAT | O_TRUNC, 0666));
505 }
506 }
507
508 wrapper = malloc(sizeof(*wrapper));
509 if (wrapper) {
510 struct SparseBundleContext *wrapped_ctx;
511
512 wrapped_ctx = malloc(sizeof(*wrapped_ctx));
513 if (wrapped_ctx) {
514 *wrapped_ctx = ctx;
515 wrapped_ctx->cfd = -1;
516
517 wrapper->writer = &WriteExtentToSparse;
518 wrapper->reader = &doSparseRead;
519 wrapper->getprog = &GetProgress;
520 wrapper->setprog = &SetProgress;
521 wrapper->cleanup = &doCleanup;
522 wrapper->context = wrapped_ctx;
523 } else {
524 free(wrapper);
525 wrapper = NULL;
526 }
527 }
528 done:
529 if (wrapper == NULL) {
530 if (ctx.pathname)
531 free(ctx.pathname);
532 }
533
534 free(tmpname);
535 return wrapper;
536 }