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 program is free software; you can redistribute it and/or modify it
15 * under the terms of the GNU General Public License as published by the
16 * Free Software Foundation version 2 of the License.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 675 Mass Ave, Cambridge, MA 02139, USA.
37 #include <asm/types.h>
39 #include "volume_id.h"
42 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
43 (((__u32)(x) & 0x00ff0000u) >> 8) | \
44 (((__u32)(x) & 0x0000ff00u) << 8) | \
45 (((__u32)(x) & 0x000000ffu) << 24))
47 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
48 #define cpu_to_le32(x) (x)
49 #elif (__BYTE_ORDER == __BIG_ENDIAN)
50 #define cpu_to_le32(x) bswap32(x)
53 #define VOLUME_ID_BUFFER_SIZE 0x11000 /* reiser offset is 64k */
56 static void set_label(struct volume_id *id, char *buf, int count)
60 memcpy(id->label, buf, count);
62 memcpy(id->label_string, buf, count);
64 /* remove trailing whitespace */
65 i = strlen(id->label_string);
67 if (! isspace(id->label_string[i]))
70 id->label_string[i+1] = '\0';
73 static void set_uuid(struct volume_id *id, unsigned char *buf, int count)
77 memcpy(id->uuid, buf, count);
79 /* create string if uuid is set */
80 for (i = 0; i < count; i++)
88 sprintf(id->uuid_string, "%02X%02X-%02X%02X",
89 buf[3], buf[2], buf[1], buf[0]);
92 sprintf(id->uuid_string,
93 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
94 buf[0], buf[1], buf[2], buf[3],
98 buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
103 static int open_superblock(struct volume_id *id)
105 /* get buffer to read the first block */
106 if (id->buf == NULL) {
107 id->buf = malloc(VOLUME_ID_BUFFER_SIZE);
112 /* try to read the first 64k, but at least the first block */
113 memset(id->buf, 0x00, VOLUME_ID_BUFFER_SIZE);
114 lseek(id->fd, 0, SEEK_SET);
115 if (read(id->fd, id->buf, VOLUME_ID_BUFFER_SIZE) < 0x200)
121 static void close_superblock(struct volume_id *id)
123 if (id->buf != NULL) {
129 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
130 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
131 #define EXT_SUPERBLOCK_OFFSET 0x400
132 static int probe_ext(struct volume_id *id)
134 struct ext2_super_block {
135 __u32 s_inodes_count;
136 __u32 s_blocks_count;
137 __u32 s_r_blocks_count;
138 __u32 s_free_blocks_count;
139 __u32 s_free_inodes_count;
140 __u32 s_first_data_block;
141 __u32 s_log_block_size;
143 unsigned char s_magic[2];
146 __u32 s_feature_compat;
147 __u32 s_feature_incompat;
148 __u32 s_feature_ro_compat;
149 unsigned char s_uuid[16];
150 char s_volume_name[16];
153 es = (struct ext2_super_block *) (id->buf + EXT_SUPERBLOCK_OFFSET);
155 if (es->s_magic[0] != 0123 ||
156 es->s_magic[1] != 0357)
159 set_label(id, es->s_volume_name, 16);
160 set_uuid(id, es->s_uuid, 16);
162 if ((cpu_to_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
164 id->fs_name = "ext3";
167 id->fs_name = "ext2";
173 #define REISER1_SUPERBLOCK_OFFSET 0x2000
174 #define REISER_SUPERBLOCK_OFFSET 0x10000
175 static int probe_reiser(struct volume_id *id)
177 struct reiser_super_block {
178 __u32 rs_blocks_count;
179 __u32 rs_free_blocks;
181 __u32 rs_journal_block;
182 __u32 rs_journal_dev;
183 __u32 rs_orig_journal_size;
187 unsigned char rs_magic[12];
189 unsigned char rs_uuid[16];
193 rs = (struct reiser_super_block *) &(id->buf[REISER1_SUPERBLOCK_OFFSET]);
195 if (strncmp(rs->rs_magic, "ReIsErFs", 8) == 0)
198 rs = (struct reiser_super_block *) &(id->buf[REISER_SUPERBLOCK_OFFSET]);
200 if (strncmp(rs->rs_magic, "ReIsEr2Fs", 9) == 0)
202 if (strncmp(rs->rs_magic, "ReIsEr3Fs", 9) == 0)
208 set_label(id, rs->rs_label, 16);
209 set_uuid(id, rs->rs_uuid, 16);
211 id->fs_type = REISER;
212 id->fs_name = "reiser";
217 static int probe_xfs(struct volume_id *id)
219 struct xfs_super_block {
220 unsigned char xs_magic[4];
225 unsigned char xs_uuid[16];
234 xs = (struct xfs_super_block *) id->buf;
236 if (strncmp(xs->xs_magic, "XFSB", 4) != 0)
239 set_label(id, xs->xs_fname, 12);
240 set_uuid(id, xs->xs_uuid, 16);
248 #define JFS_SUPERBLOCK_OFFSET 0x8000
249 static int probe_jfs(struct volume_id *id)
251 struct jfs_super_block {
252 unsigned char js_magic[4];
259 unsigned char js_uuid[16];
260 unsigned char js_label[16];
261 unsigned char js_loguuid[16];
264 js = (struct jfs_super_block *) &(id->buf[JFS_SUPERBLOCK_OFFSET]);
266 if (strncmp(js->js_magic, "JFS1", 4) != 0)
269 set_label(id, js->js_label, 16);
270 set_uuid(id, js->js_uuid, 16);
278 static int probe_vfat(struct volume_id *id)
280 struct vfat_super_block {
281 unsigned char vs_ignored[3];
282 unsigned char vs_sysid[8];
283 unsigned char vs_sector_size[2];
284 __u8 vs_cluster_size;
287 unsigned char vs_dir_entries[2];
288 unsigned char vs_sectors[2];
289 unsigned char vs_media;
295 __u32 vs_fat32_length;
298 __u32 vs_root_cluster;
299 __u16 vs_insfo_sector;
300 __u16 vs_backup_boot;
301 __u16 vs_reserved2[6];
302 unsigned char vs_unknown[3];
303 unsigned char vs_serno[4];
305 unsigned char vs_magic[8];
306 unsigned char vs_dummy2[164];
307 unsigned char vs_pmagic[2];
310 vs = (struct vfat_super_block *) id->buf;
312 if (strncmp(vs->vs_magic, "MSWIN", 5) == 0)
314 if (strncmp(vs->vs_magic, "FAT32 ", 8) == 0)
319 memcpy(id->label, vs->vs_label, 11);
320 memcpy(id->uuid, vs->vs_serno, 4);
323 id->fs_name = "vfat";
328 static int probe_msdos(struct volume_id *id)
330 struct msdos_super_block {
331 unsigned char ms_ignored[3];
332 unsigned char ms_sysid[8];
333 unsigned char ms_sector_size[2];
334 __u8 ms_cluster_size;
337 unsigned char ms_dir_entries[2];
338 unsigned char ms_sectors[2];
339 unsigned char ms_media;
345 unsigned char ms_unknown[3];
346 unsigned char ms_serno[4];
348 unsigned char ms_magic[8];
349 unsigned char ms_dummy2[192];
350 unsigned char ms_pmagic[2];
353 ms = (struct msdos_super_block *) id->buf;
355 if (strncmp(ms->ms_magic, "MSDOS", 5) == 0)
357 if (strncmp(ms->ms_magic, "FAT16 ", 8) == 0)
359 if (strncmp(ms->ms_magic, "FAT12 ", 8) == 0)
364 set_label(id, ms->ms_label, 11);
365 set_uuid(id, ms->ms_serno, 4);
368 id->fs_name = "msdos";
373 static int probe_ntfs(struct volume_id *id)
375 struct ntfs_super_block {
380 ns = (struct ntfs_super_block *) id->buf;
382 if (strncmp(ns->oem_id, "NTFS", 4) != 0)
386 id->fs_name = "ntfs";
391 static int probe_swap(struct volume_id *id)
395 /* huhh, the swap signature is on the end of the PAGE_SIZE */
396 for (magic = 0x1000; magic <= 0x4000; magic <<= 1) {
397 if (strncmp(&(id->buf[magic -10]), "SWAP-SPACE", 10) == 0)
399 if (strncmp(&(id->buf[magic -10]), "SWAPSPACE2", 10) == 0)
406 id->fs_name = "swap";
411 /* probe volume for filesystem type and try to read label+uuid */
412 int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
419 if (open_superblock(id) != 0)
428 rc = probe_reiser(id);
437 rc = probe_msdos(id);
452 rc = probe_reiser(id);
461 rc = probe_msdos(id);
477 close_superblock(id);
482 /* open volume by already open file descriptor */
483 struct volume_id *volume_id_open_fd(int fd)
485 struct volume_id *id;
487 id = malloc(sizeof(struct volume_id));
490 memset(id, 0x00, sizeof(struct volume_id));
497 /* open volume by device node */
498 struct volume_id *volume_id_open_node(const char *path)
500 struct volume_id *id;
503 fd = open(path, O_RDONLY);
507 id = volume_id_open_fd(fd);
511 /* close fd on device close */
517 /* open volume by major/minor */
518 struct volume_id *volume_id_open_dev_t(dev_t devt)
520 struct volume_id *id;
521 char tmp_node[VOLUME_ID_PATH_MAX];
523 snprintf(tmp_node, VOLUME_ID_PATH_MAX,
524 "/tmp/volume-%u-%u", major(devt), minor(devt));
525 tmp_node[VOLUME_ID_PATH_MAX] = '\0';
527 /* create tempory node to open the block device */
528 if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
531 id = volume_id_open_node(tmp_node);
538 /* free allocated volume info */
539 void volume_id_close(struct volume_id *id)
544 if (id->fd_close != 0)
547 close_superblock(id);