X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=extras%2Fvolume_id%2Fvolume_id.c;h=42d658895f11ffe8f68d37eb170ee85c78527fbf;hb=a5d00f9d3725c701d5a2d7a14dfcc7864aac27fb;hp=daeb00038a73ccca6324768430595ce35870d645;hpb=c506c4087efe567b3cb382bd228644cde3453b04;p=elogind.git diff --git a/extras/volume_id/volume_id.c b/extras/volume_id/volume_id.c index daeb00038..42d658895 100644 --- a/extras/volume_id/volume_id.c +++ b/extras/volume_id/volume_id.c @@ -24,17 +24,17 @@ */ #ifndef _GNU_SOURCE -#define _GNU_SOURCE +#define _GNU_SOURCE 1 #endif #ifdef HAVE_CONFIG_H # include #endif -#include #include -#include +#include #include +#include #include #include #include @@ -42,32 +42,24 @@ #include #include "volume_id.h" - -#ifdef DEBUG -#define dbg(format, arg...) \ - do { \ - printf("%s: " format "\n", __FUNCTION__ , ## arg); \ - } while (0) -#else -#define dbg(format, arg...) do {} while (0) -#endif /* DEBUG */ +#include "volume_id_logging.h" #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \ - (((__u32)(x) & 0xff00u) >> 8)) + (((__u16)(x) & 0xff00u) >> 8)) #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \ (((__u32)(x) & 0x00ff0000u) >> 8) | \ (((__u32)(x) & 0x0000ff00u) << 8) | \ (((__u32)(x) & 0x000000ffu) << 24)) -#define bswap64(x) (__u64)((((__u64)(x) & 0xff00000000000000u) >> 56) | \ - (((__u64)(x) & 0x00ff000000000000u) >> 40) | \ - (((__u64)(x) & 0x0000ff0000000000u) >> 24) | \ - (((__u64)(x) & 0x000000ff00000000u) >> 8) | \ - (((__u64)(x) & 0x00000000ff000000u) << 8) | \ - (((__u64)(x) & 0x0000000000ff0000u) << 24) | \ - (((__u64)(x) & 0x000000000000ff00u) << 40) | \ - (((__u64)(x) & 0x00000000000000ffu) << 56)) +#define bswap64(x) (__u64)((((__u64)(x) & 0xff00000000000000ull) >> 56) | \ + (((__u64)(x) & 0x00ff000000000000ull) >> 40) | \ + (((__u64)(x) & 0x0000ff0000000000ull) >> 24) | \ + (((__u64)(x) & 0x000000ff00000000ull) >> 8) | \ + (((__u64)(x) & 0x00000000ff000000ull) << 8) | \ + (((__u64)(x) & 0x0000000000ff0000ull) << 24) | \ + (((__u64)(x) & 0x000000000000ff00ull) << 40) | \ + (((__u64)(x) & 0x00000000000000ffull) << 56)) #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define le16_to_cpu(x) (x) @@ -144,34 +136,56 @@ static void set_label_unicode16(struct volume_id *id, } } -static void set_uuid(struct volume_id *id, - const __u8 *buf, unsigned int count) +enum uuid_format { + UUID_DCE, + UUID_DOS, + UUID_NTFS, + UUID_HFS, +}; + +static void set_uuid(struct volume_id *id, const __u8 *buf, enum uuid_format format) { unsigned int i; + unsigned int count = 0; + switch(format) { + case UUID_DOS: + count = 4; + break; + case UUID_NTFS: + case UUID_HFS: + count = 8; + break; + case UUID_DCE: + count = 16; + } memcpy(id->uuid_raw, buf, count); - /* create string if uuid is set */ + /* if set, create string in the same format, the native platform uses */ for (i = 0; i < count; i++) if (buf[i] != 0) goto set; return; set: - switch(count) { - case 4: + switch(format) { + case UUID_DOS: 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", + case UUID_NTFS: + 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: + case UUID_HFS: + sprintf(id->uuid,"%02X%02X%02X%02X%02X%02X%02X%02X", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + break; + case UUID_DCE: sprintf(id->uuid, - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x", + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], @@ -249,6 +263,36 @@ static void free_buffer(struct volume_id *id) } } +#define HPT37X_CONFIG_OFF 0x1200 +#define HPT37X_MAGIC_OK 0x5a7816f0 +#define HPT37X_MAGIC_BAD 0x5a7816fd +static int probe_highpoint_ataraid(struct volume_id *id, __u64 off) +{ + struct hpt37x { + __u8 filler1[32]; + __u32 magic; + __u32 magic_0; + __u32 magic_1; + } __attribute__((packed)) *hpt; + + const __u8 *buf; + + buf = get_buffer(id, off + HPT37X_CONFIG_OFF, 0x200); + if (buf == NULL) + return -1; + + hpt = (struct hpt37x *) buf; + + if (hpt->magic != HPT37X_MAGIC_OK && hpt->magic != HPT37X_MAGIC_BAD) + return -1; + + id->usage_id = VOLUME_ID_RAID; + id->type_id = VOLUME_ID_HPTRAID; + id->type = "hpt_ataraid_member"; + + return 0; +} + #define LVM1_SB_OFF 0x400 #define LVM1_MAGIC "HM" static int probe_lvm1(struct volume_id *id, __u64 off) @@ -307,7 +351,7 @@ static int probe_lvm2(struct volume_id *id, __u64 off) found: strncpy(id->type_version, lvm->type, 8); id->usage_id = VOLUME_ID_RAID; - id->type_id = VOLUME_ID_LVM1; + id->type_id = VOLUME_ID_LVM2; id->type = "LVM2_member"; return 0; @@ -337,12 +381,13 @@ static int probe_linux_raid(struct volume_id *id, __u64 off, __u64 size) } __attribute__((packed)) *mdp; const __u8 *buf; - __u64 sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES; + __u64 sboff; __u8 uuid[16]; if (size < 0x10000) return -1; + sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES; buf = get_buffer(id, off + sboff, 0x800); if (buf == NULL) return -1; @@ -354,7 +399,7 @@ static int probe_linux_raid(struct volume_id *id, __u64 off, __u64 size) memcpy(uuid, &mdp->set_uuid0, 4); memcpy(&uuid[4], &mdp->set_uuid1, 12); - set_uuid(id, uuid, 16); + set_uuid(id, uuid, UUID_DCE); snprintf(id->type_version, VOLUME_ID_FORMAT_SIZE-1, "%u.%u.%u", le32_to_cpu(mdp->major_version), @@ -446,6 +491,8 @@ static int probe_msdos_part_table(struct volume_id *id, __u64 off) p = &id->partitions[i]; + p->partition_type_raw = part[i].sys_ind; + if (is_extended(part[i].sys_ind)) { dbg("found extended partition at 0x%llx", poff); p->usage_id = VOLUME_ID_PARTITIONTABLE; @@ -520,6 +567,9 @@ static int probe_msdos_part_table(struct volume_id *id, __u64 off) p->off = current + poff; p->len = plen; id->partition_count++; + + p->partition_type_raw = part[i].sys_ind; + if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) { dbg("to many partitions"); next = 0; @@ -572,7 +622,7 @@ static int probe_ext(struct volume_id *id, __u64 off) set_label_raw(id, es->volume_name, 16); set_label_string(id, es->volume_name, 16); - set_uuid(id, es->uuid, 16); + set_uuid(id, es->uuid, UUID_DCE); if ((le32_to_cpu(es->feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) { @@ -638,7 +688,7 @@ static int probe_reiserfs(struct volume_id *id, __u64 off) found: set_label_raw(id, rs->label, 16); set_label_string(id, rs->label, 16); - set_uuid(id, rs->uuid, 16); + set_uuid(id, rs->uuid, UUID_DCE); id->usage_id = VOLUME_ID_FILESYSTEM; id->type_id = VOLUME_ID_REISERFS; @@ -673,7 +723,7 @@ static int probe_xfs(struct volume_id *id, __u64 off) set_label_raw(id, xs->fname, 12); set_label_string(id, xs->fname, 12); - set_uuid(id, xs->uuid, 16); + set_uuid(id, xs->uuid, UUID_DCE); id->usage_id = VOLUME_ID_FILESYSTEM; id->type_id = VOLUME_ID_XFS; @@ -708,7 +758,7 @@ static int probe_jfs(struct volume_id *id, __u64 off) set_label_raw(id, js->label, 16); set_label_string(id, js->label, 16); - set_uuid(id, js->uuid, 16); + set_uuid(id, js->uuid, UUID_DCE); id->usage_id = VOLUME_ID_FILESYSTEM; id->type_id = VOLUME_ID_JFS; @@ -720,52 +770,6 @@ static int probe_jfs(struct volume_id *id, __u64 off) #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 { @@ -810,6 +814,20 @@ static int probe_vfat(struct volume_id *id, __u64 off) } __attribute__((__packed__)) type; } __attribute__((__packed__)) *vs; + 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__)) *dir; + __u16 sector_size; __u16 dir_entries; __u32 sect_count; @@ -826,6 +844,8 @@ static int probe_vfat(struct volume_id *id, __u64 off) __u32 buf_size; __u8 *label = NULL; __u32 next; + int maxloop; + int i; vs = (struct vfat_super_block *) get_buffer(id, off, 0x200); if (vs == NULL) @@ -892,7 +912,7 @@ valid: sect_count = le16_to_cpu(vs->sectors); if (sect_count == 0) - sect_count = vs->total_sect; + sect_count = le32_to_cpu(vs->total_sect); dbg("sect_count 0x%x", sect_count); fat_length = le16_to_cpu(vs->fat_length); @@ -920,15 +940,35 @@ valid: /* the label may be an attribute in the root directory */ root_start = (reserved + fat_size) * sector_size; + dbg("root dir start 0x%llx", root_start); root_dir_entries = le16_to_cpu(vs->dir_entries); - dbg("root dir start 0x%x", root_start); + dbg("expected entries 0x%x", root_dir_entries); buf_size = root_dir_entries * sizeof(struct vfat_dir_entry); buf = get_buffer(id, off + root_start, buf_size); if (buf == NULL) goto found; - label = vfat_search_label_in_dir(buf, buf_size); + dir = (struct vfat_dir_entry*) buf; + + for (i = 0; i <= root_dir_entries; i++) { + /* end marker */ + if (dir[i].name[0] == 0x00) { + dbg("end of dir"); + break; + } + + /* empty entry */ + if (dir[i].name[0] == 0xe5) + continue; + + if (dir[i].attr == FAT_ATTR_VOLUME) { + dbg("found ATTR_VOLUME id in root dir"); + label = dir[i].name; + } + + dbg("skip dir entry"); + } if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) { set_label_raw(id, label, 11); @@ -937,7 +977,7 @@ valid: 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); + set_uuid(id, vs->type.fat.serno, UUID_DOS); goto found; fat32: @@ -948,24 +988,46 @@ fat32: start_data_sect = reserved + fat_size; next = root_cluster; - while (1) { + maxloop = 100; + while (--maxloop) { __u32 next_sect_off; __u64 next_off; __u64 fat_entry_off; + int count; 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); + dbg("cluster offset 0x%llx", next_off); /* get cluster */ buf = get_buffer(id, off + next_off, buf_size); if (buf == NULL) goto found; - label = vfat_search_label_in_dir(buf, buf_size); - if (label != NULL) - break; + dir = (struct vfat_dir_entry*) buf; + count = buf_size / sizeof(struct vfat_dir_entry); + dbg("expected entries 0x%x", count); + + for (i = 0; i <= count; i++) { + /* end marker */ + if (dir[i].name[0] == 0x00) { + dbg("end of dir"); + goto fat32_label; + } + + /* empty entry */ + if (dir[i].name[0] == 0xe5) + continue; + + if (dir[i].attr == FAT_ATTR_VOLUME) { + dbg("found ATTR_VOLUME id in root dir"); + label = dir[i].name; + goto fat32_label; + } + + dbg("skip dir entry"); + } /* get FAT entry */ fat_entry_off = (reserved * sector_size) + (next * sizeof(__u32)); @@ -978,7 +1040,10 @@ fat32: if (next == 0) break; } + if (maxloop == 0) + dbg("reached maximum follow count of root cluster chain, give up"); +fat32_label: if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) { set_label_raw(id, label, 11); set_label_string(id, label, 11); @@ -986,7 +1051,7 @@ fat32: 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); + set_uuid(id, vs->type.fat32.serno, UUID_DOS); found: id->usage_id = VOLUME_ID_FILESYSTEM; @@ -1479,9 +1544,10 @@ static int probe_mac_partition_map(struct volume_id *id, __u64 off) #define HFS_SUPERBLOCK_OFFSET 0x400 #define HFS_NODE_LEAF 0xff #define HFSPLUS_POR_CNID 1 +#define HFSPLUS_EXTENT_COUNT 8 static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) { - struct finder_info { + struct hfs_finder_info{ __u32 boot_folder; __u32 start_app; __u32 open_folder; @@ -1515,7 +1581,7 @@ static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) __u16 num_root_dirs; __u32 file_count; __u32 dir_count; - struct finder_info finfo; + struct hfs_finder_info finder_info; __u8 embed_sig[2]; __u16 embed_startblock; __u16 embed_blockcount; @@ -1555,7 +1621,7 @@ static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) __u64 total_size; __u32 clump_size; __u32 total_blocks; - struct hfsplus_extent extents[8]; + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; } __attribute__((__packed__)); struct hfsplus_vol_header { @@ -1579,7 +1645,7 @@ static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) __u32 next_cnid; __u32 write_count; __u64 encodings_bmp; - struct finder_info finfo; + struct hfs_finder_info finder_info; struct hfsplus_fork alloc_file; struct hfsplus_fork ext_file; struct hfsplus_fork cat_file; @@ -1589,18 +1655,23 @@ static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) unsigned int blocksize; unsigned int cat_block; - unsigned int cat_block_count; - unsigned int cat_off; - unsigned int cat_len; + unsigned int ext_block_start; + unsigned int ext_block_count; + int ext; unsigned int leaf_node_head; + unsigned int leaf_node_count; unsigned int leaf_node_size; + unsigned int leaf_block; + __u64 leaf_off; unsigned int alloc_block_size; unsigned int alloc_first_block; unsigned int embed_first_block; + unsigned int record_count; struct hfsplus_bnode_descriptor *descr; struct hfsplus_bheader_record *bnode; struct hfsplus_catalog_key *key; unsigned int label_len; + struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; const __u8 *buf; buf = get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200); @@ -1637,6 +1708,8 @@ static int probe_hfs_hfsplus(struct volume_id *id, __u64 off) set_label_string(id, hfs->label, hfs->label_len) ; } + set_uuid(id, hfs->finder_info.id, UUID_HFS); + id->usage_id = VOLUME_ID_FILESYSTEM; id->type_id = VOLUME_ID_HFS; id->type = "hfs"; @@ -1652,14 +1725,16 @@ checkplus: return -1; hfsplus: + set_uuid(id, hfsplus->finder_info.id, UUID_HFS); + 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); + dbg("blocksize %u", blocksize); - buf = get_buffer(id, off + cat_off, 0x2000); + memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); + cat_block = be32_to_cpu(extents[0].start_block); + dbg("catalog start block 0x%x", cat_block); + + buf = get_buffer(id, off + (cat_block * blocksize), 0x2000); if (buf == NULL) goto found; @@ -1667,18 +1742,51 @@ hfsplus: &buf[sizeof(struct hfsplus_bnode_descriptor)]; leaf_node_head = be32_to_cpu(bnode->leaf_head); + dbg("catalog leaf node 0x%x", leaf_node_head); + leaf_node_size = be16_to_cpu(bnode->node_size); + dbg("leaf node size 0x%x", leaf_node_size); + + leaf_node_count = be32_to_cpu(bnode->leaf_count); + dbg("leaf node count 0x%x", leaf_node_count); + if (leaf_node_count == 0) + goto found; + + leaf_block = (leaf_node_head * leaf_node_size) / blocksize; - dbg("catalog leaf node 0x%x, size 0x%x", - leaf_node_head, leaf_node_size); + /* get physical location */ + for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { + ext_block_start = be32_to_cpu(extents[ext].start_block); + ext_block_count = be32_to_cpu(extents[ext].block_count); + dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count); - buf = get_buffer(id, off + cat_off + (leaf_node_head * leaf_node_size), - leaf_node_size); + if (ext_block_count == 0) + goto found; + + /* this is our extent */ + if (leaf_block < ext_block_count) + break; + + leaf_block -= ext_block_count; + } + if (ext == HFSPLUS_EXTENT_COUNT) + goto found; + dbg("found block in extent %i", ext); + + leaf_off = (ext_block_start + leaf_block) * blocksize; + + buf = get_buffer(id, off + leaf_off, leaf_node_size); if (buf == NULL) goto found; descr = (struct hfsplus_bnode_descriptor *) buf; dbg("descriptor type 0x%x", descr->type); + + record_count = be16_to_cpu(descr->num_recs); + dbg("number of records %u", record_count); + if (record_count == 0) + goto found; + if (descr->type != HFS_NODE_LEAF) goto found; @@ -1787,7 +1895,7 @@ static int probe_ntfs(struct volume_id *id, __u64 off) if (strncmp(ns->oem_id, "NTFS", 4) != 0) return -1; - set_uuid(id, ns->volume_serial, 8); + set_uuid(id, ns->volume_serial, UUID_NTFS); sector_size = le16_to_cpu(ns->bytes_per_sector); cluster_size = ns->sectors_per_cluster * sector_size; @@ -1960,20 +2068,30 @@ int volume_id_probe(struct volume_id *id, case VOLUME_ID_LVM2: rc = probe_lvm2(id, off); break; + case VOLUME_ID_HPTRAID: + rc = probe_highpoint_ataraid(id, off); + break; case VOLUME_ID_ALL: default: + /* probe for raid first, cause fs probes may be successful on raid members */ rc = probe_linux_raid(id, off, size); if (rc == 0) break; - - /* signature in the first block */ - rc = probe_ntfs(id, off); + rc = probe_lvm1(id, off); if (rc == 0) break; - rc = probe_vfat(id, off); + rc = probe_lvm2(id, off); if (rc == 0) break; + rc = probe_highpoint_ataraid(id, off); + if (rc == 0) + break; + + /* signature in the first block, only small buffer needed */ rc = probe_msdos_part_table(id, off); + if (rc == 0) + break; + rc = probe_vfat(id, off); if (rc == 0) break; rc = probe_mac_partition_map(id, off); @@ -2010,10 +2128,7 @@ int volume_id_probe(struct volume_id *id, rc = probe_ufs(id, off); if (rc == 0) break; - rc = probe_lvm1(id, off); - if (rc == 0) - break; - rc = probe_lvm2(id, off); + rc = probe_ntfs(id, off); if (rc == 0) break; @@ -2049,7 +2164,7 @@ struct volume_id *volume_id_open_node(const char *path) struct volume_id *id; int fd; - fd = open(path, O_RDONLY | O_NONBLOCK); + fd = open(path, O_RDONLY); if (fd < 0) { dbg("unable to open '%s'", path); return NULL;