]> git.saurik.com Git - apple/hfs.git/blob - CopyHFSMeta/ScanExtents.c
hfs-191.1.tar.gz
[apple/hfs.git] / CopyHFSMeta / ScanExtents.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <err.h>
5 #include <errno.h>
6
7 #include "hfsmeta.h"
8 #include "Data.h"
9
10 /*
11 * Functions to scan through the extents overflow file, grabbing
12 * overflow extents for the special files.
13 */
14
15 /*
16 * Given an extent record, return the logical block address (in the volume)
17 * for the requested block offset into the file. It returns 0 if it can't
18 * find it.
19 */
20 static unsigned int
21 FindBlock(HFSPlusExtentRecord *erp, unsigned int blockNum)
22 {
23 unsigned int lba = 0;
24 unsigned int base = 0;
25 HFSPlusExtentDescriptor *ep = &(*erp)[0];
26 int i;
27
28 for (i = 0; i < kHFSPlusExtentDensity; i++) {
29 if (ep->startBlock == 0 || ep->blockCount == 0)
30 break;
31 if ((base + S32(ep->blockCount)) > blockNum) {
32 lba = S32(ep->startBlock) + (blockNum - base);
33 break;
34 }
35 base += S32(ep->blockCount);
36 ep++;
37 }
38 return lba;
39 }
40
41 /*
42 * Get the given node from the extents-overflow file. Returns -1 on error, and
43 * 0 on success.
44 */
45 static int
46 GetNode(DeviceInfo_t *devp, HFSPlusVolumeHeader *hp, int nodeNum, int blocksPerNode, void *nodePtr)
47 {
48 int retval = 0;
49 unsigned char *ptr, *endPtr;
50 unsigned int offset;
51 HFSPlusExtentRecord *erp = &hp->extentsFile.extents;
52
53 ptr = nodePtr;
54 endPtr = ptr + (blocksPerNode * S32(hp->blockSize));
55 offset = nodeNum * blocksPerNode;
56
57 /*
58 * We have two block sizes to consider here. The device blocksize, and the
59 * btree node size.
60 */
61 while (ptr < endPtr) {
62 ssize_t rv;
63 off_t lba;
64 int i;
65
66
67 lba = FindBlock(erp, offset);
68 if (lba == 0) {
69 warnx("Cannot find block %u in extents overflow file", offset);
70 return -1;
71 }
72 lba = lba * S32(hp->blockSize);
73 for (i = 0; i < S32(hp->blockSize) / devp->blockSize; i++) {
74 // printf("Trying to get block %lld\n", lba + i);
75 rv = GetBlock(devp, lba + (i * devp->blockSize), ptr);
76 if (rv == -1) {
77 warnx("Cannot read block %u in extents overflow file", lba + i);
78 return -1;
79 }
80 ptr += devp->blockSize;
81 }
82 offset++;
83 }
84
85 done:
86 return retval;
87 }
88
89 /*
90 * Scan through an extentes overflow node, looking for File ID's less than
91 * the first user file ID. For each one it finds, it adds the extents to
92 * the volume structure list. It returns the number of the next node
93 * (which will be 0 when we've hit the end of the list); it also returns 0
94 * when it encounters a CNID larger than the system files'.
95 */
96 static unsigned int
97 ScanNode(VolumeObjects_t *vop, uint8_t *nodePtr, size_t nodeSize, off_t blockSize)
98 {
99 u_int16_t *offsetPtr;
100 BTNodeDescriptor *descp;
101 int indx;
102 int numRecords;
103 HFSPlusExtentKey *keyp;
104 HFSPlusExtentRecord *datap;
105 uint8_t *recp;
106 unsigned int retval;
107
108 descp = (BTNodeDescriptor*)nodePtr;
109
110 if (descp->kind != kBTLeafNode)
111 return 0;
112
113 numRecords = S16(descp->numRecords);
114 offsetPtr = (u_int16_t*)((uint8_t*)nodePtr + nodeSize);
115
116 retval = S32(descp->fLink);
117 for (indx = 1; indx <= numRecords; indx++) {
118 int recOffset = S16(offsetPtr[-indx]);
119 recp = nodePtr + recOffset;
120 if (recp > (nodePtr + nodeSize)) {
121 return -1; // Corrupt node
122 }
123 keyp = (HFSPlusExtentKey*)recp;
124 datap = (HFSPlusExtentRecord*)(recp + sizeof(HFSPlusExtentKey));
125 // printf("Node index #%d: fileID = %u\n", indx, S32(keyp->fileID));
126 if (S32(keyp->fileID) >= kHFSFirstUserCatalogNodeID) {
127 if (debug) printf("Done scanning extents overflow file\n");
128 retval = 0;
129 break;
130 } else {
131 int i;
132 for (i = 0; i < kHFSPlusExtentDensity; i++) {
133 off_t start = S32((*datap)[i].startBlock) * (off_t)blockSize;
134 off_t len = S32((*datap)[i].blockCount) * (off_t)blockSize;
135 if (start && len)
136 AddExtent(vop, start, len);
137 }
138 }
139 }
140
141 return retval;
142 }
143
144 /*
145 * Given a volme structure list, scan through the extents overflow file
146 * looking for system-file extents (those with a CNID < 16). If useAltHdr
147 * is set, it'll use the extents overflow descriptor in the alternate header.
148 */
149 int
150 ScanExtents(VolumeObjects_t *vop, int useAltHdr)
151 {
152 int retval = -1;
153 ssize_t rv;
154 char buffer[vop->devp->blockSize];
155 struct RootNode {
156 BTNodeDescriptor desc;
157 BTHeaderRec header;
158 } *headerNode;
159 HFSPlusVolumeHeader *hp;
160 HFSPlusExtentRecord *erp;
161 off_t vBlockSize;
162 size_t tBlockSize;
163 int blocksPerNode;
164 void *nodePtr = NULL;
165 unsigned int nodeNum = 0;
166
167 hp = useAltHdr ? &vop->vdp->altHeader : & vop->vdp->priHeader;
168 vBlockSize = S32(hp->blockSize);
169
170 rv = GetBlock(vop->devp, S32(hp->extentsFile.extents[0].startBlock) * vBlockSize, buffer);
171 if (rv == -1) {
172 warnx("Cannot get btree header node for extents file for %s header", useAltHdr ? "alternate" : "primary");
173 retval = -1;
174 goto done;
175 }
176 headerNode = (struct RootNode*)buffer;
177
178 if (headerNode->desc.kind != kBTHeaderNode) {
179 warnx("Root node is not a header node (%x)", headerNode->desc.kind);
180 goto done;
181 }
182
183 tBlockSize = S16(headerNode->header.nodeSize);
184 blocksPerNode = tBlockSize / vBlockSize;
185
186 nodePtr = malloc(tBlockSize);
187 if (nodePtr == NULL) {
188 warn("cannot allocate buffer for node");
189 goto done;
190 }
191 nodeNum = S32(headerNode->header.firstLeafNode);
192
193 if (debug) printf("nodenum = %u\n", nodeNum);
194
195 /*
196 * Iterate through the leaf nodes.
197 */
198 while (nodeNum != 0) {
199 if (debug) printf("Getting node %u\n", nodeNum);
200
201 rv = GetNode(vop->devp, hp, nodeNum, blocksPerNode, nodePtr);
202 if (rv == -1) {
203 warnx("Cannot get node %u", nodeNum);
204 retval = -1;
205 goto done;
206 }
207 nodeNum = ScanNode(vop, nodePtr, tBlockSize, vBlockSize);
208 }
209 retval = 0;
210
211 done:
212 if (nodePtr)
213 free(nodePtr);
214 return retval;
215
216 }