diff --git a/tsk3/fs/hfs.c b/tsk3/fs/hfs.c
index bcd3cd3163afefb9d5d143f2916ba573eb011d17..7507b2a6cc037720f355926b5df1ecc2a0b87516 100644
--- a/tsk3/fs/hfs.c
+++ b/tsk3/fs/hfs.c
@@ -107,51 +107,28 @@ hfs2unixtime(uint32_t hfsdate)
 }
 
 
+/**
+ * Convert a cnid (metadata address) to big endian array.
+ * This is used to create the key for tree lookups.
+ * @param cnid Metadata address to convert
+ * @param array [out] Array to write data into.
+ */
+static void 
+cnid_to_array(uint32_t cnid, uint8_t array[4])
+{
+    array[3] = (cnid >> 0) & 0xff;
+    array[2] = (cnid >> 8) & 0xff;
+    array[1] = (cnid >> 16) & 0xff;
+    array[0] = (cnid >> 24) & 0xff;
+}
+
 /**********************************************************************
  *
  * Lookup Functions
  *
  **********************************************************************/
 
-static int hfs_load_blockmap(HFS_INFO *);
 
-/** \internal
- * Get allocation status of file system block.
- * adapted from IsAllocationBlockUsed from:
- * http://developer.apple.com/technotes/tn/tn1150.html
- *
- * @param hfs File system being analyzed
- * @param b Block address 
- * @returns 1 if allocated, 0 if not, -1 on error
- */
-static int8_t
-hfs_is_block_alloc(HFS_INFO * hfs, TSK_DADDR_T b)
-{
-    TSK_DADDR_T a;
-    uint8_t this_byte;
-
-    // lazy loading of block map
-    if (hfs->block_map == NULL) {
-        if (hfs_load_blockmap(hfs)) {
-            // @@@ FIX error
-            tsk_fprintf(stderr,
-                "ERROR hfs_is_block_alloc: failed to load block map\n");
-            return -1;
-        }
-    }
-
-    a = b / 8;
-    if (a > hfs->block_map_size) {
-        // @@@ FIX error
-        tsk_fprintf(stderr,
-            "WARNING hfs_is_block_alloc: block %" PRIuDADDR
-            " is past the end of the allocation file\n", b);
-        return -1;
-    }
-
-    this_byte = hfs->block_map[a];
-    return (this_byte & (1 << (7 - (b % 8)))) != 0;
-}
 
 /* Compares the given HFS+ Extents B-tree key to key constructed
  * for finding the beginning of the data fork extents for the given
@@ -159,7 +136,7 @@ hfs_is_block_alloc(HFS_INFO * hfs, TSK_DADDR_T b)
  * fork = 0 and start_block = 0.)
  */
 static int
-hfs_compare_extent_keys(HFS_INFO * hfs, uint32_t cnid, hfs_ext_key * key)
+hfs_ext_compare_keys(HFS_INFO * hfs, uint32_t cnid, hfs_ext_key * key)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
     uint32_t key_cnid;
@@ -493,7 +470,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
 
     hfs_btree_header_record header;     /* header for the Extents btree */
-    uint16_t leafsize;          /* size of nodes (all, regardless of the name) */
+    uint16_t nodesize;          /* size of nodes (all, regardless of the name) */
 
     uint32_t cur_node;          /* node id of the current node */
 
@@ -544,7 +521,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
             free(out_ext);
         return NULL;
     }
-    leafsize = tsk_getu16(fs->endian, header.nodesize);
+    nodesize = tsk_getu16(fs->endian, header.nodesize);
 
     /* start at root node */
     cur_node = tsk_getu32(fs->endian, header.root);
@@ -562,8 +539,8 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
 
     if (tsk_verbose)
         tsk_fprintf(stderr, "hfs_ext_find_extent_record: starting at "
-            "root node %" PRIu32 "; header @ %" PRIuOFF "; leafsize = %"
-            PRIu16 "\n", cur_node, off, leafsize);
+            "root node %" PRIu32 "; header @ %" PRIuOFF "; nodesize = %"
+            PRIu16 "\n", cur_node, off, nodesize);
 
     while (1) {
         TSK_OFF_T cur_off;      /* start address of cur_node */
@@ -620,7 +597,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
             int cmp;
 
             // get the record offset
-            addr = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
+            addr = hfs_get_bt_rec_off(hfs, cur_off, nodesize, rec);
             if (addr == 0) {
                 snprintf(tsk_errstr2, TSK_ERRSTR_L,
                     "hfs_ext_find_extent_record: finding record %" PRIu16
@@ -642,7 +619,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
                     free(out_ext);
                 return NULL;
             }
-            cmp = hfs_compare_extent_keys(hfs, cnid, &key);
+            cmp = hfs_ext_compare_keys(hfs, cnid, &key);
 
             if (tsk_verbose)
                 tsk_fprintf(stderr,
@@ -680,7 +657,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
             rec = recno;        /* using rec as our counting variable again, for kicks */
 
             /* reget key */
-            addr = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
+            addr = hfs_get_bt_rec_off(hfs, cur_off, nodesize, rec);
             if (addr == 0) {
                 snprintf(tsk_errstr2, TSK_ERRSTR_L,
                     "hfs_ext_find_extent_record: finding record %" PRIu16
@@ -736,7 +713,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
                 }
 
                 /* load new key data, since I'm about to use it */
-                addr = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
+                addr = hfs_get_bt_rec_off(hfs, cur_off, nodesize, rec);
                 if (addr == 0) {
                     snprintf(tsk_errstr2, TSK_ERRSTR_L,
                         "hfs_ext_find_extent_record: finding record %"
@@ -825,7 +802,7 @@ hfs_ext_find_extent_record(HFS_INFO * hfs, uint32_t cnid,
                 }
 
                 /* load new key data */
-                addr = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
+                addr = hfs_get_bt_rec_off(hfs, cur_off, nodesize, rec);
                 if (addr == 0) {
                     snprintf(tsk_errstr2, TSK_ERRSTR_L,
                         "hfs_ext_find_extent_record: finding record %"
@@ -1029,7 +1006,7 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
                     // @@@ ERROR
                 }
                 key = (hfs_ext_key *) & node[rec_off];
-                cmp = hfs_compare_extent_keys(hfs, cnid, key);
+                cmp = hfs_ext_compare_keys(hfs, cnid, key);
 
                 if (tsk_verbose)
                     tsk_fprintf(stderr,
@@ -1128,169 +1105,6 @@ hfs_ext_find_extent_record_attr(HFS_INFO * hfs, uint32_t cnid,
 }
 
 
-/* return the offset into the image that catalog btree node 'node' is at */
-/* returns 0 on failure; may set up to error string 1 */
-TSK_OFF_T
-hfs_cat_find_node_offset(HFS_INFO * hfs, uint32_t nodenum)
-{
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
-    uint16_t nodesize;          /* size of each node */
-    int i;
-    uint64_t bytes;             /* bytes left this extent */
-    TSK_OFF_T r_offs;           /* offset we are reading from */
-    TSK_OFF_T f_offs;           /* offset into the catalog file */
-    TSK_OFF_T n_offs;           /* offset of the node we are looking for */
-    hfs_ext_desc *extents;
-
-    if (tsk_verbose)
-        tsk_fprintf(stderr, "hfs_cat_find_node_offset: finding offset of "
-            "btree node: %" PRIu32 "\n", nodenum);
-
-    extents = hfs->cat_extents;
-
-    /* find first extent with data in it */
-    /* as above, holdover from previous code */
-    i = 0;
-    while (!(tsk_getu32(fs->endian, extents[i].blk_cnt)))
-        i++;
-
-    if (i > 7) {
-        tsk_errno = TSK_ERR_FS_GENFS;
-        snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "hfs_cat_find_node_offset: no data found in catalog file extents");
-        return 0;
-    }
-
-    bytes =
-        tsk_getu32(fs->endian,
-        extents[i].blk_cnt) * (TSK_OFF_T) fs->block_size;
-    r_offs =
-        tsk_getu32(fs->endian,
-        extents[i].start_blk) * (TSK_OFF_T) fs->block_size;
-    f_offs = 0;
-
-    nodesize = tsk_getu16(fs->endian, hfs->catalog_header.nodesize);
-
-    /* calculate where we will find the 'nodenum' node */
-    n_offs = nodesize * nodenum;
-
-    while (f_offs < n_offs) {
-
-        if (n_offs <= (f_offs + (TSK_OFF_T)bytes)) {
-
-            r_offs += n_offs - f_offs;
-            f_offs = n_offs;
-
-        }
-        else {
-
-            i++;
-
-            if (i > 7) {
-                tsk_errno = TSK_ERR_FS_GENFS;
-                snprintf(tsk_errstr, TSK_ERRSTR_L,
-                    "hfs_cat_find_node_offset: file seek error while searching for node %"
-                    PRIu32 "\n", nodenum);
-                return 0;
-            }
-
-            r_offs =
-                tsk_getu32(fs->endian,
-                extents[i].start_blk) * (TSK_OFF_T) fs->block_size;
-            f_offs += bytes;
-            bytes =
-                tsk_getu32(fs->endian,
-                extents[i].blk_cnt) * (TSK_OFF_T) fs->block_size;
-
-        }
-    }
-
-    return r_offs;
-}
-
-/* Advances to the next record in the Catalog B-tree, given information about
- * where you currently are in the B-tree.
- * Assumes that you are actually keeping track of these many fields. They
- * must correctly contain the current values. If the current node is changed,
- * they will be changed to their new values.
- * Returns cur_node. If you have reached the end of the node chain (no more
- * records), cur_node will be set to zero and returned.
- * May set up to error string 2. Returns 0 on error. */
-uint32_t
-hfs_cat_next_record(HFS_INFO * hfs, uint16_t * rec, uint16_t * num_rec,
-    hfs_btree_node * node, uint32_t * cur_node, TSK_OFF_T * cur_off,
-    hfs_btree_header_record * header)
-{
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
-
-    tsk_error_reset();
-
-    (*rec)++;
-
-    if (*rec >= *num_rec) {     /* ran out of records in this node */
-        *cur_node = tsk_getu32(fs->endian, node->flink);
-        if (*cur_node == 0)
-            return *cur_node;
-        *cur_off = hfs_cat_find_node_offset(hfs, *cur_node);
-        if (*cur_off == 0) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                "hfs_cat_next_record: find next node offset (%" PRIu32 ")",
-                *cur_node);
-            return 0;
-        }
-        if (hfs_checked_read_random(fs, (char *) node,
-                sizeof(hfs_btree_node), *cur_off)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                "hfs_cat_next_record: read btree node %" PRIu32 " at %"
-                PRIuDADDR, *cur_node, *cur_off);
-            return 0;
-        }
-        *num_rec = tsk_getu16(fs->endian, node->num_rec);
-        *rec = 0;
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "hfs_cat_next_record: advanced to next node %" PRIu32
-                "(@ %" PRIu64 ", has %" PRIu16 "records \n", *cur_node,
-                *cur_off, *num_rec);
-    }
-    else {
-        if (tsk_verbose)
-            tsk_fprintf(stderr,
-                "hfs_cat_next_record: advanced to record %" PRIu16 "\n",
-                *rec);
-    }
-
-    return *cur_node;
-}
-
-/** \internal
- * Returns the largest inode number in file system
- * @param hfs File system being analyzed
- * @returns largest metadata address
- */
-static TSK_INUM_T
-hfs_find_highest_inum(HFS_INFO * hfs)
-{
-    // @@@ get actual number from Catalog file
-    /* 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
-       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)
-        return (TSK_INUM_T) 0xffffffff;
-    else
-        return (TSK_INUM_T) tsk_getu32(fs->endian,
-            hfs->fs->next_cat_id) - 1;
-}
-
-
-// @@@ We should have a version of this that allows one key to have cnid already in local order...
-
 /** \internal
  * Compares two Catalog B-tree keys.
  * @param hfs File System being analyzed
@@ -1299,7 +1113,7 @@ hfs_find_highest_inum(HFS_INFO * hfs)
  * @returns -1 if key1 is smaller, 0 if equal, and 1 if key1 is larger
  */
 int
-hfs_compare_catalog_keys(HFS_INFO * hfs, hfs_cat_key * key1,
+hfs_cat_compare_keys(HFS_INFO * hfs, hfs_cat_key * key1,
     hfs_cat_key * key2)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
@@ -1317,200 +1131,227 @@ hfs_compare_catalog_keys(HFS_INFO * hfs, hfs_cat_key * key1,
 }
 
 
+
+
 /** \internal
- * Find the byte offset (from the start of the disk) 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.
- */
+* 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_catalog_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle)
+hfs_cat_get_record_offset(HFS_INFO * hfs, hfs_cat_key * needle)
 {
-    TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
-
-    hfs_btree_header_record header;     /* header for the Catalog btree */
-    uint16_t leafsize;          /* size of nodes (all, regardless of the name) */
+    TSK_FS_INFO *fs = &(hfs->fs_info);
     uint32_t cur_node;          /* node id of the current node */
-    TSK_OFF_T off;
+    char *node;    
 
+    uint16_t nodesize;
+    uint8_t is_done = 0;
+    
     tsk_error_reset();
 
-    /* read catalog header record */
-    off = hfs_cat_find_node_offset(hfs, 0);
-    if (off == 0) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
-            "hfs_catalog_get_record_offset: find catalog header node");
+    
+    nodesize = tsk_getu16(fs->endian, hfs->catalog_header.nodesize);
+    if ((node = (char *) tsk_malloc(nodesize)) == NULL)
         return 0;
-    }
-    off += 14;                  // sizeof header
-    if (hfs_checked_read_random(fs, (char *) &header, sizeof(header), off)) {
-        snprintf(tsk_errstr2, TSK_ERRSTR_L,
-            "hfs_catalog_get_record_offset: read catalog header node");
+    // @@@ ADD FREE CODE
+    
+    /* start at root node */
+    cur_node = tsk_getu32(fs->endian, hfs->catalog_header.root);
+    
+    /* 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_cat_get_record_offset: "
+                        "empty extents btree\n");
         return 0;
     }
-    leafsize = tsk_getu16(fs->endian, header.nodesize); // @@@ This should be hard coded
-
-    /* start at root node */
-    cur_node = tsk_getu32(fs->endian, header.root);
-
+    
     if (tsk_verbose)
-        tsk_fprintf(stderr, "hfs_catalog_get_record_offset: starting at "
-            "root node %" PRIu32 "; header @ %" PRIu64 "; leafsize = %"
-            PRIu16 "\n", cur_node, off, leafsize);
-
-    while (1) {
+        tsk_fprintf(stderr, "hfs_cat_get_record_offset: starting at "
+                    "root node %" PRIu32 "; nodesize = %"
+                    PRIu16 "\n", cur_node, nodesize);
+    
+    is_done = 0;
+    while (is_done == 0) {
         TSK_OFF_T cur_off;      /* start address of cur_node */
-        hfs_cat_key key;        /* current key */
         uint16_t num_rec;       /* number of records in this node */
-        TSK_DADDR_T recaddr;
-        uint16_t rec, recno;
-        char buf[4];
-        int cmp;
-        hfs_btree_node node;    /* data of the current node */
-
-        /* load node header */
-        cur_off = hfs_cat_find_node_offset(hfs, cur_node);
-        if (cur_off == 0) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                "hfs_catalog_get_record_offset: find catalog node %" PRIu32,
-                cur_node);
-            return 0;
-        }
-        if (hfs_checked_read_random(fs, (char *) &node, sizeof(node),
-                cur_off)) {
-            snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                "hfs_catalog_get_record_offset: read catalog node %" PRIu32
-                " at %" PRIuDADDR, cur_node, cur_off);
+        ssize_t cnt;
+        hfs_btree_node *node_desc;
+        
+        cur_off = cur_node * nodesize;
+        
+        cnt = tsk_fs_attr_read(hfs->catalog_attr, cur_off,
+                               node, nodesize, 0);
+        if (cnt != nodesize) {
+            // @@@
             return 0;
         }
-        num_rec = tsk_getu16(fs->endian, node.num_rec);
-
+        
+        node_desc = (hfs_btree_node *) node;
+        
+        num_rec = tsk_getu16(fs->endian, node_desc->num_rec);
+        
         if (tsk_verbose)
-            tsk_fprintf(stderr, "hfs_catalog_get_record_offset: node %" PRIu32
-                " @ %" PRIu64 " has %" PRIu16 " records\n",
-                cur_node, cur_off, num_rec);
-
+            tsk_fprintf(stderr, "hfs_cat_get_record_offset: 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_catalog_get_record_offset: zero records in node %" PRIu32,
-                cur_node);
+                     "hfs_cat_get_record_offset: zero records in node %"
+                     PRIu32, cur_node);
             return 0;
         }
-
-        /* find largest key smaller than or equal to our key */
-        recno = 0;
-        recaddr = 0;
-        for (rec = 0; rec < num_rec; rec++) {
-
-            off = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
-            if (off == 0) {
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                    "hfs_catalog_get_record_offset: finding record %" PRIu16
-                    " in node %" PRIu32, rec, cur_node);
-                return 0;
-            }
-            off =
-                hfs_read_key(hfs, &header, off, (char *) &key,
-                sizeof(hfs_cat_key), 1);
-            if (off == 0) {
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                    "hfs_catalog_get_record_offset: reading record %" PRIu16
-                    " in node %" PRIu32, rec, cur_node);
-                return 0;
-            }
-            cmp = hfs_compare_catalog_keys(hfs, &key, needle);
-
-            if (tsk_verbose)
-                tsk_fprintf(stderr,
-                    "hfs_catalog_get_record_offset: record %" PRIu16 " @ %"
-                    PRIu64 "; keylen %" PRIu16 " (%" PRIu32 ", %" PRIu16
-                    "); compare: %d\n", rec, off, tsk_getu16(fs->endian,
-                        key.key_len), tsk_getu32(fs->endian,
-                        key.parent_cnid), tsk_getu16(fs->endian,
-                        key.name.length), cmp);
-
-            /* find the largest key less than or equal to our key */
-            /* if all keys are larger than our key, select the leftmost key */
-            if ((cmp <= 0) || (recaddr == 0)) {
-                recaddr = off;
-                recno = rec;
+        
+        if (node_desc->kind == HFS_BTREE_INDEX_NODE) {
+            uint32_t next_node = 0;
+            int rec;
+            
+            /* find largest key smaller than or equal to cnid */
+            for (rec = 0; rec < num_rec; rec++) {
+                size_t rec_off;
+                hfs_cat_key *key;
+                
+                // get the record offset in the node
+                rec_off =
+                    tsk_getu16(fs->endian,
+                               &node[nodesize - (rec + 1) * 2]);
+                if (rec_off > nodesize) {
+                    // @@@ ERROR
+                    return TSK_ERR;
+                }
+                key = (hfs_cat_key *) & node[rec_off];
+                
+                if (tsk_verbose)
+                    tsk_fprintf(stderr,
+                                "hfs_cat_get_record_offset: record %" PRIu16
+                                " ; keylen %" PRIu16 " (%" PRIu32")\n", rec,
+                                tsk_getu16(fs->endian, key->key_len),
+                                tsk_getu32(fs->endian, key->parent_cnid));
+                
+                /* find the largest key less than or equal to our key */
+                /* if all keys are larger than our key, select the leftmost key */
+                if ((hfs_cat_compare_keys(hfs, key, needle) <= 0) || (next_node == 0)) {
+                    int keylen = tsk_getu16(fs->endian, key->key_len) + 2;
+                    if (rec_off + keylen > nodesize) {
+                        // @@@ ERROR
+                        return TSK_ERR;
+                    }
+                    next_node =
+                        tsk_getu32(fs->endian, &node[rec_off + keylen]);
+                }
+                else {
+                    break;
+                }
             }
-            if (cmp >= 0)
+            if (next_node == 0) {
+                // @@@@
+                is_done = 1;
                 break;
-        }
-
-        if (node.kind == HFS_BTREE_INDEX_NODE) {
-            /* replace cur node number with the node number referenced
-             * by the found key, continue */
-            if (hfs_checked_read_random(fs, buf, 4, recaddr)) {
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                    "hfs_catalog_get_record_offset: reading pointer in record %"
-                    PRIu16 " in node %" PRIu32, rec, cur_node);
-                return 0;
             }
-            cur_node = tsk_getu32(fs->endian, buf);
+            cur_node = next_node;
         }
-        else if (node.kind == HFS_BTREE_LEAF_NODE) {
-            rec = recno;
-
-            /* reget key */
-            off = hfs_get_bt_rec_off(hfs, cur_off, leafsize, rec);
-            if (off == 0) {
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                    "hfs_catalog_get_record_offset: finding record %" PRIu16
-                    " in node %" PRIu32, rec, cur_node);
-                return 0;
-            }
-            off =
-                hfs_read_key(hfs, &header, off, (char *) &key,
-                sizeof(hfs_ext_key), 1);
-            if (off == 0) {
-                snprintf(tsk_errstr2, TSK_ERRSTR_L,
-                    "hfs_catalog_get_record_offset: reading record %" PRIu16
-                    " in node %" PRIu32, rec, cur_node);
-                return 0;
+        
+        else if (node_desc->kind == HFS_BTREE_LEAF_NODE) {
+            int rec;
+            
+            for (rec = 0; rec < num_rec; rec++) {
+                size_t rec_off;
+                hfs_cat_key *key;
+                size_t rec_off2;
+                int diff;
+                
+                // get the record offset in the node
+                rec_off =
+                    tsk_getu16(fs->endian,
+                               &node[nodesize - (rec + 1) * 2]);
+                if (rec_off > nodesize) {
+                    // @@@ ERROR
+                    return 0;
+                }
+                key = (hfs_cat_key *) & node[rec_off];
+                
+                if (tsk_verbose)
+                    tsk_fprintf(stderr,
+                                "hfs_cat_get_record_offset: 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) {
+                    is_done = 1;
+                    break;
+                }
+                
+                rec_off2 = rec_off + 2 + tsk_getu16(fs->endian, key->key_len);
+                if (rec_off2 > nodesize) {
+                    // @@@ ERROR
+                    return 0;
+                }
+                
+                return cur_off + rec_off2;
             }
-
-            if (hfs_compare_catalog_keys(hfs, &key, needle) == 0)
-                return off;
-            return 0;           /* this key not found */
-
         }
         else {
             tsk_errno = TSK_ERR_FS_GENFS;
             snprintf(tsk_errstr, TSK_ERRSTR_L,
-                "hfs_catalog_get_record_offset: btree node %" PRIu32
-                " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")",
-                cur_node, cur_off, node.kind);
+                     "hfs_cat_get_record_offset: btree node %" PRIu32
+                     " (%" PRIu64 ") is neither index nor leaf (%" PRIu8 ")",
+                     cur_node, cur_off, node_desc->kind);
             return 0;
         }
     }
+    return 0;
 }
 
-/* Thread records are variable-length. This function reads in from disk only that
- * data actually contained within the thread record into a fixed-size (maximum-size)
- * hfs_thread structure, zeroing the remainder of the structure
- * Returns 0 on success, 1 on failure; sets up to error string 1 */
+
+
+
+/** \internal
+ * Given a byte offset to a leaf record in teh catalog file, read the data as
+ * a thread record. This will zero the buffer and read in the size of the thread
+ * data. 
+ * @param hfs File System
+ * @param off Byte offset of record in catalog file (not including key)
+ * @param thread [out] Buffer to write thread data into.
+ * @returns 0 on success, 1 on failure; sets up to error string 1 */
 uint8_t
-hfs_read_thread_record(HFS_INFO * hfs, TSK_DADDR_T addr,
+hfs_cat_read_thread_record(HFS_INFO * hfs, TSK_OFF_T off,
     hfs_thread * thread)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
     uint16_t uni_len;
+    size_t cnt;
 
     memset(thread, 0, sizeof(hfs_thread));
-    if (hfs_checked_read_random(fs, (char *) thread, 10, addr))
+    cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *)thread, 10, 0);
+    if (cnt != 10) {
+        // @@@
         return 1;
+    }
 
     if ((tsk_getu16(fs->endian, thread->record_type) != HFS_FOLDER_THREAD)
         && (tsk_getu16(fs->endian,
                 thread->record_type) != HFS_FILE_THREAD)) {
         tsk_errno = TSK_ERR_FS_GENFS;
         snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "hfs_read_thread_record: unexpected record type %" PRIu16,
+            "hfs_cat_read_thread_record: unexpected record type %" PRIu16,
             tsk_getu16(fs->endian, thread->record_type));
         return 1;
     }
@@ -1520,14 +1361,16 @@ hfs_read_thread_record(HFS_INFO * hfs, TSK_DADDR_T addr,
     if (uni_len > 255) {
         tsk_errno = TSK_ERR_FS_INODE_COR;
         snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "hfs_read_thread_record: invalid string length (%" PRIu16 ")",
+            "hfs_cat_read_thread_record: invalid string length (%" PRIu16 ")",
             uni_len);
         return 1;
     }
 
-    if (hfs_checked_read_random(fs, (char *) thread->name.unicode,
-            uni_len * 2, addr + 10))
+    cnt = tsk_fs_attr_read(hfs->catalog_attr, off + 10, (char *)thread->name.unicode, uni_len*2, 0);
+    if (cnt != uni_len*2) {
+        // @@@
         return 1;
+    }
 
     return 0;
 }
@@ -1536,35 +1379,43 @@ hfs_read_thread_record(HFS_INFO * hfs, TSK_DADDR_T addr,
  * Read a catalog record into a local data structure.  This reads the
  * correct amount, depending on if it is a file or folder. 
  * @param hfs File system being analyzed
- * @param off Byte offset (in disk) of record 
+ * @param off Byte offset (in catalog file) of record (not including key)
  * @param record [out] Structure to read data into
  * @returns 1 on error
  */
 uint8_t
-hfs_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
+hfs_cat_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
     hfs_file_folder * record)
 {
     TSK_FS_INFO *fs = (TSK_FS_INFO *) & (hfs->fs_info);
+    size_t cnt;
 
     memset(record, 0, sizeof(hfs_file_folder));
-    if (hfs_checked_read_random(fs, (char *) record, 2, off))
+    cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *)record, 2, 0);
+    if (cnt != 2) {
+        // @@@
         return 1;
-
+    }
+        
     if (tsk_getu16(fs->endian, record->file.rec_type) == HFS_FOLDER_RECORD) {
-        if (hfs_checked_read_random(fs, ((char *) record) + 2,
-                sizeof(hfs_folder) - 2, off + 2))
+        cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *)record, sizeof(hfs_folder), 0);
+        if (cnt != sizeof(hfs_folder)) {
+            // @@@
             return 1;
+        }
     }
     else if (tsk_getu16(fs->endian,
             record->file.rec_type) == HFS_FILE_RECORD) {
-        if (hfs_checked_read_random(fs, ((char *) record) + 2,
-                sizeof(hfs_file) - 2, off + 2))
+        cnt = tsk_fs_attr_read(hfs->catalog_attr, off, (char *)record, sizeof(hfs_file), 0);
+        if (cnt != sizeof(hfs_file)) {
+            // @@@
             return 1;
+        }
     }
     else {
         tsk_errno = TSK_ERR_FS_GENFS;
         snprintf(tsk_errstr, TSK_ERRSTR_L,
-            "hfs_read_file_folder_record: unexpected record type %" PRIu16,
+            "hfs_cat_read_file_folder_record: unexpected record type %" PRIu16,
             tsk_getu16(fs->endian, record->file.rec_type));
         return 1;
     }
@@ -1582,23 +1433,19 @@ hfs_read_file_folder_record(HFS_INFO * hfs, TSK_OFF_T off,
  * to differentiate between error and not found.
  */
 static uint8_t
-hfs_catalog_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
+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 */
-    uint32_t cnid;              /* catalog node ID of the entry (= inum) */
     hfs_thread thread;          /* thread record */
     hfs_file_folder record;     /* file/folder record */
     TSK_OFF_T off;
 
-    char fname[HFS_MAXNAMLEN + 1];
-	uint32_t *temp_32ptr;
-
     tsk_error_reset();
 
     if (tsk_verbose)
         tsk_fprintf(stderr,
-            "hfs_catalog_lookup: called for inum %" PRIuINUM "\n", inum);
+            "hfs_cat_file_lookup: called for inum %" PRIuINUM "\n", inum);
     
     // Test if this is a special file that is not located in the catalog
     if ((inum == HFS_EXTENTS_FILE_ID) ||
@@ -1610,61 +1457,43 @@ hfs_catalog_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
         return 1;
     }
     
-    
     /* first look up the thread record for the item we're searching for */
-
+    
     /* set up the thread record key */
-    cnid = (uint32_t) inum;
     memset((char *) &key, 0, sizeof(hfs_cat_key));
-
+    cnid_to_array((uint32_t)inum, key.parent_cnid);
     
-    temp_32ptr = (uint32_t *) (key.parent_cnid);
-    // @@@ Why is this needed, cnid is inum, which is local ordering...
-    // I think the goal is to put it back into BE ordering, but that seems to not work..
-    *temp_32ptr = tsk_getu32(fs->endian, (char *) &cnid);
-
     /* look up the thread record */
-    off = hfs_catalog_get_record_offset(hfs, &key);
+    off = hfs_cat_get_record_offset(hfs, &key);
 
     if (off == 0)
         return 1;
 
     /* read the thread record */
-    if (hfs_read_thread_record(hfs, off, &thread))
-        return 1;
-
-    if (hfs_uni2ascii(fs, thread.name.unicode,
-            tsk_getu16(fs->endian, thread.name.length),
-            fname, HFS_MAXNAMLEN + 1))
+    if (hfs_cat_read_thread_record(hfs, off, &thread))
         return 1;
 
-    if (tsk_verbose)
-        fprintf(stderr,
-            "hfs_catalog_lookup: parent cnid %" PRIu32 " node name (%"
-            PRIu16 ") %s\n", tsk_getu32(fs->endian, thread.parent_cnid),
-            tsk_getu16(fs->endian, thread.name.length), fname);
-
     /* now look up the actual file/folder record */
 
     /* build key */
     memset((char *) &key, 0, sizeof(hfs_cat_key));
-    memcpy(((char *) &key) + 2, ((char *) &thread) + 4,
-        sizeof(hfs_cat_key) - 2);
+    memcpy((char *)key.parent_cnid, (char *)thread.parent_cnid, sizeof(key.parent_cnid));
+    memcpy((char *)&key.name, (char *)&thread.name, sizeof(key.name));
 
     /* look up the record */
-    off = hfs_catalog_get_record_offset(hfs, &key);
+    off = hfs_cat_get_record_offset(hfs, &key);
     if (off == 0)
         return 1;
 
     /* read the record */
-    if (hfs_read_file_folder_record(hfs, off, &record))
+    if (hfs_cat_read_file_folder_record(hfs, off, &record))
         return 1;
 
     /* these memcpy can be gotten rid of, really */
     if (tsk_getu16(fs->endian, record.file.rec_type) == HFS_FOLDER_RECORD) {
         if (tsk_verbose)
             fprintf(stderr,
-                "hfs_catalog_lookup: found folder record valence %" PRIu32
+                "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));
@@ -1674,11 +1503,11 @@ hfs_catalog_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
             record.file.rec_type) == HFS_FILE_RECORD) {
         if (tsk_verbose)
             fprintf(stderr,
-                "hfs_catalog_lookup: found file record cnid %" PRIu32 "\n",
+                "hfs_cat_file_lookup: found file record cnid %" PRIu32 "\n",
                 tsk_getu32(fs->endian, record.file.cnid));
         memcpy((char *) &entry->cat, (char *) &record, sizeof(hfs_file));
     }
-    /* other cases already caught by hfs_read_file_folder_record */
+    /* other cases already caught by hfs_cat_read_file_folder_record */
 
     memcpy((char *) &entry->thread, (char *) &thread, sizeof(hfs_thread));
 
@@ -1686,80 +1515,37 @@ hfs_catalog_lookup(HFS_INFO * hfs, TSK_INUM_T inum, HFS_ENTRY * entry)
     entry->inum = inum;
 
     if (tsk_verbose)
-        tsk_fprintf(stderr, "hfs_catalog_lookup exited\n");
+        tsk_fprintf(stderr, "hfs_cat_file_lookup exited\n");
     return 0;
 }
 
 
-/* hfs_load_blockmap - This function will allocate a bitmap of blocks which
- * are allocated.
- */
-static int
-hfs_load_blockmap(HFS_INFO * hfs)
+/** \internal
+* Returns the largest inode number in file system
+* @param hfs File system being analyzed
+* @returns largest metadata address
+*/
+static TSK_INUM_T
+hfs_find_highest_inum(HFS_INFO * hfs)
 {
+    // @@@ get actual number from Catalog file
+    /* 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
+    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);
-    hfs_ext_desc *extents;
-    int i;
-    uint8_t *ptr;
-    uint32_t bytes_remaining;
-
-    if (tsk_verbose)
-        tsk_fprintf(stderr, "hfs_load_blockmap: called\n");
-
-    /* Note: the allocation file can be larger than the number of bytes
-       computed below. According to TN1150, all extra bits must be set to 0.
-       We avoid storing those empty bits; a consistency checker may want to
-       actually load the whole allocation file and check that these bits
-       are in fact empty. */
-
-    hfs->block_map_size =
-        (uint32_t) roundup(fs->block_count / 8, fs->block_size);
-    if ((hfs->block_map =
-            (uint8_t *) tsk_malloc(hfs->block_map_size)) == NULL)
-        return 1;
-
-    memset(hfs->block_map, 0, hfs->block_map_size);
-
-    extents =
-        hfs_ext_find_extent_record(hfs, HFS_ALLOCATION_FILE_ID,
-        hfs->fs->alloc_file.extents);
-    if (extents == NULL) {
-        tsk_fprintf(stderr,
-            "hfs_load_blockmap: failed to find extents for allocation file\n");
-        return 1;
-    }
-
-    i = 0;
-    ptr = hfs->block_map;
-    bytes_remaining = hfs->block_map_size;
-    while (bytes_remaining > 0) {
-        uint32_t blocks;
-        uint32_t bytes;
-        TSK_OFF_T offset;
-
-        blocks = tsk_getu32(fs->endian, extents[i].blk_cnt);
-        if (blocks == 0) {
-            tsk_fprintf(stderr,
-                "hfs_load_blockmap: ran out of data for allocation file\n");
-            free(extents);
-            return 1;
-        }
+    
+    if (tsk_getu32(fs->endian,
+                   hfs->fs->attr) & HFS_BIT_VOLUME_CNIDS_REUSED)
+        return (TSK_INUM_T) 0xffffffff;
+    else
+        return (TSK_INUM_T) tsk_getu32(fs->endian,
+                                       hfs->fs->next_cat_id) - 1;
+}
 
-		bytes = (bytes_remaining < blocks * fs->block_size) ? bytes_remaining : blocks * fs->block_size;
-        offset =
-            (TSK_OFF_T) tsk_getu32(fs->endian,
-            extents[i].start_blk) * fs->block_size;
-        if (hfs_checked_read_random(fs, (char *) ptr, bytes, offset)) {
-            free(extents);
-            return 1;
-        }
-        bytes_remaining -= bytes;
-        ptr += bytes;
-    }
 
-    free(extents);
-    return 0;
-}
 
 static TSK_FS_META_MODE_ENUM
 hfsmode2tskmode(uint16_t a_mode)
@@ -1979,6 +1765,88 @@ hfs_make_extents(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
     return 0;
 }
 
+
+/**
+* \internal
+ * Create an FS_INODE structure for the blockmap / allocation 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_blockmap(HFS_INFO * hfs, TSK_FS_FILE * fs_file)
+{
+    TSK_FS_INFO *fs = (TSK_FS_INFO *) hfs;
+    TSK_FS_ATTR *fs_attr;
+    TSK_FS_ATTR_RUN *attr_run;
+    
+    fs_file->meta->type = TSK_FS_META_TYPE_VIRT;
+    fs_file->meta->mode = 0;
+    fs_file->meta->nlink = 1;
+    fs_file->meta->addr = HFS_ALLOCATION_FILE_ID;
+    fs_file->meta->flags = (TSK_FS_META_FLAG_USED | TSK_FS_META_FLAG_ALLOC);
+    fs_file->meta->uid = fs_file->meta->gid = 0;
+    fs_file->meta->mtime = fs_file->meta->atime = fs_file->meta->ctime = fs_file->meta->crtime = 0;
+    
+    if (fs_file->meta->name2 == NULL) {
+        if ((fs_file->meta->name2 = (TSK_FS_META_NAME_LIST *)
+             tsk_malloc(sizeof(TSK_FS_META_NAME_LIST))) == NULL)
+            return 1;
+        fs_file->meta->name2->next = NULL;
+    }
+    strncpy(fs_file->meta->name2->name, HFS_ALLOCATIONNAME,
+            TSK_FS_META_NAME_LIST_NSIZE);
+    
+    fs_file->meta->size = tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz); 
+    
+    
+    if (fs_file->meta->attr != NULL) {
+        tsk_fs_attrlist_markunused(fs_file->meta->attr);
+    }
+    else  {
+        fs_file->meta->attr = tsk_fs_attrlist_alloc();
+    }
+    
+    if ((attr_run = hfs_extents_to_attr(fs, hfs->fs->alloc_file.extents, 0)) == NULL) {
+        strncat(tsk_errstr2, " - hfs_make_catalog",
+                TSK_ERRSTR_L - strlen(tsk_errstr2));
+        return 1;
+    }
+    
+    if ((fs_attr = tsk_fs_attrlist_getnew(fs_file->meta->attr, TSK_FS_ATTR_NONRES)) == NULL) {
+        strncat(tsk_errstr2, " - hfs_make_catalog",
+                TSK_ERRSTR_L - strlen(tsk_errstr2));
+        tsk_fs_attr_run_free(attr_run);
+        return 1;
+    }
+    
+    // initialize the data run
+    if (tsk_fs_attr_set_run(fs_file, fs_attr, attr_run, NULL,
+                            TSK_FS_ATTR_TYPE_DEFAULT, TSK_FS_ATTR_ID_DEFAULT,
+                            tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz), 
+                            tsk_getu64(fs->endian, hfs->fs->alloc_file.logic_sz), 0, 0)) {
+        strncat(tsk_errstr2, " - hfs_make_catalog",
+                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_ALLOCATION_FILE_ID,
+                                        fs_attr)) {
+        strncat(tsk_errstr2, " - hfs_make_catalog",
+                TSK_ERRSTR_L - strlen(tsk_errstr2));
+        fs_file->meta->attr_state = TSK_FS_META_ATTR_ERROR;
+        return 1;
+    }
+    
+    fs_file->meta->attr_state = TSK_FS_META_ATTR_STUDIED;
+    return 0;
+}
+
+
 /*
  * Copy the inode into the generic structure
  * Returns 1 on error.
@@ -2106,7 +1974,10 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
             return 0;
     }
     else if (inum == HFS_ALLOCATION_FILE_ID) {
-        // @@@
+        if (hfs_make_blockmap(hfs, a_fs_file))
+            return 1;
+        else
+            return 0;
     }
     else if (inum == HFS_STARTUP_FILE_ID) {
         // @@@
@@ -2116,7 +1987,7 @@ hfs_inode_lookup(TSK_FS_INFO * fs, TSK_FS_FILE * a_fs_file,
     }
     
     /* Lookup inode and store it in the HFS structure */
-    if (hfs_catalog_lookup(hfs, inum, &entry))
+    if (hfs_cat_file_lookup(hfs, inum, &entry))
         return 1;
 
     /* Copy the structure in hfs to generic fs_inode */
@@ -2179,7 +2050,7 @@ hfs_make_data_run(TSK_FS_FILE * fs_file)
     // look up the catalog entries for this file
     // (they have already been looked up once before, but that information
     // isn't propagated to here, so we look it up again)
-    if (hfs_catalog_lookup(hfs, fs_file->meta->addr, &entry))
+    if (hfs_cat_file_lookup(hfs, fs_file->meta->addr, &entry))
         return 1;
 
     // if the catalog entry is not a file entry (presumably it would have
@@ -2236,10 +2107,75 @@ hfs_make_data_run(TSK_FS_FILE * fs_file)
 }
 
 
+
+
+/** \internal
+* Get allocation status of file system block.
+* adapted from IsAllocationBlockUsed from:
+* http://developer.apple.com/technotes/tn/tn1150.html
+*
+* @param hfs File system being analyzed
+* @param b Block address 
+* @returns 1 if allocated, 0 if not, -1 on error
+*/
+static int8_t
+hfs_block_is_alloc(HFS_INFO * hfs, TSK_DADDR_T a_addr)
+{
+    TSK_FS_INFO *fs = &(hfs->fs_info);
+    int b;
+    int b2;
+    
+    // lazy loading
+    if (hfs->blockmap_file == NULL) {
+        if ((hfs->blockmap_file =
+             tsk_fs_file_open_meta(fs, NULL, HFS_ALLOCATION_FILE_ID)) == NULL) {
+            strncat(tsk_errstr2, " - Loading blockmap file",
+                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+            return -1;
+        }
+        
+        /* cache the data attribute */
+        hfs->blockmap_attr =
+            tsk_fs_attrlist_get(hfs->blockmap_file->meta->attr, TSK_FS_ATTR_TYPE_DEFAULT);
+        if (!hfs->blockmap_attr) {
+            strncat(tsk_errstr2, " - Data Attribute not found in Blockmap File",
+                    TSK_ERRSTR_L - strlen(tsk_errstr2));
+            return -1;
+        }
+        hfs->blockmap_cache_start = -1;
+    }
+    
+    // get the byte offset
+    b = (int)a_addr / 8;
+    if (b > hfs->blockmap_file->meta->size) {
+        tsk_errno = TSK_ERR_FS_CORRUPT;
+        snprintf(tsk_errstr, TSK_ERRSTR_L,
+                    "hfs_block_is_alloc: block %" PRIuDADDR
+                    " is too large for bitmap (%"PRIuOFF")", a_addr, hfs->blockmap_file->meta->size);
+        return -1;
+    }
+    
+    // see if it is in the cache
+    if ((hfs->blockmap_cache_start == -1) || (hfs->blockmap_cache_start > b) 
+        || (hfs->blockmap_cache_start + sizeof(hfs->blockmap_cache) <= b)) {
+        size_t cnt = tsk_fs_attr_read(hfs->blockmap_attr, b,
+                               hfs->blockmap_cache,
+                               sizeof(hfs->blockmap_cache), 0);
+        if (cnt != sizeof(hfs->blockmap_cache)) {
+            // @@@
+            return -1;
+        }
+        hfs->blockmap_cache_start = b;
+    }
+    b2 = b - hfs->blockmap_cache_start;
+    return (hfs->blockmap_cache[b2] & (1 << (7 - (a_addr % 8)))) != 0;
+}
+
+
 TSK_FS_BLOCK_FLAG_ENUM
 hfs_block_getflags(TSK_FS_INFO * a_fs, TSK_DADDR_T a_addr)
 {
-    return hfs_is_block_alloc((HFS_INFO *) a_fs, a_addr) ?
+    return (hfs_block_is_alloc((HFS_INFO *) a_fs, a_addr) == 1) ?
         TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC;
 }
 
@@ -2267,13 +2203,15 @@ hfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start_blk,
      * Sanity checks.
      */
     if (start_blk < fs->first_block || start_blk > fs->last_block) {
-        tsk_fprintf(stderr,
+        tsk_errno = TSK_ERR_FS_WALK_RNG;
+        snprintf(tsk_errstr, TSK_ERRSTR_L,
             "%s: invalid start block number: %" PRIuDADDR "", myname,
             start_blk);
         return 1;
     }
     if (end_blk < fs->first_block || end_blk > fs->last_block) {
-        tsk_fprintf(stderr,
+        tsk_errno = TSK_ERR_FS_WALK_RNG;
+        snprintf(tsk_errstr, TSK_ERRSTR_L,
             "%s: invalid last block number: %" PRIuDADDR "", myname,
             end_blk);
         return 1;
@@ -2307,7 +2245,7 @@ hfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start_blk,
         int myflags;
 
         /* identify if the block is allocated or not */
-        myflags = hfs_is_block_alloc(hfs, addr) ?
+        myflags = hfs_block_is_alloc(hfs, addr) ?
             TSK_FS_BLOCK_FLAG_ALLOC : TSK_FS_BLOCK_FLAG_UNALLOC;
 
         // test if we should call the callback with this one
@@ -2320,9 +2258,7 @@ hfs_block_walk(TSK_FS_INFO * fs, TSK_DADDR_T start_blk,
 
 
         if (tsk_fs_block_get(fs, fs_block, addr) == NULL) {
-            tsk_fprintf(stderr,
-                "hfs_block_walk: Error reading block %" PRIuDADDR
-                ": %m", addr);
+            tsk_fs_block_free(fs_block);
             return 1;
         }
 
@@ -2399,7 +2335,7 @@ hfs_inode_walk(TSK_FS_INFO * fs, TSK_INUM_T start_inum,
          * but reuses a single malloc'ed fs_inode
          */
 
-        if (hfs_catalog_lookup(hfs, inum, &entry)) {
+        if (hfs_cat_file_lookup(hfs, inum, &entry)) {
             if (tsk_errno == 0)
                 continue;
             else
@@ -2440,7 +2376,7 @@ print_inode_name(FILE * hFile, TSK_FS_INFO * fs, TSK_INUM_T inum)
     char fn[HFS_MAXNAMLEN + 1];
     HFS_ENTRY entry;
 
-    if (hfs_catalog_lookup(hfs, inum, &entry))
+    if (hfs_cat_file_lookup(hfs, inum, &entry))
         return 1;
 
     if (hfs_uni2ascii(fs, entry.thread.name.unicode,
@@ -2474,7 +2410,7 @@ print_parent_path(FILE * hFile, TSK_FS_INFO * fs, TSK_INUM_T inum)
         return 1;
     }
 
-    if (hfs_catalog_lookup(hfs, inum, &entry))
+    if (hfs_cat_file_lookup(hfs, inum, &entry))
         return 1;
 
     if (hfs_uni2ascii(fs, entry.thread.name.unicode,
@@ -2749,7 +2685,7 @@ hfs_istat(TSK_FS_INFO * fs, FILE * hFile, TSK_INUM_T inum,
                 ctime(&fs_file->meta->time2.hfs.bkup_time));
     
     
-    if (hfs_catalog_lookup(hfs, inum, &entry) == 0) {
+    if (hfs_cat_file_lookup(hfs, inum, &entry) == 0) {
         tsk_fprintf(hFile, "Owner-ID:\t%" PRIu32 "\n", tsk_getu32(fs->endian,
                 entry.cat.perm.owner));
         tsk_fprintf(hFile, "Group-ID:\t%" PRIu32 "\n", tsk_getu32(fs->endian,
@@ -2840,9 +2776,13 @@ hfs_close(TSK_FS_INFO * fs)
 
     free(hfs->fs);
     tsk_fs_file_close(hfs->catalog_file);
+    hfs->catalog_attr = NULL;
 
-    if (hfs->block_map != NULL)
-        free(hfs->block_map);
+    if (hfs->blockmap_file) {
+        tsk_fs_file_close(hfs->blockmap_file);
+        hfs->blockmap_attr = NULL;
+    }
+    
     free(hfs);
 }
 
@@ -2953,9 +2893,9 @@ hfs_open(TSK_IMG_INFO * img_info, TSK_OFF_T offset,
     fs->close = hfs_close;
 
     // lazy loading of block map
-    hfs->block_map = NULL;
-    hfs->block_map_size = 0;
-
+    hfs->blockmap_file = NULL;
+    hfs->blockmap_attr = NULL;
+    hfs->blockmap_cache_start = -1;
 
     fs->first_inum = HFS_ROOT_INUM;
     fs->root_inum = HFS_ROOT_INUM;
diff --git a/tsk3/fs/hfs_dent.c b/tsk3/fs/hfs_dent.c
index 4d79ed5a8440444ce8aec43bce0fa9e43870399f..9fc569831d363fde780b0dd3b936589b5db00603 100644
--- a/tsk3/fs/hfs_dent.c
+++ b/tsk3/fs/hfs_dent.c
@@ -339,7 +339,7 @@ hfs_dir_open_meta3(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
                     " in node %" PRIu32, rec, cur_node);
                 return 0;
             }
-            cmp = hfs_compare_catalog_keys(hfs, &key, &needle);
+            cmp = hfs_cat_compare_keys(hfs, &key, &needle);
 
             if (tsk_verbose >= 2)
                 tsk_fprintf(stderr, "hfs_dir_open_meta: record %" PRIu16
@@ -392,7 +392,7 @@ hfs_dir_open_meta3(TSK_FS_INFO * fs, TSK_FS_DIR ** a_fs_dir,
                 return 1;
             }
 
-            if (hfs_compare_catalog_keys(hfs, &key, &needle) == 0) {
+            if (hfs_cat_compare_keys(hfs, &key, &needle) == 0) {
 
             /*** thread record found ***/
 
diff --git a/tsk3/fs/tsk_hfs.h b/tsk3/fs/tsk_hfs.h
index 313cf520ef7ad1e3dc2339cb681523e78b13a25a..f67c5e972f6016507a06e113c9f12dc1bde90345 100644
--- a/tsk3/fs/tsk_hfs.h
+++ b/tsk3/fs/tsk_hfs.h
@@ -149,6 +149,7 @@
 
 #define HFS_CATALOGNAME "$CatalogFile"
 #define HFS_EXTENTSNAME "$ExtentsFile"
+#define HFS_ALLOCATIONNAME "$BitMap"
 
 /*
  * HFS structures
@@ -384,11 +385,14 @@ typedef struct {
     
     hfs_sb *fs;                 /* cached superblock */
     hfs_ext_desc *cat_extents;  /* full extents of the Catalog file */
-    uint8_t *block_map;         /* cached block allocation bitmap */
-    uint32_t block_map_size;
     
     char is_case_sensitive;
     
+    TSK_FS_FILE *blockmap_file;
+    const TSK_FS_ATTR *blockmap_attr;
+    char blockmap_cache[4096];
+    int blockmap_cache_start;
+    
     TSK_FS_FILE *catalog_file;
     const TSK_FS_ATTR *catalog_attr;
     hfs_btree_header_record catalog_header;
@@ -434,6 +438,7 @@ 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 *);
 
+/*
 extern TSK_OFF_T hfs_cat_find_node_offset(HFS_INFO *, uint32_t);
 extern TSK_OFF_T hfs_get_bt_rec_off(HFS_INFO *, TSK_OFF_T, uint16_t,
     uint16_t);
@@ -441,11 +446,12 @@ extern TSK_OFF_T hfs_read_key(HFS_INFO *, hfs_btree_header_record *,
     TSK_OFF_T, char *, int, uint8_t);
 extern int hfs_compare_catalog_keys(HFS_INFO *, hfs_cat_key *,
     hfs_cat_key *);
-extern uint8_t hfs_read_thread_record(HFS_INFO *, TSK_DADDR_T,
+
+extern uint8_t hfs_read_thread_record(HFS_INFO *, TSK_OFF_T,
     hfs_thread *);
 extern uint32_t hfs_cat_next_record(HFS_INFO *, uint16_t *, uint16_t *,
     hfs_btree_node *, uint32_t *, TSK_OFF_T *, hfs_btree_header_record *);
 extern uint8_t hfs_read_file_folder_record(HFS_INFO *, TSK_OFF_T,
     hfs_file_folder *);
-
+*/
 #endif
diff --git a/xcode/sleuthkit.xcodeproj/project.pbxproj b/xcode/sleuthkit.xcodeproj/project.pbxproj
index ad0954fb973b3b3b1fc37c62cfeb90943ef3fda9..e531c90434340edc61254f5aa1caaef6cb650115 100644
--- a/xcode/sleuthkit.xcodeproj/project.pbxproj
+++ b/xcode/sleuthkit.xcodeproj/project.pbxproj
@@ -24,6 +24,7 @@
 		02260C770D64895B0027BE02 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; };
 		02260C780D64895B0027BE02 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = Makefile.am; sourceTree = "<group>"; };
 		02260C790D64895B0027BE02 /* Makefile.in */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = Makefile.in; sourceTree = "<group>"; };
+		0229714A0EBD0AC0001AC9C7 /* hfs_unicompare.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = hfs_unicompare.c; sourceTree = "<group>"; };
 		022B139A0DB6486D00C4BE09 /* nofs_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = nofs_misc.c; sourceTree = "<group>"; };
 		025328FF0E59B5ED000595D8 /* img_io.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = img_io.c; sourceTree = "<group>"; };
 		025558630DA1C67E00A635EC /* fs_block.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = fs_block.c; sourceTree = "<group>"; };
@@ -303,6 +304,7 @@
 				026FB4570D19C868000434C7 /* hfs.c */,
 				026FB45A0D19C868000434C7 /* hfs_dent.c */,
 				026FB45D0D19C868000434C7 /* hfs_journal.c */,
+				0229714A0EBD0AC0001AC9C7 /* hfs_unicompare.c */,
 				026FB4600D19C868000434C7 /* icat_lib.c */,
 				026FB4630D19C868000434C7 /* ifind_lib.c */,
 				026FB4660D19C868000434C7 /* ils_lib.c */,