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