]>
git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/SparseBundle.c
8c7c2daba361fb9e72ff3f5ad76ff19f7f966173
  10 #include <sys/fcntl.h> 
  11 #include <removefile.h> 
  13 #include <CoreFoundation/CoreFoundation.h> 
  14 #include <System/sys/fsctl.h>  
  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 
  28         ({ __typeof(a) __a = (a); __typeof(b) __b = (b); \ 
  29                 __a < __b ? __a : __b; }) 
  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 
  38 struct SparseBundleContext 
{ 
  41         int cfd
;        // Cached file descriptor 
  42         int cBandNum
;   // cached bandfile number 
  45 static const int kBandSize 
= 8388608; 
  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" 
  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" 
  62                 "\t<integer>%llu</integer>\n" 
  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. 
  72 doSparseRead(struct IOWrapper 
*context
, off_t offset
, void *buffer
, off_t len
) 
  74         struct SparseBundleContext 
*ctx 
= context
->context
; 
  75         off_t blockSize 
= ctx
->bandSize
; 
  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 
  87                 asprintf(&bandName
, "%s/bands/%llx", ctx
->pathname
, bandNum
); 
  89                         warn("Cannot allocate memory for path '%s/bands/%llx'", ctx
->pathname
, bandNum
); 
  93                 fd 
= open(bandName
, O_RDONLY
); 
  95                         if (errno 
== ENOENT
) { 
  96                                 // Doesn't exist, so we just write zeroes 
  98                                 memset(buffer 
+ nread
, 0, amount
); 
 102                         warn("Cannot open band file %s for offset %llu", bandName
, offset 
+ nread
); 
 108                 n 
= pread(fd
, (char*)buffer 
+ nread
, amount
, bandOffset
); 
 110                         warn("Cannot read from band file %s for offset %llu for amount %zu", bandName
, offset
+nread
, amount
); 
 115                 if (n 
< amount
) {       // hit EOF, pad out with zeroes 
 116                         memset(buffer 
+ nread 
+ amount
, 0, amount 
- n
); 
 129  * Write a chunk of data to a bundle. 
 132 doSparseWrite(IOWrapper_t 
*context
, off_t offset
, void *buffer
, off_t len
) 
 134         struct SparseBundleContext 
*ctx 
= context
->context
; 
 135         off_t blockSize 
= ctx
->bandSize
; 
 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 
 146                 if (ctx
->cfd 
== -1 || ctx
->cBandNum 
!= bandNum
) { 
 147                         char *bandName 
= NULL
; 
 149                         if (ctx
->cfd 
!= -1) { 
 153                         asprintf(&bandName
, "%s/bands/%llx", ctx
->pathname
, bandNum
); 
 155                                 warnx("Cannot allocate memory for band %s/bands/%llx", ctx
->pathname
, bandNum
); 
 160                         fd 
= open(bandName
, O_WRONLY 
| O_CREAT
, 0666); 
 162                                 warn("Cannot open band file %s for offset %llu", bandName
, offset 
+ written
); 
 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. 
 174                         fsync_volume_np(fd
, 0); 
 175                         fcntl(fd
, F_NOCACHE
, 1); 
 179                         assert(bandNum 
< INT_MAX
); 
 180                         ctx
->cBandNum 
= (int)bandNum
; 
 184                 nwritten 
= pwrite(fd
, (char*)buffer 
+ written
, amount
, bandOffset
); 
 185                 if (nwritten 
== -1) { 
 186                         warn("Cannot write to band file %s/band/%llx for offset %llu for amount %zu", ctx
->pathname
, bandNum
, offset
+written
, amount
); 
 192                 // Sync the data out. 
 203  * Write a given extent (<start, length> pair) from an input device to the 
 204  * sparse bundle.  We also use a block to update progress. 
 207 WriteExtentToSparse(struct IOWrapper 
* context
, DeviceInfo_t 
*devp
, off_t start
, off_t len
, void (^bp
)(off_t
)) 
 209         const ssize_t bufSize 
= 1024 * 1024; 
 210         uint8_t *buffer 
= NULL
; 
 214         if (debug
) printf("Writing extent <%lld, %lld>\n", start
, len
); 
 215         buffer 
= malloc(bufSize
); 
 216         if (buffer 
== NULL
) { 
 217                 warn("%s(%s):  Could not allocate %zu bytes for buffer", __FILE__
, __FUNCTION__
, bufSize
); 
 222         while (total 
< len
) { 
 225                 ssize_t amt 
= MIN(bufSize
, len 
- total
); 
 226                 nread 
= UnalignedRead(devp
, buffer
, amt
, start 
+ total
); 
 228                         warn("Cannot read from device at offset %lld", start 
+ total
); 
 233                         warnx("Short read from source device -- got %zd, expected %zd", nread
, amt
); 
 235                 nwritten 
= doSparseWrite(context
, start 
+ total
, buffer
, nread
); 
 236                 if (nwritten 
== -1) { 
 243         if (debug
) printf("\twrote %lld\n", total
); 
 250 static const CFStringRef kBandSizeKey 
= CFSTR("band-size"); 
 251 static const CFStringRef kDevSizeKey 
= CFSTR("size"); 
 254  * We need to be able to get the size of the "device" from a sparse bundle; 
 255  * we do this by using CF routines to parse the Info.plist file, and then 
 256  * get the two keys we care about:  band-size (size of the band files), and 
 257  * size (size -- in bytes -- of the "disk"). 
 260 GetSizesFromPlist(const char *path
, size_t *bandSize
, off_t 
*devSize
) 
 263         CFReadStreamRef inFile 
= NULL
; 
 264         CFURLRef inFileURL 
= NULL
; 
 265         CFStringRef cfPath 
= NULL
; 
 266         CFPropertyListRef cfDict 
= NULL
; 
 267         CFNumberRef cfVal 
= NULL
; 
 272         inFileURL 
= CFURLCreateFromFileSystemRepresentation(NULL
, (const UInt8 
*)path
, strlen(path
), FALSE
); 
 273         if (inFileURL 
== NULL
) { 
 274                 if (debug
) warn("Cannot create url from pathname %s", path
); 
 278         inFile 
= CFReadStreamCreateWithFile(NULL
, inFileURL
); 
 279         if (inFile 
== NULL
) { 
 280                 if (debug
) warn("cannot create read stream from path %s", path
); 
 284         if (CFReadStreamOpen(inFile
) == FALSE
) { 
 285                 if (debug
) warn("cannot open read stream"); 
 289         cfDict 
= CFPropertyListCreateWithStream(NULL
, inFile
, 0, 0, NULL
, NULL
); 
 290         if (cfDict 
== NULL
) { 
 291                 if (debug
) warnx("cannot create propertly list from stream for path %s", path
); 
 295         cfVal 
= CFDictionaryGetValue(cfDict
, kBandSizeKey
); 
 297                 if (debug
) warnx("cannot get bandsize key from plist"); 
 301         if (CFNumberGetValue(cfVal
, kCFNumberIntType
, &tmpInt
) == false) { 
 302                 if (debug
) warnx("cannot get value from band size number"); 
 308         cfVal 
= CFDictionaryGetValue(cfDict
, kDevSizeKey
); 
 310                 if (debug
) warnx("cannot get dev size key from plist"); 
 313         if (CFNumberGetValue(cfVal
, kCFNumberLongLongType
, &tmpLL
) == false) { 
 325                 CFRelease(inFileURL
); 
 333 #define kProgressName "HC.progress.txt" 
 336  * Get the progress state from a sparse bundle.  If it's not there, then 
 340 GetProgress(struct IOWrapper 
*context
) 
 342         struct SparseBundleContext 
*ctx 
= context
->context
; 
 345         char progFile
[strlen(ctx
->pathname
) + sizeof(kProgressName
) + 2];       // '/' and NUL 
 347         sprintf(progFile
, "%s/%s", ctx
->pathname
, kProgressName
); 
 348         fp 
= fopen(progFile
, "r"); 
 352         if (fscanf(fp
, "%llu", &retval
) != 1) { 
 361  * Write the progress information out.  This involves writing a file in 
 362  * the sparse bundle with the amount -- in bytes -- we've written so far. 
 365 SetProgress(struct IOWrapper 
*context
, off_t prog
) 
 367         struct SparseBundleContext 
*ctx 
= context
->context
; 
 369         char progFile
[strlen(ctx
->pathname
) + sizeof(kProgressName
) + 2];       // '/' and NUL 
 371         sprintf(progFile
, "%s/%s", ctx
->pathname
, kProgressName
); 
 375                 fp 
= fopen(progFile
, "w"); 
 377                         (void)fprintf(fp
, "%llu\n", prog
); 
 385  * Clean up.  This is used when we have to initialize the bundle, but don't 
 386  * have any progress information -- in that case, we don't want to have any 
 387  * of the old band files laying around.  We use removefile() to recursively 
 388  * remove them, but keep the bands directory. 
 391 doCleanup(struct IOWrapper 
*ctx
) 
 393         struct SparseBundleContext 
*context 
= ctx
->context
; 
 395         char bandsDir
[strlen(context
->pathname
) + sizeof("/bands") + 1];        // 1 for NUL 
 397         sprintf(bandsDir
, "%s/bands", context
->pathname
); 
 400                 fprintf(stderr
, "Cleaning up, about to call removefile\n"); 
 401         rv 
= removefile(bandsDir
, NULL
, REMOVEFILE_RECURSIVE 
| REMOVEFILE_KEEP_PARENT
); 
 403                 fprintf(stderr
, "removefile returned %d\n", rv
); 
 405         return (rv 
== 0) ? 0 : -1; 
 409  * Initialize the IOWrapper structure for a sparse bundle.  This will 
 410  * create the bundle directory (but not its parents!) if needed, and 
 411  * will populate it out.  It checks to see if there is an existing bundle 
 412  * of the same name, and, if so, ensures that the izes are correct.  Then 
 413  * it sets up all the function pointers. 
 416 InitSparseBundle(const char *path
, DeviceInfo_t 
*devp
) 
 418         struct SparseBundleContext ctx 
= { 0 }; 
 419         IOWrapper_t 
*wrapper 
= NULL
; 
 421         char *tmpname 
= NULL
; 
 424         if (strstr(path
, ".sparsebundle") == NULL
) { 
 425                 asprintf(&ctx
.pathname
, "%s.sparsebundle", path
); 
 427                 ctx
.pathname 
= strdup(path
); 
 430         if (ctx
.pathname 
== NULL
) { 
 434         if (lstat(ctx
.pathname
, &sb
) == -1) { 
 435                 if (errno 
!= ENOENT
) { 
 436                         warn("cannot check sparse bundle %s", ctx
.pathname
); 
 439                 if (mkdir(ctx
.pathname
, 0777) == -1) { 
 440                         warn("cannot create sparse bundle %s", ctx
.pathname
); 
 443         } else if ((sb
.st_mode 
& S_IFMT
) != S_IFDIR
) { 
 444                 warnx("sparse bundle object %s is not a directory", ctx
.pathname
); 
 447         /* NB! All names we create inside the bundle directory are shorter 
 448          * then "Info.plist", we re-use the buffer allocated here for 
 449          * multiple file names. */ 
 450         tmplen 
= asprintf(&tmpname
, "%s/Info.plist", ctx
.pathname
); 
 451         if (tmpname 
== NULL
) { 
 455         if (stat(tmpname
, &sb
) != -1) { 
 458                 if (GetSizesFromPlist(tmpname
, &bandSize
, &devSize
) == -1) { 
 459                         warnx("Existing sparse bundle can't be parsed"); 
 463                         printf("Existing sparse bundle size = %lld, bandsize = %zu\n", devSize
, bandSize
); 
 465                 if (devSize 
!= devp
->size
) { 
 466                         warnx("Existing sparse bundle size (%lld) != dev size (%lld)", devSize
, devp
->size
); 
 469                 ctx
.bandSize 
= bandSize
; 
 471                 FILE *fp 
= fopen(tmpname
, "w"); 
 473                         warn("cannot create sparse bundle info plist %s", tmpname
); 
 476                 ctx
.bandSize 
= kBandSize
; 
 477                 if (fprintf(fp
, bundlePrototype
, kBandSize
, devp
->size
) < 0) { 
 478                         warn("failed to set bundle information in %s", tmpname
); 
 479                         fclose(fp
); /* eat error return */ 
 482                 if (fclose(fp
) != 0) { 
 483                         warn("failed to update bundle information in %s", tmpname
); 
 486                 snprintf(tmpname
, tmplen 
+ 1, "%s/Info.bckup", ctx
.pathname
); 
 487                 if ((int)strlen(tmpname
) == tmplen
) { /* Only update backup if we get the full path */ 
 488                         /* Failure to create a backup is not fatal */ 
 489                         fp 
= fopen(tmpname
, "w"); 
 491                                 if ((fprintf(fp
, bundlePrototype
, kBandSize
, devp
->size
) < 0) || 
 497                         warn("Cannot create backup bundle information file in %s", tmpname
); 
 499                 if ((snprintf(tmpname
, tmplen 
+ 1, "%s/bands", ctx
.pathname
) > tmplen
) || 
 500                         (mkdir(tmpname
, 0777) == -1)) { 
 501                         warn("cannot create bands directory in sparse bundle %s", ctx
.pathname
); 
 504                 if (snprintf(tmpname
, tmplen 
+ 1, "%s/token", ctx
.pathname
) <= tmplen
) { 
 505                         close(open(tmpname
, O_CREAT 
| O_TRUNC
, 0666)); 
 509         wrapper 
= malloc(sizeof(*wrapper
)); 
 511                 struct SparseBundleContext 
*wrapped_ctx
; 
 513                 wrapped_ctx 
= malloc(sizeof(*wrapped_ctx
)); 
 516                         wrapped_ctx
->cfd 
= -1; 
 518                         wrapper
->writer 
= &WriteExtentToSparse
; 
 519                         wrapper
->reader 
= &doSparseRead
; 
 520                         wrapper
->getprog 
= &GetProgress
; 
 521                         wrapper
->setprog 
= &SetProgress
; 
 522                         wrapper
->cleanup 
= &doCleanup
; 
 523                         wrapper
->context 
= wrapped_ctx
; 
 530         if (wrapper 
== NULL
) {