summaryrefslogtreecommitdiff
path: root/extras/volume_id/volume_id.c
diff options
context:
space:
mode:
authorkay.sievers@vrfy.org <kay.sievers@vrfy.org>2004-09-05 18:05:36 +0200
committerGreg KH <gregkh@suse.de>2005-04-26 21:37:00 -0700
commitc506c4087efe567b3cb382bd228644cde3453b04 (patch)
tree4bf11457bb3b02b30ca64c64878ad5d62919e906 /extras/volume_id/volume_id.c
parentcb7c281b8dfb4b1c53902b197c98ac7bd4c7e421 (diff)
[PATCH] update udev_volume_id
Here is an update for the volume_id callout to catch up to the latest and greatest: o It is able to skip the label reading of linux raid members, which are otherwise recognized as a normal filesystem. o It reads FAT labels stored in the directory instead of the superblock (Windows only writes in the directory). o The NTFS uuid is the right one now. o It reads all the Apple HFS(+) formats with the labels. o UFS volumes are recognized but no labels are extracted. o We use CFLAGS+=-D_FILE_OFFSET_BITS=64 instead of lsee64() which may fix a bug mentioned on the klibc mailing list. A lot of other new features are only used in HAL and not needed in this simple callout. But if someone stumbles over it and want's to send a patch for some exotic formats, we better keep it up to date :)
Diffstat (limited to 'extras/volume_id/volume_id.c')
-rw-r--r--extras/volume_id/volume_id.c1554
1 files changed, 1313 insertions, 241 deletions
diff --git a/extras/volume_id/volume_id.c b/extras/volume_id/volume_id.c
index ac7e76b9d4..daeb00038a 100644
--- a/extras/volume_id/volume_id.c
+++ b/extras/volume_id/volume_id.c
@@ -3,13 +3,10 @@
*
* Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
*
- * The superblock structs are taken from the libblkid living inside
- * the e2fsprogs. This is a simple straightforward implementation for
- * reading the label strings of only the most common filesystems.
- * If you need a full featured library with attribute caching, support for
- * much more partition/media types or non-root disk access, you may have
- * a look at:
- * http://e2fsprogs.sourceforge.net.
+ * The superblock structs are taken from the linux kernel sources
+ * and the libblkid living inside the e2fsprogs. This is a simple
+ * straightforward implementation for reading the label strings of the
+ * most common filesystems.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -30,6 +27,10 @@
#define _GNU_SOURCE
#endif
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -49,7 +50,7 @@
} while (0)
#else
#define dbg(format, arg...) do {} while (0)
-#endif
+#endif /* DEBUG */
#define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
(((__u32)(x) & 0xff00u) >> 8))
@@ -68,20 +69,24 @@
(((__u64)(x) & 0x000000000000ff00u) << 40) | \
(((__u64)(x) & 0x00000000000000ffu) << 56))
-#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define le16_to_cpu(x) (x)
#define le32_to_cpu(x) (x)
#define le64_to_cpu(x) (x)
+#define be16_to_cpu(x) bswap16(x)
+#define be32_to_cpu(x) bswap32(x)
#elif (__BYTE_ORDER == __BIG_ENDIAN)
#define le16_to_cpu(x) bswap16(x)
#define le32_to_cpu(x) bswap32(x)
#define le64_to_cpu(x) bswap64(x)
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
#endif
-/* size of superblock buffer, reiser block is at 64k */
+/* size of superblock buffer, reiserfs block is at 64k */
#define SB_BUFFER_SIZE 0x11000
-/* size of seek buffer 2k */
-#define SEEK_BUFFER_SIZE 0x800
+/* size of seek buffer 4k */
+#define SEEK_BUFFER_SIZE 0x1000
static void set_label_raw(struct volume_id *id,
@@ -96,15 +101,15 @@ static void set_label_string(struct volume_id *id,
{
unsigned int i;
- memcpy(id->label_string, buf, count);
+ memcpy(id->label, buf, count);
/* remove trailing whitespace */
- i = strnlen(id->label_string, count);
+ i = strnlen(id->label, count);
while (i--) {
- if (! isspace(id->label_string[i]))
+ if (! isspace(id->label[i]))
break;
}
- id->label_string[i+1] = '\0';
+ id->label[i+1] = '\0';
}
#define LE 0
@@ -118,23 +123,23 @@ static void set_label_unicode16(struct volume_id *id,
__u16 c;
j = 0;
- for (i = 0; i <= count-2; i += 2) {
+ for (i = 0; i + 2 <= count; i += 2) {
if (endianess == LE)
c = (buf[i+1] << 8) | buf[i];
else
c = (buf[i] << 8) | buf[i+1];
if (c == 0) {
- id->label_string[j] = '\0';
+ id->label[j] = '\0';
break;
} else if (c < 0x80) {
- id->label_string[j++] = (__u8) c;
+ id->label[j++] = (__u8) c;
} else if (c < 0x800) {
- id->label_string[j++] = (__u8) (0xc0 | (c >> 6));
- id->label_string[j++] = (__u8) (0x80 | (c & 0x3f));
+ id->label[j++] = (__u8) (0xc0 | (c >> 6));
+ id->label[j++] = (__u8) (0x80 | (c & 0x3f));
} else {
- id->label_string[j++] = (__u8) (0xe0 | (c >> 12));
- id->label_string[j++] = (__u8) (0x80 | ((c >> 6) & 0x3f));
- id->label_string[j++] = (__u8) (0x80 | (c & 0x3f));
+ id->label[j++] = (__u8) (0xe0 | (c >> 12));
+ id->label[j++] = (__u8) (0x80 | ((c >> 6) & 0x3f));
+ id->label[j++] = (__u8) (0x80 | (c & 0x3f));
}
}
}
@@ -144,10 +149,10 @@ static void set_uuid(struct volume_id *id,
{
unsigned int i;
- memcpy(id->uuid, buf, count);
+ memcpy(id->uuid_raw, buf, count);
/* create string if uuid is set */
- for (i = 0; i < count; i++)
+ for (i = 0; i < count; i++)
if (buf[i] != 0)
goto set;
return;
@@ -155,11 +160,16 @@ static void set_uuid(struct volume_id *id,
set:
switch(count) {
case 4:
- sprintf(id->uuid_string, "%02X%02X-%02X%02X",
+ sprintf(id->uuid, "%02X%02X-%02X%02X",
+ buf[3], buf[2], buf[1], buf[0]);
+ break;
+ case 8:
+ sprintf(id->uuid,"%02X%02X-%02X%02X-%02X%02X-%02X%02X",
+ buf[7], buf[6], buf[5], buf[4],
buf[3], buf[2], buf[1], buf[0]);
break;
case 16:
- sprintf(id->uuid_string,
+ sprintf(id->uuid,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x",
buf[0], buf[1], buf[2], buf[3],
@@ -171,11 +181,11 @@ set:
}
}
-static __u8 *get_buffer(struct volume_id *id,
- unsigned long off, unsigned int len)
+static __u8 *get_buffer(struct volume_id *id, __u64 off, unsigned int len)
{
unsigned int buf_len;
+ dbg("get buffer off 0x%llx, len 0x%x", off, len);
/* check if requested area fits in superblock buffer */
if (off + len <= SB_BUFFER_SIZE) {
if (id->sbbuf == NULL) {
@@ -186,9 +196,10 @@ static __u8 *get_buffer(struct volume_id *id,
/* check if we need to read */
if ((off + len) > id->sbbuf_len) {
- dbg("read sbbuf len:0x%lx", off + len);
- lseek64(id->fd, 0, SEEK_SET);
+ dbg("read sbbuf len:0x%llx", off + len);
+ lseek(id->fd, 0, SEEK_SET);
buf_len = read(id->fd, id->sbbuf, off + len);
+ dbg("got 0x%x (%i) bytes", buf_len, buf_len);
id->sbbuf_len = buf_len;
if (buf_len < off + len)
return NULL;
@@ -209,8 +220,9 @@ static __u8 *get_buffer(struct volume_id *id,
/* check if we need to read */
if ((off < id->seekbuf_off) ||
((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
- dbg("read seekbuf off:0x%lx len:0x%x", off, len);
- lseek64(id->fd, off, SEEK_SET);
+ dbg("read seekbuf off:0x%llx len:0x%x", off, len);
+ if (lseek(id->fd, off, SEEK_SET) == -1)
+ return NULL;
buf_len = read(id->fd, id->seekbuf, len);
dbg("got 0x%x (%i) bytes", buf_len, buf_len);
id->seekbuf_off = off;
@@ -237,10 +249,298 @@ static void free_buffer(struct volume_id *id)
}
}
+#define LVM1_SB_OFF 0x400
+#define LVM1_MAGIC "HM"
+static int probe_lvm1(struct volume_id *id, __u64 off)
+{
+ struct lvm2_super_block {
+ __u8 id[2];
+ } __attribute__((packed)) *lvm;
+
+ const __u8 *buf;
+
+ buf = get_buffer(id, off + LVM1_SB_OFF, 0x800);
+ if (buf == NULL)
+ return -1;
+
+ lvm = (struct lvm2_super_block *) buf;
+
+ if (strncmp(lvm->id, LVM1_MAGIC, 2) != 0)
+ return -1;
+
+ id->usage_id = VOLUME_ID_RAID;
+ id->type_id = VOLUME_ID_LVM1;
+ id->type = "LVM1_member";
+
+ return 0;
+}
+
+#define LVM2_LABEL_ID "LABELONE"
+#define LVM2LABEL_SCAN_SECTORS 4
+static int probe_lvm2(struct volume_id *id, __u64 off)
+{
+ struct lvm2_super_block {
+ __u8 id[8];
+ __u64 sector_xl;
+ __u32 crc_xl;
+ __u32 offset_xl;
+ __u8 type[8];
+ } __attribute__((packed)) *lvm;
+
+ const __u8 *buf;
+ unsigned int soff;
+
+ buf = get_buffer(id, off, LVM2LABEL_SCAN_SECTORS * 0x200);
+ if (buf == NULL)
+ return -1;
+
+
+ for (soff = 0; soff < LVM2LABEL_SCAN_SECTORS * 0x200; soff += 0x200) {
+ lvm = (struct lvm2_super_block *) &buf[soff];
+
+ if (strncmp(lvm->id, LVM2_LABEL_ID, 8) == 0)
+ goto found;
+ }
+
+ return -1;
+
+found:
+ strncpy(id->type_version, lvm->type, 8);
+ id->usage_id = VOLUME_ID_RAID;
+ id->type_id = VOLUME_ID_LVM1;
+ id->type = "LVM2_member";
+
+ return 0;
+}
+
+#define MD_RESERVED_BYTES 0x10000
+#define MD_MAGIC 0xa92b4efc
+static int probe_linux_raid(struct volume_id *id, __u64 off, __u64 size)
+{
+ struct mdp_super_block {
+ __u32 md_magic;
+ __u32 major_version;
+ __u32 minor_version;
+ __u32 patch_version;
+ __u32 gvalid_words;
+ __u32 set_uuid0;
+ __u32 ctime;
+ __u32 level;
+ __u32 size;
+ __u32 nr_disks;
+ __u32 raid_disks;
+ __u32 md_minor;
+ __u32 not_persistent;
+ __u32 set_uuid1;
+ __u32 set_uuid2;
+ __u32 set_uuid3;
+ } __attribute__((packed)) *mdp;
+
+ const __u8 *buf;
+ __u64 sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
+ __u8 uuid[16];
+
+ if (size < 0x10000)
+ return -1;
+
+ buf = get_buffer(id, off + sboff, 0x800);
+ if (buf == NULL)
+ return -1;
+
+ mdp = (struct mdp_super_block *) buf;
+
+ if (le32_to_cpu(mdp->md_magic) != MD_MAGIC)
+ return -1;
+
+ memcpy(uuid, &mdp->set_uuid0, 4);
+ memcpy(&uuid[4], &mdp->set_uuid1, 12);
+ set_uuid(id, uuid, 16);
+
+ snprintf(id->type_version, VOLUME_ID_FORMAT_SIZE-1, "%u.%u.%u",
+ le32_to_cpu(mdp->major_version),
+ le32_to_cpu(mdp->minor_version),
+ le32_to_cpu(mdp->patch_version));
+
+ dbg("found raid signature");
+ id->usage_id = VOLUME_ID_RAID;
+ id->type = "linux_raid_member";
+
+ return 0;
+}
+
+#define MSDOS_MAGIC "\x55\xaa"
+#define MSDOS_PARTTABLE_OFFSET 0x1be
+#define MSDOS_SIG_OFF 0x1fe
+#define BSIZE 0x200
+#define DOS_EXTENDED_PARTITION 0x05
+#define LINUX_EXTENDED_PARTITION 0x85
+#define WIN98_EXTENDED_PARTITION 0x0f
+#define LINUX_RAID_PARTITION 0xfd
+#define is_extended(type) \
+ (type == DOS_EXTENDED_PARTITION || \
+ type == WIN98_EXTENDED_PARTITION || \
+ type == LINUX_EXTENDED_PARTITION)
+#define is_raid(type) \
+ (type == LINUX_RAID_PARTITION)
+static int probe_msdos_part_table(struct volume_id *id, __u64 off)
+{
+ struct msdos_partition_entry {
+ __u8 boot_ind;
+ __u8 head;
+ __u8 sector;
+ __u8 cyl;
+ __u8 sys_ind;
+ __u8 end_head;
+ __u8 end_sector;
+ __u8 end_cyl;
+ __u32 start_sect;
+ __u32 nr_sects;
+ } __attribute__((packed)) *part;
+
+ const __u8 *buf;
+ int i;
+ __u64 poff;
+ __u64 plen;
+ __u64 extended = 0;
+ __u64 current;
+ __u64 next;
+ int limit;
+ int empty = 1;
+ struct volume_id_partition *p;
+
+ buf = get_buffer(id, off, 0x200);
+ if (buf == NULL)
+ return -1;
+
+ if (strncmp(&buf[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0)
+ return -1;
+
+ /* check flags on all entries for a valid partition table */
+ part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+ for (i = 0; i < 4; i++) {
+ if (part[i].boot_ind != 0 &&
+ part[i].boot_ind != 0x80)
+ return -1;
+
+ if (le32_to_cpu(part[i].nr_sects) != 0)
+ empty = 0;
+ }
+ if (empty == 1)
+ return -1;
+
+ if (id->partitions != NULL)
+ free(id->partitions);
+ id->partitions = malloc(VOLUME_ID_PARTITIONS_MAX *
+ sizeof(struct volume_id_partition));
+ if (id->partitions == NULL)
+ return -1;
+ memset(id->partitions, 0x00,
+ VOLUME_ID_PARTITIONS_MAX * sizeof(struct volume_id_partition));
+
+ for (i = 0; i < 4; i++) {
+ poff = (__u64) le32_to_cpu(part[i].start_sect) * BSIZE;
+ plen = (__u64) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+ if (plen == 0)
+ continue;
+
+ p = &id->partitions[i];
+
+ if (is_extended(part[i].sys_ind)) {
+ dbg("found extended partition at 0x%llx", poff);
+ p->usage_id = VOLUME_ID_PARTITIONTABLE;
+ p->type_id = VOLUME_ID_MSDOSEXTENDED;
+ p->type = "msdos_extended_partition";
+ if (extended == 0)
+ extended = off + poff;
+ } else {
+ dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
+ part[i].sys_ind, poff, plen);
+
+ if (is_raid(part[i].sys_ind))
+ p->usage_id = VOLUME_ID_RAID;
+ else
+ p->usage_id = VOLUME_ID_UNPROBED;
+ }
+
+ p->off = off + poff;
+ p->len = plen;
+ id->partition_count = i+1;
+ }
+
+ next = extended;
+ current = extended;
+ limit = 50;
+
+ /* follow extended partition chain and add data partitions */
+ while (next != 0) {
+ if (limit-- == 0) {
+ dbg("extended chain limit reached");
+ break;
+ }
+
+ buf = get_buffer(id, current, 0x200);
+ if (buf == NULL)
+ break;
+
+ part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+
+ if (strncmp(&buf[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0)
+ break;
+
+ next = 0;
+
+ for (i = 0; i < 4; i++) {
+ poff = (__u64) le32_to_cpu(part[i].start_sect) * BSIZE;
+ plen = (__u64) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+ if (plen == 0)
+ continue;
+
+ if (is_extended(part[i].sys_ind)) {
+ dbg("found extended partition at 0x%llx", poff);
+ if (next == 0)
+ next = extended + poff;
+ } else {
+ dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
+ part[i].sys_ind, poff, plen);
+
+ /* we always start at the 5th entry */
+ while (id->partition_count < 4)
+ id->partitions[id->partition_count++].usage_id =
+ VOLUME_ID_UNUSED;
+
+ p = &id->partitions[id->partition_count];
+
+ if (is_raid(part[i].sys_ind))
+ p->usage_id = VOLUME_ID_RAID;
+ else
+ p->usage_id = VOLUME_ID_UNPROBED;
+
+ p->off = current + poff;
+ p->len = plen;
+ id->partition_count++;
+ if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) {
+ dbg("to many partitions");
+ next = 0;
+ }
+ }
+ }
+
+ current = next;
+ }
+
+ id->usage_id = VOLUME_ID_PARTITIONTABLE;
+ id->type_id = VOLUME_ID_MSDOSPARTTABLE;
+ id->type = "msdos_partition_table";
+
+ return 0;
+}
+
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
#define EXT_SUPERBLOCK_OFFSET 0x400
-static int probe_ext(struct volume_id *id)
+static int probe_ext(struct volume_id *id, __u64 off)
{
struct ext2_super_block {
__u32 inodes_count;
@@ -262,7 +562,7 @@ static int probe_ext(struct volume_id *id)
} __attribute__((__packed__)) *es;
es = (struct ext2_super_block *)
- get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
+ get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200);
if (es == NULL)
return -1;
@@ -276,21 +576,23 @@ static int probe_ext(struct volume_id *id)
if ((le32_to_cpu(es->feature_compat) &
EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
- id->fs_type = EXT3;
- id->fs_name = "ext3";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_EXT3;
+ id->type = "ext3";
} else {
- id->fs_type = EXT2;
- id->fs_name = "ext2";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_EXT2;
+ id->type = "ext2";
}
return 0;
}
-#define REISER1_SUPERBLOCK_OFFSET 0x2000
-#define REISER_SUPERBLOCK_OFFSET 0x10000
-static int probe_reiser(struct volume_id *id)
+#define REISERFS1_SUPERBLOCK_OFFSET 0x2000
+#define REISERFS_SUPERBLOCK_OFFSET 0x10000
+static int probe_reiserfs(struct volume_id *id, __u64 off)
{
- struct reiser_super_block {
+ struct reiserfs_super_block {
__u32 blocks_count;
__u32 free_blocks;
__u32 root_block;
@@ -306,23 +608,30 @@ static int probe_reiser(struct volume_id *id)
__u8 label[16];
} __attribute__((__packed__)) *rs;
- rs = (struct reiser_super_block *)
- get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
+ rs = (struct reiserfs_super_block *)
+ get_buffer(id, off + REISERFS_SUPERBLOCK_OFFSET, 0x200);
if (rs == NULL)
return -1;
- if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
+ if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0) {
+ strcpy(id->type_version, "3.6");
goto found;
- if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
+ }
+
+ if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0) {
+ strcpy(id->type_version, "JR");
goto found;
+ }
- rs = (struct reiser_super_block *)
- get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
+ rs = (struct reiserfs_super_block *)
+ get_buffer(id, off + REISERFS1_SUPERBLOCK_OFFSET, 0x200);
if (rs == NULL)
return -1;
- if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
+ if (strncmp(rs->magic, "ReIsErFs", 8) == 0) {
+ strcpy(id->type_version, "3.5");
goto found;
+ }
return -1;
@@ -331,13 +640,14 @@ found:
set_label_string(id, rs->label, 16);
set_uuid(id, rs->uuid, 16);
- id->fs_type = REISER;
- id->fs_name = "reiser";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_REISERFS;
+ id->type = "reiserfs";
return 0;
}
-static int probe_xfs(struct volume_id *id)
+static int probe_xfs(struct volume_id *id, __u64 off)
{
struct xfs_super_block {
__u8 magic[4];
@@ -354,7 +664,7 @@ static int probe_xfs(struct volume_id *id)
__u64 fdblocks;
} __attribute__((__packed__)) *xs;
- xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
+ xs = (struct xfs_super_block *) get_buffer(id, off, 0x200);
if (xs == NULL)
return -1;
@@ -365,14 +675,15 @@ static int probe_xfs(struct volume_id *id)
set_label_string(id, xs->fname, 12);
set_uuid(id, xs->uuid, 16);
- id->fs_type = XFS;
- id->fs_name = "xfs";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_XFS;
+ id->type = "xfs";
return 0;
}
#define JFS_SUPERBLOCK_OFFSET 0x8000
-static int probe_jfs(struct volume_id *id)
+static int probe_jfs(struct volume_id *id, __u64 off)
{
struct jfs_super_block {
__u8 magic[4];
@@ -388,7 +699,7 @@ static int probe_jfs(struct volume_id *id)
} __attribute__((__packed__)) *js;
js = (struct jfs_super_block *)
- get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
+ get_buffer(id, off + JFS_SUPERBLOCK_OFFSET, 0x200);
if (js == NULL)
return -1;
@@ -399,115 +710,294 @@ static int probe_jfs(struct volume_id *id)
set_label_string(id, js->label, 16);
set_uuid(id, js->uuid, 16);
- id->fs_type = JFS;
- id->fs_name = "jfs";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_JFS;
+ id->type = "jfs";
return 0;
}
-static int probe_vfat(struct volume_id *id)
+#define FAT12_MAX 0xff5
+#define FAT16_MAX 0xfff5
+#define FAT_ATTR_VOLUME 0x08
+struct vfat_dir_entry {
+ __u8 name[11];
+ __u8 attr;
+ __u16 time_creat;
+ __u16 date_creat;
+ __u16 time_acc;
+ __u16 date_acc;
+ __u16 cluster_high;
+ __u16 time_write;
+ __u16 date_write;
+ __u16 cluster_low;
+ __u32 size;
+} __attribute__((__packed__));
+
+static char *vfat_search_label_in_dir(const __u8 *buf, __u16 size)
+{
+ struct vfat_dir_entry *dir;
+ int i;
+ __u16 count;
+
+ dir = (struct vfat_dir_entry*) buf;
+ count = size / sizeof(struct vfat_dir_entry);
+ dbg("expected entries 0x%x", count);
+
+ for (i = 0; i <= count; i++) {
+ /* end marker */
+ if (dir[i].attr == 0x00) {
+ dbg("end of dir");
+ return NULL;
+ }
+
+ /* empty entry */
+ if (dir[i].attr == 0xe5)
+ continue;
+
+ if (dir[i].attr == FAT_ATTR_VOLUME) {
+ dbg("found ATTR_VOLUME id in root dir");
+ return dir[i].name;
+ }
+
+ dbg("skip dir entry");
+ }
+
+ return NULL;
+}
+
+static int probe_vfat(struct volume_id *id, __u64 off)
{
struct vfat_super_block {
- __u8 ignored[3];
+ __u8 boot_jump[3];
__u8 sysid[8];
- __u8 sector_size[2];
- __u8 cluster_size;
+ __u16 sector_size;
+ __u8 sectors_per_cluster;
__u16 reserved;
__u8 fats;
- __u8 dir_entries[2];
- __u8 sectors[2];
+ __u16 dir_entries;
+ __u16 sectors;
__u8 media;
__u16 fat_length;
__u16 secs_track;
__u16 heads;
__u32 hidden;
__u32 total_sect;
- __u32 fat32_length;
- __u16 flags;
- __u8 version[2];
- __u32 root_cluster;
- __u16 insfo_sector;
- __u16 backup_boot;
- __u16 reserved2[6];
- __u8 unknown[3];
- __u8 serno[4];
- __u8 label[11];
- __u8 magic[8];
- __u8 dummy2[164];
- __u8 pmagic[2];
+ union {
+ struct fat_super_block {
+ __u8 unknown[3];
+ __u8 serno[4];
+ __u8 label[11];
+ __u8 magic[8];
+ __u8 dummy2[192];
+ __u8 pmagic[2];
+ } __attribute__((__packed__)) fat;
+ struct fat32_super_block {
+ __u32 fat32_length;
+ __u16 flags;
+ __u8 version[2];
+ __u32 root_cluster;
+ __u16 insfo_sector;
+ __u16 backup_boot;
+ __u16 reserved2[6];
+ __u8 unknown[3];
+ __u8 serno[4];
+ __u8 label[11];
+ __u8 magic[8];
+ __u8 dummy2[164];
+ __u8 pmagic[2];
+ } __attribute__((__packed__)) fat32;
+ } __attribute__((__packed__)) type;
} __attribute__((__packed__)) *vs;
- vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
+ __u16 sector_size;
+ __u16 dir_entries;
+ __u32 sect_count;
+ __u16 reserved;
+ __u16 fat_size;
+ __u32 root_cluster;
+ __u32 dir_size;
+ __u32 cluster_count;
+ __u32 fat_length;
+ __u64 root_start;
+ __u32 start_data_sect;
+ __u16 root_dir_entries;
+ __u8 *buf;
+ __u32 buf_size;
+ __u8 *label = NULL;
+ __u32 next;
+
+ vs = (struct vfat_super_block *) get_buffer(id, off, 0x200);
if (vs == NULL)
return -1;
- if (strncmp(vs->magic, "MSWIN", 5) == 0)
- goto found;
- if (strncmp(vs->magic, "FAT32 ", 8) == 0)
- goto found;
- return -1;
+ /* believe only that's fat, don't trust the version
+ * the cluster_count will tell us
+ */
+ if (strncmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
+ goto valid;
-found:
- set_label_raw(id, vs->label, 11);
- set_label_string(id, vs->label, 11);
- set_uuid(id, vs->serno, 4);
+ if (strncmp(vs->type.fat32.magic, "FAT32 ", 8) == 0)
+ goto valid;
- id->fs_type = VFAT;
- id->fs_name = "vfat";
+ if (strncmp(vs->type.fat.magic, "FAT16 ", 8) == 0)
+ goto valid;
- return 0;
-}
+ if (strncmp(vs->type.fat.magic, "MSDOS", 5) == 0)
+ goto valid;
-static int probe_msdos(struct volume_id *id)
-{
- struct msdos_super_block {
- __u8 ignored[3];
- __u8 sysid[8];
- __u8 sector_size[2];
- __u8 cluster_size;
- __u16 reserved;
- __u8 fats;
- __u8 dir_entries[2];
- __u8 sectors[2];
- __u8 media;
- __u16 fat_length;
- __u16 secs_track;
- __u16 heads;
- __u32 hidden;
- __u32 total_sect;
- __u8 unknown[3];
- __u8 serno[4];
- __u8 label[11];
- __u8 magic[8];
- __u8 dummy2[192];
- __u8 pmagic[2];
- } __attribute__((__packed__)) *ms;
-
- ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
- if (ms == NULL)
+ if (strncmp(vs->type.fat.magic, "FAT12 ", 8) == 0)
+ goto valid;
+
+ /*
+ * There are old floppies out there without a magic, so we check
+ * for well known values and guess if it's a fat volume
+ */
+
+ /* boot jump address check */
+ if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90) &&
+ vs->boot_jump[0] != 0xe9)
return -1;
- if (strncmp(ms->magic, "MSDOS", 5) == 0)
- goto found;
- if (strncmp(ms->magic, "FAT16 ", 8) == 0)
- goto found;
- if (strncmp(ms->magic, "FAT12 ", 8) == 0)
+ /* heads check */
+ if (vs->heads == 0)
+ return -1;
+
+ /* cluster size check*/
+ if (vs->sectors_per_cluster == 0 ||
+ (vs->sectors_per_cluster & (vs->sectors_per_cluster-1)))
+ return -1;
+
+ /* media check */
+ if (vs->media < 0xf8 && vs->media != 0xf0)
+ return -1;
+
+ /* fat count*/
+ if (vs->fats != 2)
+ return -1;
+
+valid:
+ /* sector size check */
+ sector_size = le16_to_cpu(vs->sector_size);
+ if (sector_size != 0x200 && sector_size != 0x400 &&
+ sector_size != 0x800 && sector_size != 0x1000)
+ return -1;
+
+ dbg("sector_size 0x%x", sector_size);
+ dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
+
+ dir_entries = le16_to_cpu(vs->dir_entries);
+ reserved = le16_to_cpu(vs->reserved);
+ dbg("reserved 0x%x", reserved);
+
+ sect_count = le16_to_cpu(vs->sectors);
+ if (sect_count == 0)
+ sect_count = vs->total_sect;
+ dbg("sect_count 0x%x", sect_count);
+
+ fat_length = le16_to_cpu(vs->fat_length);
+ if (fat_length == 0)
+ fat_length = le32_to_cpu(vs->type.fat32.fat32_length);
+ dbg("fat_length 0x%x", fat_length);
+
+ fat_size = fat_length * vs->fats;
+ dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+ (sector_size-1)) / sector_size;
+ dbg("dir_size 0x%x", dir_size);
+
+ cluster_count = sect_count - (reserved + fat_size + dir_size);
+ cluster_count /= vs->sectors_per_cluster;
+ dbg("cluster_count 0x%x", cluster_count);
+
+ if (cluster_count < FAT12_MAX) {
+ strcpy(id->type_version, "FAT12");
+ } else if (cluster_count < FAT16_MAX) {
+ strcpy(id->type_version, "FAT16");
+ } else {
+ strcpy(id->type_version, "FAT32");
+ goto fat32;
+ }
+
+ /* the label may be an attribute in the root directory */
+ root_start = (reserved + fat_size) * sector_size;
+ root_dir_entries = le16_to_cpu(vs->dir_entries);
+ dbg("root dir start 0x%x", root_start);
+
+ buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
+ buf = get_buffer(id, off + root_start, buf_size);
+ if (buf == NULL)
goto found;
- return -1;
-found:
- set_label_raw(id, ms->label, 11);
- set_label_string(id, ms->label, 11);
- set_uuid(id, ms->serno, 4);
+ label = vfat_search_label_in_dir(buf, buf_size);
+
+ if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) {
+ set_label_raw(id, label, 11);
+ set_label_string(id, label, 11);
+ } else if (strncmp(vs->type.fat.label, "NO NAME ", 11) != 0) {
+ set_label_raw(id, vs->type.fat.label, 11);
+ set_label_string(id, vs->type.fat.label, 11);
+ }
+ set_uuid(id, vs->type.fat.serno, 4);
+ goto found;
+
+fat32:
+ /* FAT32 root dir is a cluster chain like any other directory */
+ buf_size = vs->sectors_per_cluster * sector_size;
+ root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
+ dbg("root dir cluster %u", root_cluster);
+ start_data_sect = reserved + fat_size;
+
+ next = root_cluster;
+ while (1) {
+ __u32 next_sect_off;
+ __u64 next_off;
+ __u64 fat_entry_off;
+
+ dbg("next cluster %u", next);
+ next_sect_off = (next - 2) * vs->sectors_per_cluster;
+ next_off = (start_data_sect + next_sect_off) * sector_size;
+ dbg("cluster offset 0x%x", next_off);
+
+ /* get cluster */
+ buf = get_buffer(id, off + next_off, buf_size);
+ if (buf == NULL)
+ goto found;
- id->fs_type = MSDOS;
- id->fs_name = "msdos";
+ label = vfat_search_label_in_dir(buf, buf_size);
+ if (label != NULL)
+ break;
+
+ /* get FAT entry */
+ fat_entry_off = (reserved * sector_size) + (next * sizeof(__u32));
+ buf = get_buffer(id, off + fat_entry_off, buf_size);
+ if (buf == NULL)
+ goto found;
+
+ /* set next cluster */
+ next = le32_to_cpu(*((__u32 *) buf) & 0x0fffffff);
+ if (next == 0)
+ break;
+ }
+
+ if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) {
+ set_label_raw(id, label, 11);
+ set_label_string(id, label, 11);
+ } else if (strncmp(vs->type.fat32.label, "NO NAME ", 11) == 0) {
+ set_label_raw(id, vs->type.fat32.label, 11);
+ set_label_string(id, vs->type.fat32.label, 11);
+ }
+ set_uuid(id, vs->type.fat32.serno, 4);
+
+found:
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_VFAT;
+ id->type = "vfat";
return 0;
}
#define UDF_VSD_OFFSET 0x8000
-static int probe_udf(struct volume_id *id)
+static int probe_udf(struct volume_id *id, __u64 off)
{
struct volume_descriptor {
struct descriptor_tag {
@@ -550,7 +1040,7 @@ static int probe_udf(struct volume_id *id)
unsigned int clen;
vsd = (struct volume_structure_descriptor *)
- get_buffer(id, UDF_VSD_OFFSET, 0x200);
+ get_buffer(id, off + UDF_VSD_OFFSET, 0x200);
if (vsd == NULL)
return -1;
@@ -574,7 +1064,7 @@ blocksize:
/* search the next VSD to get the logical block size of the volume */
for (bs = 0x800; bs < 0x8000; bs += 0x800) {
vsd = (struct volume_structure_descriptor *)
- get_buffer(id, UDF_VSD_OFFSET + bs, 0x800);
+ get_buffer(id, off + UDF_VSD_OFFSET + bs, 0x800);
if (vsd == NULL)
return -1;
dbg("test for blocksize: 0x%x", bs);
@@ -587,7 +1077,7 @@ nsr:
/* search the list of VSDs for a NSR descriptor */
for (b = 0; b < 64; b++) {
vsd = (struct volume_structure_descriptor *)
- get_buffer(id, UDF_VSD_OFFSET + (b * bs), 0x800);
+ get_buffer(id, off + UDF_VSD_OFFSET + (b * bs), 0x800);
if (vsd == NULL)
return -1;
@@ -605,7 +1095,8 @@ nsr:
anchor:
/* read anchor volume descriptor */
- vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
+ vd = (struct volume_descriptor *)
+ get_buffer(id, off + (256 * bs), 0x200);
if (vd == NULL)
return -1;
@@ -621,7 +1112,7 @@ anchor:
/* pick the primary descriptor from the list */
for (b = 0; b < count; b++) {
vd = (struct volume_descriptor *)
- get_buffer(id, (loc + b) * bs, 0x200);
+ get_buffer(id, off + ((loc + b) * bs), 0x200);
if (vd == NULL)
return -1;
@@ -644,20 +1135,21 @@ pvd:
clen = vd->type.primary.ident.clen;
dbg("label string charsize=%i bit", clen);
- if (clen == 8)
+ if (clen == 8)
set_label_string(id, vd->type.primary.ident.c, 31);
else if (clen == 16)
set_label_unicode16(id, vd->type.primary.ident.c, BE,31);
found:
- id->fs_type = UDF;
- id->fs_name = "udf";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_UDF;
+ id->type = "udf";
return 0;
}
#define ISO_SUPERBLOCK_OFFSET 0x8000
-static int probe_iso9660(struct volume_id *id)
+static int probe_iso9660(struct volume_id *id, __u64 off)
{
union iso_super_block {
struct iso_header {
@@ -677,7 +1169,7 @@ static int probe_iso9660(struct volume_id *id)
} __attribute__((__packed__)) *is;
is = (union iso_super_block *)
- get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
+ get_buffer(id, off + ISO_SUPERBLOCK_OFFSET, 0x200);
if (is == NULL)
return -1;
@@ -691,40 +1183,557 @@ static int probe_iso9660(struct volume_id *id)
return -1;
found:
- id->fs_type = ISO9660;
- id->fs_name = "iso9660";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_ISO9660;
+ id->type = "iso9660";
+
+ return 0;
+}
+
+#define UFS_MAGIC 0x00011954
+#define UFS2_MAGIC 0x19540119
+#define UFS_MAGIC_FEA 0x00195612
+#define UFS_MAGIC_LFN 0x00095014
+
+
+static int probe_ufs(struct volume_id *id, __u64 off)
+{
+ struct ufs_super_block {
+ __u32 fs_link;
+ __u32 fs_rlink;
+ __u32 fs_sblkno;
+ __u32 fs_cblkno;
+ __u32 fs_iblkno;
+ __u32 fs_dblkno;
+ __u32 fs_cgoffset;
+ __u32 fs_cgmask;
+ __u32 fs_time;
+ __u32 fs_size;
+ __u32 fs_dsize;
+ __u32 fs_ncg;
+ __u32 fs_bsize;
+ __u32 fs_fsize;
+ __u32 fs_frag;
+ __u32 fs_minfree;
+ __u32 fs_rotdelay;
+ __u32 fs_rps;
+ __u32 fs_bmask;
+ __u32 fs_fmask;
+ __u32 fs_bshift;
+ __u32 fs_fshift;
+ __u32 fs_maxcontig;
+ __u32 fs_maxbpg;
+ __u32 fs_fragshift;
+ __u32 fs_fsbtodb;
+ __u32 fs_sbsize;
+ __u32 fs_csmask;
+ __u32 fs_csshift;
+ __u32 fs_nindir;
+ __u32 fs_inopb;
+ __u32 fs_nspf;
+ __u32 fs_optim;
+ __u32 fs_npsect_state;
+ __u32 fs_interleave;
+ __u32 fs_trackskew;
+ __u32 fs_id[2];
+ __u32 fs_csaddr;
+ __u32 fs_cssize;
+ __u32 fs_cgsize;
+ __u32 fs_ntrak;
+ __u32 fs_nsect;
+ __u32 fs_spc;
+ __u32 fs_ncyl;
+ __u32 fs_cpg;
+ __u32 fs_ipg;
+ __u32 fs_fpg;
+ struct ufs_csum {
+ __u32 cs_ndir;
+ __u32 cs_nbfree;
+ __u32 cs_nifree;
+ __u32 cs_nffree;
+ } __attribute__((__packed__)) fs_cstotal;
+ __s8 fs_fmod;
+ __s8 fs_clean;
+ __s8 fs_ronly;
+ __s8 fs_flags;
+ union {
+ struct {
+ __s8 fs_fsmnt[512];
+ __u32 fs_cgrotor;
+ __u32 fs_csp[31];
+ __u32 fs_maxcluster;
+ __u32 fs_cpc;
+ __u16 fs_opostbl[16][8];
+ } __attribute__((__packed__)) fs_u1;
+ struct {
+ __s8 fs_fsmnt[468];
+ __u8 fs_volname[32];
+ __u64 fs_swuid;
+ __s32 fs_pad;
+ __u32 fs_cgrotor;
+ __u32 fs_ocsp[28];
+ __u32 fs_contigdirs;
+ __u32 fs_csp;
+ __u32 fs_maxcluster;
+ __u32 fs_active;
+ __s32 fs_old_cpc;
+ __s32 fs_maxbsize;
+ __s64 fs_sparecon64[17];
+ __s64 fs_sblockloc;
+ struct ufs2_csum_total {
+ __u64 cs_ndir;
+ __u64 cs_nbfree;
+ __u64 cs_nifree;
+ __u64 cs_nffree;
+ __u64 cs_numclusters;
+ __u64 cs_spare[3];
+ } __attribute__((__packed__)) fs_cstotal;
+ struct ufs_timeval {
+ __s32 tv_sec;
+ __s32 tv_usec;
+ } __attribute__((__packed__)) fs_time;
+ __s64 fs_size;
+ __s64 fs_dsize;
+ __u64 fs_csaddr;
+ __s64 fs_pendingblocks;
+ __s32 fs_pendinginodes;
+ } __attribute__((__packed__)) fs_u2;
+ } fs_u11;
+ union {
+ struct {
+ __s32 fs_sparecon[53];
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __s32 fs_state;
+ __u32 fs_qbmask[2];
+ __u32 fs_qfmask[2];
+ } __attribute__((__packed__)) fs_sun;
+ struct {
+ __s32 fs_sparecon[53];
+ __s32 fs_reclaim;
+ __s32 fs_sparecon2[1];
+ __u32 fs_npsect;
+ __u32 fs_qbmask[2];
+ __u32 fs_qfmask[2];
+ } __attribute__((__packed__)) fs_sunx86;
+ struct {
+ __s32 fs_sparecon[50];
+ __s32 fs_contigsumsize;
+ __s32 fs_maxsymlinklen;
+ __s32 fs_inodefmt;
+ __u32 fs_maxfilesize[2];
+ __u32 fs_qbmask[2];
+ __u32 fs_qfmask[2];
+ __s32 fs_state;
+ } __attribute__((__packed__)) fs_44;
+ } fs_u2;
+ __s32 fs_postblformat;
+ __s32 fs_nrpos;
+ __s32 fs_postbloff;
+ __s32 fs_rotbloff;
+ __u32 fs_magic;
+ __u8 fs_space[1];
+ } __attribute__((__packed__)) *ufs;
+
+ __u32 magic;
+ int i;
+ int offsets[] = {0, 8, 64, 256, -1};
+
+ for (i = 0; offsets[i] >= 0; i++) {
+ ufs = (struct ufs_super_block *)
+ get_buffer(id, off + (offsets[i] * 0x400), 0x800);
+ if (ufs == NULL)
+ return -1;
+
+ dbg("offset 0x%x", offsets[i] * 0x400);
+ magic = be32_to_cpu(ufs->fs_magic);
+ if ((magic == UFS_MAGIC) ||
+ (magic == UFS2_MAGIC) ||
+ (magic == UFS_MAGIC_FEA) ||
+ (magic == UFS_MAGIC_LFN)) {
+ dbg("magic 0x%08x(be)", magic);
+ goto found;
+ }
+ magic = le32_to_cpu(ufs->fs_magic);
+ if ((magic == UFS_MAGIC) ||
+ (magic == UFS2_MAGIC) ||
+ (magic == UFS_MAGIC_FEA) ||
+ (magic == UFS_MAGIC_LFN)) {
+ dbg("magic 0x%08x(le)", magic);
+ goto found;
+ }
+ }
+ return -1;
+
+found:
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_UFS;
+ id->type = "ufs";
+
+ return 0;
+}
+
+static int probe_mac_partition_map(struct volume_id *id, __u64 off)
+{
+ struct mac_driver_desc {
+ __u8 signature[2];
+ __u16 block_size;
+ __u32 block_count;
+ } __attribute__((__packed__)) *driver;
+
+ struct mac_partition {
+ __u8 signature[2];
+ __u16 res1;
+ __u32 map_count;
+ __u32 start_block;
+ __u32 block_count;
+ __u8 name[32];
+ __u8 type[32];
+ } __attribute__((__packed__)) *part;
+
+ const __u8 *buf;
+
+ buf = get_buffer(id, off, 0x200);
+ if (buf == NULL)
+ return -1;
+
+ part = (struct mac_partition *) buf;
+ if ((strncmp(part->signature, "PM", 2) == 0) &&
+ (strncmp(part->type, "Apple_partition_map", 19) == 0)) {
+ /* linux creates an own subdevice for the map
+ * just return the type if the drive header is missing */
+ id->usage_id = VOLUME_ID_PARTITIONTABLE;
+ id->type_id = VOLUME_ID_MACPARTMAP;
+ id->type = "mac_partition_map";
+ return 0;
+ }
+
+ driver = (struct mac_driver_desc *) buf;
+ if (strncmp(driver->signature, "ER", 2) == 0) {
+ /* we are on a main device, like a CD
+ * just try to probe the first partition from the map */
+ unsigned int bsize = be16_to_cpu(driver->block_size);
+ int part_count;
+ int i;
+
+ /* get first entry of partition table */
+ buf = get_buffer(id, off + bsize, 0x200);
+ if (buf == NULL)
+ return -1;
+
+ part = (struct mac_partition *) buf;
+ if (strncmp(part->signature, "PM", 2) != 0)
+ return -1;
+
+ part_count = be32_to_cpu(part->map_count);
+ dbg("expecting %d partition entries", part_count);
+
+ if (id->partitions != NULL)
+ free(id->partitions);
+ id->partitions =
+ malloc(part_count * sizeof(struct volume_id_partition));
+ if (id->partitions == NULL)
+ return -1;
+ memset(id->partitions, 0x00, sizeof(struct volume_id_partition));
+
+ id->partition_count = part_count;
+
+ for (i = 0; i < part_count; i++) {
+ __u64 poff;
+ __u64 plen;
+
+ buf = get_buffer(id, off + ((i+1) * bsize), 0x200);
+ if (buf == NULL)
+ return -1;
+
+ part = (struct mac_partition *) buf;
+ if (strncmp(part->signature, "PM", 2) != 0)
+ return -1;
+
+ poff = be32_to_cpu(part->start_block) * bsize;
+ plen = be32_to_cpu(part->block_count) * bsize;
+ dbg("found '%s' partition entry at 0x%llx, len 0x%llx",
+ part->type, poff, plen);
+
+ id->partitions[i].off = poff;
+ id->partitions[i].len = plen;
+
+ if (strncmp(part->type, "Apple_Free", 10) == 0) {
+ id->partitions[i].usage_id = VOLUME_ID_UNUSED;
+ } else if (strncmp(part->type, "Apple_partition_map", 19) == 0) {
+ id->partitions[i].usage_id = VOLUME_ID_PARTITIONTABLE;
+ id->partitions[i].type_id = VOLUME_ID_MACPARTMAP;
+ } else {
+ id->partitions[i].usage_id = VOLUME_ID_UNPROBED;
+ }
+ }
+ id->usage_id = VOLUME_ID_PARTITIONTABLE;
+ id->type_id = VOLUME_ID_MACPARTMAP;
+ id->type = "mac_partition_map";
+ return 0;
+ }
+
+ return -1;
+}
+
+#define HFS_SUPERBLOCK_OFFSET 0x400
+#define HFS_NODE_LEAF 0xff
+#define HFSPLUS_POR_CNID 1
+static int probe_hfs_hfsplus(struct volume_id *id, __u64 off)
+{
+ struct finder_info {
+ __u32 boot_folder;
+ __u32 start_app;
+ __u32 open_folder;
+ __u32 os9_folder;
+ __u32 reserved;
+ __u32 osx_folder;
+ __u8 id[8];
+ } __attribute__((__packed__));
+
+ struct hfs_mdb {
+ __u8 signature[2];
+ __u32 cr_date;
+ __u32 ls_Mod;
+ __u16 atrb;
+ __u16 nm_fls;
+ __u16 vbm_st;
+ __u16 alloc_ptr;
+ __u16 nm_al_blks;
+ __u32 al_blk_size;
+ __u32 clp_size;
+ __u16 al_bl_st;
+ __u32 nxt_cnid;
+ __u16 free_bks;
+ __u8 label_len;
+ __u8 label[27];
+ __u32 vol_bkup;
+ __u16 vol_seq_num;
+ __u32 wr_cnt;
+ __u32 xt_clump_size;
+ __u32 ct_clump_size;
+ __u16 num_root_dirs;
+ __u32 file_count;
+ __u32 dir_count;
+ struct finder_info finfo;
+ __u8 embed_sig[2];
+ __u16 embed_startblock;
+ __u16 embed_blockcount;
+ } __attribute__((__packed__)) *hfs;
+
+ 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__));
+
+ struct hfsplus_fork {
+ __u64 total_size;
+ __u32 clump_size;
+ __u32 total_blocks;
+ struct hfsplus_extent extents[8];
+ } __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 finder_info finfo;
+ 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__)) *hfsplus;
+
+ unsigned int blocksize;
+ unsigned int cat_block;
+ unsigned int cat_block_count;
+ unsigned int cat_off;
+ unsigned int cat_len;
+ unsigned int leaf_node_head;
+ unsigned int leaf_node_size;
+ unsigned int alloc_block_size;
+ unsigned int alloc_first_block;
+ unsigned int embed_first_block;
+ struct hfsplus_bnode_descriptor *descr;
+ struct hfsplus_bheader_record *bnode;
+ struct hfsplus_catalog_key *key;
+ unsigned int label_len;
+ const __u8 *buf;
+
+ buf = get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+ if (buf == NULL)
+ return -1;
+
+ hfs = (struct hfs_mdb *) buf;
+ if (strncmp(hfs->signature, "BD", 2) != 0)
+ goto checkplus;
+
+ /* it may be just a hfs wrapper for hfs+ */
+ if (strncmp(hfs->embed_sig, "H+", 2) == 0) {
+ alloc_block_size = be32_to_cpu(hfs->al_blk_size);
+ dbg("alloc_block_size 0x%x", alloc_block_size);
+
+ alloc_first_block = be16_to_cpu(hfs->al_bl_st);
+ dbg("alloc_first_block 0x%x", alloc_first_block);
+
+ embed_first_block = be16_to_cpu(hfs->embed_startblock);
+ dbg("embed_first_block 0x%x", embed_first_block);
+
+ off += (alloc_first_block * 512) +
+ (embed_first_block * alloc_block_size);
+ dbg("hfs wrapped hfs+ found at offset 0x%llx", off);
+
+ buf = get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+ if (buf == NULL)
+ return -1;
+ goto checkplus;
+ }
+
+ if (hfs->label_len > 0 && hfs->label_len < 28) {
+ set_label_raw(id, hfs->label, hfs->label_len);
+ set_label_string(id, hfs->label, hfs->label_len) ;
+ }
+
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_HFS;
+ id->type = "hfs";
+
+ return 0;
+
+checkplus:
+ hfsplus = (struct hfsplus_vol_header *) buf;
+ if (strncmp(hfsplus->signature, "H+", 2) == 0)
+ goto hfsplus;
+ if (strncmp(hfsplus->signature, "HX", 2) == 0)
+ goto hfsplus;
+ return -1;
+
+hfsplus:
+ blocksize = be32_to_cpu(hfsplus->blocksize);
+ cat_block = be32_to_cpu(hfsplus->cat_file.extents[0].start_block);
+ cat_block_count = be32_to_cpu(hfsplus->cat_file.extents[0].block_count);
+ cat_off = (cat_block * blocksize);
+ cat_len = cat_block_count * blocksize;
+ dbg("catalog start 0x%llx, len 0x%x", off + cat_off, cat_len);
+
+ buf = get_buffer(id, off + cat_off, 0x2000);
+ if (buf == NULL)
+ goto found;
+
+ 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);
+
+ dbg("catalog leaf node 0x%x, size 0x%x",
+ leaf_node_head, leaf_node_size);
+
+ buf = get_buffer(id, off + cat_off + (leaf_node_head * leaf_node_size),
+ leaf_node_size);
+ if (buf == NULL)
+ goto found;
+
+ descr = (struct hfsplus_bnode_descriptor *) buf;
+ dbg("descriptor type 0x%x", descr->type);
+ if (descr->type != HFS_NODE_LEAF)
+ goto found;
+
+ key = (struct hfsplus_catalog_key *)
+ &buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+ dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
+ if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
+ goto found;
+
+ label_len = be16_to_cpu(key->unicode_len) * 2;
+ dbg("label unicode16 len %i", label_len);
+ set_label_raw(id, key->unicode, label_len);
+ set_label_unicode16(id, key->unicode, BE, label_len);
+
+found:
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_HFSPLUS;
+ id->type = "hfsplus";
return 0;
}
#define MFT_RECORD_VOLUME 3
-#define MFT_RECORD_ATTR_VOLUME_NAME 0x60u
-#define MFT_RECORD_ATTR_OBJECT_ID 0x40u
+#define MFT_RECORD_ATTR_VOLUME_NAME 0x60
+#define MFT_RECORD_ATTR_VOLUME_INFO 0x70
+#define MFT_RECORD_ATTR_OBJECT_ID 0x40
#define MFT_RECORD_ATTR_END 0xffffffffu
-static int probe_ntfs(struct volume_id *id)
+static int probe_ntfs(struct volume_id *id, __u64 off)
{
struct ntfs_super_block {
__u8 jump[3];
__u8 oem_id[8];
- struct bios_param_block {
- __u16 bytes_per_sector;
- __u8 sectors_per_cluster;
- __u16 reserved_sectors;
- __u8 fats;
- __u16 root_entries;
- __u16 sectors;
- __u8 media_type; /* 0xf8 = hard disk */
- __u16 sectors_per_fat;
- __u16 sectors_per_track;
- __u16 heads;
- __u32 hidden_sectors;
- __u32 large_sectors;
- } __attribute__((__packed__)) bpb;
- __u8 unused[4];
+ __u16 bytes_per_sector;
+ __u8 sectors_per_cluster;
+ __u16 reserved_sectors;
+ __u8 fats;
+ __u16 root_entries;
+ __u16 sectors;
+ __u8 media_type;
+ __u16 sectors_per_fat;
+ __u16 sectors_per_track;
+ __u16 heads;
+ __u32 hidden_sectors;
+ __u32 large_sectors;
+ __u16 unused[2];
__u64 number_of_sectors;
__u64 mft_cluster_location;
__u64 mft_mirror_cluster_location;
__s8 cluster_per_mft_record;
+ __u8 reserved1[3];
+ __s8 cluster_per_index_record;
+ __u8 reserved2[3];
+ __u8 volume_serial[8];
+ __u16 checksum;
} __attribute__((__packed__)) *ns;
struct master_file_table_record {
@@ -752,28 +1761,36 @@ static int probe_ntfs(struct volume_id *id)
__u16 value_offset;
} __attribute__((__packed__)) *attr;
- unsigned int sector_size;
- unsigned int cluster_size;
- unsigned long mft_cluster;
- unsigned long mft_off;
- unsigned int mft_record_size;
- unsigned int attr_type;
- unsigned int attr_off;
- unsigned int attr_len;
- unsigned int val_off;
- unsigned int val_len;
+ struct volume_info {
+ __u64 reserved;
+ __u8 major_ver;
+ __u8 minor_ver;
+ } __attribute__((__packed__)) *info;
+
+ unsigned int sector_size;
+ unsigned int cluster_size;
+ __u64 mft_cluster;
+ __u64 mft_off;
+ unsigned int mft_record_size;
+ unsigned int attr_type;
+ unsigned int attr_off;
+ unsigned int attr_len;
+ unsigned int val_off;
+ unsigned int val_len;
const __u8 *buf;
const __u8 *val;
- ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
+ ns = (struct ntfs_super_block *) get_buffer(id, off, 0x200);
if (ns == NULL)
return -1;
if (strncmp(ns->oem_id, "NTFS", 4) != 0)
return -1;
- sector_size = le16_to_cpu(ns->bpb.bytes_per_sector);
- cluster_size = ns->bpb.sectors_per_cluster * sector_size;
+ set_uuid(id, ns->volume_serial, 8);
+
+ sector_size = le16_to_cpu(ns->bytes_per_sector);
+ cluster_size = ns->sectors_per_cluster * sector_size;
mft_cluster = le64_to_cpu(ns->mft_cluster_location);
mft_off = mft_cluster * cluster_size;
@@ -785,22 +1802,19 @@ static int probe_ntfs(struct volume_id *id)
dbg("sectorsize 0x%x", sector_size);
dbg("clustersize 0x%x", cluster_size);
- dbg("mftcluster %li", mft_cluster);
- dbg("mftoffset 0x%lx", mft_off);
+ dbg("mftcluster %lli", mft_cluster);
+ dbg("mftoffset 0x%llx", mft_off);
dbg("cluster per mft_record %i", ns->cluster_per_mft_record);
dbg("mft record size %i", mft_record_size);
- buf = get_buffer(id, mft_off + (MFT_RECORD_VOLUME * mft_record_size),
+ buf = get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size),
mft_record_size);
if (buf == NULL)
goto found;
mftr = (struct master_file_table_record*) buf;
- dbg("mftr->magic[0] = '%c' %03d, 0x%02x", mftr->magic[0], mftr->magic[0], mftr->magic[0]);
- dbg("mftr->magic[1] = '%c' %03d, 0x%02x", mftr->magic[1], mftr->magic[1], mftr->magic[1]);
- dbg("mftr->magic[2] = '%c' %03d, 0x%02x", mftr->magic[2], mftr->magic[2], mftr->magic[2]);
- dbg("mftr->magic[3] = '%c' %03d, 0x%02x", mftr->magic[3], mftr->magic[3], mftr->magic[3]);
+ dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]);
if (strncmp(mftr->magic, "FILE", 4) != 0)
goto found;
@@ -813,6 +1827,13 @@ static int probe_ntfs(struct volume_id *id)
attr_len = le16_to_cpu(attr->len);
val_off = le16_to_cpu(attr->value_offset);
val_len = le32_to_cpu(attr->value_len);
+ attr_off += attr_len;
+
+ if (attr_len == 0)
+ break;
+
+ if (attr_off >= mft_record_size)
+ break;
if (attr_type == MFT_RECORD_ATTR_END)
break;
@@ -820,136 +1841,182 @@ static int probe_ntfs(struct volume_id *id)
dbg("found attribute type 0x%x, len %i, at offset %i",
attr_type, attr_len, attr_off);
+ if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) {
+ dbg("found info, len %i", val_len);
+ info = (struct volume_info*) (((__u8 *) attr) + val_off);
+ snprintf(id->type_version, VOLUME_ID_FORMAT_SIZE-1,
+ "%u.%u", info->major_ver, info->minor_ver);
+ }
+
if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
dbg("found label, len %i", val_len);
if (val_len > VOLUME_ID_LABEL_SIZE)
val_len = VOLUME_ID_LABEL_SIZE;
- val = &((__u8 *) attr)[val_off];
+ val = ((__u8 *) attr) + val_off;
set_label_raw(id, val, val_len);
set_label_unicode16(id, val, LE, val_len);
}
-
- if (attr_type == MFT_RECORD_ATTR_OBJECT_ID) {
- dbg("found uuid");
- val = &((__u8 *) attr)[val_off];
- set_uuid(id, val, 16);
- }
-
- if (attr_len == 0)
- break;
- attr_off += attr_len;
- if (attr_off >= mft_record_size)
- break;
}
found:
- id->fs_type = NTFS;
- id->fs_name = "ntfs";
+ id->usage_id = VOLUME_ID_FILESYSTEM;
+ id->type_id = VOLUME_ID_NTFS;
+ id->type = "ntfs";
return 0;
}
#define LARGEST_PAGESIZE 0x4000
-static int probe_swap(struct volume_id *id)
+static int probe_swap(struct volume_id *id, __u64 off)
{
const __u8 *sig;
unsigned int page;
/* huhh, the swap signature is on the end of the PAGE_SIZE */
for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
- sig = get_buffer(id, page-10, 10);
+ sig = get_buffer(id, off + page-10, 10);
if (sig == NULL)
return -1;
- if (strncmp(sig, "SWAP-SPACE", 10) == 0)
+ if (strncmp(sig, "SWAP-SPACE", 10) == 0) {
+ strcpy(id->type_version, "1");
goto found;
- if (strncmp(sig, "SWAPSPACE2", 10) == 0)
+ }
+ if (strncmp(sig, "SWAPSPACE2", 10) == 0) {
+ strcpy(id->type_version, "2");
goto found;
+ }
}
return -1;
found:
- id->fs_type = SWAP;
- id->fs_name = "swap";
+ id->usage_id = VOLUME_ID_OTHER;
+ id->type_id = VOLUME_ID_SWAP;
+ id->type = "swap";
return 0;
}
/* probe volume for filesystem type and try to read label+uuid */
-int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
+int volume_id_probe(struct volume_id *id,
+ enum volume_id_type type,
+ unsigned long long off,
+ unsigned long long size)
{
int rc;
if (id == NULL)
return -EINVAL;
- switch (fs_type) {
- case EXT3:
- case EXT2:
- rc = probe_ext(id);
+ switch (type) {
+ case VOLUME_ID_MSDOSPARTTABLE:
+ rc = probe_msdos_part_table(id, off);
+ break;
+ case VOLUME_ID_EXT3:
+ case VOLUME_ID_EXT2:
+ rc = probe_ext(id, off);
+ break;
+ case VOLUME_ID_REISERFS:
+ rc = probe_reiserfs(id, off);
+ break;
+ case VOLUME_ID_XFS:
+ rc = probe_xfs(id, off);
+ break;
+ case VOLUME_ID_JFS:
+ rc = probe_jfs(id, off);
+ break;
+ case VOLUME_ID_VFAT:
+ rc = probe_vfat(id, off);
break;
- case REISER:
- rc = probe_reiser(id);
+ case VOLUME_ID_UDF:
+ rc = probe_udf(id, off);
break;
- case XFS:
- rc = probe_xfs(id);
+ case VOLUME_ID_ISO9660:
+ rc = probe_iso9660(id, off);
break;
- case JFS:
- rc = probe_jfs(id);
+ case VOLUME_ID_MACPARTMAP:
+ rc = probe_mac_partition_map(id, off);
break;
- case MSDOS:
- rc = probe_msdos(id);
+ case VOLUME_ID_HFS:
+ case VOLUME_ID_HFSPLUS:
+ rc = probe_hfs_hfsplus(id, off);
break;
- case VFAT:
- rc = probe_vfat(id);
+ case VOLUME_ID_UFS:
+ rc = probe_ufs(id, off);
break;
- case UDF:
- rc = probe_udf(id);
+ case VOLUME_ID_NTFS:
+ rc = probe_ntfs(id, off);
break;
- case ISO9660:
- rc = probe_iso9660(id);
+ case VOLUME_ID_SWAP:
+ rc = probe_swap(id, off);
break;
- case NTFS:
- rc = probe_ntfs(id);
+ case VOLUME_ID_LINUX_RAID:
+ rc = probe_linux_raid(id, off, size);
break;
- case SWAP:
- rc = probe_swap(id);
+ case VOLUME_ID_LVM1:
+ rc = probe_lvm1(id, off);
break;
- case ALL:
+ case VOLUME_ID_LVM2:
+ rc = probe_lvm2(id, off);
+ break;
+ case VOLUME_ID_ALL:
default:
+ rc = probe_linux_raid(id, off, size);
+ if (rc == 0)
+ break;
+
+ /* signature in the first block */
+ rc = probe_ntfs(id, off);
+ if (rc == 0)
+ break;
+ rc = probe_vfat(id, off);
+ if (rc == 0)
+ break;
+ rc = probe_msdos_part_table(id, off);
+ if (rc == 0)
+ break;
+ rc = probe_mac_partition_map(id, off);
+ if (rc == 0)
+ break;
+ rc = probe_xfs(id, off);
+ if (rc == 0)
+ break;
+
/* fill buffer with maximum */
get_buffer(id, 0, SB_BUFFER_SIZE);
- rc = probe_ext(id);
+
+ rc = probe_swap(id, off);
if (rc == 0)
break;
- rc = probe_reiser(id);
+ rc = probe_ext(id, off);
if (rc == 0)
break;
- rc = probe_xfs(id);
+ rc = probe_reiserfs(id, off);
if (rc == 0)
break;
- rc = probe_jfs(id);
+ rc = probe_jfs(id, off);
if (rc == 0)
break;
- rc = probe_msdos(id);
+ rc = probe_udf(id, off);
if (rc == 0)
break;
- rc = probe_vfat(id);
+ rc = probe_iso9660(id, off);
if (rc == 0)
break;
- rc = probe_udf(id);
+ rc = probe_hfs_hfsplus(id, off);
if (rc == 0)
break;
- rc = probe_iso9660(id);
+ rc = probe_ufs(id, off);
if (rc == 0)
break;
- rc = probe_ntfs(id);
+ rc = probe_lvm1(id, off);
if (rc == 0)
break;
- rc = probe_swap(id);
+ rc = probe_lvm2(id, off);
if (rc == 0)
break;
+
rc = -1;
}
@@ -982,9 +2049,11 @@ struct volume_id *volume_id_open_node(const char *path)
struct volume_id *id;
int fd;
- fd = open(path, O_RDONLY);
- if (fd < 0)
+ fd = open(path, O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ dbg("unable to open '%s'", path);
return NULL;
+ }
id = volume_id_open_fd(fd);
if (id == NULL)
@@ -1029,5 +2098,8 @@ void volume_id_close(struct volume_id *id)
free_buffer(id);
+ if (id->partitions != NULL)
+ free(id->partitions);
+
free(id);
}