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