diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c index 6d0f3419144fcf362bc8ff4e1c154ca1d398fd99..a8fa3f4705d17760327910f7d89602aae76da93e 100644 --- a/tsk3/fs/hfs.c +++ b/tsk3/fs/hfs.c @@ -139,7 +139,7 @@ cnid_to_array(uint32_t cnid, uint8_t array[4]) */ static int hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, - hfs_btree_key_ext * key) + const hfs_btree_key_ext * key) { TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); uint32_t key_cnid; @@ -152,9 +152,8 @@ hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, /* referring to the same cnids */ - /* we are always looking for the data fork (0); - a nonzero fork (e.g., the resource fork 0xff) is higher */ - if (key->fork_type[0] != 0) + /* we are always looking for the data fork */ + if (key->fork_type != HFS_EXT_KEY_TYPE_DATA) return 1; /* we are always looking for a start_block of zero @@ -192,69 +191,6 @@ hfs_get_idxkeylen(HFS_INFO * hfs, uint16_t keylen, return tsk_getu16(fs->endian, header->maxKeyLen); } -#if 0 -/** \internal - * Process a B-Tree node record and return the record contents and the - * offset of the data content in the record. - * Note that this neither detects nor correctly handles odd-length keys - * or 8-bit keys (neither should be present in HFS+). - * @param hfs File system being analyzed - * @param header Header of B-Tree record is in. If NULL, then - * only the keylength in the record is used (i.e. flag settings ignored). - * @param rec_off Byte offset in disk where record starts - * @param [out] a_buf Pointer to buffer to store record in (or NULL). - * Must be at least 2 bytes long if it is not NULL. - * @param a_buf_len Length of buf (amount of record to read) - * @param clear If 1, clear the key value before reading into it. - * @returns Offset of data content in record or 0 on error - */ -TSK_OFF_T -hfs_read_key(HFS_INFO * hfs, hfs_btree_header_record * header, - TSK_OFF_T rec_off, char *a_buf, int a_buf_len, uint8_t clear) -{ - TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); - char buf[2]; - char *dest = a_buf ? a_buf : buf; - uint16_t keylen; - - tsk_error_reset(); - - if (a_buf && clear) /* zero a_buf */ - memset(a_buf + 2, 0, a_buf_len - 2); - - // get the key length as reported in the record - if (hfs_checked_read_random(fs, dest, 2, rec_off)) /* read size */ - return 0; - - keylen = tsk_getu16(fs->endian, dest); - // use the header to figure out if we should be ignoring this length or not - if (header) - keylen = hfs_get_idxkeylen(hfs, keylen, header); - - if ((header && (keylen > tsk_getu16(fs->endian, header->maxKeyLen))) - || (!header && (keylen > 516))) { /* sanity check key length */ - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_read_key: key length out of range (%" PRIu16 ")", keylen); - return 0; - } - - // read the key and other data into the buffer if they asked for it - if (a_buf) { /* read key */ - if (hfs_checked_read_random(fs, a_buf + 2, - (keylen + 2 <= a_buf_len) ? - keylen : a_buf_len - 2, rec_off + 2)) - return 0; - } - - if (tsk_verbose) - tsk_fprintf(stderr, - "hfs_read_key: read key of length %" PRIu16 "\n", keylen); - - return rec_off + 2 + keylen; /* return record data address */ -} -#endif - /** * Convert the extents runs to TSK_FS_ATTR_RUN runs. @@ -406,8 +342,24 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, ssize_t cnt; hfs_btree_node *node_desc; + // sanity check + if (cur_node > tsk_getu32(fs->endian, + hfs->extents_header.totalNodes)) { + tsk_errno = TSK_ERR_FS_GENFS; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_ext_find_extent_record_attr: Node %d too large for file", + cur_node); + free(node); + return 1; + } + // read the current node cur_off = 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) { @@ -426,11 +378,6 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, node_desc = (hfs_btree_node *) node; num_rec = tsk_getu16(fs->endian, node_desc->num_rec); - if (tsk_verbose) - tsk_fprintf(stderr, "hfs_ext_find_extent_record: node %" PRIu32 - " @ %" PRIu64 " has %" PRIu16 " records\n", - cur_node, cur_off, num_rec); - if (num_rec == 0) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, @@ -447,6 +394,12 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, 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; @@ -471,18 +424,19 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, if (tsk_verbose) tsk_fprintf(stderr, "hfs_ext_find_extent_record: record %" PRIu16 - " ; keylen %" PRIu16 " (%" PRIu32 - ", %" PRIu8 ", %" PRIu32 "); compare: %d\n", rec, - tsk_getu16(fs->endian, key->key_len), - tsk_getu32(fs->endian, key->file_id), - key->fork_type[0], tsk_getu32(fs->endian, - key->start_block), cmp); + " ; 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 = - hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, - key->key_len) + 2, &(hfs->extents_header)); + 2 + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, + key->key_len), &(hfs->extents_header)); if (rec_off + keylen > nodesize) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, @@ -492,11 +446,14 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, free(node); return 1; } - next_node = - tsk_getu32(fs->endian, &node[rec_off + keylen]); + idx_rec = + (hfs_btree_index_record *) & node[rec_off + + keylen]; + next_node = tsk_getu32(fs->endian, idx_rec->childNode); } - else { - // we are bigger than cnid, so move on to the next node + + // we are bigger than cnid, so move on to the next node + if (cmp > 0) { break; } } @@ -517,6 +474,12 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, 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; @@ -547,7 +510,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, ", %" PRIu8 ", %" PRIu32 ")\n", rec, tsk_getu16(fs->endian, key->key_len), tsk_getu32(fs->endian, key->file_id), - key->fork_type[0], tsk_getu32(fs->endian, + key->fork_type, tsk_getu32(fs->endian, key->start_block)); rec_cnid = tsk_getu32(fs->endian, key->file_id); @@ -556,17 +519,18 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, if (rec_cnid < cnid) { continue; } - else if ((rec_cnid > cnid) || (key->fork_type[0] != 0)) { + else if ((rec_cnid > cnid) + || (key->fork_type != HFS_EXT_KEY_TYPE_DATA)) { is_done = 1; break; } - keylen = tsk_getu16(fs->endian, key->key_len); - if (rec_off + 2 + keylen > nodesize) { + keylen = 2 + tsk_getu16(fs->endian, key->key_len); + if (rec_off + keylen > nodesize) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, "hfs_ext_find_extent_record_attr: offset and keylenth of record %d in leaf node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off + 2 + keylen, + PRIu16 ")", rec, cur_node, rec_off + keylen, nodesize); free(node); return 1; @@ -576,7 +540,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, ext_off = tsk_getu32(fs->endian, key->start_block); // convert the extents to the TSK format - extents = (hfs_extents *) & node[rec_off + 2 + keylen]; + extents = (hfs_extents *) & node[rec_off + keylen]; attr_run = hfs_extents_to_attr(fs, extents->extents, ext_off); @@ -625,8 +589,8 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, * @returns -1 if key1 is smaller, 0 if equal, and 1 if key1 is larger */ int -hfs_cat_compare_keys(HFS_INFO * hfs, hfs_btree_key_cat * key1, - hfs_btree_key_cat * key2) +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; @@ -643,18 +607,13 @@ hfs_cat_compare_keys(HFS_INFO * hfs, hfs_btree_key_cat * key1, } - - -/** \internal -* Find the byte offset (from the start of the catalog file) to a record -* in the catalog file. -* @param hfs File System being analyzed -* @param needle Key to search for -* @returns Byte offset or 0 on error. 0 is also returned if catalog -* record was not found. Check tsk_errno to determine if error occured. -*/ -static TSK_OFF_T -hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) +/** + * @param targ_data can be null + * @returns 1 on error + */ +uint8_t +hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data, + TSK_HFS_BTREE_CB a_cb, void *ptr) { TSK_FS_INFO *fs = &(hfs->fs_info); uint32_t cur_node; /* node id of the current node */ @@ -667,7 +626,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) nodesize = tsk_getu16(fs->endian, hfs->catalog_header.nodesize); if ((node = (char *) tsk_malloc(nodesize)) == NULL) - return 0; + return 1; /* start at root node */ cur_node = tsk_getu32(fs->endian, hfs->catalog_header.rootNode); @@ -678,14 +637,14 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) the header node */ if (cur_node == 0) { if (tsk_verbose) - tsk_fprintf(stderr, "hfs_cat_get_record_offset: " + tsk_fprintf(stderr, "hfs_cat_traverse: " "empty extents btree\n"); free(node); - return 0; + return 1; } if (tsk_verbose) - tsk_fprintf(stderr, "hfs_cat_get_record_offset: starting at " + tsk_fprintf(stderr, "hfs_cat_traverse: starting at " "root node %" PRIu32 "; nodesize = %" PRIu16 "\n", cur_node, nodesize); @@ -697,6 +656,16 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) ssize_t cnt; hfs_btree_node *node_desc; + // sanity check + if (cur_node > tsk_getu32(fs->endian, + hfs->catalog_header.totalNodes)) { + tsk_errno = TSK_ERR_FS_GENFS; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_cat_traverse: Node %d too large for file", cur_node); + free(node); + return 1; + } + // read the current node cur_off = cur_node * nodesize; cnt = tsk_fs_attr_read(hfs->catalog_attr, cur_off, @@ -707,10 +676,10 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: Error reading node %d at offset %" + "hfs_cat_traverse: Error reading node %d at offset %" PRIuOFF, cur_node, cur_off); free(node); - return 0; + return 1; } // process the header / descriptor @@ -718,17 +687,17 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) num_rec = tsk_getu16(fs->endian, node_desc->num_rec); if (tsk_verbose) - tsk_fprintf(stderr, "hfs_cat_get_record_offset: node %" PRIu32 + tsk_fprintf(stderr, "hfs_cat_traverse: node %" PRIu32 " @ %" PRIu64 " has %" PRIu16 " records\n", cur_node, cur_off, num_rec); if (num_rec == 0) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: zero records in node %" + "hfs_cat_traverse: zero records in node %" PRIu32, cur_node); free(node); - return 0; + return 1; } /* With an index node, find the record with the largest key that is smaller @@ -740,6 +709,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) for (rec = 0; rec < num_rec; rec++) { size_t rec_off; hfs_btree_key_cat *key; + uint8_t retval; // get the record offset in the node rec_off = @@ -748,7 +718,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) if (rec_off > nodesize) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: offset of record %d in index node %d too large (%zu vs %" + "hfs_cat_traverse: offset of record %d in index node %d too large (%zu vs %" PRIu16 ")", rec, cur_node, rec_off, nodesize); free(node); return 1; @@ -758,32 +728,44 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) /* if (tsk_verbose) tsk_fprintf(stderr, - "hfs_cat_get_record_offset: record %" PRIu16 + "hfs_cat_traverse: record %" PRIu16 " ; keylen %" PRIu16 " (%" PRIu32 ")\n", rec, tsk_getu16(fs->endian, key->key_len), tsk_getu32(fs->endian, key->parent_cnid)); */ - /* save the info from this record unless it is bigger than cnid */ - if ((hfs_cat_compare_keys(hfs, key, needle) <= 0) - || (next_node == 0)) { + /* save the info from this record unless it is too big */ + retval = + a_cb(hfs, HFS_BT_NODE_TYPE_IDX, targ_data, key, + cur_off + rec_off, ptr); + if (retval == HFS_BTREE_CB_ERR) { + tsk_errno = TSK_ERR_FS_GENFS; + snprintf(tsk_errstr2, TSK_ERRSTR_L, + "hfs_cat_traverse: Callback returned error"); + free(node); + return 1; + } + else if ((retval == HFS_BTREE_CB_GO) || (next_node == 0)) { + hfs_btree_index_record *idx_rec; int keylen = - hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, - key->key_len) + 2, &(hfs->catalog_header)); + 2 + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, + key->key_len), &(hfs->catalog_header)); if (rec_off + keylen > nodesize) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: offset of record and keylength %d in index node %d too large (%zu vs %" + "hfs_cat_traverse: offset of record and keylength %d in index node %d too large (%zu vs %" PRIu16 ")", rec, cur_node, rec_off + keylen, nodesize); free(node); - return 0; + return 1; } - next_node = - tsk_getu32(fs->endian, &node[rec_off + keylen]); + idx_rec = + (hfs_btree_index_record *) & node[rec_off + + keylen]; + next_node = tsk_getu32(fs->endian, idx_rec->childNode); } - else { - // we are bigger than cnid, so move down to the next node + if (retval == HFS_BTREE_CB_STOP) { + // we are now too big, so move down to the next node break; } } @@ -791,7 +773,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) if (next_node == 0) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: did not find any keys in index node %d", + "hfs_cat_traverse: did not find any keys in index node %d", cur_node); is_done = 1; break; @@ -806,8 +788,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) for (rec = 0; rec < num_rec; rec++) { size_t rec_off; hfs_btree_key_cat *key; - size_t rec_off2; - int diff; + uint8_t retval; // get the record offset in the node rec_off = @@ -816,56 +797,55 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) if (rec_off > nodesize) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: offset of record %d in leaf node %d too large (%zu vs %" + "hfs_cat_traverse: offset of record %d in leaf node %d too large (%zu vs %" PRIu16 ")", rec, cur_node, rec_off, nodesize); free(node); - return 0; + return 1; } key = (hfs_btree_key_cat *) & node[rec_off]; /* if (tsk_verbose) tsk_fprintf(stderr, - "hfs_cat_get_record_offset: record %" PRIu16 + "hfs_cat_traverse: record %" PRIu16 "; keylen %" PRIu16 " (%" PRIu32 ")\n", rec, tsk_getu16(fs->endian, key->key_len), tsk_getu32(fs->endian, key->parent_cnid)); */ // rec_cnid = tsk_getu32(fs->endian, key->file_id); - diff = hfs_cat_compare_keys(hfs, key, needle); - - // see if this record is for our file or if we passed the interesting entries - if (diff < 0) { - continue; - } - else if (diff > 0) { + retval = + a_cb(hfs, HFS_BT_NODE_TYPE_LEAF, targ_data, key, + cur_off + rec_off, ptr); + if (retval == HFS_BTREE_CB_STOP) { is_done = 1; break; } - - rec_off2 = - rec_off + 2 + tsk_getu16(fs->endian, key->key_len); - if (rec_off2 > nodesize) { + else if (retval == HFS_BTREE_CB_ERR) { tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: offset of record and keylength %d in leaf node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off2, nodesize); + snprintf(tsk_errstr2, TSK_ERRSTR_L, + "hfs_cat_traverse: Callback returned error"); free(node); - return 0; + return 1; + } + } + + // move right to the next node if we got this far + if (is_done == 0) { + cur_node = tsk_getu32(fs->endian, node_desc->flink); + if (cur_node == 0) { + is_done = 1; } - free(node); - return cur_off + rec_off2; } } else { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_cat_get_record_offset: btree node %" PRIu32 + "hfs_cat_traverse: btree node %" PRIu32 " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")", cur_node, cur_off, node_desc->type); free(node); - return 0; + return 1; } } free(node); @@ -873,6 +853,56 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) } +static uint8_t +hfs_cat_get_record_offset_cb(HFS_INFO * hfs, int8_t level_type, + const void *targ_data, const hfs_btree_key_cat * cur_key, + TSK_OFF_T key_off, void *ptr) +{ + const hfs_btree_key_cat *targ_key = (hfs_btree_key_cat *) targ_data; + if (level_type == HFS_BT_NODE_TYPE_IDX) { + if (hfs_cat_compare_keys(hfs, cur_key, targ_key) <= 0) + return HFS_BTREE_CB_GO; + else + return HFS_BTREE_CB_STOP; + } + else { + int diff; + + diff = hfs_cat_compare_keys(hfs, cur_key, targ_key); + + // see if this record is for our file or if we passed the interesting entries + if (diff < 0) { + return HFS_BTREE_CB_GO; + } + else if (diff == 0) { + TSK_OFF_T *off = (TSK_OFF_T *) ptr; + *off = + key_off + 2 + tsk_getu16(hfs->fs_info.endian, + cur_key->key_len); + } + return HFS_BTREE_CB_STOP; + } +} + + +/** \internal + * Find the byte offset (from the start of the catalog file) to a record + * in the catalog file. + * @param hfs File System being analyzed + * @param needle Key to search for + * @returns Byte offset or 0 on error. 0 is also returned if catalog + * record was not found. Check tsk_errno to determine if error occured. + */ +static TSK_OFF_T +hfs_cat_get_record_offset(HFS_INFO * hfs, const hfs_btree_key_cat * needle) +{ + TSK_OFF_T off = 0; + if (hfs_cat_traverse(hfs, needle, hfs_cat_get_record_offset_cb, &off)) { + return 0; + } + return off; +} + /** \internal @@ -904,13 +934,12 @@ hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off, return 1; } - if ((tsk_getu16(fs->endian, thread->record_type) != HFS_FOLDER_THREAD) - && (tsk_getu16(fs->endian, - thread->record_type) != HFS_FILE_THREAD)) { + if ((tsk_getu16(fs->endian, thread->rec_type) != HFS_FOLDER_THREAD) + && (tsk_getu16(fs->endian, thread->rec_type) != HFS_FILE_THREAD)) { tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, "hfs_cat_read_thread_record: unexpected record type %" PRIu16, - tsk_getu16(fs->endian, thread->record_type)); + tsk_getu16(fs->endian, thread->rec_type)); return 1; } @@ -955,21 +984,23 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off, { TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); size_t cnt; + char rec_type[2]; memset(record, 0, sizeof(hfs_file_folder)); - cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *) record, 2, 0); + + cnt = tsk_fs_attr_read(hfs->catalog_attr, off, rec_type, 2, 0); if (cnt != 2) { if (cnt >= 0) { tsk_error_reset(); tsk_errno = TSK_ERR_FS_READ; } snprintf(tsk_errstr2, TSK_ERRSTR_L, - "hfs_cat_read_file_folder_record: Error reading catalog offset %" + "hfs_cat_read_file_folder_record: Error reading record type from catalog offset %" PRIuOFF " (header)", off); return 1; } - if (tsk_getu16(fs->endian, record->file.rec_type) == HFS_FOLDER_RECORD) { + 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); @@ -984,8 +1015,7 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off, return 1; } } - else if (tsk_getu16(fs->endian, - record->file.rec_type) == HFS_FILE_RECORD) { + 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); @@ -1004,7 +1034,7 @@ hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off, tsk_errno = TSK_ERR_FS_GENFS; snprintf(tsk_errstr, TSK_ERRSTR_L, "hfs_cat_read_file_folder_record: unexpected record type %" - PRIu16, tsk_getu16(fs->endian, record->file.rec_type)); + PRIu16, tsk_getu16(fs->endian, rec_type)); return 1; } @@ -1059,8 +1089,18 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) /* look up the thread record */ off = hfs_cat_get_record_offset(hfs, &key); if (off == 0) { - snprintf(tsk_errstr2, TSK_ERRSTR_L, - " hfs_cat_file_lookup: thread for file (%" PRIuINUM ")", inum); + // no parsing error, just not found + if (tsk_errno == 0) { + tsk_errno = TSK_ERR_FS_INODE_NUM; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_cat_file_lookup: Error finding thread node for file (%" + PRIuINUM ")", inum); + } + else { + snprintf(tsk_errstr2, TSK_ERRSTR_L, + " hfs_cat_file_lookup: thread for file (%" PRIuINUM ")", + inum); + } return 1; } @@ -1082,8 +1122,17 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) /* look up the record */ off = hfs_cat_get_record_offset(hfs, &key); if (off == 0) { - snprintf(tsk_errstr2, TSK_ERRSTR_L, - " hfs_cat_file_lookup: file (%" PRIuINUM ")", inum); + // no parsing error, just not found + if (tsk_errno == 0) { + tsk_errno = TSK_ERR_FS_INODE_NUM; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_cat_file_lookup: Error finding record node %" + PRIuINUM, inum); + } + else { + snprintf(tsk_errstr2, TSK_ERRSTR_L, + " hfs_cat_file_lookup: file (%" PRIuINUM ")", inum); + } return 1; } @@ -1095,21 +1144,22 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) } /* these memcpy can be gotten rid of, really */ - if (tsk_getu16(fs->endian, record.file.rec_type) == HFS_FOLDER_RECORD) { + if (tsk_getu16(fs->endian, + record.file.std.rec_type) == HFS_FOLDER_RECORD) { if (tsk_verbose) fprintf(stderr, "hfs_cat_file_lookup: found folder record valence %" PRIu32 ", cnid %" PRIu32 "\n", tsk_getu32(fs->endian, - record.folder.valence), tsk_getu32(fs->endian, - record.folder.cnid)); + 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.rec_type) == HFS_FILE_RECORD) { + record.file.std.rec_type) == HFS_FILE_RECORD) { if (tsk_verbose) fprintf(stderr, "hfs_cat_file_lookup: found file record cnid %" PRIu32 - "\n", tsk_getu32(fs->endian, record.file.cnid)); + "\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 */ @@ -1133,7 +1183,7 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) static TSK_INUM_T hfs_find_highest_inum(HFS_INFO * hfs) { - // @@@ get actual number from Catalog file (go to far right) + // @@@ get actual number from Catalog file (go to far right) (we can't always trust the vol header) /* I haven't gotten looking at the end of the Catalog B-Tree to work properly. A fast method: if HFS_VH_ATTR_CNIDS_REUSED is set, then the maximum CNID is 2^32-1; if it's not set, then nextCatalogId is @@ -1525,7 +1575,7 @@ hfs_make_startfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file) /** -* \internal + * \internal * Create an FS_INODE structure for the attributes file. * * @param hfs File system to analyze @@ -1595,18 +1645,87 @@ hfs_make_attrfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file) return 0; } +/** + * \internal + * Create an FS_FILE structure for the BadBlocks file. + * + * @param hfs File system to analyze + * @param fs_file Structure to copy file information into. + * @return 1 on error and 0 on success + */ +static uint8_t +hfs_make_badblockfile(HFS_INFO * hfs, TSK_FS_FILE * fs_file) +{ + TSK_FS_ATTR *fs_attr; + TSK_FS_ATTR_RUN *attr_run; + + if (tsk_verbose) + tsk_fprintf(stderr, + "hfs_make_badblockfile: Making virtual badblock file\n"); + + if (hfs_make_specialbase(fs_file)) + return 1; + + fs_file->meta->addr = HFS_BAD_BLOCK_FILE_ID; + strncpy(fs_file->meta->name2->name, HFS_BAD_BLOCK_FILE_NAME, + TSK_FS_META_NAME_LIST_NSIZE); + + fs_file->meta->size = 0; + + if ((fs_attr = + tsk_fs_attrlist_getnew(fs_file->meta->attr, + TSK_FS_ATTR_NONRES)) == NULL) { + strncat(tsk_errstr2, " - hfs_make_attrfile", + TSK_ERRSTR_L - strlen(tsk_errstr2)); + tsk_fs_attr_run_free(attr_run); + return 1; + } + + // dd the run to the file. + if (tsk_fs_attr_set_run(fs_file, fs_attr, NULL, NULL, + TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT, + fs_file->meta->size, fs_file->meta->size, 0, 0)) { + strncat(tsk_errstr2, " - hfs_make_attrfile", + TSK_ERRSTR_L - strlen(tsk_errstr2)); + tsk_fs_attr_free(fs_attr); + tsk_fs_attr_run_free(attr_run); + return 1; + } + + // see if catalog file has additional runs + if (hfs_ext_find_extent_record_attr(hfs, HFS_BAD_BLOCK_FILE_ID, + fs_attr)) { + strncat(tsk_errstr2, " - hfs_make_attrfile", + TSK_ERRSTR_L - strlen(tsk_errstr2)); + fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR; + return 1; + } + + /* @@@ We have a chicken and egg problem here... The current design of + * fs_attr_set() requires the size to be set, but we dont' know the size + * until we look into the extents file (which adds to an attribute...). + * This does not seem to be the best design... neeed a way to test this. */ + fs_file->meta->size = fs_attr->nrd.initsize; + fs_attr->size = fs_file->meta->size; + fs_attr->nrd.allocsize = fs_file->meta->size; + + fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED; + return 0; +} + /** \internal - * Copy the catalog file record entry into a TSK data structure. + * Copy the catalog file or folder record entry into a TSK data structure. * @param a_hfs File system being analyzed - * @param a_entry Catalog record entry (could be a hfs_folder structure too) + * @param a_entry Catalog record entry * @param a_fs_meta Structure to copy data into * Returns 1 on error. */ static uint8_t -hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file * a_entry, +hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file_folder * a_entry, TSK_FS_META * a_fs_meta) { + const hfs_file_fold_std *std; TSK_FS_INFO *fs = (TSK_FS_INFO *) & a_hfs->fs_info; if (a_fs_meta == NULL) { @@ -1616,10 +1735,13 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file * a_entry, return 1; } + // both files and folders start of the same + std = &(a_entry->file.std); + if (tsk_verbose) tsk_fprintf(stderr, - "hfs_dinode_copy: called for file %" PRIu32 "\n", - tsk_getu32(fs->endian, a_entry->cnid)); + "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 = @@ -1628,29 +1750,27 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file * a_entry, 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); } - a_fs_meta->mode = - hfsmode2tskmode(tsk_getu16(fs->endian, a_entry->perm.mode)); - a_fs_meta->type = - hfsmode2tskmetatype(tsk_getu16(fs->endian, a_entry->perm.mode)); - - if (tsk_getu16(fs->endian, a_entry->rec_type) == HFS_FOLDER_RECORD) { + /* + * Copy the file type specific stuff first + */ + if (tsk_getu16(fs->endian, std->rec_type) == HFS_FOLDER_RECORD) { a_fs_meta->size = 0; memset(a_fs_meta->content_ptr, 0, HFS_FILE_CONTENT_LEN); } - else if (tsk_getu16(fs->endian, a_entry->rec_type) == HFS_FILE_RECORD) { + else if (tsk_getu16(fs->endian, std->rec_type) == HFS_FILE_RECORD) { hfs_fork *fork; - a_fs_meta->size = tsk_getu64(fs->endian, a_entry->data.logic_sz); + a_fs_meta->size = + tsk_getu64(fs->endian, a_entry->file.data.logic_sz); // copy the data and resource forks fork = (hfs_fork *) a_fs_meta->content_ptr; - memcpy(fork, &(a_entry->data), sizeof(hfs_fork)); - memcpy(&fork[1], &(a_entry->resource), sizeof(hfs_fork)); + memcpy(fork, &(a_entry->file.data), sizeof(hfs_fork)); + memcpy(&fork[1], &(a_entry->file.resource), sizeof(hfs_fork)); } else { tsk_fprintf(stderr, @@ -1658,27 +1778,30 @@ hfs_dinode_copy(HFS_INFO * a_hfs, const hfs_file * a_entry, return 1; } - a_fs_meta->uid = tsk_getu32(fs->endian, a_entry->perm.owner); - a_fs_meta->gid = tsk_getu32(fs->endian, a_entry->perm.group); + /* + * Copy the standard stuff + */ + a_fs_meta->mode = + hfsmode2tskmode(tsk_getu16(fs->endian, std->perm.mode)); + a_fs_meta->type = + hfsmode2tskmetatype(tsk_getu16(fs->endian, std->perm.mode)); + + a_fs_meta->uid = tsk_getu32(fs->endian, std->perm.owner); + a_fs_meta->gid = tsk_getu32(fs->endian, std->perm.group); // this field is set only for "indirect" entries - if (tsk_getu32(fs->endian, a_entry->perm.special.nlink)) - a_fs_meta->nlink = - tsk_getu32(fs->endian, a_entry->perm.special.nlink); + 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 = - hfs2unixtime(tsk_getu32(fs->endian, a_entry->cmtime)); - a_fs_meta->atime = - hfs2unixtime(tsk_getu32(fs->endian, a_entry->atime)); - a_fs_meta->crtime = - hfs2unixtime(tsk_getu32(fs->endian, a_entry->ctime)); - a_fs_meta->ctime = - hfs2unixtime(tsk_getu32(fs->endian, a_entry->attr_mtime)); + a_fs_meta->mtime = hfs2unixtime(tsk_getu32(fs->endian, std->cmtime)); + a_fs_meta->atime = hfs2unixtime(tsk_getu32(fs->endian, std->atime)); + a_fs_meta->crtime = hfs2unixtime(tsk_getu32(fs->endian, std->crtime)); + a_fs_meta->ctime = hfs2unixtime(tsk_getu32(fs->endian, std->amtime)); a_fs_meta->time2.hfs.bkup_time = - hfs2unixtime(tsk_getu32(fs->endian, a_entry->bkup_date)); + hfs2unixtime(tsk_getu32(fs->endian, std->bkup_date)); - a_fs_meta->addr = tsk_getu32(fs->endian, a_entry->cnid); + a_fs_meta->addr = tsk_getu32(fs->endian, std->cnid); // All entries here are used. a_fs_meta->flags = TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_USED; @@ -1751,6 +1874,12 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, else return 0; } + else if (inum == HFS_BAD_BLOCK_FILE_ID) { + if (hfs_make_badblockfile(hfs, a_fs_file)) + return 1; + else + return 0; + } else if (inum == HFS_ALLOCATION_FILE_ID) { if (hfs_make_blockmap(hfs, a_fs_file)) return 1; @@ -1775,7 +1904,8 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file, return 1; /* Copy the structure in hfs to generic fs_inode */ - if (hfs_dinode_copy(hfs, &entry.cat, a_fs_file->meta)) { + if (hfs_dinode_copy(hfs, (const hfs_file_folder *) &entry.cat, + a_fs_file->meta)) { return 1; } @@ -1869,13 +1999,13 @@ hfs_load_attrs(TSK_FS_FILE * fs_file) //Detect Hard links else if ((tsk_getu32(fs->endian, - entry.cat.u_info.file_type) == HFS_HARDLINK_FILE_TYPE) + entry.cat.std.u_info.file_type) == HFS_HARDLINK_FILE_TYPE) && (tsk_getu32(fs->endian, - entry.cat.u_info.file_cr) == + entry.cat.std.u_info.file_cr) == HFS_HARDLINK_FILE_CREATOR)) { // Get the indirect node value - tsk_getu32(fs->endian, entry.cat.perm.special.inum) + tsk_getu32(fs->endian, entry.cat.std.perm.special.inum) // Find the indirect node "/____HFS+ Private Data/iNodeXXXX" @@ -1905,7 +2035,8 @@ hfs_load_attrs(TSK_FS_FILE * fs_file) } // see if extents file has additional runs - if (hfs_ext_find_extent_record_attr(hfs, (uint32_t)fs_file->meta->addr, fs_attr)) { + if (hfs_ext_find_extent_record_attr(hfs, + (uint32_t) fs_file->meta->addr, fs_attr)) { strncat(tsk_errstr2, " - hfs_load_attrs", TSK_ERRSTR_L - strlen(tsk_errstr2)); fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR; @@ -1989,7 +2120,7 @@ hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr) hfs->blockmap_cache_start = b; hfs->blockmap_cache_len = cnt; } - b2 = (size_t)(b - hfs->blockmap_cache_start); + b2 = (size_t) (b - hfs->blockmap_cache_start); return (hfs->blockmap_cache[b2] & (1 << (7 - (a_addr % 8)))) != 0; } @@ -2103,10 +2234,8 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, TSK_INUM_T end_inum, TSK_FS_META_FLAG_ENUM flags, TSK_FS_META_WALK_CB action, void *ptr) { - HFS_INFO *hfs = (HFS_INFO *) fs; TSK_INUM_T inum; TSK_FS_FILE *fs_file; - HFS_ENTRY entry; if (tsk_verbose) tsk_fprintf(stderr, @@ -2141,6 +2270,29 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, return 1; } + /* If ORPHAN is wanted, then make sure that the flags are correct */ + if (flags & TSK_FS_META_FLAG_ORPHAN) { + flags |= TSK_FS_META_FLAG_UNALLOC; + flags &= ~TSK_FS_META_FLAG_ALLOC; + flags |= TSK_FS_META_FLAG_USED; + flags &= ~TSK_FS_META_FLAG_UNUSED; + } + + else { + if (((flags & TSK_FS_META_FLAG_ALLOC) == 0) && + ((flags & TSK_FS_META_FLAG_UNALLOC) == 0)) { + flags |= (TSK_FS_META_FLAG_ALLOC | TSK_FS_META_FLAG_UNALLOC); + } + + /* If neither of the USED or UNUSED flags are set, then set them + * both + */ + if (((flags & TSK_FS_META_FLAG_USED) == 0) && + ((flags & TSK_FS_META_FLAG_UNUSED) == 0)) { + flags |= (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_UNUSED); + } + } + if ((fs_file = tsk_fs_file_alloc(fs)) == NULL) return 1; @@ -2153,22 +2305,18 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, for (inum = start_inum; inum <= end_inum; inum++) { int retval; - /* this section is logically the same as just calling hfs_inode_lookup, - * but reuses a single malloc'ed fs_inode - */ - - if (hfs_cat_file_lookup(hfs, inum, &entry)) { - if (tsk_errno == 0) + if (hfs_inode_lookup(fs, fs_file, inum)) { + // deleted files may not exist in the catalog + if (tsk_errno == TSK_ERR_FS_INODE_NUM) { + tsk_error_reset(); continue; - else + } + else { return 1; + } } - /* Copy the structure in hfs to generic fs_inode */ - if (hfs_dinode_copy(hfs, &entry.cat, fs_file->meta)) - return 1; - - if ((fs_file->meta->flags & flags) != flags) + if ((fs_file->meta->flags & flags) != fs_file->meta->flags) continue; /* call action */ @@ -2183,9 +2331,6 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum, } } - /* - * Cleamup. - */ tsk_fs_file_close(fs_file); return 0; } @@ -2285,7 +2430,7 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) { // char *myname = "hfs_fsstat"; HFS_INFO *hfs = (HFS_INFO *) fs; - hfs_sb *sb = hfs->fs; + hfs_plus_vh *sb = hfs->fs; time_t mac_time; TSK_INUM_T inode; @@ -2537,53 +2682,56 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum, 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.perm.special.raw)); + tsk_getu32(fs->endian, entry.cat.std.perm.special.raw)); } else if ((tsk_getu32(fs->endian, - entry.cat.u_info.file_type) == HFS_HARDLINK_FILE_TYPE) + entry.cat.std.u_info.file_type) == + HFS_HARDLINK_FILE_TYPE) && (tsk_getu32(fs->endian, - entry.cat.u_info.file_cr) == + entry.cat.std.u_info.file_cr) == HFS_HARDLINK_FILE_CREATOR)) { // technically, the creation date of this item should be the same as either the // creation date of the "HFS+ Private Data" folder or the creation date of the root folder tsk_fprintf(hFile, "Hard link inode number\t %" PRIu32 "\n", - tsk_getu32(fs->endian, entry.cat.perm.special.inum)); + tsk_getu32(fs->endian, entry.cat.std.perm.special.inum)); } - if (tsk_getu16(fs->endian, entry.cat.flags) & HFS_FILE_FLAG_LOCKED) + 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.flags) & HFS_FILE_FLAG_ATTR) + 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.flags) & HFS_FILE_FLAG_ACL) + if (tsk_getu16(fs->endian, + entry.cat.std.flags) & HFS_FILE_FLAG_ACL) tsk_fprintf(hFile, "Has security data (ACLs)\n"); tsk_fprintf(hFile, "File type:\t%04" PRIx32 "\nFile creator:\t%04" PRIx32 "\n", - tsk_getu32(fs->endian, entry.cat.u_info.file_type), - tsk_getu32(fs->endian, entry.cat.u_info.file_type)); + tsk_getu32(fs->endian, entry.cat.std.u_info.file_type), + tsk_getu32(fs->endian, entry.cat.std.u_info.file_type)); if (tsk_getu16(fs->endian, - entry.cat.u_info.flags) & HFS_FINDER_FLAG_NAME_LOCKED) + entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_NAME_LOCKED) tsk_fprintf(hFile, "Name locked\n"); if (tsk_getu16(fs->endian, - entry.cat.u_info.flags) & HFS_FINDER_FLAG_HAS_BUNDLE) + entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_HAS_BUNDLE) tsk_fprintf(hFile, "Has bundle\n"); if (tsk_getu16(fs->endian, - entry.cat.u_info.flags) & HFS_FINDER_FLAG_IS_INVISIBLE) + entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_IS_INVISIBLE) tsk_fprintf(hFile, "Is invisible\n"); if (tsk_getu16(fs->endian, - entry.cat.u_info.flags) & HFS_FINDER_FLAG_IS_ALIAS) + entry.cat.std.u_info.flags) & HFS_FINDER_FLAG_IS_ALIAS) tsk_fprintf(hFile, "Is alias\n"); // @@@ The tech note has a table that converts nums to encoding names. tsk_fprintf(hFile, "Text encoding:\t%" PRIx32 "\n", - tsk_getu32(fs->endian, entry.cat.text_enc)); + tsk_getu32(fs->endian, entry.cat.std.text_enc)); - if (tsk_getu16(fs->endian, entry.cat.rec_type) == HFS_FILE_RECORD) { - tsk_fprintf(hFile, - "Resource fork size:\t%" - PRIu64 "\n", tsk_getu64(fs->endian, - entry.cat.resource.logic_sz)); + 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)); } } @@ -2707,8 +2855,8 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, /* * Read the superblock. */ - len = sizeof(hfs_sb); - if ((hfs->fs = (hfs_sb *) tsk_malloc(len)) == NULL) { + len = sizeof(hfs_plus_vh); + if ((hfs->fs = (hfs_plus_vh *) tsk_malloc(len)) == NULL) { fs->tag = 0; free(hfs); return NULL; @@ -2740,27 +2888,36 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, } /* - * Handle an HFS-wrapped HFS+ image + * Handle an HFS-wrapped HFS+ image, which is a HFS volume that contains + * the HFS+ volume inside of it. */ if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFS) { - hfs_wrapper_sb *wrapper_sb = (hfs_wrapper_sb *) hfs->fs; + hfs_mdb *wrapper_sb = (hfs_mdb *) hfs->fs; + // Verify that we are setting a wrapper and not a normal HFS volume if ((tsk_getu16(fs->endian, wrapper_sb->drEmbedSigWord) == HFS_VH_SIG_HFSPLUS) || (tsk_getu16(fs->endian, wrapper_sb->drEmbedSigWord) == HFS_VH_SIG_HFSX)) { TSK_FS_INFO *fs_info2; + // offset in sectors to start of first HFS block uint16_t drAlBlSt = tsk_getu16(fs->endian, wrapper_sb->drAlBlSt); + + // size of each HFS block uint32_t drAlBlkSiz = tsk_getu32(fs->endian, wrapper_sb->drAlBlkSiz); + + // start of embedded FS uint16_t startBlock = tsk_getu16(fs->endian, wrapper_sb->drEmbedExtent_startBlock); + TSK_OFF_T hfsplus_offset = (drAlBlSt * (TSK_OFF_T) 512) + (drAlBlkSiz * (TSK_OFF_T) startBlock); + if (tsk_verbose) tsk_fprintf(stderr, "hfs_open: HFS+/HFSX within HFS wrapper at byte offset %" @@ -2773,17 +2930,22 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, /* just re-open with the new offset, then record the offset */ fs_info2 = hfs_open(img_info, offset + hfsplus_offset, ftype, test); - ((HFS_INFO *) fs_info2)->hfs_wrapper_offset = hfsplus_offset; + + if (fs_info2) + ((HFS_INFO *) fs_info2)->hfs_wrapper_offset = + hfsplus_offset; + return fs_info2; } - - fs->tag = 0; - free(hfs->fs); - free(hfs); - tsk_errno = TSK_ERR_FS_MAGIC; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "HFS file systems (other than wrappers HFS+/HFSX file systems) are not supported"); - return NULL; + else { + fs->tag = 0; + free(hfs->fs); + free(hfs); + tsk_errno = TSK_ERR_FS_MAGIC; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "HFS file systems (other than wrappers HFS+/HFSX file systems) are not supported"); + return NULL; + } } fs->block_count = tsk_getu32(fs->endian, hfs->fs->blk_cnt); diff --git a/tsk3/fs/hfs_dent.c b/tsk3/fs/hfs_dent.c index 76302c540639c2f3570cbbe21f8b41efa89d5b10..3c47724ea7b763fd280d8ac33577ea8e0f832cf1 100644 --- a/tsk3/fs/hfs_dent.c +++ b/tsk3/fs/hfs_dent.c @@ -162,31 +162,141 @@ hfsmode2tsknametype(uint16_t a_mode) } } + +// used to pass data to the callback +typedef struct { + TSK_FS_DIR *fs_dir; + TSK_FS_NAME *fs_name; +} HFS_DIR_OPEN_META_INFO; + +static uint8_t +hfs_dir_open_meta_cb(HFS_INFO * hfs, int8_t level_type, + const void *targ_data, const hfs_btree_key_cat * cur_key, + TSK_OFF_T key_off, void *ptr) +{ + uint32_t *cnid_p = (uint32_t *) targ_data; + HFS_DIR_OPEN_META_INFO *info = (HFS_DIR_OPEN_META_INFO *) ptr; + TSK_FS_INFO *fs = &hfs->fs_info; + + // for both node types, continue for smaller values and stop for bigger + if (tsk_getu32(hfs->fs_info.endian, cur_key->parent_cnid) < *cnid_p) + return HFS_BTREE_CB_GO; + else if (tsk_getu32(hfs->fs_info.endian, + cur_key->parent_cnid) > *cnid_p) + return HFS_BTREE_CB_STOP; + + if (level_type == HFS_BT_NODE_TYPE_IDX) { + // for equals, return "go" + return HFS_BTREE_CB_GO; + } + else { + uint8_t *rec_buf = (uint8_t *) cur_key; + uint16_t rec_type; + + size_t rec_off2 = + 2 + tsk_getu16(hfs->fs_info.endian, cur_key->key_len); + // @@@ NEED TO REPLACE THIS SOMEHOW, but need to figure out the max length + /* + if (rec_off2 > nodesize) { + tsk_errno = TSK_ERR_FS_GENFS; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_dir_open_meta: offset of record+keylen %d in leaf node %d too large (%zu vs %" + PRIu16 ")", rec, cur_node, rec_off2, nodesize); + tsk_fs_name_free(fs_name); + free(node); + return TSK_COR; + } + */ + rec_type = tsk_getu16(hfs->fs_info.endian, &rec_buf[rec_off2]); + + // Catalog entry is for a file + if (rec_type == HFS_FILE_THREAD) { + tsk_errno = TSK_ERR_FS_GENFS; + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_dir_open_meta: Entry" " is a file, not a folder"); + return HFS_BTREE_CB_ERR; + } + + /* This will link the folder to its parent, which is the ".." entry */ + else if (rec_type == HFS_FOLDER_THREAD) { + hfs_thread *thread = (hfs_thread *) & rec_buf[rec_off2]; + strcpy(info->fs_name->name, ".."); + info->fs_name->meta_addr = + tsk_getu32(hfs->fs_info.endian, thread->parent_cnid); + info->fs_name->type = TSK_FS_NAME_TYPE_DIR; + info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + } + + /* This is a folder in the folder */ + else if (rec_type == HFS_FOLDER_RECORD) { + hfs_folder *folder = (hfs_folder *) & rec_buf[rec_off2]; + + info->fs_name->meta_addr = + tsk_getu32(hfs->fs_info.endian, folder->std.cnid); + info->fs_name->type = TSK_FS_NAME_TYPE_DIR; + info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + + + if (hfs_uni2ascii(fs, (uint8_t *) cur_key->name.unicode, + tsk_getu16(hfs->fs_info.endian, cur_key->name.length), + info->fs_name->name, HFS_MAXNAMLEN + 1)) { + return HFS_BTREE_CB_ERR; + } + } + + /* This is a normal file in the folder */ + else if (rec_type == HFS_FILE_RECORD) { + hfs_file *file = (hfs_file *) & rec_buf[rec_off2]; + info->fs_name->meta_addr = + tsk_getu32(hfs->fs_info.endian, file->std.cnid); + info->fs_name->type = + hfsmode2tsknametype(tsk_getu16(hfs->fs_info.endian, + file->std.perm.mode)); + info->fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; + if (hfs_uni2ascii(fs, (uint8_t *) cur_key->name.unicode, + tsk_getu16(hfs->fs_info.endian, cur_key->name.length), + info->fs_name->name, HFS_MAXNAMLEN + 1)) { + return HFS_BTREE_CB_ERR; + } + } + else { + tsk_errno = TSK_ERR_FS_GENFS; + // @@@ MAY NEED TO IMPROVE BELOW MESSAGE + snprintf(tsk_errstr, TSK_ERRSTR_L, + "hfs_dir_open_meta: Unknown record type %d in leaf node", + rec_type); + return HFS_BTREE_CB_ERR; + } + + if (tsk_fs_dir_add(info->fs_dir, info->fs_name)) { + return HFS_BTREE_CB_ERR; + } + return HFS_BTREE_CB_GO; + } +} + /** \internal - * Process a directory and load up FS_DIR with the entries. If a pointer to - * an already allocated FS_DIR struture is given, it will be cleared. If no existing - * FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return - * value is error or corruption, then the FS_DIR structure could - * have entries (depending on when the error occured). - * - * @param a_fs File system to analyze - * @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated - * structure or a new structure. - * @param a_addr Address of directory to process. - * @returns error, corruption, ok etc. - */ +* Process a directory and load up FS_DIR with the entries. If a pointer to +* an already allocated FS_DIR struture is given, it will be cleared. If no existing +* FS_DIR structure is passed (i.e. NULL), then a new one will be created. If the return +* value is error or corruption, then the FS_DIR structure could +* have entries (depending on when the error occured). +* +* @param a_fs File system to analyze +* @param a_fs_dir Pointer to FS_DIR pointer. Can contain an already allocated +* structure or a new structure. +* @param a_addr Address of directory to process. +* @returns error, corruption, ok etc. +*/ TSK_RETVAL_ENUM hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, TSK_INUM_T a_addr) { HFS_INFO *hfs = (HFS_INFO *) fs; uint32_t cnid; /* catalog node ID of the entry (= inum) */ - uint32_t cur_node; /* node id of the current node */ - char *node; TSK_FS_DIR *fs_dir; TSK_FS_NAME *fs_name; - uint16_t nodesize; - uint8_t is_done = 0; + HFS_DIR_OPEN_META_INFO info; tsk_error_reset(); @@ -207,7 +317,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, tsk_error_reset(); tsk_errno = TSK_ERR_FS_ARG; snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: NULL fs_data argument given"); + "hfs_dir_open_meta: NULL fs_dir argument given"); return TSK_ERR; } @@ -220,15 +330,15 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, if (fs_dir) { tsk_fs_dir_reset(fs_dir); } - else { - if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(fs, 128)) == NULL) { - return TSK_ERR; - } + else if ((*a_fs_dir = fs_dir = tsk_fs_dir_alloc(fs, 128)) == NULL) { + return TSK_ERR; } if ((fs_name = tsk_fs_name_alloc(HFS_MAXNAMLEN, 0)) == NULL) { return TSK_ERR; } + info.fs_dir = fs_dir; + info.fs_name = fs_name; if ((fs_dir->fs_file = tsk_fs_file_open_meta(fs, NULL, a_addr)) == NULL) { @@ -293,295 +403,11 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, } } - nodesize = tsk_getu16(fs->endian, hfs->catalog_header.nodesize); - if ((node = (char *) tsk_malloc(nodesize)) == NULL) { + if (hfs_cat_traverse(hfs, &cnid, hfs_dir_open_meta_cb, &info)) { tsk_fs_name_free(fs_name); return TSK_ERR; } - /* start at root node */ - cur_node = tsk_getu32(fs->endian, hfs->catalog_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_dir_open_meta: " - "empty extents btree\n"); - tsk_fs_name_free(fs_name); - free(node); - return TSK_OK; - } - - if (tsk_verbose) - tsk_fprintf(stderr, "hfs_dir_open_meta: 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; - - // read the current node - cur_off = cur_node * nodesize; - cnt = tsk_fs_attr_read(hfs->catalog_attr, cur_off, - node, nodesize, 0); - if (cnt != nodesize) { - if (cnt >= 0) { - tsk_error_reset(); - tsk_errno = TSK_ERR_FS_READ; - } - snprintf(tsk_errstr2, TSK_ERRSTR_L, - "hfs_dir_open_meta: Error reading catalog node %d at offset %" - PRIuOFF, cur_node, cur_off); - tsk_fs_name_free(fs_name); - free(node); - return TSK_ERR; - } - - // process the header / descriptor - node_desc = (hfs_btree_node *) node; - num_rec = tsk_getu16(fs->endian, node_desc->num_rec); - - if (tsk_verbose) - tsk_fprintf(stderr, "hfs_dir_open_meta: node %" PRIu32 - " @ %" PRIu64 " has %" PRIu16 " records\n", - cur_node, cur_off, num_rec); - - if (num_rec == 0) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: zero records in node %" - PRIu32, cur_node); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - - /* 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; - - for (rec = 0; rec < num_rec; rec++) { - size_t rec_off; - hfs_btree_key_cat *key; - - // get the record offset in the node - rec_off = - tsk_getu16(fs->endian, - &node[nodesize - (rec + 1) * 2]); - if (rec_off > nodesize) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: offset of record %d in index node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off, nodesize); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - key = (hfs_btree_key_cat *) & node[rec_off]; - - if (tsk_verbose) - tsk_fprintf(stderr, - "hfs_dir_open_meta: record %" PRIu16 - " ; keylen %" PRIu16 " (%" PRIu32 ")\n", rec, - tsk_getu16(fs->endian, key->key_len), - tsk_getu32(fs->endian, key->parent_cnid)); - - /* save the info from this record unless it is bigger than cnid */ - if ((tsk_getu32(fs->endian, key->parent_cnid) <= cnid) - || (next_node == 0)) { - int keylen = - hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, - key->key_len) + 2, &(hfs->catalog_header)); - if (rec_off + keylen > nodesize) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: offset of record + keylen %d in index node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off + keylen, - nodesize); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - next_node = - tsk_getu32(fs->endian, &node[rec_off + keylen]); - } - else { - // we are bigger than cnid, so move down to the next node - break; - } - } - - // check if we found a relevant node - if (next_node == 0) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: did not find any keys for %d in index node %d", - cnid, cur_node); - // @@@ is this an error? It is probably just an empty directory, so the error msg does not ned to be set.. - 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; - - for (rec = 0; rec < num_rec; rec++) { - size_t rec_off; - hfs_btree_key_cat *key; - uint16_t rec_type; - size_t rec_off2; - - // get the record offset in the node - rec_off = - tsk_getu16(fs->endian, - &node[nodesize - (rec + 1) * 2]); - if (rec_off > nodesize) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: offset of record %d in leaf node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off, nodesize); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - key = (hfs_btree_key_cat *) & node[rec_off]; - - if (tsk_verbose) - tsk_fprintf(stderr, - "hfs_dir_open_meta: record %" PRIu16 - "; keylen %" PRIu16 " (%" PRIu32 ")\n", rec, - tsk_getu16(fs->endian, key->key_len), - tsk_getu32(fs->endian, key->parent_cnid)); - - // see if this record is for our file or if we passed the interesting entries - if (tsk_getu32(fs->endian, key->parent_cnid) < cnid) { - continue; - } - else if (tsk_getu32(fs->endian, key->parent_cnid) > cnid) { - is_done = 1; - break; - } - - rec_off2 = - rec_off + 2 + tsk_getu16(fs->endian, key->key_len); - if (rec_off2 > nodesize) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: offset of record+keylen %d in leaf node %d too large (%zu vs %" - PRIu16 ")", rec, cur_node, rec_off2, nodesize); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - rec_type = tsk_getu16(fs->endian, &node[rec_off2]); - - // Catalog entry is for a file - if (rec_type == HFS_FILE_THREAD) { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: Entry %" PRIuINUM - " is a file, not a folder", a_addr); - tsk_fs_name_free(fs_name); - free(node); - return TSK_ERR; - } - - /* This will link the folder to its parent, which is the ".." entry */ - else if (rec_type == HFS_FOLDER_THREAD) { - hfs_thread *thread = (hfs_thread *) & node[rec_off2]; - - strcpy(fs_name->name, ".."); - fs_name->meta_addr = - tsk_getu32(fs->endian, thread->parent_cnid); - fs_name->type = TSK_FS_NAME_TYPE_DIR; - fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; - } - - /* This is a folder in the folder */ - else if (rec_type == HFS_FOLDER_RECORD) { - hfs_folder *folder = (hfs_folder *) & node[rec_off2]; - - fs_name->meta_addr = - tsk_getu32(fs->endian, folder->cnid); - fs_name->type = TSK_FS_NAME_TYPE_DIR; - fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; - - if (hfs_uni2ascii(fs, key->name.unicode, - tsk_getu16(fs->endian, key->name.length), - fs_name->name, HFS_MAXNAMLEN + 1)) { - tsk_fs_name_free(fs_name); - free(node); - return TSK_ERR; - } - } - - /* This is a normal file in the folder */ - else if (rec_type == HFS_FILE_RECORD) { - hfs_file *file = (hfs_file *) & node[rec_off2]; - fs_name->meta_addr = - tsk_getu32(fs->endian, file->cnid); - fs_name->type = - hfsmode2tsknametype(tsk_getu16(fs->endian, - file->perm.mode)); - fs_name->flags = TSK_FS_NAME_FLAG_ALLOC; - if (hfs_uni2ascii(fs, key->name.unicode, - tsk_getu16(fs->endian, key->name.length), - fs_name->name, HFS_MAXNAMLEN + 1)) { - tsk_fs_name_free(fs_name); - free(node); - return TSK_ERR; - } - } - else { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: Unknown record type %d in leaf node %d", - rec_type, cur_node); - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - - if (tsk_fs_dir_add(fs_dir, fs_name)) { - tsk_fs_name_free(fs_name); - free(node); - return TSK_ERR; - } - } - - // move right to the next node if we got this far - cur_node = tsk_getu32(fs->endian, node_desc->flink); - if (cur_node == 0) { - is_done = 1; - break; - } - } - else { - tsk_errno = TSK_ERR_FS_GENFS; - snprintf(tsk_errstr, TSK_ERRSTR_L, - "hfs_dir_open_meta: btree node %" PRIu32 - " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")", - cur_node, cur_off, node_desc->type); - - tsk_fs_name_free(fs_name); - free(node); - return TSK_COR; - } - } tsk_fs_name_free(fs_name); - free(node); return TSK_OK; } diff --git a/tsk3/fs/hfs_unicompare.c b/tsk3/fs/hfs_unicompare.c index e1881e8d36497b6b70072572d548581da4f11aae..08982bf431230b1d715f8319a2bf8809281fd565 100644 --- a/tsk3/fs/hfs_unicompare.c +++ b/tsk3/fs/hfs_unicompare.c @@ -108,15 +108,16 @@ #include "tsk_fs_i.h" #include "tsk_hfs.h" -static int hfs_unicode_compare_int(uint16_t endian, hfs_uni_str * uni1, - hfs_uni_str * uni2); +static int hfs_unicode_compare_int(uint16_t endian, + const hfs_uni_str * uni1, const hfs_uni_str * uni2); int -hfs_unicode_compare(HFS_INFO * hfs, hfs_uni_str * uni1, hfs_uni_str * uni2) +hfs_unicode_compare(HFS_INFO * hfs, const hfs_uni_str * uni1, + const hfs_uni_str * uni2) { if (hfs->is_case_sensitive) { uint16_t l1, l2; - uint8_t *s1, *s2; + const uint8_t *s1, *s2; uint16_t c1, c2; l1 = tsk_getu16(hfs->fs_info.endian, uni1->length); @@ -150,19 +151,17 @@ hfs_unicode_compare(HFS_INFO * hfs, hfs_uni_str * uni1, hfs_uni_str * uni2) extern uint16_t gLowerCaseTable[]; static int -hfs_unicode_compare_int(uint16_t endian, hfs_uni_str * uni1, - hfs_uni_str * uni2) +hfs_unicode_compare_int(uint16_t endian, const hfs_uni_str * uni1, + const hfs_uni_str * uni2) { uint16_t c1, c2; uint16_t temp; uint16_t *lowerCaseTable; - uint16_t length1, length2; - uint8_t *str1 = uni1->unicode; - uint8_t *str2 = uni2->unicode; - - length1 = tsk_getu16(endian, uni1->length); - length2 = tsk_getu16(endian, uni2->length); + const uint8_t *str1 = uni1->unicode; + const uint8_t *str2 = uni2->unicode; + uint16_t length1 = tsk_getu16(endian, uni1->length); + uint16_t length2 = tsk_getu16(endian, uni2->length); lowerCaseTable = gLowerCaseTable; diff --git a/tsk3/fs/tsk_fs_i.h b/tsk3/fs/tsk_fs_i.h index d3017b419022a441ba5d93d001efdd807bdab643..71e4c7d7faf6c680b9ee5a6ec5c8a1c9389cd3bc 100644 --- a/tsk3/fs/tsk_fs_i.h +++ b/tsk3/fs/tsk_fs_i.h @@ -53,7 +53,7 @@ extern "C" { // set to 1 to open HFS+ file systems -- which is not fully tested #ifndef TSK_USE_HFS -#define TSK_USE_HFS 0 +#define TSK_USE_HFS 1 #endif diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h index 9430860ddd6b2c77bd96b4be95a611fb9439b524..395e3aa7f263701bad585fcea5a5da150ea2e0c7 100644 --- a/tsk3/fs/tsk_hfs.h +++ b/tsk3/fs/tsk_hfs.h @@ -87,11 +87,6 @@ #define HFS_MAXNAMLEN 765 /* maximum HFS+ name length in bytes, when encoded in UTF8, not including terminating null */ -/* catalog file data types */ -#define HFS_FOLDER_RECORD 0x0001 -#define HFS_FILE_RECORD 0X0002 -#define HFS_FOLDER_THREAD 0x0003 -#define HFS_FILE_THREAD 0x0004 /* * HFS uses its own time system, which is seconds since Jan 1 1904 @@ -287,13 +282,13 @@ typedef struct { hfs_fork cat_file; /* location and size of catalog file */ hfs_fork attr_file; /* location and size of attributes file */ hfs_fork start_file; /* location and size of startup file */ -} hfs_sb; +} hfs_plus_vh; /* -** HFS Super Block for wrapped HFS+/HFSX file systems +** HFS (non-Plus) Master Directory Block (volume header-like) (used with wrapped HFS+/HFSX file systems) */ typedef struct { - uint8_t drSigWord[2]; /* "BD" for HFS (same location as hfs_sb.signature) */ + uint8_t drSigWord[2]; /* "BD" for HFS (same location as hfs_plus_vh.signature) */ uint8_t drCrDate[4]; /* volume creation date */ uint8_t drLsMod[4]; /* volume last modified date */ uint8_t drAtrb[2]; /* volume attributes */ @@ -316,14 +311,14 @@ typedef struct { uint8_t drFilCnt[4]; /* number of files on volume */ uint8_t drDirCnt[4]; /* number of directories on volume */ uint8_t drFndrInfo[32]; /* Finder info */ - uint8_t drEmbedSigWord[2]; /* signature of the embedded HFS+ volume (eg, "H+") */ + uint8_t drEmbedSigWord[2]; /* signature of the embedded HFS+ volume (eg, "H+") - 0x7c offset */ uint8_t drEmbedExtent_startBlock[2]; /* extent descriptor for start of embedded volume */ - uint8_t drEmbedExtent_blockCount[2]; /* extent descriptor for start of embedded volume */ + uint8_t drEmbedExtent_blockCount[2]; /* extent descriptor for num of blks in of embedded volume */ uint8_t drXTFlSize[4]; /* size of the extents overflow file */ uint8_t drXTExtRec[12]; /* extent record with size and location of extents overflow file */ uint8_t drCTFlSize[4]; /* size of the catalog file */ uint8_t drCTExtRec[12]; /* extent record with size and location of catalog file */ -} hfs_wrapper_sb; +} hfs_mdb; @@ -389,12 +384,16 @@ typedef struct { } hfs_btree_key_cat; /* Key for extents records */ +// fork_type values +#define HFS_EXT_KEY_TYPE_DATA 0x00 // extents key is for data fork +#define HFS_EXT_KEY_TYPE_RSRC 0xFF // extents key is for resource fork + typedef struct { - uint8_t key_len[2]; // length of key minus 2 - uint8_t fork_type[1]; - uint8_t pad[1]; - uint8_t file_id[4]; - uint8_t start_block[4]; + uint8_t key_len[2]; // length of key minus 2 (should always be 10) + uint8_t fork_type; // data or resource fork + uint8_t pad; // reserved + uint8_t file_id[4]; // the cnid that this key is for + uint8_t start_block[4]; // the offset in the file (in blocks) that this run is for } hfs_btree_key_ext; /* Record contents for index record after key */ @@ -402,16 +401,9 @@ typedef struct { uint8_t childNode[4]; } hfs_btree_index_record; -typedef struct { - uint32_t inum; /* inode number */ - uint32_t parent; /* parent directory number */ - uint32_t node; /* btree leaf node */ - TSK_DADDR_T offs; /* offset of beginning of inode */ -} htsk_fs_inode_mode_struct; - - +/********* CATALOG Record structures *********/ typedef struct { int8_t v[2]; int8_t h[2]; @@ -422,6 +414,7 @@ typedef struct { #define HFS_FINDER_FLAG_IS_INVISIBLE 0x4000 #define HFS_FINDER_FLAG_IS_ALIAS 0x8000 +// Finder info stored in file and folder structures typedef struct { uint8_t file_type[4]; /* file type */ uint8_t file_cr[4]; /* file creator */ @@ -437,69 +430,79 @@ typedef struct { uint8_t folderid[4]; /* putaway folder id */ } hfs_extendedfileinfo; -#define HFS_FILE_FLAG_LOCKED 0x0001 /* file is locked */ -#define HFS_FILE_FLAG_ATTR 0x0004 /* file has extended attributes */ -#define HFS_FILE_FLAG_ACL 0x0008 /* file has security data (ACLs) */ +/* Note that the file, folder, and thread structures all +* start with a 2-byte type field. */ -typedef struct { - uint8_t rec_type[2]; /* record type */ - uint8_t flags[2]; /* flags - reserved */ - uint8_t valence[4]; /* valence - items in this folder */ - uint8_t cnid[4]; /* catalog node id */ - uint8_t ctime[4]; /* create date */ - uint8_t cmtime[4]; /* content mod date */ - uint8_t amtime[4]; /* attribute mod date */ - uint8_t atime[4]; /* access date */ - uint8_t bkup_time[4]; /* backup time */ - hfs_access_perm perm; /* HFS permissions */ - hfs_fileinfo u_info; /* user info */ - hfs_extendedfileinfo f_info; /* finder info */ - uint8_t txt_enc[4]; /* text encoding */ - uint8_t res[4]; /* reserved */ -} hfs_folder; +// values for rec_type Record Type fields +#define HFS_FOLDER_RECORD 0x0001 +#define HFS_FILE_RECORD 0X0002 +#define HFS_FOLDER_THREAD 0x0003 +#define HFS_FILE_THREAD 0x0004 +// the start of the folder and file catalog entries are the same typedef struct { uint8_t rec_type[2]; /* record type */ - uint8_t flags[2]; - uint8_t res[4]; /* reserved */ - uint8_t cnid[4]; /* catalog node id */ - uint8_t ctime[4]; /* create date */ - uint8_t cmtime[4]; /* content modification date */ - uint8_t attr_mtime[4]; /* attribute mod date */ + uint8_t flags[2]; /* Flags (reserved (0) for folders) */ + uint8_t valence[4]; /* valence - items in this folder (folders only) */ + uint8_t cnid[4]; /* CNID of this file or folder */ + uint8_t crtime[4]; /* create date */ + uint8_t cmtime[4]; /* content modification date (m-time) */ + uint8_t amtime[4]; /* attribute mod date (c-time) */ uint8_t atime[4]; /* access date */ uint8_t bkup_date[4]; /* backup date */ hfs_access_perm perm; /* permissions */ - hfs_fileinfo u_info; /* user info */ + hfs_fileinfo u_info; /* user info (Used by Finder) */ hfs_extendedfileinfo f_info; /* finder info */ - uint8_t text_enc[4]; /* text encoding */ + uint8_t text_enc[4]; /* text encoding hint for file name */ uint8_t res2[4]; /* reserved 2 */ +} hfs_file_fold_std; + +// structure for folder data in catalog leaf records +typedef struct { + hfs_file_fold_std std; /* standard data that files and folders share */ +} hfs_folder; + +// value for flags in hfs_file +#define HFS_FILE_FLAG_LOCKED 0x0001 /* file is locked */ +#define HFS_FILE_FLAG_THREAD 0x0002 /* File has a thread entry */ + +// @@@ BC: I Can't find a reference to these values... +#define HFS_FILE_FLAG_ATTR 0x0004 /* file has extended attributes */ +#define HFS_FILE_FLAG_ACL 0x0008 /* file has security data (ACLs) */ + +// structure for file data in catalog leaf records +typedef struct { + hfs_file_fold_std std; /* standard data that files and folders share */ hfs_fork data; /* data fork */ hfs_fork resource; /* resource fork */ } hfs_file; -typedef union { - hfs_folder folder; - hfs_file file; -} hfs_file_folder; - +// structure for thread data in catalog leaf records typedef struct { - uint8_t record_type[2]; /* == kHFSPlusFolderThreadRecord or kHFSPlusFileThreadRecord */ - uint8_t reserved[2]; /* reserved - initialized as zero */ + uint8_t rec_type[2]; /* == kHFSPlusFolderThreadRecord or kHFSPlusFileThreadRecord */ + uint8_t res[2]; /* reserved - initialized as zero */ uint8_t parent_cnid[4]; /* parent ID for this catalog node */ hfs_uni_str name; /* name of this catalog node (variable length) */ } hfs_thread; + +// internally used structure to pass around both files and folders +typedef union { + hfs_folder folder; + hfs_file file; +} hfs_file_folder; + typedef struct { TSK_FS_INFO fs_info; /* SUPER CLASS */ - hfs_sb *fs; /* cached superblock */ + hfs_plus_vh *fs; /* cached superblock */ char is_case_sensitive; TSK_FS_FILE *blockmap_file; const TSK_FS_ATTR *blockmap_attr; char blockmap_cache[4096]; ///< Cache for blockmap - TSK_OFF_T blockmap_cache_start; ///< Byte offset of blockmap where cache starts + TSK_OFF_T blockmap_cache_start; ///< Byte offset of blockmap where cache starts size_t blockmap_cache_len; ///< Length of cache that is being used TSK_FS_FILE *catalog_file; @@ -537,7 +540,8 @@ typedef struct { extern uint8_t hfs_checked_read_random(TSK_FS_INFO *, char *, size_t, TSK_OFF_T); extern uint8_t hfs_uni2ascii(TSK_FS_INFO *, uint8_t *, int, char *, int); -extern int hfs_unicode_compare(HFS_INFO *, hfs_uni_str *, hfs_uni_str *); +extern int hfs_unicode_compare(HFS_INFO *, const hfs_uni_str *, + const hfs_uni_str *); extern uint16_t hfs_get_idxkeylen(HFS_INFO * hfs, uint16_t keylen, const hfs_btree_header_record * header); @@ -551,4 +555,17 @@ extern uint8_t hfs_jblk_walk(TSK_FS_INFO *, TSK_DADDR_T, TSK_DADDR_T, int, extern uint8_t hfs_jentry_walk(TSK_FS_INFO *, int, TSK_FS_JENTRY_WALK_CB, void *); + +typedef uint8_t(*TSK_HFS_BTREE_CB) (HFS_INFO *, int8_t level_type, + const void *targ_key, const hfs_btree_key_cat * cur_key, + TSK_OFF_T key_off, void *); +// return values for callback +#define HFS_BTREE_CB_GO 0 +#define HFS_BTREE_CB_STOP 1 +#define HFS_BTREE_CB_ERR 2 + +extern uint8_t hfs_cat_traverse(HFS_INFO * hfs, const void *targ_data, + TSK_HFS_BTREE_CB a_cb, void *ptr); + + #endif