Commit message: Merge pull request #1374 from JordyZomer/develop

Fix CVE-2018-19497.
CVE: CVE-2018-19497
CWE: CWE-125
Vulnerability description: In The Sleuth Kit (TSK) through 4.6.4, hfs_cat_traverse in tsk/fs/hfs.c does not properly determine when a key length is too large, which allows attackers to cause a denial of service (SEGV on unknown address with READ memory access in a tsk_getu16 call in hfs_dir_open_meta_cb in tsk/fs/hfs_dent.c).

Project: sleuthkit

close_attr_file(ATTR_FILE_T * attr_file)
{
    if (attr_file == NULL) {
        tsk_error_set_errno(TSK_ERR_FS_READ);
        tsk_error_set_errstr("close_attr_file: NULL attr_file arg");
        return 1;
    }

    if (attr_file->file != NULL) {
        tsk_fs_file_close(attr_file->file);
        attr_file->file = NULL;
    }

    free(attr_file->header);
    attr_file->header = NULL;

    attr_file->rootNode = 0;
    attr_file->nodeSize = 0;
    return 0;
}

Project: sleuthkit
File: hfs.c

cnid_to_array(uint32_t cnid, uint8_t array[4])
{
    array[3] = (cnid >> 0) & 0xff;
    array[2] = (cnid >> 8) & 0xff;
    array[1] = (cnid >> 16) & 0xff;
    array[0] = (cnid >> 24) & 0xff;
}

Project: sleuthkit

error_detected(uint32_t errnum, char *errstr, ...)
{
    va_list args;

    va_start(args, errstr);

    {
        TSK_ERROR_INFO *errInfo = tsk_error_get_info();
        char *loc_errstr = errInfo->errstr;

        if (errInfo->t_errno == 0)
            errInfo->t_errno = errnum;
        else {
            size_t sl = strlen(errstr);
            snprintf(loc_errstr + sl, TSK_ERROR_STRING_MAX_LENGTH - sl,
                " Next errnum: 0x%x ", errnum);
        }
        if (errstr != NULL) {
            size_t sl = strlen(loc_errstr);
            vsnprintf(loc_errstr + sl, TSK_ERROR_STRING_MAX_LENGTH - sl,
                errstr, args);
        }
    }

    va_end(args);

}

Project: sleuthkit

error_returned(char *errstr, ...)
{
    va_list args;
    va_start(args, errstr);

    {
        TSK_ERROR_INFO *errInfo = tsk_error_get_info();
        char *loc_errstr2 = errInfo->errstr2;

        if (errInfo->t_errno == 0)
            errInfo->t_errno = TSK_ERR_AUX_GENERIC;
        if (errstr != NULL) {
            size_t sl = strlen(loc_errstr2);
            vsnprintf(loc_errstr2 + sl, TSK_ERROR_STRING_MAX_LENGTH - sl,
                errstr, args);
        }
    }
    va_end(args);
}

Project: sleuthkit

free_res_descriptor(RES_DESCRIPTOR * rd)
{
    RES_DESCRIPTOR *nxt;

    if (rd == NULL)
        return;
    nxt = rd->next;
    free(rd->name);
    free(rd);
    free_res_descriptor(nxt);   // tail recursive
}

Project: sleuthkit
File: hfs.c

hfs_attrTypeName(uint32_t typeNum)
{
    switch (typeNum) {
    case TSK_FS_ATTR_TYPE_HFS_DEFAULT:
        return "DFLT";
    case TSK_FS_ATTR_TYPE_HFS_DATA:
        return "DATA";
    case TSK_FS_ATTR_TYPE_HFS_EXT_ATTR:
        return "ExATTR";
    case TSK_FS_ATTR_TYPE_HFS_COMP_REC:
        return "CMPF";
    case TSK_FS_ATTR_TYPE_HFS_RSRC:
        return "RSRC";
    default:
        return "UNKN";
    }
}

Project: sleuthkit

hfs_attr_walk_compressed_rsrc(const TSK_FS_ATTR * fs_attr,
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr,
    int (*read_block_table)(const TSK_FS_ATTR *rAttr,
                            CMP_OFFSET_ENTRY** offsetTableOut,
                            uint32_t* tableSizeOut,
                            uint32_t* tableOffsetOut),
    int (*decompress_block)(char* rawBuf,
                            uint32_t len,
                            char* uncBuf,
                            uint64_t* uncLen))
{
    TSK_FS_INFO *fs;
    TSK_FS_FILE *fs_file;
    const TSK_FS_ATTR *rAttr;   // resource fork attribute
    char *rawBuf = NULL;               // compressed data
    char *uncBuf = NULL;               // uncompressed data
    uint32_t offsetTableOffset;
    uint32_t offsetTableSize;         // The number of table entries
    CMP_OFFSET_ENTRY *offsetTable = NULL;
    size_t indx;                // index for looping over the offset table
    TSK_OFF_T off = 0;          // the offset in the uncompressed data stream consumed thus far

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "%s:  Entered, because this is a compressed file with compressed data in the resource fork\n", __func__);

    tsk_error_reset();
    if ((fs_attr == NULL) || (fs_attr->fs_file == NULL)
        || (fs_attr->fs_file->meta == NULL)
        || (fs_attr->fs_file->fs_info == NULL)) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr("%s: Null arguments given\n", __func__);
        return 1;
    }

    if ((fs_attr->id != HFS_FS_ATTR_ID_DATA) ||
        (fs_attr->type != TSK_FS_ATTR_TYPE_HFS_DATA)) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: arg specified an attribute %u-%u that is not the data fork, "
            "Only the data fork can be compressed.", __func__, fs_attr->type,
            fs_attr->id);
        return 1;
    }

    /* This MUST be a compressed attribute     */
    if (!(fs_attr->flags & TSK_FS_ATTR_COMP)) {
        error_detected(TSK_ERR_FS_FWALK,
            "%s: called with non-special attribute: %x",
            __func__, fs_attr->flags);
        return 1;
    }

    fs = fs_attr->fs_file->fs_info;
    fs_file = fs_attr->fs_file;

    /********  Open the Resource Fork ***********/

    rAttr =
        tsk_fs_file_attr_get_type(fs_file, TSK_FS_ATTR_TYPE_HFS_RSRC,
        HFS_FS_ATTR_ID_RSRC, TRUE);
    if (rAttr == NULL) {
        error_returned
            (" %s: could not get the attribute for the resource fork of the file", __func__);
        return 1;
    }

    if (!read_block_table(rAttr, &offsetTable, &offsetTableSize, &offsetTableOffset)) {
      return 1;
    }

    /* Raw data can be COMPRESSION_UNIT_SIZE+1 if the data is not
     * compressed and there is a 1-byte flag that indicates that
     * the data is not compressed. */
    rawBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE + 1);
    if (rawBuf == NULL) {
        error_returned
            (" %s: buffers for reading and uncompressing", __func__);
        goto on_error;
    }

    uncBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE);
    if (uncBuf == NULL) {
        error_returned
            (" %s: buffers for reading and uncompressing", __func__);
        goto on_error;
    }

    for (indx = 0; indx < offsetTableSize; ++indx) {
        ssize_t uncLen;        // uncompressed length
        unsigned int blockSize;
        uint64_t lumpSize;
        uint64_t remaining;
        char *lumpStart;

        switch ((uncLen = read_and_decompress_block(
                    rAttr, rawBuf, uncBuf,
                    offsetTable, offsetTableSize, offsetTableOffset, indx,
                    decompress_block)))
        {
        case -1:
            goto on_error;
        case  0:
            continue;
        default:
            break;
        }

        blockSize = fs->block_size;
        remaining = uncLen;
        lumpStart = uncBuf;

        while (remaining > 0) {
            int retval;         // action return value
            lumpSize = remaining <= blockSize ? remaining : blockSize;

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "%s: Calling action on lump of size %"
                    PRIu64 " offset %" PRIu64 " in the compression unit\n",
                    __func__, lumpSize, uncLen - remaining);
            if (lumpSize > SIZE_MAX) {
                error_detected(TSK_ERR_FS_FWALK,
                    " %s: lumpSize is too large for the action", __func__);
                goto on_error;
            }

            retval = a_action(fs_attr->fs_file, off, 0, lumpStart,
                (size_t) lumpSize,   // cast OK because of above test
                TSK_FS_BLOCK_FLAG_COMP, ptr);

            if (retval == TSK_WALK_ERROR) {
                error_detected(TSK_ERR_FS | 201,
                    "%s: callback returned an error", __func__);
                goto on_error;
            }
            else if (retval == TSK_WALK_STOP) {
                break;
            }

            off += lumpSize;
            remaining -= lumpSize;
            lumpStart += lumpSize;
        }
    }

    free(offsetTable);
    free(rawBuf);
    free(uncBuf);
    return 0;

on_error:
    free(offsetTable);
    free(rawBuf);
    free(uncBuf);
    return 1;
}

Project: sleuthkit
File: hfs.c

hfs_attr_walk_lzvn_rsrc(const TSK_FS_ATTR * fs_attr,
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
{
    return hfs_attr_walk_compressed_rsrc(
      fs_attr, flags, a_action, ptr,
      hfs_read_lzvn_block_table,
      hfs_decompress_lzvn_block
    );
}

Project: sleuthkit
File: hfs.c

hfs_attr_walk_zlib_rsrc(const TSK_FS_ATTR * fs_attr,
    int flags, TSK_FS_FILE_WALK_CB a_action, void *ptr)
{
    return hfs_attr_walk_compressed_rsrc(
      fs_attr, flags, a_action, ptr,
      hfs_read_zlib_block_table,
      hfs_decompress_zlib_block
    );
}

Project: sleuthkit
File: hfs.c

hfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
{
    return (hfs_block_is_alloc((HFS_INFO *) a_fs, a_addr) == 1) ?
        TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC;
}

Project: sleuthkit

hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
{
    TSK_FS_INFO *fs = &(hfs->fs_info);
    TSK_OFF_T b;
    size_t b2;

    if (hfs->blockmap_file == NULL) {
        if ((hfs->blockmap_file =
                tsk_fs_file_open_meta(fs, NULL,
                    HFS_ALLOCATION_FILE_ID)) == NULL) {
            tsk_error_errstr2_concat(" - Loading blockmap file");
            return -1;
        }

        /* cache the data attribute */
        hfs->blockmap_attr =
            tsk_fs_attrlist_get(hfs->blockmap_file->meta->attr,
            TSK_FS_ATTR_TYPE_DEFAULT);
        if (!hfs->blockmap_attr) {
            tsk_error_errstr2_concat
                (" - Data Attribute not found in Blockmap File");
            return -1;
        }
        hfs->blockmap_cache_start = -1;
        hfs->blockmap_cache_len = 0;
    }

    b = (TSK_OFF_T) a_addr / 8;
    if (b > hfs->blockmap_file->meta->size) {
        tsk_error_set_errno(TSK_ERR_FS_CORRUPT);
        tsk_error_set_errstr("hfs_block_is_alloc: block %" PRIuDADDR
            " is too large for bitmap (%" PRIuOFF ")", a_addr,
            hfs->blockmap_file->meta->size);
        return -1;
    }

    if ((hfs->blockmap_cache_start == -1)
        || (hfs->blockmap_cache_start > b)
        || (hfs->blockmap_cache_start + hfs->blockmap_cache_len <= (size_t) b)) {
        ssize_t cnt = tsk_fs_attr_read(hfs->blockmap_attr, b,
            hfs->blockmap_cache,
            sizeof(hfs->blockmap_cache), 0);
        if (cnt < 1) {
            tsk_error_set_errstr2
                ("hfs_block_is_alloc: Error reading block bitmap at offset %"
                PRIuOFF, b);
            return -1;
        }
        hfs->blockmap_cache_start = b;
        hfs->blockmap_cache_len = cnt;
    }
    b2 = (size_t) (b - hfs->blockmap_cache_start);
    return (hfs->blockmap_cache[b2] & (1 << (7 - (a_addr % 8)))) != 0;
}

Project: sleuthkit
File: hfs.c

hfs_cat_compare_keys(HFS_INFO * hfs, const hfs_btree_key_cat * key1,
    const hfs_btree_key_cat * key2)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
    uint32_t cnid1, cnid2;

    cnid1 = tsk_getu32(fs->endian, key1->parent_cnid);
    cnid2 = tsk_getu32(fs->endian, key2->parent_cnid);

    if (cnid1 < cnid2)
        return -1;
    if (cnid1 > cnid2)
        return 1;

    return hfs_unicode_compare(hfs, &key1->name, &key2->name);
}

Project: sleuthkit

hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry,
    unsigned char follow_hard_link)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
    hfs_btree_key_cat key;      /* current catalog key */
    hfs_thread thread;          /* thread record */
    hfs_file_folder record;     /* file/folder record */
    TSK_OFF_T off;

    tsk_error_reset();

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_cat_file_lookup: called for inum %" PRIuINUM "\n", inum);

    if ((inum == HFS_EXTENTS_FILE_ID) ||
        (inum == HFS_CATALOG_FILE_ID) ||
        (inum == HFS_ALLOCATION_FILE_ID) ||
        (inum == HFS_STARTUP_FILE_ID) ||
        (inum == HFS_ATTRIBUTES_FILE_ID)) {
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
        tsk_error_set_errstr
            ("hfs_cat_file_lookup: Called on special file: %" PRIuINUM,
            inum);
        return 1;
    }


    /* first look up the thread record for the item we're searching for */

    /* set up the thread record key */
    memset((char *) &key, 0, sizeof(hfs_btree_key_cat));
    cnid_to_array((uint32_t) inum, key.parent_cnid);

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_cat_file_lookup: Looking up thread record (%" PRIuINUM
            ")\n", inum);

    /* look up the thread record */
    off = hfs_cat_get_record_offset(hfs, &key);
    if (off == 0) {
        if (tsk_error_get_errno() == 0) {
            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
            tsk_error_set_errstr
                ("hfs_cat_file_lookup: Error finding thread node for file (%"
                PRIuINUM ")", inum);
        }
        else {
            tsk_error_set_errstr2
                (" hfs_cat_file_lookup: thread for file (%" PRIuINUM ")",
                inum);
        }
        return 1;
    }

    /* read the thread record */
    if (hfs_cat_read_thread_record(hfs, off, &thread)) {
        tsk_error_set_errstr2(" hfs_cat_file_lookup: file (%" PRIuINUM ")",
            inum);
        return 1;
    }

    /* now look up the actual file/folder record */

    /* build key */
    memset((char *) &key, 0, sizeof(hfs_btree_key_cat));
    memcpy((char *) key.parent_cnid, (char *) thread.parent_cnid,
        sizeof(key.parent_cnid));
    memcpy((char *) &key.name, (char *) &thread.name, sizeof(key.name));

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_cat_file_lookup: Looking up file record (parent: %"
            PRIuINUM ")\n", (uint64_t) tsk_getu32(fs->endian,
                key.parent_cnid));

    /* look up the record */
    off = hfs_cat_get_record_offset(hfs, &key);
    if (off == 0) {
        if (tsk_error_get_errno() == 0) {
            tsk_error_set_errno(TSK_ERR_FS_INODE_NUM);
            tsk_error_set_errstr
                ("hfs_cat_file_lookup: Error finding record node %"
                PRIuINUM, inum);
        }
        else {
            tsk_error_set_errstr2(" hfs_cat_file_lookup: file (%" PRIuINUM
                ")", inum);
        }
        return 1;
    }

    /* read the record */
    if (hfs_cat_read_file_folder_record(hfs, off, &record)) {
        tsk_error_set_errstr2(" hfs_cat_file_lookup: file (%" PRIuINUM ")",
            inum);
        return 1;
    }

    /* these memcpy can be gotten rid of, really */
    if (tsk_getu16(fs->endian,
            record.file.std.rec_type) == HFS_FOLDER_RECORD) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_cat_file_lookup: found folder record valence %" PRIu32
                ", cnid %" PRIu32 "\n", tsk_getu32(fs->endian,
                    record.folder.std.valence), tsk_getu32(fs->endian,
                    record.folder.std.cnid));
        memcpy((char *) &entry->cat, (char *) &record, sizeof(hfs_folder));
    }
    else if (tsk_getu16(fs->endian,
            record.file.std.rec_type) == HFS_FILE_RECORD) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_cat_file_lookup: found file record cnid %" PRIu32
                "\n", tsk_getu32(fs->endian, record.file.std.cnid));
        memcpy((char *) &entry->cat, (char *) &record, sizeof(hfs_file));
    }
    /* other cases already caught by hfs_cat_read_file_folder_record */

    memcpy((char *) &entry->thread, (char *) &thread, sizeof(hfs_thread));

    entry->flags = TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED;
    entry->inum = inum;

    if (follow_hard_link) {
        unsigned char is_err;
        TSK_INUM_T target_cnid =
            hfs_follow_hard_link(hfs, &(entry->cat), &is_err);
        if (is_err > 1) {
            error_returned
                ("hfs_cat_file_lookup: error occurred while following a possible hard link for "
                "inum (cnid) =  %" PRIuINUM, inum);
            return 1;
        }
        if (target_cnid != inum) {
            uint8_t res =
                hfs_cat_file_lookup(hfs, target_cnid, entry, FALSE);
            if (res != 0) {
                error_returned
                    ("hfs_cat_file_lookup: error occurred while looking up the Catalog entry for "
                    "the target of inum (cnid) = %" PRIuINUM " target",
                    inum);
            }
            return 1;
        }

    }

    if (tsk_verbose)
        tsk_fprintf(stderr, "hfs_cat_file_lookup exiting\n");
    return 0;
}

Project: sleuthkit
File: hfs.c

hfs_cat_get_record_offset(HFS_INFO * hfs, const hfs_btree_key_cat * needle)
{
    HFS_CAT_GET_RECORD_OFFSET_DATA offset_data;
    offset_data.off = 0;
    offset_data.targ_key = needle;
    if (hfs_cat_traverse(hfs, hfs_cat_get_record_offset_cb, &offset_data)) {
        return 0;
    }
    return offset_data.off;
}

Project: sleuthkit

hfs_cat_get_record_offset_cb(HFS_INFO * hfs, int8_t level_type,
    const hfs_btree_key_cat * cur_key,
    TSK_OFF_T key_off, void *ptr)
{
    HFS_CAT_GET_RECORD_OFFSET_DATA *offset_data = (HFS_CAT_GET_RECORD_OFFSET_DATA *)ptr;
    const hfs_btree_key_cat *targ_key = offset_data->targ_key;

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_cat_get_record_offset_cb: %s node want: %" PRIu32
            " vs have: %" PRIu32 "\n",
            (level_type == HFS_BT_NODE_TYPE_IDX) ? "Index" : "Leaf",
            tsk_getu32(hfs->fs_info.endian, targ_key->parent_cnid),
            tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid));

    if (level_type == HFS_BT_NODE_TYPE_IDX) {
        int diff = hfs_cat_compare_keys(hfs, cur_key, targ_key);
        if (diff < 0)
            return HFS_BTREE_CB_IDX_LT;
        else
            return HFS_BTREE_CB_IDX_EQGT;
    }
    else {
        int diff = hfs_cat_compare_keys(hfs, cur_key, targ_key);

        if (diff < 0) {
            return HFS_BTREE_CB_LEAF_GO;
        }
        else if (diff == 0) {
            offset_data->off =
                key_off + 2 + tsk_getu16(hfs->fs_info.endian,
                cur_key->key_len);
        }
        return HFS_BTREE_CB_LEAF_STOP;
    }
}

Project: sleuthkit
File: hfs.c

hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
    hfs_file_folder * record)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
    ssize_t cnt;
    char rec_type[2];

    memset(record, 0, sizeof(hfs_file_folder));

    cnt = tsk_fs_attr_read(hfs->catalog_attr, off, rec_type, 2, 0);
    if (cnt != 2) {
        if (cnt >= 0) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_READ);
        }
        tsk_error_set_errstr2
            ("hfs_cat_read_file_folder_record: Error reading record type from catalog offset %"
            PRIuOFF " (header)", off);
        return 1;
    }

    if (tsk_getu16(fs->endian, rec_type) == HFS_FOLDER_RECORD) {
        cnt =
            tsk_fs_attr_read(hfs->catalog_attr, off, (char *) record,
            sizeof(hfs_folder), 0);
        if (cnt != sizeof(hfs_folder)) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2
                ("hfs_cat_read_file_folder_record: Error reading catalog offset %"
                PRIuOFF " (folder)", off);
            return 1;
        }
    }
    else if (tsk_getu16(fs->endian, rec_type) == HFS_FILE_RECORD) {
        cnt =
            tsk_fs_attr_read(hfs->catalog_attr, off, (char *) record,
            sizeof(hfs_file), 0);
        if (cnt != sizeof(hfs_file)) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2
                ("hfs_cat_read_file_folder_record: Error reading catalog offset %"
                PRIuOFF " (file)", off);
            return 1;
        }
    }
    else {
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
        tsk_error_set_errstr
            ("hfs_cat_read_file_folder_record: unexpected record type %"
            PRIu16, tsk_getu16(fs->endian, rec_type));
        return 1;
    }

    return 0;
}

Project: sleuthkit
File: hfs.c

hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
    hfs_thread * thread)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
    uint16_t uni_len;
    ssize_t cnt;

    memset(thread, 0, sizeof(hfs_thread));
    cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *) thread, 10, 0);
    if (cnt != 10) {
        if (cnt >= 0) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_READ);
        }
        tsk_error_set_errstr2
            ("hfs_cat_read_thread_record: Error reading catalog offset %"
            PRIuOFF " (header)", off);
        return 1;
    }

    if ((tsk_getu16(fs->endian, thread->rec_type) != HFS_FOLDER_THREAD)
        && (tsk_getu16(fs->endian, thread->rec_type) != HFS_FILE_THREAD)) {
        tsk_error_set_errno(TSK_ERR_FS_GENFS);
        tsk_error_set_errstr
            ("hfs_cat_read_thread_record: unexpected record type %" PRIu16,
            tsk_getu16(fs->endian, thread->rec_type));
        return 1;
    }

    uni_len = tsk_getu16(fs->endian, thread->name.length);

    if (uni_len > 255) {
        tsk_error_set_errno(TSK_ERR_FS_INODE_COR);
        tsk_error_set_errstr
            ("hfs_cat_read_thread_record: invalid string length (%" PRIu16
            ")", uni_len);
        return 1;
    }

    cnt =
        tsk_fs_attr_read(hfs->catalog_attr, off + 10,
        (char *) thread->name.unicode, uni_len * 2, 0);
    if (cnt != uni_len * 2) {
        if (cnt >= 0) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_READ);
        }
        tsk_error_set_errstr2
            ("hfs_cat_read_thread_record: Error reading catalog offset %"
            PRIuOFF " (name)", off + 10);
        return 1;
    }

    return 0;
}

Project: sleuthkit

hfs_checked_read_random(TSK_FS_INFO * fs, char *buf, size_t len,
    TSK_OFF_T offs)
{
    ssize_t r;

    r = tsk_fs_read(fs, offs, buf, len);
    if (r != (ssize_t) len) {
        if (r >= 0) {
            tsk_error_reset();
            tsk_error_set_errno(TSK_ERR_FS_READ);
        }
        return 1;
    }
    return 0;
}

Project: sleuthkit

hfs_close(TSK_FS_INFO * fs)
{
    HFS_INFO *hfs = (HFS_INFO *) fs;
    tsk_take_lock(&(hfs->metadata_dir_cache_lock));
    fs->tag = 0;

    free(hfs->fs);

    if (hfs->catalog_file) {
        tsk_fs_file_close(hfs->catalog_file);
        hfs->catalog_attr = NULL;
    }

    if (hfs->blockmap_file) {
        tsk_fs_file_close(hfs->blockmap_file);
        hfs->blockmap_attr = NULL;
    }

    if (hfs->meta_dir) {
        tsk_fs_dir_close(hfs->meta_dir);
        hfs->meta_dir = NULL;
    }

    if (hfs->dir_meta_dir) {
        tsk_fs_dir_close(hfs->dir_meta_dir);
        hfs->dir_meta_dir = NULL;
    }

    if (hfs->extents_file) {
        tsk_fs_file_close(hfs->extents_file);
        hfs->extents_file = NULL;
    }

    tsk_release_lock(&(hfs->metadata_dir_cache_lock));
    tsk_deinit_lock(&(hfs->metadata_dir_cache_lock));

    tsk_fs_free((TSK_FS_INFO *)hfs);
}

Project: sleuthkit
File: hfs.c

hfs_convert_2_unix_time(uint32_t hfsdate)
{
    if (hfsdate < NSEC_BTWN_1904_1970)
        return 0;
    return (uint32_t) (hfsdate - NSEC_BTWN_1904_1970);
}

Project: sleuthkit

static int hfs_decompress_lzvn_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen)
{
    if (len > 0 && rawBuf[0] != 0x06) {
        *uncLen = lzvn_decode_buffer(uncBuf, COMPRESSION_UNIT_SIZE, rawBuf, len);
        return 1;  // apparently this can't fail
    }
    else {
        return hfs_decompress_noncompressed_block(rawBuf, len, uncBuf, uncLen);
    }
}

Project: sleuthkit

static int hfs_decompress_noncompressed_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen) {
    if (tsk_verbose)
        tsk_fprintf(stderr,
           "%s: Copying an uncompressed compression unit\n", __func__);

    if ((len - 1) > COMPRESSION_UNIT_SIZE) {
        error_detected(TSK_ERR_FS_READ,
            "%s: uncompressed block length %u is longer "
            "than compression unit size %u", __func__, len - 1,
            COMPRESSION_UNIT_SIZE);
        return 0;
    }
    memcpy(uncBuf, rawBuf + 1, len - 1);
    *uncLen = len - 1;
    return 1;
}

Project: sleuthkit

static int hfs_decompress_zlib_attr(char* rawBuf, uint32_t rawSize, uint64_t uncSize, char** dstBuf, uint64_t* dstSize, int* dstBufFree)
{
    if ((rawBuf[0] & 0x0F) == 0x0F) {
        return hfs_decompress_noncompressed_attr(
            rawBuf, rawSize, uncSize, dstBuf, dstSize, dstBufFree);
    }
    else {
#ifdef HAVE_LIBZ
        char* uncBuf = NULL;
        uint64_t uLen;
        unsigned long bytesConsumed;
        int infResult;

        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "%s: Uncompressing (inflating) data.", __func__);

        uncBuf = (char *) tsk_malloc((size_t) uncSize + 100); // add some extra space
        if (uncBuf == NULL) {
            error_returned
                (" - %s, space for the uncompressed attr", __func__);
            return 0;
        }

        infResult = zlib_inflate(rawBuf, (uint64_t) rawSize,
                                 uncBuf, (uint64_t) (uncSize + 100),
                                 &uLen, &bytesConsumed);
        if (infResult != 0) {
            error_returned
                (" %s, zlib could not uncompress attr", __func__);
            free(uncBuf);
            return 0;
        }

        if (bytesConsumed != rawSize) {
            error_detected(TSK_ERR_FS_READ,
                " %s, decompressor did not consume the whole compressed data",
                __func__);
            free(uncBuf);
            return 0;
        }

        *dstBuf = uncBuf;
        *dstSize = uncSize;
        *dstBufFree = TRUE;
#else

        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "%s: ZLIB not available, so loading an empty default DATA attribute.\n", __func__);

        static uint8_t dummy[1];

        *dstBuf = dummy;
        *dstSize = 0;
        *dstBufFree = FALSE;
#endif
    }

    return 1;
}

Project: sleuthkit

static int hfs_decompress_zlib_block(char* rawBuf, uint32_t len, char* uncBuf, uint64_t* uncLen)
{
    if (len > 0 && (rawBuf[0] & 0x0F) != 0x0F) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                        "%s: Inflating the compression unit\n", __func__);

        unsigned long bytesConsumed;
        int infResult = zlib_inflate(rawBuf, (uint64_t) len,
            uncBuf, (uint64_t) COMPRESSION_UNIT_SIZE,
            uncLen, &bytesConsumed);
        if (infResult != 0) {
            error_returned
                  (" %s: zlib inflation (uncompression) failed",
                  __func__, infResult);
            return 0;
        }

        if (bytesConsumed != len) {
            error_detected(TSK_ERR_FS_READ,
                " %s, decompressor did not consume the whole compressed data",
                __func__);
            return 0;
        }

        return 1;
    }
    else {
        return hfs_decompress_noncompressed_block(rawBuf, len, uncBuf, uncLen);
    }
}

Project: sleuthkit

hfs_dinode_copy(HFS_INFO * a_hfs, const HFS_ENTRY * a_hfs_entry,
    TSK_FS_FILE * a_fs_file)
{

    const hfs_file_folder *a_entry =
        (hfs_file_folder *) & (a_hfs_entry->cat);
    const hfs_file_fold_std *std;
    TSK_FS_META *a_fs_meta = a_fs_file->meta;
    TSK_FS_INFO *fs;
    uint16_t hfsmode;
    TSK_INUM_T iStd;            // the inum (or CNID) that occurs in the standard file metadata

    if (a_entry == NULL) {
        error_detected(TSK_ERR_FS_ARG,
            "hfs_dinode_copy: a_entry = a_hfs_entry->cat is NULL");
        return 1;
    }

    fs = (TSK_FS_INFO *) & a_hfs->fs_info;


    iStd = tsk_getu32(fs->endian, a_entry->file.std.cnid);
    if (iStd != a_hfs_entry->inum) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "WARNING: hfs_dinode_copy:  HFS_ENTRY with conflicting values for inum (or cnid).\n");
    }

    if (a_fs_meta == NULL) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr("hfs_dinode_copy: a_fs_meta is NULL");
        return 1;
    }

    std = &(a_entry->file.std);

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_dinode_copy: called for file/folder %" PRIu32 "\n",
            tsk_getu32(fs->endian, std->cnid));

    if (a_fs_meta->content_len < HFS_FILE_CONTENT_LEN) {
        if ((a_fs_meta =
                tsk_fs_meta_realloc(a_fs_meta,
                    HFS_FILE_CONTENT_LEN)) == NULL) {
            return 1;
        }
    }
    a_fs_meta->attr_state = TSK_FS_META_ATTR_EMPTY;
    if (a_fs_meta->attr) {
        tsk_fs_attrlist_markunused(a_fs_meta->attr);
    }


    /*
     * Copy the file type specific stuff first
     */
    hfsmode = tsk_getu16(fs->endian, std->perm.mode);

    if (tsk_getu16(fs->endian, std->rec_type) == HFS_FOLDER_RECORD) {
        if ((hfsmode & HFS_IN_IFMT) == 0)
            a_fs_meta->type = TSK_FS_META_TYPE_DIR;
        a_fs_meta->size = 0;
        memset(a_fs_meta->content_ptr, 0, HFS_FILE_CONTENT_LEN);
    }
    else if (tsk_getu16(fs->endian, std->rec_type) == HFS_FILE_RECORD) {
        hfs_fork *fork;
        if ((hfsmode & HFS_IN_IFMT) == 0)
            a_fs_meta->type = TSK_FS_META_TYPE_REG;
        a_fs_meta->size =
            tsk_getu64(fs->endian, a_entry->file.data.logic_sz);

        fork = (hfs_fork *) a_fs_meta->content_ptr;
        memcpy(fork, &(a_entry->file.data), sizeof(hfs_fork));
        memcpy(&fork[1], &(a_entry->file.resource), sizeof(hfs_fork));
    }
    else {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_dinode_copy error: catalog entry is neither file nor folder\n");
        return 1;
    }

    /*
     * Copy the standard stuff.
     * Use default values (as defined in spec) if mode is not defined.
     */
    if ((hfsmode & HFS_IN_IFMT) == 0) {
        a_fs_meta->mode = 0;
        a_fs_meta->uid = 99;
        a_fs_meta->gid = 99;
    }
    else {
        a_fs_meta->mode = hfs_mode_to_tsk_mode(hfsmode);
        a_fs_meta->type = hfs_mode_to_tsk_meta_type(hfsmode);
        a_fs_meta->uid = tsk_getu32(fs->endian, std->perm.owner);
        a_fs_meta->gid = tsk_getu32(fs->endian, std->perm.group);
    }

    if (tsk_getu32(fs->endian, std->perm.special.nlink))
        a_fs_meta->nlink = tsk_getu32(fs->endian, std->perm.special.nlink);
    else
        a_fs_meta->nlink = 1;

    a_fs_meta->mtime =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, std->cmtime));
    a_fs_meta->atime =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, std->atime));
    a_fs_meta->crtime =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, std->crtime));
    a_fs_meta->ctime =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, std->amtime));
    a_fs_meta->time2.hfs.bkup_time =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, std->bkup_date));
    a_fs_meta->mtime_nano = a_fs_meta->atime_nano = a_fs_meta->ctime_nano =
        a_fs_meta->crtime_nano = 0;
    a_fs_meta->time2.hfs.bkup_time_nano = 0;

    a_fs_meta->addr = tsk_getu32(fs->endian, std->cnid);

    a_fs_meta->flags = TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED;

    if (std->perm.o_flags & HFS_PERM_OFLAG_COMPRESSED)
        a_fs_meta->flags |= TSK_FS_META_FLAG_COMP;


    if (a_fs_file->name != NULL) {
        a_fs_file->name->meta_addr = a_fs_meta->addr;
    }

    /* TODO @@@ could fill in name2 with this entry's name and parent inode
       from Catalog entry */

    /* set the link string (if the file is a link)
     * The size check is a sanity check so that we don't try to allocate
     * a huge amount of memory for a bad inode value
     */
    if ((a_fs_meta->type == TSK_FS_META_TYPE_LNK) &&
        (a_fs_meta->size >= 0) && (a_fs_meta->size < HFS_MAXPATHLEN)) {

        ssize_t bytes_read;

        a_fs_meta->link = tsk_malloc((size_t) a_fs_meta->size + 1);
        if (a_fs_meta->link == NULL)
            return 1;

        bytes_read = tsk_fs_file_read(a_fs_file, (TSK_OFF_T) 0,
            a_fs_meta->link, (size_t) a_fs_meta->size,
            TSK_FS_FILE_READ_FLAG_NONE);
        a_fs_meta->link[a_fs_meta->size] = '\0';

        if (bytes_read != a_fs_meta->size) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "hfs_dinode_copy: failed to read contents of symbolic link; "
                    "expected %u bytes but tsk_fs_file_read() returned %u\n",
                    a_fs_meta->size, bytes_read);
            free(a_fs_meta->link);
            a_fs_meta->link = NULL;
            return 1;
        }
    }

    return 0;
}

Project: sleuthkit

hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
    TSK_FS_ATTR * a_attr, unsigned char dataForkQ)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
    uint16_t nodesize;          /* size of nodes (all, regardless of the name) */
    uint32_t cur_node;          /* node id of the current node */
    char *node = NULL;
    uint8_t is_done;
    uint8_t desiredType;

    tsk_error_reset();

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_ext_find_extent_record_attr: Looking for extents for file %"
            PRIu32 " %s\n", cnid,
            dataForkQ ? "data fork" : "resource fork");

    if (!hfs->has_extents_file) {
        return 0;
    }

    desiredType =
        dataForkQ ? HFS_EXT_KEY_TYPE_DATA : HFS_EXT_KEY_TYPE_RSRC;

    if (hfs->extents_file == NULL) {
        ssize_t cnt;

        if ((hfs->extents_file =
                tsk_fs_file_open_meta(fs, NULL,
                    HFS_EXTENTS_FILE_ID)) == NULL) {
            return 1;
        }

        /* cache the data attribute */
        hfs->extents_attr =
            tsk_fs_attrlist_get(hfs->extents_file->meta->attr,
            TSK_FS_ATTR_TYPE_DEFAULT);
        if (!hfs->extents_attr) {
            tsk_error_errstr2_concat
                (" - Default Attribute not found in Extents File");
            return 1;
        }

        cnt = tsk_fs_attr_read(hfs->extents_attr, 14,
            (char *) &(hfs->extents_header),
            sizeof(hfs_btree_header_record), 0);
        if (cnt != sizeof(hfs_btree_header_record)) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2
                ("hfs_ext_find_extent_record_attr: Error reading header");
            return 1;
        }
    }

    nodesize = tsk_getu16(fs->endian, hfs->extents_header.nodesize);
    if ((node = (char *) tsk_malloc(nodesize)) == NULL) {
        return 1;
    }

    /* start at root node */
    cur_node = tsk_getu32(fs->endian, hfs->extents_header.rootNode);

    /* if the root node is zero, then the extents btree is empty */
    /* if no files have overflow extents, the Extents B-tree still
       exists on disk, but is an empty B-tree containing only
       the header node */
    if (cur_node == 0) {
        if (tsk_verbose)
            tsk_fprintf(stderr, "hfs_ext_find_extent_record: "
                "empty extents btree\n");
        free(node);
        return 0;
    }

    if (tsk_verbose)
        tsk_fprintf(stderr, "hfs_ext_find_extent_record: starting at "
            "root node %" PRIu32 "; nodesize = %"
            PRIu16 "\n", cur_node, nodesize);

    /* Recurse down to the needed leaf nodes and then go forward */
    is_done = 0;
    while (is_done == 0) {
        TSK_OFF_T cur_off;      /* start address of cur_node */
        uint16_t num_rec;       /* number of records in this node */
        ssize_t cnt;
        hfs_btree_node *node_desc;

        if (cur_node > tsk_getu32(fs->endian,
                hfs->extents_header.totalNodes)) {
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
            tsk_error_set_errstr
                ("hfs_ext_find_extent_record_attr: Node %d too large for file",
                cur_node);
            free(node);
            return 1;
        }

        cur_off = (TSK_OFF_T)cur_node * nodesize;
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_ext_find_extent_record: reading node %" PRIu32
                " at offset %" PRIuOFF "\n", cur_node, cur_off);

        cnt = tsk_fs_attr_read(hfs->extents_attr, cur_off,
            node, nodesize, 0);
        if (cnt != nodesize) {
            if (cnt >= 0) {
                tsk_error_reset();
                tsk_error_set_errno(TSK_ERR_FS_READ);
            }
            tsk_error_set_errstr2
                ("hfs_ext_find_extent_record_attr: Error reading node %d at offset %"
                PRIuOFF, cur_node, cur_off);
            free(node);
            return 1;
        }

        if (nodesize < sizeof(hfs_btree_node)) {
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
            tsk_error_set_errstr
                ("hfs_ext_find_extent_record_attr: Node size %d is too small to be valid", nodesize);
            free(node);
            return 1;
        }
        node_desc = (hfs_btree_node *) node;
        num_rec = tsk_getu16(fs->endian, node_desc->num_rec);

        if (num_rec == 0) {
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
            tsk_error_set_errstr
                ("hfs_ext_find_extent_record: zero records in node %"
                PRIu32, cur_node);
            free(node);
            return 1;
        }


        /* With an index node, find the record with the largest key that is smaller
         * to or equal to cnid */
        if (node_desc->type == HFS_BT_NODE_TYPE_IDX) {
            uint32_t next_node = 0;
            int rec;

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "hfs_ext_find_extent_record: Index node %" PRIu32
                    " @ %" PRIu64 " has %" PRIu16 " records\n", cur_node,
                    cur_off, num_rec);

            for (rec = 0; rec < num_rec; ++rec) {
                int cmp;
                size_t rec_off;
                hfs_btree_key_ext *key;

                rec_off =
                    tsk_getu16(fs->endian,
                    &node[nodesize - (rec + 1) * 2]);
                if (rec_off + sizeof(hfs_btree_key_ext) > nodesize) {
                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
                    tsk_error_set_errstr
                        ("hfs_ext_find_extent_record_attr: offset of record %d in index node %d too large (%d vs %"
                        PRIu16 ")", rec, cur_node, (int) rec_off,
                        nodesize);
                    free(node);
                    return 1;
                }
                key = (hfs_btree_key_ext *) & node[rec_off];

                cmp = hfs_ext_compare_keys(hfs, cnid, key);

                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_ext_find_extent_record: record %" PRIu16
                        " ; keylen %" PRIu16 " (FileId: %" PRIu32
                        ", ForkType: %" PRIu8 ", StartBlk: %" PRIu32
                        "); compare: %d\n", rec, tsk_getu16(fs->endian,
                            key->key_len), tsk_getu32(fs->endian,
                            key->file_id), key->fork_type,
                        tsk_getu32(fs->endian, key->start_block), cmp);

                /* save the info from this record unless it is bigger than cnid */
                if ((cmp <= 0) || (next_node == 0)) {
                    hfs_btree_index_record *idx_rec;
                    int keylen =
                        2 + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian,
                            key->key_len), &(hfs->extents_header));
                    if (rec_off + keylen > nodesize) {
                        tsk_error_set_errno(TSK_ERR_FS_GENFS);
                        tsk_error_set_errstr
                            ("hfs_ext_find_extent_record_attr: offset and keylenth of record %d in index node %d too large (%d vs %"
                            PRIu16 ")", rec, cur_node,
                            (int) rec_off + keylen, nodesize);
                        free(node);
                        return 1;
                    }
                    idx_rec =
                        (hfs_btree_index_record *) & node[rec_off +
                        keylen];
                    next_node = tsk_getu32(fs->endian, idx_rec->childNode);
                }

                if (cmp > 0) {
                    break;
                }
            }

            if (next_node == 0) {
                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_ext_find_extent_record_attr: did not find any keys for %d in index node %d",
                        cnid, cur_node);
                is_done = 1;
                break;
            }
            cur_node = next_node;
        }

        /* with a leaf, we process until we are past cnid.  We move right too if we can */
        else if (node_desc->type == HFS_BT_NODE_TYPE_LEAF) {
            int rec;

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "hfs_ext_find_extent_record: Leaf node %" PRIu32 " @ %"
                    PRIu64 " has %" PRIu16 " records\n", cur_node, cur_off,
                    num_rec);

            for (rec = 0; rec < num_rec; ++rec) {
                size_t rec_off;
                hfs_btree_key_ext *key;
                uint32_t rec_cnid;
                hfs_extents *extents;
                TSK_OFF_T ext_off = 0;
                int keylen;
                TSK_FS_ATTR_RUN *attr_run;

                rec_off =
                    tsk_getu16(fs->endian,
                    &node[nodesize - (rec + 1) * 2]);
                if (rec_off > nodesize) {
                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
                    tsk_error_set_errstr
                        ("hfs_ext_find_extent_record_attr: offset of record %d in leaf node %d too large (%d vs %"
                        PRIu16 ")", rec, cur_node, (int) rec_off,
                        nodesize);
                    free(node);
                    return 1;
                }
                key = (hfs_btree_key_ext *) & node[rec_off];

                if (tsk_verbose)
                    tsk_fprintf(stderr,
                        "hfs_ext_find_extent_record: record %" PRIu16
                        "; keylen %" PRIu16 " (%" PRIu32
                        ", %" PRIu8 ", %" PRIu32 ")\n", rec,
                        tsk_getu16(fs->endian, key->key_len),
                        tsk_getu32(fs->endian, key->file_id),
                        key->fork_type, tsk_getu32(fs->endian,
                            key->start_block));

                rec_cnid = tsk_getu32(fs->endian, key->file_id);


                if (rec_cnid < cnid) {
                    continue;
                }
                if (rec_cnid > cnid) {
                    is_done = 1;
                    break;
                }


                if (key->fork_type != desiredType) {
                    if (dataForkQ) {
                        is_done = 1;
                        break;
                    }
                    else
                        continue;
                }

                keylen = 2 + tsk_getu16(fs->endian, key->key_len);
                if (rec_off + keylen + sizeof(hfs_extents) > nodesize) {
                    tsk_error_set_errno(TSK_ERR_FS_GENFS);
                    tsk_error_set_errstr
                        ("hfs_ext_find_extent_record_attr: offset and keylenth of record %d in leaf node %d too large (%d vs %"
                        PRIu16 ")", rec, cur_node, (int) rec_off + keylen,
                        nodesize);
                    free(node);
                    return 1;
                }

                ext_off = tsk_getu32(fs->endian, key->start_block);

                extents = (hfs_extents *) & node[rec_off + keylen];

                attr_run =
                    hfs_extents_to_attr(fs, extents->extents, ext_off);
                if ((attr_run == NULL) && (tsk_error_get_errno() != 0)) {
                    tsk_error_errstr2_concat
                        (" - hfs_ext_find_extent_record_attr");
                    free(node);
                    return 1;
                }

                if (tsk_fs_attr_add_run(fs, a_attr, attr_run)) {
                    tsk_error_errstr2_concat
                        (" - hfs_ext_find_extent_record_attr");
                    free(node);
                    return 1;
                }
            }
            cur_node = tsk_getu32(fs->endian, node_desc->flink);
            if (cur_node == 0) {
                is_done = 1;
                break;
            }
        }
        else {
            tsk_error_set_errno(TSK_ERR_FS_GENFS);
            tsk_error_set_errstr("hfs_ext_find_extent_record: btree node %"
                PRIu32 " (%" PRIuOFF ") is neither index nor leaf (%" PRIu8
                ")", cur_node, cur_off, node_desc->type);
            free(node);
            return 1;
        }
    }
    free(node);
    return 0;
}

Project: sleuthkit

hfs_extents_to_attr(TSK_FS_INFO * a_fs, const hfs_ext_desc * a_extents,
    TSK_OFF_T a_start_off)
{
    TSK_FS_ATTR_RUN *head_run = NULL;
    TSK_FS_ATTR_RUN *prev_run = NULL;
    int i;
    TSK_OFF_T cur_off = a_start_off;

    tsk_error_reset();

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_extents_to_attr: Converting extents from offset %" PRIuOFF
            " to runlist\n", a_start_off);

    for (i = 0; i < 8; ++i) {
        TSK_FS_ATTR_RUN *cur_run;

        uint32_t addr = tsk_getu32(a_fs->endian, a_extents[i].start_blk);
        uint32_t len = tsk_getu32(a_fs->endian, a_extents[i].blk_cnt);

        if (tsk_verbose)
            tsk_fprintf(stderr,
                "hfs_extents_to_attr: run %i at addr %" PRIu32
                " with len %" PRIu32 "\n", i, addr, len);

        if ((addr == 0) && (len == 0)) {
            break;
        }

        if ((cur_run = tsk_fs_attr_run_alloc()) == NULL) {
            error_returned(" - hfs_extents_to_attr");
            return NULL;
        }

        cur_run->addr = addr;
        cur_run->len = len;
        cur_run->offset = cur_off;

        if (head_run == NULL)
            head_run = cur_run;
        if (prev_run != NULL)
            prev_run->next = cur_run;
        cur_off += cur_run->len;
        prev_run = cur_run;
    }

    return head_run;
}

Project: sleuthkit

hfs_file_read_compressed_attr(TSK_FS_FILE* fs_file,
                              uint8_t cmpType,
                              char* buffer,
                              uint32_t attributeLength,
                              uint64_t uncSize,
                              int (*decompress_attr)(char* rawBuf,
                                                     uint32_t rawSize,
                                                     uint64_t uncSize,
                                                     char** dstBuf,
                                                     uint64_t* dstSize,
                                                     int* dstBufFree))
{
    if (tsk_verbose)
        tsk_fprintf(stderr,
            "%s: Compressed data is inline in the attribute, will load this as the default DATA attribute.\n", __func__);

    if (attributeLength <= 16) {
        if (tsk_verbose)
            tsk_fprintf(stderr,
                "%s: WARNING, Compression Record of type %u is not followed by"
                " compressed data. No data will be loaded into the DATA"
                " attribute.\n", __func__, cmpType);

        return 1;
    }

    TSK_FS_ATTR *fs_attr_unc;

    if ((fs_attr_unc = tsk_fs_attrlist_getnew(
          fs_file->meta->attr, TSK_FS_ATTR_RES)) == NULL)
    {
        error_returned(" - %s, FS_ATTR for uncompressed data", __func__);
        return 0;
    }

    char* dstBuf;
    uint64_t dstSize;
    int dstBufFree = FALSE;

    if (!decompress_attr(buffer + 16, attributeLength - 16, uncSize,
                         &dstBuf, &dstSize, &dstBufFree)) {
        return 0;
    }

    if (dstSize != uncSize) {
        error_detected(TSK_ERR_FS_READ,
            " %s, actual uncompressed size not equal to the size in the compression record", __func__);
        goto on_error;
    }

    if (tsk_verbose)
       tsk_fprintf(stderr,
                   "%s: Loading decompressed data as default DATA attribute.",
                   __func__);

    if (tsk_fs_attr_set_str(fs_file,
                            fs_attr_unc, "DATA",
                            TSK_FS_ATTR_TYPE_HFS_DATA,
                            HFS_FS_ATTR_ID_DATA, dstBuf,
                            dstSize))
    {
        error_returned(" - %s", __func__);
        goto on_error;
    }

    if (dstBufFree) {
        free(dstBuf);
    }
    return 1;

on_error:
    if (dstBufFree) {
        free(dstBuf);
    }
    return 0;
}

Project: sleuthkit

hfs_file_read_compressed_rsrc(const TSK_FS_ATTR * a_fs_attr,
    TSK_OFF_T a_offset, char *a_buf, size_t a_len,
    int (*read_block_table)(const TSK_FS_ATTR *rAttr,
                            CMP_OFFSET_ENTRY** offsetTableOut,
                            uint32_t* tableSizeOut,
                            uint32_t* tableOffsetOut),
    int (*decompress_block)(char* rawBuf,
                            uint32_t len,
                            char* uncBuf,
                            uint64_t* uncLen))
{
    TSK_FS_FILE *fs_file;
    const TSK_FS_ATTR *rAttr;
    char *rawBuf = NULL;
    char *uncBuf = NULL;
    uint32_t offsetTableOffset;
    uint32_t offsetTableSize;         // Size of the offset table
    CMP_OFFSET_ENTRY *offsetTable = NULL;
    TSK_OFF_T indx;                // index for looping over the offset table
    TSK_OFF_T startUnit = 0;
    uint32_t startUnitOffset = 0;
    TSK_OFF_T endUnit = 0;
    uint64_t bytesCopied;

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "%s: called because this file is compressed, with data in the resource fork\n", __func__);

    if (a_len == 0)
        return 0;

    if (a_offset < 0) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: reading from file at a negative offset",
             __func__);
        return -1;
    }

    if (a_len > SIZE_MAX / 2) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: trying to read more than SIZE_MAX/2 is not supported.",
            __func__);
        return -1;
    }

    if ((a_fs_attr == NULL) || (a_fs_attr->fs_file == NULL)
        || (a_fs_attr->fs_file->meta == NULL)
        || (a_fs_attr->fs_file->fs_info == NULL)) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: NULL parameters passed", __func__);
        return -1;
    }

    if (!(a_fs_attr->flags & TSK_FS_ATTR_COMP)) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: called with non-special attribute: %x",
            __func__, a_fs_attr->flags);
        return -1;
    }

    if ((a_fs_attr->id != HFS_FS_ATTR_ID_DATA) ||
        (a_fs_attr->type != TSK_FS_ATTR_TYPE_HFS_DATA)) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: arg specified an attribute %u-%u that is not the data fork, "
            "Only the data fork can be compressed.", __func__,
            a_fs_attr->type, a_fs_attr->id);
        return -1;
    }

    /********  Open the Resource Fork ***********/
    fs_file = a_fs_attr->fs_file;

    rAttr =
        tsk_fs_file_attr_get_type(fs_file, TSK_FS_ATTR_TYPE_HFS_RSRC,
        HFS_FS_ATTR_ID_RSRC, TRUE);
    if (rAttr == NULL) {
        error_returned
            (" %s: could not get the attribute for the resource fork of the file", __func__);
        return -1;
    }

    if (!read_block_table(rAttr, &offsetTable, &offsetTableSize, &offsetTableOffset)) {
      return -1;
    }

    startUnit = a_offset / COMPRESSION_UNIT_SIZE;
    startUnitOffset = a_offset % COMPRESSION_UNIT_SIZE;
    endUnit = (a_offset + a_len - 1) / COMPRESSION_UNIT_SIZE;

    if (startUnit >= offsetTableSize || endUnit >= offsetTableSize) {
        error_detected(TSK_ERR_FS_ARG,
            "%s: range of bytes requested %lld - %lld falls past the "
            "end of the uncompressed stream %llu\n",
            __func__, a_offset, a_offset + a_len,
            offsetTable[offsetTableSize-1].offset +
            offsetTable[offsetTableSize-1].length);
        goto on_error;
    }

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "%s: reading compression units: %" PRIuOFF
            " to %" PRIuOFF "\n", __func__, startUnit, endUnit);
    bytesCopied = 0;

    /* Raw data can be COMPRESSION_UNIT_SIZE+1 if the zlib data is not
     * compressed and there is a 1-byte flag that indicates that
     * the data is not compressed. */
    rawBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE + 1);
    if (rawBuf == NULL) {
        error_returned
            (" %s: buffers for reading and uncompressing", __func__);
        goto on_error;
    }

    uncBuf = (char *) tsk_malloc(COMPRESSION_UNIT_SIZE);
    if (uncBuf == NULL) {
        error_returned
            (" %s: buffers for reading and uncompressing", __func__);
        goto on_error;
    }

    for (indx = startUnit; indx <= endUnit; ++indx) {
        uint64_t uncLen;
        char *uncBufPtr = uncBuf;
        size_t bytesToCopy;

        switch ((uncLen = read_and_decompress_block(
                    rAttr, rawBuf, uncBuf,
                    offsetTable, offsetTableSize, offsetTableOffset, (size_t)indx,
                    decompress_block)))
        {
        case -1:
            goto on_error;
        case  0:
            continue;
        default:
            break;
        }

        if (indx == startUnit) {
            uncLen -= startUnitOffset;
            uncBufPtr += startUnitOffset;
        }


        if (bytesCopied + uncLen < (uint64_t) a_len)    // cast OK because a_len > 0
            bytesToCopy = (size_t) uncLen;      // uncLen <= size of compression unit, which is small, so cast is OK
        else
            bytesToCopy = (size_t) (((uint64_t) a_len) - bytesCopied);  // diff <= compression unit size, so cast is OK

        memcpy(a_buf + bytesCopied, uncBufPtr, bytesToCopy);
        bytesCopied += bytesToCopy;
    }


    if (bytesCopied < a_len) {
        memset(a_buf + bytesCopied, 0, a_len - (size_t) bytesCopied);   // cast OK because diff must be < compression unit size
    }

    free(offsetTable);
    free(rawBuf);
    free(uncBuf);

    return (ssize_t) bytesCopied;       // cast OK, cannot be greater than a_len which cannot be greater than SIZE_MAX/2 (rounded down).

on_error:
    free(offsetTable);
    free(rawBuf);
    free(uncBuf);
    return -1;
}

Project: sleuthkit
File: hfs.c

static int hfs_file_read_lzvn_attr(TSK_FS_FILE* fs_file,
                            char* buffer,
                            uint32_t attributeLength,
                            uint64_t uncSize)
{
    return hfs_file_read_compressed_attr(
        fs_file, DECMPFS_TYPE_LZVN_ATTR,
        buffer, attributeLength, uncSize,
        hfs_decompress_lzvn_attr
    );
}

Project: sleuthkit
File: hfs.c

hfs_file_read_lzvn_rsrc(const TSK_FS_ATTR * a_fs_attr,
    TSK_OFF_T a_offset, char *a_buf, size_t a_len)
{
    return hfs_file_read_compressed_rsrc(
        a_fs_attr, a_offset, a_buf, a_len,
        hfs_read_lzvn_block_table,
        hfs_decompress_lzvn_block
    );
}

Project: sleuthkit
File: hfs.c

static int hfs_file_read_zlib_attr(TSK_FS_FILE* fs_file,
                            char* buffer,
                            uint32_t attributeLength,
                            uint64_t uncSize)
{
    return hfs_file_read_compressed_attr(
        fs_file, DECMPFS_TYPE_ZLIB_ATTR,
        buffer, attributeLength, uncSize,
        hfs_decompress_zlib_attr
    );
}

Project: sleuthkit
File: hfs.c

hfs_file_read_zlib_rsrc(const TSK_FS_ATTR * a_fs_attr,
    TSK_OFF_T a_offset, char *a_buf, size_t a_len)
{
    return hfs_file_read_compressed_rsrc(
        a_fs_attr, a_offset, a_buf, a_len,
        hfs_read_zlib_block_table,
        hfs_decompress_zlib_block
    );
}

Project: sleuthkit

hfs_follow_hard_link(HFS_INFO * hfs, hfs_file * cat,
    unsigned char *is_error)
{

    TSK_FS_INFO *fs = (TSK_FS_INFO *) hfs;
    TSK_INUM_T cnid;
    time_t crtime;
    uint32_t file_type;
    uint32_t file_creator;

    *is_error = 0;              // default, not an error

    if (cat == NULL) {
        error_detected(TSK_ERR_FS_ARG,
            "hfs_follow_hard_link: Pointer to Catalog entry (2nd arg) is null");
        return 0;
    }

    cnid = tsk_getu32(fs->endian, cat->std.cnid);

    if (cnid < HFS_FIRST_USER_CNID) {
        return cnid;
    }

    crtime =
        (time_t) hfs_convert_2_unix_time(tsk_getu32(fs->endian,
            cat->std.crtime));


    file_type = tsk_getu32(fs->endian, cat->std.u_info.file_type);
    file_creator = tsk_getu32(fs->endian, cat->std.u_info.file_cr);

    if (file_type == HFS_HARDLINK_FILE_TYPE
        && file_creator == HFS_HARDLINK_FILE_CREATOR) {

        if (hfs->meta_inum == 0)
            return cnid;

        if ((!hfs->has_root_crtime) && (!hfs->has_meta_dir_crtime)
            && (!hfs->has_meta_crtime)) {
            uint32_t linkNum =
                tsk_getu32(fs->endian, cat->std.perm.special.inum);
            *is_error = 1;
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "WARNING: hfs_follow_hard_link: File system creation times are not set. "
                    "Cannot test inode for hard link. File type and creator indicate that this"
                    " is a hard link (file), with LINK ID = %" PRIu32 "\n",
                    linkNum);
            return cnid;
        }

        if ((!hfs->has_root_crtime) || (!hfs->has_meta_crtime)) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "WARNING: hfs_follow_hard_link: Either the root folder or the"
                    " file metadata folder is not accessible.  Testing this potential hard link"
                    " may be impaired.\n");
        }

        if ((hfs->has_meta_crtime && (crtime == hfs->meta_crtime)) ||
            (hfs->has_meta_dir_crtime && (crtime == hfs->metadir_crtime))
            || (hfs->has_root_crtime && (crtime == hfs->root_crtime))) {
            uint32_t linkNum =
                tsk_getu32(fs->endian, cat->std.perm.special.inum);

            return linkNum;
        }
    }
    else if (file_type == HFS_LINKDIR_FILE_TYPE
        && file_creator == HFS_LINKDIR_FILE_CREATOR) {

        if (hfs->meta_dir_inum == 0)
            return cnid;

        if ((!hfs->has_root_crtime) && (!hfs->has_meta_dir_crtime)
            && (!hfs->has_meta_crtime)) {
            uint32_t linkNum =
                tsk_getu32(fs->endian, cat->std.perm.special.inum);
            *is_error = 1;

            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "WARNING: hfs_follow_hard_link: File system creation times are not set. "
                    "Cannot test inode for hard link. File type and creator indicate that this"
                    " is a hard link (directory), with LINK ID = %" PRIu32
                    "\n", linkNum);
            return cnid;
        }

        if ((!hfs->has_root_crtime) || (!hfs->has_meta_crtime)
            || (!hfs->has_meta_dir_crtime)) {
            if (tsk_verbose)
                tsk_fprintf(stderr,
                    "WARNING: hfs_follow_hard_link: Either the root folder or the"
                    " file metadata folder or the directory metatdata folder is"
                    " not accessible.  Testing this potential hard linked folder "
                    "may be impaired.\n");
        }

        if ((hfs->has_meta_crtime && (crtime == hfs->meta_crtime)) ||
            (hfs->has_meta_dir_crtime && (crtime == hfs->metadir_crtime))
            || (hfs->has_root_crtime && (crtime == hfs->root_crtime))) {
            uint32_t linkNum =
                tsk_getu32(fs->endian, cat->std.perm.special.inum);

            return linkNum;
        }
    }

    return cnid;
}

Project: sleuthkit
File: hfs.c

hfs_fscheck(TSK_FS_INFO * fs, FILE * hFile)
{
    tsk_error_reset();
    tsk_error_set_errno(TSK_ERR_FS_UNSUPFUNC);
    tsk_error_set_errstr("fscheck not implemented for HFS yet");
    return 1;
}

Project: sleuthkit

hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile)
{
    HFS_INFO *hfs = (HFS_INFO *) fs;
    hfs_plus_vh *sb = hfs->fs;
    time_t mac_time;
    TSK_INUM_T inode;
    char timeBuf[128];

    if (tsk_verbose)
        tsk_fprintf(stderr, "hfs_fstat: called\n");

    tsk_fprintf(hFile, "FILE SYSTEM INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    tsk_fprintf(hFile, "File System Type: ");
    if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFSPLUS)
        tsk_fprintf(hFile, "HFS+\n");
    else if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFSX)
        tsk_fprintf(hFile, "HFSX\n");
    else
        tsk_fprintf(hFile, "Unknown\n");

    tsk_fprintf(hFile, "File System Version: ");
    switch (tsk_getu16(fs->endian, hfs->fs->version)) {
    case 4:
        tsk_fprintf(hFile, "HFS+\n");
        break;
    case 5:
        tsk_fprintf(hFile, "HFSX\n");
        break;
    default:
        tsk_fprintf(hFile, "Unknown (%" PRIu16 ")\n",
            tsk_getu16(fs->endian, hfs->fs->version));
        break;
    }

    if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFSX) {
        tsk_fprintf(hFile, "Case Sensitive: %s\n",
            hfs->is_case_sensitive ? "yes" : "no");
    }

    if (hfs->hfs_wrapper_offset > 0) {
        tsk_fprintf(hFile,
            "File system is embedded in an HFS wrapper at offset %" PRIuOFF
            "\n", hfs->hfs_wrapper_offset);
    }

    tsk_fprintf(hFile, "\nVolume Name: ");
    if (print_inode_name(hFile, fs, HFS_ROOT_INUM))
        return 1;
    tsk_fprintf(hFile, "\n");

    tsk_fprintf(hFile, "Volume Identifier: %08" PRIx32 "%08" PRIx32 "\n",
        tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_ID1]),
        tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_ID2]));


    tsk_fprintf(hFile, "\nLast Mounted By: ");
    if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFS_VH_MVER_HFSPLUS)
        tsk_fprintf(hFile, "Mac OS X\n");
    else if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFS_VH_MVER_HFSJ)
        tsk_fprintf(hFile, "Mac OS X, Journaled\n");
    else if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFS_VH_MVER_FSK)
        tsk_fprintf(hFile, "failed journal replay\n");
    else if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFS_VH_MVER_FSCK)
        tsk_fprintf(hFile, "fsck_hfs\n");
    else if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFS_VH_MVER_OS89)
        tsk_fprintf(hFile, "Mac OS 8.1 - 9.2.2\n");
    else
        tsk_fprintf(hFile, "Unknown (%" PRIx32 "\n",
            tsk_getu32(fs->endian, sb->last_mnt_ver));

    /* State of the file system */
    if ((tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_UNMOUNTED)
        && (!(tsk_getu32(fs->endian,
                    hfs->fs->attr) & HFS_VH_ATTR_INCONSISTENT)))
        tsk_fprintf(hFile, "Volume Unmounted Properly\n");
    else
        tsk_fprintf(hFile, "Volume Unmounted Improperly\n");

    tsk_fprintf(hFile, "Mount Count: %" PRIu32 "\n",
        tsk_getu32(fs->endian, sb->write_cnt));


    mac_time =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, hfs->fs->cr_date));
    tsk_fprintf(hFile, "\nCreation Date: \t%s\n",
        tsk_fs_time_to_str(mktime(gmtime(&mac_time)), timeBuf));

    mac_time =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, hfs->fs->m_date));
    tsk_fprintf(hFile, "Last Written Date: \t%s\n",
        tsk_fs_time_to_str(mac_time, timeBuf));

    mac_time =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian,
            hfs->fs->bkup_date));
    tsk_fprintf(hFile, "Last Backup Date: \t%s\n",
        tsk_fs_time_to_str(mac_time, timeBuf));

    mac_time =
        hfs_convert_2_unix_time(tsk_getu32(fs->endian, hfs->fs->chk_date));
    tsk_fprintf(hFile, "Last Checked Date: \t%s\n",
        tsk_fs_time_to_str(mac_time, timeBuf));


    if (tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_SOFTWARE_LOCK)
        tsk_fprintf(hFile, "Software write protect enabled\n");

    /* Print journal information */
    if (tsk_getu32(fs->endian, sb->attr) & HFS_VH_ATTR_JOURNALED) {
        tsk_fprintf(hFile, "\nJournal Info Block: %" PRIu32 "\n",
            tsk_getu32(fs->endian, sb->jinfo_blk));
    }

    tsk_fprintf(hFile, "\nMETADATA INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n",
        fs->first_inum, fs->last_inum);

    inode = tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_BOOT]);
    tsk_fprintf(hFile, "Bootable Folder ID: %" PRIuINUM, inode);
    if (inode > 0)
        print_inode_file(hFile, fs, inode);
    tsk_fprintf(hFile, "\n");

    inode = tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_START]);
    tsk_fprintf(hFile, "Startup App ID: %" PRIuINUM, inode);
    if (inode > 0)
        print_inode_file(hFile, fs, inode);
    tsk_fprintf(hFile, "\n");

    inode = tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_OPEN]);
    tsk_fprintf(hFile, "Startup Open Folder ID: %" PRIuINUM, inode);
    if (inode > 0)
        print_inode_file(hFile, fs, inode);
    tsk_fprintf(hFile, "\n");

    inode = tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_BOOT9]);
    tsk_fprintf(hFile, "Mac OS 8/9 Blessed System Folder ID: %" PRIuINUM,
        inode);
    if (inode > 0)
        print_inode_file(hFile, fs, inode);
    tsk_fprintf(hFile, "\n");

    inode = tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_BOOTX]);
    tsk_fprintf(hFile, "Mac OS X Blessed System Folder ID: %" PRIuINUM,
        inode);
    if (inode > 0)
        print_inode_file(hFile, fs, inode);
    tsk_fprintf(hFile, "\n");

    tsk_fprintf(hFile, "Number of files: %" PRIu32 "\n",
        tsk_getu32(fs->endian, sb->file_cnt));
    tsk_fprintf(hFile, "Number of folders: %" PRIu32 "\n",
        tsk_getu32(fs->endian, sb->fldr_cnt));


    tsk_fprintf(hFile, "\nCONTENT INFORMATION\n");
    tsk_fprintf(hFile, "--------------------------------------------\n");

    tsk_fprintf(hFile, "Block Range: %" PRIuDADDR " - %" PRIuDADDR "\n",
        fs->first_block, fs->last_block);

    if (fs->last_block != fs->last_block_act)
        tsk_fprintf(hFile,
            "Total Range in Image: %" PRIuDADDR " - %" PRIuDADDR "\n",
            fs->first_block, fs->last_block_act);

    tsk_fprintf(hFile, "Allocation Block Size: %u\n", fs->block_size);

    tsk_fprintf(hFile, "Number of Free Blocks: %" PRIu32 "\n",
        tsk_getu32(fs->endian, sb->free_blks));

    if (tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_BADBLOCKS)
        tsk_fprintf(hFile, "Volume has bad blocks\n");

    return 0;
}

Project: sleuthkit

hfs_get_default_attr_type(const TSK_FS_FILE * a_file)
{
    TSK_INUM_T inum = a_file->meta->addr;
    if (inum == 3 ||            // Extents File
        inum == 4 ||            // Catalog File
        inum == 5 ||            // Bad Blocks File
        inum == 6 ||            // Block Map (Allocation File)
        inum == 7 ||            // Startup File
        inum == 8 ||            // Attributes File
        inum == 14 ||           // Not sure if these two will actually work.  I don't see
        inum == 15)             // any code to load the attrs of these files, if they exist.
        return TSK_FS_ATTR_TYPE_DEFAULT;
    if (a_file->meta->type == TSK_FS_META_TYPE_REG ||
        a_file->meta->type == TSK_FS_META_TYPE_LNK)
        return TSK_FS_ATTR_TYPE_HFS_DATA;

    return TSK_FS_ATTR_TYPE_DEFAULT;
}

Project: sleuthkit

hfs_get_idxkeylen(HFS_INFO * hfs, uint16_t keylen,
    const hfs_btree_header_record * header)
{
    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);

    if (tsk_getu32(fs->endian, header->attr) & HFS_BT_HEAD_ATTR_VARIDXKEYS)
        return keylen;
    else
        return tsk_getu16(fs->endian, header->maxKeyLen);
}

Project: sleuthkit
File: hfs.c

hfs_get_inode_name(TSK_FS_INFO * fs, TSK_INUM_T inum)
{
    HFS_INFO *hfs = (HFS_INFO *) fs;
    HFS_ENTRY entry;
    char *fn = NULL;

    if (hfs_cat_file_lookup(hfs, inum, &entry, FALSE))
        return NULL;

    fn = malloc(HFS_MAXNAMLEN + 1);
    if (fn == NULL)
        return NULL;

    if (hfs_UTF16toUTF8(fs, entry.thread.name.unicode,
            tsk_getu16(fs->endian, entry.thread.name.length), fn,
            HFS_MAXNAMLEN + 1, HFS_U16U8_FLAG_REPLACE_SLASH)) {
        free(fn);
        return NULL;
    }

    return fn;
}

Project: sleuthkit

hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
    TSK_INUM_T inum)
{
    HFS_INFO *hfs = (HFS_INFO *) fs;
    HFS_ENTRY entry;

    if (a_fs_file == NULL) {
        tsk_error_set_errno(TSK_ERR_FS_ARG);
        tsk_error_set_errstr("hfs_inode_lookup: fs_file is NULL");
        return 1;
    }

    if (a_fs_file->meta == NULL) {
        a_fs_file->meta = tsk_fs_meta_alloc(HFS_FILE_CONTENT_LEN);
    }

    if (a_fs_file->meta == NULL) {
        return 1;
    }
    else {
        tsk_fs_meta_reset(a_fs_file->meta);
    }

    if (tsk_verbose)
        tsk_fprintf(stderr, "hfs_inode_lookup: looking up %" PRIuINUM "\n",
            inum);


    /* First see if this is a special entry
     * the special ones have their metadata stored in the volume header */
    if (inum == HFS_EXTENTS_FILE_ID) {
        if (!hfs->has_extents_file) {
            error_detected(TSK_ERR_FS_INODE_NUM,
                "Extents File not present");
            return 1;
        }

        return hfs_make_extents(hfs, a_fs_file);
    }
    else if (inum == HFS_CATALOG_FILE_ID) {
        return hfs_make_catalog(hfs, a_fs_file);
    }
    else if (inum == HFS_BAD_BLOCK_FILE_ID) {
        if (!hfs->has_extents_file) {
            error_detected(TSK_ERR_FS_INODE_NUM,
                "BadBlocks File not present");
            return 1;
        }
        return hfs_make_badblockfile(hfs, a_fs_file);
    }
    else if (inum == HFS_ALLOCATION_FILE_ID) {
        return hfs_make_blockmap(hfs, a_fs_file);
    }
    else if (inum == HFS_STARTUP_FILE_ID) {
        if (!hfs->has_startup_file) {
            error_detected(TSK_ERR_FS_INODE_NUM,
                "Startup File not present");
            return 1;
        }
        return hfs_make_startfile(hfs, a_fs_file);
    }
    else if (inum == HFS_ATTRIBUTES_FILE_ID) {
        if (!hfs->has_attributes_file) {
            error_detected(TSK_ERR_FS_INODE_NUM,
                "Attributes File not present");
            return 1;
        }
        return hfs_make_attrfile(hfs, a_fs_file);
    }

    /* Lookup inode and store it in the HFS structure */
    if (hfs_cat_file_lookup(hfs, inum, &entry, TRUE)) {
        return 1;
    }

    /* Copy the structure in hfs to generic fs_inode */
    if (hfs_dinode_copy(hfs, &entry, a_fs_file)) {
        return 1;
    }

    /* If this is potentially a compressed file, its
     * actual size is unknown until we examine the
     * extended attributes */
    if ((a_fs_file->meta->size == 0) &&
        (a_fs_file->meta->type == TSK_FS_META_TYPE_REG) &&
        (a_fs_file->meta->attr_state != TSK_FS_META_ATTR_ERROR) &&
        ((a_fs_file->meta->attr_state != TSK_FS_META_ATTR_STUDIED) ||
            (a_fs_file->meta->attr == NULL))) {
        hfs_load_attrs(a_fs_file);
    }

    return 0;
}

Project: sleuthkit

hfs_istat(TSK_FS_INFO * fs, TSK_FS_ISTAT_FLAG_ENUM istat_flags, FILE * hFile, TSK_INUM_T inum,
    TSK_DADDR_T numblock, int32_t sec_skew)
{
    HFS_INFO *hfs = (HFS_INFO *) fs;
    TSK_FS_FILE *fs_file;
    char hfs_mode[12];
    HFS_PRINT_ADDR print;
    HFS_ENTRY entry;
    char timeBuf[128];
    const TSK_FS_ATTR *compressionAttr = NULL;
    RES_DESCRIPTOR *rd;         // descriptor of a resource

    tsk_error_reset();

    if (tsk_verbose)
        tsk_fprintf(stderr,
            "hfs_istat: inum: %" PRIuINUM " numblock: %" PRIu32 "\n",
            inum, numblock);

    if ((fs_file = tsk_fs_file_open_meta(fs, NULL, inum)) == NULL) {
        error_returned("hfs_istat: getting metadata for the file");
        return 1;
    }

    if (inum >= HFS_FIRST_USER_CNID) {
        int rslt;
        tsk_fprintf(hFile, "File Path: ");
        rslt = print_parent_path(hFile, fs, inum);
        if (rslt != 0)
            tsk_fprintf(hFile, " Error in printing path\n");
        else
            tsk_fprintf(hFile, "\n");
    }
    else {
        if (fs_file->meta->name2 != NULL)
            tsk_fprintf(hFile, "File Name: %s\n",
                fs_file->meta->name2->name);
    }

    tsk_fprintf(hFile, "Catalog Record: %" PRIuINUM "\n", inum);
    tsk_fprintf(hFile, "%sAllocated\n",
        (fs_file->meta->flags & TSK_FS_META_FLAG_UNALLOC) ? "Not " : "");

    tsk_fprintf(hFile, "Type:\t");
    if (fs_file->meta->type == TSK_FS_META_TYPE_REG)
        tsk_fprintf(hFile, "File\n");
    else if (TSK_FS_IS_DIR_META(fs_file->meta->type))
        tsk_fprintf(hFile, "Folder\n");
    else
        tsk_fprintf(hFile, "\n");

    tsk_fs_meta_make_ls(fs_file->meta, hfs_mode, sizeof(hfs_mode));
    tsk_fprintf(hFile, "Mode:\t%s\n", hfs_mode);
    tsk_fprintf(hFile, "Size:\t%" PRIuOFF "\n", fs_file->meta->size);

    if (fs_file->meta->link)
        tsk_fprintf(hFile, "Symbolic link to:\t%s\n", fs_file->meta->link);

    tsk_fprintf(hFile, "uid / gid: %" PRIuUID " / %" PRIuGID "\n",
        fs_file->meta->uid, fs_file->meta->gid);

    tsk_fprintf(hFile, "Link count:\t%d\n", fs_file->meta->nlink);

    if (hfs_cat_file_lookup(hfs, inum, &entry, TRUE) == 0) {
        hfs_uni_str *nm = &entry.thread.name;
        char name_buf[HFS_MAXNAMLEN + 1];
        TSK_INUM_T par_cnid;    // parent CNID

        tsk_fprintf(hFile, "\n");
        hfs_UTF16toUTF8(fs, nm->unicode, (int) tsk_getu16(fs->endian,
                nm->length), &name_buf[0], HFS_MAXNAMLEN + 1,
            HFS_U16U8_FLAG_REPLACE_SLASH | HFS_U16U8_FLAG_REPLACE_CONTROL);
        tsk_fprintf(hFile, "File Name: %s\n", name_buf);

        par_cnid = tsk_getu32(fs->endian, &(entry.thread.parent_cnid));
        if ((hfs->has_meta_dir_crtime && par_cnid == hfs->meta_dir_inum) ||
            (hfs->has_meta_crtime && par_cnid == hfs->meta_inum)) {
            int instr = strncmp(name_buf, "iNode", 5);
            int drstr = strncmp(name_buf, "dir_", 4);

            if (instr == 0 &&
                hfs->has_meta_crtime && par_cnid == hfs->meta_inum) {
                tsk_fprintf(hFile, "This is a hard link to a file\n");
            }
            else if (drstr == 0 &&
                hfs->has_meta_dir_crtime &&
                par_cnid == hfs->meta_dir_inum) {
                tsk_fprintf(hFile, "This is a hard link to a folder.\n");
            }
        }

        /* The cat.perm union contains file-type specific values.
         * Print them if they are relevant. */
        if ((fs_file->meta->type == TSK_FS_META_TYPE_CHR) ||
            (fs_file->meta->type == TSK_FS_META_TYPE_BLK)) {
            tsk_fprintf(hFile, "Device ID:\t%" PRIu32 "\n",
                tsk_getu32(fs->endian, entry.cat.std.perm.special.raw));
        }
        else if ((tsk_getu32(fs->endian,
                    entry.cat.std.u_info.file_type) ==
                HFS_HARDLINK_FILE_TYPE)
            && (tsk_getu32(fs->endian,
                    entry.cat.std.u_info.file_cr) ==
                HFS_HARDLINK_FILE_CREATOR)) {
            tsk_fprintf(hFile, "Hard link inode number\t %" PRIu32 "\n",
                tsk_getu32(fs->endian, entry.cat.std.perm.special.inum));
        }

        tsk_fprintf(hFile, "Admin flags: %" PRIu8,
            entry.cat.std.perm.a_flags);
        if (entry.cat.std.perm.a_flags != 0) {
            tsk_fprintf(hFile, " - ");
            if (entry.cat.std.perm.a_flags & HFS_PERM_AFLAG_ARCHIVED)
                tsk_fprintf(hFile, "archived ");
            if (entry.cat.std.perm.a_flags & HFS_PERM_AFLAG_IMMUTABLE)
                tsk_fprintf(hFile, "immutable ");
            if (entry.cat.std.perm.a_flags & HFS_PERM_AFLAG_APPEND)
                tsk_fprintf(hFile, "append-only ");
        }
        tsk_fprintf(hFile, "\n");

        tsk_fprintf(hFile, "Owner flags: %" PRIu8,
            entry.cat.std.perm.o_flags);
        if (entry.cat.std.perm.o_flags != 0) {
            tsk_fprintf(hFile, " - ");
            if (entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_NODUMP)
                tsk_fprintf(hFile, "no-dump ");
            if (entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_IMMUTABLE)
                tsk_fprintf(hFile, "immutable ");
            if (entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_APPEND)
                tsk_fprintf(hFile, "append-only ");
            if (entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_OPAQUE)
                tsk_fprintf(hFile, "opaque ");
            if (entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_COMPRESSED)
                tsk_fprintf(hFile, "compressed ");
        }
        tsk_fprintf(hFile, "\n");

        if (tsk_getu16(fs->endian,
                entry.cat.std.flags) & HFS_FILE_FLAG_LOCKED)
            tsk_fprintf(hFile, "Locked\n");
        if (tsk_getu16(fs->endian,
                entry.cat.std.flags) & HFS_FILE_FLAG_ATTR)
            tsk_fprintf(hFile, "Has extended attributes\n");
        if (tsk_getu16(fs->endian,
                entry.cat.std.flags) & HFS_FILE_FLAG_ACL)
            tsk_fprintf(hFile, "Has security data (ACLs)\n");

        if ( !TSK_FS_IS_DIR_META(fs_file->meta->type)){
            int windx;          // loop index
            tsk_fprintf(hFile,
                "File type:\t%04" PRIx32 "  ",
                tsk_getu32(fs->endian, entry.cat.std.u_info.file_type));

            for (windx = 0; windx < 4; ++windx) {
                uint8_t cu = entry.cat.std.u_info.file_type[windx];
                if (cu >= 32 && cu <= 126)
                    tsk_fprintf(hFile, "%c", (char) cu);
                else
                    tsk_fprintf(hFile, " ");
            }
            tsk_fprintf(hFile, "\n");
            tsk_fprintf(hFile,
                "File creator:\t%04" PRIx32 "  ",
                tsk_getu32(fs->endian, entry.cat.std.u_info.file_cr));
            for (windx = 0; windx < 4; ++windx) {
                uint8_t cu = entry.cat.std.u_info.file_cr[windx];
                if (cu >= 32 && cu <= 126)
                    tsk_fprintf(hFile, "%c", (char) cu);
                else
                    tsk_fprintf(hFile, " ");
            }
            tsk_fprintf(hFile, "\n");
        }                       // END if(not folder)

        if (tsk_getu16(fs->endian,
                entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_NAME_LOCKED)
            tsk_fprintf(hFile, "Name locked\n");
        if (tsk_getu16(fs->endian,
                entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_HAS_BUNDLE)
            tsk_fprintf(hFile, "Has bundle\n");
        if (tsk_getu16(fs->endian,
                entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_IS_INVISIBLE)
            tsk_fprintf(hFile, "Is invisible\n");
        if (tsk_getu16(fs->endian,
                entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_IS_ALIAS)
            tsk_fprintf(hFile, "Is alias\n");

        tsk_fprintf(hFile, "Text encoding:\t%" PRIx32 " = %s\n",
            tsk_getu32(fs->endian, entry.cat.std.text_enc),
            text_encoding_name(tsk_getu32(fs->endian,
                    entry.cat.std.text_enc)));

        if (tsk_getu16(fs->endian,
                entry.cat.std.rec_type) == HFS_FILE_RECORD) {
            tsk_fprintf(hFile, "Resource fork size:\t%" PRIu64 "\n",
                tsk_getu64(fs->endian, entry.cat.resource.logic_sz));
        }
    }

    if (sec_skew != 0) {
        tsk_fprintf(hFile, "\nAdjusted times:\n");
        if (fs_file->meta->mtime)
            fs_file->meta->mtime -= sec_skew;
        if (fs_file->meta->atime)
            fs_file->meta->atime -= sec_skew;
        if (fs_file->meta->ctime)
            fs_file->meta->ctime -= sec_skew;
        if (fs_file->meta->crtime)
            fs_file->meta->crtime -= sec_skew;
        if (fs_file->meta->time2.hfs.bkup_time)
            fs_file->meta->time2.hfs.bkup_time -= sec_skew;

        tsk_fprintf(hFile, "Created:\t%s\n",
            tsk_fs_time_to_str(fs_file->meta->crtime, timeBuf));
        tsk_fprintf(hFile, "Content Modified:\t%s\n",
            tsk_fs_time_to_str(fs_file->meta->mtime, timeBuf));
        tsk_fprintf(hFile, "Attributes Modified:\t%s\n",
            tsk_fs_time_to_str(fs_file->meta->ctime, timeBuf));
        tsk_fprintf(hFile, "Accessed:\t%s\n",
            tsk_fs_time_to_str(fs_file->meta->atime, timeBuf));
        tsk_fprintf(hFile, "Backed Up:\t%s\n",
            tsk_fs_time_to_str(fs_file->meta->time2.hfs.bkup_time,
                timeBuf));

        if (fs_file->meta->mtime)
            fs_file->meta->mtime += sec_skew;
        if (fs_file->meta->atime)
            fs_file->meta->atime += sec_skew;
        if (fs_file->meta->ctime)
            fs_file->meta->ctime += sec_skew;
        if (fs_file->meta->crtime)
            fs_file->meta->crtime += sec_skew;
        if (fs_file->meta->time2.hfs.bkup_time)
            fs_file->meta->time2.hfs.bkup_time += sec_skew;

        tsk_fprintf(hFile, "\nOriginal times:\n");
    }
    else {
        tsk_fprintf(hFile, "\nTimes:\n");
    }

    tsk_fprintf(hFile, "Created:\t%s\n",
        tsk_fs_time_to_str(fs_file->meta->crtime, timeBuf));
    tsk_fprintf(hFile, "Content Modified:\t%s\n",
        tsk_fs_time_to_str(fs_file->meta->mtime, timeBuf));
    tsk_fprintf(hFile, "Attributes Modified:\t%s\n",
        tsk_fs_time_to_str(fs_file->meta->ctime, timeBuf));
    tsk_fprintf(hFile, "Accessed:\t%s\n",
        tsk_fs_time_to_str(fs_file->meta->atime, timeBuf));
    tsk_fprintf(hFile, "Backed Up:\t%s\n",
        tsk_fs_time_to_str(fs_file->meta->time2.hfs.bkup_time, timeBuf));

    if (tsk_getu16(fs->endian, entry.cat.std.rec_type) == HFS_FILE_RECORD) {
        if (!(entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_COMPRESSED)) {

            if (!(istat_flags & TSK_FS_ISTAT_RUNLIST)) {
                tsk_fprintf(hFile, "\nData Fork Blocks:\n");
                print.idx = 0;
                print.hFile = hFile;
                print.accumulating = FALSE;
                print.startBlock = 0;
                print.blockCount = 0;

                if (tsk_fs_file_walk_type(fs_file,
                    TSK_FS_ATTR_TYPE_HFS_DATA, HFS_FS_ATTR_ID_DATA,
                    (TSK_FS_FILE_WALK_FLAG_AONLY |
                        TSK_FS_FILE_WALK_FLAG_SLACK), print_addr_act,
                        (void *)&print)) {
                    tsk_fprintf(hFile, "\nError reading file data fork\n");
                    tsk_error_print(hFile);
                    tsk_error_reset();
                }
                else {
                    output_print_addr(&print);
                    if (print.idx != 0)
                        tsk_fprintf(hFile, "\n");
                }
            }
        }

        if (tsk_getu64(fs->endian, entry.cat.resource.logic_sz) > 0) {

            if (! (istat_flags & TSK_FS_ISTAT_RUNLIST)) {
                tsk_fprintf(hFile, "\nResource Fork Blocks:\n");

                print.idx = 0;
                print.hFile = hFile;
                print.accumulating = FALSE;
                print.startBlock = 0;
                print.blockCount = 0;

                if (tsk_fs_file_walk_type(fs_file,
                    TSK_FS_ATTR_TYPE_HFS_RSRC, HFS_FS_ATTR_ID_RSRC,
                    (TSK_FS_FILE_WALK_FLAG_AONLY |
                        TSK_FS_FILE_WALK_FLAG_SLACK), print_addr_act,
                        (void *)&print)) {
                    tsk_fprintf(hFile, "\nError reading file resource fork\n");
                    tsk_error_print(hFile);
                    tsk_error_reset();
                }
                else {
                    output_print_addr(&print);
                    if (print.idx != 0)
                        tsk_fprintf(hFile, "\n");
                }
            }
        }
    }

    (void) tsk_fs_file_attr_get(fs_file);

    /* Print all of the attributes */
    tsk_fprintf(hFile, "\nAttributes: \n");
    if (fs_file->meta->attr) {
        int cnt, i;

        cnt = tsk_fs_file_attr_getsize(fs_file);
        for (i = 0; i < cnt; ++i) {
            const char *type;   // type of the attribute as a string
            const TSK_FS_ATTR *fs_attr =
                tsk_fs_file_attr_get_idx(fs_file, i);
            if (!fs_attr)
                continue;

            type = hfs_attrTypeName((uint32_t) fs_attr->type);


            /* print the layout if it is non-resident and not "special" */
            if (fs_attr->flags & TSK_FS_ATTR_NONRES) {

                tsk_fprintf(hFile,
                    "Type: %s (%" PRIu32 "-%" PRIu16
                    ")   Name: %s   Non-Resident%s%s%s   size: %"
                    PRIuOFF "  init_size: %" PRIuOFF "\n", type,
                    fs_attr->type, fs_attr->id,
                    (fs_attr->name) ? fs_attr->name : "N/A",
                    (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted" :
                    "",
                    (fs_attr->flags & TSK_FS_ATTR_COMP) ? ", Compressed" :
                    "",
                    (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" :
                    "", fs_attr->size, fs_attr->nrd.initsize);

                if (istat_flags & TSK_FS_ISTAT_RUNLIST) {
                    if (tsk_fs_attr_print(fs_attr, hFile)) {
                        tsk_fprintf(hFile, "\nError creating run lists\n");
                        tsk_error_print(hFile);
                        tsk_error_reset();
                    }
                }
            }                   // END:  non-resident attribute case
            else {
                tsk_fprintf(hFile,
                    "Type: %s (%" PRIu32 "-%" PRIu16
                    ")   Name: %s   Resident%s%s%s   size: %"
                    PRIuOFF "\n",
                    type,
                    fs_attr->type,
                    fs_attr->id,
                    (fs_attr->name) ? fs_attr->name : "N/A",
                    (fs_attr->flags & TSK_FS_ATTR_ENC) ? ", Encrypted" :
                    "",
                    (fs_attr->flags & TSK_FS_ATTR_COMP) ? ", Compressed" :
                    "",
                    (fs_attr->flags & TSK_FS_ATTR_SPARSE) ? ", Sparse" :
                    "", fs_attr->size);
                if (fs_attr->type == TSK_FS_ATTR_TYPE_HFS_COMP_REC) {
                    if (compressionAttr == NULL) {
                        compressionAttr = fs_attr;
                    }
                    else {
                        error_detected(TSK_ERR_FS_CORRUPT,
                            "hfs_istat: more than one compression attribute");
                        return 1;
                    }
                }
            }                   // END: else (RESIDENT attribute case)
        }                       // END:  for(;;)  loop over attributes
    }                           // END:  if(fs_file->meta->attr is non-NULL)

    if ((entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_COMPRESSED)
        && (compressionAttr == NULL))
        tsk_fprintf(hFile,
            "WARNING: Compression Flag is set, but there"
            " is no compression record for this file.\n");
    if (((entry.cat.std.perm.o_flags & HFS_PERM_OFLAG_COMPRESSED) == 0)
        && (compressionAttr != NULL))
        tsk_fprintf(hFile,
            "WARNING: Compression Flag is NOT set, but there"
            " is a compression record for this file.\n");

    if (compressionAttr != NULL) {
        const TSK_FS_ATTR *fs_attr = compressionAttr;
        ssize_t attrReadResult;
        DECMPFS_DISK_HEADER *cmph;
        uint32_t cmpType;
        uint64_t uncSize;
        uint64_t cmpSize = 0;

        char *aBuf = (char *) tsk_malloc((size_t) fs_attr->size);
        if (aBuf == NULL) {
            error_returned("hfs_istat: space for a compression attribute");
            return 1;
        }
        attrReadResult = tsk_fs_attr_read(fs_attr, (TSK_OFF_T) 0,
            aBuf, (size_t) fs_attr->size,
            (TSK_FS_FILE_READ_FLAG_ENUM) 0x00);
        if (attrReadResult == -1) {
            error_returned("hfs_istat: reading the compression attribute");
            free(aBuf);
            return 1;
        }
        else if (attrReadResult < fs_attr->size) {
            error_detected(TSK_ERR_FS_READ,
                "hfs_istat: could not read the whole compression attribute");
            free(aBuf);
            return 1;
        }
        cmph = (DECMPFS_DISK_HEADER *) aBuf;
        cmpType = tsk_getu32(TSK_LIT_ENDIAN, cmph->compression_type);
        uncSize = tsk_getu64(TSK_LIT_ENDIAN, cmph->uncompressed_size);

        tsk_fprintf(hFile, "\nCompressed File:\n");
        tsk_fprintf(hFile, "    Uncompressed size: %llu\n", uncSize);

        switch (cmpType) {
        case DECMPFS_TYPE_ZLIB_ATTR:
            {
                uint32_t off = (cmph->attr_bytes[0] & 0x0F) == 0x0F ? 17 : 16;
                cmpSize = fs_attr->size - off;

                tsk_fprintf(hFile,
                    "    Data follows compression record in the CMPF attribute\n"
                    "    %" PRIu64 " bytes of data at offset %u, %s compressed\n",
                    cmpSize, off, off == 16 ? "zlib" : "not");
            }
            break;

        case DECMPFS_TYPE_LZVN_ATTR:
            {
                uint32_t off = cmph->attr_bytes[0] == 0x06 ? 17 : 16;
                cmpSize = fs_attr->size - off;

                tsk_fprintf(hFile,
                    "    Data follows compression record in the CMPF attribute\n"
                    "    %" PRIu64 " bytes of data at offset %u, %s compressed\n",
                    cmpSize, off, off == 16 ? "lzvn" : "not");
            }
            break;

        case DECMPFS_TYPE_ZLIB_RSRC:
            tsk_fprintf(hFile,
                "    Data is zlib compressed in the resource fork\n");
            break;

        case DECMPFS_TYPE_LZVN_RSRC:
            tsk_fprintf(hFile,
                "    Data is lzvn compressed in the resource fork\n");
            break;

        default:
            tsk_fprintf(hFile, "    Compression type is %u: UNKNOWN\n",
                cmpType);
        }

        free(aBuf);

        if ((cmpType == DECMPFS_TYPE_ZLIB_RSRC ||
             cmpType == DECMPFS_TYPE_LZVN_RSRC)
            && (tsk_getu64(fs->endian, entry.cat.resource.logic_sz) == 0))
            tsk_fprintf(hFile,
                "WARNING: Compression record indicates compressed data"
                " in the RSRC Fork, but that fork is empty.\n");
    }

    rd = hfs_parse_resource_fork(fs_file);

    if (rd != NULL) {
        tsk_fprintf(hFile, "\nResources:\n");
        while (rd) {
            tsk_fprintf(hFile,
                "  Type: %s \tID: %-5u \tOffset: %-5u \tSize: %-5u \tName: %s\n",
                rd->type, rd->id, rd->offset, rd->length, rd->name);
            rd = rd->next;
        }
    }
    free_res_descriptor(rd);

    tsk_fs_file_close(fs_file);
    return 0;
}