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__
23DeviceInfo_t *
24OpenDevice(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	// Only use an exclusive open if we're not debugging.
95	fd = open(dev.devname, O_RDONLY | (debug ? 0 : O_EXLOCK));
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__
122VolumeDescriptor_t *
123VolumeInfo(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__
156int
157CompareVolumeHeaders(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 */
178static int
179IsValidSigWord(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__
190int
191AddHeaders(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
222done:
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__
233void
234AddJournal(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__
280void
281AddFileExtents(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
319static int
320ScanCatalogNode(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
373static int
374ScanAttrNode(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__
440int
441FindOtherMetadata(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
556done:
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__
566ssize_t
567UnalignedRead(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
598done:
599	free(tmpbuf);
600	return nread;
601}
602
603__private_extern__
604void
605ReleaseDeviceInfo(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__
619void
620ReleaseVolumeDescriptor(VolumeDescriptor_t *vdp)
621{
622	if (vdp)
623		free(vdp);	// No contained pointers!
624	return;
625}
626
627__private_extern__
628void
629ReleaseVolumeObjects(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}
649