]> err.no Git - util-linux/commitdiff
blkid: add missing hfs.c
authorKarel Zak <kzak@redhat.com>
Mon, 13 Oct 2008 10:17:19 +0000 (12:17 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 11 Feb 2009 22:21:46 +0000 (23:21 +0100)
Signed-off-by: Karel Zak <kzak@redhat.com>
libs/blkid/src/probers/hfs.c [new file with mode: 0644]

diff --git a/libs/blkid/src/probers/hfs.c b/libs/blkid/src/probers/hfs.c
new file mode 100644 (file)
index 0000000..9e1e632
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "blkidP.h"
+
+/* HFS / HFS+ */
+struct hfs_finder_info {
+        uint32_t        boot_folder;
+        uint32_t        start_app;
+        uint32_t        open_folder;
+        uint32_t        os9_folder;
+        uint32_t        reserved;
+        uint32_t        osx_folder;
+        uint8_t         id[8];
+} __attribute__((packed));
+
+struct hfs_mdb {
+        uint8_t         signature[2];
+        uint32_t        cr_date;
+        uint32_t        ls_Mod;
+        uint16_t        atrb;
+        uint16_t        nm_fls;
+        uint16_t        vbm_st;
+        uint16_t        alloc_ptr;
+        uint16_t        nm_al_blks;
+        uint32_t        al_blk_size;
+        uint32_t        clp_size;
+        uint16_t        al_bl_st;
+        uint32_t        nxt_cnid;
+        uint16_t        free_bks;
+        uint8_t         label_len;
+        uint8_t         label[27];
+        uint32_t        vol_bkup;
+        uint16_t        vol_seq_num;
+        uint32_t        wr_cnt;
+        uint32_t        xt_clump_size;
+        uint32_t        ct_clump_size;
+        uint16_t        num_root_dirs;
+        uint32_t        file_count;
+        uint32_t        dir_count;
+        struct hfs_finder_info finder_info;
+        uint8_t         embed_sig[2];
+        uint16_t        embed_startblock;
+        uint16_t        embed_blockcount;
+} __attribute__((packed));
+
+
+#define HFS_NODE_LEAF                  0xff
+#define HFSPLUS_POR_CNID               1
+
+struct hfsplus_bnode_descriptor {
+       uint32_t                next;
+       uint32_t                prev;
+       uint8_t         type;
+       uint8_t         height;
+       uint16_t                num_recs;
+       uint16_t                reserved;
+} __attribute__((packed));
+
+struct hfsplus_bheader_record {
+       uint16_t                depth;
+       uint32_t                root;
+       uint32_t                leaf_count;
+       uint32_t                leaf_head;
+       uint32_t                leaf_tail;
+       uint16_t                node_size;
+} __attribute__((packed));
+
+struct hfsplus_catalog_key {
+       uint16_t        key_len;
+       uint32_t        parent_id;
+       uint16_t        unicode_len;
+       uint8_t         unicode[255 * 2];
+} __attribute__((packed));
+
+struct hfsplus_extent {
+       uint32_t                start_block;
+       uint32_t                block_count;
+} __attribute__((packed));
+
+#define HFSPLUS_EXTENT_COUNT           8
+struct hfsplus_fork {
+       uint64_t                total_size;
+       uint32_t                clump_size;
+       uint32_t                total_blocks;
+       struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((packed));
+
+struct hfsplus_vol_header {
+       uint8_t         signature[2];
+       uint16_t                version;
+       uint32_t                attributes;
+       uint32_t                last_mount_vers;
+       uint32_t                reserved;
+       uint32_t                create_date;
+       uint32_t                modify_date;
+       uint32_t                backup_date;
+       uint32_t                checked_date;
+       uint32_t                file_count;
+       uint32_t                folder_count;
+       uint32_t                blocksize;
+       uint32_t                total_blocks;
+       uint32_t                free_blocks;
+       uint32_t                next_alloc;
+       uint32_t                rsrc_clump_sz;
+       uint32_t                data_clump_sz;
+       uint32_t                next_cnid;
+       uint32_t                write_count;
+       uint64_t                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));
+
+static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       struct hfs_mdb  *hfs;
+       uint64_t        uuid;
+
+       hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+       if (!hfs)
+               return -1;
+
+       if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+           (memcmp(hfs->embed_sig, "HX", 2) == 0))
+               return 1;       /* Not hfs, but an embedded HFS+ */
+
+       uuid = le64_to_cpu(*((uint64_t *) hfs->finder_info.id));
+       if (uuid)
+               blkid_probe_sprintf_uuid(pr,
+                               hfs->finder_info.id,
+                               sizeof(hfs->finder_info.id),
+                               "%016" PRIX64, uuid);
+
+       blkid_probe_set_label(pr, hfs->label, hfs->label_len);
+       return 0;
+}
+
+static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       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;
+       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;
+       int ext;
+       uint64_t leaf_off, uuid;
+       unsigned char *buf;
+
+       sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+       if (!sbd)
+               return -1;
+
+       /* Check for a HFS+ volume embedded in a HFS volume */
+       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 = be32_to_cpu(sbd->al_blk_size);
+               alloc_first_block = be16_to_cpu(sbd->al_bl_st);
+               embed_first_block = be16_to_cpu(sbd->embed_startblock);
+               off = (alloc_first_block * 512) +
+                       (embed_first_block * alloc_block_size);
+
+               buf = blkid_probe_get_buffer(pr,
+                               off + (mag->kboff * 1024),
+                               sizeof(struct hfsplus_vol_header));
+               hfsplus = (struct hfsplus_vol_header *) buf;
+
+       } else
+               hfsplus = blkid_probe_get_sb(pr, mag,
+                               struct hfsplus_vol_header);
+
+       if (!hfsplus)
+               return -1;
+
+       if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
+           (memcmp(hfsplus->signature, "HX", 2) != 0))
+               return 1;
+
+       uuid = be64_to_cpu(*((unsigned long long *) hfsplus->finder_info.id));
+       if (uuid)
+               blkid_probe_sprintf_uuid(pr,
+                               hfsplus->finder_info.id,
+                               sizeof(hfsplus->finder_info.id),
+                               "%016" PRIX64, uuid);
+
+       blocksize = be32_to_cpu(hfsplus->blocksize);
+       memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+       cat_block = be32_to_cpu(extents[0].start_block);
+
+       buf = blkid_probe_get_buffer(pr,
+                       off + (cat_block * blocksize), 0x2000);
+       if (!buf)
+               return 0;
+
+       bnode = (struct hfsplus_bheader_record *)
+               &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+       leaf_node_head = be32_to_cpu(bnode->leaf_head);
+       leaf_node_size = be16_to_cpu(bnode->node_size);
+       leaf_node_count = be32_to_cpu(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 = be32_to_cpu(extents[ext].start_block);
+               ext_block_count = be32_to_cpu(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 = blkid_probe_get_buffer(pr, off + leaf_off, leaf_node_size);
+       if (!buf)
+               return 0;
+
+       descr = (struct hfsplus_bnode_descriptor *) buf;
+       record_count = be16_to_cpu(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 (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
+               return 0;
+
+       blkid_probe_set_utf8label(pr, key->unicode,
+                       be16_to_cpu(key->unicode_len) * 2,
+                       BLKID_ENC_UTF16BE);
+       return 0;
+}
+
+const struct blkid_idinfo hfs_idinfo =
+{
+       .name           = "hfs",
+       .usage          = BLKID_USAGE_FILESYSTEM,
+       .probefunc      = probe_hfs,
+       .magics         =
+       {
+               { .magic = "BD", .len = 2, .kboff = 1 },
+               { NULL }
+       }
+};
+
+const struct blkid_idinfo hfsplus_idinfo =
+{
+       .name           = "hfsplus",
+       .usage          = BLKID_USAGE_FILESYSTEM,
+       .probefunc      = probe_hfsplus,
+       .magics         =
+       {
+               { .magic = "BD", .len = 2, .kboff = 1 },
+               { .magic = "H+", .len = 2, .kboff = 1 },
+               { .magic = "HX", .len = 2, .kboff = 1 },
+               { NULL }
+       }
+};