diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/openhackware/src/libfs | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'roms/openhackware/src/libfs')
| -rw-r--r-- | roms/openhackware/src/libfs/core.c | 562 | ||||
| -rw-r--r-- | roms/openhackware/src/libfs/ext2.c | 32 | ||||
| -rw-r--r-- | roms/openhackware/src/libfs/hfs.c | 2007 | ||||
| -rw-r--r-- | roms/openhackware/src/libfs/isofs.c | 32 | ||||
| -rw-r--r-- | roms/openhackware/src/libfs/libfs.h | 129 | ||||
| -rw-r--r-- | roms/openhackware/src/libfs/raw.c | 178 | 
6 files changed, 2940 insertions, 0 deletions
diff --git a/roms/openhackware/src/libfs/core.c b/roms/openhackware/src/libfs/core.c new file mode 100644 index 00000000..9743a64a --- /dev/null +++ b/roms/openhackware/src/libfs/core.c @@ -0,0 +1,562 @@ +/* + * <fs.c> + * + * Open Hack'Ware BIOS file systems 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 + */ + +#include <stdlib.h> +#include <stdio.h> +#include "bios.h" +#include "libfs.h" +#undef FS_DPRINTF +#define FS_DPRINTF(fmt, args...) do { } while (0) + +static int special_file_get_type (const unsigned char *name) +{ +    int ret; + +    if (strcmp(name, "root") == 0) +        ret = FILE_ROOT; +    else if (strcmp(name, "boot") == 0) +        ret = FILE_BOOT; +    else if (strcmp(name, "bootdir") == 0) +        ret = FILE_BOOTDIR; +    else +        ret = FILE_UNKNOWN; + +    return ret; +} + +void fs_cache_add_inode (inode_t *parent, inode_t *inode) +{ +    inode_t **cur; + +    if (parent == NULL || inode == NULL) +        return; +    FS_DPRINTF("Add inode '%s' to '%s' cache\n", inode->name, parent->name); +    for (cur = &parent->child; *cur != NULL; cur = &((*cur)->next)) { +        if (strcmp((*cur)->name, inode->name) == 0) { +            return; +        } +    } +    *cur = inode; +} + +static inode_t *fs_cache_get_inode (inode_t *parent, +                                    const unsigned char *name) +{ +    inode_t *cur, *rec; +    int dec; + +    FS_DPRINTF("Look for '%s' into '%s' cache\n", name, parent->name); +    if (name == NULL || parent == NULL) +        return NULL; +    if (name[0] == '/' && name[1] == '\0') +        return parent->fs->root; +    if (is_special_file(name)) +        dec = strlen(FS_SPECIAL) + 2; +    else +        dec = 0; +    for (cur = parent->child; cur != NULL; cur = cur->next) { +        if (strcmp(cur->name + dec, name + dec) == 0) { +            cur->refcount++; +            for (rec = parent; rec != NULL; rec = rec->parent) +                rec->refcount++; +            break; +        } +    } +    cur = NULL; + +    return cur; +} + +static void fs_cache_put_inode (inode_t *inode) +{ +    void (*put_inode)(inode_t *inode); +    inode_t *cur, **upd; + +    if (inode != NULL && --inode->refcount == 0) { +        if (inode->parent == NULL) +            return; +        fs_cache_put_inode(inode->parent); +        upd = &inode->parent->child; +        for (cur = *upd; cur != NULL; cur = cur->next) { +            if (cur == inode) { +                (*upd) = cur->next; +                put_inode = inode->fs->fs_ops->put_inode; +                (*put_inode)(cur); +                FS_DPRINTF("Free inode '%s' from '%s' cache\n", +                           inode->name, inode->parent->name); +                free(cur); +                return; +            } +            upd = &cur; +        } +        FS_ERROR("didn't find inode in list !\n"); +    } +} + +static inode_t *fs_get_inode (inode_t *parent, const unsigned char *name) +{ +    inode_t *(*get_inode)(inode_t *parent, const unsigned char *name); +    inode_t *cur; + +    if (parent == NULL) { +        FS_ERROR("Invalide inode '%s' (NULL)\n", name); +        return NULL; +    } else { +        if (fs_inode_get_type(parent) != INODE_TYPE_DIR) { +            FS_ERROR("Try to recurse in a non-directory inode (%d)\n", +                     parent->flags); +            return NULL; +        } +    } +    if (is_special_file(name)) { +        int type; +        /* Special files */ +        FS_DPRINTF("look for special file '%s'\n", +                   name + strlen(FS_SPECIAL) + 2); +        type = special_file_get_type(name + strlen(FS_SPECIAL) + 2); +        if (type == FILE_UNKNOWN) { +            FS_ERROR("Unknown special file '%s'\n", +                     name + strlen(FS_SPECIAL) + 2); +            return NULL; +        } +        cur = (*parent->fs->fs_ops->get_special_inode)(parent->fs, type); +        FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n", +                   parent->fs->bootfile, parent->fs->bootfile->name, +                   &parent->fs->bootfile, +                   parent->fs->bootdir, parent->fs->bootdir->name, +                   &parent->fs->bootdir); +        switch (type) { +        case FILE_ROOT: +            parent->fs->root = cur; +            cur->parent = NULL; +            cur->fs = parent->fs; +            cur->name = strdup(""); +            return cur; +        case FILE_BOOT: +            parent->fs->bootfile = cur; +            break; +        case FILE_BOOTDIR: +            parent->fs->bootdir = cur; +            break; +        } +#if 0 +        parent = cur->parent; +#else +        cur->fs = parent->fs; +        return cur; +#endif +    } else { +        FS_DPRINTF("look for file '%s' in %p '%s'\n", name, parent, +                   parent->name); +        DPRINTF("look for file '%s' in %p '%s'\n", name, parent, +                   parent->name); +        cur = fs_cache_get_inode(parent, name); +        if (cur != NULL) { +            FS_DPRINTF("found inode '%s' %p in cache\n", name, cur); +            DPRINTF("found inode '%s' %p in cache\n", name, cur); +            return cur; +        } +        get_inode = parent->fs->fs_ops->get_inode; +        cur = (*get_inode)(parent, name); +        cur->name = strdup(name); +    } +    if (cur != NULL) { +        cur->parent = parent; +        cur->fs = parent->fs; +        fs_cache_add_inode(parent, cur); +        FS_DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n", +                   name, parent->name, cur->nb_blocs, cur->size.bloc, +                   cur->size.offset); +        DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n", +                name, parent->name, cur->nb_blocs, cur->size.bloc, +                cur->size.offset); +    } else { +        FS_ERROR("Inode '%s' not found in '%s'\n", name, parent->name); +    } + +    return cur; +} + +static inline void fs_put_inode (inode_t *inode) +{ +    fs_cache_put_inode(inode); +} + +static inode_t *_fs_walk (inode_t *parent, const unsigned char *name) +{ +    unsigned char tmpname[MAXNAME_LEN], *sl; +    inode_t *new, *subdir; +     +    FS_DPRINTF("'%s' %p\n", name, parent); +    DPRINTF("'%s' %p\n", name, parent); +    for (; *name == '/'; name++) +        continue; +    DPRINTF("'%s' %p\n", name, parent); +    strcpy(tmpname, name); +    sl = strchr(tmpname, '/'); +    if (sl != NULL) { +        *sl = '\0'; +        subdir = fs_get_inode(parent, tmpname); +        if (subdir == NULL) +            return NULL; +        new = _fs_walk(subdir, sl + 1); +    } else { +        new = fs_get_inode(parent, tmpname); +    } + +    return new; +} + +static inode_t *fs_walk (inode_t *parent, const unsigned char *name) +{ +    unsigned char tmpname[MAXNAME_LEN]; +    int len; +     +    FS_DPRINTF("'%s' %p\n", name, parent); +    DPRINTF("'%s' %p %p\n", name, parent, parent->fs->root); +    len = strlen(name); +    memcpy(tmpname, name, len + 1); +    if (tmpname[len - 1] == '/') +        tmpname[--len] = '\0'; +    if (parent == parent->fs->root && tmpname[0] == '\0') +        return parent->fs->root; + +    return _fs_walk(parent, tmpname); +} + +static unsigned char *fs_inode_get_path (inode_t *inode) +{ +    unsigned char tmpname[MAXNAME_LEN], *pname; +    int len; +    inode_t *parent; + +    parent = inode->parent; +    if (parent == NULL || (inode->name[0] == '/' && inode->name[1] == '\0')) { +        FS_DPRINTF("Reached root node '/'\n"); +        return strdup("/"); +    } +    FS_DPRINTF("Recurse to root '%s'...\n", inode->name); +    pname = fs_inode_get_path(parent); +    FS_DPRINTF("'%s' '%s'\n", pname, inode->name); +    len = strlen(pname); +    memcpy(tmpname, pname, len); +    if (tmpname[len - 1] != '/') +        tmpname[len++] = '/'; +    strcpy(tmpname + len, inode->name); +    free(pname); +    FS_DPRINTF(" => '%s'\n", tmpname); + +    return strdup(tmpname); +} + +static inline uint32_t fs_map_bloc (inode_t *inode, uint32_t bloc) +{ +    FS_DPRINTF("%s: inode %p bloc %d %p %p %p\n", __func__, inode, bloc, +               inode->fs, inode->fs->fs_ops, inode->fs->fs_ops->map_bloc); +    return (*inode->fs->fs_ops->map_bloc)(inode, bloc); +} + +fs_t *fs_probe (part_t *part, int set_raw) +{ +    fs_t *new; +    inode_t fake_inode; +    fs_ops_t *fs_ops = NULL; +    unsigned char *name = NULL; +    void *private = NULL; +    uint32_t size = 0; +    int type = FS_TYPE_UNKNOWN; + +    FS_DPRINTF("\n"); +    if (set_raw == 2) { +        DPRINTF("Check raw only\n"); +        goto raw_only; +    } +    DPRINTF("Probe ext2\n"); +    type = fs_ext2_probe(part, &size, &fs_ops, &name, &private); +    if (type == FS_TYPE_UNKNOWN) { +        DPRINTF("Probe isofs\n"); +        type = fs_isofs_probe(part, &size, &fs_ops, &name, &private); +        if (type == FS_TYPE_UNKNOWN) { +            DPRINTF("Probe HFS\n"); +            type = fs_hfs_probe(part, &size, &fs_ops, &name, &private); +            if (set_raw) { +                DPRINTF("Probe raw\n"); +            raw_only: +                type = fs_raw_probe(part, &size, &fs_ops, &name, &private); +            } +            if (type == FS_TYPE_UNKNOWN) { +                FS_ERROR("FS not identified\n"); +                return NULL; +            } +        } +    } +    if (fs_ops == NULL || size == 0) { +        FS_ERROR("Missing param: %p %d\n", fs_ops, size); +        return NULL; +    } +    new = malloc(sizeof(fs_t)); +    if (new == NULL) +        return NULL; +    new->type = type; +    new->part = part; +    new->size = size; +    new->fs_ops = fs_ops; +    new->name = name; +    new->private = private; +    /* Get root inode */ +    memset(&fake_inode, 0, sizeof(inode_t)); +    fake_inode.name = "fake_root"; +    fake_inode.fs = new; +    fake_inode.refcount = 1; +    fs_get_inode(&fake_inode, "\0" FS_SPECIAL "\0root"); +    if (new->root == NULL) { +        FS_ERROR("Didn't find root inode\n"); +        free(new); +        return NULL; +    } +    FS_DPRINTF("fs: %p root: %p root fs: %p\n", new, new->root, new->root->fs); +    FS_DPRINTF("OK\n"); + +    return new; +} + +dir_t *fs_opendir (fs_t *fs, const unsigned char *name) +{ +    inode_t *inode; +    dir_t *new; + +    FS_DPRINTF("'%s'\n", name); +    inode = fs_walk(fs->root, name); +    if (inode == NULL) +        return NULL; +    new = malloc(sizeof(dir_t)); +    new->inode = inode; + +    return new; +} + +dirent_t *fs_readdir (dir_t *dir) +{ +    void (*put_inode)(inode_t *inode); +    inode_t *inode; + +    inode = fs_get_inode(dir->inode, NULL); +    if (inode == NULL) +        return NULL; +    if (dir->cur == NULL) { +        dir->cur = malloc(sizeof(dirent_t)); +        dir->cur->dir = dir; +    } else { +        put_inode = dir->inode->fs->fs_ops->put_inode; +        (*put_inode)(dir->cur->inode); +    } +    dir->cur->inode = inode; +    dir->cur->dname = inode->name; + +    return dir->cur; +} + +unsigned char *fs_get_path (dirent_t *dirent) +{ +    return fs_inode_get_path(dirent->inode); +} + +void fs_closedir (dir_t *dir) +{ +    void (*put_inode)(inode_t *inode); + +    if (dir->cur != NULL) { +        put_inode = dir->inode->fs->fs_ops->put_inode; +        (*put_inode)(dir->cur->inode); +        free(dir->cur); +    } +    free(dir); +} + +inode_t *fs_open (fs_t *fs, const unsigned char *name) +{ +    inode_t *inode; + +    FS_DPRINTF("'%s'\n", name); +    inode = fs_walk(fs->root, name); +    if (inode != NULL) +        fs_seek(inode, 0, 0); + +    return inode; +} + +int fs_seek (inode_t *inode, uint32_t bloc, uint32_t pos) +{ +    if (inode == NULL || inode->fs == NULL) { +        ERROR("%s: no inode / fs ! %p %p\n", __func__, inode, +              inode == NULL ? NULL : inode->fs); +        return -1; +    } +    FS_DPRINTF("%08x %08x\n", bloc, pos); +    if (part_seek(inode->fs->part, fs_map_bloc(inode, bloc), pos) == -1) +        return -1; +    inode->vbloc = bloc; +    inode->vpos = pos; + +    return 0; +} + +int fs_read (inode_t *inode, void *buffer, int len) +{ +    uint32_t bsize, total; +    int done, tmp; +     +    bsize = part_blocsize(inode->fs->part); +    total = 0; +    if (fs_seek(inode, inode->vbloc, inode->vpos) < 0) +        return -1; +    for (; len != 0; len -= done) { +        tmp = bsize - inode->vpos; +        if (len < tmp) +            tmp = len; +        done = part_read(inode->fs->part, buffer, tmp); +        if (done < 0) +            return -1; +        inode->vpos += done; +        if (inode->vpos >= bsize) { +            inode->vbloc++; +            inode->vpos -= bsize; +        } +        buffer += done; +        total += done; +    } + +    return total; +} + +int fs_write (inode_t *inode, const void *buffer, unused int len) +{ +    uint32_t bsize, total; +    int done, tmp; +     +    bsize = part_blocsize(inode->fs->part); +    total = 0; +    for (; len != 0; len -= done) { +        tmp = bsize - inode->vpos; +        if (len < tmp) +            tmp = len; +        done = part_write(inode->fs->part, buffer, tmp); +        if (done < 0) +            return -1; +        inode->vpos += done; +        if (inode->vpos >= bsize) { +            inode->vbloc++; +            inode->vpos -= bsize; +            if (fs_seek(inode, inode->vbloc, inode->vpos) < 0) +                return -1; +        } +        buffer += done; +        total += done; +    } + +    return total; +} + +void fs_close (inode_t *inode) +{ +    fs_put_inode(inode); +} + +uint32_t fs_inode_get_type (inode_t *inode) +{ +    return inode->flags & INODE_TYPE_MASK; +} + +uint32_t fs_inode_get_flags (inode_t *inode) +{ +    return inode->flags & INODE_FLAG_MASK; +} + +uint32_t fs_inode_get_size (inode_t *inode) +{ +    DPRINTF("%s: (%d * %d) + %d\n", __func__, inode->size.bloc, +            part_blocsize(inode->fs->part), inode->size.offset); +    return (inode->size.bloc * part_blocsize(inode->fs->part)) + +        inode->size.offset; +} + +part_t *fs_part (fs_t *fs) +{ +    return fs->part; +} + +uint32_t fs_get_type (fs_t *fs) +{ +    return fs->type; +} + +part_t *fs_inode_get_part (inode_t *inode) +{ +    return inode->fs->part; +} + +inode_t *fs_get_bootdir (fs_t *fs) +{ +    FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs); +    if (fs->bootdir == NULL) { +        fs->bootdir = fs_get_inode(fs->root, "\0" FS_SPECIAL "\0bootdir"); +    } +    FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs); +    FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n", +               fs->bootfile, fs->bootfile->name, &fs->bootfile, +               fs->bootdir, fs->bootdir->name, &fs->bootdir); + +    return fs->bootdir; +} +unsigned char *fs_get_boot_dirname (fs_t *fs) +{ +    if (fs->bootdir == NULL) { +        fs_get_bootdir(fs); +        if (fs->bootdir == NULL) +            return NULL; +    } +    FS_DPRINTF("boot file: %p '%s' boot dir: %p '%s'\n", +               fs->bootfile, fs->bootfile->name, +               fs->bootdir, fs->bootdir->name); + +    return fs_inode_get_path(fs->bootdir); +} + +inode_t *fs_get_bootfile (fs_t *fs) +{ +    FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs); +    FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n", +               fs->bootfile, fs->bootfile->name, &fs->bootfile, +               fs->bootdir, fs->bootdir->name, &fs->bootdir); +    if (fs->bootfile == NULL) { +        if (fs->bootdir == NULL) +            fs_get_bootdir(fs); +        if (fs->bootdir == NULL) +            return NULL; +        fs->bootfile = fs_get_inode(fs->bootdir, "\0" FS_SPECIAL "\0boot"); +    } +    FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs); +    FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n", +               fs->bootfile, fs->bootfile->name, &fs->bootfile, +               fs->bootdir, fs->bootdir->name, &fs->bootdir); + +    return fs->bootfile; +} diff --git a/roms/openhackware/src/libfs/ext2.c b/roms/openhackware/src/libfs/ext2.c new file mode 100644 index 00000000..122f4089 --- /dev/null +++ b/roms/openhackware/src/libfs/ext2.c @@ -0,0 +1,32 @@ +/* + * <ext2.c> + * + * Open Hack'Ware BIOS ext2 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 + */ + +#include <stdlib.h> +#include "bios.h" +#include "libfs.h" + +/* ext2 filesystem */ +int fs_ext2_probe (unused part_t *part, unused uint32_t *size, +                   unused fs_ops_t **fs_ops, unused unsigned char **name, +                   unused void **private) +{ +    return -1; +} diff --git a/roms/openhackware/src/libfs/hfs.c b/roms/openhackware/src/libfs/hfs.c new file mode 100644 index 00000000..b2420ebe --- /dev/null +++ b/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; +} diff --git a/roms/openhackware/src/libfs/isofs.c b/roms/openhackware/src/libfs/isofs.c new file mode 100644 index 00000000..0e09094e --- /dev/null +++ b/roms/openhackware/src/libfs/isofs.c @@ -0,0 +1,32 @@ +/* + * <isofs.c> + * + * Open Hack'Ware BIOS ISO 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 + */ + +#include <stdlib.h> +#include "bios.h" +#include "libfs.h" + +/* ISOFS filesystem */ +int fs_isofs_probe (unused part_t *part, unused uint32_t *size, +                    unused fs_ops_t **fs_ops, unused unsigned char **name, +                    unused void **private) +{ +    return -1; +} diff --git a/roms/openhackware/src/libfs/libfs.h b/roms/openhackware/src/libfs/libfs.h new file mode 100644 index 00000000..1c05bcbb --- /dev/null +++ b/roms/openhackware/src/libfs/libfs.h @@ -0,0 +1,129 @@ +/* + * <libfs.h> + * + * Open Hack'Ware BIOS: file system library definitions + *  + * 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 + */ + +#if !defined(__OHW_LIBFS_H__) +#define __OHW_LIBFS_H__ + +//#define DEBUG_FS 1 +#define FS_SPECIAL "<special>" + +static inline int is_special_file (const unsigned char *name) +{ +    int splen = strlen(FS_SPECIAL); + +    return name[0] == '\0' && memcmp(name + 1, FS_SPECIAL, splen) == 0 && +        name[splen + 1] == '\0'; +} + +#if defined (DEBUG_FS) +#define FS_DPRINTF(fmt, args...) \ +do { dprintf("%s: " fmt, __func__ , ##args); } while (0) +#else +#define FS_DPRINTF(fmt, args...) \ +do { } while (0) +#endif +#define FS_ERROR(fmt, args...) \ +do { printf("ERROR in %s: " fmt, __func__ , ##args); } while (0) + +typedef struct fs_ops_t { +    inode_t *(*get_inode)(inode_t *parent, const unsigned char *name); +    void (*put_inode)(inode_t *inode); +    uint32_t (*map_bloc)(inode_t *inode, uint32_t bloc); +    inode_t *(*get_special_inode)(fs_t *fs, int type); +} fs_ops_t; + +#define MAXNAME_LEN 1024 + +struct fs_t { +    int type; +    part_t *part; +    inode_t *root; +    fs_ops_t *fs_ops; +    uint32_t size; +    unsigned char *name; +    inode_t *bootfile; +    inode_t *bootdir; +    void *private; +}; + +struct dir_t { +    inode_t *inode; +    dirent_t *cur; +    int pos; +}; + +/* All internals use inodes */ +struct inode_t { +    fs_t *fs; +    /* parent inode */ +    inode_t *parent; +    /* Next inode at the same level */ +    inode_t *next; +    /* First child inode */ +    inode_t *child; +    /* Private data */ +    int refcount; +    uint32_t flags; +    unsigned char *name; +    int nb_blocs; +    pos_t *blocs; +    pos_t size; +    void *private; +    uint32_t vbloc; +    uint32_t vpos; +}; + +/* Low-level helpers */ +enum { +    FILE_UNKNOWN = -1, +    FILE_ROOT    = 0, +    FILE_BOOT, +    FILE_BOOTDIR, +}; + +void fs_cache_add_inode (inode_t *parent, inode_t *inode); + +int fs_raw_probe (part_t *part, uint32_t *size, +                  fs_ops_t **fs_ops, unsigned char **name, +                  void **private); +int fs_ext2_probe (part_t *part, uint32_t *size, +                   fs_ops_t **fs_ops, unsigned char **name, +                   void **private); +int fs_isofs_probe (part_t *part, uint32_t *size, +                    fs_ops_t **fs_ops, unsigned char **name, +                    void **private); +int fs_hfs_probe (part_t *part, uint32_t *size, +                  fs_ops_t **fs_ops, unsigned char **name, +                  void **private); +int fs_raw_set_bootfile (part_t *part, +                         uint32_t start_bloc, uint32_t start_offset, +                         uint32_t size_bloc, uint32_t size_offset); + +enum { +    FS_TYPE_UNKNOWN = -1, +    FS_TYPE_RAW     = 0, +    FS_TYPE_EXT2, +    FS_TYPE_ISOFS, +    FS_TYPE_HFS, +    FS_TYPE_HFSP, +}; + +#endif /* !defined(__OHW_LIBFS_H__) */ diff --git a/roms/openhackware/src/libfs/raw.c b/roms/openhackware/src/libfs/raw.c new file mode 100644 index 00000000..9ecd23ad --- /dev/null +++ b/roms/openhackware/src/libfs/raw.c @@ -0,0 +1,178 @@ +/* + * <raw.c> + * + * Open Hack'Ware BIOS raw 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 + */ + +#include <stdlib.h> +#include <stdio.h> +#include "bios.h" +#include "../libpart/libpart.h" +#include "libfs.h" + +/* Raw filesystem (ie no filesystem) */ +static inode_t *fs_raw_get_inode (inode_t *parent, const unsigned char *name) +{ +    inode_t *new; +    fs_t *fs; +    int flags; + +    if (parent != NULL) { +        return NULL; +    } +    /* Open root inode */ +    flags = INODE_TYPE_DIR; +    fs = NULL; +    new = malloc(sizeof(inode_t)); +    memset(new, 0, sizeof(inode_t)); +    new->flags = flags; +    new->name = strdup(name); + +    return new; +} + +static void fs_raw_put_inode (inode_t *inode) +{ +    free(inode); +} + +static uint32_t fs_raw_map_bloc (unused inode_t *inode, uint32_t bloc) +{ +    if (inode != NULL +        /* XXX: can't figure out why I did this... */ +        /* && inode == inode->fs->bootfile*/ +        ) +         bloc += inode->blocs[0].bloc; + +    return bloc; +} + +static inode_t *fs_raw_get_special_inode (fs_t *fs, int type) +{ +    const unsigned char *name; +    inode_t *new, *parent, **inp; +    int flags; + +    new = NULL; +    name = NULL; +    parent = NULL; +    inp = NULL; +    flags = 0; +    switch (type) { +    case FILE_ROOT: +        if (fs->root != NULL) { +            new = fs->root; +        } else { +            flags = INODE_TYPE_DIR; +            parent = NULL; +            name = NULL; +            inp = &fs->root; +        } +        break; +    case FILE_BOOT: +        if (fs->bootfile != NULL) { +            dprintf("bootfile already exists\n"); +            new = fs->bootfile; +        } else { +            new = part_private_get(fs_part(fs)); +            if (fs->bootdir == NULL) { +                dprintf("Get boot directory\n"); +                fs->bootdir = fs_raw_get_special_inode(fs, FILE_BOOTDIR); +            } +            parent = fs->bootdir; +            if (new != NULL) { +                dprintf("Fix bootfile\n"); +                new->parent = parent; +                new->fs = fs; +            } else { +                dprintf("New bootfile\n"); +                flags = INODE_TYPE_FILE | INODE_FLAG_EXEC | INODE_FLAG_BOOT; +                name = "ofwboot"; +                inp = &fs->bootfile; +            } +        } +        break; +    case FILE_BOOTDIR: +        if (fs->bootdir != NULL) { +            new = fs->bootdir; +        } else { +            flags = INODE_TYPE_DIR; +            parent = fs->root; +            name = "boot"; +            inp = &fs->bootdir; +        } +        break; +    default: +        return NULL; +    } +    if (new == NULL) { +        new = malloc(sizeof(inode_t)); +        memset(new, 0, sizeof(inode_t)); +        new->flags = flags; +        new->parent = parent; +        if (name != NULL) +            new->name = strdup(name); +        new->fs = fs; +        *inp = new; +    } +     +    return new; +} + +static fs_ops_t fs_ops_raw = { +    &fs_raw_get_inode, +    &fs_raw_put_inode, +    &fs_raw_map_bloc, +    &fs_raw_get_special_inode, +}; + +int fs_raw_set_bootfile (part_t *part, +                         uint32_t start_bloc, uint32_t start_offset, +                         uint32_t size_bloc, uint32_t size_offset) +{ +    inode_t *new; + +    new = malloc(sizeof(inode_t)); +    if (new == NULL) +        return -1; +    DPRINTF("%s: pos %d %d size %d %d\n", __func__, start_bloc, start_offset, +            size_bloc, size_offset); +    memset(new, 0, sizeof(inode_t)); +    new->flags = INODE_TYPE_FILE | INODE_FLAG_EXEC | INODE_FLAG_BOOT; +    new->name = "ofwboot"; +    new->blocs[0].bloc = start_bloc; +    new->blocs[0].offset = start_offset; +    new->size.bloc = size_bloc; +    new->size.offset = size_offset; +    new->nb_blocs = size_bloc; +    part_private_set(part, new); + +    return 0; +} + +int fs_raw_probe (part_t *part, uint32_t *size, +                  fs_ops_t **fs_ops, unsigned char **name, +                  unused void **private) +{ +    DPRINTF("%s: %p map_bloc %p\n", __func__, &fs_ops_raw, &fs_raw_map_bloc); +    *fs_ops = &fs_ops_raw; +    *name = "Raw FS"; +    *size = part_size(part); + +    return FS_TYPE_RAW; +}  | 
