2008-10-13 12:17:19 +02:00
|
|
|
/*
|
2008-12-06 17:12:55 +01:00
|
|
|
* Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
|
2008-10-13 12:17:19 +02:00
|
|
|
* Copyright (C) 2008 Karel Zak <kzak@redhat.com>
|
|
|
|
*
|
2008-12-03 14:37:46 +01:00
|
|
|
* This file may be redistributed under the terms of the
|
|
|
|
* GNU Lesser General Public License.
|
2008-10-13 12:17:19 +02:00
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <inttypes.h>
|
|
|
|
|
2009-07-28 14:11:45 +02:00
|
|
|
#include "superblocks.h"
|
2008-12-06 17:12:55 +01:00
|
|
|
#include "md5.h"
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
2018-04-12 14:21:47 +02:00
|
|
|
#define HFS_SECTOR_SIZE 512
|
|
|
|
|
2008-10-13 12:17:19 +02:00
|
|
|
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));
|
|
|
|
|
2011-05-06 13:00:07 +02:00
|
|
|
#define HFSPLUS_SECTOR_SIZE 512
|
|
|
|
|
2009-01-14 16:25:56 +01:00
|
|
|
static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
|
2008-12-06 17:12:55 +01:00
|
|
|
{
|
2017-12-12 11:54:08 +01:00
|
|
|
static unsigned char const hash_init[UL_MD5LENGTH] = {
|
2008-12-06 17:12:55 +01:00
|
|
|
0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
|
|
|
|
0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
|
|
|
|
};
|
2017-12-12 11:54:08 +01:00
|
|
|
unsigned char uuid[UL_MD5LENGTH];
|
|
|
|
struct UL_MD5Context md5c;
|
2008-12-06 17:12:55 +01:00
|
|
|
|
2009-01-14 16:25:56 +01:00
|
|
|
if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
|
2008-12-08 16:28:40 +01:00
|
|
|
return -1;
|
2017-12-12 11:54:08 +01:00
|
|
|
|
|
|
|
ul_MD5Init(&md5c);
|
|
|
|
ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH);
|
|
|
|
ul_MD5Update(&md5c, hfs_info, len);
|
|
|
|
ul_MD5Final(uuid, &md5c);
|
|
|
|
|
2008-12-06 17:12:55 +01:00
|
|
|
uuid[6] = 0x30 | (uuid[6] & 0x0f);
|
|
|
|
uuid[8] = 0x80 | (uuid[8] & 0x3f);
|
|
|
|
return blkid_probe_set_uuid(pr, uuid);
|
|
|
|
}
|
|
|
|
|
2008-10-13 12:17:19 +02:00
|
|
|
static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
|
|
|
|
{
|
|
|
|
struct hfs_mdb *hfs;
|
2018-04-12 14:21:47 +02:00
|
|
|
int size;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
|
|
|
|
if (!hfs)
|
2014-03-20 11:03:50 +01:00
|
|
|
return errno ? -errno : 1;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
|
|
|
|
(memcmp(hfs->embed_sig, "HX", 2) == 0))
|
|
|
|
return 1; /* Not hfs, but an embedded HFS+ */
|
|
|
|
|
2018-04-12 14:21:47 +02:00
|
|
|
size = be32_to_cpu(hfs->al_blk_size);
|
|
|
|
if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
|
|
|
|
DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore"));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-01-14 16:25:56 +01:00
|
|
|
hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
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;
|
2019-02-09 10:34:52 +01:00
|
|
|
unsigned int ext_block_start = 0;
|
2008-10-13 12:17:19 +02:00
|
|
|
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;
|
2008-12-06 17:12:55 +01:00
|
|
|
uint64_t leaf_off;
|
2008-10-13 12:17:19 +02:00
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
|
|
|
|
if (!sbd)
|
2014-03-20 11:03:50 +01:00
|
|
|
return errno ? -errno : 1;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
/* 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)
|
2014-03-20 11:03:50 +01:00
|
|
|
return errno ? -errno : 1;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
|
|
|
|
(memcmp(hfsplus->signature, "HX", 2) != 0))
|
|
|
|
return 1;
|
|
|
|
|
2009-01-14 16:25:56 +01:00
|
|
|
hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
blocksize = be32_to_cpu(hfsplus->blocksize);
|
2011-05-06 13:00:07 +02:00
|
|
|
if (blocksize < HFSPLUS_SECTOR_SIZE)
|
2014-03-20 11:03:50 +01:00
|
|
|
return 1;
|
2011-05-06 11:04:24 +02:00
|
|
|
|
2019-09-02 12:28:39 +02:00
|
|
|
blkid_probe_set_block_size(pr, blocksize);
|
|
|
|
|
2008-10-13 12:17:19 +02:00
|
|
|
memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
|
|
|
|
cat_block = be32_to_cpu(extents[0].start_block);
|
|
|
|
|
|
|
|
buf = blkid_probe_get_buffer(pr,
|
2016-01-12 18:42:02 +01:00
|
|
|
off + ((uint64_t) cat_block * blocksize), 0x2000);
|
2008-10-13 12:17:19 +02:00
|
|
|
if (!buf)
|
2014-03-20 11:03:50 +01:00
|
|
|
return errno ? -errno : 0;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
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);
|
2017-03-24 11:46:33 +01:00
|
|
|
|
2017-03-15 22:12:00 +01:00
|
|
|
if (leaf_node_size < sizeof(struct hfsplus_bnode_descriptor) +
|
|
|
|
sizeof(struct hfsplus_catalog_key) || leaf_node_count == 0)
|
2008-10-13 12:17:19 +02:00
|
|
|
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;
|
|
|
|
|
2015-01-28 15:02:41 +01:00
|
|
|
leaf_off = ((uint64_t) ext_block_start + leaf_block) * blocksize;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
2011-03-16 14:06:23 +01:00
|
|
|
buf = blkid_probe_get_buffer(pr,
|
2016-01-12 18:42:02 +01:00
|
|
|
(uint64_t) off + leaf_off,
|
2011-03-16 14:06:23 +01:00
|
|
|
leaf_node_size);
|
2008-10-13 12:17:19 +02:00
|
|
|
if (!buf)
|
2014-03-20 11:03:50 +01:00
|
|
|
return errno ? -errno : 0;
|
2008-10-13 12:17:19 +02:00
|
|
|
|
|
|
|
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)];
|
|
|
|
|
2017-03-15 22:12:00 +01:00
|
|
|
if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID ||
|
2017-03-24 11:46:33 +01:00
|
|
|
be16_to_cpu(key->unicode_len) > 255)
|
2008-10-13 12:17:19 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
blkid_probe_set_utf8label(pr, key->unicode,
|
|
|
|
be16_to_cpu(key->unicode_len) * 2,
|
2020-02-25 12:00:27 +01:00
|
|
|
UL_ENCODE_UTF16BE);
|
2008-10-13 12:17:19 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct blkid_idinfo hfs_idinfo =
|
|
|
|
{
|
|
|
|
.name = "hfs",
|
|
|
|
.usage = BLKID_USAGE_FILESYSTEM,
|
|
|
|
.probefunc = probe_hfs,
|
2010-07-20 01:04:33 +02:00
|
|
|
.flags = BLKID_IDINFO_TOLERANT,
|
2008-10-13 12:17:19 +02:00
|
|
|
.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 }
|
|
|
|
}
|
|
|
|
};
|