2 * volume_id - reads filesystem label and uuid
4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
6 * The superblock structs are taken from the libblkid living inside
7 * the e2fsprogs. This is a simple straightforward implementation for
8 * reading the label strings of only the most common filesystems.
9 * If you need a full featured library with attribute caching, support for
10 * much more partition/media types or non-root disk access, you may have
12 * http://e2fsprogs.sourceforge.net.
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41 #include <asm/types.h>
43 #include "volume_id.h"
46 #define dbg(format, arg...) \
48 printf("%s: " format "\n", __FUNCTION__ , ## arg); \
51 #define dbg(format, arg...) do {} while (0)
54 #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
55 (((__u32)(x) & 0xff00u) >> 8))
57 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
58 (((__u32)(x) & 0x00ff0000u) >> 8) | \
59 (((__u32)(x) & 0x0000ff00u) << 8) | \
60 (((__u32)(x) & 0x000000ffu) << 24))
62 #define bswap64(x) (__u64)((((__u64)(x) & 0xff00000000000000u) >> 56) | \
63 (((__u64)(x) & 0x00ff000000000000u) >> 40) | \
64 (((__u64)(x) & 0x0000ff0000000000u) >> 24) | \
65 (((__u64)(x) & 0x000000ff00000000u) >> 8) | \
66 (((__u64)(x) & 0x00000000ff000000u) << 8) | \
67 (((__u64)(x) & 0x0000000000ff0000u) << 24) | \
68 (((__u64)(x) & 0x000000000000ff00u) << 40) | \
69 (((__u64)(x) & 0x00000000000000ffu) << 56))
71 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
72 #define le16_to_cpu(x) (x)
73 #define le32_to_cpu(x) (x)
74 #define le64_to_cpu(x) (x)
75 #elif (__BYTE_ORDER == __BIG_ENDIAN)
76 #define le16_to_cpu(x) bswap16(x)
77 #define le32_to_cpu(x) bswap32(x)
78 #define le64_to_cpu(x) bswap64(x)
81 /* size of superblock buffer, reiser block is at 64k */
82 #define SB_BUFFER_SIZE 0x11000
83 /* size of seek buffer 2k */
84 #define SEEK_BUFFER_SIZE 0x800
87 static void set_label_raw(struct volume_id *id,
88 const __u8 *buf, unsigned int count)
90 memcpy(id->label_raw, buf, count);
91 id->label_raw_len = count;
94 static void set_label_string(struct volume_id *id,
95 const __u8 *buf, unsigned int count)
99 memcpy(id->label_string, buf, count);
101 /* remove trailing whitespace */
102 i = strnlen(id->label_string, count);
104 if (! isspace(id->label_string[i]))
107 id->label_string[i+1] = '\0';
112 static void set_label_unicode16(struct volume_id *id,
114 unsigned int endianess,
121 for (i = 0; i <= count-2; i += 2) {
123 c = (buf[i+1] << 8) | buf[i];
125 c = (buf[i] << 8) | buf[i+1];
127 id->label_string[j] = '\0';
129 } else if (c < 0x80) {
130 id->label_string[j++] = (__u8) c;
131 } else if (c < 0x800) {
132 id->label_string[j++] = (__u8) (0xc0 | (c >> 6));
133 id->label_string[j++] = (__u8) (0x80 | (c & 0x3f));
135 id->label_string[j++] = (__u8) (0xe0 | (c >> 12));
136 id->label_string[j++] = (__u8) (0x80 | ((c >> 6) & 0x3f));
137 id->label_string[j++] = (__u8) (0x80 | (c & 0x3f));
142 static void set_uuid(struct volume_id *id,
143 const __u8 *buf, unsigned int count)
147 memcpy(id->uuid, buf, count);
149 /* create string if uuid is set */
150 for (i = 0; i < count; i++)
158 sprintf(id->uuid_string, "%02X%02X-%02X%02X",
159 buf[3], buf[2], buf[1], buf[0]);
162 sprintf(id->uuid_string,
163 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
164 "%02x%02x%02x%02x%02x%02x",
165 buf[0], buf[1], buf[2], buf[3],
169 buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
174 static __u8 *get_buffer(struct volume_id *id,
175 unsigned long off, unsigned int len)
177 unsigned int buf_len;
179 /* check if requested area fits in superblock buffer */
180 if (off + len <= SB_BUFFER_SIZE) {
181 if (id->sbbuf == NULL) {
182 id->sbbuf = malloc(SB_BUFFER_SIZE);
183 if (id->sbbuf == NULL)
187 /* check if we need to read */
188 if ((off + len) > id->sbbuf_len) {
189 dbg("read sbbuf len:0x%lx", off + len);
190 lseek64(id->fd, 0, SEEK_SET);
191 buf_len = read(id->fd, id->sbbuf, off + len);
192 id->sbbuf_len = buf_len;
193 if (buf_len < off + len)
197 return &(id->sbbuf[off]);
199 if (len > SEEK_BUFFER_SIZE)
200 len = SEEK_BUFFER_SIZE;
202 /* get seek buffer */
203 if (id->seekbuf == NULL) {
204 id->seekbuf = malloc(SEEK_BUFFER_SIZE);
205 if (id->seekbuf == NULL)
209 /* check if we need to read */
210 if ((off < id->seekbuf_off) ||
211 ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
212 dbg("read seekbuf off:0x%lx len:0x%x", off, len);
213 lseek64(id->fd, off, SEEK_SET);
214 buf_len = read(id->fd, id->seekbuf, len);
215 dbg("got 0x%x (%i) bytes", buf_len, buf_len);
216 id->seekbuf_off = off;
217 id->seekbuf_len = buf_len;
222 return &(id->seekbuf[off - id->seekbuf_off]);
226 static void free_buffer(struct volume_id *id)
228 if (id->sbbuf != NULL) {
233 if (id->seekbuf != NULL) {
240 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
241 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
242 #define EXT_SUPERBLOCK_OFFSET 0x400
243 static int probe_ext(struct volume_id *id)
245 struct ext2_super_block {
248 __u32 r_blocks_count;
249 __u32 free_blocks_count;
250 __u32 free_inodes_count;
251 __u32 first_data_block;
252 __u32 log_block_size;
257 __u32 feature_compat;
258 __u32 feature_incompat;
259 __u32 feature_ro_compat;
261 __u8 volume_name[16];
262 } __attribute__((__packed__)) *es;
264 es = (struct ext2_super_block *)
265 get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
269 if (es->magic[0] != 0123 ||
270 es->magic[1] != 0357)
273 set_label_raw(id, es->volume_name, 16);
274 set_label_string(id, es->volume_name, 16);
275 set_uuid(id, es->uuid, 16);
277 if ((le32_to_cpu(es->feature_compat) &
278 EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
280 id->fs_name = "ext3";
283 id->fs_name = "ext2";
289 #define REISER1_SUPERBLOCK_OFFSET 0x2000
290 #define REISER_SUPERBLOCK_OFFSET 0x10000
291 static int probe_reiser(struct volume_id *id)
293 struct reiser_super_block {
299 __u32 orig_journal_size;
307 } __attribute__((__packed__)) *rs;
309 rs = (struct reiser_super_block *)
310 get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
314 if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
316 if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
319 rs = (struct reiser_super_block *)
320 get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
324 if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
330 set_label_raw(id, rs->label, 16);
331 set_label_string(id, rs->label, 16);
332 set_uuid(id, rs->uuid, 16);
334 id->fs_type = REISER;
335 id->fs_name = "reiser";
340 static int probe_xfs(struct volume_id *id)
342 struct xfs_super_block {
355 } __attribute__((__packed__)) *xs;
357 xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
361 if (strncmp(xs->magic, "XFSB", 4) != 0)
364 set_label_raw(id, xs->fname, 12);
365 set_label_string(id, xs->fname, 12);
366 set_uuid(id, xs->uuid, 16);
374 #define JFS_SUPERBLOCK_OFFSET 0x8000
375 static int probe_jfs(struct volume_id *id)
377 struct jfs_super_block {
388 } __attribute__((__packed__)) *js;
390 js = (struct jfs_super_block *)
391 get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
395 if (strncmp(js->magic, "JFS1", 4) != 0)
398 set_label_raw(id, js->label, 16);
399 set_label_string(id, js->label, 16);
400 set_uuid(id, js->uuid, 16);
408 static int probe_vfat(struct volume_id *id)
410 struct vfat_super_block {
438 } __attribute__((__packed__)) *vs;
440 vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
444 if (strncmp(vs->magic, "MSWIN", 5) == 0)
446 if (strncmp(vs->magic, "FAT32 ", 8) == 0)
451 set_label_raw(id, vs->label, 11);
452 set_label_string(id, vs->label, 11);
453 set_uuid(id, vs->serno, 4);
456 id->fs_name = "vfat";
461 static int probe_msdos(struct volume_id *id)
463 struct msdos_super_block {
484 } __attribute__((__packed__)) *ms;
486 ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
490 if (strncmp(ms->magic, "MSDOS", 5) == 0)
492 if (strncmp(ms->magic, "FAT16 ", 8) == 0)
494 if (strncmp(ms->magic, "FAT12 ", 8) == 0)
499 set_label_raw(id, ms->label, 11);
500 set_label_string(id, ms->label, 11);
501 set_uuid(id, ms->serno, 4);
504 id->fs_name = "msdos";
509 #define UDF_VSD_OFFSET 0x8000
510 static int probe_udf(struct volume_id *id)
512 struct volume_descriptor {
513 struct descriptor_tag {
522 } __attribute__((__packed__)) tag;
524 struct anchor_descriptor {
527 } __attribute__((__packed__)) anchor;
528 struct primary_descriptor {
534 } __attribute__((__packed__)) ident;
535 } __attribute__((__packed__)) primary;
536 } __attribute__((__packed__)) type;
537 } __attribute__((__packed__)) *vd;
539 struct volume_structure_descriptor {
552 vsd = (struct volume_structure_descriptor *)
553 get_buffer(id, UDF_VSD_OFFSET, 0x200);
557 if (strncmp(vsd->id, "NSR02", 5) == 0)
559 if (strncmp(vsd->id, "NSR03", 5) == 0)
561 if (strncmp(vsd->id, "BEA01", 5) == 0)
563 if (strncmp(vsd->id, "BOOT2", 5) == 0)
565 if (strncmp(vsd->id, "CD001", 5) == 0)
567 if (strncmp(vsd->id, "CDW02", 5) == 0)
569 if (strncmp(vsd->id, "TEA03", 5) == 0)
574 /* search the next VSD to get the logical block size of the volume */
575 for (bs = 0x800; bs < 0x8000; bs += 0x800) {
576 vsd = (struct volume_structure_descriptor *)
577 get_buffer(id, UDF_VSD_OFFSET + bs, 0x800);
580 dbg("test for blocksize: 0x%x", bs);
581 if (vsd->id[0] != '\0')
587 /* search the list of VSDs for a NSR descriptor */
588 for (b = 0; b < 64; b++) {
589 vsd = (struct volume_structure_descriptor *)
590 get_buffer(id, UDF_VSD_OFFSET + (b * bs), 0x800);
594 dbg("vsd: %c%c%c%c%c",
595 vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
597 if (vsd->id[0] == '\0')
599 if (strncmp(vsd->id, "NSR02", 5) == 0)
601 if (strncmp(vsd->id, "NSR03", 5) == 0)
607 /* read anchor volume descriptor */
608 vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
612 type = le16_to_cpu(vd->tag.id);
613 if (type != 2) /* TAG_ID_AVDP */
616 /* get desriptor list address and block count */
617 count = le32_to_cpu(vd->type.anchor.length) / bs;
618 loc = le32_to_cpu(vd->type.anchor.location);
619 dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
621 /* pick the primary descriptor from the list */
622 for (b = 0; b < count; b++) {
623 vd = (struct volume_descriptor *)
624 get_buffer(id, (loc + b) * bs, 0x200);
628 type = le16_to_cpu(vd->tag.id);
629 dbg("descriptor type %i", type);
634 if (le32_to_cpu(vd->tag.location) != loc + b)
637 if (type == 1) /* TAG_ID_PVD */
643 set_label_raw(id, &(vd->type.primary.ident.clen), 32);
645 clen = vd->type.primary.ident.clen;
646 dbg("label string charsize=%i bit", clen);
648 set_label_string(id, vd->type.primary.ident.c, 31);
650 set_label_unicode16(id, vd->type.primary.ident.c, BE,31);
659 #define ISO_SUPERBLOCK_OFFSET 0x8000
660 static int probe_iso9660(struct volume_id *id)
662 union iso_super_block {
670 } __attribute__((__packed__)) iso;
676 } __attribute__((__packed__)) hs;
677 } __attribute__((__packed__)) *is;
679 is = (union iso_super_block *)
680 get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
684 if (strncmp(is->iso.id, "CD001", 5) == 0) {
685 set_label_raw(id, is->iso.volume_id, 32);
686 set_label_string(id, is->iso.volume_id, 32);
689 if (strncmp(is->hs.id, "CDROM", 5) == 0)
694 id->fs_type = ISO9660;
695 id->fs_name = "iso9660";
700 #define MFT_RECORD_VOLUME 3
701 #define MFT_RECORD_ATTR_VOLUME_NAME 0x60u
702 #define MFT_RECORD_ATTR_OBJECT_ID 0x40u
703 #define MFT_RECORD_ATTR_END 0xffffffffu
704 static int probe_ntfs(struct volume_id *id)
706 struct ntfs_super_block {
709 struct bios_param_block {
710 __u16 bytes_per_sector;
711 __u8 sectors_per_cluster;
712 __u16 reserved_sectors;
716 __u8 media_type; /* 0xf8 = hard disk */
717 __u16 sectors_per_fat;
718 __u16 sectors_per_track;
720 __u32 hidden_sectors;
722 } __attribute__((__packed__)) bpb;
724 __u64 number_of_sectors;
725 __u64 mft_cluster_location;
726 __u64 mft_mirror_cluster_location;
727 __s8 cluster_per_mft_record;
728 } __attribute__((__packed__)) *ns;
730 struct master_file_table_record {
735 __u16 sequence_number;
740 __u32 bytes_allocated;
741 } __attribute__((__packed__)) *mftr;
743 struct file_attribute {
753 } __attribute__((__packed__)) *attr;
755 unsigned int sector_size;
756 unsigned int cluster_size;
757 unsigned long mft_cluster;
758 unsigned long mft_off;
759 unsigned int mft_record_size;
760 unsigned int attr_type;
761 unsigned int attr_off;
762 unsigned int attr_len;
763 unsigned int val_off;
764 unsigned int val_len;
768 ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
772 if (strncmp(ns->oem_id, "NTFS", 4) != 0)
775 sector_size = le16_to_cpu(ns->bpb.bytes_per_sector);
776 cluster_size = ns->bpb.sectors_per_cluster * sector_size;
777 mft_cluster = le64_to_cpu(ns->mft_cluster_location);
778 mft_off = mft_cluster * cluster_size;
780 if (ns->cluster_per_mft_record < 0)
781 /* size = -log2(mft_record_size); normally 1024 Bytes */
782 mft_record_size = 1 << -ns->cluster_per_mft_record;
784 mft_record_size = ns->cluster_per_mft_record * cluster_size;
786 dbg("sectorsize 0x%x", sector_size);
787 dbg("clustersize 0x%x", cluster_size);
788 dbg("mftcluster %li", mft_cluster);
789 dbg("mftoffset 0x%lx", mft_off);
790 dbg("cluster per mft_record %i", ns->cluster_per_mft_record);
791 dbg("mft record size %i", mft_record_size);
793 buf = get_buffer(id, mft_off + (MFT_RECORD_VOLUME * mft_record_size),
798 mftr = (struct master_file_table_record*) buf;
800 dbg("mftr->magic[0] = '%c' %03d, 0x%02x", mftr->magic[0], mftr->magic[0], mftr->magic[0]);
801 dbg("mftr->magic[1] = '%c' %03d, 0x%02x", mftr->magic[1], mftr->magic[1], mftr->magic[1]);
802 dbg("mftr->magic[2] = '%c' %03d, 0x%02x", mftr->magic[2], mftr->magic[2], mftr->magic[2]);
803 dbg("mftr->magic[3] = '%c' %03d, 0x%02x", mftr->magic[3], mftr->magic[3], mftr->magic[3]);
804 if (strncmp(mftr->magic, "FILE", 4) != 0)
807 attr_off = le16_to_cpu(mftr->attrs_offset);
808 dbg("file $Volume's attributes are at offset %i", attr_off);
811 attr = (struct file_attribute*) &buf[attr_off];
812 attr_type = le32_to_cpu(attr->type);
813 attr_len = le16_to_cpu(attr->len);
814 val_off = le16_to_cpu(attr->value_offset);
815 val_len = le32_to_cpu(attr->value_len);
817 if (attr_type == MFT_RECORD_ATTR_END)
820 dbg("found attribute type 0x%x, len %i, at offset %i",
821 attr_type, attr_len, attr_off);
823 if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
824 dbg("found label, len %i", val_len);
825 if (val_len > VOLUME_ID_LABEL_SIZE)
826 val_len = VOLUME_ID_LABEL_SIZE;
828 val = &((__u8 *) attr)[val_off];
829 set_label_raw(id, val, val_len);
830 set_label_unicode16(id, val, LE, val_len);
833 if (attr_type == MFT_RECORD_ATTR_OBJECT_ID) {
835 val = &((__u8 *) attr)[val_off];
836 set_uuid(id, val, 16);
841 attr_off += attr_len;
842 if (attr_off >= mft_record_size)
848 id->fs_name = "ntfs";
853 #define LARGEST_PAGESIZE 0x4000
854 static int probe_swap(struct volume_id *id)
859 /* huhh, the swap signature is on the end of the PAGE_SIZE */
860 for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
861 sig = get_buffer(id, page-10, 10);
865 if (strncmp(sig, "SWAP-SPACE", 10) == 0)
867 if (strncmp(sig, "SWAPSPACE2", 10) == 0)
874 id->fs_name = "swap";
879 /* probe volume for filesystem type and try to read label+uuid */
880 int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
893 rc = probe_reiser(id);
902 rc = probe_msdos(id);
911 rc = probe_iso9660(id);
921 /* fill buffer with maximum */
922 get_buffer(id, 0, SB_BUFFER_SIZE);
926 rc = probe_reiser(id);
935 rc = probe_msdos(id);
944 rc = probe_iso9660(id);
956 /* If the filestystem in recognized, we free the allocated buffers,
957 otherwise they will stay in place for the possible next probe call */
964 /* open volume by already open file descriptor */
965 struct volume_id *volume_id_open_fd(int fd)
967 struct volume_id *id;
969 id = malloc(sizeof(struct volume_id));
972 memset(id, 0x00, sizeof(struct volume_id));
979 /* open volume by device node */
980 struct volume_id *volume_id_open_node(const char *path)
982 struct volume_id *id;
985 fd = open(path, O_RDONLY);
989 id = volume_id_open_fd(fd);
993 /* close fd on device close */
999 /* open volume by major/minor */
1000 struct volume_id *volume_id_open_dev_t(dev_t devt)
1002 struct volume_id *id;
1003 __u8 tmp_node[VOLUME_ID_PATH_MAX];
1005 snprintf(tmp_node, VOLUME_ID_PATH_MAX,
1006 "/tmp/volume-%u-%u-%u", getpid(), major(devt), minor(devt));
1007 tmp_node[VOLUME_ID_PATH_MAX] = '\0';
1009 /* create tempory node to open the block device */
1011 if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
1014 id = volume_id_open_node(tmp_node);
1021 /* free allocated volume info */
1022 void volume_id_close(struct volume_id *id)
1027 if (id->fd_close != 0)