diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c index 77252a91..e2d8a257 100644 --- a/lib/blkid/probe.c +++ b/lib/blkid/probe.c @@ -1028,17 +1028,178 @@ static int probe_gfs2(struct blkid_probe *probe, return 1; } -static int probe_hfsplus(struct blkid_probe *probe __BLKID_ATTR((unused)), +void unicode_16be_to_utf8(unsigned char *str, int out_len, + const unsigned char *buf, int in_len) +{ + int i, j; + unsigned int c; + + for (i = j = 0; i + 2 <= in_len; i += 2) { + c = (buf[i] << 8) | buf[i+1]; + if (c == 0) { + str[j] = '\0'; + break; + } else if (c < 0x80) { + if (j+1 >= out_len) + break; + str[j++] = (unsigned char) c; + } else if (c < 0x800) { + if (j+2 >= out_len) + break; + str[j++] = (unsigned char) (0xc0 | (c >> 6)); + str[j++] = (unsigned char) (0x80 | (c & 0x3f)); + } else { + if (j+3 >= out_len) + break; + str[j++] = (unsigned char) (0xe0 | (c >> 12)); + str[j++] = (unsigned char) (0x80 | ((c >> 6) & 0x3f)); + str[j++] = (unsigned char) (0x80 | (c & 0x3f)); + } + } + str[j] = '\0'; +} + +static int probe_hfs(struct blkid_probe *probe __BLKID_ATTR((unused)), struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { - struct hfs_mdb *sbd = (struct hfs_mdb *)buf; + struct hfs_mdb *hfs = (struct hfs_mdb *) buf; + char uuid_str[17]; + __u64 uuid; + + if ((memcmp(hfs->embed_sig, "H+", 2) == 0) || + (memcmp(hfs->embed_sig, "HX", 2) == 0)) + return 1; /* Not hfs, but an embedded HFS+ */ + + uuid = blkid_le64(*((unsigned long long *) hfs->finder_info.id)); + if (uuid) { + sprintf(uuid_str, "%016llX", uuid); + blkid_set_tag(probe->dev, "UUID", uuid_str, 0); + } + blkid_set_tag(probe->dev, "LABEL", hfs->label, hfs->label_len); + return 0; +} + + +static int probe_hfsplus(struct blkid_probe *probe, + struct blkid_magic *id, + unsigned char *buf) +{ + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; + struct hfsplus_bnode_descriptor *descr; + struct hfsplus_bheader_record *bnode; + struct hfsplus_catalog_key *key; + struct hfsplus_vol_header *hfsplus; + struct hfs_mdb *sbd = (struct hfs_mdb *) buf; + unsigned int alloc_block_size; + unsigned int alloc_first_block; + unsigned int embed_first_block; + unsigned int off = 0; + unsigned int blocksize; + unsigned int cat_block; + unsigned int ext_block_start; + unsigned int ext_block_count; + unsigned int record_count; + unsigned int leaf_node_head; + unsigned int leaf_node_count; + unsigned int leaf_node_size; + unsigned int leaf_block; + unsigned int label_len; + int ext; + __u64 leaf_off, uuid; + char uuid_str[17], label[512]; /* Check for a HFS+ volume embedded in a HFS volume */ - if (memcmp(sbd->embed_sig, "H+", 2) == 0) + if (memcmp(sbd->signature, "BD", 2) == 0) { + if ((memcmp(sbd->embed_sig, "H+", 2) != 0) && + (memcmp(sbd->embed_sig, "HX", 2) != 0)) + /* This must be an HFS volume, so fail */ + return 1; + + alloc_block_size = blkid_be32(sbd->al_blk_size); + alloc_first_block = blkid_be16(sbd->al_bl_st); + embed_first_block = blkid_be16(sbd->embed_startblock); + off = (alloc_first_block * 512) + + (embed_first_block * alloc_block_size); + buf = get_buffer(probe, off + (id->bim_kboff * 1024), + sizeof(sbd)); + if (!buf) + return 1; + + hfsplus = (struct hfsplus_vol_header *) buf; + } + + hfsplus = (struct hfsplus_vol_header *) buf; + + if ((memcmp(hfsplus->signature, "H+", 2) != 0) && + (memcmp(hfsplus->signature, "HX", 2) != 0)) + return 1; + + uuid = blkid_le64(*((unsigned long long *) hfsplus->finder_info.id)); + if (uuid) { + sprintf(uuid_str, "%016llX", uuid); + blkid_set_tag(probe->dev, "UUID", uuid_str, 0); + } + + blocksize = blkid_be32(hfsplus->blocksize); + memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); + cat_block = blkid_be32(extents[0].start_block); + + buf = get_buffer(probe, off + (cat_block * blocksize), 0x2000); + if (!buf) return 0; - return 1; + bnode = (struct hfsplus_bheader_record *) + &buf[sizeof(struct hfsplus_bnode_descriptor)]; + + leaf_node_head = blkid_be32(bnode->leaf_head); + leaf_node_size = blkid_be16(bnode->node_size); + leaf_node_count = blkid_be32(bnode->leaf_count); + if (leaf_node_count == 0) + return 0; + + leaf_block = (leaf_node_head * leaf_node_size) / blocksize; + + /* get physical location */ + for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { + ext_block_start = blkid_be32(extents[ext].start_block); + ext_block_count = blkid_be32(extents[ext].block_count); + if (ext_block_count == 0) + return 0; + + /* this is our extent */ + if (leaf_block < ext_block_count) + break; + + leaf_block -= ext_block_count; + } + if (ext == HFSPLUS_EXTENT_COUNT) + return 0; + + leaf_off = (ext_block_start + leaf_block) * blocksize; + + buf = get_buffer(probe, off + leaf_off, leaf_node_size); + if (!buf) + return 0; + + descr = (struct hfsplus_bnode_descriptor *) buf; + record_count = blkid_be16(descr->num_recs); + if (record_count == 0) + return 0; + + if (descr->type != HFS_NODE_LEAF) + return 0; + + key = (struct hfsplus_catalog_key *) + &buf[sizeof(struct hfsplus_bnode_descriptor)]; + + if (blkid_be32(key->parent_id) != HFSPLUS_POR_CNID) + return 0; + + label_len = blkid_be16(key->unicode_len) * 2; + unicode_16be_to_utf8(label, sizeof(label), key->unicode, label_len); + blkid_set_tag(probe->dev, "LABEL", label, 0); + return 0; } #define LVM2_LABEL_SIZE 512 @@ -1162,8 +1323,9 @@ static struct blkid_magic type_array[] = { { "zfs", 264, 0, 8, "\0\0\x02\xf5\xb0\x07\xb1\x0c", probe_zfs }, { "zfs", 264, 0, 8, "\x0c\xb1\x07\xb0\xf5\x02\0\0", probe_zfs }, { "hfsplus", 1, 0, 2, "BD", probe_hfsplus }, - { "hfsplus", 1, 0, 2, "H+", 0 }, - { "hfs", 1, 0, 2, "BD", 0 }, + { "hfsplus", 1, 0, 2, "H+", probe_hfsplus }, + { "hfsplus", 1, 0, 2, "HX", probe_hfsplus }, + { "hfs", 1, 0, 2, "BD", probe_hfs }, { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 }, { "hpfs", 8, 0, 4, "I\350\225\371", 0 }, { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 }, diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h index b3cc26b9..ac3a0830 100644 --- a/lib/blkid/probe.h +++ b/lib/blkid/probe.h @@ -531,6 +531,78 @@ struct hfs_mdb { __u16 embed_blockcount; } __attribute__((packed)); + +#define HFS_NODE_LEAF 0xff +#define HFSPLUS_POR_CNID 1 + +struct hfsplus_bnode_descriptor { + __u32 next; + __u32 prev; + __u8 type; + __u8 height; + __u16 num_recs; + __u16 reserved; +} __attribute__((packed)); + +struct hfsplus_bheader_record { + __u16 depth; + __u32 root; + __u32 leaf_count; + __u32 leaf_head; + __u32 leaf_tail; + __u16 node_size; +} __attribute__((packed)); + +struct hfsplus_catalog_key { + __u16 key_len; + __u32 parent_id; + __u16 unicode_len; + __u8 unicode[255 * 2]; +} __attribute__((packed)); + +struct hfsplus_extent { + __u32 start_block; + __u32 block_count; +} __attribute__((packed)); + +#define HFSPLUS_EXTENT_COUNT 8 +struct hfsplus_fork { + __u64 total_size; + __u32 clump_size; + __u32 total_blocks; + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; +} __attribute__((packed)); + +struct hfsplus_vol_header { + __u8 signature[2]; + __u16 version; + __u32 attributes; + __u32 last_mount_vers; + __u32 reserved; + __u32 create_date; + __u32 modify_date; + __u32 backup_date; + __u32 checked_date; + __u32 file_count; + __u32 folder_count; + __u32 blocksize; + __u32 total_blocks; + __u32 free_blocks; + __u32 next_alloc; + __u32 rsrc_clump_sz; + __u32 data_clump_sz; + __u32 next_cnid; + __u32 write_count; + __u64 encodings_bmp; + struct hfs_finder_info finder_info; + struct hfsplus_fork alloc_file; + struct hfsplus_fork ext_file; + struct hfsplus_fork cat_file; + struct hfsplus_fork attr_file; + struct hfsplus_fork start_file; +} __attribute__((packed)); + + /* this is lvm's label_header & pv_header combined. */ #define LVM2_ID_LEN 32