]>
Commit | Line | Data |
---|---|---|
1 | #include <stdio.h> | |
2 | #include <stdlib.h> | |
3 | #include <unistd.h> | |
4 | #include <string.h> | |
5 | #include <fcntl.h> | |
6 | #include <err.h> | |
7 | #include <errno.h> | |
8 | #include <sys/stat.h> | |
9 | #include <sys/disk.h> | |
10 | #include <sys/sysctl.h> | |
11 | #include <hfs/hfs_mount.h> | |
12 | #include <Block.h> | |
13 | #include "hfsmeta.h" | |
14 | #include "Data.h" | |
15 | ||
16 | /* | |
17 | * Open the source device. In addition to opening the device, | |
18 | * this also attempts to flush the journal, and then sets up a | |
19 | * DeviceInfo_t object that will be used when doing the actual | |
20 | * reading. | |
21 | */ | |
22 | __private_extern__ | |
23 | DeviceInfo_t * | |
24 | OpenDevice(const char *devname, int flushJournal) | |
25 | { | |
26 | DeviceInfo_t *retval = NULL; | |
27 | int fd; | |
28 | DeviceInfo_t dev = { 0 }; | |
29 | struct stat sb; | |
30 | struct vfsconf vfc; | |
31 | ||
32 | if (stat(devname, &sb) == -1) { | |
33 | err(kBadExit, "cannot open device %s", devname); | |
34 | } | |
35 | /* | |
36 | * Attempt to flush the journal if requested. If it fails, we just warn, but don't abort. | |
37 | */ | |
38 | if (flushJournal && getvfsbyname("hfs", &vfc) == 0) { | |
39 | int rv; | |
40 | int mib[4]; | |
41 | char block_device[MAXPATHLEN+1]; | |
42 | int jfd; | |
43 | ||
44 | /* | |
45 | * The journal replay code, sadly, requires a block device. | |
46 | * So we need to go from the raw device to block device, if | |
47 | * necessary. | |
48 | */ | |
49 | if (strncmp(devname, "/dev/rdisk", 10) == 0) { | |
50 | snprintf(block_device, sizeof(block_device), "/dev/%s", devname+6); | |
51 | } else { | |
52 | snprintf(block_device, sizeof(block_device), "%s", devname); | |
53 | } | |
54 | jfd = open(block_device, O_RDWR); | |
55 | if (jfd == -1) { | |
56 | warn("Cannot open block device %s for read-write", block_device); | |
57 | } else { | |
58 | mib[0] = CTL_VFS; | |
59 | mib[1] = vfc.vfc_typenum; | |
60 | mib[2] = HFS_REPLAY_JOURNAL; | |
61 | mib[3] = jfd; | |
62 | if (debug) | |
63 | fprintf(stderr, "about to replay journal\n"); | |
64 | rv = sysctl(mib, 4, NULL, NULL, NULL, 0); | |
65 | if (rv == -1) { | |
66 | warn("cannot replay journal"); | |
67 | } | |
68 | /* This is probably not necessary, but we couldn't prove it. */ | |
69 | (void)fcntl(jfd, F_FULLFSYNC, 0); | |
70 | close(jfd); | |
71 | } | |
72 | } | |
73 | /* | |
74 | * We only allow a character device (e.g., /dev/rdisk1s2) | |
75 | * If we're given a non-character device, we'll try to turn | |
76 | * into a character device assuming a name pattern of /dev/rdisk* | |
77 | */ | |
78 | if ((sb.st_mode & S_IFMT) == S_IFCHR) { | |
79 | dev.devname = strdup(devname); | |
80 | } else if (strncmp(devname, "/dev/disk", 9) == 0) { | |
81 | // Turn "/dev/diskFoo" into "/dev/rdiskFoo" | |
82 | char tmpname[strlen(devname) + 2]; | |
83 | (void)snprintf(tmpname, sizeof(tmpname), "/dev/rdisk%s", devname + sizeof("/dev/disk") - 1); | |
84 | if (stat(tmpname, &sb) == -1) { | |
85 | err(kBadExit, "cannot open raw device %s", tmpname); | |
86 | } | |
87 | if ((sb.st_mode & S_IFMT) != S_IFCHR) { | |
88 | errx(kBadExit, "raw device %s is not a raw device", tmpname); | |
89 | } | |
90 | dev.devname = strdup(tmpname); | |
91 | } else { | |
92 | errx(kBadExit, "device name `%s' does not fit pattern", devname); | |
93 | } | |
94 | // Open with a shared lock if we're not debugging, since the hfs estimator api is invoked from the booted system | |
95 | fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_SHLOCK)); | |
96 | if (fd == -1) { | |
97 | err(kBadExit, "cannot open raw device %s", dev.devname); | |
98 | } | |
99 | // Get the block size and counts for the device. | |
100 | if (ioctl(fd, DKIOCGETBLOCKSIZE, &dev.blockSize) == -1) { | |
101 | dev.blockSize = 512; // Sane default, I hope | |
102 | } | |
103 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, &dev.blockCount) == -1) { | |
104 | err(kBadExit, "cannot get size of device %s", dev.devname); | |
105 | } | |
106 | ||
107 | dev.size = dev.blockCount * dev.blockSize; | |
108 | dev.fd = fd; | |
109 | ||
110 | retval = malloc(sizeof(*retval)); | |
111 | if (retval == NULL) { | |
112 | err(kBadExit, "cannot allocate device info structure"); | |
113 | } | |
114 | *retval = dev; | |
115 | return retval; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Get the header and alternate header for a device. | |
120 | */ | |
121 | __private_extern__ | |
122 | VolumeDescriptor_t * | |
123 | VolumeInfo(DeviceInfo_t *devp) | |
124 | { | |
125 | uint8_t buffer[devp->blockSize]; | |
126 | VolumeDescriptor_t *vdp = NULL, vd = { 0 }; | |
127 | ssize_t rv; | |
128 | ||
129 | vd.priOffset = 1024; // primary volume header is at 1024 bytes | |
130 | vd.altOffset = devp->size - 1024; // alternate header is 1024 bytes from the end | |
131 | ||
132 | rv = GetBlock(devp, vd.priOffset, buffer); | |
133 | if (rv == -1) { | |
134 | err(kBadExit, "cannot get primary volume header for device %s", devp->devname); | |
135 | } | |
136 | vd.priHeader = *(HFSPlusVolumeHeader*)buffer; | |
137 | ||
138 | rv = GetBlock(devp, vd.altOffset, buffer); | |
139 | if (rv == -1) { | |
140 | err(kBadExit, "cannot get alternate volume header for device %s", devp->devname); | |
141 | } | |
142 | vd.altHeader = *(HFSPlusVolumeHeader*)buffer; | |
143 | ||
144 | vdp = malloc(sizeof(*vdp)); | |
145 | *vdp = vd; | |
146 | ||
147 | return vdp; | |
148 | } | |
149 | ||
150 | /* | |
151 | * Compare the primary and alternate volume headers. | |
152 | * We only care about the "important" bits (namely, the | |
153 | * portions related to extents). | |
154 | */ | |
155 | __private_extern__ | |
156 | int | |
157 | CompareVolumeHeaders(VolumeDescriptor_t *vdp) | |
158 | { | |
159 | int retval = -1; | |
160 | ||
161 | #define CMP_FILE(v, f) memcmp(&(v)->priHeader.f, &(v)->altHeader.f, sizeof(v->priHeader.f)) | |
162 | ||
163 | if (vdp && | |
164 | vdp->priHeader.journalInfoBlock == vdp->altHeader.journalInfoBlock && | |
165 | CMP_FILE(vdp, allocationFile) == 0 && | |
166 | CMP_FILE(vdp, extentsFile) == 0 && | |
167 | CMP_FILE(vdp, catalogFile) == 0 && | |
168 | CMP_FILE(vdp, attributesFile) == 0 && | |
169 | CMP_FILE(vdp, startupFile) == 0) | |
170 | retval = 0; | |
171 | #undef CMP_FILE | |
172 | return retval; | |
173 | } | |
174 | ||
175 | /* | |
176 | * Only two (currently) types of signatures are valid: H+ and HX. | |
177 | */ | |
178 | static int | |
179 | IsValidSigWord(uint16_t word) { | |
180 | if (word == kHFSPlusSigWord || | |
181 | word == kHFSXSigWord) | |
182 | return 1; | |
183 | return 0; | |
184 | } | |
185 | ||
186 | /* | |
187 | * Add the volume headers to the in-core volume information list. | |
188 | */ | |
189 | __private_extern__ | |
190 | int | |
191 | AddHeaders(VolumeObjects_t *vop, int roundBlock) | |
192 | { | |
193 | int retval = 1; | |
194 | HFSPlusVolumeHeader *hp; | |
195 | uint8_t buffer[vop->devp->blockSize]; | |
196 | ssize_t rv; | |
197 | ||
198 | hp = &vop->vdp->priHeader; | |
199 | ||
200 | if (IsValidSigWord(S16(hp->signature)) == 0) { | |
201 | warnx("primary volume header signature = %x, invalid", S16(hp->signature)); | |
202 | retval = 0; | |
203 | } | |
204 | if (roundBlock) { | |
205 | AddExtent(vop, 1024 / vop->devp->blockSize, vop->devp->blockSize); | |
206 | } else { | |
207 | AddExtent(vop, 1024, 512); | |
208 | } | |
209 | ||
210 | hp = &vop->vdp->altHeader; | |
211 | ||
212 | if (IsValidSigWord(S16(hp->signature)) == 0) { | |
213 | warnx("alternate volume header signature = %x, invalid", S16(hp->signature)); | |
214 | retval = 0; | |
215 | } | |
216 | if (roundBlock) { | |
217 | AddExtent(vop, (vop->vdp->altOffset / vop->devp->blockSize) * vop->devp->blockSize, vop->devp->blockSize); | |
218 | } else { | |
219 | AddExtent(vop, vop->vdp->altOffset, 512); | |
220 | } | |
221 | ||
222 | done: | |
223 | return retval; | |
224 | } | |
225 | ||
226 | /* | |
227 | * Add the journal information to the in-core volume list. | |
228 | * This means the journal info block, the journal itself, and | |
229 | * the contents of the same as described by the alternate volume | |
230 | * header (if it's different from the primary volume header). | |
231 | */ | |
232 | __private_extern__ | |
233 | void | |
234 | AddJournal(VolumeObjects_t *vop) | |
235 | { | |
236 | DeviceInfo_t *devp = vop->devp; | |
237 | uint8_t buffer[devp->blockSize]; | |
238 | ssize_t rv; | |
239 | HFSPlusVolumeHeader *php, *ahp; | |
240 | JournalInfoBlock *jib; | |
241 | ||
242 | php = &vop->vdp->priHeader; | |
243 | ahp = &vop->vdp->altHeader; | |
244 | ||
245 | if (php->journalInfoBlock) { | |
246 | off_t jOffset = (off_t)S32(php->journalInfoBlock) * S32(php->blockSize); | |
247 | rv = GetBlock(devp, jOffset, buffer); | |
248 | if (rv == -1) { | |
249 | err(kBadExit, "cannot get primary header's copy of journal info block"); | |
250 | } | |
251 | AddExtent(vop, jOffset, sizeof(buffer)); | |
252 | jib = (JournalInfoBlock*)buffer; | |
253 | if (S32(jib->flags) & kJIJournalInFSMask) { | |
254 | AddExtent(vop, S64(jib->offset), S64(jib->size)); | |
255 | } | |
256 | } | |
257 | ||
258 | if (ahp->journalInfoBlock && | |
259 | ahp->journalInfoBlock != php->journalInfoBlock) { | |
260 | off_t jOffset = (off_t)S32(ahp->journalInfoBlock) * S32(ahp->blockSize); | |
261 | rv = GetBlock(devp, jOffset, buffer); | |
262 | if (rv == -1) { | |
263 | err(kBadExit, "cannot get alternate header's copy of journal info block"); | |
264 | } | |
265 | AddExtent(vop, jOffset, sizeof(buffer)); | |
266 | jib = (JournalInfoBlock*)buffer; | |
267 | if (S32(jib->flags) & kJIJournalInFSMask) { | |
268 | AddExtent(vop, S64(jib->offset), S64(jib->size)); | |
269 | } | |
270 | } | |
271 | ||
272 | } | |
273 | ||
274 | /* | |
275 | * Add the extents for the special files in the volume header. Compare | |
276 | * them with the alternate volume header's versions, and if they're different, | |
277 | * add that as well. | |
278 | */ | |
279 | __private_extern__ | |
280 | void | |
281 | AddFileExtents(VolumeObjects_t *vop) | |
282 | { | |
283 | int useAlt = 0; | |
284 | #define ADDEXTS(vop, file, fid) \ | |
285 | do { \ | |
286 | off_t pSize = S32(vop->vdp->priHeader.blockSize); \ | |
287 | off_t aSize = S32(vop->vdp->altHeader.blockSize); \ | |
288 | int i; \ | |
289 | if (debug) printf("Adding " #file " extents\n"); \ | |
290 | for (i = 0; i < kHFSPlusExtentDensity; i++) { \ | |
291 | HFSPlusExtentDescriptor *ep = &vop->vdp->priHeader. file .extents[i]; \ | |
292 | HFSPlusExtentDescriptor *ap = &vop->vdp->altHeader. file .extents[i]; \ | |
293 | if (debug) printf("\tExtent <%u, %u>\n", S32(ep->startBlock), S32(ep->blockCount)); \ | |
294 | if (ep->startBlock && ep->blockCount) { \ | |
295 | AddExtentForFile(vop, S32(ep->startBlock) * pSize, S32(ep->blockCount) * pSize, fid); \ | |
296 | if (memcmp(ep, ap, sizeof(*ep)) != 0) { \ | |
297 | AddExtentForFile(vop, S32(ap->startBlock) * aSize, S32(ap->blockCount) * aSize, fid); \ | |
298 | useAlt = 1; \ | |
299 | } \ | |
300 | } \ | |
301 | } \ | |
302 | } while (0) | |
303 | ||
304 | ADDEXTS(vop, allocationFile, kHFSAllocationFileID); | |
305 | ADDEXTS(vop, extentsFile, kHFSExtentsFileID); | |
306 | ADDEXTS(vop, catalogFile, kHFSCatalogFileID); | |
307 | ADDEXTS(vop, attributesFile, kHFSAttributesFileID); | |
308 | ADDEXTS(vop, startupFile, kHFSStartupFileID); | |
309 | ||
310 | #undef ADDEXTS | |
311 | ||
312 | ScanExtents(vop, 0); | |
313 | if (useAlt) | |
314 | ScanExtents(vop, useAlt); | |
315 | ||
316 | return; | |
317 | } | |
318 | ||
319 | static int | |
320 | ScanCatalogNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) | |
321 | { | |
322 | BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; | |
323 | uint16_t *indices = (uint16_t*)(buffer + nodeSize); | |
324 | size_t counter; | |
325 | off_t blockSize = S32(vop->vdp->priHeader.blockSize); | |
326 | int retval = 0; | |
327 | ||
328 | if (ndp->kind != kBTLeafNode) // Skip if it's not a leaf node | |
329 | return 0; | |
330 | ||
331 | if (debug) | |
332 | fprintf(stderr, "%s: scanning catalog node\n", __FUNCTION__); | |
333 | ||
334 | for (counter = 1; counter <= S16(ndp->numRecords); counter++) { | |
335 | // Need to get past the end of the key | |
336 | uint16_t recOffset = S16(indices[-counter]); | |
337 | HFSPlusCatalogKey *keyp = (HFSPlusCatalogKey*)(buffer + recOffset); | |
338 | size_t keyLength = S16(keyp->keyLength); | |
339 | // Add two because the keyLength field is not included. | |
340 | HFSPlusCatalogFile *fp = (HFSPlusCatalogFile*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); | |
341 | ||
342 | if (S16(fp->recordType) != kHFSPlusFileRecord) { | |
343 | if (debug) | |
344 | fprintf(stderr, "%s: skipping node record %zu because it is type %#x, at offset %u keyLength %zu\n", __FUNCTION__, counter, S16(fp->recordType), recOffset, keyLength); | |
345 | continue; | |
346 | } | |
347 | ||
348 | if (debug) | |
349 | fprintf(stderr, "%s: node record %zu, file id = %u\n", __FUNCTION__, counter, S32(fp->fileID)); | |
350 | if (S32(fp->userInfo.fdType) == kSymLinkFileType && | |
351 | S32(fp->userInfo.fdCreator) == kSymLinkCreator) { | |
352 | unsigned int fid = S32(fp->fileID); | |
353 | HFSPlusExtentDescriptor *extPtr = fp->dataFork.extents; | |
354 | int i; | |
355 | ||
356 | for (i = 0; i < 8; i++) { | |
357 | if (extPtr[i].startBlock && | |
358 | extPtr[i].blockCount) { | |
359 | off_t start = blockSize * S32(extPtr[i].startBlock); | |
360 | off_t length = blockSize * S32(extPtr[i].blockCount); | |
361 | retval = handler(fid, start, length); | |
362 | if (retval != 0) | |
363 | return retval; | |
364 | } else { | |
365 | break; | |
366 | } | |
367 | } | |
368 | } | |
369 | } | |
370 | return retval; | |
371 | } | |
372 | ||
373 | static int | |
374 | ScanAttrNode(VolumeObjects_t *vop, uint8_t *buffer, size_t nodeSize, extent_handler_t handler) | |
375 | { | |
376 | BTNodeDescriptor *ndp = (BTNodeDescriptor *)buffer; | |
377 | uint16_t *indices = (uint16_t*)(buffer + nodeSize); | |
378 | size_t counter; | |
379 | off_t blockSize = S32(vop->vdp->priHeader.blockSize); | |
380 | int retval = 0; | |
381 | ||
382 | if (ndp->kind != kBTLeafNode) | |
383 | return 0; // Skip if it's not a leaf node | |
384 | ||
385 | /* | |
386 | * Look for records of type kHFSPlusForkData and kHFSPlusAttrExtents | |
387 | */ | |
388 | for (counter = 1; counter <= S16(ndp->numRecords); counter++) { | |
389 | // Need to get past the end of the key | |
390 | unsigned int fid; | |
391 | HFSPlusAttrKey *keyp = (HFSPlusAttrKey*)(buffer + S16(indices[-counter])); | |
392 | size_t keyLength = S16(keyp->keyLength); | |
393 | // Add two because the keyLength field is not included. | |
394 | HFSPlusAttrRecord *ap = (HFSPlusAttrRecord*)(((uint8_t*)keyp) + 2 + keyLength + (keyLength & 1)); | |
395 | HFSPlusExtentDescriptor *theExtents = NULL; | |
396 | switch (S32(ap->recordType)) { | |
397 | case kHFSPlusAttrForkData: | |
398 | theExtents = ap->forkData.theFork.extents; | |
399 | break; | |
400 | case kHFSPlusAttrExtents: | |
401 | theExtents = ap->overflowExtents.extents; | |
402 | break; | |
403 | default: | |
404 | break; | |
405 | } | |
406 | if (theExtents != NULL) { | |
407 | HFSPlusExtentDescriptor *extPtr = theExtents; | |
408 | int i; | |
409 | fid = S32(keyp->fileID); | |
410 | ||
411 | for (i = 0; i < 8; i++) { | |
412 | if (extPtr[i].startBlock && | |
413 | extPtr[i].blockCount) { | |
414 | off_t start = blockSize * S32(extPtr[i].startBlock); | |
415 | off_t length = blockSize * S32(extPtr[i].blockCount); | |
416 | retval = handler(fid, start, length); | |
417 | if (retval != 0) | |
418 | return retval; | |
419 | } else { | |
420 | break; | |
421 | } | |
422 | } | |
423 | } | |
424 | } | |
425 | return retval; | |
426 | } | |
427 | ||
428 | ||
429 | /* | |
430 | * Given a VolumeObject_t, search for the other metadata that | |
431 | * aren't described by the system files, but rather in the | |
432 | * system files. This includes symbolic links, and large EA | |
433 | * extents. We can do this at one of two times -- while copying | |
434 | * the data, or while setting up the list of extents. The | |
435 | * former is going to be more efficient, but the latter will | |
436 | * mean the estimates and continuation will be less likely to | |
437 | * be wrong as we add extents to the list. | |
438 | */ | |
439 | __private_extern__ | |
440 | int | |
441 | FindOtherMetadata(VolumeObjects_t *vop, extent_handler_t handler) | |
442 | { | |
443 | size_t catNodeSize = 0, attrNodeSize = 0; | |
444 | off_t node0_location = 0; | |
445 | uint8_t *tBuffer; | |
446 | BTHeaderRec *hdp; | |
447 | BTNodeDescriptor *ndp; | |
448 | int retval = 0; | |
449 | ||
450 | tBuffer = calloc(1, vop->devp->blockSize); | |
451 | if (tBuffer == NULL) { | |
452 | warn("Could not allocate memory to collect extra metadata"); | |
453 | goto done; | |
454 | } | |
455 | /* | |
456 | * First, do the catalog file | |
457 | */ | |
458 | if (vop->vdp->priHeader.catalogFile.logicalSize) { | |
459 | ||
460 | node0_location = S32(vop->vdp->priHeader.catalogFile.extents[0].startBlock); | |
461 | node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); | |
462 | if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { | |
463 | warn("Could not read catalog header node"); | |
464 | } else { | |
465 | ndp = (BTNodeDescriptor*)tBuffer; | |
466 | hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); | |
467 | ||
468 | if (ndp->kind != kBTHeaderNode) { | |
469 | warnx("Did not read header node for catalog as expected"); | |
470 | } else { | |
471 | catNodeSize = S16(hdp->nodeSize); | |
472 | } | |
473 | } | |
474 | } | |
475 | /* | |
476 | * Now, the attributes file. | |
477 | */ | |
478 | if (vop->vdp->priHeader.attributesFile.logicalSize) { | |
479 | ||
480 | node0_location = S32(vop->vdp->priHeader.attributesFile.extents[0].startBlock); | |
481 | node0_location = node0_location * S32(vop->vdp->priHeader.blockSize); | |
482 | if (GetBlock(vop->devp, node0_location, tBuffer) == -1) { | |
483 | warn("Could not read attributes file header node"); | |
484 | } else { | |
485 | ndp = (BTNodeDescriptor*)tBuffer; | |
486 | hdp = (BTHeaderRec*)(tBuffer + sizeof(BTNodeDescriptor)); | |
487 | ||
488 | if (ndp->kind != kBTHeaderNode) { | |
489 | warnx("Did not read header node for attributes file as expected"); | |
490 | } else { | |
491 | attrNodeSize = S16(hdp->nodeSize); | |
492 | } | |
493 | } | |
494 | } | |
495 | if (debug) | |
496 | fprintf(stderr, "Catalog node size = %zu, attributes node size = %zu\n", catNodeSize, attrNodeSize); | |
497 | ||
498 | /* | |
499 | * We start reading the extents now. | |
500 | * | |
501 | * This is a lot of duplicated code, unfortunately. | |
502 | */ | |
503 | ExtentList_t *exts; | |
504 | for (exts = vop->list; | |
505 | exts; | |
506 | exts = exts->next) { | |
507 | size_t indx; | |
508 | ||
509 | for (indx = 0; indx < exts->count; indx++) { | |
510 | off_t start = exts->extents[indx].base; | |
511 | off_t len = exts->extents[indx].length; | |
512 | off_t nread = 0; | |
513 | if (exts->extents[indx].fid == 0) { | |
514 | continue; // Unknown file, skip | |
515 | } else { | |
516 | if (debug) fprintf(stderr, "%s: fid = %u, start = %llu, len = %llu\n", __FUNCTION__, exts->extents[indx].fid, start, len); | |
517 | while (nread < len) { | |
518 | size_t bufSize; | |
519 | uint8_t *buffer; | |
520 | bufSize = MIN(len - nread, 1024 * 1024); // Read 1mbyte max | |
521 | buffer = calloc(1, bufSize); | |
522 | if (buffer == NULL) { | |
523 | warn("Cannot allocate %zu bytes for buffer, skipping node scan", bufSize); | |
524 | } else { | |
525 | ssize_t t = UnalignedRead(vop->devp, buffer, bufSize, start + nread); | |
526 | if (t != bufSize) { | |
527 | warn("Attempted to read %zu bytes, only read %zd, skipping node scan", bufSize, t); | |
528 | } else { | |
529 | uint8_t *curPtr = buffer, *endPtr = (buffer + bufSize); | |
530 | size_t nodeSize = 0; | |
531 | int (*func)(VolumeObjects_t *, uint8_t *, size_t, extent_handler_t) = NULL; | |
532 | if (exts->extents[indx].fid == kHFSCatalogFileID) { | |
533 | func = ScanCatalogNode; | |
534 | nodeSize = catNodeSize; | |
535 | } else if (exts->extents[indx].fid == kHFSAttributesFileID) { | |
536 | func = ScanAttrNode; | |
537 | nodeSize = attrNodeSize; | |
538 | } | |
539 | if (func) { | |
540 | while (curPtr < endPtr && retval == 0) { | |
541 | retval = (*func)(vop, curPtr, nodeSize, handler); | |
542 | curPtr += nodeSize; | |
543 | } | |
544 | } | |
545 | } | |
546 | free(buffer); | |
547 | } | |
548 | if (retval != 0) | |
549 | goto done; | |
550 | nread += bufSize; | |
551 | } | |
552 | } | |
553 | } | |
554 | } | |
555 | ||
556 | done: | |
557 | if (tBuffer) | |
558 | free(tBuffer); | |
559 | return retval; | |
560 | } | |
561 | ||
562 | /* | |
563 | * Perform a (potentially) unaligned read from a given input device. | |
564 | */ | |
565 | __private_extern__ | |
566 | ssize_t | |
567 | UnalignedRead(DeviceInfo_t *devp, void *buffer, size_t size, off_t offset) | |
568 | { | |
569 | ssize_t nread = -1; | |
570 | size_t readSize = ((size + devp->blockSize - 1) / devp->blockSize) * devp->blockSize; | |
571 | off_t baseOffset = (offset / devp->blockSize) * devp->blockSize; | |
572 | size_t off = offset - baseOffset; | |
573 | char *tmpbuf = NULL; | |
574 | ||
575 | if ((baseOffset == offset) && (readSize == size)) { | |
576 | /* | |
577 | * The read is already properly aligned, so call pread. | |
578 | */ | |
579 | return pread(devp->fd, buffer, size, offset); | |
580 | } | |
581 | ||
582 | tmpbuf = malloc(readSize); | |
583 | if (!tmpbuf) { | |
584 | goto done; | |
585 | } | |
586 | ||
587 | nread = pread(devp->fd, tmpbuf, readSize, baseOffset); | |
588 | if (nread == -1) { | |
589 | goto done; | |
590 | } | |
591 | ||
592 | nread -= off; | |
593 | if (nread > (ssize_t)size) { | |
594 | nread = size; | |
595 | } | |
596 | memcpy(buffer, tmpbuf + off, nread); | |
597 | ||
598 | done: | |
599 | free(tmpbuf); | |
600 | return nread; | |
601 | } | |
602 | ||
603 | __private_extern__ | |
604 | void | |
605 | ReleaseDeviceInfo(DeviceInfo_t *devp) | |
606 | { | |
607 | if (devp) { | |
608 | if (devp->fd != -1) { | |
609 | close(devp->fd); | |
610 | } | |
611 | if (devp->devname) | |
612 | free(devp->devname); | |
613 | free(devp); | |
614 | } | |
615 | return; | |
616 | } | |
617 | ||
618 | __private_extern__ | |
619 | void | |
620 | ReleaseVolumeDescriptor(VolumeDescriptor_t *vdp) | |
621 | { | |
622 | if (vdp) | |
623 | free(vdp); // No contained pointers! | |
624 | return; | |
625 | } | |
626 | ||
627 | __private_extern__ | |
628 | void | |
629 | ReleaseVolumeObjects(VolumeObjects_t *vop) | |
630 | { | |
631 | if (vop) { | |
632 | if (vop->devp) { | |
633 | ReleaseDeviceInfo(vop->devp); | |
634 | } | |
635 | if (vop->vdp) { | |
636 | ReleaseVolumeDescriptor(vop->vdp); | |
637 | } | |
638 | ExtentList_t *extList; | |
639 | for (extList = vop->list; | |
640 | extList; | |
641 | ) { | |
642 | ExtentList_t *next = extList->next; | |
643 | free(extList); | |
644 | extList = next; | |
645 | } | |
646 | free(vop); | |
647 | } | |
648 | } |