diff options
Diffstat (limited to 'qemu/roms/openhackware/src/libfs/hfs.c')
-rw-r--r-- | qemu/roms/openhackware/src/libfs/hfs.c | 2007 |
1 files changed, 2007 insertions, 0 deletions
diff --git a/qemu/roms/openhackware/src/libfs/hfs.c b/qemu/roms/openhackware/src/libfs/hfs.c new file mode 100644 index 000000000..b2420ebe8 --- /dev/null +++ b/qemu/roms/openhackware/src/libfs/hfs.c @@ -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; +} |