/* * * * 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 */ #include #include #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; }