Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openhackware / src / libfs / hfs.c
diff --git a/qemu/roms/openhackware/src/libfs/hfs.c b/qemu/roms/openhackware/src/libfs/hfs.c
new file mode 100644 (file)
index 0000000..b2420eb
--- /dev/null
@@ -0,0 +1,2007 @@
+/*
+ * <hfs.c>
+ *
+ * Open Hack'Ware BIOS HFS file system management
+ * 
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ * 
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License V2
+ *   as published by the Free Software Foundation
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Major rework and debug by Thayne Harbaugh <thayne@realmsys.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "bios.h"
+#include "libfs.h"
+
+//#define DEBUG_HFS 1
+
+/* HFS / HFSplus */
+#if defined (DEBUG_HFS)
+#define HFS_DPRINTF(fmt, args...) \
+do { dprintf("%s: " fmt, __func__ , ##args); } while (0)
+#else
+#define HFS_DPRINTF(fmt, args...) \
+do { } while (0)
+#endif
+#define HFS_ERROR(fmt, args...) \
+do { dprintf("HFS ERROR in %s: " fmt, __func__ , ##args); } while (0)
+
+/* HFS/HFS+ common definitions */
+#define HFS_SECTOR_SIZE        512
+#define HFS_VOLHEAD_SECTOR       2
+#define HFS_NODE_SIZE          0x200
+
+/* HFS signature */
+#define HFS_VOLHEAD_SIG         0x4244
+/* HFS+ signature */
+#define HFSPLUS_VOLHEAD_SIG     0x482b
+
+/* HFS+ filesystem support */
+/* Files CNID */
+enum {
+    HFS_ROOT_PARENT  = 1,  /* Parent of root folder */
+    HFS_ROOT_FOLDER  = 2,  /* root folder */
+    HFS_EXTENT_FILE  = 3,  /* file extents file */
+    HFS_CATALOG_FILE = 4,  /* catalog file */
+    HFS_BBLOCS_FILE  = 5,  /* badblocks file */
+    HFS_ALLOC_FILE   = 6,  /* allocation file (HFSplus) */
+    HFS_STARTUP_FILE = 7,  /* startup file (HFSplus) */
+    HFS_ATTR_FILE    = 8,  /* attribute file (HFSplus) */
+    HFS_BEXTENT_FILE = 15, /* file extents temporary file */
+    HFS_FIRST_USERID = 16,
+};
+
+typedef uint32_t HFS_cnid_t;
+
+static inline HFS_cnid_t HFS_get_cnid (HFS_cnid_t *cnidp)
+{
+    return get_be32(cnidp);
+}
+
+typedef uint16_t HFSP_unichr_t;
+
+static inline HFSP_unichr_t HFSP_get_unichr (HFSP_unichr_t *chrp)
+{
+    return get_be16(chrp);
+}
+
+/* A single contiguous area of a file */
+typedef struct HFSP_extent_t HFSP_extent_t;
+struct HFSP_extent_t {
+    uint32_t start_block;
+    uint32_t block_count;
+} __attribute__ ((packed));
+
+static inline HFSP_extent_t *HFSP_get_extent (HFSP_extent_t *extp)
+{
+    extp->start_block = get_be32(&extp->start_block);
+    extp->block_count = get_be32(&extp->block_count);
+
+    return extp;
+}
+
+/* Information for a "Fork" in a file */
+typedef struct HFSP_fork_t HFSP_fork_t;
+struct HFSP_fork_t {
+    /* 0x00 */
+    uint64_t total_size;
+    uint32_t clump_size;
+    uint32_t total_blocks;
+    /* 0x10 */
+    HFSP_extent_t extents[8];
+    /* 0x50 */
+} __attribute__ ((packed));
+
+static inline HFSP_fork_t *HFSP_get_fork (HFSP_fork_t *forkp)
+{
+    int i;
+
+    forkp->total_size = get_be64(&forkp->total_size);
+    forkp->clump_size = get_be32(&forkp->clump_size);
+    forkp->total_blocks = get_be32(&forkp->total_blocks);
+    for (i = 0; i < 8; i++) {
+        HFSP_get_extent(&forkp->extents[i]);
+    }
+
+    return forkp;
+}
+
+/* HFS+ Volume Header */
+typedef struct HFSP_vh_t HFSP_vh_t;
+struct HFSP_vh_t {
+    /* 0x000 */
+    uint16_t signature;
+    uint16_t version;
+    uint32_t attributes;
+    uint32_t last_mount_vers;
+    uint32_t reserved;
+    
+    /* 0x010 */
+    uint32_t create_date;
+    uint32_t modify_date;
+    uint32_t backup_date;
+    uint32_t checked_date;
+    
+    /* 0x020 */
+    uint32_t file_count;
+    uint32_t folder_count;
+    uint32_t blocksize;
+    uint32_t total_blocks;
+
+    /* 0x030 */
+    uint32_t free_blocks;
+    uint32_t next_alloc;
+    uint32_t rsrc_clump_sz;
+    uint32_t data_clump_sz;
+
+    /* 0x040 */
+    HFS_cnid_t next_cnid;
+    uint32_t write_count;
+    uint64_t encodings_bmp;
+    
+    /* 0x050 */
+    uint32_t finder_info[8];
+    
+    /* 0x070 */
+    HFSP_fork_t alloc_file;
+    /* 0x0C0 */
+    HFSP_fork_t ext_file;
+    /* 0x110 */
+    HFSP_fork_t cat_file;
+    /* 0x160 */
+    HFSP_fork_t attr_file;
+    /* 0x1B0 */
+    HFSP_fork_t start_file;
+    /* 0x1F0 */
+    uint8_t pad[16];
+} __attribute__ ((packed));
+
+static HFSP_vh_t *HFSP_read_volhead (part_t *part, uint32_t bloc,
+                                     uint32_t offset, void *buffer, int size)
+{
+    HFSP_vh_t *vh;
+    int i;
+    
+    if (part_seek(part, bloc, offset) == -1)
+        return NULL;
+    if (part_read(part, buffer, size) < 0)
+        return NULL;
+    vh = buffer;
+    vh->signature = get_be16(&vh->signature);
+    vh->version = get_be16(&vh->version);
+    vh->attributes = get_be32(&vh->attributes);
+    vh->last_mount_vers = get_be32(&vh->last_mount_vers);
+    vh->create_date = get_be32(&vh->create_date);
+    vh->modify_date = get_be32(&vh->modify_date);
+    vh->backup_date = get_be32(&vh->backup_date);
+    vh->checked_date = get_be32(&vh->checked_date);
+    vh->file_count = get_be32(&vh->file_count);
+    vh->folder_count = get_be32(&vh->folder_count);
+    vh->blocksize = get_be32(&vh->blocksize);
+    vh->total_blocks = get_be32(&vh->total_blocks);
+    vh->free_blocks = get_be32(&vh->free_blocks);
+    vh->next_alloc = get_be32(&vh->next_alloc);
+    vh->rsrc_clump_sz = get_be32(&vh->rsrc_clump_sz);
+    vh->data_clump_sz = get_be32(&vh->data_clump_sz);
+    HFS_get_cnid(&vh->next_cnid);
+    vh->write_count = get_be32(&vh->write_count);
+    vh->encodings_bmp = get_be32(&vh->encodings_bmp);
+    for (i = 0; i < 8; i++) {
+        vh->finder_info[i] = get_be32(&vh->finder_info[i]);
+    }
+    HFSP_get_fork(&vh->alloc_file);
+    HFSP_get_fork(&vh->ext_file);
+    HFSP_get_fork(&vh->cat_file);
+    HFSP_get_fork(&vh->attr_file);
+    HFSP_get_fork(&vh->start_file);
+
+    return vh;
+}
+
+/* HFS support */
+/* A single contiguous area of a file */
+typedef struct HFS_extent_t HFS_extent_t;
+struct HFS_extent_t {
+    uint16_t start_block;
+    uint16_t block_count;
+} __attribute__ ((packed));
+
+static inline HFS_extent_t *HFS_get_extent (HFS_extent_t *extp)
+{
+    extp->start_block = get_be16(&extp->start_block);
+    extp->block_count = get_be16(&extp->block_count);
+
+    return extp;
+}
+
+/* HFS Volume Header */
+typedef struct HFS_vh_t HFS_vh_t;
+struct HFS_vh_t {
+    /* 0x000 */
+    uint16_t signature;
+    uint32_t create_date;
+    uint32_t modify_date;
+    uint16_t attributes;
+    uint16_t root_file_count;
+    uint16_t bitmap_start;
+
+    /* 0x010 */
+    uint16_t alloc_ptr;
+    uint16_t alloc_blocs;
+    uint32_t alloc_size;
+
+    /* 0x018 */
+    uint32_t clump_size;
+    uint16_t alloc_start;
+    HFS_cnid_t next_cnid;
+    uint16_t free_blocs;
+
+    /* 0x024 */
+    uint8_t  label[28];
+
+    /* 0x040 */
+    uint32_t backup_tmsp;
+    uint16_t backup_seq;
+    uint32_t write_count;
+
+    /* 0x04A */
+    uint32_t ext_clump_size;
+    /* 0x04E */
+    uint32_t cat_clump_size;
+
+    /* 0x052 */
+    uint16_t root_dir_cnt;
+    /* 0x054 */
+    uint32_t file_cnt;
+    uint32_t dir_cnt;
+    /* 0x05C */
+    uint32_t finder_info[8];
+
+    /* 0x07C */
+    uint16_t embed_sig;
+    HFS_extent_t embed_ext;
+
+    /* 0x082 */
+    uint32_t ext_size;
+    HFS_extent_t ext_rec[3];
+
+    /* 0x092 */
+    uint32_t cat_size;
+    HFS_extent_t cat_rec[3];
+
+    /* 0x0A2 */
+} __attribute__(( __packed__ ));
+
+static HFS_vh_t *HFS_read_volhead (part_t *part, uint32_t bloc,
+                                   uint32_t offset, void *buffer, int size)
+{
+    HFS_vh_t *vh;
+    int i;
+    
+    if (part_seek(part, bloc, offset) == -1)
+        return NULL;
+    if (part_read(part, buffer, size) < 0)
+        return NULL;
+    vh = buffer;
+    vh->signature = get_be16(&vh->signature);
+    vh->create_date = get_be32(&vh->create_date);
+    vh->modify_date = get_be32(&vh->modify_date);
+    vh->attributes = get_be16(&vh->attributes);
+    vh->root_file_count = get_be16(&vh->root_file_count);
+    vh->bitmap_start = get_be16(&vh->bitmap_start);
+    vh->alloc_ptr = get_be16(&vh->alloc_ptr);
+    vh->alloc_blocs = get_be16(&vh->alloc_blocs);
+    vh->alloc_size = get_be32(&vh->alloc_size);
+    vh->clump_size = get_be32(&vh->clump_size);
+    vh->alloc_start = get_be16(&vh->alloc_start);
+    HFS_get_cnid(&vh->next_cnid);
+    vh->free_blocs = get_be16(&vh->free_blocs);
+    vh->backup_tmsp = get_be32(&vh->backup_tmsp);
+    vh->backup_seq = get_be16(&vh->backup_seq);
+    vh->write_count = get_be32(&vh->write_count);
+    vh->ext_clump_size = get_be32(&vh->ext_clump_size);
+    vh->cat_clump_size = get_be32(&vh->cat_clump_size);
+    vh->root_dir_cnt = get_be16(&vh->root_dir_cnt);
+    vh->file_cnt = get_be32(&vh->file_cnt);
+    vh->dir_cnt = get_be32(&vh->dir_cnt);
+    for (i = 0; i < 8; i++) {
+        vh->finder_info[i] = get_be32(&vh->finder_info[i]);
+    }
+    vh->embed_sig = get_be16(&vh->embed_sig);
+    HFS_get_extent(&vh->embed_ext);
+    vh->ext_size = get_be16(&vh->ext_size);
+    for (i = 0; i < 3; i++) {
+        HFS_get_extent(&vh->ext_rec[i]);
+    }
+    vh->cat_size = get_be16(&vh->cat_size);
+    for (i = 0; i < 3; i++) {
+        HFS_get_extent(&vh->cat_rec[i]);
+    }
+
+    return vh;
+}
+
+enum {
+    HFS_NODE_LEAF = 0xFF,
+    HFS_NODE_IDX  = 0x00,
+    HFS_NODE_HEAD = 0x01,
+    HFS_NODE_MAP  = 0x02,
+};
+
+/* HFS B-tree structures */
+typedef struct HFS_bnode_t HFS_bnode_t;
+struct HFS_bnode_t {
+    uint32_t next;
+    uint32_t prev;
+    uint8_t  type;
+    uint8_t  height;
+    uint16_t nrecs;
+    uint16_t pad;
+} __attribute__ ((packed));
+
+static HFS_bnode_t *HFS_read_Hnode (part_t *part, uint32_t bloc,
+                                    uint32_t offset, void *buffer, int nsize)
+{
+    HFS_bnode_t *Hnode;
+    
+    if (part_seek(part, bloc, offset) == -1) {
+        HFS_DPRINTF("seek failed\n");
+        return NULL;
+    }
+    if (part_read(part, buffer, nsize) < 0) {
+        HFS_DPRINTF("read failed\n");
+        return NULL;
+    }
+    Hnode = (void *)buffer;
+    Hnode->next = get_be32(&Hnode->next);
+    Hnode->prev = get_be32(&Hnode->prev);
+    Hnode->nrecs = get_be16(&Hnode->nrecs);
+
+    return Hnode;
+}
+
+typedef struct HFS_headrec_t HFS_headrec_t;
+struct HFS_headrec_t {
+    /* 0x00 */
+    uint16_t depth;
+    uint32_t rootnode;
+    /* 0x06 */
+    uint32_t nbleaves;
+    uint32_t firstleaf;
+    /* 0x0E */
+    uint32_t lastleaf;
+    uint16_t nodesize;
+    /* 0x14 */
+    uint16_t maxkeylen;
+    uint32_t nbnodes;
+    /* 0x18 */
+    uint32_t freenodes;
+    uint16_t pad0;
+    /* 0x1E */
+    uint32_t clump_size;
+    uint8_t  type;
+    uint8_t  pad1;
+    /* 0x24 */
+    uint32_t attr;
+    /* 0x28 */
+    uint32_t pad2[16];
+    /* 0x68 */
+} __attribute__ ((packed));
+
+static HFS_headrec_t *HFS_get_headrec (void *pos)
+{
+    HFS_headrec_t *head = pos;
+
+    head->depth = get_be16(&head->depth);
+    head->rootnode = get_be32(&head->rootnode);
+    head->nbleaves = get_be32(&head->nbleaves);
+    head->firstleaf = get_be32(&head->firstleaf);
+    head->lastleaf = get_be32(&head->lastleaf);
+    head->maxkeylen = get_be16(&head->maxkeylen);
+    head->nbnodes = get_be32(&head->nbnodes);
+    head->freenodes = get_be32(&head->freenodes);
+    head->clump_size = get_be32(&head->clump_size);
+    head->attr = get_be32(&head->attr);
+
+    return head;
+}
+
+typedef struct HFS_catkey_t HFS_catkey_t;
+struct HFS_catkey_t {
+    uint8_t len;
+    uint8_t pad;
+    HFS_cnid_t pID;
+    uint8_t nlen;
+    unsigned char name[0x1F];
+} __attribute__ ((packed));
+
+typedef struct HFSP_catkey_t HFSP_catkey_t;
+struct HFSP_catkey_t {
+    uint16_t len;
+    HFS_cnid_t pID;
+    uint16_t nlen;
+    HFSP_unichr_t uniname[255];
+} __attribute__ ((packed));
+
+enum {
+    HFS_CAT_FOLDER  = 0x0100,
+    HFS_CAT_FILE    = 0x0200,
+    HFS_CAT_FOLDTH  = 0x0300,
+    HFS_CAT_FILETH  = 0x0400,
+    HFSP_CAT_FOLDER = 0x0001,
+    HFSP_CAT_FILE   = 0x0002,
+    HFSP_CAT_FOLDTH = 0x0003,
+    HFSP_CAT_FILETH = 0x0004,
+};
+
+typedef struct HFS_win_t HFS_win_t;
+struct HFS_win_t {
+    uint16_t top;
+    uint16_t left;
+    uint16_t bot;
+    uint16_t right;
+}  __attribute__ ((packed));
+
+typedef struct HFS_pos_t HFS_pos_t;
+struct HFS_pos_t {
+    uint16_t y;
+    uint16_t x;
+} __attribute__ ((packed));
+
+typedef struct HFS_fdir_info_t HFS_fdir_info_t;
+struct HFS_fdir_info_t {
+    HFS_win_t win;
+    uint16_t  flags;
+    HFS_pos_t pos;
+    uint16_t  pad;
+} __attribute__ ((packed));
+
+typedef struct HFS_file_info_t HFS_file_info_t;
+struct HFS_file_info_t {
+    uint32_t  ftype;
+    uint32_t  owner;
+    uint16_t  flags;
+    HFS_pos_t pos;
+    uint16_t  pad;
+} __attribute__ ((packed));
+
+typedef struct HFSP_BSD_info_t HFSP_BSD_info_t;
+struct HFSP_BSD_info_t {
+    uint32_t owner;
+    uint32_t group;
+    uint8_t aflags;
+    uint8_t oflags;
+    uint16_t mode;
+    union {
+        uint32_t inum;
+        uint32_t lcount;
+        uint32_t device;
+    } u;
+} __attribute__ ((packed));
+
+typedef struct HFS_fold_t HFS_fold_t;
+struct HFS_fold_t {
+    uint16_t type;
+    uint16_t flags;
+    uint16_t valence;
+    HFS_cnid_t ID;
+    uint32_t created;
+    uint32_t modifd;
+    uint32_t backupd;
+    HFS_fdir_info_t finder_dir;
+    uint8_t  finder_pad[16];
+    uint32_t pad[4];
+} __attribute__ ((packed));
+
+typedef struct HFSP_fold_t HFSP_fold_t;
+struct HFSP_fold_t {
+    uint16_t type;
+    uint16_t flags;
+    uint32_t valence;
+    HFS_cnid_t ID;
+    uint32_t created;
+    uint32_t modifd;
+    uint32_t attrd;
+    uint32_t accessd;
+    uint32_t attrmd;
+    HFSP_BSD_info_t BSD_infos;
+    HFS_fdir_info_t finder_dir;
+    uint8_t  finder_pad[16];
+    uint32_t encoding;
+    uint32_t pad;
+} __attribute__ ((packed));
+
+typedef struct HFS_file_t HFS_file_t;
+struct HFS_file_t {
+    /* 0x00 */
+    uint16_t type;
+    uint8_t  flags;
+    uint8_t  ftype;
+    /* 0x04 */
+    HFS_file_info_t finder_file;
+    /* 0x14 */
+    HFS_cnid_t ID;
+    /* 0x18 */
+    uint16_t dstart;
+    uint32_t dlsize;
+    uint32_t dpsize;
+    uint16_t rstart;
+    /* 0x24 */
+    uint32_t rlsize;
+    uint32_t rpsize;
+    /* 0x2C */
+    uint32_t created;
+    /* 0x30 */
+    uint32_t modifd;
+    uint32_t backupd;
+    /* 0x38 */
+    uint8_t  finder_pad[16];
+    /* 0x48 */
+    uint16_t clump_size;
+    /* 0x4C */
+    HFS_extent_t extents[3];
+    /* 0x54 */
+} __attribute__ ((packed));
+
+typedef struct HFSP_file_t HFSP_file_t;
+struct HFSP_file_t {
+    /* 0x00 */
+    uint16_t type;
+    uint16_t flags;
+    uint32_t pad;
+    /* 0x08 */
+    HFS_cnid_t ID;
+    uint32_t created;
+    /* 0x10 */
+    uint32_t modifd;
+    uint32_t attrd;
+    uint32_t accessd;
+    uint32_t backupd;
+    /* 0x20 */
+    HFSP_BSD_info_t BSD_infos;
+    /* 0x30 */
+    HFS_file_info_t finder_file;
+    /* 0x40 */
+    uint8_t  finder_pad[16];
+    /* 0x50 */
+    uint32_t encoding;
+    uint32_t pad1[3];
+    HFSP_fork_t data;
+    HFSP_fork_t ressources;
+} __attribute__ ((packed));
+
+typedef struct HFS_thread_t HFS_thread_t;
+struct HFS_thread_t {
+    uint16_t type;
+    uint32_t pad[2];
+    HFS_cnid_t pid;
+    uint8_t pad0;
+    unsigned char name[32];
+} __attribute__ ((packed));
+
+typedef struct HFSP_thread_t HFSP_thread_t;
+struct HFSP_thread_t {
+    uint16_t type;
+    uint16_t pad;
+    HFS_cnid_t pid;
+    uint16_t nlen;
+    HFSP_unichr_t uniname[255];
+} __attribute__ ((packed));
+
+/* in memory structures */
+typedef struct hfs_vol_t hfs_vol_t;
+typedef struct hfs_btree_t hfs_btree_t;
+typedef struct hfs_rec_t hfs_rec_t;
+
+/* Volume/file structures */
+typedef struct hfs_extent_t {
+    uint32_t start;
+    uint32_t count;
+} hfs_extent_t;
+
+typedef struct hfs_fork_t {
+    hfs_vol_t *volume;
+    uint32_t nb_blocs;
+    hfs_extent_t extents[8];
+    hfs_rec_t *catrec;
+    hfs_rec_t *extrec;
+} hfs_fork_t;
+
+struct hfs_vol_t {
+    part_t *part;
+    int type;
+    HFS_cnid_t boot_id;
+    uint32_t embed_offset;
+    uint32_t start_offset;
+    uint32_t bsize;
+    hfs_fork_t alloc_file;
+    hfs_fork_t cat_file;
+    hfs_fork_t ext_file;
+    hfs_fork_t *boot_file;
+    hfs_btree_t *cat_tree;
+    hfs_btree_t *ext_tree;
+};
+
+/* Btree structures */
+/* Btree node */
+typedef struct hfs_bnode_t {
+    hfs_btree_t *tree;
+    uint32_t prev;
+    uint32_t next;
+    int type;
+    uint32_t nrecs;
+    hfs_rec_t *recs;
+} hfs_bnode_t;
+
+/* Cached Btree node */
+typedef struct hfs_cbnode_t hfs_cbnode_t;
+struct hfs_cbnode_t {
+    uint32_t location;
+    hfs_cbnode_t *next;
+    hfs_bnode_t bnode;
+};
+
+/* Bnode records */
+enum {
+    RECORD_HEAD = 0,
+    RECORD_IDX,
+    RECORD_CAT,
+    RECORD_EXT,
+};
+
+/* Header record */
+typedef struct hfs_headrec_t {
+    uint32_t rootnode;
+    uint32_t firstleaf;
+    uint32_t lastleaf;
+    uint32_t nodesize;
+} hfs_headrec_t;
+
+/* Index record */
+typedef struct hfs_idxrec_t {
+    HFS_cnid_t pid;
+    HFS_cnid_t uid;
+    unsigned char name[0x20];
+} hfs_idxrec_t;
+
+/* File extent records */
+/* TODO */
+typedef struct hfs_extrec_t {
+    HFS_cnid_t ID;
+} hfs_extrec_t;
+
+/* Catalog records */
+typedef struct hfs_catrec_t {
+    HFS_cnid_t ID;
+    HFS_cnid_t pid;
+    int type;
+    unsigned char name[0x20];
+    unsigned char finfo[9];
+    hfs_fork_t fork;
+} hfs_catrec_t;
+
+/* Generic record */
+struct hfs_rec_t {
+    hfs_bnode_t *node;
+    int type;
+    int num;
+    union {
+        hfs_headrec_t headrec;
+        hfs_idxrec_t  idxrec;
+        hfs_catrec_t  catrec;
+        hfs_extrec_t  extrec;
+    } u;
+};
+
+struct hfs_btree_t {
+    hfs_fork_t *file;
+    hfs_cbnode_t *cache;
+    hfs_rec_t *head_rec;
+    hfs_bnode_t *root_node;
+    hfs_rec_t *root_catrec;
+    hfs_rec_t *root_extrec;
+    uint32_t nodesize;
+    unsigned char *buf;
+    int type;
+    int (*compare)(int type, HFS_cnid_t cnid,
+                   const void *more, hfs_rec_t *rec, int rectype);
+};
+
+/* Unicode to ISO-8859-15, stolen from Linux nls */
+static unsigned char page00[256] = {
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0xa0, 0xa1, 0xa2, 0xa3, 0x00, 0xa5, 0x00, 0xa7, /* 0xa0-0xa7 */
+    0x00, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+    0xb0, 0xb1, 0xb2, 0xb3, 0x00, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+    0x00, 0xb9, 0xba, 0xbb, 0x00, 0x00, 0x00, 0xbf, /* 0xb8-0xbf */
+    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
+    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+};
+
+static unsigned char page01[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0xbc, 0xbd, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0xa6, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0xbe, 0x00, 0x00, 0x00, 0x00, 0xb4, 0xb8, 0x00, /* 0x78-0x7f */
+};
+
+static unsigned char page20[256] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+    0x00, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+};
+
+static unsigned char *page_uni2charset[256] = {
+    page00, page01, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   
+    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+    
+    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+    
+    page20, NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+    NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,   NULL,
+};
+
+static int uni2char (uint16_t uni, unsigned char *out)
+{
+    unsigned char *uni2charset;
+    unsigned char cl = uni & 0x00ff;
+    unsigned char ch = (uni & 0xff00) >> 8;
+
+    uni2charset = page_uni2charset[ch];
+    if (uni2charset && uni2charset[cl])
+        *out = uni2charset[cl];
+    else
+        return -1;
+
+    return 0;
+}
+
+static void hfs_get_str (unsigned char *out, int len, uint16_t *hfs_str)
+{
+    int i;
+    char c;
+    for (i = 0; i < len; i++) {
+        if (uni2char(*hfs_str++, &c) < 0)
+            c = '?';
+        out[i] = c;
+    }
+    out[i] = '\0';
+}
+
+/* Locate a bloc in the partition given a file and an offset */
+static uint32_t hfs_get_bloc (hfs_fork_t *file, uint32_t bloc)
+{
+    hfs_vol_t *volume;
+    hfs_extent_t *extent;
+    uint32_t abloc, aoffset;
+    int i;
+    
+    volume = file->volume;
+    abloc = bloc / volume->bsize;
+    aoffset = bloc - (abloc * volume->bsize);
+    extent = file->extents;
+#if 0
+    HFS_DPRINTF("Look for bloc %08x => %08x %08x (%08x)\n",
+                bloc, abloc, aoffset, volume->bsize);
+#endif
+    for (i = 0; i < 8; i++) {
+#if 0
+        HFS_DPRINTF("Check extent %d %08x %08x (%08x)\n",
+                    i, extent->start, extent->count, abloc);
+#endif
+        if (extent->count == 0)
+            break;
+        if (abloc < extent->count) {
+            return volume->start_offset + /*volume->embed_offset +*/
+                ((extent->start + abloc) * volume->bsize) + aoffset;
+        }
+        abloc -= extent->count;
+        extent++;
+    }
+    HFS_ERROR("Block %d not found\n", bloc);
+
+    return -1;
+}
+
+/* Convert HFS/HFS plus extent/fork records to memory structure */
+static inline void hfs_get_extent (hfs_extent_t *dst, HFS_extent_t *src)
+{
+    dst->start = src->start_block;
+    dst->count = src->block_count;
+}
+
+static void hfs_get_fork (hfs_fork_t *dst, uint32_t blocs,
+                          HFS_extent_t *extents)
+{
+    int i;
+
+    dst->nb_blocs = blocs;
+    for (i = 0; i < 3; i++) {
+        hfs_get_extent(&dst->extents[i], &extents[i]);
+    }
+    memset(&dst->extents[3], 0, 5 * sizeof(hfs_extent_t));
+}
+
+static inline void hfsp_get_extent (hfs_extent_t *dst, HFSP_extent_t *src)
+{
+    dst->start = src->start_block;
+    dst->count = src->block_count;
+}
+
+static void hfsp_get_fork (hfs_fork_t *dst, uint32_t blocs,
+                           HFSP_extent_t *extents)
+{
+    int i;
+
+    dst->nb_blocs = blocs;
+    for (i = 0; i < 8; i++) {
+        hfsp_get_extent(&dst->extents[i], &extents[i]);
+    }
+}
+
+static void hfs_dump_fork (hfs_fork_t *fork)
+{
+    int i;
+
+    HFS_DPRINTF("Nb blocs: %d\n", fork->nb_blocs);
+    for (i = 0; i < 8; i++) {
+        if (fork->extents[i].count == 0)
+            break;
+        HFS_DPRINTF("  extent %d: start: %08x count: %08x\n",
+                    i, fork->extents[i].start, fork->extents[i].count);
+    }
+}
+
+/* Btree nodes cache */
+static inline void *hfs_brec_get (HFS_bnode_t *node, uint32_t nodesize, int nb)
+{
+    uint16_t *off;
+
+    if (nb < 1 || nb > node->nrecs) {
+        HFS_ERROR("nb=%d nrec=%d\n", nb, node->nrecs);
+        return NULL;
+    }
+    off = (void *)((char *)node + nodesize);
+    off -= nb;
+    HFS_DPRINTF("%d => %02x node %p off %p %p %d\n",
+                nb, *off, node, off, (char *)node + nodesize, nodesize);
+    
+    return (char *)node + *off;
+}
+
+static hfs_bnode_t *hfs_bnode_get (hfs_btree_t *tree, uint32_t location)
+{
+    unsigned char *buffer, tmpbuf[HFS_NODE_SIZE];
+    void *HFS_recp;
+    HFS_bnode_t *Hnode;
+    HFS_headrec_t *Hhead;
+    HFSP_catkey_t *HPkey = NULL;
+    HFS_catkey_t *Hkey = NULL;
+    HFSP_thread_t *HPthread;
+    HFS_thread_t *Hthread;
+    HFSP_fold_t *HPdir;
+    HFS_fold_t *Hdir;
+    HFSP_file_t *HPfile;
+    HFS_file_t *Hfile;
+    hfs_headrec_t *head;
+    hfs_cbnode_t **cur;
+    hfs_bnode_t *node;
+    hfs_rec_t *rec;
+    uint32_t bloc, offset, bsize, *upID, nsize;
+    uint16_t *ptype;
+    int i, j, is_hfs;
+    
+#if 1
+    for (cur = &tree->cache; *cur != NULL; cur = &((*cur)->next)) {
+        if ((*cur)->location == location) {
+            HFS_DPRINTF("found node %08x in cache (%08x %08x)\n",
+                        location, (*cur)->bnode.prev, (*cur)->bnode.next);
+            return &(*cur)->bnode;
+        }
+    }
+#endif
+    /* Not found in cache, get it from disk */
+    head = &tree->head_rec->u.headrec;
+    if (tree->nodesize != 0) {
+        nsize = tree->nodesize;
+        buffer = tree->buf;
+    } else {
+        nsize = HFS_NODE_SIZE;
+        buffer = tmpbuf;
+    }
+    bsize = part_blocsize(tree->file->volume->part);
+    bloc = location * nsize / 512;
+    HFS_DPRINTF("Get node from %08x %08x %p\n",
+                bloc, nsize, tree->file->volume->part);
+    bloc = hfs_get_bloc(tree->file, bloc);
+    if (bloc == (uint32_t)-1)
+        return NULL;
+    HFS_DPRINTF("  => %08x\n", bloc);
+#if 0
+    offset = bloc % bsize;
+    bloc /= bsize;
+#else
+    offset = 0;
+#endif
+    HFS_DPRINTF("  => %08x %08x (%d)\n", bloc, offset, bsize);
+    Hnode = HFS_read_Hnode(tree->file->volume->part,
+                           bloc, offset, buffer, nsize);
+    if (Hnode == NULL) {
+        HFS_DPRINTF("No Hnode !\n");
+        return NULL;
+    }
+    *cur = malloc(sizeof(hfs_cbnode_t) + (Hnode->nrecs * sizeof(hfs_rec_t)));
+    if (*cur == NULL)
+        return NULL;
+    memset(*cur, 0, sizeof(hfs_cbnode_t) + (Hnode->nrecs * sizeof(hfs_rec_t)));
+    (*cur)->location = location;
+    node = &(*cur)->bnode;
+    node->tree = tree;
+    node->prev = Hnode->prev;
+    node->next = Hnode->next;
+    node->type = Hnode->type;
+    node->nrecs = Hnode->nrecs;
+    node->recs = (void *)(node + 1);
+    if (tree->nodesize == 0 && node->type != HFS_NODE_HEAD) {
+        HFS_ERROR("first node should be a header !\n");
+        return NULL;
+    }
+    if (node->type == HFS_NODE_HEAD) {
+        Hhead = HFS_get_headrec(Hnode + 1);
+        nsize = Hhead->nodesize;
+        if (nsize == 0)
+            nsize = HFS_NODE_SIZE;
+        HFS_DPRINTF("Set node size to %d\n", nsize);
+        tree->nodesize = nsize;
+        tree->buf = malloc(nsize);
+        if (tree->buf == NULL)
+            return NULL;
+        memset(tree->buf, 0, nsize);
+        buffer = tree->buf;
+        Hnode = HFS_read_Hnode(tree->file->volume->part,
+                               bloc, offset, buffer, nsize);
+        if (Hnode == NULL)
+            return NULL;
+    }
+    HFS_DPRINTF("New node %08x prev: %08x next: %08x type: %d nrecs: %d\n",
+                location, node->prev, node->next, node->type, node->nrecs);
+    is_hfs = tree->file->volume->type == FS_TYPE_HFS;
+    for (i = 0; i < (int)node->nrecs; i++) {
+        rec = &node->recs[i];
+        rec->node = node;
+        rec->num = i + 1;
+        HFS_recp = hfs_brec_get(Hnode, nsize, i + 1);
+        if (HFS_recp == NULL) {
+            HFS_ERROR("can't get record %d\n", i + 1);
+            continue;
+        }
+        if (is_hfs) {
+            Hkey = HFS_recp;
+#if 0
+            upID = (void *)(((uint32_t)Hkey + 2 + Hkey->len));
+#else
+            upID = (void *)(((uint32_t)Hkey + 2 + Hkey->len) & ~1);
+#endif
+        } else {
+            HPkey = HFS_recp;
+            upID = (void *)(((uint32_t)HPkey + 2 + HPkey->len) & ~1);
+        }
+        switch (node->type) {
+        case HFS_NODE_LEAF:
+            HFS_DPRINTF("record %d: leaf %p %p %d\n", i + 1, upID, HFS_recp,
+                        (char *)upID - (char *)HFS_recp);
+            rec->type = tree->type;
+            switch (rec->type) {
+            case RECORD_CAT:
+                ptype = (void *)upID;
+                if (is_hfs) {
+                    memcpy(rec->u.catrec.name, Hkey->name, Hkey->nlen);
+                    rec->u.catrec.name[Hkey->nlen] = '\0';
+                    rec->u.catrec.pid = Hkey->pID;
+                } else {
+                    hfs_get_str(rec->u.catrec.name,
+                                HPkey->nlen, HPkey->uniname);
+                    rec->u.catrec.pid = HPkey->pID;
+                }
+                rec->u.catrec.type = *ptype;
+                rec->u.catrec.fork.volume = tree->file->volume;
+                rec->u.catrec.fork.catrec = rec;
+                switch (*ptype) {
+                case HFS_CAT_FOLDER:
+                    Hdir = (void *)ptype;
+                    rec->u.catrec.ID = Hdir->ID;
+                    HFS_DPRINTF("HFS Catalog folder ID: %08x name '%s' %08x\n",
+                                rec->u.catrec.ID, rec->u.catrec.name,
+                                rec->u.catrec.pid);
+                    break;
+                case HFS_CAT_FILE:
+                    Hfile = (void *)ptype;
+                    rec->u.catrec.ID = Hfile->ID;
+                    memcpy(rec->u.catrec.finfo, &Hfile->finder_file, 8);
+                    rec->u.catrec.finfo[8] = '\0';
+                    for (j = 0; j < 3; j++) {
+                        hfs_get_extent(&rec->u.catrec.fork.extents[j],
+                                       &Hfile->extents[j]);
+#if 0
+                        HFS_DPRINTF("Extent %04x %04x => %08x %08x\n",
+                                    Hfile->extents[j].start_block,
+                                    Hfile->extents[j].block_count,
+                                    rec->u.catrec.fork.extents[j].start,
+                                    rec->u.catrec.fork.extents[j].count);
+#endif
+                    }
+                    memset(&rec->u.catrec.fork.extents[3], 0,
+                           5 * sizeof(hfs_extent_t));
+                    HFS_DPRINTF("HFS Catalog file ID: %08x name '%s' '%s' %08x\n",
+                                rec->u.catrec.ID, rec->u.catrec.name,
+                                rec->u.catrec.finfo, rec->u.catrec.pid);
+#if 0
+                    HFS_DPRINTF("Extent %08x %08x\n",
+                                rec->u.catrec.fork.extents[0].start,
+                                rec->u.catrec.fork.extents[0].count);
+#endif
+                    break;
+                case HFS_CAT_FOLDTH:
+                    Hthread = (void *)ptype;
+                    strcpy(rec->u.catrec.name, Hthread->name);
+                    rec->u.catrec.ID = rec->u.catrec.pid;
+                    rec->u.catrec.pid = Hthread->pid;
+                    HFS_DPRINTF("HFS Catalog folder thread '%s' %08x %08x\n",
+                                rec->u.catrec.name, rec->u.catrec.ID,
+                                rec->u.catrec.pid);
+                    continue;
+                case HFS_CAT_FILETH:
+                    Hthread = (void *)ptype;
+                    strcpy(rec->u.catrec.name, Hthread->name);
+                    rec->u.catrec.ID = rec->u.catrec.pid;
+                    rec->u.catrec.pid = Hthread->pid;
+                    HFS_DPRINTF("HFS Catalog file thread '%s' %08x %08x\n",
+                                rec->u.catrec.name, rec->u.catrec.ID,
+                                rec->u.catrec.pid);
+                    continue;
+                case HFSP_CAT_FOLDER:
+                    HPdir = (void *)ptype;
+                    rec->u.catrec.ID = HPdir->ID;
+                    HFS_DPRINTF("HFSplus Catalog folder ID: %08x name '%s'\n",
+                                rec->u.catrec.ID, rec->u.catrec.name);
+                    break;
+                case HFSP_CAT_FILE:
+                    HPfile = (void *)ptype;
+                    rec->u.catrec.ID = HPfile->ID;
+                    memcpy(rec->u.catrec.finfo, &HPfile->finder_file, 8);
+                    rec->u.catrec.finfo[8] = '\0';
+                    memcpy(&rec->u.catrec.fork, &HPfile->data,
+                           sizeof(HFSP_fork_t));
+                    HFS_DPRINTF("HFSPlus Catalog file ID: %08x name '%s' '%s'\n",
+                                rec->u.catrec.ID, rec->u.catrec.name,
+                                rec->u.catrec.finfo);
+                    HFS_DPRINTF("Extent %08x %08x\n",
+                                rec->u.catrec.fork.extents[0].start,
+                                rec->u.catrec.fork.extents[0].count);
+                    break;
+                case HFSP_CAT_FOLDTH:
+                    HPthread = (void *)ptype;
+                    rec->u.catrec.ID = rec->u.catrec.pid;
+                    rec->u.catrec.pid = HPthread->pid;
+                    hfs_get_str(rec->u.catrec.name,
+                                HPthread->nlen, HPthread->uniname);
+                    HFS_DPRINTF("HFSplus Catalog folder thread '%s'...\n",
+                                rec->u.catrec.name);
+                    break;
+                case HFSP_CAT_FILETH:
+                    HPthread = (void *)ptype;
+                    hfs_get_str(rec->u.catrec.name,
+                                HPthread->nlen, HPthread->uniname);
+                    rec->u.catrec.ID = rec->u.catrec.pid;
+                    rec->u.catrec.pid = HPthread->pid;
+                    HFS_DPRINTF("HFSplus Catalog file thread '%s'...\n",
+                                rec->u.catrec.name);
+                    break;
+                default:
+                    printf("Unknown catalog entry %d %d '%s' %d\n", rec->type,
+                           *ptype, rec->u.catrec.name, (char *)ptype - (char *)Hkey);
+                    continue;
+                }
+                break;
+            case RECORD_EXT:
+                /* TODO */
+                HFS_DPRINTF("Extent file entry\n");
+                continue;
+            default:
+                HFS_ERROR("Unknown entry\n");
+                continue;
+            }
+            break;
+        case HFS_NODE_IDX:
+            rec->type = RECORD_IDX;
+            rec->u.idxrec.uid = *upID;
+            if (is_hfs) {
+                rec->u.idxrec.pid = Hkey->pID;
+                memcpy(rec->u.idxrec.name, Hkey->name, Hkey->nlen);
+                rec->u.idxrec.name[Hkey->nlen] = '\0';
+                HFS_DPRINTF("HFS IDX record %d parent: %08x up: %08x name '%s'\n",
+                            i + 1, rec->u.idxrec.pid, rec->u.idxrec.uid,
+                            rec->u.idxrec.name);
+                HFS_DPRINTF("uidp : %d %d\n", (char *)upID - (char *)Hkey,
+                            (char *)(Hkey + 1) - (char *)Hkey);
+            } else {
+                rec->u.idxrec.pid = HPkey->pID;
+                hfs_get_str(rec->u.idxrec.name,
+                            HPkey->nlen, HPkey->uniname);
+                HFS_DPRINTF("HFSplus IDX record %d parent: %08x up: %08x "
+                            "name '%s'\n", i + 1, rec->u.idxrec.pid,
+                            rec->u.idxrec.uid, rec->u.idxrec.name);
+            }
+            break;
+        case HFS_NODE_HEAD:
+            Hhead = HFS_get_headrec(HFS_recp);
+            rec->type = RECORD_HEAD;
+            rec->u.headrec.rootnode = Hhead->rootnode;
+            rec->u.headrec.firstleaf = Hhead->firstleaf;
+            rec->u.headrec.lastleaf = Hhead->lastleaf;
+            rec->u.headrec.nodesize = Hhead->nodesize;
+            HFS_DPRINTF("Header record %d root: %08x first: %08x last: %08x "
+                        "size: %08x\n", i + 1, rec->u.headrec.rootnode,
+                        rec->u.headrec.firstleaf, rec->u.headrec.lastleaf,
+                        rec->u.headrec.nodesize);
+            node->nrecs = 1;
+            goto out;
+        case HFS_NODE_MAP:
+            /* TODO */
+        default:
+            continue;
+        }
+    }
+
+ out:
+    return node;
+}
+
+static inline hfs_rec_t *hfs_rec_get (hfs_bnode_t *node, int nb)
+{
+    if (nb < 1 || nb > (int)node->nrecs) {
+        HFS_ERROR("nb: %d min: %d max: %d\n", nb, 1, node->nrecs);
+        return NULL;
+    }
+
+    return &node->recs[nb - 1];
+}
+
+static inline hfs_bnode_t *hfs_bnode_prev (hfs_bnode_t *cur)
+{
+    if (cur->prev == 0x00000000)
+        return NULL;
+
+    return hfs_bnode_get(cur->tree, cur->prev);
+}
+
+static inline hfs_bnode_t *hfs_bnode_next (hfs_bnode_t *cur)
+{
+    if (cur->next == 0x00000000)
+        return NULL;
+
+    return hfs_bnode_get(cur->tree, cur->next);
+}
+
+unused static hfs_rec_t *hfs_rec_prev (hfs_rec_t *cur)
+{
+    hfs_bnode_t *curn;
+    int num;
+
+    num = cur->num;
+    curn = cur->node;
+    if (num == 1) {
+        curn = hfs_bnode_prev(curn);
+        if (curn == NULL)
+            return NULL;
+        num = curn->nrecs + 1;
+    }
+    
+    return hfs_rec_get(curn, num - 1);
+}
+
+unused static hfs_rec_t *hfs_rec_next (hfs_rec_t *cur)
+{
+    hfs_bnode_t *curn;
+    int num;
+
+    num = cur->num;
+    curn = cur->node;
+    if (num == (int)curn->nrecs) {
+        curn = hfs_bnode_next(curn);
+        if (curn == NULL)
+            return NULL;
+        num = 1;
+    }
+    
+    return hfs_rec_get(curn, num - 1);
+}
+
+static int hfs_cat_compare (int type, HFS_cnid_t cnid,
+                            const void *more, hfs_rec_t *rec, int rectype);
+
+/* Simplified Btree recurse function from Linux */
+static hfs_rec_t *hfs_rec_find (hfs_btree_t *tree,
+                                HFS_cnid_t cnid, const char *name, int rectype)
+{
+    hfs_bnode_t *curn;
+    hfs_rec_t *cur;
+    unsigned int i;
+    int ret;
+
+    /*
+     * This is an ugly scattering of #if, but it's wonderful for debugging
+     * hfs_rec_find().  If you set this to 1, then the loop will traverse
+     * and show all of the records in a node before descending the correct
+     * record.
+     */
+#define DEBUG_HFS_REC_FIND 0
+#if DEBUG_HFS_REC_FIND
+    hfs_rec_t *idx_cur;
+    unsigned int idx;
+    int idx_ret;
+#endif /* DEBUG_HFS_REC_FIND */
+
+    HFS_DPRINTF("look for ID: %08x '%s'\n", cnid, name);
+    cur = NULL;
+    ret = -1;
+    i = 0;
+    for (curn = tree->root_node; curn != NULL;) {
+#if DEBUG_HFS_REC_FIND
+        idx = 0;
+        idx_ret = 0;
+        idx_cur = NULL;
+#endif /* DEBUG_HFS_REC_FIND */
+        for (i = curn->nrecs; i != 0; i--) {
+            cur = hfs_rec_get(curn, i);
+            if (cur == NULL) {
+                HFS_ERROR("Cannot get record %d\n", i);
+                return NULL;
+            }
+            HFS_DPRINTF("Check record %d %d %p %p %p\n", i, cur->type, cur,
+                        curn->tree->compare, &hfs_cat_compare);
+            ret = (*curn->tree->compare)(cur->type, cnid, name, cur, rectype);
+            HFS_DPRINTF("\t%u:%d\n", i, ret);
+            if (ret >= 0) {
+#if !DEBUG_HFS_REC_FIND
+                break;
+#else
+                if (!idx) {
+                    idx = i;
+                    idx_ret = ret;
+                    idx_cur = cur;
+                }
+#endif /* DEBUG_HFS_REC_FIND */
+            }
+        }
+#if DEBUG_HFS_REC_FIND
+        if (idx) {
+            i = idx;
+            ret = idx_ret;
+            cur = idx_cur;
+        }
+#endif /* DEBUG_HFS_REC_FIND */
+        HFS_DPRINTF("ret=%d HFS_NODE=%02x RECORD=%02x\n",
+                    ret, curn->type, cur->type);
+        if (i == 0 ||                          /* exhausted all the records */
+            curn->type == HFS_NODE_LEAF) {     /* Can't descend any lower */
+            break;
+        }
+        HFS_DPRINTF("Recurse to record: %d %08x => %08x\n",
+                    i, cnid, cur->u.idxrec.uid);
+        curn = hfs_bnode_get(curn->tree, cur->u.idxrec.uid);
+    }
+    if (ret != 0 || curn == NULL) {
+        /* We won't find what we're looking for... */
+        HFS_DPRINTF("NOT FOUND\n");
+        return NULL;
+    }
+#if 0
+    if (ret != 0 && cur->u.catrec.ID != cnid) {
+        HFS_ERROR("%d %d\n", cur->u.catrec.ID, cnid);
+        return NULL;
+    }
+#endif
+    HFS_DPRINTF("found %p %p %d %p\n", cur, curn, i, hfs_rec_get(curn, i));
+    
+    return cur;
+}
+
+static inline hfs_rec_t *hfs_get_dir (hfs_btree_t *tree, HFS_cnid_t cnid,
+                                      const unsigned char *name)
+{
+    return hfs_rec_find(tree, cnid, name, 1);
+}
+
+static hfs_rec_t *hfs_get_dirfile (hfs_rec_t *dir, HFS_cnid_t cnid,
+                                   const unsigned char *name,
+                                   const unsigned char *info)
+{
+    hfs_btree_t *tree;
+    hfs_bnode_t *cur;
+    hfs_rec_t *rec;
+    hfs_catrec_t *frec;
+    int idx;
+
+    cur = dir->node;
+    tree = cur->tree;
+    for (idx = dir->num + 1;; idx++) {
+        if (idx > (int)cur->nrecs) {
+            HFS_DPRINTF("Go to next node %08x\n", cur->next);
+            cur = hfs_bnode_next(cur);
+            if (cur == NULL) {
+                HFS_ERROR("Node %08x not found\n", cur->next);
+                break;
+            }
+            idx = 1;
+        }
+        rec = hfs_rec_get(cur, idx);
+        if (rec == NULL) {
+            HFS_ERROR("Cannot get record %d\n", idx);
+            return NULL;
+        }
+        HFS_DPRINTF("Check record %d '%s' '%s' '%s' '%s'\n",
+                   idx, rec->u.catrec.name, rec->u.catrec.finfo, name, info);
+        if (rec->type == RECORD_IDX) {
+            continue;
+        }
+        frec = &rec->u.catrec;
+        if (frec->type != HFS_CAT_FILE && frec->type != HFS_CAT_FILETH &&
+            frec->type != HFSP_CAT_FILE && frec->type != HFSP_CAT_FILETH)
+            continue;
+        if (frec->pid != cnid) {
+            HFS_ERROR("Out of directory %08x %08x\n", cnid, frec->pid);
+            break;
+        }
+        if (info != NULL && memcmp(frec->finfo, info, strlen(info)) != 0)
+            continue;
+        /* Beware: HFS is case insensitive ! */
+        if (name != NULL && strcasecmp(frec->name, name) != 0)
+            continue;
+        return rec;
+    }
+
+    return NULL;
+}
+
+static hfs_btree_t *hfs_btree_open (hfs_fork_t *fork, int type,
+                                    int (*compare)(int type,
+                                                   HFS_cnid_t cnid,
+                                                   const void *more,
+                                                   hfs_rec_t *rec,
+                                                   int rectype))
+{
+    hfs_bnode_t *node;
+    hfs_rec_t *rec;
+    hfs_headrec_t *head;
+    hfs_btree_t *newt;
+    uint32_t bloc;
+
+    bloc = hfs_get_bloc(fork, 0);
+    if (bloc == (uint32_t)-1)
+        return NULL;
+    HFS_DPRINTF("Open btree: bloc=%08x\n", bloc);
+    /* Allocate tree */
+    newt = malloc(sizeof(hfs_btree_t));
+    if (newt == NULL)
+        return NULL;
+    memset(newt, 0, sizeof(hfs_btree_t));
+    newt->file = fork;
+    newt->cache = NULL;
+    newt->type = type;
+    newt->compare = compare;
+    /* Get tree header */
+    HFS_DPRINTF("Get first node\n");
+    node = hfs_bnode_get(newt, 0);
+    if (node == NULL) {
+        HFS_ERROR("Cannot get tree head\n");
+        return NULL;
+    }
+    HFS_DPRINTF("Get first record\n");
+    rec = hfs_rec_get(node, 1);
+    if (rec == NULL) {
+        HFS_ERROR("Cannot get first record\n");
+        return NULL;
+    }
+    if (rec->type != RECORD_HEAD) {
+        HFS_ERROR("Not an header record !\n");
+        return NULL;
+    }
+    head = &rec->u.headrec;
+    newt->head_rec = rec;
+    /* Get root node */
+    HFS_DPRINTF("Get root entry node: %08x\n", head->rootnode);
+    newt->root_node = hfs_bnode_get(newt, head->rootnode);
+    if (newt->root_node == NULL)
+        return NULL;
+    /* Get root directory record */
+    HFS_DPRINTF("Get root folder record\n");
+    newt->root_catrec = hfs_get_dir(newt, HFS_ROOT_FOLDER, "");
+    HFS_DPRINTF("Found root folder record: %p\n", newt->root_catrec);
+    if (newt->root_catrec == NULL)
+        return NULL;
+    
+    return newt;
+}
+
+static int hfs_cat_compare (int type, HFS_cnid_t cnid,
+                            const void *more, hfs_rec_t *rec, int rectype)
+{
+    hfs_idxrec_t *idxrec;
+    hfs_catrec_t *catrec;
+    const unsigned char *name;
+    HFS_cnid_t id;
+    int ret;
+    
+    if (type == RECORD_IDX) {
+        idxrec = &rec->u.idxrec;
+        id = idxrec->pid;
+        name = idxrec->name;
+        catrec = NULL;
+    } else {
+        catrec = &rec->u.catrec;
+        name = catrec->name;
+        if (type != RECORD_IDX &&
+            (catrec->type == HFS_CAT_FOLDTH ||
+             catrec->type == HFS_CAT_FILETH ||
+             catrec->type == HFSP_CAT_FOLDTH ||
+             catrec->type == HFSP_CAT_FILETH)) {
+            HFS_DPRINTF("CHECK FOLDER %08x %08x!\n", catrec->ID, catrec->pid);
+            id = catrec->ID;
+        } else {
+            id = catrec->pid;
+        }
+    }
+    HFS_DPRINTF("Compare cnid (%08x '%s') vs (%08x '%s') %08x %d\n",
+                cnid, (char *)more, id, name, catrec->type, rectype);
+    
+    /*
+     * Always diff Record_IDXs, but diff RECORDS_CATs iff they match the type
+     * being looked for: THREAD vs NON-THREAD (rectype).
+     */
+    ret = cnid - id;
+    
+    if (ret == 0 && type != RECORD_IDX) {
+        /* out on a leaf - don't compare different types */
+        if (rectype &&
+            (catrec->type == HFS_CAT_FILE ||
+             catrec->type == HFS_CAT_FOLDER ||
+             catrec->type == HFSP_CAT_FILE ||
+             catrec->type == HFSP_CAT_FOLDER)) {
+            /* looking for thread and this is a file/folder - keep looking */
+            ret = -1;
+        } else if (!rectype &&
+                   (catrec->type == HFS_CAT_FILETH ||
+                    catrec->type == HFS_CAT_FOLDTH ||
+                    catrec->type == HFSP_CAT_FILETH ||
+                    catrec->type == HFSP_CAT_FOLDTH)) {
+            /* looking for file/folder and this is a thread - keep looking */
+            ret = -1;
+        }
+    }
+
+    if (ret == 0 &&
+       /* Apparently there is still a match - further constrain it by
+        * checking if the name matches.  Name matchs should be
+        * skipped if we're looking for a thread and we've reached a
+        * leaf record (that case will match solely on the record
+        * type and the cnid which has already been done).
+        */
+        (type == RECORD_IDX ||
+         (!rectype &&
+          (catrec->type == HFS_CAT_FILE ||
+           catrec->type == HFS_CAT_FOLDER ||
+           catrec->type == HFSP_CAT_FILE ||
+           catrec->type == HFSP_CAT_FOLDER)))) {
+        /* HFS is case insensitive - HFSP *can* be case sensitive */
+        ret = strcasecmp(more, name);
+    }
+    
+    HFS_DPRINTF("ret %d catrec %p catrec->type %08x\n",
+                ret, catrec, catrec ? catrec->type : 0);
+    return ret;
+}
+
+static hfs_btree_t *hfs_cat_open (hfs_vol_t *volume)
+{
+    HFS_DPRINTF("Open HFS catalog\n");
+    return hfs_btree_open(&volume->cat_file, RECORD_CAT, &hfs_cat_compare);
+}
+
+unused static int hfs_ext_compare (unused int type, unused HFS_cnid_t cnid,
+                                   unused const void *more,
+                                   unused hfs_rec_t *rec)
+{
+    /* TODO */
+    return -1;
+}
+
+static hfs_btree_t *hfs_ext_open (unused hfs_vol_t *volume)
+{
+    HFS_DPRINTF("Open HFS extents file\n");
+#if 0
+    return hfs_btree_open(&volume->ext_file, RECORD_EXT, &hfs_ext_compare);
+#else
+    return NULL;
+#endif
+}
+
+static void hfs_map_boot_file (part_t *part, hfs_vol_t *volume,
+                               uint32_t *boot_start, uint32_t *boot_offset,
+                               uint32_t *boot_size)
+{
+    uint32_t bloc, size;
+
+    /* Now, patch the partition to register the boot file
+     * XXX: we "know" that only one extent is used...
+     *      this may not be true if booting from a hard drive...
+     */
+    volume->boot_file->volume = volume;
+    bloc = hfs_get_bloc(volume->boot_file, 0);
+    if (bloc == (uint32_t)(-1)) {
+        printf("Cannot get boot file start bloc\n");
+        return;
+    }
+    size = volume->boot_file->extents[0].count * volume->bsize;
+    //    printf("Map boot file bloc 0 to %08x\n", bloc);
+    part_set_boot_file(part, bloc, 0, size);
+    *boot_start = bloc;
+    *boot_size = size;
+    *boot_offset = 0;
+}
+
+static inode_t *fs_hfs_get_inode (inode_t *parent, const unsigned char *name)
+{
+    inode_t *new;
+    hfs_fork_t *pfile, *file;
+    hfs_rec_t *catrec, *extrec;
+    uint32_t size;
+    int i;
+
+    pfile = parent->private;
+    HFS_DPRINTF("Get inode '%s' %p %p %p %08x\n", name, pfile, pfile->catrec,
+                pfile->catrec->node->tree, pfile->catrec->u.catrec.pid);
+    catrec = hfs_rec_find(pfile->catrec->node->tree,
+                          pfile->catrec->u.catrec.ID, name, 0);
+#if 0
+    extrec = hfs_rec_find(pfile->extrec->node->tree,
+                          pfile->extrec->u.extrec.pid, name, 0);
+#else
+    extrec = NULL;
+#endif
+    if (catrec == NULL /* || extrec == NULL */)
+        return NULL;
+    new = malloc(sizeof(inode_t));
+    if (new == NULL)
+        return NULL;
+    memset(new, 0, sizeof(inode_t));
+    new->flags = 0;
+    file = &catrec->u.catrec.fork;
+    new->private = file;
+    size = 0;
+    for (i = 0; i < 8; i++) {
+        if (file->extents[i].count == 0)
+            break;
+        size += file->extents[i].count;
+    }
+    size *= file->volume->bsize;
+    new->size.bloc = size;
+    new->size.offset = 0;
+    HFS_DPRINTF("File: '%s'\n", name);
+    hfs_dump_fork(new->private); 
+   
+    return new;
+}
+
+static void fs_hfs_put_inode (unused inode_t *inode)
+{
+}
+
+static uint32_t fs_hfs_map_bloc (inode_t *inode, uint32_t bloc)
+{
+    return hfs_get_bloc(inode->private, bloc);
+}
+
+static inode_t *fs_hfs_get_special_inode (fs_t *fs, int type)
+{
+    hfs_vol_t *volume;
+    inode_t *bfile, *bdir, *cur;
+    hfs_rec_t *drec, *rec;
+    hfs_fork_t *fork;
+    uint32_t boot_start, boot_size, boot_offset;
+    HFS_cnid_t id;
+
+    volume = fs->private;
+    switch (type) {
+    case FILE_ROOT:
+        if (fs->root == NULL) {
+            volume->cat_tree = hfs_cat_open(volume);
+            volume->ext_tree = hfs_ext_open(volume);
+            if (volume->cat_tree == NULL /*|| volume->ext_tree == NULL*/) {
+                HFS_ERROR("Can't open volume catalog/extent files\n");
+                return NULL;
+            }
+            cur = malloc(sizeof(inode_t));
+            if (cur == NULL)
+                return NULL;
+            memset(cur, 0, sizeof(inode_t));
+            cur->flags = INODE_TYPE_DIR;
+            cur->private = &volume->cat_tree->root_catrec->u.catrec.fork;
+            cur->parent = NULL;
+        } else {
+            cur = fs->root;
+        }
+        return cur;
+    case FILE_BOOT:
+        if (fs->bootfile != NULL)
+            return fs->bootfile;
+        break;
+    case FILE_BOOTDIR:
+        if (fs->bootdir != NULL)
+            return fs->bootdir;
+        if (volume->boot_file != NULL) {
+            bfile = malloc(sizeof(inode_t));
+            if (bfile == NULL)
+                return NULL;
+            memset(bfile, 0, sizeof(inode_t));
+            fs->bootfile = bfile;
+            rec = volume->boot_file->catrec;
+            bfile->name = strdup(rec->u.catrec.name);
+            if (bfile->name == NULL) {
+                free(bfile);
+                fs->bootfile = NULL;
+                return NULL;
+            }
+            bfile->private = volume->boot_file;
+            bfile->flags = INODE_TYPE_FILE | INODE_FLAG_EXEC | INODE_FLAG_BOOT;
+            fs->bootdir = fs->root;
+            hfs_map_boot_file(fs->part, volume,
+                              &boot_start, &boot_offset, &boot_size);
+        }
+        break;
+    default:
+        return NULL;
+    }
+    HFS_DPRINTF("Look for boot file (%d)\n", volume->boot_id);
+    if (volume->boot_file == NULL ||
+        volume->boot_file->extents[0].count == 0) {
+        if (volume->boot_id != 0x00000000) {
+            /* Try to find regular MacOS bootfile */
+            drec = hfs_get_dir(volume->cat_tree, volume->boot_id, "");
+            if (drec == NULL) {
+                HFS_ERROR("Didn't find boot directory %d\n", volume->boot_id);
+                return NULL;
+            }
+            HFS_DPRINTF("Found boot directory '%s'\n", drec->u.catrec.name);
+            rec = hfs_get_dirfile(drec, volume->boot_id, NULL, "tbxi");
+        } else {
+            /* Try NetBSD boot */
+            drec = hfs_get_dir(volume->cat_tree, HFS_ROOT_FOLDER, "");
+            if (drec == NULL)
+                return NULL;
+            rec = hfs_get_dirfile(drec, HFS_ROOT_FOLDER, "ofwboot", NULL);
+            if (rec == NULL) {
+                rec = hfs_get_dirfile(drec, HFS_ROOT_FOLDER,
+                                      "ofwboot.xcf", NULL);
+                if (rec == NULL) {
+                    rec = hfs_get_dirfile(drec, HFS_ROOT_FOLDER,
+                                          "ofwboot.elf", NULL);
+                }
+            }
+            if (rec != NULL) {
+                volume->boot_id = rec->u.catrec.pid;
+                drec = hfs_get_dir(volume->cat_tree, volume->boot_id, "");
+            }
+        }
+        if (rec == NULL) {
+            HFS_ERROR("Didn't find boot file\n");
+            return NULL;
+        }
+        volume->boot_file = &rec->u.catrec.fork;
+        hfs_map_boot_file(fs->part, volume,
+                          &boot_start, &boot_offset, &boot_size);
+        HFS_DPRINTF("boot file mapped: %08x-%08x %08x\n",
+                    boot_start, boot_offset, boot_size);
+#if 0
+        hfs_treat_boot_file(fs->part, volume,
+                            &boot_start, &boot_offset, &boot_size);
+#endif
+        HFS_DPRINTF("Dump boot file\n");
+        hfs_dump_fork(volume->boot_file);
+        HFS_DPRINTF("boot file mapped: %08x-%08x %08x\n",
+                    boot_start, boot_offset, boot_size);
+    } else {
+        drec = hfs_get_dir(volume->cat_tree, HFS_ROOT_FOLDER, "");
+        if (drec == NULL)
+            return NULL;
+    }
+    rec = volume->boot_file->catrec;
+    fork = volume->boot_file;
+    HFS_DPRINTF("boot file: %p '%s' boot dir: %p '%s'\n",
+                rec, rec->u.catrec.name, drec, drec->u.catrec.name);
+    bfile = malloc(sizeof(inode_t));
+    if (bfile == NULL)
+        return NULL;
+    memset(bfile, 0, sizeof(inode_t));
+    fs->bootfile = bfile;
+    bfile->name = strdup(rec->u.catrec.name);
+    if (bfile->name == NULL) {
+        free(bfile);
+        return NULL;
+    }
+    bfile->private = fork;
+    bfile->flags = INODE_TYPE_FILE | INODE_FLAG_EXEC | INODE_FLAG_BOOT;
+    bfile->size.bloc = boot_size / part_blocsize(volume->part);
+    bfile->size.offset = boot_size % part_blocsize(volume->part);
+    HFS_DPRINTF("%s: look for parent ID: %08x\n", __func__, volume->boot_id);
+    bdir = NULL;
+    cur = NULL;
+    if (type == FILE_BOOT) {
+        cur = bfile;
+    }
+    for (id = volume->boot_id; id != HFS_ROOT_FOLDER;
+         id = drec->u.catrec.pid) {
+        drec = hfs_get_dir(volume->cat_tree, id, "");
+        if (drec == NULL)
+            return NULL;
+        bdir = malloc(sizeof(inode_t));
+        if (bdir == NULL)
+            return NULL;
+        memset(bdir, 0, sizeof(inode_t));
+        if (id == volume->boot_id) {
+            if (type == FILE_BOOTDIR)
+                cur = bdir;
+            fs->bootdir = bdir;
+        }
+        bdir->name = strdup(drec->u.catrec.name);
+        if (bdir->name == NULL) {
+            free(bdir);
+            return NULL;
+        }
+        bdir->private = &drec->u.catrec.fork;
+        bdir->flags = INODE_TYPE_DIR;
+        bfile->parent = bdir;
+        HFS_DPRINTF("%s: cache '%s' into '%s'\n",
+                    __func__, bfile->name, bdir->name);
+        fs_cache_add_inode(bdir, bfile);
+        bfile = bdir;
+    }
+    bfile->parent = fs->root;
+    HFS_DPRINTF("%s: cache '%s' into root dir\n", __func__, bfile->name);
+    fs_cache_add_inode(fs->root, bfile);
+    if (bdir == NULL) {
+        bdir = fs->root;
+        fs->bootdir = bdir;
+        if (type == FILE_BOOTDIR)
+            cur = bdir;
+    }
+    cur->fs = fs;
+    HFS_DPRINTF("boot file: %p '%s' boot dir: %p '%s'\n",
+                fs->bootfile, fs->bootfile->name,
+                fs->bootdir, fs->bootdir->name);
+    HFS_DPRINTF("boot fork %p rec %p %p %08x\n",
+                bfile->private, rec, rec->u.catrec.fork.catrec,
+                rec->u.catrec.ID);
+    HFS_DPRINTF("boot dir fork %p rec %p %p %08x %08x\n",
+                bdir->private, drec, drec->u.catrec.fork.catrec,
+                drec->u.catrec.ID, volume->boot_id);
+    HFS_DPRINTF("FS cat tree: %p\n", volume->cat_tree);
+
+    return cur;
+}
+
+static fs_ops_t hfs_fs_ops = {
+    &fs_hfs_get_inode,
+    &fs_hfs_put_inode,
+    &fs_hfs_map_bloc,
+    &fs_hfs_get_special_inode,
+};
+
+int fs_hfs_probe (part_t *part, uint32_t *size,
+                  fs_ops_t **fs_ops, unsigned char **name,
+                  void **private)
+{
+    unsigned char buffer[512];
+    HFSP_vh_t *hfsp_vh;
+    HFS_vh_t *hfs_vh;
+    hfs_vol_t *volume;
+    uint32_t embed_offset = 0, boot_id;
+    int type;
+
+    hfs_vh = HFS_read_volhead(part, HFS_VOLHEAD_SECTOR, 0, buffer, 512);
+    hfsp_vh = NULL;
+    if (hfs_vh == NULL) {
+        DPRINTF("Can't read HFS volume header\n");
+        return -1;
+    }
+    type = -1;
+    if (hfs_vh->signature == HFS_VOLHEAD_SIG) {
+        /* HFS volume */
+        printf("HFS volume\n");
+        if (hfs_vh->embed_sig == HFSPLUS_VOLHEAD_SIG) {
+            embed_offset = hfs_vh->embed_ext.start_block *
+                hfs_vh->alloc_size / HFS_SECTOR_SIZE;
+            embed_offset += hfs_vh->alloc_start;
+            printf("HFSplus embedded volume offset=%08x\n", embed_offset);
+            hfsp_vh = HFSP_read_volhead(part,
+                                        HFS_VOLHEAD_SECTOR + embed_offset,
+                                        0, buffer, 512);
+            goto handle_hfsp;
+        }
+        boot_id = hfs_vh->finder_info[0];
+        DPRINTF("HFS boot id : %d %04x\n", boot_id, boot_id);
+        volume = malloc(sizeof(hfs_vol_t));
+        if (volume == NULL)
+            return -1;
+        memset(volume, 0, sizeof(hfs_vol_t));
+        HFS_DPRINTF("sig: %x %x %x\n", hfs_vh->signature,
+                    hfs_vh->embed_sig, HFSPLUS_VOLHEAD_SIG);
+        HFS_DPRINTF("cr: %08x mod: %08x attr: %04x count: %04x\n",
+                    hfs_vh->create_date, hfs_vh->modify_date,
+                    hfs_vh->attributes, hfs_vh->root_file_count);
+        HFS_DPRINTF("alloc ptr: %04x blocs: %04x size: %08x bmap %04x\n",
+                    hfs_vh->alloc_ptr, hfs_vh->alloc_blocs, hfs_vh->alloc_size,
+                    hfs_vh->bitmap_start);
+        volume->bsize = hfs_vh->alloc_size / HFS_SECTOR_SIZE;
+        volume->start_offset = hfs_vh->alloc_start;
+        /* Alloc file */
+        volume->alloc_file.volume = volume;
+        volume->alloc_file.nb_blocs = hfs_vh->alloc_size * volume->bsize;
+        volume->alloc_file.extents[0].start = 0;
+        volume->alloc_file.extents[0].count = hfs_vh->alloc_size;
+        /* Catalog file */
+        volume->cat_file.volume = volume;
+        hfs_get_fork(&volume->cat_file, hfs_vh->cat_size, hfs_vh->cat_rec);
+        /* Extents file */
+        volume->ext_file.volume = volume;
+        hfs_get_fork(&volume->ext_file, hfs_vh->ext_size, hfs_vh->ext_rec);
+        *size = hfs_vh->alloc_blocs * volume->bsize;
+        *name = strdup(hfs_vh->label);
+        if (*name == NULL)
+            return -1;
+        type = FS_TYPE_HFS;
+    } else {
+        hfsp_vh = HFSP_read_volhead(part, HFS_VOLHEAD_SECTOR, 0, buffer, 512);
+    handle_hfsp:
+        if (hfsp_vh == NULL) {
+            DPRINTF("Can't read HFS+ volume header\n");
+            return -1;
+        }
+        if (hfsp_vh->signature != HFSPLUS_VOLHEAD_SIG) {
+            DPRINTF("Bad HFS+ signature %02x %02x\n",
+                    hfsp_vh->signature, HFSPLUS_VOLHEAD_SIG);
+            return -1;
+        }
+        /* HFS+ volume */
+        printf("HFSplus volume\n");
+        volume = malloc(sizeof(hfs_vol_t));
+        if (volume == NULL)
+            return -1;
+        memset(volume, 0, sizeof(hfs_vol_t));
+        volume->embed_offset = embed_offset;
+        volume->start_offset = embed_offset;
+        volume->bsize = hfsp_vh->blocksize / HFS_SECTOR_SIZE;
+        //        volume->bsize = 2048;
+        /* Boot file */
+        HFS_DPRINTF("Boot file: %d %d\n",
+                    hfsp_vh->start_file.total_blocks,
+                    hfsp_vh->start_file.extents[0].block_count);
+        if (hfsp_vh->start_file.total_blocks != 0) {
+            volume->boot_file = malloc(sizeof(hfs_fork_t));
+            memset(volume->boot_file, 0, sizeof(hfs_fork_t));
+            volume->boot_file->volume = volume;
+            hfsp_get_fork(volume->boot_file,
+                          hfsp_vh->start_file.total_blocks,
+                          hfsp_vh->start_file.extents);
+            boot_id = 2;
+        } else {
+            boot_id = hfsp_vh->finder_info[0];
+        }
+            DPRINTF("HFS+ boot id : %d %04x %d\n", boot_id, boot_id,
+                    hfsp_vh->start_file.total_blocks);
+        /* Catalog file */
+        volume->cat_file.volume = volume;
+        hfsp_get_fork(&volume->cat_file,
+                      hfsp_vh->cat_file.total_blocks,
+                      hfsp_vh->cat_file.extents);
+        /* Extents file */
+        volume->ext_file.volume = volume;
+        hfsp_get_fork(&volume->ext_file,
+                      hfsp_vh->ext_file.total_blocks,
+                      hfsp_vh->ext_file.extents);
+        *size = hfsp_vh->total_blocks * volume->bsize;
+        type = FS_TYPE_HFSP;
+    }
+    volume->boot_id = boot_id;
+    volume->type = type;
+    HFS_DPRINTF("%s volume: type: %d bsize: %d start_offset: %d\n",
+                type == FS_TYPE_HFS ? "HFS" : "HFSplus",
+                volume->type, volume->bsize, volume->start_offset);
+    HFS_DPRINTF("Catalog file:\n");
+    hfs_dump_fork(&volume->cat_file);
+    HFS_DPRINTF("Extents file:\n");
+    hfs_dump_fork(&volume->ext_file);
+    if (volume->boot_file != NULL) {
+        HFS_DPRINTF("Boot file:\n");
+        hfs_dump_fork(volume->boot_file);
+    }
+    *fs_ops = &hfs_fs_ops;
+    HFS_DPRINTF("Set part to %p\n", part);
+    volume->part = part;
+    *private = volume;
+
+    return type;
+}