diff --git a/extras/volume_id/volume_id.c b/extras/volume_id/volume_id.c index dcc341c4dd8..ac7e76b9d47 100644 --- a/extras/volume_id/volume_id.c +++ b/extras/volume_id/volume_id.c @@ -7,7 +7,7 @@ * 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 data access, you may have + * much more partition/media types or non-root disk access, you may have * a look at: * http://e2fsprogs.sourceforge.net. * @@ -26,6 +26,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + #include #include #include @@ -55,12 +59,23 @@ (((__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)) + #if (__BYTE_ORDER == __LITTLE_ENDIAN) #define le16_to_cpu(x) (x) #define le32_to_cpu(x) (x) +#define le64_to_cpu(x) (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) #endif /* size of superblock buffer, reiser block is at 64k */ @@ -69,15 +84,17 @@ #define SEEK_BUFFER_SIZE 0x800 -static void set_label_raw(struct volume_id *id, char *buf, int count) +static void set_label_raw(struct volume_id *id, + const __u8 *buf, unsigned int count) { memcpy(id->label_raw, buf, count); id->label_raw_len = count; } -static void set_label_string(struct volume_id *id, char *buf, int count) +static void set_label_string(struct volume_id *id, + const __u8 *buf, unsigned int count) { - int i; + unsigned int i; memcpy(id->label_string, buf, count); @@ -90,9 +107,42 @@ static void set_label_string(struct volume_id *id, char *buf, int count) id->label_string[i+1] = '\0'; } -static void set_uuid(struct volume_id *id, unsigned char *buf, int count) +#define LE 0 +#define BE 1 +static void set_label_unicode16(struct volume_id *id, + const __u8 *buf, + unsigned int endianess, + unsigned int count) { - int i; + unsigned int i, j; + __u16 c; + + j = 0; + for (i = 0; i <= count-2; 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'; + break; + } else if (c < 0x80) { + id->label_string[j++] = (__u8) c; + } else if (c < 0x800) { + id->label_string[j++] = (__u8) (0xc0 | (c >> 6)); + id->label_string[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)); + } + } +} + +static void set_uuid(struct volume_id *id, + const __u8 *buf, unsigned int count) +{ + unsigned int i; memcpy(id->uuid, buf, count); @@ -121,9 +171,10 @@ set: } } -static char *get_buffer(struct volume_id *id, size_t off, size_t len) +static __u8 *get_buffer(struct volume_id *id, + unsigned long off, unsigned int len) { - size_t buf_len; + unsigned int buf_len; /* check if requested area fits in superblock buffer */ if (off + len <= SB_BUFFER_SIZE) { @@ -135,8 +186,8 @@ static char *get_buffer(struct volume_id *id, size_t off, size_t len) /* check if we need to read */ if ((off + len) > id->sbbuf_len) { - dbg("read sbbuf len:0x%x", off + len); - lseek(id->fd, 0, SEEK_SET); + dbg("read sbbuf len:0x%lx", off + len); + lseek64(id->fd, 0, SEEK_SET); buf_len = read(id->fd, id->sbbuf, off + len); id->sbbuf_len = buf_len; if (buf_len < off + len) @@ -158,9 +209,10 @@ static char *get_buffer(struct volume_id *id, size_t off, size_t len) /* check if we need to read */ if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) { - dbg("read seekbuf off:0x%x len:0x%x", off, len); - lseek(id->fd, off, SEEK_SET); + dbg("read seekbuf off:0x%lx len:0x%x", off, len); + lseek64(id->fd, off, SEEK_SET); buf_len = read(id->fd, id->seekbuf, len); + dbg("got 0x%x (%i) bytes", buf_len, buf_len); id->seekbuf_off = off; id->seekbuf_len = buf_len; if (buf_len < len) @@ -191,23 +243,23 @@ static void free_buffer(struct volume_id *id) static int probe_ext(struct volume_id *id) { struct ext2_super_block { - __u32 inodes_count; - __u32 blocks_count; - __u32 r_blocks_count; - __u32 free_blocks_count; - __u32 free_inodes_count; - __u32 first_data_block; - __u32 log_block_size; - __u32 dummy3[7]; - unsigned char magic[2]; - __u16 state; - __u32 dummy5[8]; - __u32 feature_compat; - __u32 feature_incompat; - __u32 feature_ro_compat; - unsigned char uuid[16]; - char volume_name[16]; - } *es; + __u32 inodes_count; + __u32 blocks_count; + __u32 r_blocks_count; + __u32 free_blocks_count; + __u32 free_inodes_count; + __u32 first_data_block; + __u32 log_block_size; + __u32 dummy3[7]; + __u8 magic[2]; + __u16 state; + __u32 dummy5[8]; + __u32 feature_compat; + __u32 feature_incompat; + __u32 feature_ro_compat; + __u8 uuid[16]; + __u8 volume_name[16]; + } __attribute__((__packed__)) *es; es = (struct ext2_super_block *) get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200); @@ -239,20 +291,20 @@ static int probe_ext(struct volume_id *id) static int probe_reiser(struct volume_id *id) { struct reiser_super_block { - __u32 blocks_count; - __u32 free_blocks; - __u32 root_block; - __u32 journal_block; - __u32 journal_dev; - __u32 orig_journal_size; - __u32 dummy2[5]; - __u16 blocksize; - __u16 dummy3[3]; - unsigned char magic[12]; - __u32 dummy4[5]; - unsigned char uuid[16]; - char label[16]; - } *rs; + __u32 blocks_count; + __u32 free_blocks; + __u32 root_block; + __u32 journal_block; + __u32 journal_dev; + __u32 orig_journal_size; + __u32 dummy2[5]; + __u16 blocksize; + __u16 dummy3[3]; + __u8 magic[12]; + __u32 dummy4[5]; + __u8 uuid[16]; + __u8 label[16]; + } __attribute__((__packed__)) *rs; rs = (struct reiser_super_block *) get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200); @@ -288,19 +340,19 @@ found: static int probe_xfs(struct volume_id *id) { struct xfs_super_block { - unsigned char magic[4]; - __u32 blocksize; - __u64 dblocks; - __u64 rblocks; - __u32 dummy1[2]; - unsigned char uuid[16]; - __u32 dummy2[15]; - char fname[12]; - __u32 dummy3[2]; - __u64 icount; - __u64 ifree; - __u64 fdblocks; - } *xs; + __u8 magic[4]; + __u32 blocksize; + __u64 dblocks; + __u64 rblocks; + __u32 dummy1[2]; + __u8 uuid[16]; + __u32 dummy2[15]; + __u8 fname[12]; + __u32 dummy3[2]; + __u64 icount; + __u64 ifree; + __u64 fdblocks; + } __attribute__((__packed__)) *xs; xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200); if (xs == NULL) @@ -323,17 +375,17 @@ static int probe_xfs(struct volume_id *id) static int probe_jfs(struct volume_id *id) { struct jfs_super_block { - unsigned char magic[4]; - __u32 version; - __u64 size; - __u32 bsize; - __u32 dummy1; - __u32 pbsize; - __u32 dummy2[27]; - unsigned char uuid[16]; - unsigned char label[16]; - unsigned char loguuid[16]; - } *js; + __u8 magic[4]; + __u32 version; + __u64 size; + __u32 bsize; + __u32 dummy1; + __u32 pbsize; + __u32 dummy2[27]; + __u8 uuid[16]; + __u8 label[16]; + __u8 loguuid[16]; + } __attribute__((__packed__)) *js; js = (struct jfs_super_block *) get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200); @@ -356,34 +408,34 @@ static int probe_jfs(struct volume_id *id) static int probe_vfat(struct volume_id *id) { struct vfat_super_block { - unsigned char ignored[3]; - unsigned char sysid[8]; - unsigned char sector_size[2]; - __u8 cluster_size; - __u16 reserved; - __u8 fats; - unsigned char dir_entries[2]; - unsigned char sectors[2]; - unsigned char 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]; - unsigned char unknown[3]; - unsigned char serno[4]; - char label[11]; - unsigned char magic[8]; - unsigned char dummy2[164]; - unsigned char pmagic[2]; - } *vs; + __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; + __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__)) *vs; vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200); if (vs == NULL) @@ -409,27 +461,27 @@ found: static int probe_msdos(struct volume_id *id) { struct msdos_super_block { - unsigned char ignored[3]; - unsigned char sysid[8]; - unsigned char sector_size[2]; - __u8 cluster_size; - __u16 reserved; - __u8 fats; - unsigned char dir_entries[2]; - unsigned char sectors[2]; - unsigned char media; - __u16 fat_length; - __u16 secs_track; - __u16 heads; - __u32 hidden; - __u32 total_sect; - unsigned char unknown[3]; - unsigned char serno[4]; - char label[11]; - unsigned char magic[8]; - unsigned char dummy2[192]; - unsigned char pmagic[2]; - } *ms; + __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) @@ -459,45 +511,43 @@ static int probe_udf(struct volume_id *id) { struct volume_descriptor { struct descriptor_tag { - __u16 id; - __u16 version; - unsigned char checksum; - unsigned char reserved; - __u16 serial; - __u16 crc; - __u16 crc_len; - __u32 location; - } tag; + __u16 id; + __u16 version; + __u8 checksum; + __u8 reserved; + __u16 serial; + __u16 crc; + __u16 crc_len; + __u32 location; + } __attribute__((__packed__)) tag; union { struct anchor_descriptor { - __u32 length; - __u32 location; - } anchor; + __u32 length; + __u32 location; + } __attribute__((__packed__)) anchor; struct primary_descriptor { - __u32 seq_num; - __u32 desc_num; + __u32 seq_num; + __u32 desc_num; struct dstring { - char clen; - char c[31]; - } ident; - } primary; - } type; - } *vd; + __u8 clen; + __u8 c[31]; + } __attribute__((__packed__)) ident; + } __attribute__((__packed__)) primary; + } __attribute__((__packed__)) type; + } __attribute__((__packed__)) *vd; struct volume_structure_descriptor { - unsigned char type; - char id[5]; - unsigned char version; + __u8 type; + __u8 id[5]; + __u8 version; } *vsd; - size_t bs; - size_t b; - int type; - int count; - int loc; - int clen; - int i,j; - int c; + unsigned int bs; + unsigned int b; + unsigned int type; + unsigned int count; + unsigned int loc; + unsigned int clen; vsd = (struct volume_structure_descriptor *) get_buffer(id, UDF_VSD_OFFSET, 0x200); @@ -594,29 +644,10 @@ 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) { - /* convert unicode OSTA dstring to UTF-8 */ - j = 0; - for (i = 0; i < 32; i += 2) { - c = (vd->type.primary.ident.c[i] << 8) | - vd->type.primary.ident.c[i+1]; - if (c == 0) { - id->label_string[j] = '\0'; - break; - }else if (c < 0x80U) { - id->label_string[j++] = (char) c; - } else if (c < 0x800U) { - id->label_string[j++] = (char) (0xc0 | (c >> 6)); - id->label_string[j++] = (char) (0x80 | (c & 0x3f)); - } else { - id->label_string[j++] = (char) (0xe0 | (c >> 12)); - id->label_string[j++] = (char) (0x80 | ((c >> 6) & 0x3f)); - id->label_string[j++] = (char) (0x80 | (c & 0x3f)); - } - } - } + else if (clen == 16) + set_label_unicode16(id, vd->type.primary.ident.c, BE,31); found: id->fs_type = UDF; @@ -630,20 +661,20 @@ static int probe_iso9660(struct volume_id *id) { union iso_super_block { struct iso_header { - unsigned char type; - char id[5]; - unsigned char version; - unsigned char unused1; - char system_id[32]; - char volume_id[32]; - } iso; + __u8 type; + __u8 id[5]; + __u8 version; + __u8 unused1; + __u8 system_id[32]; + __u8 volume_id[32]; + } __attribute__((__packed__)) iso; struct hs_header { - char foo[8]; - unsigned char type; - char id[4]; - unsigned char version; - } hs; - } *is; + __u8 foo[8]; + __u8 type; + __u8 id[4]; + __u8 version; + } __attribute__((__packed__)) hs; + } __attribute__((__packed__)) *is; is = (union iso_super_block *) get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200); @@ -666,12 +697,73 @@ found: 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_END 0xffffffffu static int probe_ntfs(struct volume_id *id) { struct ntfs_super_block { - char jump[3]; - char oem_id[4]; - } *ns; + __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]; + __u64 number_of_sectors; + __u64 mft_cluster_location; + __u64 mft_mirror_cluster_location; + __s8 cluster_per_mft_record; + } __attribute__((__packed__)) *ns; + + struct master_file_table_record { + __u8 magic[4]; + __u16 usa_ofs; + __u16 usa_count; + __u64 lsn; + __u16 sequence_number; + __u16 link_count; + __u16 attrs_offset; + __u16 flags; + __u32 bytes_in_use; + __u32 bytes_allocated; + } __attribute__((__packed__)) *mftr; + + struct file_attribute { + __u32 type; + __u32 len; + __u8 non_resident; + __u8 name_len; + __u16 name_offset; + __u16 flags; + __u16 instance; + __u32 value_len; + __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; + const __u8 *buf; + const __u8 *val; ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200); if (ns == NULL) @@ -680,6 +772,78 @@ static int probe_ntfs(struct volume_id *id) 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; + mft_cluster = le64_to_cpu(ns->mft_cluster_location); + mft_off = mft_cluster * cluster_size; + + if (ns->cluster_per_mft_record < 0) + /* size = -log2(mft_record_size); normally 1024 Bytes */ + mft_record_size = 1 << -ns->cluster_per_mft_record; + else + mft_record_size = ns->cluster_per_mft_record * cluster_size; + + dbg("sectorsize 0x%x", sector_size); + dbg("clustersize 0x%x", cluster_size); + dbg("mftcluster %li", mft_cluster); + dbg("mftoffset 0x%lx", 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), + 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]); + if (strncmp(mftr->magic, "FILE", 4) != 0) + goto found; + + attr_off = le16_to_cpu(mftr->attrs_offset); + dbg("file $Volume's attributes are at offset %i", attr_off); + + while (1) { + attr = (struct file_attribute*) &buf[attr_off]; + attr_type = le32_to_cpu(attr->type); + attr_len = le16_to_cpu(attr->len); + val_off = le16_to_cpu(attr->value_offset); + val_len = le32_to_cpu(attr->value_len); + + if (attr_type == MFT_RECORD_ATTR_END) + break; + + dbg("found attribute type 0x%x, len %i, at offset %i", + attr_type, attr_len, attr_off); + + 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]; + 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"; @@ -689,8 +853,8 @@ static int probe_ntfs(struct volume_id *id) #define LARGEST_PAGESIZE 0x4000 static int probe_swap(struct volume_id *id) { - char *sig; - size_t page; + 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) { @@ -836,7 +1000,7 @@ struct volume_id *volume_id_open_node(const char *path) struct volume_id *volume_id_open_dev_t(dev_t devt) { struct volume_id *id; - char tmp_node[VOLUME_ID_PATH_MAX]; + __u8 tmp_node[VOLUME_ID_PATH_MAX]; snprintf(tmp_node, VOLUME_ID_PATH_MAX, "/tmp/volume-%u-%u-%u", getpid(), major(devt), minor(devt)); diff --git a/extras/volume_id/volume_id.h b/extras/volume_id/volume_id.h index 2f20244c7c7..a939743dc1e 100644 --- a/extras/volume_id/volume_id.h +++ b/extras/volume_id/volume_id.h @@ -21,9 +21,9 @@ #ifndef _VOLUME_ID_H_ #define _VOLUME_ID_H_ -#define VOLUME_ID_VERSION 002 +#define VOLUME_ID_VERSION 004 -#define VOLUME_ID_LABEL_SIZE 32 +#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 @@ -45,19 +45,19 @@ enum filesystem_type { }; struct volume_id { - char label_raw[VOLUME_ID_LABEL_SIZE]; - size_t label_raw_len; + 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; int fd; - char *sbbuf; - size_t sbbuf_len; - char *seekbuf; - size_t seekbuf_off; - size_t seekbuf_len; + unsigned char *sbbuf; + unsigned int sbbuf_len; + unsigned char *seekbuf; + unsigned int seekbuf_off; + unsigned int seekbuf_len; int fd_close; };