Browse code

libclamav: HFS+/HFSX signature tweaks, and initial version of scanfile (initial extents only)

David Raynor authored on 2013/09/22 09:56:37
Showing 3 changed files
... ...
@@ -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