From: kay.sievers@vrfy.org Date: Sun, 5 Sep 2004 16:05:36 +0000 (+0200) Subject: [PATCH] update udev_volume_id X-Git-Tag: 031~8 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c506c4087efe567b3cb382bd228644cde3453b04;p=systemd [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 :) --- diff --git a/extras/volume_id/Makefile b/extras/volume_id/Makefile index 307fdc2f..8813f9fc 100644 --- a/extras/volume_id/Makefile +++ b/extras/volume_id/Makefile @@ -28,7 +28,11 @@ INSTALL_PROGRAM = ${INSTALL} INSTALL_DATA = ${INSTALL} -m 644 INSTALL_SCRIPT = ${INSTALL_PROGRAM} -override CFLAGS+=-Wall -fno-builtin +override CFLAGS+=-Wall -fno-builtin -Wchar-subscripts -Wmissing-declarations \ + -Wnested-externs -Wpointer-arith -Wcast-align \ + -Wsign-compare + +override CFLAGS+=-D_FILE_OFFSET_BITS=64 SYSFS = ../../libsysfs/sysfs_bus.o \ ../../libsysfs/sysfs_class.o \ diff --git a/extras/volume_id/udev_volume_id.c b/extras/volume_id/udev_volume_id.c index 18915cb6..e8844505 100644 --- a/extras/volume_id/udev_volume_id.c +++ b/extras/volume_id/udev_volume_id.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include "../../libsysfs/sysfs/libsysfs.h" #include "../../udev_lib.h" @@ -71,6 +73,36 @@ static struct volume_id *open_classdev(struct sysfs_class_device *class_dev) return vid; } +static unsigned long long get_size(struct volume_id *vid) +{ + unsigned long long size; + + if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0) + size = 0; + + return size; +} + +static char *usage_id_name(enum volume_id_usage usage) +{ + switch(usage) { + case VOLUME_ID_UNUSED: + return "unused"; + case VOLUME_ID_UNPROBED: + return "unprobed"; + case VOLUME_ID_OTHER: + return "other"; + case VOLUME_ID_PARTITIONTABLE: + return "partitiontable"; + case VOLUME_ID_FILESYSTEM: + return "filesystem"; + case VOLUME_ID_RAID: + return "raid"; + default: + return "unknown type_id"; + } +} + int main(int argc, char *argv[]) { const char help[] = "usage: udev_volume_id [-t|-l|-u|-d]\n" @@ -80,7 +112,6 @@ int main(int argc, char *argv[]) " -d disk label from main device\n" "\n"; static const char short_options[] = "htlud"; - int option; char sysfs_path[SYSFS_PATH_MAX]; char dev_path[SYSFS_PATH_MAX]; struct sysfs_class_device *class_dev = NULL; @@ -92,9 +123,12 @@ int main(int argc, char *argv[]) char dasd_label[7]; static char name[VOLUME_ID_LABEL_SIZE]; int len, i, j; + unsigned long long size; int rc = 1; while (1) { + int option; + option = getopt(argc, argv, short_options); if (option == -1) break; @@ -146,24 +180,26 @@ int main(int argc, char *argv[]) vid = open_classdev(class_dev); if (vid == NULL) goto exit; - if (volume_id_probe(vid, ALL) == 0) + + size = get_size(vid); + + if (volume_id_probe(vid, VOLUME_ID_ALL, 0, size) == 0) goto print; break; case 'd' : - /* if we are on a partition, close it and open main block device */ + /* if we are on a partition, open main block device instead */ class_dev_parent = sysfs_get_classdev_parent(class_dev); - if (class_dev_parent != NULL) { - volume_id_close(vid); + if (class_dev_parent != NULL) vid = open_classdev(class_dev_parent); - } else { + else vid = open_classdev(class_dev_parent); - } if (vid == NULL) goto exit; + if (probe_ibm_partition(vid->fd, dasd_label) == 0) { - vid->fs_name = "dasd"; - strncpy(vid->label_string, dasd_label, 6); - vid->label_string[6] = '\0'; + vid->type = "dasd"; + strncpy(vid->label, dasd_label, 6); + vid->label[6] = '\0'; goto print; } break; @@ -174,10 +210,10 @@ int main(int argc, char *argv[]) print: - len = strnlen(vid->label_string, VOLUME_ID_LABEL_SIZE); + len = strnlen(vid->label, VOLUME_ID_LABEL_SIZE); /* remove trailing spaces */ - while (len > 0 && isspace(vid->label_string[len-1])) + while (len > 0 && isspace(vid->label[len-1])) len--; name[len] = '\0'; @@ -185,14 +221,14 @@ print: i = 0; j = 0; while (j < len) { - switch(vid->label_string[j]) { + switch(vid->label[j]) { case '/' : break; case ' ' : name[i++] = '_'; break; default : - name[i++] = vid->label_string[j]; + name[i++] = vid->label[j]; } j++; } @@ -200,27 +236,29 @@ print: switch (print) { case 't': - printf("%s\n", vid->fs_name); + printf("%s\n", vid->type); break; case 'l': - if (name[0] == '\0') { + if (name[0] == '\0' || vid->usage_id != VOLUME_ID_FILESYSTEM) { rc = 2; goto exit; } printf("%s\n", name); break; case 'u': - if (vid->uuid_string[0] == '\0') { + if (vid->uuid[0] == '\0' || vid->usage_id != VOLUME_ID_FILESYSTEM) { rc = 2; goto exit; } - printf("%s\n", vid->uuid_string); + printf("%s\n", vid->uuid); break; case 'a': - printf("T:%s\n", vid->fs_name); - printf("L:%s\n", vid->label_string); + printf("F:%s\n", usage_id_name(vid->usage_id)); + printf("T:%s\n", vid->type); + printf("V:%s\n", vid->type_version); + printf("L:%s\n", vid->label); printf("N:%s\n", name); - printf("U:%s\n", vid->uuid_string); + printf("U:%s\n", vid->uuid); } rc = 0; diff --git a/extras/volume_id/volume_id.c b/extras/volume_id/volume_id.c index ac7e76b9..daeb0003 100644 --- a/extras/volume_id/volume_id.c +++ b/extras/volume_id/volume_id.c @@ -3,13 +3,10 @@ * * Copyright (C) 2004 Kay Sievers * - * 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 +#endif + #include #include #include @@ -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); } diff --git a/extras/volume_id/volume_id.h b/extras/volume_id/volume_id.h index a939743d..eb903906 100644 --- a/extras/volume_id/volume_id.h +++ b/extras/volume_id/volume_id.h @@ -21,42 +21,72 @@ #ifndef _VOLUME_ID_H_ #define _VOLUME_ID_H_ -#define VOLUME_ID_VERSION 004 +#define VOLUME_ID_VERSION 022 #define VOLUME_ID_LABEL_SIZE 64 #define VOLUME_ID_UUID_SIZE 16 #define VOLUME_ID_UUID_STRING_SIZE 37 -#define VOLUME_ID_PATH_MAX 255 +#define VOLUME_ID_FORMAT_SIZE 32 +#define VOLUME_ID_PATH_MAX 256 +#define VOLUME_ID_PARTITIONS_MAX 16 +enum volume_id_usage { + VOLUME_ID_UNUSED, + VOLUME_ID_UNPROBED, + VOLUME_ID_OTHER, + VOLUME_ID_FILESYSTEM, + VOLUME_ID_PARTITIONTABLE, + VOLUME_ID_RAID +}; + +enum volume_id_type { + VOLUME_ID_ALL, + VOLUME_ID_MSDOSPARTTABLE, + VOLUME_ID_MSDOSEXTENDED, + VOLUME_ID_SWAP, + VOLUME_ID_EXT2, + VOLUME_ID_EXT3, + VOLUME_ID_REISERFS, + VOLUME_ID_XFS, + VOLUME_ID_JFS, + VOLUME_ID_VFAT, + VOLUME_ID_UDF, + VOLUME_ID_ISO9660, + VOLUME_ID_NTFS, + VOLUME_ID_MACPARTMAP, + VOLUME_ID_HFS, + VOLUME_ID_HFSPLUS, + VOLUME_ID_UFS, + VOLUME_ID_LINUX_RAID, + VOLUME_ID_LVM1, + VOLUME_ID_LVM2 +}; -enum filesystem_type { - ALL, - EXT2, - EXT3, - REISER, - XFS, - JFS, - MSDOS, - VFAT, - UDF, - ISO9660, - NTFS, - SWAP +struct volume_id_partition { + enum volume_id_usage usage_id; + enum volume_id_type type_id; + char *type; + unsigned long long off; + unsigned long long len; }; struct volume_id { unsigned char label_raw[VOLUME_ID_LABEL_SIZE]; unsigned int label_raw_len; - char label_string[VOLUME_ID_LABEL_SIZE+1]; - unsigned char uuid[VOLUME_ID_UUID_SIZE]; - char uuid_string[VOLUME_ID_UUID_STRING_SIZE]; - enum filesystem_type fs_type; - char *fs_name; + char label[VOLUME_ID_LABEL_SIZE+1]; + unsigned char uuid_raw[VOLUME_ID_UUID_SIZE]; + char uuid[VOLUME_ID_UUID_STRING_SIZE]; + enum volume_id_usage usage_id; + enum volume_id_type type_id; + char *type; + char type_version[VOLUME_ID_FORMAT_SIZE]; + struct volume_id_partition *partitions; + unsigned int partition_count; int fd; unsigned char *sbbuf; unsigned int sbbuf_len; unsigned char *seekbuf; - unsigned int seekbuf_off; + unsigned long long seekbuf_off; unsigned int seekbuf_len; int fd_close; }; @@ -71,7 +101,8 @@ extern struct volume_id *volume_id_open_node(const char *path); extern struct volume_id *volume_id_open_dev_t(dev_t devt); /* probe volume for filesystem type and try to read label/uuid */ -extern int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type); +extern int volume_id_probe(struct volume_id *id, enum volume_id_type type, + unsigned long long off, unsigned long long size); /* free allocated device info */ extern void volume_id_close(struct volume_id *id);