Coverage Report

Created: 2024-02-25 07:17

/src/sleuthkit/tsk/fs/decmpfs.c
Line
Count
Source (jump to first uncovered line)
1
/* This file contains decompression routines used by APFS and HFS
2
 * It has one method derived from public domain ZLIB and others
3
 * that are TSK-specific. 
4
 *
5
 * It would probably be cleaner to separate these into two files.
6
 */
7
8
#include "../libtsk.h"
9
#include "tsk_fs_i.h"
10
#include "decmpfs.h"
11
12
#ifdef HAVE_LIBZ
13
#include <zlib.h>
14
#endif
15
16
#include "lzvn.h"
17
#include "tsk_hfs.h"
18
19
#ifdef HAVE_LIBZ
20
21
/***************** ZLIB stuff *******************************/
22
23
/* The zlib_inflate method is adapted from the public domain
24
 * zpipe.c (part of zlib) at http://zlib.net/zpipe.c
25
 *
26
 * zpipe.c: example of proper use of zlib's inflate() and deflate()
27
 * Not copyrighted -- provided to the public domain
28
 * Version 1.4  11 December 2005  Mark Adler */
29
30
0
#define CHUNK 16384
31
32
/*
33
 * Invokes the zlib library to inflate (uncompress) data.
34
 *
35
 * Returns and error code.  Places the uncompressed data in a buffer supplied by the caller.  Also
36
 * returns the uncompressed length, and the number of compressed bytes consumed.
37
 *
38
 * Will stop short of the end of compressed data, if a natural end of a compression unit is reached.  Using
39
 * bytesConsumed, the caller can then advance the source pointer, and re-invoke the function.  This will then
40
 * inflate the next following compression unit in the data stream.
41
 *
42
 * @param source - buffer of compressed data
43
 * @param sourceLen  - length of the compressed data.
44
 * @param dest  -- buffer to  hold the uncompressed results
45
 * @param destLen -- length of the dest buffer
46
 * @param uncompressedLength  -- return of the length of the uncompressed data found.
47
 * @param bytesConsumed  -- return of the number of input bytes of compressed data used.
48
 * @return 0 on success, a negative number on error
49
 */
50
int
51
zlib_inflate(char *source, uint64_t sourceLen, char *dest, uint64_t destLen, uint64_t * uncompressedLength, unsigned long *bytesConsumed)       // this is unsigned long because that's what zlib uses.
52
0
{
53
54
0
    int ret;
55
0
    unsigned have;
56
0
    z_stream strm;
57
0
    unsigned char in[CHUNK];
58
0
    unsigned char out[CHUNK];
59
60
    // Some vars to help with copying bytes into "in"
61
0
    char *srcPtr = source;
62
0
    char *destPtr = dest;
63
0
    uint64_t srcAvail = sourceLen;      //uint64_t
64
0
    uint64_t amtToCopy;
65
0
    uint64_t copiedSoFar = 0;
66
67
    /* allocate inflate state */
68
0
    strm.zalloc = Z_NULL;
69
0
    strm.zfree = Z_NULL;
70
0
    strm.opaque = Z_NULL;
71
0
    strm.avail_in = 0;
72
0
    strm.next_in = Z_NULL;
73
0
    ret = inflateInit(&strm);
74
0
    if (ret != Z_OK) {
75
0
        error_detected(TSK_ERR_FS_READ,
76
0
            "zlib_inflate: failed to initialize inflation engine (%d)",
77
0
            ret);
78
0
        return ret;
79
0
    }
80
81
    /* decompress until deflate stream ends or end of file */
82
0
    do {
83
84
        // Copy up to CHUNK bytes into "in" from source, advancing the pointer, and
85
        // setting strm.avail_in equal to the number of bytes copied.
86
0
        if (srcAvail >= CHUNK) {
87
0
            amtToCopy = CHUNK;
88
0
            srcAvail -= CHUNK;
89
0
        }
90
0
        else {
91
0
            amtToCopy = srcAvail;
92
0
            srcAvail = 0;
93
0
        }
94
        // wipe out any previous value, copy in the bytes, advance the pointer, record number of bytes.
95
0
        memset(in, 0, CHUNK);
96
0
        if (amtToCopy > SIZE_MAX || amtToCopy > UINT_MAX) {
97
0
            error_detected(TSK_ERR_FS_READ,
98
0
                "zlib_inflate: amtToCopy in one chunk is too large");
99
0
            return -100;
100
0
        }
101
0
        memcpy(in, srcPtr, (size_t) amtToCopy); // cast OK because of above test
102
0
        srcPtr += amtToCopy;
103
0
        strm.avail_in = (uInt) amtToCopy;       // cast OK because of above test
104
105
0
        if (strm.avail_in == 0)
106
0
            break;
107
0
        strm.next_in = in;
108
109
        /* run inflate() on input until output buffer not full */
110
0
        do {
111
0
            strm.avail_out = CHUNK;
112
0
            strm.next_out = out;
113
0
            ret = inflate(&strm, Z_NO_FLUSH);
114
0
            if (ret == Z_NEED_DICT)
115
0
                ret = Z_DATA_ERROR;     // we don't have a custom dict
116
0
            if (ret < 0 && ret != Z_BUF_ERROR) { // Z_BUF_ERROR is not fatal
117
0
                error_detected(TSK_ERR_FS_READ,
118
0
                    " zlib_inflate: zlib returned error %d (%s)", ret,
119
0
                    strm.msg);
120
0
                (void) inflateEnd(&strm);
121
0
                return ret;
122
0
            }
123
124
0
            have = CHUNK - strm.avail_out;
125
            // Is there enough space in dest to copy the current chunk?
126
0
            if (copiedSoFar + have > destLen) {
127
                // There is not enough space, so better return an error
128
0
                error_detected(TSK_ERR_FS_READ,
129
0
                    " zlib_inflate: not enough space in inflation destination\n");
130
0
                (void) inflateEnd(&strm);
131
0
                return -200;
132
0
            }
133
134
            // Copy "have" bytes from out to destPtr, advance destPtr
135
0
            memcpy(destPtr, out, have);
136
0
            destPtr += have;
137
0
            copiedSoFar += have;
138
139
0
        } while ((strm.avail_out == 0) && (ret != Z_STREAM_END));
140
141
142
        /* done when inflate() says it's done */
143
0
    } while (ret != Z_STREAM_END);
144
145
0
    if (ret == Z_STREAM_END)
146
0
        *uncompressedLength = copiedSoFar;
147
148
0
    *bytesConsumed = strm.total_in;
149
    /* clean up and return */
150
0
    (void) inflateEnd(&strm);
151
0
    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
152
0
}
153
154
#endif
155
156
157
158
/********************* TSK STUFF **********************/
159
160
/*
161
 * The Sleuth Kit
162
 *
163
 * Brian Carrier [carrier <at> sleuthkit [dot] org]
164
 * Copyright (c) 2019-2020 Brian Carrier.  All Rights reserved
165
 * Copyright (c) 2018-2019 BlackBag Technologies.  All Rights reserved
166
 *
167
 * This software is distributed under the Common Public License 1.0
168
 */
169
170
typedef struct {
171
    uint32_t offset;
172
    uint32_t length;
173
} CMP_OFFSET_ENTRY;
174
175
/**
176
 * \internal
177
 * Reads the ZLIB compression block table from the attribute.
178
 *
179
 * @param rAtttr the attribute to read
180
 * @param offsetTableOut block table
181
 * @param tableSizeOut size of block table
182
 * @param tableOffsetOut the offset of the block table in the resource fork
183
 * @return 1 on success, 0 on error
184
 */
185
static int
186
0
decmpfs_read_zlib_block_table(const TSK_FS_ATTR *rAttr, CMP_OFFSET_ENTRY** offsetTableOut, uint32_t* tableSizeOut, uint32_t* tableOffsetOut) {
187
0
    ssize_t attrReadResult;
188
0
    hfs_resource_fork_header rfHeader;
189
0
    uint32_t dataOffset;
190
0
    uint32_t offsetTableOffset;
191
0
    char fourBytes[4];          // Size of the offset table, little endian
192
0
    uint32_t tableSize;         // Size of the offset table
193
0
    char *offsetTableData = NULL;
194
0
    CMP_OFFSET_ENTRY *offsetTable = NULL;
195
0
    size_t indx;
196
197
    // Read the resource fork header
198
0
    attrReadResult = tsk_fs_attr_read(rAttr, 0, (char *) &rfHeader,
199
0
        sizeof(hfs_resource_fork_header), TSK_FS_FILE_READ_FLAG_NONE);
200
0
    if (attrReadResult != sizeof(hfs_resource_fork_header)) {
201
0
        error_returned
202
0
            (" %s: trying to read the resource fork header", __func__);
203
0
        return 0;
204
0
    }
205
206
    // Begin to parse the resource fork. For now, we just need the data offset.
207
0
    dataOffset = tsk_getu32(TSK_BIG_ENDIAN, rfHeader.dataOffset);
208
209
    // The resource's data begins with an offset table, which defines blocks
210
    // of (optionally) zlib-compressed data (so that the OS can do file seeks
211
    // efficiently; each uncompressed block is 64KB).
212
0
    offsetTableOffset = dataOffset + 4;
213
214
    // read 4 bytes, the number of table entries, little endian
215
0
    attrReadResult =
216
0
        tsk_fs_attr_read(rAttr, offsetTableOffset, fourBytes, 4,
217
0
        TSK_FS_FILE_READ_FLAG_NONE);
218
0
    if (attrReadResult != 4) {
219
0
        error_returned
220
0
            (" %s: trying to read the offset table size, "
221
0
            "return value of %u should have been 4", __func__, attrReadResult);
222
0
        return 0;
223
0
    }
224
0
    tableSize = tsk_getu32(TSK_LIT_ENDIAN, fourBytes);
225
226
    // Each table entry is 8 bytes long
227
0
    offsetTableData = tsk_malloc(tableSize * 8);
228
0
    if (offsetTableData == NULL) {
229
0
        error_returned
230
0
            (" %s: space for the offset table raw data", __func__);
231
0
        return 0;
232
0
    }
233
234
0
    offsetTable =
235
0
        (CMP_OFFSET_ENTRY *) tsk_malloc(tableSize *
236
0
        sizeof(CMP_OFFSET_ENTRY));
237
0
    if (offsetTable == NULL) {
238
0
        error_returned
239
0
            (" %s: space for the offset table", __func__);
240
0
        goto on_error;
241
0
    }
242
243
0
    attrReadResult = tsk_fs_attr_read(rAttr, offsetTableOffset + 4,
244
0
        offsetTableData, tableSize * 8, TSK_FS_FILE_READ_FLAG_NONE);
245
0
    if (attrReadResult != (ssize_t) tableSize * 8) {
246
0
        error_returned
247
0
            (" %s: reading in the compression offset table, "
248
0
            "return value %u should have been %u", __func__, attrReadResult,
249
0
            tableSize * 8);
250
0
        goto on_error;
251
0
    }
252
253
0
    for (indx = 0; indx < tableSize; ++indx) {
254
0
        offsetTable[indx].offset =
255
0
            tsk_getu32(TSK_LIT_ENDIAN, offsetTableData + indx * 8);
256
0
        offsetTable[indx].length =
257
0
            tsk_getu32(TSK_LIT_ENDIAN, offsetTableData + indx * 8 + 4);
258
0
    }
259
260
0
    free(offsetTableData);
261
262
0
    *offsetTableOut = offsetTable;
263
0
    *tableSizeOut = tableSize;
264
0
    *tableOffsetOut = offsetTableOffset;
265
0
    return 1;
266
267
0
on_error:
268
0
    free(offsetTable);
269
0
    free(offsetTableData);
270
0
    return 0;
271
0
}
272
273
/**
274
 * \internal
275
 * Reads the LZVN compression block table from the attribute.
276
 *
277
 * @param rAtttr the attribute to read
278
 * @param offsetTableOut block table
279
 * @param tableSizeOut size of block table
280
 * @param tableOffsetOut the offset of the block table in the resource fork
281
 * @return 1 on success, 0 on error
282
 */
283
static int
284
0
decmpfs_read_lzvn_block_table(const TSK_FS_ATTR *rAttr, CMP_OFFSET_ENTRY** offsetTableOut, uint32_t* tableSizeOut, uint32_t* tableOffsetOut) {
285
0
    ssize_t attrReadResult;
286
0
    char fourBytes[4];
287
0
    uint32_t tableDataSize;
288
0
    uint32_t tableSize;         // Size of the offset table
289
0
    char *offsetTableData = NULL;
290
0
    CMP_OFFSET_ENTRY *offsetTable = NULL;
291
292
    // The offset table is a sequence of 4-byte offsets of compressed
293
    // blocks. The first 4 bytes is thus the offset of the first block,
294
    // but also 4 times the number of entries in the table.
295
0
    attrReadResult = tsk_fs_attr_read(rAttr, 0, fourBytes, 4,
296
0
                                      TSK_FS_FILE_READ_FLAG_NONE);
297
0
    if (attrReadResult != 4) {
298
0
        error_returned
299
0
            (" %s: trying to read the offset table size, "
300
0
            "return value of %u should have been 4", __func__, attrReadResult);
301
0
        return 0;
302
0
    }
303
304
0
    tableDataSize = tsk_getu32(TSK_LIT_ENDIAN, fourBytes);
305
306
0
    offsetTableData = tsk_malloc(tableDataSize);
307
0
    if (offsetTableData == NULL) {
308
0
        error_returned
309
0
            (" %s: space for the offset table raw data", __func__);
310
0
        return 0;
311
0
    }
312
313
    // table entries are 4 bytes, last entry is end of data
314
0
    tableSize = tableDataSize / 4 - 1;
315
316
0
    offsetTable =
317
0
        (CMP_OFFSET_ENTRY *) tsk_malloc(tableSize *
318
0
        sizeof(CMP_OFFSET_ENTRY));
319
0
    if (offsetTable == NULL) {
320
0
        error_returned
321
0
            (" %s: space for the offset table", __func__);
322
0
        goto on_error;
323
0
    }
324
325
0
    attrReadResult = tsk_fs_attr_read(rAttr, 0,
326
0
        offsetTableData, tableDataSize, TSK_FS_FILE_READ_FLAG_NONE);
327
0
    if (attrReadResult != (ssize_t) tableDataSize) {
328
0
        error_returned
329
0
            (" %s: reading in the compression offset table, "
330
0
            "return value %u should have been %u", __func__, attrReadResult,
331
0
            tableDataSize);
332
0
        goto on_error;
333
0
    }
334
335
0
    uint32_t a = tableDataSize;
336
0
    uint32_t b;
337
0
    size_t i;
338
339
0
    for (i = 0; i < tableSize; ++i) {
340
0
        b = tsk_getu32(TSK_LIT_ENDIAN, offsetTableData + 4*(i+1));
341
0
        offsetTable[i].offset = a;
342
0
        offsetTable[i].length = b - a;
343
0
        a = b;
344
0
    }
345
346
0
    free(offsetTableData);
347
348
0
    *offsetTableOut = offsetTable;
349
0
    *tableSizeOut = tableSize;
350
0
    *tableOffsetOut = 0;
351
0
    return 1;
352
353
0
on_error:
354
0
    free(offsetTable);
355
0
    free(offsetTableData);
356
0
    return 0;
357
0
}
358
359
/**
360
 * \internal
361
 * "Decompress" a block which was stored uncompressed.
362
 *
363
 * @param rawBuf the compressed data
364
 * @param len length of the compressed data
365
 * @param uncBuf the decompressed data
366
 * @param uncLen length of the decompressed data
367
 * @return 1 on success, 0 on error
368
 */
369
0
static int decmpfs_decompress_noncompressed_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen) {
370
    // actually an uncompressed block of data; just copy
371
0
    if (tsk_verbose)
372
0
        tsk_fprintf(stderr,
373
0
           "%s: Copying an uncompressed compression unit\n", __func__);
374
375
0
    if ((len - 1) > COMPRESSION_UNIT_SIZE) {
376
0
        error_detected(TSK_ERR_FS_READ,
377
0
            "%s: uncompressed block length %u is longer "
378
0
            "than compression unit size %u", __func__, len - 1,
379
0
            COMPRESSION_UNIT_SIZE);
380
0
        return 0;
381
0
    }
382
0
    memcpy(uncBuf, rawBuf + 1, len - 1);
383
0
    *uncLen = len - 1;
384
0
    return 1;
385
0
}
386
387
388
#ifdef HAVE_LIBZ
389
/**
390
 * \internal
391
 * Decompress a block which was stored with ZLIB.
392
 *
393
 * @param rawBuf the compressed data
394
 * @param len length of the compressed data
395
 * @param uncBuf the decompressed data
396
 * @param uncLen length of the decompressed data
397
 * @return 1 on success, 0 on error
398
 */
399
static int decmpfs_decompress_zlib_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen)
400
0
{
401
    // see if this block is compressed
402
0
    if (len > 0 && (rawBuf[0] & 0x0F) != 0x0F) {
403
        // Uncompress the chunk of data
404
0
        if (tsk_verbose)
405
0
            tsk_fprintf(stderr,
406
0
                        "%s: Inflating the compression unit\n", __func__);
407
408
0
        unsigned long bytesConsumed;
409
0
        int infResult = zlib_inflate(rawBuf, (uint64_t) len,
410
0
            uncBuf, (uint64_t) COMPRESSION_UNIT_SIZE,
411
0
            uncLen, &bytesConsumed);
412
0
        if (infResult != 0) {
413
0
            error_returned
414
0
                  (" %s: zlib inflation (uncompression) failed",
415
0
                  __func__, infResult);
416
0
            return 0;
417
0
        }
418
419
0
        if (bytesConsumed != len) {
420
0
            error_detected(TSK_ERR_FS_READ,
421
0
                " %s, decompressor did not consume the whole compressed data",
422
0
                __func__);
423
0
            return 0;
424
0
        }
425
426
0
        return 1;
427
0
    }
428
0
    else {
429
        // actually an uncompressed block of data; just copy
430
0
        return decmpfs_decompress_noncompressed_block(rawBuf, len, uncBuf, uncLen);
431
0
    }
432
0
}
433
#endif
434
435
/**
436
 * \internal
437
 * Decompress a block which was stored with LZVN.
438
 *
439
 * @param rawBuf the compressed data
440
 * @param len length of the compressed data
441
 * @param uncBuf the decompressed data
442
 * @param uncLen length of the decompressed data
443
 * @return 1 on success, 0 on error
444
 */
445
static int decmpfs_decompress_lzvn_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen)
446
0
{
447
    // see if this block is compressed
448
0
    if (len > 0 && rawBuf[0] != 0x06) {
449
0
        *uncLen = lzvn_decode_buffer(uncBuf, COMPRESSION_UNIT_SIZE, rawBuf, len);
450
0
        return 1;  // apparently this can't fail
451
0
    }
452
0
    else {
453
        // actually an uncompressed block of data; just copy
454
0
        return decmpfs_decompress_noncompressed_block(rawBuf, len, uncBuf, uncLen);
455
0
    }
456
0
}
457
458
/**
459
 * \internal
460
 * Decompress a block.
461
 *
462
 * @param rAttr the attribute to read
463
 * @param rawBuf the compressed data
464
 * @param uncBuf the decompressed data
465
 * @param offsetTable table of compressed block offsets
466
 * @param offsetTableSize size of table of compressed block offsets
467
 * @param offsetTableOffset offset of table of compressed block offsets
468
 * @param indx index of block to read
469
 * @param decompress_block pointer to decompression function
470
 * @return decompressed size on success, -1 on error
471
 */
472
static ssize_t read_and_decompress_block(
473
  const TSK_FS_ATTR* rAttr,
474
  char* rawBuf,
475
  char* uncBuf,
476
  const CMP_OFFSET_ENTRY* offsetTable,
477
  uint32_t offsetTableSize,
478
  uint32_t offsetTableOffset,
479
  size_t indx,
480
  int (*decompress_block)(char* rawBuf,
481
                          uint32_t len,
482
                          char* uncBuf,
483
                          uint64_t* uncLen)
484
)
485
0
{
486
    // @@@ BC: Looks like we should have bounds checks that indx < offsetTableSize, but we should confirm
487
0
    ssize_t attrReadResult;
488
0
    uint32_t offset = offsetTableOffset + offsetTable[indx].offset;
489
0
    uint32_t len = offsetTable[indx].length;
490
0
    uint64_t uncLen;
491
492
0
    if (tsk_verbose)
493
0
        tsk_fprintf(stderr,
494
0
            "%s: Reading compression unit %d, length %d\n",
495
0
            __func__, indx, len);
496
497
    /* Github #383 referenced that if len is 0, then the below code causes
498
     * problems. Added this check, but I don't have data to verify this on.
499
     * it looks like it should at least not crash, but it isn't clear if it
500
     * will also do the right thing and if should actually break here
501
     * instead. */
502
0
    if (len == 0) {
503
0
        return 0;
504
0
    }
505
506
0
    if (len > COMPRESSION_UNIT_SIZE + 1) {
507
0
      error_detected(TSK_ERR_FS_READ,
508
0
          "%s: block size is too large: %u", __func__, len);
509
0
      return -1;
510
0
    }
511
512
    // Read in the block of compressed data
513
0
    attrReadResult = tsk_fs_attr_read(rAttr, offset,
514
0
        rawBuf, len, TSK_FS_FILE_READ_FLAG_NONE);
515
0
    if (attrReadResult != (ssize_t) len) {
516
0
        char msg[] =
517
0
            "%s%s: reading in the compression offset table, "
518
0
            "return value %u should have been %u";
519
520
0
        if (attrReadResult < 0 ) {
521
0
            error_returned(msg, " ", __func__, attrReadResult, len);
522
0
        }
523
0
        else {
524
0
            error_detected(TSK_ERR_FS_READ, "", __func__, attrReadResult, len);
525
0
        }
526
0
        return -1;
527
0
    }
528
529
0
    if (!decompress_block(rawBuf, len, uncBuf, &uncLen)) {
530
0
        return -1;
531
0
    }
532
/*
533
    // If size is a multiple of COMPRESSION_UNIT_SIZE,
534
    // expected uncompressed length is COMPRESSION_UNIT_SIZE
535
    const uint32_t expUncLen = indx == offsetTableSize - 1 ?
536
        ((rAttr->fs_file->meta->size - 1) % COMPRESSION_UNIT_SIZE) + 1 :
537
        COMPRESSION_UNIT_SIZE;
538
539
    if (uncLen != expUncLen) {
540
        error_detected(TSK_ERR_FS_READ,
541
            "%s: compressed block decompressed to %u bytes, "
542
            "should have been %u bytes", __func__, uncLen, expUncLen);
543
        return -1;
544
    }
545
*/
546
547
    // There are now uncLen bytes of uncompressed data available from
548
    // this comp unit.
549
0
    return (ssize_t)uncLen;
550
0
}
551
552
/**
553
 * \internal
554
 * Attr walk callback function for compressed resources
555
 *
556
 * @param fs_attr the attribute to read
557
 * @param flags
558
 * @param a_action action callback
559
 * @param ptr context for the action callback
560
 * @param read_block_table pointer to block table read function
561
 * @param decompress_block pointer to decompression function
562
 * @return 0 on success, 1 on error
563
 */
564
static uint8_t
565
decmpfs_attr_walk_compressed_rsrc(const TSK_FS_ATTR * fs_attr,
566
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr,
567
    int (*read_block_table)(const TSK_FS_ATTR *rAttr,
568
                            CMP_OFFSET_ENTRY** offsetTableOut,
569
                            uint32_t* tableSizeOut,
570
                            uint32_t* tableOffsetOut),
571
    int (*decompress_block)(char* rawBuf,
572
                            uint32_t len,
573
                            char* uncBuf,
574
                            uint64_t* uncLen))
575
0
{
576
0
    TSK_FS_INFO *fs;
577
0
    TSK_FS_FILE *fs_file;
578
0
    const TSK_FS_ATTR *rAttr;   // resource fork attribute
579
0
    char *rawBuf = NULL;               // compressed data
580
0
    char *uncBuf = NULL;               // uncompressed data
581
0
    uint32_t offsetTableOffset;
582
0
    uint32_t offsetTableSize;         // The number of table entries
583
0
    CMP_OFFSET_ENTRY *offsetTable = NULL;
584
0
    size_t indx;                // index for looping over the offset table
585
0
    TSK_OFF_T off = 0;          // the offset in the uncompressed data stream consumed thus far
586
587
0
    if (tsk_verbose)
588
0
        tsk_fprintf(stderr,
589
0
            "%s:  Entered, because this is a compressed file with compressed data in the resource fork\n", __func__);
590
591
    // clean up any error messages that are lying around
592
0
    tsk_error_reset();
593
0
    if ((fs_attr == NULL) || (fs_attr->fs_file == NULL)
594
0
        || (fs_attr->fs_file->meta == NULL)
595
0
        || (fs_attr->fs_file->fs_info == NULL)) {
596
0
        tsk_error_set_errno(TSK_ERR_FS_ARG);
597
0
        tsk_error_set_errstr("%s: Null arguments given\n", __func__);
598
0
        return 1;
599
0
    }
600
601
    // Check that the ATTR being read is the main DATA resource, 128-0,
602
    // because this is the only one that can be compressed in HFS+
603
0
    if ((fs_attr->id != HFS_FS_ATTR_ID_DATA) ||
604
0
        (fs_attr->type != TSK_FS_ATTR_TYPE_HFS_DATA)) {
605
0
        error_detected(TSK_ERR_FS_ARG,
606
0
            "%s: arg specified an attribute %u-%u that is not the data fork, "
607
0
            "Only the data fork can be compressed.", __func__, fs_attr->type,
608
0
            fs_attr->id);
609
0
        return 1;
610
0
    }
611
612
    /* This MUST be a compressed attribute     */
613
0
    if (!(fs_attr->flags & TSK_FS_ATTR_COMP)) {
614
0
        error_detected(TSK_ERR_FS_FWALK,
615
0
            "%s: called with non-special attribute: %x",
616
0
            __func__, fs_attr->flags);
617
0
        return 1;
618
0
    }
619
620
0
    fs = fs_attr->fs_file->fs_info;
621
0
    fs_file = fs_attr->fs_file;
622
623
    /********  Open the Resource Fork ***********/
624
625
    // find the attribute for the resource fork
626
0
    rAttr =
627
0
        tsk_fs_file_attr_get_type(fs_file, TSK_FS_ATTR_TYPE_HFS_RSRC,
628
0
        HFS_FS_ATTR_ID_RSRC, FALSE);
629
0
    if (rAttr == NULL) {
630
0
        error_returned
631
0
            (" %s: could not get the attribute for the resource fork of the file", __func__);
632
0
        return 1;
633
0
    }
634
635
    // read the offset table from the fork header
636
0
    if (!read_block_table(rAttr, &offsetTable, &offsetTableSize, &offsetTableOffset)) {
637
0
      return 1;
638
0
    }
639
640
    // Allocate two buffers for the raw and uncompressed data
641
    /* Raw data can be COMPRESSION_UNIT_SIZE+1 if the data is not
642
     * compressed and there is a 1-byte flag that indicates that
643
     * the data is not compressed. */
644
0
    rawBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE + 1);
645
0
    if (rawBuf == NULL) {
646
0
        error_returned
647
0
            (" %s: buffers for reading and uncompressing", __func__);
648
0
        goto on_error;
649
0
    }
650
651
0
    uncBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE);
652
0
    if (uncBuf == NULL) {
653
0
        error_returned
654
0
            (" %s: buffers for reading and uncompressing", __func__);
655
0
        goto on_error;
656
0
    }
657
658
    // FOR entry in the table DO
659
0
    for (indx = 0; indx < offsetTableSize; ++indx) {
660
0
        ssize_t uncLen;        // uncompressed length
661
0
        unsigned int blockSize;
662
0
        uint64_t lumpSize;
663
0
        uint64_t remaining;
664
0
        char *lumpStart;
665
666
0
        switch ((uncLen = read_and_decompress_block(
667
0
                    rAttr, rawBuf, uncBuf,
668
0
                    offsetTable, offsetTableSize, offsetTableOffset, indx,
669
0
                    decompress_block)))
670
0
        {
671
0
        case -1:
672
0
            goto on_error;
673
0
        case  0:
674
0
            continue;
675
0
        default:
676
0
            break;
677
0
        }
678
679
        // Call the a_action callback with "Lumps"
680
        // that are at most the block size.
681
0
        blockSize = fs->block_size;
682
0
        remaining = uncLen;
683
0
        lumpStart = uncBuf;
684
685
0
        while (remaining > 0) {
686
0
            int retval;         // action return value
687
0
            lumpSize = remaining <= blockSize ? remaining : blockSize;
688
689
            // Apply the callback function
690
0
            if (tsk_verbose)
691
0
                tsk_fprintf(stderr,
692
0
                    "%s: Calling action on lump of size %"
693
0
                    PRIu64 " offset %" PRIu64 " in the compression unit\n",
694
0
                    __func__, lumpSize, uncLen - remaining);
695
0
            if (lumpSize > SIZE_MAX) {
696
0
                error_detected(TSK_ERR_FS_FWALK,
697
0
                    " %s: lumpSize is too large for the action", __func__);
698
0
                goto on_error;
699
0
            }
700
701
0
            retval = a_action(fs_attr->fs_file, off, 0, lumpStart,
702
0
                (size_t) lumpSize,   // cast OK because of above test
703
0
                TSK_FS_BLOCK_FLAG_COMP, ptr);
704
705
0
            if (retval == TSK_WALK_ERROR) {
706
0
                error_detected(TSK_ERR_FS | 201,
707
0
                    "%s: callback returned an error", __func__);
708
0
                goto on_error;
709
0
            }
710
0
            else if (retval == TSK_WALK_STOP) {
711
0
                break;
712
0
            }
713
714
            // Find the next lump
715
0
            off += lumpSize;
716
0
            remaining -= lumpSize;
717
0
            lumpStart += lumpSize;
718
0
        }
719
0
    }
720
721
    // Done, so free up the allocated resources.
722
0
    free(offsetTable);
723
0
    free(rawBuf);
724
0
    free(uncBuf);
725
0
    return 0;
726
727
0
on_error:
728
0
    free(offsetTable);
729
0
    free(rawBuf);
730
0
    free(uncBuf);
731
0
    return 1;
732
0
}
733
734
735
#ifdef HAVE_LIBZ
736
/**
737
 * \internal
738
 * Attr walk callback function for ZLIB compressed resources
739
 *
740
 * @param fs_attr the attribute to read
741
 * @param flags
742
 * @param a_action action callback
743
 * @param ptr context for the action callback
744
 * @return 0 on success, 1 on error
745
 */
746
uint8_t
747
decmpfs_attr_walk_zlib_rsrc(const TSK_FS_ATTR * fs_attr,
748
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
749
0
{
750
0
    return decmpfs_attr_walk_compressed_rsrc(
751
0
      fs_attr, flags, a_action, ptr,
752
0
      decmpfs_read_zlib_block_table,
753
0
      decmpfs_decompress_zlib_block
754
0
    );
755
0
}
756
#endif
757
758
/**
759
 * \internal
760
 * Attr walk callback function for LZVN compressed resources
761
 *
762
 * @param fs_attr the attribute to read
763
 * @param flags
764
 * @param a_action action callback
765
 * @param ptr context for the action callback
766
 * @return 0 on success, 1 on error
767
 */
768
uint8_t
769
decmpfs_attr_walk_lzvn_rsrc(const TSK_FS_ATTR * fs_attr,
770
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
771
0
{
772
0
    return decmpfs_attr_walk_compressed_rsrc(
773
0
      fs_attr, flags, a_action, ptr,
774
0
      decmpfs_read_lzvn_block_table,
775
0
      decmpfs_decompress_lzvn_block
776
0
    );
777
0
}
778
779
780
/**
781
 * \internal
782
 * Read a compressed resource
783
 *
784
 * @param fs_attr the attribute to read
785
 * @param a_offset the offset from which to read
786
 * @param a_buf the buffer into which to read
787
 * @param a_len the length of the buffer
788
 * @param read_block_table pointer to block table read function
789
 * @param decompress_block pointer to decompression function
790
 * @return number of bytes read or -1 on error (incl if offset is past EOF)
791
 */
792
static ssize_t
793
decmpfs_file_read_compressed_rsrc(const TSK_FS_ATTR * a_fs_attr,
794
    TSK_OFF_T a_offset, char *a_buf, size_t a_len,
795
    int (*read_block_table)(const TSK_FS_ATTR *rAttr,
796
                            CMP_OFFSET_ENTRY** offsetTableOut,
797
                            uint32_t* tableSizeOut,
798
                            uint32_t* tableOffsetOut),
799
    int (*decompress_block)(char* rawBuf,
800
                            uint32_t len,
801
                            char* uncBuf,
802
                            uint64_t* uncLen))
803
0
{
804
0
    TSK_FS_FILE *fs_file;
805
0
    const TSK_FS_ATTR *rAttr;
806
0
    char *rawBuf = NULL;
807
0
    char *uncBuf = NULL;
808
0
    uint32_t offsetTableOffset;
809
0
    uint32_t offsetTableSize;         // Size of the offset table
810
0
    CMP_OFFSET_ENTRY *offsetTable = NULL;
811
0
    TSK_OFF_T indx;                // index for looping over the offset table
812
0
    TSK_OFF_T startUnit = 0;
813
0
    uint32_t startUnitOffset = 0;
814
0
    TSK_OFF_T endUnit = 0;
815
0
    uint64_t bytesCopied;
816
817
0
    if (tsk_verbose)
818
0
        tsk_fprintf(stderr,
819
0
            "%s: called because this file is compressed, with data in the resource fork\n", __func__);
820
821
    // Reading zero bytes?  OK at any offset, I say!
822
0
    if (a_len == 0)
823
0
        return 0;
824
825
0
    if (a_offset < 0) {
826
0
        error_detected(TSK_ERR_FS_ARG,
827
0
            "%s: reading from file at a negative offset",
828
0
             __func__);
829
0
        return -1;
830
0
    }
831
832
0
    if (a_len > SIZE_MAX / 2) {
833
0
        error_detected(TSK_ERR_FS_ARG,
834
0
            "%s: trying to read more than SIZE_MAX/2 is not supported.",
835
0
            __func__);
836
0
        return -1;
837
0
    }
838
839
0
    if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
840
0
        || (a_fs_attr->fs_file->meta == NULL)
841
0
        || (a_fs_attr->fs_file->fs_info == NULL)) {
842
0
        error_detected(TSK_ERR_FS_ARG,
843
0
            "%s: NULL parameters passed", __func__);
844
0
        return -1;
845
0
    }
846
847
    // This should be a compressed file.  If not, that's an error!
848
0
    if (!(a_fs_attr->flags & TSK_FS_ATTR_COMP)) {
849
0
        error_detected(TSK_ERR_FS_ARG,
850
0
            "%s: called with non-special attribute: %x",
851
0
            __func__, a_fs_attr->flags);
852
0
        return -1;
853
0
    }
854
855
    // Check that the ATTR being read is the main DATA resource, 4352-0,
856
    // because this is the only one that can be compressed in HFS+
857
0
    if ((a_fs_attr->id != HFS_FS_ATTR_ID_DATA) ||
858
0
        (a_fs_attr->type != TSK_FS_ATTR_TYPE_HFS_DATA)) {
859
0
        error_detected(TSK_ERR_FS_ARG,
860
0
            "%s: arg specified an attribute %u-%u that is not the data fork, "
861
0
            "Only the data fork can be compressed.", __func__,
862
0
            a_fs_attr->type, a_fs_attr->id);
863
0
        return -1;
864
0
    }
865
866
    /********  Open the Resource Fork ***********/
867
    // The file
868
0
    fs_file = a_fs_attr->fs_file;
869
870
    // find the attribute for the resource fork
871
0
    rAttr =
872
0
        tsk_fs_file_attr_get_type(fs_file, TSK_FS_ATTR_TYPE_HFS_RSRC,
873
0
        HFS_FS_ATTR_ID_RSRC, FALSE);
874
0
    if (rAttr == NULL) {
875
0
        error_returned
876
0
            (" %s: could not get the attribute for the resource fork of the file", __func__);
877
0
        return -1;
878
0
    }
879
880
    // read the offset table from the fork header
881
0
    if (!read_block_table(rAttr, &offsetTable, &offsetTableSize, &offsetTableOffset)) {
882
0
      return -1;
883
0
    }
884
885
    // Compute the range of compression units needed for the request
886
0
    startUnit = a_offset / COMPRESSION_UNIT_SIZE;
887
0
    startUnitOffset = a_offset % COMPRESSION_UNIT_SIZE;
888
0
    endUnit = (a_offset + a_len - 1) / COMPRESSION_UNIT_SIZE;
889
890
0
    if (startUnit >= offsetTableSize || endUnit >= offsetTableSize) {
891
0
        error_detected(TSK_ERR_FS_ARG,
892
0
            "%s: range of bytes requested %lld - %lld falls past the "
893
0
            "end of the uncompressed stream %llu\n",
894
0
            __func__, a_offset, a_offset + a_len,
895
0
            offsetTable[offsetTableSize-1].offset +
896
0
            offsetTable[offsetTableSize-1].length);
897
0
        goto on_error;
898
0
    }
899
900
0
    if (tsk_verbose)
901
0
        tsk_fprintf(stderr,
902
0
            "%s: reading compression units: %" PRIdOFF
903
0
            " to %" PRIdOFF "\n", __func__, startUnit, endUnit);
904
0
   bytesCopied = 0;
905
906
    // Allocate buffers for the raw and uncompressed data
907
    /* Raw data can be COMPRESSION_UNIT_SIZE+1 if the zlib data is not
908
     * compressed and there is a 1-byte flag that indicates that
909
     * the data is not compressed. */
910
0
    rawBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE + 1);
911
0
    if (rawBuf == NULL) {
912
0
        error_returned
913
0
            (" %s: buffers for reading and uncompressing", __func__);
914
0
        goto on_error;
915
0
    }
916
917
0
    uncBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE);
918
0
    if (uncBuf == NULL) {
919
0
        error_returned
920
0
            (" %s: buffers for reading and uncompressing", __func__);
921
0
        goto on_error;
922
0
    }
923
924
    // Read from the indicated comp units
925
0
    for (indx = startUnit; indx <= endUnit; ++indx) {
926
0
        uint64_t uncLen;
927
0
        char *uncBufPtr = uncBuf;
928
0
        size_t bytesToCopy;
929
930
0
        switch ((uncLen = read_and_decompress_block(
931
0
                    rAttr, rawBuf, uncBuf,
932
0
                    offsetTable, offsetTableSize, offsetTableOffset, (size_t)indx,
933
0
                    decompress_block)))
934
0
        {
935
0
        case -1:
936
0
            goto on_error;
937
0
        case  0:
938
0
            continue;
939
0
        default:
940
0
            break;
941
0
        }
942
943
        // If this is the first comp unit, then we must skip over the
944
        // startUnitOffset bytes.
945
0
        if (indx == startUnit) {
946
0
            uncLen -= startUnitOffset;
947
0
            uncBufPtr += startUnitOffset;
948
0
        }
949
950
        // How many bytes to copy from this compression unit?
951
952
0
        if (bytesCopied + uncLen < (uint64_t) a_len)    // cast OK because a_len > 0
953
0
            bytesToCopy = (size_t) uncLen;      // uncLen <= size of compression unit, which is small, so cast is OK
954
0
        else
955
0
            bytesToCopy = (size_t) (((uint64_t) a_len) - bytesCopied);  // diff <= compression unit size, so cast is OK
956
957
        // Copy into the output buffer, and update bookkeeping.
958
0
        memcpy(a_buf + bytesCopied, uncBufPtr, bytesToCopy);
959
0
        bytesCopied += bytesToCopy;
960
0
    }
961
962
    // Well, we don't know (without a lot of work) what the
963
    // true uncompressed size of the stream is.  All we know is the "upper bound" which
964
    // assumes that all of the compression units expand to their full size.  If we did
965
    // know the true size, then we could reject requests that go beyond the end of the
966
    // stream.  Instead, we treat the stream as if it is padded out to the full size of
967
    // the last compression unit with zeros.
968
969
    // Have we read and copied all of the bytes requested?
970
0
    if (bytesCopied < a_len) {
971
        // set the remaining bytes to zero
972
0
        memset(a_buf + bytesCopied, 0, a_len - (size_t) bytesCopied);   // cast OK because diff must be < compression unit size
973
0
    }
974
975
0
    free(offsetTable);
976
0
    free(rawBuf);
977
0
    free(uncBuf);
978
979
0
    return (ssize_t) bytesCopied;       // cast OK, cannot be greater than a_len which cannot be greater than SIZE_MAX/2 (rounded down).
980
981
0
on_error:
982
0
    free(offsetTable);
983
0
    free(rawBuf);
984
0
    free(uncBuf);
985
0
    return -1;
986
0
}
987
988
989
#ifdef HAVE_LIBZ
990
/**
991
 * \internal
992
 * Read a ZLIB compressed resource
993
 *
994
 * @param fs_attr the attribute to read
995
 * @param a_offset the offset from which to read
996
 * @param a_buf the buffer into which to read
997
 * @param a_len the length of the buffer
998
 * @return number of bytes read or -1 on error (incl if offset is past EOF)
999
 */
1000
ssize_t
1001
decmpfs_file_read_zlib_rsrc(const TSK_FS_ATTR * a_fs_attr,
1002
    TSK_OFF_T a_offset, char *a_buf, size_t a_len)
1003
0
{
1004
0
    return decmpfs_file_read_compressed_rsrc(
1005
0
        a_fs_attr, a_offset, a_buf, a_len,
1006
0
        decmpfs_read_zlib_block_table,
1007
0
        decmpfs_decompress_zlib_block
1008
0
    );
1009
0
}
1010
#endif
1011
1012
/**
1013
 * Read an LZVN compressed resource
1014
 *
1015
 * @param fs_attr the attribute to read
1016
 * @param a_offset the offset from which to read
1017
 * @param a_buf the buffer into which to read
1018
 * @param a_len the length of the buffer
1019
 * @return number of bytes read or -1 on error (incl if offset is past EOF)
1020
 */
1021
ssize_t
1022
decmpfs_file_read_lzvn_rsrc(const TSK_FS_ATTR * a_fs_attr,
1023
    TSK_OFF_T a_offset, char *a_buf, size_t a_len)
1024
0
{
1025
0
    return decmpfs_file_read_compressed_rsrc(
1026
0
        a_fs_attr, a_offset, a_buf, a_len,
1027
0
        decmpfs_read_lzvn_block_table,
1028
0
        decmpfs_decompress_lzvn_block
1029
0
    );
1030
0
}
1031
1032
/**
1033
 * \internal
1034
 * "Decompress" an uncompressed attr
1035
 *
1036
 * HFS+ compression schemes allow for some blocks to be stored uncompressed.
1037
 *
1038
 * @param rawBuf source buffer
1039
 * @param rawSize size of source buffer
1040
 * @param uncSize expected uncompressed size
1041
 * @param dstBuf destination buffer
1042
 * @param dstSize size of destination buffer
1043
 * @param dstBufFree true iff the caller must free the destination buffer
1044
 * @return 1
1045
 */
1046
0
static int decmpfs_decompress_noncompressed_attr(char* rawBuf, uint32_t rawSize, uint64_t uncSize, char** dstBuf, uint64_t* dstSize, int* dstBufFree) {
1047
0
    if (tsk_verbose)
1048
0
        tsk_fprintf(stderr,
1049
0
            "%s: Leading byte, 0x%02x, indicates that the data is not really compressed.\n"
1050
0
            "%s:  Loading the default DATA attribute.", __func__, rawBuf[0], __func__);
1051
1052
0
    *dstBuf = rawBuf + 1;  // + 1 indicator byte
1053
0
    *dstSize = uncSize;
1054
0
    *dstBufFree = FALSE;
1055
0
    return 1;
1056
0
}
1057
1058
/**
1059
 * \internal
1060
 * Decompress a ZLIB compressed attr
1061
 *
1062
 * @param rawBuf source buffer
1063
 * @param rawSize size of source buffer
1064
 * @param uncSize expected uncompressed size
1065
 * @param dstBuf destination buffer
1066
 * @param dstSize size of destination buffer
1067
 * @param dstBufFree true iff the caller must free the destination buffer
1068
 * @return 1 on success, 0 on error
1069
 */
1070
static int decmpfs_decompress_zlib_attr(char* rawBuf, uint32_t rawSize, uint64_t uncSize, char** dstBuf, uint64_t* dstSize, int* dstBufFree)
1071
0
{
1072
    // ZLIB blocks cannot start with 0xF as the low nibble, so that's used
1073
    // as the flag for noncompressed blocks
1074
0
    if ((rawBuf[0] & 0x0F) == 0x0F) {
1075
0
        return decmpfs_decompress_noncompressed_attr(
1076
0
            rawBuf, rawSize, uncSize, dstBuf, dstSize, dstBufFree);
1077
0
    }
1078
0
    else {
1079
0
#ifdef HAVE_LIBZ
1080
0
        char* uncBuf = NULL;
1081
0
        uint64_t uLen;
1082
0
        unsigned long bytesConsumed;
1083
0
        int infResult;
1084
1085
0
        if (tsk_verbose)
1086
0
            tsk_fprintf(stderr,
1087
0
                        "%s: Uncompressing (inflating) data.", __func__);
1088
        // Uncompress the remainder of the attribute, and load as 128-0
1089
        // Note: cast is OK because uncSize will be quite modest, < 4000.
1090
1091
0
        uncBuf = (char *) tsk_malloc((size_t) uncSize + 100); // add some extra space
1092
0
        if (uncBuf == NULL) {
1093
0
            error_returned
1094
0
                (" - %s, space for the uncompressed attr", __func__);
1095
0
            return 0;
1096
0
        }
1097
1098
0
        infResult = zlib_inflate(rawBuf, (uint64_t) rawSize,
1099
0
                                 uncBuf, (uint64_t) (uncSize + 100),
1100
0
                                 &uLen, &bytesConsumed);
1101
0
        if (infResult != 0) {
1102
0
            error_returned
1103
0
                (" %s, zlib could not uncompress attr", __func__);
1104
0
            free(uncBuf);
1105
0
            return 0;
1106
0
        }
1107
1108
0
        if (bytesConsumed != rawSize) {
1109
0
            error_detected(TSK_ERR_FS_READ,
1110
0
                " %s, decompressor did not consume the whole compressed data",
1111
0
                __func__);
1112
0
            free(uncBuf);
1113
0
            return 0;
1114
0
        }
1115
1116
0
        *dstBuf = uncBuf;
1117
0
        *dstSize = uncSize;
1118
0
        *dstBufFree = TRUE;
1119
#else
1120
        // ZLIB compression library is not available, so we will load a
1121
        // zero-length default DATA attribute. Without this, icat may
1122
        // misbehave.
1123
1124
        if (tsk_verbose)
1125
            tsk_fprintf(stderr,
1126
                        "%s: ZLIB not available, so loading an empty default DATA attribute.\n", __func__);
1127
1128
        // Dummy is one byte long, so the ptr is not null, but we set the
1129
        // length to zero bytes, so it is never read.
1130
        static uint8_t dummy[1];
1131
1132
        *dstBuf = dummy;
1133
        *dstSize = 0;
1134
        *dstBufFree = FALSE;
1135
#endif
1136
0
    }
1137
1138
0
    return 1;
1139
0
}
1140
1141
/**
1142
 * \internal
1143
 * Decompress an LZVN compressed attr
1144
 *
1145
 * @param rawBuf source buffer
1146
 * @param rawSize size of source buffer
1147
 * @param uncSize expected uncompressed size
1148
 * @param dstBuf destination buffer
1149
 * @param dstSize size of destination buffer
1150
 * @param dstBufFree true iff the caller must free the destination buffer
1151
 * @return 1 on success, 0 on error
1152
 */
1153
static int decmpfs_decompress_lzvn_attr(char* rawBuf, uint32_t rawSize, uint64_t uncSize, char** dstBuf, uint64_t* dstSize, int* dstBufFree)
1154
0
{
1155
    // LZVN blocks cannot start with 0x06, so that's used as the flag for
1156
    // noncompressed blocks
1157
0
    if (rawBuf[0] == 0x06) {
1158
0
        return decmpfs_decompress_noncompressed_attr(
1159
0
            rawBuf, rawSize, uncSize, dstBuf, dstSize, dstBufFree);
1160
0
    }
1161
1162
0
    char* uncBuf = (char *) tsk_malloc((size_t) uncSize);
1163
0
    *dstSize = lzvn_decode_buffer(uncBuf, uncSize, rawBuf, rawSize);
1164
0
    *dstBuf = uncBuf;
1165
0
    *dstBufFree = TRUE;
1166
1167
0
    return 1;
1168
0
}
1169
1170
/**
1171
 * \internal
1172
 * Read a compressed attr
1173
 *
1174
 * @param fs_file the file
1175
 * @param cmpType compression type
1176
 * @param buffer destination buffer
1177
 * @param attributeLength length of the attribute
1178
 * @param uncSize uncompressed size
1179
 * @param decompress_attr pointer to the decompression function
1180
 * @return 1 on success, 0 on error
1181
 */
1182
static int
1183
decmpfs_file_read_compressed_attr(TSK_FS_FILE* fs_file,
1184
                              uint8_t cmpType,
1185
                              char* buffer,
1186
                              TSK_OFF_T attributeLength,
1187
                              uint64_t uncSize,
1188
                              int (*decompress_attr)(char* rawBuf,
1189
                                                     uint32_t rawSize,
1190
                                                     uint64_t uncSize,
1191
                                                     char** dstBuf,
1192
                                                     uint64_t* dstSize,
1193
                                                     int* dstBufFree))
1194
0
{
1195
    // Data is inline. We will load the uncompressed data as a
1196
    // resident attribute.
1197
0
    if (tsk_verbose)
1198
0
        tsk_fprintf(stderr,
1199
0
            "%s: Compressed data is inline in the attribute, will load this as the default DATA attribute.\n", __func__);
1200
1201
0
    if (attributeLength <= 16) {
1202
0
        if (tsk_verbose)
1203
0
            tsk_fprintf(stderr,
1204
0
                "%s: WARNING, Compression Record of type %u is not followed by"
1205
0
                " compressed data. No data will be loaded into the DATA"
1206
0
                " attribute.\n", __func__, cmpType);
1207
1208
        // oddly, this is not actually considered an error
1209
0
        return 1;
1210
0
    }
1211
1212
0
    TSK_FS_ATTR *fs_attr_unc;
1213
1214
    // There is data following the compression record, as there should be.
1215
0
    if ((fs_attr_unc = tsk_fs_attrlist_getnew(
1216
0
          fs_file->meta->attr, TSK_FS_ATTR_RES)) == NULL)
1217
0
    {
1218
0
        error_returned(" - %s, FS_ATTR for uncompressed data", __func__);
1219
0
        return 0;
1220
0
    }
1221
1222
0
    char* dstBuf;
1223
0
    uint64_t dstSize;
1224
0
    int dstBufFree = FALSE;
1225
1226
0
    if (!decompress_attr(buffer + 16, attributeLength - 16, uncSize,
1227
0
                         &dstBuf, &dstSize, &dstBufFree)) {
1228
0
        return 0;
1229
0
    }
1230
1231
0
    if (dstSize != uncSize) {
1232
0
        error_detected(TSK_ERR_FS_READ,
1233
0
            " %s, actual uncompressed size not equal to the size in the compression record", __func__);
1234
0
        goto on_error;
1235
0
    }
1236
1237
0
    if (tsk_verbose)
1238
0
       tsk_fprintf(stderr,
1239
0
                   "%s: Loading decompressed data as default DATA attribute.",
1240
0
                   __func__);
1241
1242
    // Load the remainder of the attribute as 128-0
1243
    // set the details in the fs_attr structure.
1244
    // Note, we are loading this as a RESIDENT attribute.
1245
0
    if (tsk_fs_attr_set_str(fs_file,
1246
0
                            fs_attr_unc, "DECOMP",
1247
0
                            TSK_FS_ATTR_TYPE_HFS_DATA, 
1248
0
                            TSK_FS_ATTR_ID_DEFAULT, 
1249
0
                            dstBuf,
1250
0
                            dstSize))
1251
0
    {
1252
0
        error_returned(" - %s", __func__);
1253
0
        goto on_error;
1254
0
    }
1255
1256
0
    if (dstBufFree) {
1257
0
        free(dstBuf);
1258
0
    }
1259
0
    return 1;
1260
1261
0
on_error:
1262
0
    if (dstBufFree) {
1263
0
        free(dstBuf);
1264
0
    }
1265
0
    return 0;
1266
0
}
1267
1268
/**
1269
 * \internal
1270
 * Read a ZLIB compressed attr
1271
 *
1272
 * @param fs_file the file
1273
 * @param buffer destination buffer
1274
 * @param attributeLength length of the attribute
1275
 * @param uncSize uncompressed size
1276
 * @return 1 on success, 0 on error
1277
 */
1278
int decmpfs_file_read_zlib_attr(TSK_FS_FILE* fs_file,
1279
                            char* buffer,
1280
                            TSK_OFF_T attributeLength,
1281
                            uint64_t uncSize)
1282
0
{
1283
0
    return decmpfs_file_read_compressed_attr(
1284
0
        fs_file, DECMPFS_TYPE_ZLIB_ATTR,
1285
0
        buffer, attributeLength, uncSize,
1286
0
        decmpfs_decompress_zlib_attr
1287
0
    );
1288
0
}
1289
1290
/**
1291
 * \internal
1292
 * Read an LZVN compressed attr
1293
 *
1294
 * @param fs_file the file
1295
 * @param buffer destination buffer
1296
 * @param attributeLength length of the attribute
1297
 * @param uncSize uncompressed size
1298
 * @return 1 on success, 0 on error
1299
 */
1300
int decmpfs_file_read_lzvn_attr(TSK_FS_FILE* fs_file,
1301
                            char* buffer,
1302
                            TSK_OFF_T attributeLength,
1303
                            uint64_t uncSize)
1304
0
{
1305
0
    return decmpfs_file_read_compressed_attr(
1306
0
        fs_file, DECMPFS_TYPE_LZVN_ATTR,
1307
0
        buffer, attributeLength, uncSize,
1308
0
        decmpfs_decompress_lzvn_attr
1309
0
    );
1310
0
}