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