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 data 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
37 #include <asm/types.h>
39 #include "volume_id.h"
42 #define dbg(format, arg...) \
44 printf("%s: " format "\n", __FUNCTION__ , ## arg); \
47 #define dbg(format, arg...) do {} while (0)
50 #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
51 (((__u32)(x) & 0xff00u) >> 8))
53 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
54 (((__u32)(x) & 0x00ff0000u) >> 8) | \
55 (((__u32)(x) & 0x0000ff00u) << 8) | \
56 (((__u32)(x) & 0x000000ffu) << 24))
58 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
59 #define le16_to_cpu(x) (x)
60 #define le32_to_cpu(x) (x)
61 #elif (__BYTE_ORDER == __BIG_ENDIAN)
62 #define le16_to_cpu(x) bswap16(x)
63 #define le32_to_cpu(x) bswap32(x)
66 /* size of superblock buffer, reiser block is at 64k */
67 #define SB_BUFFER_SIZE 0x11000
68 /* size of seek buffer 2k */
69 #define SEEK_BUFFER_SIZE 0x800
72 static void set_label_raw(struct volume_id *id, char *buf, int count)
74 memcpy(id->label_raw, buf, count);
75 id->label_raw_len = count;
78 static void set_label_string(struct volume_id *id, char *buf, int count)
82 memcpy(id->label_string, buf, count);
84 /* remove trailing whitespace */
85 i = strlen(id->label_string);
87 if (! isspace(id->label_string[i]))
90 id->label_string[i+1] = '\0';
93 static void set_uuid(struct volume_id *id, unsigned char *buf, int count)
97 memcpy(id->uuid, buf, count);
99 /* create string if uuid is set */
100 for (i = 0; i < count; i++)
108 sprintf(id->uuid_string, "%02X%02X-%02X%02X",
109 buf[3], buf[2], buf[1], buf[0]);
112 sprintf(id->uuid_string,
113 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
114 "%02x%02x%02x%02x%02x%02x",
115 buf[0], buf[1], buf[2], buf[3],
119 buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
124 static char *get_buffer(struct volume_id *id, size_t off, size_t len)
128 /* check if requested area fits in superblock buffer */
129 if (off + len <= SB_BUFFER_SIZE) {
130 if (id->sbbuf == NULL) {
131 id->sbbuf = malloc(SB_BUFFER_SIZE);
132 if (id->sbbuf == NULL)
136 /* check if we need to read */
137 if ((off + len) > id->sbbuf_len) {
138 dbg("read sbbuf len:0x%x", off + len);
139 lseek(id->fd, 0, SEEK_SET);
140 buf_len = read(id->fd, id->sbbuf, off + len);
141 id->sbbuf_len = buf_len;
142 if (buf_len < off + len)
146 return &(id->sbbuf[off]);
148 if (len > SEEK_BUFFER_SIZE)
149 len = SEEK_BUFFER_SIZE;
151 /* get seek buffer */
152 if (id->seekbuf == NULL) {
153 id->seekbuf = malloc(SEEK_BUFFER_SIZE);
154 if (id->seekbuf == NULL)
158 /* check if we need to read */
159 if ((off < id->seekbuf_off) ||
160 ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
161 dbg("read seekbuf off:0x%x len:0x%x", off, len);
162 lseek(id->fd, off, SEEK_SET);
163 buf_len = read(id->fd, id->seekbuf, len);
164 id->seekbuf_off = off;
165 id->seekbuf_len = buf_len;
170 return &(id->seekbuf[off - id->seekbuf_off]);
174 static void free_buffer(struct volume_id *id)
176 if (id->sbbuf != NULL) {
181 if (id->seekbuf != NULL) {
188 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
189 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
190 #define EXT_SUPERBLOCK_OFFSET 0x400
191 static int probe_ext(struct volume_id *id)
193 struct ext2_super_block {
196 __u32 r_blocks_count;
197 __u32 free_blocks_count;
198 __u32 free_inodes_count;
199 __u32 first_data_block;
200 __u32 log_block_size;
202 unsigned char magic[2];
205 __u32 feature_compat;
206 __u32 feature_incompat;
207 __u32 feature_ro_compat;
208 unsigned char uuid[16];
209 char volume_name[16];
212 es = (struct ext2_super_block *)
213 get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
217 if (es->magic[0] != 0123 ||
218 es->magic[1] != 0357)
221 set_label_raw(id, es->volume_name, 16);
222 set_label_string(id, es->volume_name, 16);
223 set_uuid(id, es->uuid, 16);
225 if ((le32_to_cpu(es->feature_compat) &
226 EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
228 id->fs_name = "ext3";
231 id->fs_name = "ext2";
237 #define REISER1_SUPERBLOCK_OFFSET 0x2000
238 #define REISER_SUPERBLOCK_OFFSET 0x10000
239 static int probe_reiser(struct volume_id *id)
241 struct reiser_super_block {
247 __u32 orig_journal_size;
251 unsigned char magic[12];
253 unsigned char uuid[16];
257 rs = (struct reiser_super_block *)
258 get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
262 if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
264 if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
267 rs = (struct reiser_super_block *)
268 get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
272 if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
278 set_label_raw(id, rs->label, 16);
279 set_label_string(id, rs->label, 16);
280 set_uuid(id, rs->uuid, 16);
282 id->fs_type = REISER;
283 id->fs_name = "reiser";
288 static int probe_xfs(struct volume_id *id)
290 struct xfs_super_block {
291 unsigned char magic[4];
296 unsigned char uuid[16];
305 xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
309 if (strncmp(xs->magic, "XFSB", 4) != 0)
312 set_label_raw(id, xs->fname, 12);
313 set_label_string(id, xs->fname, 12);
314 set_uuid(id, xs->uuid, 16);
322 #define JFS_SUPERBLOCK_OFFSET 0x8000
323 static int probe_jfs(struct volume_id *id)
325 struct jfs_super_block {
326 unsigned char magic[4];
333 unsigned char uuid[16];
334 unsigned char label[16];
335 unsigned char loguuid[16];
338 js = (struct jfs_super_block *)
339 get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
343 if (strncmp(js->magic, "JFS1", 4) != 0)
346 set_label_raw(id, js->label, 16);
347 set_label_string(id, js->label, 16);
348 set_uuid(id, js->uuid, 16);
356 static int probe_vfat(struct volume_id *id)
358 struct vfat_super_block {
359 unsigned char ignored[3];
360 unsigned char sysid[8];
361 unsigned char sector_size[2];
365 unsigned char dir_entries[2];
366 unsigned char sectors[2];
380 unsigned char unknown[3];
381 unsigned char serno[4];
383 unsigned char magic[8];
384 unsigned char dummy2[164];
385 unsigned char pmagic[2];
388 vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
392 if (strncmp(vs->magic, "MSWIN", 5) == 0)
394 if (strncmp(vs->magic, "FAT32 ", 8) == 0)
399 set_label_raw(id, vs->label, 11);
400 set_label_string(id, vs->label, 11);
401 set_uuid(id, vs->serno, 4);
404 id->fs_name = "vfat";
409 static int probe_msdos(struct volume_id *id)
411 struct msdos_super_block {
412 unsigned char ignored[3];
413 unsigned char sysid[8];
414 unsigned char sector_size[2];
418 unsigned char dir_entries[2];
419 unsigned char sectors[2];
426 unsigned char unknown[3];
427 unsigned char serno[4];
429 unsigned char magic[8];
430 unsigned char dummy2[192];
431 unsigned char pmagic[2];
434 ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
438 if (strncmp(ms->magic, "MSDOS", 5) == 0)
440 if (strncmp(ms->magic, "FAT16 ", 8) == 0)
442 if (strncmp(ms->magic, "FAT12 ", 8) == 0)
447 set_label_raw(id, ms->label, 11);
448 set_label_string(id, ms->label, 11);
449 set_uuid(id, ms->serno, 4);
452 id->fs_name = "msdos";
457 #define UDF_VSD_OFFSET 0x8000
458 static int probe_udf(struct volume_id *id)
460 struct volume_descriptor {
461 struct descriptor_tag {
464 unsigned char checksum;
465 unsigned char reserved;
472 struct anchor_descriptor {
476 struct primary_descriptor {
487 struct volume_structure_descriptor {
490 unsigned char version;
502 vsd = (struct volume_structure_descriptor *)
503 get_buffer(id, UDF_VSD_OFFSET, 0x200);
507 if (strncmp(vsd->id, "NSR02", 5) == 0)
509 if (strncmp(vsd->id, "NSR03", 5) == 0)
511 if (strncmp(vsd->id, "BEA01", 5) == 0)
513 if (strncmp(vsd->id, "BOOT2", 5) == 0)
515 if (strncmp(vsd->id, "CD001", 5) == 0)
517 if (strncmp(vsd->id, "CDW02", 5) == 0)
519 if (strncmp(vsd->id, "TEA03", 5) == 0)
524 /* search the next VSD to get the logical block size of the volume */
525 for (bs = 0x800; bs < 0x8000; bs += 0x800) {
526 vsd = (struct volume_structure_descriptor *)
527 get_buffer(id, UDF_VSD_OFFSET + bs, 0x800);
530 dbg("test for blocksize: 0x%x", bs);
531 if (vsd->id[0] != '\0')
537 /* search the list of VSDs for a NSR descriptor */
538 for (b = 0; b < 64; b++) {
539 vsd = (struct volume_structure_descriptor *)
540 get_buffer(id, UDF_VSD_OFFSET + (b * bs), 0x800);
544 dbg("vsd: %c%c%c%c%c",
545 vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
547 if (vsd->id[0] == '\0')
549 if (strncmp(vsd->id, "NSR02", 5) == 0)
551 if (strncmp(vsd->id, "NSR03", 5) == 0)
557 /* read anchor volume descriptor */
558 vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
562 type = le16_to_cpu(vd->tag.id);
563 if (type != 2) /* TAG_ID_AVDP */
566 /* get desriptor list address and block count */
567 count = le32_to_cpu(vd->type.anchor.length) / bs;
568 loc = le32_to_cpu(vd->type.anchor.location);
569 dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
571 /* pick the primary descriptor from the list */
572 for (b = 0; b < count; b++) {
573 vd = (struct volume_descriptor *)
574 get_buffer(id, (loc + b) * bs, 0x200);
578 type = le16_to_cpu(vd->tag.id);
579 dbg("descriptor type %i", type);
584 if (le32_to_cpu(vd->tag.location) != loc + b)
587 if (type == 1) /* TAG_ID_PVD */
593 set_label_raw(id, &(vd->type.primary.ident.clen), 32);
595 clen = vd->type.primary.ident.clen;
596 dbg("label string charsize=%i bit", clen);
598 set_label_string(id, vd->type.primary.ident.c, 31);
599 } else if (clen == 16) {
600 /* convert unicode OSTA dstring to UTF-8 */
602 for (i = 0; i < 32; i += 2) {
603 c = (vd->type.primary.ident.c[i] << 8) |
604 vd->type.primary.ident.c[i+1];
606 id->label_string[j] = '\0';
608 }else if (c < 0x80U) {
609 id->label_string[j++] = (char) c;
610 } else if (c < 0x800U) {
611 id->label_string[j++] = (char) (0xc0 | (c >> 6));
612 id->label_string[j++] = (char) (0x80 | (c & 0x3f));
614 id->label_string[j++] = (char) (0xe0 | (c >> 12));
615 id->label_string[j++] = (char) (0x80 | ((c >> 6) & 0x3f));
616 id->label_string[j++] = (char) (0x80 | (c & 0x3f));
628 #define ISO_SUPERBLOCK_OFFSET 0x8000
629 static int probe_iso9660(struct volume_id *id)
631 union iso_super_block {
635 unsigned char version;
636 unsigned char unused1;
644 unsigned char version;
648 is = (union iso_super_block *)
649 get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
653 if (strncmp(is->iso.id, "CD001", 5) == 0) {
654 set_label_raw(id, is->iso.volume_id, 32);
655 set_label_string(id, is->iso.volume_id, 32);
658 if (strncmp(is->hs.id, "CDROM", 5) == 0)
663 id->fs_type = ISO9660;
664 id->fs_name = "iso9660";
669 static int probe_ntfs(struct volume_id *id)
671 struct ntfs_super_block {
676 ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
680 if (strncmp(ns->oem_id, "NTFS", 4) != 0)
684 id->fs_name = "ntfs";
689 #define LARGEST_PAGESIZE 0x4000
690 static int probe_swap(struct volume_id *id)
695 /* huhh, the swap signature is on the end of the PAGE_SIZE */
696 for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
697 sig = get_buffer(id, page-10, 10);
701 if (strncmp(sig, "SWAP-SPACE", 10) == 0)
703 if (strncmp(sig, "SWAPSPACE2", 10) == 0)
710 id->fs_name = "swap";
715 /* probe volume for filesystem type and try to read label+uuid */
716 int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
729 rc = probe_reiser(id);
738 rc = probe_msdos(id);
747 rc = probe_iso9660(id);
757 /* fill buffer with maximum */
758 get_buffer(id, 0, SB_BUFFER_SIZE);
762 rc = probe_reiser(id);
771 rc = probe_msdos(id);
780 rc = probe_iso9660(id);
792 /* If the filestystem in recognized, we free the allocated buffers,
793 otherwise they will stay in place for the possible next probe call */
800 /* open volume by already open file descriptor */
801 struct volume_id *volume_id_open_fd(int fd)
803 struct volume_id *id;
805 id = malloc(sizeof(struct volume_id));
808 memset(id, 0x00, sizeof(struct volume_id));
815 /* open volume by device node */
816 struct volume_id *volume_id_open_node(const char *path)
818 struct volume_id *id;
821 fd = open(path, O_RDONLY);
825 id = volume_id_open_fd(fd);
829 /* close fd on device close */
835 /* open volume by major/minor */
836 struct volume_id *volume_id_open_dev_t(dev_t devt)
838 struct volume_id *id;
839 char tmp_node[VOLUME_ID_PATH_MAX];
841 snprintf(tmp_node, VOLUME_ID_PATH_MAX,
842 "/tmp/volume-%u-%u", major(devt), minor(devt));
843 tmp_node[VOLUME_ID_PATH_MAX] = '\0';
845 /* create tempory node to open the block device */
846 if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
849 id = volume_id_open_node(tmp_node);
856 /* free allocated volume info */
857 void volume_id_close(struct volume_id *id)
862 if (id->fd_close != 0)