1/* 2 * volume_id - reads filesystem label and uuid 3 * 4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation version 2 of the License. 9 */ 10 11#ifndef _GNU_SOURCE 12#define _GNU_SOURCE 1 13#endif 14 15#ifdef HAVE_CONFIG_H 16# include <config.h> 17#endif 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <unistd.h> 22#include <string.h> 23#include <errno.h> 24#include <ctype.h> 25 26#include "libvolume_id.h" 27#include "util.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 39static struct 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 *hfs; 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 107static struct 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 *hfsplus; 135 136#define HFS_SUPERBLOCK_OFFSET 0x400 137#define HFS_NODE_LEAF 0xff 138#define HFSPLUS_POR_CNID 1 139 140static void hfsid_set_uuid(struct volume_id *id, const uint8_t *hfs_id) 141{ 142 143 volume_id_set_uuid(id, hfs_id, 0, UUID_64BIT_BE); 144} 145 146int volume_id_probe_hfs_hfsplus(struct volume_id *id, uint64_t off, uint64_t size) 147{ 148 unsigned int blocksize; 149 unsigned int cat_block; 150 unsigned int ext_block_start; 151 unsigned int ext_block_count; 152 int ext; 153 unsigned int leaf_node_head; 154 unsigned int leaf_node_count; 155 unsigned int leaf_node_size; 156 unsigned int leaf_block; 157 uint64_t leaf_off; 158 unsigned int alloc_block_size; 159 unsigned int alloc_first_block; 160 unsigned int embed_first_block; 161 unsigned int record_count; 162 struct hfsplus_bnode_descriptor *descr; 163 struct hfsplus_bheader_record *bnode; 164 struct hfsplus_catalog_key *key; 165 unsigned int label_len; 166 struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; 167 const uint8_t *buf; 168 169 info("probing at offset 0x%llx", (unsigned long long) off); 170 171 buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200); 172 if (buf == NULL) 173 return -1; 174 175 hfs = (struct hfs_mdb *) buf; 176 if (memcmp(hfs->signature, "BD", 2) != 0) 177 goto checkplus; 178 179 /* it may be just a hfs wrapper for hfs+ */ 180 if (memcmp(hfs->embed_sig, "H+", 2) == 0) { 181 alloc_block_size = be32_to_cpu(hfs->al_blk_size); 182 dbg("alloc_block_size 0x%x", alloc_block_size); 183 184 alloc_first_block = be16_to_cpu(hfs->al_bl_st); 185 dbg("alloc_first_block 0x%x", alloc_first_block); 186 187 embed_first_block = be16_to_cpu(hfs->embed_startblock); 188 dbg("embed_first_block 0x%x", embed_first_block); 189 190 off += (alloc_first_block * 512) + 191 (embed_first_block * alloc_block_size); 192 dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off); 193 194 buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200); 195 if (buf == NULL) 196 return -1; 197 goto checkplus; 198 } 199 200 if (hfs->label_len > 0 && hfs->label_len < 28) { 201 volume_id_set_label_raw(id, hfs->label, hfs->label_len); 202 volume_id_set_label_string(id, hfs->label, hfs->label_len) ; 203 } 204 205 hfsid_set_uuid(id, hfs->finder_info.id); 206 207 volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); 208 id->type = "hfs"; 209 210 return 0; 211 212checkplus: 213 hfsplus = (struct hfsplus_vol_header *) buf; 214 if (memcmp(hfsplus->signature, "H+", 2) == 0) 215 goto hfsplus; 216 if (memcmp(hfsplus->signature, "HX", 2) == 0) 217 goto hfsplus; 218 return -1; 219 220hfsplus: 221 hfsid_set_uuid(id, hfsplus->finder_info.id); 222 223 blocksize = be32_to_cpu(hfsplus->blocksize); 224 dbg("blocksize %u", blocksize); 225 226 memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); 227 cat_block = be32_to_cpu(extents[0].start_block); 228 dbg("catalog start block 0x%x", cat_block); 229 230 buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000); 231 if (buf == NULL) 232 goto found; 233 234 bnode = (struct hfsplus_bheader_record *) 235 &buf[sizeof(struct hfsplus_bnode_descriptor)]; 236 237 leaf_node_head = be32_to_cpu(bnode->leaf_head); 238 dbg("catalog leaf node 0x%x", leaf_node_head); 239 240 leaf_node_size = be16_to_cpu(bnode->node_size); 241 dbg("leaf node size 0x%x", leaf_node_size); 242 243 leaf_node_count = be32_to_cpu(bnode->leaf_count); 244 dbg("leaf node count 0x%x", leaf_node_count); 245 if (leaf_node_count == 0) 246 goto found; 247 248 leaf_block = (leaf_node_head * leaf_node_size) / blocksize; 249 250 /* get physical location */ 251 for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { 252 ext_block_start = be32_to_cpu(extents[ext].start_block); 253 ext_block_count = be32_to_cpu(extents[ext].block_count); 254 dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count); 255 256 if (ext_block_count == 0) 257 goto found; 258 259 /* this is our extent */ 260 if (leaf_block < ext_block_count) 261 break; 262 263 leaf_block -= ext_block_count; 264 } 265 if (ext == HFSPLUS_EXTENT_COUNT) 266 goto found; 267 dbg("found block in extent %i", ext); 268 269 leaf_off = (ext_block_start + leaf_block) * blocksize; 270 271 buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size); 272 if (buf == NULL) 273 goto found; 274 275 descr = (struct hfsplus_bnode_descriptor *) buf; 276 dbg("descriptor type 0x%x", descr->type); 277 278 record_count = be16_to_cpu(descr->num_recs); 279 dbg("number of records %u", record_count); 280 if (record_count == 0) 281 goto found; 282 283 if (descr->type != HFS_NODE_LEAF) 284 goto found; 285 286 key = (struct hfsplus_catalog_key *) 287 &buf[sizeof(struct hfsplus_bnode_descriptor)]; 288 289 dbg("parent id 0x%x", be32_to_cpu(key->parent_id)); 290 if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID) 291 goto found; 292 293 label_len = be16_to_cpu(key->unicode_len) * 2; 294 dbg("label unicode16 len %i", label_len); 295 volume_id_set_label_raw(id, key->unicode, label_len); 296 volume_id_set_label_unicode16(id, key->unicode, BE, label_len); 297 298found: 299 volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); 300 id->type = "hfsplus"; 301 302 return 0; 303} 304