From bd55294621fa96172624e2470d0a7f101c74bab9 Mon Sep 17 00:00:00 2001
From: Brian Carrier <carrier@sleuthkit.org>
Date: Wed, 22 Apr 2009 02:24:22 +0000
Subject: [PATCH] HFS updates that abstract catalog file parsing (and other
 changes)

---
 tsk3/fs/hfs.c            | 680 ++++++++++++++++++++++++---------------
 tsk3/fs/hfs_dent.c       | 438 ++++++++-----------------
 tsk3/fs/hfs_unicompare.c |  23 +-
 tsk3/fs/tsk_fs_i.h       |   2 +-
 tsk3/fs/tsk_hfs.h        | 139 ++++----
 5 files changed, 643 insertions(+), 639 deletions(-)

diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c
index 6d0f34191..a8fa3f470 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 76302c540..3c47724ea 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 e1881e8d3..08982bf43 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 d3017b419..71e4c7d7f 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 9430860dd..395e3aa7f 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
-- 
GitLab