... | ... |
@@ -174,7 +174,8 @@ static const char *ftypes_int[] = { |
174 | 174 |
"1:0:cafebabe0000003?:Java class file:CL_TYPE_ANY:CL_TYPE_JAVA:73", |
175 | 175 |
"1:EOF-512:6b6f6c79:DMG container file:CL_TYPE_ANY:CL_TYPE_DMG:75", |
176 | 176 |
"0:0:78617221:XAR container file:CL_TYPE_ANY:CL_TYPE_XAR:75", |
177 |
- "4:1024:482B:HFS+ partition:CL_TYPE_PART_ANY:CL_TYPE_PART_HFSPLUS:75", |
|
177 |
+ "4:1024:482B0004:HFS+ partition:CL_TYPE_PART_ANY:CL_TYPE_PART_HFSPLUS:75", |
|
178 |
+ "4:1024:48580005:HFSX partition:CL_TYPE_PART_ANY:CL_TYPE_PART_HFSPLUS:75", |
|
178 | 179 |
NULL |
179 | 180 |
}; |
180 | 181 |
|
... | ... |
@@ -27,16 +27,450 @@ |
27 | 27 |
#include "hfsplus.h" |
28 | 28 |
#include "scanners.h" |
29 | 29 |
|
30 |
+static void headerrecord_to_host(hfsHeaderRecord *); |
|
31 |
+static void headerrecord_print(const char *, hfsHeaderRecord *); |
|
32 |
+static void nodedescriptor_to_host(hfsNodeDescriptor *); |
|
33 |
+static void nodedescriptor_print(const char *, hfsNodeDescriptor *); |
|
34 |
+static void forkdata_to_host(hfsPlusForkData *); |
|
35 |
+static void forkdata_print(const char *, hfsPlusForkData *); |
|
36 |
+ |
|
37 |
+static int hfsplus_volumeheader(cli_ctx *, hfsPlusVolumeHeader **); |
|
38 |
+static int hfsplus_readheader(cli_ctx *, hfsPlusVolumeHeader *, hfsNodeDescriptor *, |
|
39 |
+ hfsHeaderRecord *, int, const char *); |
|
40 |
+static int hfsplus_scanfile(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *, |
|
41 |
+ hfsPlusForkData *, const char *); |
|
42 |
+ |
|
43 |
+/* Header Record : fix endianness for useful fields */ |
|
44 |
+static void headerrecord_to_host(hfsHeaderRecord *hdr) |
|
45 |
+{ |
|
46 |
+ hdr->treeDepth = be16_to_host(hdr->treeDepth); |
|
47 |
+ hdr->rootNode = be32_to_host(hdr->rootNode); |
|
48 |
+ hdr->leafRecords = be32_to_host(hdr->leafRecords); |
|
49 |
+ hdr->firstLeafNode = be32_to_host(hdr->firstLeafNode); |
|
50 |
+ hdr->lastLeafNode = be32_to_host(hdr->lastLeafNode); |
|
51 |
+ hdr->nodeSize = be16_to_host(hdr->nodeSize); |
|
52 |
+ hdr->maxKeyLength = be16_to_host(hdr->maxKeyLength); |
|
53 |
+ hdr->totalNodes = be32_to_host(hdr->totalNodes); |
|
54 |
+ hdr->freeNodes = be32_to_host(hdr->freeNodes); |
|
55 |
+ hdr->attributes = be32_to_host(hdr->attributes); /* not too useful */ |
|
56 |
+} |
|
57 |
+ |
|
58 |
+static void headerrecord_print(const char *pfx, hfsHeaderRecord *hdr) |
|
59 |
+{ |
|
60 |
+ cli_dbgmsg("%s Header: depth %hu root %u leafRecords %u firstLeaf %u lastLeaf %u nodeSize %hu\n", |
|
61 |
+ pfx, hdr->treeDepth, hdr->rootNode, hdr->leafRecords, hdr->firstLeafNode, |
|
62 |
+ hdr->lastLeafNode, hdr->nodeSize); |
|
63 |
+ cli_dbgmsg("%s Header: maxKeyLength %hu totalNodes %u freeNodes %u btreeType %hu attributes %x\n", |
|
64 |
+ pfx, hdr->maxKeyLength, hdr->totalNodes, hdr->freeNodes, |
|
65 |
+ hdr->btreeType, hdr->attributes); |
|
66 |
+} |
|
67 |
+ |
|
68 |
+/* Node Descriptor : fix endianness for useful fields */ |
|
69 |
+static void nodedescriptor_to_host(hfsNodeDescriptor *node) |
|
70 |
+{ |
|
71 |
+ node->fLink = be32_to_host(node->fLink); |
|
72 |
+ node->bLink = be32_to_host(node->bLink); |
|
73 |
+ node->numRecords = be16_to_host(node->numRecords); |
|
74 |
+} |
|
75 |
+ |
|
76 |
+static void nodedescriptor_print(const char *pfx, hfsNodeDescriptor *node) |
|
77 |
+{ |
|
78 |
+ cli_dbgmsg("%s Desc: fLink %u bLink %u kind %d height %u numRecords %u\n", |
|
79 |
+ pfx, node->fLink, node->bLink, node->kind, node->height, node->numRecords); |
|
80 |
+} |
|
81 |
+ |
|
82 |
+/* ForkData : fix endianness */ |
|
83 |
+static void forkdata_to_host(hfsPlusForkData *fork) |
|
84 |
+{ |
|
85 |
+ int i; |
|
86 |
+ |
|
87 |
+ fork->logicalSize = be64_to_host(fork->logicalSize); |
|
88 |
+ fork->clumpSize = be32_to_host(fork->clumpSize); /* does this matter for read-only? */ |
|
89 |
+ fork->totalBlocks = be32_to_host(fork->totalBlocks); |
|
90 |
+ for (i=0; i < 8; i++) { |
|
91 |
+ fork->extents[i].startBlock = be32_to_host(fork->extents[i].startBlock); |
|
92 |
+ fork->extents[i].blockCount = be32_to_host(fork->extents[i].blockCount); |
|
93 |
+ } |
|
94 |
+} |
|
95 |
+ |
|
96 |
+static void forkdata_print(const char *pfx, hfsPlusForkData *fork) |
|
97 |
+{ |
|
98 |
+ int i; |
|
99 |
+ cli_dbgmsg("%s logicalSize %lu clumpSize %lu totalBlocks %lu\n", pfx, |
|
100 |
+ fork->logicalSize, fork->clumpSize, fork->totalBlocks); |
|
101 |
+ for (i=0; i < 8; i++) { |
|
102 |
+ if (fork->extents[i].startBlock == 0) |
|
103 |
+ break; |
|
104 |
+ cli_dbgmsg("%s extent[%d] startBlock %lu blockCount %lu\n", pfx, i, |
|
105 |
+ fork->extents[i].startBlock, fork->extents[i].blockCount); |
|
106 |
+ } |
|
107 |
+} |
|
108 |
+ |
|
109 |
+/* Read and convert the HFS+ volume header */ |
|
110 |
+static int hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header) |
|
111 |
+{ |
|
112 |
+ hfsPlusVolumeHeader *volHeader; |
|
113 |
+ const uint8_t *mPtr; |
|
114 |
+ |
|
115 |
+ if (!header) { |
|
116 |
+ return CL_ENULLARG; |
|
117 |
+ } |
|
118 |
+ |
|
119 |
+ /* Start with volume header, 512 bytes at offset 1024 */ |
|
120 |
+ if ((*ctx->fmap)->len < 1536) { |
|
121 |
+ cli_dbgmsg("cli_scanhfsplus: too short for HFS+\n"); |
|
122 |
+ return CL_EFORMAT; |
|
123 |
+ } |
|
124 |
+ mPtr = fmap_need_off_once(*ctx->fmap, 1024, 512); |
|
125 |
+ if (!mPtr) { |
|
126 |
+ cli_errmsg("cli_scanhfsplus: cannot read header from map\n"); |
|
127 |
+ return CL_EMAP; |
|
128 |
+ } |
|
129 |
+ |
|
130 |
+ volHeader = cli_malloc(sizeof(hfsPlusVolumeHeader)); |
|
131 |
+ if (!volHeader) { |
|
132 |
+ cli_errmsg("cli_scanhfsplus: header malloc failed\n"); |
|
133 |
+ return CL_EMEM; |
|
134 |
+ } |
|
135 |
+ *header = volHeader; |
|
136 |
+ memcpy(volHeader, mPtr, 512); |
|
137 |
+ |
|
138 |
+ volHeader->signature = be16_to_host(volHeader->signature); |
|
139 |
+ volHeader->version = be16_to_host(volHeader->version); |
|
140 |
+ if ((volHeader->signature == 0x482B) && (volHeader->version == 4)) { |
|
141 |
+ cli_dbgmsg("cli_scanhfsplus: HFS+ signature matched\n"); |
|
142 |
+ return CL_EFORMAT; |
|
143 |
+ } |
|
144 |
+ if ((volHeader->signature == 0x4858) && (volHeader->version == 5)) { |
|
145 |
+ cli_dbgmsg("cli_scanhfsplus: HFSX v5 signature matched\n"); |
|
146 |
+ return CL_EFORMAT; |
|
147 |
+ } |
|
148 |
+ else { |
|
149 |
+ cli_dbgmsg("cli_scanhfsplus: no matching signature\n"); |
|
150 |
+ return CL_EFORMAT; |
|
151 |
+ } |
|
152 |
+ /* skip fields that will definitely be ignored */ |
|
153 |
+ volHeader->attributes = be32_to_host(volHeader->attributes); |
|
154 |
+ volHeader->fileCount = be32_to_host(volHeader->fileCount); |
|
155 |
+ volHeader->folderCount = be32_to_host(volHeader->folderCount); |
|
156 |
+ volHeader->blockSize = be32_to_host(volHeader->blockSize); |
|
157 |
+ volHeader->totalBlocks = be32_to_host(volHeader->totalBlocks); |
|
158 |
+ |
|
159 |
+ cli_dbgmsg("HFS+ Header:\n"); |
|
160 |
+ cli_dbgmsg("Signature: %x\n", volHeader->signature); |
|
161 |
+ cli_dbgmsg("Attributes: %x\n", volHeader->attributes); |
|
162 |
+ cli_dbgmsg("File Count: %lu\n", volHeader->fileCount); |
|
163 |
+ cli_dbgmsg("Folder Count: %lu\n", volHeader->folderCount); |
|
164 |
+ cli_dbgmsg("Block Size: %lu\n", volHeader->blockSize); |
|
165 |
+ cli_dbgmsg("Total Blocks: %lu\n", volHeader->totalBlocks); |
|
166 |
+ |
|
167 |
+ /* Block Size must be power of 2 between 512 and 1 MB */ |
|
168 |
+ if ((volHeader->blockSize < 512) || (volHeader->blockSize > (1 << 20))) { |
|
169 |
+ cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); |
|
170 |
+ return CL_EFORMAT; |
|
171 |
+ } |
|
172 |
+ if (volHeader->blockSize & (volHeader->blockSize - 1)) { |
|
173 |
+ cli_dbgmsg("cli_scanhfsplus: Invalid blocksize\n"); |
|
174 |
+ return CL_EFORMAT; |
|
175 |
+ } |
|
176 |
+ |
|
177 |
+ forkdata_to_host(&(volHeader->allocationFile)); |
|
178 |
+ forkdata_to_host(&(volHeader->extentsFile)); |
|
179 |
+ forkdata_to_host(&(volHeader->catalogFile)); |
|
180 |
+ forkdata_to_host(&(volHeader->attributesFile)); |
|
181 |
+ forkdata_to_host(&(volHeader->startupFile)); |
|
182 |
+ |
|
183 |
+ if (cli_debug_flag) { |
|
184 |
+ forkdata_print("allocationFile", &(volHeader->allocationFile)); |
|
185 |
+ forkdata_print("extentsFile", &(volHeader->extentsFile)); |
|
186 |
+ forkdata_print("catalogFile", &(volHeader->catalogFile)); |
|
187 |
+ forkdata_print("attributesFile", &(volHeader->attributesFile)); |
|
188 |
+ forkdata_print("startupFile", &(volHeader->startupFile)); |
|
189 |
+ } |
|
190 |
+ |
|
191 |
+ return CL_CLEAN; |
|
192 |
+} |
|
193 |
+ |
|
194 |
+/* Read and convert the header node */ |
|
195 |
+static int hfsplus_readheader(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsNodeDescriptor *nodeDesc, |
|
196 |
+ hfsHeaderRecord *headerRec, int headerType, const char *name) |
|
197 |
+{ |
|
198 |
+ const uint8_t *mPtr = NULL; |
|
199 |
+ off_t offset; |
|
200 |
+ uint32_t minSize; |
|
201 |
+ |
|
202 |
+ /* Node Size must be power of 2 between 512 and 32768 */ |
|
203 |
+ /* Node Size for Catalog or Attributes must be at least 4096 */ |
|
204 |
+ switch (headerType) { |
|
205 |
+ case HFS_FILETREE_ALLOCATION: |
|
206 |
+ offset = volHeader->allocationFile.extents[0].startBlock * volHeader->blockSize; |
|
207 |
+ minSize = 512; |
|
208 |
+ break; |
|
209 |
+ case HFS_FILETREE_EXTENTS: |
|
210 |
+ offset = volHeader->extentsFile.extents[0].startBlock * volHeader->blockSize; |
|
211 |
+ minSize = 512; |
|
212 |
+ break; |
|
213 |
+ case HFS_FILETREE_CATALOG: |
|
214 |
+ offset = volHeader->catalogFile.extents[0].startBlock * volHeader->blockSize; |
|
215 |
+ minSize = 4096; |
|
216 |
+ break; |
|
217 |
+ case HFS_FILETREE_ATTRIBUTES: |
|
218 |
+ offset = volHeader->attributesFile.extents[0].startBlock * volHeader->blockSize; |
|
219 |
+ minSize = 4096; |
|
220 |
+ break; |
|
221 |
+ case HFS_FILETREE_STARTUP: |
|
222 |
+ offset = volHeader->startupFile.extents[0].startBlock * volHeader->blockSize; |
|
223 |
+ minSize = 512; |
|
224 |
+ break; |
|
225 |
+ default: |
|
226 |
+ cli_errmsg("hfsplus_readheader: %s: invalid headerType %d\n", name, headerType); |
|
227 |
+ return CL_EARG; |
|
228 |
+ } |
|
229 |
+ mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); |
|
230 |
+ if (!mPtr) { |
|
231 |
+ cli_dbgmsg("hfsplus_header: %s: headerNode is out-of-range\n", name); |
|
232 |
+ return CL_EFORMAT; |
|
233 |
+ } |
|
234 |
+ |
|
235 |
+ /* Node descriptor first */ |
|
236 |
+ memcpy(nodeDesc, mPtr, sizeof(hfsNodeDescriptor)); |
|
237 |
+ nodedescriptor_to_host(nodeDesc); |
|
238 |
+ nodedescriptor_print(name, nodeDesc); |
|
239 |
+ if (nodeDesc->kind != HFS_NODEKIND_HEADER) { |
|
240 |
+ cli_dbgmsg("hfsplus_header: %s: headerNode not header kind\n", name); |
|
241 |
+ return CL_EFORMAT; |
|
242 |
+ } |
|
243 |
+ if ((nodeDesc->bLink != 0) || (nodeDesc->height != 0) || (nodeDesc->numRecords != 3)) { |
|
244 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid headerNode\n", name); |
|
245 |
+ return CL_EFORMAT; |
|
246 |
+ } |
|
247 |
+ |
|
248 |
+ /* Then header record */ |
|
249 |
+ memcpy(headerRec, mPtr + sizeof(hfsNodeDescriptor), sizeof(hfsHeaderRecord)); |
|
250 |
+ headerrecord_to_host(headerRec); |
|
251 |
+ headerrecord_print(name, headerRec); |
|
252 |
+ |
|
253 |
+ if ((headerRec->nodeSize < minSize) || (volHeader->blockSize > 32768)) { |
|
254 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid nodesize\n", name); |
|
255 |
+ return CL_EFORMAT; |
|
256 |
+ } |
|
257 |
+ if (headerRec->nodeSize & (headerRec->nodeSize - 1)) { |
|
258 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid nodesize\n", name); |
|
259 |
+ return CL_EFORMAT; |
|
260 |
+ } |
|
261 |
+ /* KeyLength must be between 6 and 516 for catalog */ |
|
262 |
+ if (headerType == HFS_FILETREE_CATALOG) { |
|
263 |
+ if ((headerRec->maxKeyLength < 6) || (headerRec->maxKeyLength > 516)) { |
|
264 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid cat maxKeyLength\n", name); |
|
265 |
+ return CL_EFORMAT; |
|
266 |
+ } |
|
267 |
+ if (headerRec->maxKeyLength > (headerRec->nodeSize / 2)) { |
|
268 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid cat maxKeyLength based on nodeSize\n", name); |
|
269 |
+ return CL_EFORMAT; |
|
270 |
+ } |
|
271 |
+ } |
|
272 |
+ else if (headerType == HFS_FILETREE_EXTENTS) { |
|
273 |
+ if (headerRec->maxKeyLength != 10) { |
|
274 |
+ cli_dbgmsg("hfsplus_header: %s: Invalid ext maxKeyLength\n", name); |
|
275 |
+ return CL_EFORMAT; |
|
276 |
+ } |
|
277 |
+ } |
|
278 |
+ |
|
279 |
+ /* hdr->treeDepth = rootnode->height */ |
|
280 |
+ return CL_CLEAN; |
|
281 |
+} |
|
282 |
+ |
|
283 |
+/* Read and dump a file for scanning */ |
|
284 |
+static int hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *extHeader, |
|
285 |
+ hfsPlusForkData *fork, const char *dirname) |
|
286 |
+{ |
|
287 |
+ hfsPlusExtentDescriptor *currExt; |
|
288 |
+ const uint8_t *mPtr = NULL; |
|
289 |
+ char *tmpname = NULL; |
|
290 |
+ int ofd, ret = CL_CLEAN; |
|
291 |
+ uint64_t targetSize; |
|
292 |
+ uint32_t outputBlocks = 0; |
|
293 |
+ uint8_t ext; |
|
294 |
+ |
|
295 |
+ /* bad record checks */ |
|
296 |
+ if (!fork || (fork->logicalSize == 0) || (fork->totalBlocks == 0)) { |
|
297 |
+ cli_dbgmsg("hfsplus_dumpfile: Empty file.\n"); |
|
298 |
+ return CL_CLEAN; |
|
299 |
+ } |
|
300 |
+ |
|
301 |
+ /* check limits */ |
|
302 |
+ targetSize = fork->logicalSize; |
|
303 |
+ if (targetSize > ULONG_MAX) { |
|
304 |
+ cli_dbgmsg("hfsplus_dumpfile: File too large for limit check.\n"); |
|
305 |
+ return CL_EFORMAT; |
|
306 |
+ } |
|
307 |
+ ret = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0); |
|
308 |
+ if (ret != CL_CLEAN) { |
|
309 |
+ return ret; |
|
310 |
+ } |
|
311 |
+ |
|
312 |
+ /* open file */ |
|
313 |
+ ret = cli_gentempfd(dirname, &tmpname, &ofd); |
|
314 |
+ if (ret != CL_CLEAN) { |
|
315 |
+ cli_dbgmsg("hfsplus_dumpfile: Cannot generate temporary file.\n"); |
|
316 |
+ return ret; |
|
317 |
+ } |
|
318 |
+ cli_dbgmsg("hfsplus_dumpfile: Extracting to %s\n", tmpname); |
|
319 |
+ |
|
320 |
+ ext = 0; |
|
321 |
+ /* Dump file, extent by extent */ |
|
322 |
+ do { |
|
323 |
+ uint32_t currBlock, endBlock, outputSize; |
|
324 |
+ if (targetSize == 0) { |
|
325 |
+ cli_dbgmsg("hfsplus_dumpfile: output complete\n"); |
|
326 |
+ break; |
|
327 |
+ } |
|
328 |
+ if (outputBlocks >= fork->totalBlocks) { |
|
329 |
+ cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); |
|
330 |
+ break; |
|
331 |
+ } |
|
332 |
+ /* Prepare extent */ |
|
333 |
+ if (ext < 8) { |
|
334 |
+ currExt = &(fork->extents[ext]); |
|
335 |
+ cli_dbgmsg("hfsplus_dumpfile: extent %u\n", ext); |
|
336 |
+ } |
|
337 |
+ else { |
|
338 |
+ cli_dbgmsg("hfsplus_dumpfile: need next extent from ExtentOverflow\n"); |
|
339 |
+ /* Not implemented yet */ |
|
340 |
+ ret = CL_EFORMAT; |
|
341 |
+ break; |
|
342 |
+ } |
|
343 |
+ /* have extent, so validate and get block range */ |
|
344 |
+ if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) { |
|
345 |
+ cli_dbgmsg("hfsplus_dumpfile: next extent empty, done\n"); |
|
346 |
+ break; |
|
347 |
+ } |
|
348 |
+ currBlock = currExt->startBlock; |
|
349 |
+ endBlock = currExt->startBlock + currExt->blockCount - 1; |
|
350 |
+ if ((currBlock > volHeader->totalBlocks) || (endBlock > volHeader->totalBlocks) |
|
351 |
+ || (currExt->blockCount > volHeader->totalBlocks)) { |
|
352 |
+ cli_dbgmsg("hfsplus_dumpfile: bad extent!\n"); |
|
353 |
+ ret = CL_EFORMAT; |
|
354 |
+ break; |
|
355 |
+ } |
|
356 |
+ /* Write the blocks, walking the map */ |
|
357 |
+ while (currBlock <= endBlock) { |
|
358 |
+ size_t to_write = MIN(targetSize, volHeader->blockSize); |
|
359 |
+ ssize_t written; |
|
360 |
+ off_t offset = currBlock * volHeader->blockSize; |
|
361 |
+ /* move map to next block */ |
|
362 |
+ mPtr = fmap_need_off_once(*ctx->fmap, offset, volHeader->blockSize); |
|
363 |
+ if (!mPtr) { |
|
364 |
+ cli_errmsg("hfsplus_dumpfile: map error\n"); |
|
365 |
+ ret = CL_EMAP; |
|
366 |
+ break; |
|
367 |
+ } |
|
368 |
+ written = cli_writen(ofd, mPtr, to_write); |
|
369 |
+ if (written != to_write) { |
|
370 |
+ cli_errmsg("hfsplus_dumpfile: write error\n"); |
|
371 |
+ ret = CL_EWRITE; |
|
372 |
+ break; |
|
373 |
+ } |
|
374 |
+ targetSize -= to_write; |
|
375 |
+ outputSize += to_write; |
|
376 |
+ currBlock++; |
|
377 |
+ if (targetSize == 0) { |
|
378 |
+ cli_dbgmsg("hfsplus_dumpfile: output complete\n"); |
|
379 |
+ break; |
|
380 |
+ } |
|
381 |
+ if (outputBlocks >= fork->totalBlocks) { |
|
382 |
+ cli_dbgmsg("hfsplus_dumpfile: output all blocks, remaining size " STDu64 "\n", targetSize); |
|
383 |
+ break; |
|
384 |
+ } |
|
385 |
+ } |
|
386 |
+ /* Finished the extent, move to next */ |
|
387 |
+ ext++; |
|
388 |
+ } while (ret == CL_CLEAN); |
|
389 |
+ |
|
390 |
+ /* if successful so far, scan the output */ |
|
391 |
+ if (ret == CL_CLEAN) { |
|
392 |
+ ret = cli_magic_scandesc(ofd, ctx); |
|
393 |
+ } |
|
394 |
+ |
|
395 |
+ if (ofd >= 0) { |
|
396 |
+ close(ofd); |
|
397 |
+ } |
|
398 |
+ if (!ctx->engine->keeptmp) { |
|
399 |
+ if (cli_unlink(tmpname)) { |
|
400 |
+ ret = CL_EUNLINK; |
|
401 |
+ } |
|
402 |
+ } |
|
403 |
+ free(tmpname); |
|
404 |
+ |
|
405 |
+ return ret; |
|
406 |
+} |
|
407 |
+ |
|
408 |
+ |
|
30 | 409 |
int cli_scanhfsplus(cli_ctx *ctx) |
31 | 410 |
{ |
411 |
+ char *targetdir = NULL; |
|
32 | 412 |
int ret = CL_CLEAN; |
413 |
+ hfsPlusVolumeHeader *volHeader = NULL; |
|
414 |
+ hfsNodeDescriptor catFileDesc; |
|
415 |
+ hfsHeaderRecord catFileHeader; |
|
416 |
+ hfsNodeDescriptor extentFileDesc; |
|
417 |
+ hfsHeaderRecord extentFileHeader; |
|
33 | 418 |
|
34 | 419 |
if (!ctx || !ctx->fmap) { |
35 | 420 |
cli_errmsg("cli_scanhfsplus: Invalid context\n"); |
36 | 421 |
return CL_ENULLARG; |
37 | 422 |
} |
38 | 423 |
|
39 |
- cli_dbgmsg("cli_scanhfsplus: starting scan\n"); |
|
424 |
+ cli_dbgmsg("cli_scanhfsplus: scanning partition content\n"); |
|
425 |
+ /* first, read volume header contents */ |
|
426 |
+ ret = hfsplus_volumeheader(ctx, &volHeader); |
|
427 |
+ if (ret != CL_CLEAN) { |
|
428 |
+ goto freeHeader; |
|
429 |
+ } |
|
430 |
+ |
|
431 |
+ |
|
432 |
+/* |
|
433 |
+cli_dbgmsg("sizeof(hfsUniStr255) is %lu\n", sizeof(hfsUniStr255)); |
|
434 |
+cli_dbgmsg("sizeof(hfsPlusBSDInfo) is %lu\n", sizeof(hfsPlusBSDInfo)); |
|
435 |
+cli_dbgmsg("sizeof(hfsPlusExtentDescriptor) is %lu\n", sizeof(hfsPlusExtentDescriptor)); |
|
436 |
+cli_dbgmsg("sizeof(hfsPlusExtentRecord) is %lu\n", sizeof(hfsPlusExtentRecord)); |
|
437 |
+cli_dbgmsg("sizeof(hfsPlusForkData) is %lu\n", sizeof(hfsPlusForkData)); |
|
438 |
+cli_dbgmsg("sizeof(hfsPlusVolumeHeader) is %lu\n", sizeof(hfsPlusVolumeHeader)); |
|
439 |
+cli_dbgmsg("sizeof(hfsNodeDescriptor) is %lu\n", sizeof(hfsNodeDescriptor)); |
|
440 |
+ */ |
|
441 |
+ |
|
442 |
+ /* Get root node (header node) of catalog file */ |
|
443 |
+ ret = hfsplus_readheader(ctx, volHeader, &extentFileDesc, &extentFileHeader, HFS_FILETREE_EXTENTS, "extentFile"); |
|
444 |
+ if (ret != CL_CLEAN) { |
|
445 |
+ goto freeHeader; |
|
446 |
+ } |
|
447 |
+ ret = hfsplus_readheader(ctx, volHeader, &catFileDesc, &catFileHeader, HFS_FILETREE_CATALOG, "catalogFile"); |
|
448 |
+ if (ret != CL_CLEAN) { |
|
449 |
+ goto freeHeader; |
|
450 |
+ } |
|
451 |
+ |
|
452 |
+ /* Create temp folder for contents */ |
|
453 |
+ if (!(targetdir = cli_gentemp(ctx->engine->tmpdir))) { |
|
454 |
+ ret = CL_ETMPDIR; |
|
455 |
+ goto freeHeader; |
|
456 |
+ } |
|
457 |
+ if (mkdir(targetdir, 0700)) { |
|
458 |
+ cli_errmsg("cli_scandmg: Cannot create temporary directory %s\n", targetdir); |
|
459 |
+ ret = CL_ETMPDIR; |
|
460 |
+ goto freeDirname; |
|
461 |
+ } |
|
462 |
+ cli_dbgmsg("cli_scandmg: Extracting into %s\n", targetdir); |
|
463 |
+ |
|
464 |
+ ret = hfsplus_scanfile(ctx, volHeader, &extentFileHeader, &(volHeader->catalogFile), targetdir); |
|
465 |
+ |
|
466 |
+ /* Clean up extracted content, if needed */ |
|
467 |
+ if (!ctx->engine->keeptmp) { |
|
468 |
+ cli_rmdirs(targetdir); |
|
469 |
+ } |
|
40 | 470 |
|
471 |
+freeDirname: |
|
472 |
+ free(targetdir); |
|
473 |
+freeHeader: |
|
474 |
+ free(volHeader); |
|
41 | 475 |
return ret; |
42 | 476 |
} |
... | ... |
@@ -27,6 +27,266 @@ |
27 | 27 |
|
28 | 28 |
#include "cltypes.h" |
29 | 29 |
|
30 |
+/* Structures based on Apple Technote 1150 */ |
|
31 |
+ |
|
32 |
+/* volume attributes that may affect reading */ |
|
33 |
+enum hfsVolAttributes { |
|
34 |
+ /* hfsVolumeHardwareLockBit = 7, */ |
|
35 |
+ hfsVolumeUnmountedBit = 8, |
|
36 |
+ hfsVolumeSparedBlocksBit = 9, |
|
37 |
+ /* hfsVolumeNoCacheRequiredBit = 10, */ |
|
38 |
+ hfsBootVolumeInconsistentBit = 11, |
|
39 |
+ hfsCatalogNodeIDsReusedBit = 12, |
|
40 |
+ hfsVolumeJournaledBit = 13 |
|
41 |
+ /* hfsVolumeSoftwareLockBit = 15 */ |
|
42 |
+}; |
|
43 |
+ |
|
44 |
+/* reserved CatalogNodeID values */ |
|
45 |
+enum { |
|
46 |
+ hfsRootParentID = 1, |
|
47 |
+ hfsRootFolderID = 2, |
|
48 |
+ hfsExtentsFileID = 3, |
|
49 |
+ hfsCatalogFileID = 4, |
|
50 |
+ hfsBadBlockFileID = 5, |
|
51 |
+ hfsAllocationFileID = 6, |
|
52 |
+ hfsStartupFileID = 7, |
|
53 |
+ hfsAttributesFileID = 8, |
|
54 |
+ hfsRepairCatalogFileID = 14, |
|
55 |
+ hfsBogusExtentFileID = 15, |
|
56 |
+ hfsFirstUserCatalogNodeID = 16 |
|
57 |
+}; |
|
58 |
+ |
|
59 |
+#ifndef HAVE_ATTRIB_PACKED |
|
60 |
+#define __attribute__(x) |
|
61 |
+#endif |
|
62 |
+ |
|
63 |
+#ifdef HAVE_PRAGMA_PACK |
|
64 |
+#pragma pack(2) |
|
65 |
+#endif |
|
66 |
+ |
|
67 |
+#ifdef HAVE_PRAGMA_PACK_HPPA |
|
68 |
+#pragma pack 2 |
|
69 |
+#endif |
|
70 |
+ |
|
71 |
+/* Basic HFS+ structures */ |
|
72 |
+struct hfsUniStr255 { |
|
73 |
+ uint16_t length; |
|
74 |
+ uint16_t unicode[255]; |
|
75 |
+} __attribute__((__packed__)); |
|
76 |
+typedef struct hfsUniStr255 hfsUniStr255; |
|
77 |
+ |
|
78 |
+struct hfsPlusExtentDescriptor { |
|
79 |
+ uint32_t startBlock; |
|
80 |
+ uint32_t blockCount; |
|
81 |
+} __attribute__((__packed__)); |
|
82 |
+typedef struct hfsPlusExtentDescriptor hfsPlusExtentDescriptor; |
|
83 |
+typedef hfsPlusExtentDescriptor hfsPlusExtentRecord[8]; |
|
84 |
+ |
|
85 |
+struct hfsPlusForkData { |
|
86 |
+ uint64_t logicalSize; |
|
87 |
+ uint32_t clumpSize; |
|
88 |
+ uint32_t totalBlocks; |
|
89 |
+ hfsPlusExtentRecord extents; |
|
90 |
+} __attribute__((__packed__)); |
|
91 |
+typedef struct hfsPlusForkData hfsPlusForkData; |
|
92 |
+ |
|
93 |
+/* HFS+ Volume Header (512 bytes) */ |
|
94 |
+struct hfsPlusVolumeHeader { |
|
95 |
+ uint16_t signature; /* H+ for HFS+, HX for HFSX */ |
|
96 |
+ uint16_t version; |
|
97 |
+ uint32_t attributes; |
|
98 |
+ uint32_t lastMountedVersion; |
|
99 |
+ uint32_t journalInfoBlock; |
|
100 |
+ |
|
101 |
+ uint32_t createDate; |
|
102 |
+ uint32_t modifyDate; |
|
103 |
+ uint32_t backupDate; |
|
104 |
+ uint32_t checkedDate; |
|
105 |
+ |
|
106 |
+ uint32_t fileCount; |
|
107 |
+ uint32_t folderCount; |
|
108 |
+ |
|
109 |
+ uint32_t blockSize; |
|
110 |
+ uint32_t totalBlocks; |
|
111 |
+ uint32_t freeBlocks; |
|
112 |
+ |
|
113 |
+ uint32_t nextAllocation; |
|
114 |
+ uint32_t rsrcClumpSize; |
|
115 |
+ uint32_t dataClumpSize; |
|
116 |
+ uint32_t nextCatalogID; /* Next unused catalog ID */ |
|
117 |
+ |
|
118 |
+ uint32_t writeCount; |
|
119 |
+ uint64_t encodingsBitmap; |
|
120 |
+ |
|
121 |
+ uint32_t finderInfo[8]; /* for Finder */ |
|
122 |
+ |
|
123 |
+ hfsPlusForkData allocationFile; |
|
124 |
+ hfsPlusForkData extentsFile; |
|
125 |
+ hfsPlusForkData catalogFile; |
|
126 |
+ hfsPlusForkData attributesFile; |
|
127 |
+ hfsPlusForkData startupFile; |
|
128 |
+} __attribute__((__packed__)); |
|
129 |
+typedef struct hfsPlusVolumeHeader hfsPlusVolumeHeader; |
|
130 |
+ |
|
131 |
+#define HFS_FILETREE_ALLOCATION 1 |
|
132 |
+#define HFS_FILETREE_EXTENTS 2 |
|
133 |
+#define HFS_FILETREE_CATALOG 3 |
|
134 |
+#define HFS_FILETREE_ATTRIBUTES 4 |
|
135 |
+#define HFS_FILETREE_STARTUP 5 |
|
136 |
+ |
|
137 |
+/******************************/ |
|
138 |
+/* File properties */ |
|
139 |
+/******************************/ |
|
140 |
+ |
|
141 |
+/* BSD object info (16 bytes) */ |
|
142 |
+/* important parts for scanning are fileMode and the special part */ |
|
143 |
+struct hfsPlusBSDInfo { |
|
144 |
+ uint32_t ownerID; |
|
145 |
+ uint32_t groupID; |
|
146 |
+ uint8_t adminFlags; |
|
147 |
+ uint8_t ownerFlags; |
|
148 |
+ uint16_t fileMode; |
|
149 |
+ union { |
|
150 |
+ uint32_t iNodeNum; |
|
151 |
+ uint32_t linkCount; |
|
152 |
+ uint32_t rawDevice; |
|
153 |
+ } special; |
|
154 |
+} __attribute__((__packed__)); |
|
155 |
+typedef struct hfsPlusBSDInfo hfsPlusBSDInfo; |
|
156 |
+ |
|
157 |
+#define HFS_MODE_TYPEMASK 0170000 |
|
158 |
+#define HFS_MODE_DIRECTORY 0040000 |
|
159 |
+#define HFS_MODE_FILE 0100000 |
|
160 |
+#define HFS_MODE_SOFTLINK 0120000 |
|
161 |
+ |
|
162 |
+/******************************/ |
|
163 |
+/* Node and tree structures */ |
|
164 |
+/******************************/ |
|
165 |
+ |
|
166 |
+/* B-tree node descriptor (14 bytes) */ |
|
167 |
+struct hfsNodeDescriptor { |
|
168 |
+ uint32_t fLink; |
|
169 |
+ uint32_t bLink; |
|
170 |
+ int8_t kind; |
|
171 |
+ uint8_t height; |
|
172 |
+ uint16_t numRecords; |
|
173 |
+ uint16_t reserved; |
|
174 |
+} __attribute__((__packed__)); |
|
175 |
+typedef struct hfsNodeDescriptor hfsNodeDescriptor; |
|
176 |
+ |
|
177 |
+/* Node kinds are int8_t */ |
|
178 |
+#define HFS_NODEKIND_LEAF -1 |
|
179 |
+#define HFS_NODEKIND_INDEX 0 |
|
180 |
+#define HFS_NODEKIND_HEADER 1 |
|
181 |
+#define HFS_NODEKIND_MAP 2 |
|
182 |
+ |
|
183 |
+/* B-tree header record (106 bytes) */ |
|
184 |
+struct hfsHeaderRecord { |
|
185 |
+ uint16_t treeDepth; |
|
186 |
+ uint32_t rootNode; |
|
187 |
+ uint32_t leafRecords; |
|
188 |
+ uint32_t firstLeafNode; |
|
189 |
+ uint32_t lastLeafNode; |
|
190 |
+ uint16_t nodeSize; |
|
191 |
+ uint16_t maxKeyLength; |
|
192 |
+ uint32_t totalNodes; |
|
193 |
+ uint32_t freeNodes; |
|
194 |
+ uint16_t reserved1; |
|
195 |
+ uint32_t clumpSize; |
|
196 |
+ uint8_t btreeType; |
|
197 |
+ uint8_t keyCompareType; |
|
198 |
+ uint32_t attributes; |
|
199 |
+ uint32_t reserved3[16]; |
|
200 |
+} __attribute__((__packed__)); |
|
201 |
+typedef struct hfsHeaderRecord hfsHeaderRecord; |
|
202 |
+ |
|
203 |
+#define HFS_HEADERATTR_MASK 0x00000006 |
|
204 |
+#define HFS_HEADERATTR_BIGKEYS 0x00000002 |
|
205 |
+#define HFS_HEADERATTR_VARKEYS 0x00000004 |
|
206 |
+ |
|
207 |
+struct hfsPlusCatalogKey { |
|
208 |
+ uint16_t keyLength; |
|
209 |
+ uint32_t parentID; /* CNID */ |
|
210 |
+ hfsUniStr255 nodeName; |
|
211 |
+} __attribute__((__packed__)); |
|
212 |
+typedef struct hfsPlusCatalogKey hfsPlusCatalogKey; |
|
213 |
+ |
|
214 |
+struct hfsPlusCatalogFolder { |
|
215 |
+ int16_t recordType; |
|
216 |
+ uint16_t flags; |
|
217 |
+ uint32_t valence; |
|
218 |
+ uint32_t folderID; /* CNID */ |
|
219 |
+ uint32_t dates[5]; |
|
220 |
+ hfsPlusBSDInfo permissions; |
|
221 |
+ uint16_t userInfo[8]; /* FolderInfo */ |
|
222 |
+ uint16_t finderInfo[8]; /* ExtendedFolderInfo */ |
|
223 |
+ uint32_t textEncoding; |
|
224 |
+ uint32_t reserved; |
|
225 |
+} __attribute__((__packed__)); |
|
226 |
+typedef struct hfsPlusCatalogFolder hfsPlusCatalogFolder; |
|
227 |
+ |
|
228 |
+struct hfsPlusCatalogFile { |
|
229 |
+ int16_t recordType; |
|
230 |
+ uint16_t flags; |
|
231 |
+ uint32_t reserved1; |
|
232 |
+ uint32_t fileID; /* CNID */ |
|
233 |
+ uint32_t dates[5]; |
|
234 |
+ hfsPlusBSDInfo permissions; |
|
235 |
+ uint16_t userInfo[8]; /* FileInfo */ |
|
236 |
+ uint16_t finderInfo[8]; /* ExtendedFileInfo */ |
|
237 |
+ uint32_t textEncoding; |
|
238 |
+ uint32_t reserved2; |
|
239 |
+ hfsPlusForkData dataFork; |
|
240 |
+ hfsPlusForkData resourceFork; |
|
241 |
+}; |
|
242 |
+typedef struct hfsPlusCatalogFile hfsPlusCatalogFile; |
|
243 |
+ |
|
244 |
+struct hfsPlusCatalogThread { |
|
245 |
+ int16_t recordType; |
|
246 |
+ int16_t reserved; |
|
247 |
+ uint32_t parentID; /* CNID */ |
|
248 |
+ hfsUniStr255 nodeName; |
|
249 |
+} __attribute__((__packed__)); |
|
250 |
+typedef struct hfsPlusCatalogThread hfsPlusCatalogThread; |
|
251 |
+ |
|
252 |
+#define HFSPLUS_RECTYPE_FOLDER 0x0001 |
|
253 |
+#define HFSPLUS_RECTYPE_FILE 0x0002 |
|
254 |
+#define HFSPLUS_RECTYPE_FOLDERTHREAD 0x0003 |
|
255 |
+#define HFSPLUS_RECTYPE_FILETHREAD 0x0004 |
|
256 |
+/* HFS types are similar |
|
257 |
+#define HFS_RECTYPE_FOLDER 0x0100 |
|
258 |
+#define HFS_RECTYPE_FILE 0x0200 |
|
259 |
+#define HFS_RECTYPE_FOLDERTHREAD 0x0300 |
|
260 |
+#define HFS_RECTYPE_FILETHREAD 0x0400 |
|
261 |
+ */ |
|
262 |
+ |
|
263 |
+#define HFS_HARD_LINK_FILE_TYPE 0x686C6E6B /* hlnk */ |
|
264 |
+ |
|
265 |
+/* Extents structures */ |
|
266 |
+struct hfsPlusExtentKey { |
|
267 |
+ uint16_t keyLength; |
|
268 |
+ uint8_t forkType; |
|
269 |
+ uint8_t pad; |
|
270 |
+ uint32_t fileID; /* CNID */ |
|
271 |
+ uint32_t startBlock; |
|
272 |
+} __attribute__((__packed__)); |
|
273 |
+typedef struct hfsPlusExtentKey hfsPlusExtentKey; |
|
274 |
+ |
|
275 |
+#define HFSPLUS_FORKTYPE_DATA 0x00 |
|
276 |
+#define HFSPLUS_FORKTYPE_RSRC 0xFF |
|
277 |
+ |
|
278 |
+#ifdef HAVE_PRAGMA_PACK |
|
279 |
+#pragma pack() |
|
280 |
+#endif |
|
281 |
+ |
|
282 |
+#ifdef HAVE_PRAGMA_PACK_HPPA |
|
283 |
+#pragma pack |
|
284 |
+#endif |
|
285 |
+ |
|
286 |
+#define HFS_VOL_INCONSISTENT(hdr) \ |
|
287 |
+ ((hdr->attributes & (1 << hfsBootVolumeInconsistentBit)) \ |
|
288 |
+ || !(hdr->attributes & (1 << hfsVolumeUnmountedBit))) |
|
289 |
+ |
|
30 | 290 |
int cli_scanhfsplus(cli_ctx *ctx); |
31 | 291 |
|
32 | 292 |
#endif |