diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c index 227e7d1b6e868554b62041e0220d5cf5a52bca34..e487848bb0b9afc63bf24c9a8187c9fddea5dde8 100644 --- a/tsk3/fs/hfs.c +++ b/tsk3/fs/hfs.c @@ -8,6 +8,8 @@ ** 14900 Conference Center Drive ** Chantilly, VA 20151 ** +** Copyright (c) 2009 Brian Carrier. All rights reserved. +** ** Judson Powers [jpowers@atc-nycorp.com] ** Copyright (c) 2008 ATC-NY. All rights reserved. ** This file contains data developed with support from the National @@ -136,7 +138,8 @@ cnid_to_array(uint32_t cnid, uint8_t array[4]) * fork = 0 and start_block = 0.) */ static int -hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, hfs_ext_key * key) +hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, + hfs_btree_key_ext * key) { TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); uint32_t key_cnid; @@ -164,8 +167,9 @@ hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, hfs_ext_key * key) return 1; } + /** \internal - * Returns the length of an HFS+ B-tree key based on the tree header + * Returns the length of an HFS+ B-tree INDEX key based on the tree header * structure and the length claimed in the record. With some trees, * the length given in the record is not used. * Note that this neither detects nor correctly handles 8-bit keys @@ -175,20 +179,20 @@ hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, hfs_ext_key * key) * @param header Tree header * @returns Length of key */ -static uint16_t -hfs_get_keylen(HFS_INFO * hfs, uint16_t keylen, - hfs_btree_header_record * header) +uint16_t +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 the flag is set, use the length given in the record - if (tsk_getu32(fs->endian, header->attr) & HFS_BT_VARKEYS) + if (tsk_getu32(fs->endian, header->attr) & HFS_BT_HEAD_ATTR_VARIDXKEYS) return keylen; else - return tsk_getu16(fs->endian, header->max_len); + 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. @@ -225,9 +229,9 @@ hfs_read_key(HFS_INFO * hfs, hfs_btree_header_record * header, 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_keylen(hfs, keylen, header); + keylen = hfs_get_idxkeylen(hfs, keylen, header); - if ((header && (keylen > tsk_getu16(fs->endian, header->max_len))) + 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, @@ -249,7 +253,7 @@ hfs_read_key(HFS_INFO * hfs, hfs_btree_header_record * header, return rec_off + 2 + keylen; /* return record data address */ } - +#endif /** @@ -375,7 +379,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, } /* start at root node */ - cur_node = tsk_getu32(fs->endian, hfs->extents_header.root); + 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 @@ -439,14 +443,14 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, /* With an index node, find the record with the largest key that is smaller * to or equal to cnid */ - if (node_desc->kind == HFS_BTREE_INDEX_NODE) { + if (node_desc->type == HFS_BT_NODE_TYPE_IDX) { uint32_t next_node = 0; int rec; for (rec = 0; rec < num_rec; rec++) { int cmp; size_t rec_off; - hfs_ext_key *key; + hfs_btree_key_ext *key; // get the record offset in the node rec_off = @@ -460,7 +464,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, free(node); return 1; } - key = (hfs_ext_key *) & node[rec_off]; + key = (hfs_btree_key_ext *) & node[rec_off]; cmp = hfs_ext_compare_keys(hfs, cnid, key); @@ -476,19 +480,20 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, /* save the info from this record unless it is bigger than cnid */ if ((cmp <= 0) || (next_node == 0)) { - int keylen = tsk_getu16(fs->endian, key->key_len); - if (rec_off + 2 + keylen > nodesize) { + int keylen = + hfs_get_idxkeylen(hfs, tsk_getu16(fs->endian, + key->key_len) + 2, &(hfs->extents_header)); + 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 index node %d too large (%zu vs %" PRIu16 ")", rec, cur_node, - rec_off + 2 + keylen, nodesize); + rec_off + keylen, nodesize); free(node); return 1; } next_node = - tsk_getu32(fs->endian, - &node[rec_off + 2 + keylen]); + tsk_getu32(fs->endian, &node[rec_off + keylen]); } else { // we are bigger than cnid, so move on to the next node @@ -509,12 +514,12 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, } /* with a leaf, we process until we are past cnid. We move right too if we can */ - else if (node_desc->kind == HFS_BTREE_LEAF_NODE) { + else if (node_desc->type == HFS_BT_NODE_TYPE_LEAF) { int rec; for (rec = 0; rec < num_rec; rec++) { size_t rec_off; - hfs_ext_key *key; + hfs_btree_key_ext *key; uint32_t rec_cnid; hfs_extents *extents; TSK_OFF_T ext_off = 0; @@ -533,7 +538,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, free(node); return 1; } - key = (hfs_ext_key *) & node[rec_off]; + key = (hfs_btree_key_ext *) & node[rec_off]; if (tsk_verbose) tsk_fprintf(stderr, @@ -602,7 +607,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid, snprintf(tsk_errstr, TSK_ERRSTR_L, "hfs_ext_find_extent_record: btree node %" PRIu32 " (%" PRIuOFF ") is neither index nor leaf (%" PRIu8 ")", - cur_node, cur_off, node_desc->kind); + cur_node, cur_off, node_desc->type); free(node); return 1; } @@ -620,8 +625,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_cat_key * key1, - hfs_cat_key * key2) +hfs_cat_compare_keys(HFS_INFO * hfs, hfs_btree_key_cat * key1, + hfs_btree_key_cat * key2) { TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); uint32_t cnid1, cnid2; @@ -649,7 +654,7 @@ hfs_cat_compare_keys(HFS_INFO * hfs, hfs_cat_key * key1, * 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_cat_key * needle) +hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_btree_key_cat * needle) { TSK_FS_INFO *fs = &(hfs->fs_info); uint32_t cur_node; /* node id of the current node */ @@ -665,7 +670,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) return 0; /* start at root node */ - cur_node = tsk_getu32(fs->endian, hfs->catalog_header.root); + 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 @@ -728,13 +733,13 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) /* With an index node, find the record with the largest key that is smaller * to or equal to cnid */ - if (node_desc->kind == HFS_BTREE_INDEX_NODE) { + 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_cat_key *key; + hfs_btree_key_cat *key; // get the record offset in the node rec_off = @@ -748,7 +753,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) free(node); return 1; } - key = (hfs_cat_key *) & node[rec_off]; + key = (hfs_btree_key_cat *) & node[rec_off]; /* if (tsk_verbose) @@ -762,7 +767,9 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) /* save the info from this record unless it is bigger than cnid */ if ((hfs_cat_compare_keys(hfs, key, needle) <= 0) || (next_node == 0)) { - int keylen = tsk_getu16(fs->endian, key->key_len) + 2; + 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, @@ -793,12 +800,12 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) } /* With a leaf, we look for the specific record. */ - else if (node_desc->kind == HFS_BTREE_LEAF_NODE) { + else if (node_desc->type == HFS_BT_NODE_TYPE_LEAF) { int rec; for (rec = 0; rec < num_rec; rec++) { size_t rec_off; - hfs_cat_key *key; + hfs_btree_key_cat *key; size_t rec_off2; int diff; @@ -814,7 +821,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) free(node); return 0; } - key = (hfs_cat_key *) & node[rec_off]; + key = (hfs_btree_key_cat *) & node[rec_off]; /* if (tsk_verbose) @@ -856,7 +863,7 @@ hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle) snprintf(tsk_errstr, TSK_ERRSTR_L, "hfs_cat_get_record_offset: btree node %" PRIu32 " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")", - cur_node, cur_off, node_desc->kind); + cur_node, cur_off, node_desc->type); free(node); return 0; } @@ -1019,7 +1026,7 @@ static uint8_t hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) { TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); - hfs_cat_key key; /* current catalog key */ + hfs_btree_key_cat key; /* current catalog key */ hfs_thread thread; /* thread record */ hfs_file_folder record; /* file/folder record */ TSK_OFF_T off; @@ -1046,7 +1053,7 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) /* 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_cat_key)); + memset((char *) &key, 0, sizeof(hfs_btree_key_cat)); cnid_to_array((uint32_t) inum, key.parent_cnid); /* look up the thread record */ @@ -1067,7 +1074,7 @@ hfs_cat_file_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry) /* now look up the actual file/folder record */ /* build key */ - memset((char *) &key, 0, sizeof(hfs_cat_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)); @@ -1128,15 +1135,14 @@ hfs_find_highest_inum(HFS_INFO * hfs) { // @@@ get actual number from Catalog file (go to far right) /* I haven't gotten looking at the end of the Catalog B-Tree to work - properly. A fast method: if HFS_BIT_VOLUME_CNIDS_REUSED is set, then + 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 supposed to be larger than all CNIDs on disk. */ TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info); - if (tsk_getu32(fs->endian, - hfs->fs->attr) & HFS_BIT_VOLUME_CNIDS_REUSED) + if (tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_CNIDS_REUSED) return (TSK_INUM_T) 0xffffffff; else return (TSK_INUM_T) tsk_getu32(fs->endian, @@ -2291,9 +2297,9 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) tsk_fprintf(hFile, "--------------------------------------------\n"); tsk_fprintf(hFile, "File System Signature: "); - if (tsk_getu16(fs->endian, hfs->fs->signature) == HFSPLUS_MAGIC) + 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) == HFSX_MAGIC) + else if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFSX) tsk_fprintf(hFile, "HFSX\n"); else tsk_fprintf(hFile, "Unknown\n"); @@ -2326,33 +2332,30 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) tsk_fprintf(hFile, "\n"); tsk_fprintf(hFile, "Volume Identifier: %08" PRIx32 "%08" PRIx32 "\n", - tsk_getu32(fs->endian, &(sb->finder_info[24])), - tsk_getu32(fs->endian, &(sb->finder_info[28]))); + tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_ID1]), + tsk_getu32(fs->endian, sb->finder_info[HFS_VH_FI_ID2])); // print last mounted info tsk_fprintf(hFile, "\nLast Mounted By: "); - if (tsk_getu32(fs->endian, sb->last_mnt_ver) == HFSPLUS_MOUNT_VERSION) + 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) == HFSJ_MOUNT_VERSION) + 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) == FSK_MOUNT_VERSION) + 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) == FSCK_MOUNT_VERSION) + 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) == OS89_MOUNT_VERSION) + 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_BIT_VOLUME_UNMOUNTED) + if ((tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_UNMOUNTED) && (!(tsk_getu32(fs->endian, - hfs->fs->attr) & HFS_BIT_VOLUME_INCONSISTENT))) + hfs->fs->attr) & HFS_VH_ATTR_INCONSISTENT))) tsk_fprintf(hFile, "Volume Unmounted Properly\n"); else tsk_fprintf(hFile, "Volume Unmounted Improperly\n"); @@ -2362,7 +2365,7 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) // Dates - mac_time = hfs2unixtime(tsk_getu32(fs->endian, hfs->fs->c_date)); + mac_time = hfs2unixtime(tsk_getu32(fs->endian, hfs->fs->cr_date)); tsk_fprintf(hFile, "\nCreation Date: \t%s", ctime(&mac_time)); mac_time = hfs2unixtime(tsk_getu32(fs->endian, hfs->fs->m_date)); @@ -2375,12 +2378,11 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) tsk_fprintf(hFile, "Last Checked Date: \t%s", ctime(&mac_time)); - if (tsk_getu32(fs->endian, - hfs->fs->attr) & HFS_BIT_VOLUME_SOFTWARE_LOCK) + 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_BIT_VOLUME_JOURNALED) { + 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)); } @@ -2391,32 +2393,32 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) tsk_fprintf(hFile, "Range: %" PRIuINUM " - %" PRIuINUM "\n", fs->first_inum, fs->last_inum); - inode = tsk_getu32(fs->endian, &(sb->finder_info[0])); + 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[4])); + 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[8])); + 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[12])); + 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[20])); + 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) @@ -2445,7 +2447,7 @@ hfs_fsstat(TSK_FS_INFO * fs, FILE * hFile) 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_BIT_VOLUME_BADBLOCKS) + if (tsk_getu32(fs->endian, hfs->fs->attr) & HFS_VH_ATTR_BADBLOCKS) tsk_fprintf(hFile, "Volume has bad blocks\n"); return 0; @@ -2531,6 +2533,8 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum, if (hfs_cat_file_lookup(hfs, inum, &entry) == 0) { tsk_fprintf(hFile, "\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", @@ -2572,6 +2576,7 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum, entry.cat.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)); @@ -2711,7 +2716,7 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, } if (hfs_checked_read_random(fs, (char *) hfs->fs, len, - (TSK_OFF_T) HFS_SBOFF)) { + (TSK_OFF_T) HFS_VH_OFF)) { snprintf(tsk_errstr2, TSK_ERRSTR_L, "hfs_open: superblock"); fs->tag = 0; free(hfs->fs); @@ -2722,9 +2727,9 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, /* * Verify we are looking at an HFS+ image */ - if (tsk_fs_guessu16(fs, hfs->fs->signature, HFSPLUS_MAGIC) && - tsk_fs_guessu16(fs, hfs->fs->signature, HFSX_MAGIC) && - tsk_fs_guessu16(fs, hfs->fs->signature, HFS_MAGIC)) { + if (tsk_fs_guessu16(fs, hfs->fs->signature, HFS_VH_SIG_HFSPLUS) && + tsk_fs_guessu16(fs, hfs->fs->signature, HFS_VH_SIG_HFSX) && + tsk_fs_guessu16(fs, hfs->fs->signature, HFS_VH_SIG_HFS)) { fs->tag = 0; free(hfs->fs); @@ -2738,14 +2743,14 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, /* * Handle an HFS-wrapped HFS+ image */ - if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_MAGIC) { + if (tsk_getu16(fs->endian, hfs->fs->signature) == HFS_VH_SIG_HFS) { hfs_wrapper_sb *wrapper_sb = (hfs_wrapper_sb *) hfs->fs; if ((tsk_getu16(fs->endian, - wrapper_sb->drEmbedSigWord) == HFSPLUS_MAGIC) + wrapper_sb->drEmbedSigWord) == HFS_VH_SIG_HFSPLUS) || (tsk_getu16(fs->endian, - wrapper_sb->drEmbedSigWord) == HFSX_MAGIC)) { + wrapper_sb->drEmbedSigWord) == HFS_VH_SIG_HFSX)) { TSK_FS_INFO *fs_info2; uint16_t drAlBlSt = @@ -2872,17 +2877,17 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, return NULL; } - if (tsk_getu16(fs->endian, hfs->fs->version) == 4) + if (tsk_getu16(fs->endian, hfs->fs->version) == HFS_VH_VER_HFSPLUS) hfs->is_case_sensitive = 0; - else if (tsk_getu16(fs->endian, hfs->fs->version) == 5) { - if (hfs->catalog_header.k_type == 0xcf) + else if (tsk_getu16(fs->endian, hfs->fs->version) == HFS_VH_VER_HFSX) { + if (hfs->catalog_header.compType == HFS_BT_HEAD_COMP_SENS) hfs->is_case_sensitive = 0; - else if (hfs->catalog_header.k_type == 0xbc) + else if (hfs->catalog_header.compType == HFS_BT_HEAD_COMP_INSENS) hfs->is_case_sensitive = 1; else { tsk_fprintf(stderr, "hfs_open: invalid value (0x%02" PRIx8 - ") for key compare type\n", hfs->catalog_header.k_type); + ") for key compare type\n", hfs->catalog_header.compType); hfs->is_case_sensitive = 0; } } @@ -2898,8 +2903,8 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset, fs->inum_count = fs->last_inum + 1; snprintf((char *) fs->fs_id, 17, "%08" PRIx32 "%08" PRIx32, - tsk_getu32(fs->endian, &(hfs->fs->finder_info[24])), - tsk_getu32(fs->endian, &(hfs->fs->finder_info[28]))); + tsk_getu32(fs->endian, hfs->fs->finder_info[HFS_VH_FI_ID1]), + tsk_getu32(fs->endian, hfs->fs->finder_info[HFS_VH_FI_ID2])); fs->fs_id_used = 16; /* journal */ diff --git a/tsk3/fs/hfs_dent.c b/tsk3/fs/hfs_dent.c index d9aaa666c137d511bc8cdcbbb76248c2764367ad..76302c540639c2f3570cbbe21f8b41efa89d5b10 100644 --- a/tsk3/fs/hfs_dent.c +++ b/tsk3/fs/hfs_dent.c @@ -8,6 +8,8 @@ ** 14900 Conference Center Drive ** Chantilly, VA 20151 ** +** Copyright (c) 2009 Brian Carrier. All rights reserved. +** ** Judson Powers [jpowers@atc-nycorp.com] ** Copyright (c) 2008 ATC-NY. All rights reserved. ** This file contains data developed with support from the National @@ -298,7 +300,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, } /* start at root node */ - cur_node = tsk_getu32(fs->endian, hfs->catalog_header.root); + 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 @@ -364,13 +366,13 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, /* With an index node, find the record with the largest key that is smaller * to or equal to cnid */ - if (node_desc->kind == HFS_BTREE_INDEX_NODE) { + 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_cat_key *key; + hfs_btree_key_cat *key; // get the record offset in the node rec_off = @@ -385,7 +387,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, free(node); return TSK_COR; } - key = (hfs_cat_key *) & node[rec_off]; + key = (hfs_btree_key_cat *) & node[rec_off]; if (tsk_verbose) tsk_fprintf(stderr, @@ -397,7 +399,9 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, /* 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 = tsk_getu16(fs->endian, key->key_len) + 2; + 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, @@ -431,12 +435,12 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, } /* with a leaf, we process until we are past cnid. We move right too if we can */ - else if (node_desc->kind == HFS_BTREE_LEAF_NODE) { + else if (node_desc->type == HFS_BT_NODE_TYPE_LEAF) { int rec; for (rec = 0; rec < num_rec; rec++) { size_t rec_off; - hfs_cat_key *key; + hfs_btree_key_cat *key; uint16_t rec_type; size_t rec_off2; @@ -453,7 +457,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, free(node); return TSK_COR; } - key = (hfs_cat_key *) & node[rec_off]; + key = (hfs_btree_key_cat *) & node[rec_off]; if (tsk_verbose) tsk_fprintf(stderr, @@ -570,7 +574,7 @@ hfs_dir_open_meta(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir, 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->kind); + cur_node, cur_off, node_desc->type); tsk_fs_name_free(fs_name); free(node); diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h index f2d2861c8e8d0ecda7797c77132f68f56f3734e3..355e0931a1c582ad382004c2da5f6faf855c22b7 100644 --- a/tsk3/fs/tsk_hfs.h +++ b/tsk3/fs/tsk_hfs.h @@ -8,6 +8,8 @@ ** 14900 Conference Center Drive ** Chantilly, VA 20151 ** +** Copyright (c) 2009 Brian Carrier. All rights reserved. +** ** Judson Powers [jpowers@atc-nycorp.com] ** Copyright (c) 2008 ATC-NY. All rights reserved. ** This file contains data developed with support from the National @@ -80,27 +82,8 @@ * Constants */ -#define HFS_MAGIC 0x4244 /* BD in big endian */ -#define HFSPLUS_MAGIC 0x482b /* H+ in big endian */ -#define HFSX_MAGIC 0x4858 /* HX in big endian */ - -#define HFSPLUS_VERSION 0x0004 /* all HFS+ volumes are version 4 */ -#define HFSX_VERSION 0x0005 /* HFSX volumes start with version 5 */ - -#define HFSPLUS_MOUNT_VERSION 0x31302e30 /* '10.0' for Mac OS X */ -#define HFSJ_MOUNT_VERSION 0x4846534a /* 'HFSJ' for journaled HFS+ on Mac OS X */ -#define FSK_MOUNT_VERSION 0x46534b21 /* 'FSK!' for failed journal replay */ -#define FSCK_MOUNT_VERSION 0x6673636b /* 'fsck' for fsck_hfs */ -#define OS89_MOUNT_VERSION 0x382e3130 /* '8.10' for Mac OS 8.1-9.2.2 */ - -#define HFS_SBOFF 1024 #define HFS_FILE_CONTENT_LEN 160 // size of two hfs_fork data structures -/* b-tree kind types */ -#define HFS_BTREE_LEAF_NODE -1 -#define HFS_BTREE_INDEX_NODE 0 -#define HFS_BTREE_HEADER_NODE 1 -#define HFS_BTREE_MAP_NODE 2 #define HFS_MAXNAMLEN 765 /* maximum HFS+ name length in bytes, when encoded in UTF8, not including terminating null */ @@ -118,19 +101,7 @@ */ #define NSEC_BTWN_1904_1970 (uint32_t) 2082844800U -#define HFS_BIT_VOLUME_UNMOUNTED (uint32_t)(1 << 8) /* set if the volume was unmounted properly; as per TN 1150, modern Macintosh OSes always leave this bit set for the boot volume */ -#define HFS_BIT_VOLUME_BADBLOCKS (uint32_t)(1 << 9) /* set if there are any bad blocks for this volume (in the Extents B-tree) */ -#define HFS_BIT_VOLUME_INCONSISTENT (uint32_t)(1 << 11) /* cleared if the volume was unmounted properly */ -#define HFS_BIT_VOLUME_CNIDS_REUSED (uint32_t)(1 << 12) /* set if CNIDs have wrapped around past the maximum value and are being reused; in this case, there are CNIDs on the disk larger than the nextCatalogId field */ -#define HFS_BIT_VOLUME_JOURNALED (uint32_t)(1 << 13) -#define HFS_BIT_VOLUME_SOFTWARE_LOCK (uint32_t)(1 << 14) /* set if volume should be write-protected in software */ -/* constants for BTree header record attributes */ -#define HFS_BT_BIGKEYS 0x00000002 /* kBTBigKeysMask : key length field is 16 bits */ -// NOTE: HFS_BT_BIGKEYS must be set for all HFS+ BTrees -#define HFS_BT_VARKEYS 0x00000004 /* kBTVariableIndexKeysMask : keys in index nodes are variable length */ -// NOTE: this bit is required to be set for the Catalog B-tree and cleared -// for the Extents B-tree /* predefined files */ #define HFS_ROOT_PARENT_ID 1 @@ -182,9 +153,9 @@ typedef struct { uint8_t o_flags; /* owner flags */ uint8_t mode[2]; /* file mode */ union { - uint8_t inum[4]; /* inode number */ - uint8_t nlink[4]; /* link count */ - uint8_t raw[4]; /* raw device */ + uint8_t inum[4]; /* inode number (for hard link files) */ + uint8_t nlink[4]; /* link count (for direct node files) */ + uint8_t raw[4]; /* device id (for block and char device files) */ } special; } hfs_access_perm; @@ -223,15 +194,62 @@ typedef struct { hfs_ext_desc extents[8]; } hfs_extents; -/* fork data structure */ +/* Fork data structure. This is used in both the volume header and catalog tree. */ typedef struct { uint8_t logic_sz[8]; /* The size (in bytes) of the fork */ - uint8_t clmp_sz[4]; /* For forks in volume header, clump size. For + uint8_t clmp_sz[4]; /* For "special files" in volume header, clump size. For * catalog files, this is number of blocks read or not used. */ uint8_t total_blk[4]; /* total blocks in all extents of the fork */ hfs_ext_desc extents[8]; } hfs_fork; + + +/**************************************************** + * Super block / volume header + */ +#define HFS_VH_OFF 1024 // byte offset in volume to volume header + +// signature values +#define HFS_VH_SIG_HFS 0x4244 /* BD in big endian */ +#define HFS_VH_SIG_HFSPLUS 0x482b /* H+ in big endian */ +#define HFS_VH_SIG_HFSX 0x4858 /* HX in big endian */ + +// version values +#define HFS_VH_VER_HFSPLUS 0x0004 /* all HFS+ volumes are version 4 */ +#define HFS_VH_VER_HFSX 0x0005 /* HFSX volumes start with version 5 */ + +// attr values ( +// bits 0 to 7 are reserved +#define HFS_VH_ATTR_UNMOUNTED (uint32_t)(1<<8) /* set if the volume was unmounted properly; as per TN 1150, modern Macintosh OSes always leave this bit set for the boot volume */ +#define HFS_VH_ATTR_BADBLOCKS (uint32_t)(1<<9) /* set if there are any bad blocks for this volume (in the Extents B-tree) */ +#define HFS_VH_ATTR_NOCACHE (uint32_t)(1<<10) /* set if volume should not be cached */ +#define HFS_VH_ATTR_INCONSISTENT (uint32_t)(1<<11) /* cleared if the volume was unmounted properly */ +#define HFS_VH_ATTR_CNIDS_REUSED (uint32_t)(1<<12) /* set if CNIDs have wrapped around past the maximum value and are being reused; in this case, there are CNIDs on the disk larger than the nextCatalogId field */ +#define HFS_VH_ATTR_JOURNALED (uint32_t)(1<<13) +// 14 is reserved +#define HFS_VH_ATTR_SOFTWARE_LOCK (uint32_t)(1 << 15) /* set if volume should be write-protected in software */ +// 16 to 31 are reserved + + +// last_mnt_ver values +#define HFS_VH_MVER_HFSPLUS 0x31302e30 /* '10.0' for Mac OS X */ +#define HFS_VH_MVER_HFSJ 0x4846534a /* 'HFSJ' for journaled HFS+ on Mac OS X */ +#define HFS_VH_MVER_FSK 0x46534b21 /* 'FSK!' for failed journal replay */ +#define HFS_VH_MVER_FSCK 0x6673636b /* 'fsck' for fsck_hfs */ +#define HFS_VH_MVER_OS89 0x382e3130 /* '8.10' for Mac OS 8.1-9.2.2 */ + +/* Index values for finder_info array */ +#define HFS_VH_FI_BOOT 0 /*Directory ID of bootable directory */ +#define HFS_VH_FI_START 1 /* Parent dir ID of startup app */ +#define HFS_VH_FI_OPEN 2 /* Directory to open when volume is mounted */ +#define HFS_VH_FI_BOOT9 3 /* Directory ID of OS 8 or 9 bootable sys folder */ +#define HFS_VH_FI_RESV1 4 +#define HFS_VH_FI_BOOTX 5 /* Directory ID of OS X bootable system (CoreServices dir) */ +#define HFS_VH_FI_ID1 6 /* OS X Volume ID part 1 */ +#define HFS_VH_FI_ID2 7 /* OS X Volume ID part 2 */ + + /* ** HFS+/HFSX Super Block */ @@ -241,23 +259,30 @@ typedef struct { uint8_t attr[4]; /* volume attributes */ uint8_t last_mnt_ver[4]; /* last mounted version */ uint8_t jinfo_blk[4]; /* journal info block */ - uint8_t c_date[4]; /* volume creation date */ - uint8_t m_date[4]; /* volume last modified date */ - uint8_t bkup_date[4]; /* volume last backup date */ - uint8_t chk_date[4]; /* date of last consistency check */ - uint8_t file_cnt[4]; /* number of files on volume */ - uint8_t fldr_cnt[4]; /* number of folders on volume */ - uint8_t blk_sz[4]; /* allocation block size */ + + uint8_t cr_date[4]; /* volume creation date (NOT in GMT) */ + uint8_t m_date[4]; /* volume last modified date (GMT) */ + uint8_t bkup_date[4]; /* volume last backup date (GMT) */ + uint8_t chk_date[4]; /* date of last consistency check (GMT) */ + + uint8_t file_cnt[4]; /* number of files on volume (not incl. special files) */ + uint8_t fldr_cnt[4]; /* number of folders on volume (not incl. root dir) */ + + uint8_t blk_sz[4]; /* allocation block size (in bytes) */ uint8_t blk_cnt[4]; /* number of blocks on disk */ uint8_t free_blks[4]; /* unused block count */ - uint8_t next_alloc[4]; /* start of next allocation search */ - uint8_t rsrc_clmp_sz[4]; /* default clump size for resource forks */ - uint8_t data_clmp_sz[4]; /* default clump size for data forks */ - uint8_t next_cat_id[4]; /* next catalog id */ - uint8_t write_cnt[4]; /* write count */ - uint8_t enc_bmp[8]; /* encoding bitmap */ - uint8_t finder_info[32]; - hfs_fork alloc_file; /* location and size of allocation file */ + + uint8_t next_alloc[4]; /* block addr to start allocation search from */ + uint8_t rsrc_clmp_sz[4]; /* default clump size for resource forks (in bytes) */ + uint8_t data_clmp_sz[4]; /* default clump size for data forks (in bytes) */ + uint8_t next_cat_id[4]; /* next catalog id for allocation */ + + uint8_t write_cnt[4]; /* write count: incremented each time it is mounted and modified */ + uint8_t enc_bmp[8]; /* encoding bitmap (identifies which encodings were used in FS) */ + + uint8_t finder_info[8][4]; /* Special finder details */ + + hfs_fork alloc_file; /* location and size of allocation bitmap file */ hfs_fork ext_file; /* location and size of extents file */ hfs_fork cat_file; /* location and size of catalog file */ hfs_fork attr_file; /* location and size of attributes file */ @@ -300,19 +325,82 @@ typedef struct { uint8_t drCTExtRec[12]; /* extent record with size and location of catalog file */ } hfs_wrapper_sb; + + +/********* B-Tree data structures **********/ + +/* Node descriptor that starts each node in a B-tree */ +// type values +#define HFS_BT_NODE_TYPE_LEAF -1 +#define HFS_BT_NODE_TYPE_IDX 0 +#define HFS_BT_NODE_TYPE_HEAD 1 +#define HFS_BT_NODE_TYPE_MAP 2 + +// header that starts every B-tree node +typedef struct { + uint8_t flink[4]; /* node num of next node of same type */ + uint8_t blink[4]; /* node num of prev node of same type */ + int8_t type; /* type of this node */ + uint8_t height; /* level in B-tree (0 for root, 1 for leaf) */ + uint8_t num_rec[2]; /* number of records this node */ + uint8_t res[2]; /* reserved */ +} hfs_btree_node; + +/*****************/ +// structure for the 1st record in the B-Tree header node + +// type values +#define HFS_BT_HEAD_TYPE_CNTL 0 // control file (catalog, extents, attributes) +#define HFS_BT_HEAD_TYPE_USER 128 // hot file +#define HFS_BT_HEAD_TYPE_RSV 255 + +// compType values +#define HFS_BT_HEAD_COMP_SENS 0xCF // case sensitive +#define HFS_BT_HEAD_COMP_INSENS 0xBC // case insensitive + +// attr values +#define HFS_BT_HEAD_ATTR_BIGKEYS 0x00000002 /* key length field is 16 bits (req'd for HFS+) */ +#define HFS_BT_HEAD_ATTR_VARIDXKEYS 0x00000004 /* Keys in INDEX nodes are variable length */ +// NOTE: VARIDXKEYS is required for the Catalog B-tree and cleared for the Extents B-tree + typedef struct { - uint8_t key_len[2]; + uint8_t depth[2]; /* current depth of btree */ + uint8_t rootNode[4]; /* node number of root node */ + uint8_t leafRecords[4]; /* number of records in leaf nodes */ + uint8_t firstLeafNode[4]; /* number of first leaf node (0 if no leafs) */ + uint8_t lastLeafNode[4]; /* number of last leaf node (0 if no leafs) */ + uint8_t nodesize[2]; /* byte size of each node (512..32768) */ + uint8_t maxKeyLen[2]; /* max key length in an index or leaf node */ + uint8_t totalNodes[4]; /* number of nodes in btree (free or in use) */ + uint8_t freeNodes[4]; /* unused nodes in btree */ + uint8_t res[2]; /* reserved */ + uint8_t clumpSize[4]; /* clump size */ + uint8_t type; /* btree type (control or user) */ + uint8_t compType; /* HFSX Only: identifies of key comparisons are case sensitive */ + uint8_t attr[4]; /* attributes */ + uint8_t res2[64]; /* reserved */ +} hfs_btree_header_record; + +/* key for category records */ +typedef struct { + uint8_t key_len[2]; // length of key minus 2 uint8_t parent_cnid[4]; hfs_uni_str name; -} hfs_cat_key; +} hfs_btree_key_cat; +/* Key for extents records */ typedef struct { - uint8_t key_len[2]; + 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]; -} hfs_ext_key; +} hfs_btree_key_ext; + +/* Record contents for index record after key */ +typedef struct { + uint8_t childNode[4]; +} hfs_btree_index_record; typedef struct { uint32_t inum; /* inode number */ @@ -321,32 +409,8 @@ typedef struct { TSK_DADDR_T offs; /* offset of beginning of inode */ } htsk_fs_inode_mode_struct; -typedef struct { - uint8_t flink[4]; /* next node number */ - uint8_t blink[4]; /* previous node number */ - int8_t kind; /* type of node */ - uint8_t height; /* level in B-tree */ - uint8_t num_rec[2]; /* number of records this node */ - uint8_t res[2]; /* reserved */ -} hfs_btree_node; -typedef struct { - uint8_t depth[2]; /* current depth of btree */ - uint8_t root[4]; /* node number of root node */ - uint8_t leaf[4]; /* number of records in leaf nodes */ - uint8_t firstleaf[4]; /* number of first leaf node */ - uint8_t lastleaf[4]; /* number of last leaf node */ - uint8_t nodesize[2]; /* byte size of leaf node (512..32768) */ - uint8_t max_len[2]; /* max key length in an index or leaf node */ - uint8_t total[4]; /* number of nodes in btree (free or in use) */ - uint8_t free[4]; /* unused nodes in btree */ - uint8_t res[2]; /* reserved */ - uint8_t clmp_sz[4]; /* clump size */ - uint8_t bt_type; /* btree type */ - uint8_t k_type; /* key compare type */ - uint8_t attr[4]; /* attributes */ - uint8_t res2[64]; /* reserved */ -} hfs_btree_header_record; + typedef struct { int8_t v[2]; @@ -474,6 +538,9 @@ 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 uint16_t hfs_get_idxkeylen(HFS_INFO * hfs, uint16_t keylen, + const hfs_btree_header_record * header); + extern TSK_RETVAL_ENUM hfs_dir_open_meta(TSK_FS_INFO *, TSK_FS_DIR **, TSK_INUM_T);