busybox/util-linux/volume_id/hfs.c
<<
>>
Prefs
   1/*
   2 * volume_id - reads filesystem label and uuid
   3 *
   4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
   5 *
   6 *      This library is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU Lesser General Public
   8 *      License as published by the Free Software Foundation; either
   9 *      version 2.1 of the License, or (at your option) any later version.
  10 *
  11 *      This library is distributed in the hope that it will be useful,
  12 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14 *      Lesser General Public License for more details.
  15 *
  16 *      You should have received a copy of the GNU Lesser General Public
  17 *      License along with this library; if not, write to the Free Software
  18 *      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19 */
  20//config:config FEATURE_VOLUMEID_HFS
  21//config:       bool "hfs filesystem"
  22//config:       default y
  23//config:       depends on VOLUMEID
  24
  25//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_HFS) += hfs.o
  26
  27#include "volume_id_internal.h"
  28
  29struct hfs_finder_info{
  30        uint32_t        boot_folder;
  31        uint32_t        start_app;
  32        uint32_t        open_folder;
  33        uint32_t        os9_folder;
  34        uint32_t        reserved;
  35        uint32_t        osx_folder;
  36        uint8_t         id[8];
  37} PACKED;
  38
  39struct hfs_mdb {
  40        uint8_t         signature[2];
  41        uint32_t        cr_date;
  42        uint32_t        ls_Mod;
  43        uint16_t        atrb;
  44        uint16_t        nm_fls;
  45        uint16_t        vbm_st;
  46        uint16_t        alloc_ptr;
  47        uint16_t        nm_al_blks;
  48        uint32_t        al_blk_size;
  49        uint32_t        clp_size;
  50        uint16_t        al_bl_st;
  51        uint32_t        nxt_cnid;
  52        uint16_t        free_bks;
  53        uint8_t         label_len;
  54        uint8_t         label[27];
  55        uint32_t        vol_bkup;
  56        uint16_t        vol_seq_num;
  57        uint32_t        wr_cnt;
  58        uint32_t        xt_clump_size;
  59        uint32_t        ct_clump_size;
  60        uint16_t        num_root_dirs;
  61        uint32_t        file_count;
  62        uint32_t        dir_count;
  63        struct hfs_finder_info finder_info;
  64        uint8_t         embed_sig[2];
  65        uint16_t        embed_startblock;
  66        uint16_t        embed_blockcount;
  67} PACKED;
  68
  69struct hfsplus_bnode_descriptor {
  70        uint32_t        next;
  71        uint32_t        prev;
  72        uint8_t         type;
  73        uint8_t         height;
  74        uint16_t        num_recs;
  75        uint16_t        reserved;
  76} PACKED;
  77
  78struct hfsplus_bheader_record {
  79        uint16_t        depth;
  80        uint32_t        root;
  81        uint32_t        leaf_count;
  82        uint32_t        leaf_head;
  83        uint32_t        leaf_tail;
  84        uint16_t        node_size;
  85} PACKED;
  86
  87struct hfsplus_catalog_key {
  88        uint16_t        key_len;
  89        uint32_t        parent_id;
  90        uint16_t        unicode_len;
  91        uint8_t         unicode[255 * 2];
  92} PACKED;
  93
  94struct hfsplus_extent {
  95        uint32_t        start_block;
  96        uint32_t        block_count;
  97} PACKED;
  98
  99#define HFSPLUS_EXTENT_COUNT            8
 100struct hfsplus_fork {
 101        uint64_t        total_size;
 102        uint32_t        clump_size;
 103        uint32_t        total_blocks;
 104        struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
 105} PACKED;
 106
 107struct hfsplus_vol_header {
 108        uint8_t         signature[2];
 109        uint16_t        version;
 110        uint32_t        attributes;
 111        uint32_t        last_mount_vers;
 112        uint32_t        reserved;
 113        uint32_t        create_date;
 114        uint32_t        modify_date;
 115        uint32_t        backup_date;
 116        uint32_t        checked_date;
 117        uint32_t        file_count;
 118        uint32_t        folder_count;
 119        uint32_t        blocksize;
 120        uint32_t        total_blocks;
 121        uint32_t        free_blocks;
 122        uint32_t        next_alloc;
 123        uint32_t        rsrc_clump_sz;
 124        uint32_t        data_clump_sz;
 125        uint32_t        next_cnid;
 126        uint32_t        write_count;
 127        uint64_t        encodings_bmp;
 128        struct hfs_finder_info finder_info;
 129        struct hfsplus_fork alloc_file;
 130        struct hfsplus_fork ext_file;
 131        struct hfsplus_fork cat_file;
 132        struct hfsplus_fork attr_file;
 133        struct hfsplus_fork start_file;
 134} PACKED;
 135
 136#define HFS_SUPERBLOCK_OFFSET           0x400
 137#define HFS_NODE_LEAF                   0xff
 138#define HFSPLUS_POR_CNID                1
 139
 140static void FAST_FUNC hfs_set_uuid(struct volume_id *id, const uint8_t *hfs_id)
 141{
 142#define hfs_id_len 8
 143        md5_ctx_t md5c;
 144        uint8_t uuid[16];
 145        unsigned i;
 146
 147        for (i = 0; i < hfs_id_len; i++)
 148                if (hfs_id[i] != 0)
 149                        goto do_md5;
 150        return;
 151 do_md5:
 152        md5_begin(&md5c);
 153        md5_hash(&md5c, "\263\342\17\71\362\222\21\326\227\244\0\60\145\103\354\254", 16);
 154        md5_hash(&md5c, hfs_id, hfs_id_len);
 155        md5_end(&md5c, uuid);
 156        uuid[6] = 0x30 | (uuid[6] & 0x0f);
 157        uuid[8] = 0x80 | (uuid[8] & 0x3f);
 158        volume_id_set_uuid(id, uuid, UUID_DCE);
 159}
 160
 161int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/)
 162{
 163        uint64_t off = 0;
 164        unsigned blocksize;
 165        unsigned cat_block;
 166        unsigned ext_block_start;
 167        unsigned ext_block_count;
 168        int ext;
 169        unsigned leaf_node_head;
 170        unsigned leaf_node_count;
 171        unsigned leaf_node_size;
 172        unsigned leaf_block;
 173        uint64_t leaf_off;
 174        unsigned alloc_block_size;
 175        unsigned alloc_first_block;
 176        unsigned embed_first_block;
 177        unsigned record_count;
 178        struct hfsplus_vol_header *hfsplus;
 179        struct hfsplus_bnode_descriptor *descr;
 180        struct hfsplus_bheader_record *bnode;
 181        struct hfsplus_catalog_key *key;
 182        unsigned label_len;
 183        struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
 184        struct hfs_mdb *hfs;
 185        const uint8_t *buf;
 186
 187        dbg("probing at offset 0x%llx", (unsigned long long) off);
 188
 189        buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
 190        if (buf == NULL)
 191                return -1;
 192
 193        hfs = (struct hfs_mdb *) buf;
 194        if (hfs->signature[0] != 'B' || hfs->signature[1] != 'D')
 195                goto checkplus;
 196
 197        /* it may be just a hfs wrapper for hfs+ */
 198        if (hfs->embed_sig[0] == 'H' && hfs->embed_sig[1] == '+') {
 199                alloc_block_size = be32_to_cpu(hfs->al_blk_size);
 200                dbg("alloc_block_size 0x%x", alloc_block_size);
 201
 202                alloc_first_block = be16_to_cpu(hfs->al_bl_st);
 203                dbg("alloc_first_block 0x%x", alloc_first_block);
 204
 205                embed_first_block = be16_to_cpu(hfs->embed_startblock);
 206                dbg("embed_first_block 0x%x", embed_first_block);
 207
 208                off += (alloc_first_block * 512) +
 209                       (embed_first_block * alloc_block_size);
 210                dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off);
 211
 212                buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
 213                if (buf == NULL)
 214                        return -1;
 215                goto checkplus;
 216        }
 217
 218        if (hfs->label_len > 0 && hfs->label_len < 28) {
 219//              volume_id_set_label_raw(id, hfs->label, hfs->label_len);
 220                volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
 221        }
 222
 223        hfs_set_uuid(id, hfs->finder_info.id);
 224//      volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
 225        IF_FEATURE_BLKID_TYPE(id->type = "hfs";)
 226
 227        return 0;
 228
 229 checkplus:
 230        hfsplus = (struct hfsplus_vol_header *) buf;
 231        if (hfs->signature[0] == 'H')
 232                if (hfs->signature[1] == '+' || hfs->signature[1] == 'X')
 233                        goto hfsplus;
 234        return -1;
 235
 236 hfsplus:
 237        hfs_set_uuid(id, hfsplus->finder_info.id);
 238
 239        blocksize = be32_to_cpu(hfsplus->blocksize);
 240        dbg("blocksize %u", blocksize);
 241
 242        memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
 243        cat_block = be32_to_cpu(extents[0].start_block);
 244        dbg("catalog start block 0x%x", cat_block);
 245
 246        buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000);
 247        if (buf == NULL)
 248                goto found;
 249
 250        bnode = (struct hfsplus_bheader_record *)
 251                &buf[sizeof(struct hfsplus_bnode_descriptor)];
 252
 253        leaf_node_head = be32_to_cpu(bnode->leaf_head);
 254        dbg("catalog leaf node 0x%x", leaf_node_head);
 255
 256        leaf_node_size = be16_to_cpu(bnode->node_size);
 257        dbg("leaf node size 0x%x", leaf_node_size);
 258
 259        leaf_node_count = be32_to_cpu(bnode->leaf_count);
 260        dbg("leaf node count 0x%x", leaf_node_count);
 261        if (leaf_node_count == 0)
 262                goto found;
 263
 264        leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
 265
 266        /* get physical location */
 267        for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
 268                ext_block_start = be32_to_cpu(extents[ext].start_block);
 269                ext_block_count = be32_to_cpu(extents[ext].block_count);
 270                dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count);
 271
 272                if (ext_block_count == 0)
 273                        goto found;
 274
 275                /* this is our extent */
 276                if (leaf_block < ext_block_count)
 277                        break;
 278
 279                leaf_block -= ext_block_count;
 280        }
 281        if (ext == HFSPLUS_EXTENT_COUNT)
 282                goto found;
 283        dbg("found block in extent %i", ext);
 284
 285        leaf_off = (ext_block_start + leaf_block) * blocksize;
 286
 287        buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size);
 288        if (buf == NULL)
 289                goto found;
 290
 291        descr = (struct hfsplus_bnode_descriptor *) buf;
 292        dbg("descriptor type 0x%x", descr->type);
 293
 294        record_count = be16_to_cpu(descr->num_recs);
 295        dbg("number of records %u", record_count);
 296        if (record_count == 0)
 297                goto found;
 298
 299        if (descr->type != HFS_NODE_LEAF)
 300                goto found;
 301
 302        key = (struct hfsplus_catalog_key *)
 303                &buf[sizeof(struct hfsplus_bnode_descriptor)];
 304
 305        dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
 306        if (key->parent_id != cpu_to_be32(HFSPLUS_POR_CNID))
 307                goto found;
 308
 309        label_len = be16_to_cpu(key->unicode_len) * 2;
 310        dbg("label unicode16 len %i", label_len);
 311//      volume_id_set_label_raw(id, key->unicode, label_len);
 312        volume_id_set_label_unicode16(id, key->unicode, BE, label_len);
 313
 314 found:
 315//      volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
 316        IF_FEATURE_BLKID_TYPE(id->type = "hfsplus";)
 317
 318        return 0;
 319}
 320