(root)/
util-linux-2.39/
libblkid/
src/
superblocks/
hfs.c
       1  /*
       2   * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
       3   * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
       4   *
       5   * This file may be redistributed under the terms of the
       6   * GNU Lesser General Public License.
       7   */
       8  #include <stdio.h>
       9  #include <stdlib.h>
      10  #include <unistd.h>
      11  #include <string.h>
      12  #include <inttypes.h>
      13  
      14  #include "superblocks.h"
      15  #include "md5.h"
      16  
      17  /* HFS / HFS+ */
      18  struct hfs_finder_info {
      19          uint32_t        boot_folder;
      20          uint32_t        start_app;
      21          uint32_t        open_folder;
      22          uint32_t        os9_folder;
      23          uint32_t        reserved;
      24          uint32_t        osx_folder;
      25          uint8_t         id[8];
      26  } __attribute__((packed));
      27  
      28  #define HFS_SECTOR_SIZE         512
      29  
      30  struct hfs_mdb {
      31          uint8_t         signature[2];
      32          uint32_t        cr_date;
      33          uint32_t        ls_Mod;
      34          uint16_t        atrb;
      35          uint16_t        nm_fls;
      36          uint16_t        vbm_st;
      37          uint16_t        alloc_ptr;
      38          uint16_t        nm_al_blks;
      39          uint32_t        al_blk_size;
      40          uint32_t        clp_size;
      41          uint16_t        al_bl_st;
      42          uint32_t        nxt_cnid;
      43          uint16_t        free_bks;
      44          uint8_t         label_len;
      45          uint8_t         label[27];
      46          uint32_t        vol_bkup;
      47          uint16_t        vol_seq_num;
      48          uint32_t        wr_cnt;
      49          uint32_t        xt_clump_size;
      50          uint32_t        ct_clump_size;
      51          uint16_t        num_root_dirs;
      52          uint32_t        file_count;
      53          uint32_t        dir_count;
      54          struct hfs_finder_info finder_info;
      55          uint8_t         embed_sig[2];
      56          uint16_t        embed_startblock;
      57          uint16_t        embed_blockcount;
      58  } __attribute__((packed));
      59  
      60  
      61  #define HFS_NODE_LEAF			0xff
      62  #define HFSPLUS_POR_CNID		1
      63  
      64  struct hfsplus_bnode_descriptor {
      65  	uint32_t		next;
      66  	uint32_t		prev;
      67  	uint8_t		type;
      68  	uint8_t		height;
      69  	uint16_t		num_recs;
      70  	uint16_t		reserved;
      71  } __attribute__((packed));
      72  
      73  struct hfsplus_bheader_record {
      74  	uint16_t		depth;
      75  	uint32_t		root;
      76  	uint32_t		leaf_count;
      77  	uint32_t		leaf_head;
      78  	uint32_t		leaf_tail;
      79  	uint16_t		node_size;
      80  } __attribute__((packed));
      81  
      82  struct hfsplus_catalog_key {
      83  	uint16_t	key_len;
      84  	uint32_t	parent_id;
      85  	uint16_t	unicode_len;
      86  	uint8_t		unicode[255 * 2];
      87  } __attribute__((packed));
      88  
      89  struct hfsplus_extent {
      90  	uint32_t		start_block;
      91  	uint32_t		block_count;
      92  } __attribute__((packed));
      93  
      94  #define HFSPLUS_EXTENT_COUNT		8
      95  struct hfsplus_fork {
      96  	uint64_t		total_size;
      97  	uint32_t		clump_size;
      98  	uint32_t		total_blocks;
      99  	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
     100  } __attribute__((packed));
     101  
     102  struct hfsplus_vol_header {
     103  	uint8_t		signature[2];
     104  	uint16_t		version;
     105  	uint32_t		attributes;
     106  	uint32_t		last_mount_vers;
     107  	uint32_t		reserved;
     108  	uint32_t		create_date;
     109  	uint32_t		modify_date;
     110  	uint32_t		backup_date;
     111  	uint32_t		checked_date;
     112  	uint32_t		file_count;
     113  	uint32_t		folder_count;
     114  	uint32_t		blocksize;
     115  	uint32_t		total_blocks;
     116  	uint32_t		free_blocks;
     117  	uint32_t		next_alloc;
     118  	uint32_t		rsrc_clump_sz;
     119  	uint32_t		data_clump_sz;
     120  	uint32_t		next_cnid;
     121  	uint32_t		write_count;
     122  	uint64_t		encodings_bmp;
     123  	struct hfs_finder_info finder_info;
     124  	struct hfsplus_fork alloc_file;
     125  	struct hfsplus_fork ext_file;
     126  	struct hfsplus_fork cat_file;
     127  	struct hfsplus_fork attr_file;
     128  	struct hfsplus_fork start_file;
     129  }  __attribute__((packed));
     130  
     131  #define HFSPLUS_SECTOR_SIZE        512
     132  
     133  static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
     134  {
     135  	static unsigned char const hash_init[UL_MD5LENGTH] = {
     136  		0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
     137  		0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
     138  	};
     139  	unsigned char uuid[UL_MD5LENGTH];
     140  	struct UL_MD5Context md5c;
     141  
     142  	if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
     143  		return -1;
     144  
     145  	ul_MD5Init(&md5c);
     146  	ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH);
     147  	ul_MD5Update(&md5c, hfs_info, len);
     148  	ul_MD5Final(uuid, &md5c);
     149  
     150  	uuid[6] = 0x30 | (uuid[6] & 0x0f);
     151  	uuid[8] = 0x80 | (uuid[8] & 0x3f);
     152  	return blkid_probe_set_uuid(pr, uuid);
     153  }
     154  
     155  static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
     156  {
     157  	struct hfs_mdb	*hfs;
     158  	int size;
     159  
     160  	hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
     161  	if (!hfs)
     162  		return errno ? -errno : 1;
     163  
     164  	if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
     165  	    (memcmp(hfs->embed_sig, "HX", 2) == 0))
     166  		return 1;	/* Not hfs, but an embedded HFS+ */
     167  
     168  	size = be32_to_cpu(hfs->al_blk_size);
     169  	if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
     170  		DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore"));
     171  		return 1;
     172  	}
     173  
     174  	hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
     175  
     176  	size = hfs->label_len;
     177  	if ((size_t) size > sizeof(hfs->label))
     178  		size = sizeof(hfs->label);
     179  	blkid_probe_set_label(pr, hfs->label, size);
     180  	return 0;
     181  }
     182  
     183  static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
     184  {
     185  	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
     186  	struct hfsplus_bnode_descriptor *descr;
     187  	struct hfsplus_bheader_record *bnode;
     188  	struct hfsplus_catalog_key *key;
     189  	struct hfsplus_vol_header *hfsplus;
     190  	struct hfs_mdb *sbd;
     191  	unsigned int alloc_block_size;
     192  	unsigned int alloc_first_block;
     193  	unsigned int embed_first_block;
     194  	unsigned int off = 0;
     195  	unsigned int blocksize;
     196  	unsigned int cat_block;
     197  	unsigned int ext_block_start = 0;
     198  	unsigned int ext_block_count;
     199  	unsigned int record_count;
     200  	unsigned int leaf_node_head;
     201  	unsigned int leaf_node_count;
     202  	unsigned int leaf_node_size;
     203  	unsigned int leaf_block;
     204  	int ext;
     205  	uint64_t leaf_off;
     206  	unsigned char *buf;
     207  
     208  	sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
     209  	if (!sbd)
     210  		return errno ? -errno : 1;
     211  
     212  	/* Check for a HFS+ volume embedded in a HFS volume */
     213  	if (memcmp(sbd->signature, "BD", 2) == 0) {
     214  		if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
     215  		    (memcmp(sbd->embed_sig, "HX", 2) != 0))
     216  			/* This must be an HFS volume, so fail */
     217  			return 1;
     218  
     219  		alloc_block_size = be32_to_cpu(sbd->al_blk_size);
     220  		alloc_first_block = be16_to_cpu(sbd->al_bl_st);
     221  		embed_first_block = be16_to_cpu(sbd->embed_startblock);
     222  		off = (alloc_first_block * 512) +
     223  			(embed_first_block * alloc_block_size);
     224  
     225  		buf = blkid_probe_get_buffer(pr,
     226  				off + (mag->kboff * 1024),
     227  				sizeof(struct hfsplus_vol_header));
     228  		hfsplus = (struct hfsplus_vol_header *) buf;
     229  
     230  	} else
     231  		hfsplus = blkid_probe_get_sb(pr, mag,
     232  				struct hfsplus_vol_header);
     233  
     234  	if (!hfsplus)
     235  		return errno ? -errno : 1;
     236  
     237  	if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
     238  	    (memcmp(hfsplus->signature, "HX", 2) != 0))
     239  		return 1;
     240  
     241  	hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
     242  
     243  	blocksize = be32_to_cpu(hfsplus->blocksize);
     244  	if (blocksize < HFSPLUS_SECTOR_SIZE)
     245  		return 1;
     246  
     247  	blkid_probe_set_fsblocksize(pr, blocksize);
     248  	blkid_probe_set_block_size(pr, blocksize);
     249  
     250  	memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
     251  	cat_block = be32_to_cpu(extents[0].start_block);
     252  
     253  	buf = blkid_probe_get_buffer(pr,
     254  			off + ((uint64_t) cat_block * blocksize), 0x2000);
     255  	if (!buf)
     256  		return errno ? -errno : 0;
     257  
     258  	bnode = (struct hfsplus_bheader_record *)
     259  		&buf[sizeof(struct hfsplus_bnode_descriptor)];
     260  
     261  	leaf_node_head = be32_to_cpu(bnode->leaf_head);
     262  	leaf_node_size = be16_to_cpu(bnode->node_size);
     263  	leaf_node_count = be32_to_cpu(bnode->leaf_count);
     264  
     265  	if (leaf_node_size < sizeof(struct hfsplus_bnode_descriptor) +
     266  	    sizeof(struct hfsplus_catalog_key) || leaf_node_count == 0)
     267  		return 0;
     268  
     269  	leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
     270  
     271  	/* get physical location */
     272  	for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
     273  		ext_block_start = be32_to_cpu(extents[ext].start_block);
     274  		ext_block_count = be32_to_cpu(extents[ext].block_count);
     275  		if (ext_block_count == 0)
     276  			return 0;
     277  
     278  		/* this is our extent */
     279  		if (leaf_block < ext_block_count)
     280  			break;
     281  
     282  		leaf_block -= ext_block_count;
     283  	}
     284  	if (ext == HFSPLUS_EXTENT_COUNT)
     285  		return 0;
     286  
     287  	leaf_off = ((uint64_t) ext_block_start + leaf_block) * blocksize;
     288  
     289  	buf = blkid_probe_get_buffer(pr,
     290  				(uint64_t) off + leaf_off,
     291  				leaf_node_size);
     292  	if (!buf)
     293  		return errno ? -errno : 0;
     294  
     295  	descr = (struct hfsplus_bnode_descriptor *) buf;
     296  	record_count = be16_to_cpu(descr->num_recs);
     297  	if (record_count == 0)
     298  		return 0;
     299  
     300  	if (descr->type != HFS_NODE_LEAF)
     301  		return 0;
     302  
     303  	key = (struct hfsplus_catalog_key *)
     304  		&buf[sizeof(struct hfsplus_bnode_descriptor)];
     305  
     306  	if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID ||
     307  	    be16_to_cpu(key->unicode_len) > 255)
     308  		return 0;
     309  
     310  	blkid_probe_set_utf8label(pr, key->unicode,
     311  			be16_to_cpu(key->unicode_len) * 2,
     312  			UL_ENCODE_UTF16BE);
     313  	return 0;
     314  }
     315  
     316  const struct blkid_idinfo hfs_idinfo =
     317  {
     318  	.name		= "hfs",
     319  	.usage		= BLKID_USAGE_FILESYSTEM,
     320  	.probefunc	= probe_hfs,
     321  	.flags		= BLKID_IDINFO_TOLERANT,
     322  	.magics		=
     323  	{
     324  		{ .magic = "BD", .len = 2, .kboff = 1 },
     325  		{ NULL }
     326  	}
     327  };
     328  
     329  const struct blkid_idinfo hfsplus_idinfo =
     330  {
     331  	.name		= "hfsplus",
     332  	.usage		= BLKID_USAGE_FILESYSTEM,
     333  	.probefunc	= probe_hfsplus,
     334  	.magics		=
     335  	{
     336  		{ .magic = "BD", .len = 2, .kboff = 1 },
     337  		{ .magic = "H+", .len = 2, .kboff = 1 },
     338  		{ .magic = "HX", .len = 2, .kboff = 1 },
     339  		{ NULL }
     340  	}
     341  };