Whamcloud - gitweb
libblkid: Enhance support for MacOS's hfs, hfsplus, and hfsx filesystems
authorTheodore Ts'o <tytso@mit.edu>
Fri, 22 Aug 2008 20:45:44 +0000 (16:45 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Fri, 22 Aug 2008 20:45:44 +0000 (16:45 -0400)
Add detection for hfsx filesystems.  Add label and uuid detetion for
hfs, hfsplus, and hfsx.

Addresses-Sourceforge-Feature-Requests: #2060292

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
lib/blkid/probe.c
lib/blkid/probe.h

index 77252a9..e2d8a25 100644 (file)
@@ -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 },
index b3cc26b..ac3a083 100644 (file)
@@ -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