]>
git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/SparseBundle.c
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
) {