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 linux kernel sources
7 * and the libblkid living inside the e2fsprogs. This is a simple
8 * straightforward implementation for reading the label strings of the
9 * most common filesystems.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
42 #include <asm/types.h>
44 #include "volume_id.h"
47 #define dbg(format, arg...) \
49 printf("%s: " format "\n", __FUNCTION__ , ## arg); \
52 #define dbg(format, arg...) do {} while (0)
55 #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
56 (((__u32)(x) & 0xff00u) >> 8))
58 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
59 (((__u32)(x) & 0x00ff0000u) >> 8) | \
60 (((__u32)(x) & 0x0000ff00u) << 8) | \
61 (((__u32)(x) & 0x000000ffu) << 24))
63 #define bswap64(x) (__u64)((((__u64)(x) & 0xff00000000000000u) >> 56) | \
64 (((__u64)(x) & 0x00ff000000000000u) >> 40) | \
65 (((__u64)(x) & 0x0000ff0000000000u) >> 24) | \
66 (((__u64)(x) & 0x000000ff00000000u) >> 8) | \
67 (((__u64)(x) & 0x00000000ff000000u) << 8) | \
68 (((__u64)(x) & 0x0000000000ff0000u) << 24) | \
69 (((__u64)(x) & 0x000000000000ff00u) << 40) | \
70 (((__u64)(x) & 0x00000000000000ffu) << 56))
72 #if (__BYTE_ORDER == __LITTLE_ENDIAN)
73 #define le16_to_cpu(x) (x)
74 #define le32_to_cpu(x) (x)
75 #define le64_to_cpu(x) (x)
76 #define be16_to_cpu(x) bswap16(x)
77 #define be32_to_cpu(x) bswap32(x)
78 #elif (__BYTE_ORDER == __BIG_ENDIAN)
79 #define le16_to_cpu(x) bswap16(x)
80 #define le32_to_cpu(x) bswap32(x)
81 #define le64_to_cpu(x) bswap64(x)
82 #define be16_to_cpu(x) (x)
83 #define be32_to_cpu(x) (x)
86 /* size of superblock buffer, reiserfs block is at 64k */
87 #define SB_BUFFER_SIZE 0x11000
88 /* size of seek buffer 4k */
89 #define SEEK_BUFFER_SIZE 0x1000
92 static void set_label_raw(struct volume_id *id,
93 const __u8 *buf, unsigned int count)
95 memcpy(id->label_raw, buf, count);
96 id->label_raw_len = count;
99 static void set_label_string(struct volume_id *id,
100 const __u8 *buf, unsigned int count)
104 memcpy(id->label, buf, count);
106 /* remove trailing whitespace */
107 i = strnlen(id->label, count);
109 if (! isspace(id->label[i]))
112 id->label[i+1] = '\0';
117 static void set_label_unicode16(struct volume_id *id,
119 unsigned int endianess,
126 for (i = 0; i + 2 <= count; i += 2) {
128 c = (buf[i+1] << 8) | buf[i];
130 c = (buf[i] << 8) | buf[i+1];
134 } else if (c < 0x80) {
135 id->label[j++] = (__u8) c;
136 } else if (c < 0x800) {
137 id->label[j++] = (__u8) (0xc0 | (c >> 6));
138 id->label[j++] = (__u8) (0x80 | (c & 0x3f));
140 id->label[j++] = (__u8) (0xe0 | (c >> 12));
141 id->label[j++] = (__u8) (0x80 | ((c >> 6) & 0x3f));
142 id->label[j++] = (__u8) (0x80 | (c & 0x3f));
147 static void set_uuid(struct volume_id *id,
148 const __u8 *buf, unsigned int count)
152 memcpy(id->uuid_raw, buf, count);
154 /* create string if uuid is set */
155 for (i = 0; i < count; i++)
163 sprintf(id->uuid, "%02X%02X-%02X%02X",
164 buf[3], buf[2], buf[1], buf[0]);
167 sprintf(id->uuid,"%02X%02X-%02X%02X-%02X%02X-%02X%02X",
168 buf[7], buf[6], buf[5], buf[4],
169 buf[3], buf[2], buf[1], buf[0]);
173 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
174 "%02x%02x%02x%02x%02x%02x",
175 buf[0], buf[1], buf[2], buf[3],
179 buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
184 static __u8 *get_buffer(struct volume_id *id, __u64 off, unsigned int len)
186 unsigned int buf_len;
188 dbg("get buffer off 0x%llx, len 0x%x", off, len);
189 /* check if requested area fits in superblock buffer */
190 if (off + len <= SB_BUFFER_SIZE) {
191 if (id->sbbuf == NULL) {
192 id->sbbuf = malloc(SB_BUFFER_SIZE);
193 if (id->sbbuf == NULL)
197 /* check if we need to read */
198 if ((off + len) > id->sbbuf_len) {
199 dbg("read sbbuf len:0x%llx", off + len);
200 lseek(id->fd, 0, SEEK_SET);
201 buf_len = read(id->fd, id->sbbuf, off + len);
202 dbg("got 0x%x (%i) bytes", buf_len, buf_len);
203 id->sbbuf_len = buf_len;
204 if (buf_len < off + len)
208 return &(id->sbbuf[off]);
210 if (len > SEEK_BUFFER_SIZE)
211 len = SEEK_BUFFER_SIZE;
213 /* get seek buffer */
214 if (id->seekbuf == NULL) {
215 id->seekbuf = malloc(SEEK_BUFFER_SIZE);
216 if (id->seekbuf == NULL)
220 /* check if we need to read */
221 if ((off < id->seekbuf_off) ||
222 ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
223 dbg("read seekbuf off:0x%llx len:0x%x", off, len);
224 if (lseek(id->fd, off, SEEK_SET) == -1)
226 buf_len = read(id->fd, id->seekbuf, len);
227 dbg("got 0x%x (%i) bytes", buf_len, buf_len);
228 id->seekbuf_off = off;
229 id->seekbuf_len = buf_len;
234 return &(id->seekbuf[off - id->seekbuf_off]);
238 static void free_buffer(struct volume_id *id)
240 if (id->sbbuf != NULL) {
245 if (id->seekbuf != NULL) {
252 #define LVM1_SB_OFF 0x400
253 #define LVM1_MAGIC "HM"
254 static int probe_lvm1(struct volume_id *id, __u64 off)
256 struct lvm2_super_block {
258 } __attribute__((packed)) *lvm;
262 buf = get_buffer(id, off + LVM1_SB_OFF, 0x800);
266 lvm = (struct lvm2_super_block *) buf;
268 if (strncmp(lvm->id, LVM1_MAGIC, 2) != 0)
271 id->usage_id = VOLUME_ID_RAID;
272 id->type_id = VOLUME_ID_LVM1;
273 id->type = "LVM1_member";
278 #define LVM2_LABEL_ID "LABELONE"
279 #define LVM2LABEL_SCAN_SECTORS 4
280 static int probe_lvm2(struct volume_id *id, __u64 off)
282 struct lvm2_super_block {
288 } __attribute__((packed)) *lvm;
293 buf = get_buffer(id, off, LVM2LABEL_SCAN_SECTORS * 0x200);
298 for (soff = 0; soff < LVM2LABEL_SCAN_SECTORS * 0x200; soff += 0x200) {
299 lvm = (struct lvm2_super_block *) &buf[soff];
301 if (strncmp(lvm->id, LVM2_LABEL_ID, 8) == 0)
308 strncpy(id->type_version, lvm->type, 8);
309 id->usage_id = VOLUME_ID_RAID;
310 id->type_id = VOLUME_ID_LVM1;
311 id->type = "LVM2_member";
316 #define MD_RESERVED_BYTES 0x10000
317 #define MD_MAGIC 0xa92b4efc
318 static int probe_linux_raid(struct volume_id *id, __u64 off, __u64 size)
320 struct mdp_super_block {
333 __u32 not_persistent;
337 } __attribute__((packed)) *mdp;
340 __u64 sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
346 buf = get_buffer(id, off + sboff, 0x800);
350 mdp = (struct mdp_super_block *) buf;
352 if (le32_to_cpu(mdp->md_magic) != MD_MAGIC)
355 memcpy(uuid, &mdp->set_uuid0, 4);
356 memcpy(&uuid[4], &mdp->set_uuid1, 12);
357 set_uuid(id, uuid, 16);
359 snprintf(id->type_version, VOLUME_ID_FORMAT_SIZE-1, "%u.%u.%u",
360 le32_to_cpu(mdp->major_version),
361 le32_to_cpu(mdp->minor_version),
362 le32_to_cpu(mdp->patch_version));
364 dbg("found raid signature");
365 id->usage_id = VOLUME_ID_RAID;
366 id->type = "linux_raid_member";
371 #define MSDOS_MAGIC "\x55\xaa"
372 #define MSDOS_PARTTABLE_OFFSET 0x1be
373 #define MSDOS_SIG_OFF 0x1fe
375 #define DOS_EXTENDED_PARTITION 0x05
376 #define LINUX_EXTENDED_PARTITION 0x85
377 #define WIN98_EXTENDED_PARTITION 0x0f
378 #define LINUX_RAID_PARTITION 0xfd
379 #define is_extended(type) \
380 (type == DOS_EXTENDED_PARTITION || \
381 type == WIN98_EXTENDED_PARTITION || \
382 type == LINUX_EXTENDED_PARTITION)
383 #define is_raid(type) \
384 (type == LINUX_RAID_PARTITION)
385 static int probe_msdos_part_table(struct volume_id *id, __u64 off)
387 struct msdos_partition_entry {
398 } __attribute__((packed)) *part;
409 struct volume_id_partition *p;
411 buf = get_buffer(id, off, 0x200);
415 if (strncmp(&buf[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0)
418 /* check flags on all entries for a valid partition table */
419 part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
420 for (i = 0; i < 4; i++) {
421 if (part[i].boot_ind != 0 &&
422 part[i].boot_ind != 0x80)
425 if (le32_to_cpu(part[i].nr_sects) != 0)
431 if (id->partitions != NULL)
432 free(id->partitions);
433 id->partitions = malloc(VOLUME_ID_PARTITIONS_MAX *
434 sizeof(struct volume_id_partition));
435 if (id->partitions == NULL)
437 memset(id->partitions, 0x00,
438 VOLUME_ID_PARTITIONS_MAX * sizeof(struct volume_id_partition));
440 for (i = 0; i < 4; i++) {
441 poff = (__u64) le32_to_cpu(part[i].start_sect) * BSIZE;
442 plen = (__u64) le32_to_cpu(part[i].nr_sects) * BSIZE;
447 p = &id->partitions[i];
449 if (is_extended(part[i].sys_ind)) {
450 dbg("found extended partition at 0x%llx", poff);
451 p->usage_id = VOLUME_ID_PARTITIONTABLE;
452 p->type_id = VOLUME_ID_MSDOSEXTENDED;
453 p->type = "msdos_extended_partition";
455 extended = off + poff;
457 dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
458 part[i].sys_ind, poff, plen);
460 if (is_raid(part[i].sys_ind))
461 p->usage_id = VOLUME_ID_RAID;
463 p->usage_id = VOLUME_ID_UNPROBED;
468 id->partition_count = i+1;
475 /* follow extended partition chain and add data partitions */
478 dbg("extended chain limit reached");
482 buf = get_buffer(id, current, 0x200);
486 part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
488 if (strncmp(&buf[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0)
493 for (i = 0; i < 4; i++) {
494 poff = (__u64) le32_to_cpu(part[i].start_sect) * BSIZE;
495 plen = (__u64) le32_to_cpu(part[i].nr_sects) * BSIZE;
500 if (is_extended(part[i].sys_ind)) {
501 dbg("found extended partition at 0x%llx", poff);
503 next = extended + poff;
505 dbg("found 0x%x data partition at 0x%llx, len 0x%llx",
506 part[i].sys_ind, poff, plen);
508 /* we always start at the 5th entry */
509 while (id->partition_count < 4)
510 id->partitions[id->partition_count++].usage_id =
513 p = &id->partitions[id->partition_count];
515 if (is_raid(part[i].sys_ind))
516 p->usage_id = VOLUME_ID_RAID;
518 p->usage_id = VOLUME_ID_UNPROBED;
520 p->off = current + poff;
522 id->partition_count++;
523 if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) {
524 dbg("to many partitions");
533 id->usage_id = VOLUME_ID_PARTITIONTABLE;
534 id->type_id = VOLUME_ID_MSDOSPARTTABLE;
535 id->type = "msdos_partition_table";
540 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x00000004
541 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x00000008
542 #define EXT_SUPERBLOCK_OFFSET 0x400
543 static int probe_ext(struct volume_id *id, __u64 off)
545 struct ext2_super_block {
548 __u32 r_blocks_count;
549 __u32 free_blocks_count;
550 __u32 free_inodes_count;
551 __u32 first_data_block;
552 __u32 log_block_size;
557 __u32 feature_compat;
558 __u32 feature_incompat;
559 __u32 feature_ro_compat;
561 __u8 volume_name[16];
562 } __attribute__((__packed__)) *es;
564 es = (struct ext2_super_block *)
565 get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200);
569 if (es->magic[0] != 0123 ||
570 es->magic[1] != 0357)
573 set_label_raw(id, es->volume_name, 16);
574 set_label_string(id, es->volume_name, 16);
575 set_uuid(id, es->uuid, 16);
577 if ((le32_to_cpu(es->feature_compat) &
578 EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
579 id->usage_id = VOLUME_ID_FILESYSTEM;
580 id->type_id = VOLUME_ID_EXT3;
583 id->usage_id = VOLUME_ID_FILESYSTEM;
584 id->type_id = VOLUME_ID_EXT2;
591 #define REISERFS1_SUPERBLOCK_OFFSET 0x2000
592 #define REISERFS_SUPERBLOCK_OFFSET 0x10000
593 static int probe_reiserfs(struct volume_id *id, __u64 off)
595 struct reiserfs_super_block {
601 __u32 orig_journal_size;
609 } __attribute__((__packed__)) *rs;
611 rs = (struct reiserfs_super_block *)
612 get_buffer(id, off + REISERFS_SUPERBLOCK_OFFSET, 0x200);
616 if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0) {
617 strcpy(id->type_version, "3.6");
621 if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0) {
622 strcpy(id->type_version, "JR");
626 rs = (struct reiserfs_super_block *)
627 get_buffer(id, off + REISERFS1_SUPERBLOCK_OFFSET, 0x200);
631 if (strncmp(rs->magic, "ReIsErFs", 8) == 0) {
632 strcpy(id->type_version, "3.5");
639 set_label_raw(id, rs->label, 16);
640 set_label_string(id, rs->label, 16);
641 set_uuid(id, rs->uuid, 16);
643 id->usage_id = VOLUME_ID_FILESYSTEM;
644 id->type_id = VOLUME_ID_REISERFS;
645 id->type = "reiserfs";
650 static int probe_xfs(struct volume_id *id, __u64 off)
652 struct xfs_super_block {
665 } __attribute__((__packed__)) *xs;
667 xs = (struct xfs_super_block *) get_buffer(id, off, 0x200);
671 if (strncmp(xs->magic, "XFSB", 4) != 0)
674 set_label_raw(id, xs->fname, 12);
675 set_label_string(id, xs->fname, 12);
676 set_uuid(id, xs->uuid, 16);
678 id->usage_id = VOLUME_ID_FILESYSTEM;
679 id->type_id = VOLUME_ID_XFS;
685 #define JFS_SUPERBLOCK_OFFSET 0x8000
686 static int probe_jfs(struct volume_id *id, __u64 off)
688 struct jfs_super_block {
699 } __attribute__((__packed__)) *js;
701 js = (struct jfs_super_block *)
702 get_buffer(id, off + JFS_SUPERBLOCK_OFFSET, 0x200);
706 if (strncmp(js->magic, "JFS1", 4) != 0)
709 set_label_raw(id, js->label, 16);
710 set_label_string(id, js->label, 16);
711 set_uuid(id, js->uuid, 16);
713 id->usage_id = VOLUME_ID_FILESYSTEM;
714 id->type_id = VOLUME_ID_JFS;
720 #define FAT12_MAX 0xff5
721 #define FAT16_MAX 0xfff5
722 #define FAT_ATTR_VOLUME 0x08
723 struct vfat_dir_entry {
735 } __attribute__((__packed__));
737 static char *vfat_search_label_in_dir(const __u8 *buf, __u16 size)
739 struct vfat_dir_entry *dir;
743 dir = (struct vfat_dir_entry*) buf;
744 count = size / sizeof(struct vfat_dir_entry);
745 dbg("expected entries 0x%x", count);
747 for (i = 0; i <= count; i++) {
749 if (dir[i].attr == 0x00) {
755 if (dir[i].attr == 0xe5)
758 if (dir[i].attr == FAT_ATTR_VOLUME) {
759 dbg("found ATTR_VOLUME id in root dir");
763 dbg("skip dir entry");
769 static int probe_vfat(struct volume_id *id, __u64 off)
771 struct vfat_super_block {
775 __u8 sectors_per_cluster;
787 struct fat_super_block {
794 } __attribute__((__packed__)) fat;
795 struct fat32_super_block {
809 } __attribute__((__packed__)) fat32;
810 } __attribute__((__packed__)) type;
811 } __attribute__((__packed__)) *vs;
823 __u32 start_data_sect;
824 __u16 root_dir_entries;
830 vs = (struct vfat_super_block *) get_buffer(id, off, 0x200);
834 /* believe only that's fat, don't trust the version
835 * the cluster_count will tell us
837 if (strncmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
840 if (strncmp(vs->type.fat32.magic, "FAT32 ", 8) == 0)
843 if (strncmp(vs->type.fat.magic, "FAT16 ", 8) == 0)
846 if (strncmp(vs->type.fat.magic, "MSDOS", 5) == 0)
849 if (strncmp(vs->type.fat.magic, "FAT12 ", 8) == 0)
853 * There are old floppies out there without a magic, so we check
854 * for well known values and guess if it's a fat volume
857 /* boot jump address check */
858 if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90) &&
859 vs->boot_jump[0] != 0xe9)
866 /* cluster size check*/
867 if (vs->sectors_per_cluster == 0 ||
868 (vs->sectors_per_cluster & (vs->sectors_per_cluster-1)))
872 if (vs->media < 0xf8 && vs->media != 0xf0)
880 /* sector size check */
881 sector_size = le16_to_cpu(vs->sector_size);
882 if (sector_size != 0x200 && sector_size != 0x400 &&
883 sector_size != 0x800 && sector_size != 0x1000)
886 dbg("sector_size 0x%x", sector_size);
887 dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
889 dir_entries = le16_to_cpu(vs->dir_entries);
890 reserved = le16_to_cpu(vs->reserved);
891 dbg("reserved 0x%x", reserved);
893 sect_count = le16_to_cpu(vs->sectors);
895 sect_count = vs->total_sect;
896 dbg("sect_count 0x%x", sect_count);
898 fat_length = le16_to_cpu(vs->fat_length);
900 fat_length = le32_to_cpu(vs->type.fat32.fat32_length);
901 dbg("fat_length 0x%x", fat_length);
903 fat_size = fat_length * vs->fats;
904 dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
905 (sector_size-1)) / sector_size;
906 dbg("dir_size 0x%x", dir_size);
908 cluster_count = sect_count - (reserved + fat_size + dir_size);
909 cluster_count /= vs->sectors_per_cluster;
910 dbg("cluster_count 0x%x", cluster_count);
912 if (cluster_count < FAT12_MAX) {
913 strcpy(id->type_version, "FAT12");
914 } else if (cluster_count < FAT16_MAX) {
915 strcpy(id->type_version, "FAT16");
917 strcpy(id->type_version, "FAT32");
921 /* the label may be an attribute in the root directory */
922 root_start = (reserved + fat_size) * sector_size;
923 root_dir_entries = le16_to_cpu(vs->dir_entries);
924 dbg("root dir start 0x%x", root_start);
926 buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
927 buf = get_buffer(id, off + root_start, buf_size);
931 label = vfat_search_label_in_dir(buf, buf_size);
933 if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) {
934 set_label_raw(id, label, 11);
935 set_label_string(id, label, 11);
936 } else if (strncmp(vs->type.fat.label, "NO NAME ", 11) != 0) {
937 set_label_raw(id, vs->type.fat.label, 11);
938 set_label_string(id, vs->type.fat.label, 11);
940 set_uuid(id, vs->type.fat.serno, 4);
944 /* FAT32 root dir is a cluster chain like any other directory */
945 buf_size = vs->sectors_per_cluster * sector_size;
946 root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
947 dbg("root dir cluster %u", root_cluster);
948 start_data_sect = reserved + fat_size;
956 dbg("next cluster %u", next);
957 next_sect_off = (next - 2) * vs->sectors_per_cluster;
958 next_off = (start_data_sect + next_sect_off) * sector_size;
959 dbg("cluster offset 0x%x", next_off);
962 buf = get_buffer(id, off + next_off, buf_size);
966 label = vfat_search_label_in_dir(buf, buf_size);
971 fat_entry_off = (reserved * sector_size) + (next * sizeof(__u32));
972 buf = get_buffer(id, off + fat_entry_off, buf_size);
976 /* set next cluster */
977 next = le32_to_cpu(*((__u32 *) buf) & 0x0fffffff);
982 if (label != NULL && strncmp(label, "NO NAME ", 11) != 0) {
983 set_label_raw(id, label, 11);
984 set_label_string(id, label, 11);
985 } else if (strncmp(vs->type.fat32.label, "NO NAME ", 11) == 0) {
986 set_label_raw(id, vs->type.fat32.label, 11);
987 set_label_string(id, vs->type.fat32.label, 11);
989 set_uuid(id, vs->type.fat32.serno, 4);
992 id->usage_id = VOLUME_ID_FILESYSTEM;
993 id->type_id = VOLUME_ID_VFAT;
999 #define UDF_VSD_OFFSET 0x8000
1000 static int probe_udf(struct volume_id *id, __u64 off)
1002 struct volume_descriptor {
1003 struct descriptor_tag {
1012 } __attribute__((__packed__)) tag;
1014 struct anchor_descriptor {
1017 } __attribute__((__packed__)) anchor;
1018 struct primary_descriptor {
1024 } __attribute__((__packed__)) ident;
1025 } __attribute__((__packed__)) primary;
1026 } __attribute__((__packed__)) type;
1027 } __attribute__((__packed__)) *vd;
1029 struct volume_structure_descriptor {
1042 vsd = (struct volume_structure_descriptor *)
1043 get_buffer(id, off + UDF_VSD_OFFSET, 0x200);
1047 if (strncmp(vsd->id, "NSR02", 5) == 0)
1049 if (strncmp(vsd->id, "NSR03", 5) == 0)
1051 if (strncmp(vsd->id, "BEA01", 5) == 0)
1053 if (strncmp(vsd->id, "BOOT2", 5) == 0)
1055 if (strncmp(vsd->id, "CD001", 5) == 0)
1057 if (strncmp(vsd->id, "CDW02", 5) == 0)
1059 if (strncmp(vsd->id, "TEA03", 5) == 0)
1064 /* search the next VSD to get the logical block size of the volume */
1065 for (bs = 0x800; bs < 0x8000; bs += 0x800) {
1066 vsd = (struct volume_structure_descriptor *)
1067 get_buffer(id, off + UDF_VSD_OFFSET + bs, 0x800);
1070 dbg("test for blocksize: 0x%x", bs);
1071 if (vsd->id[0] != '\0')
1077 /* search the list of VSDs for a NSR descriptor */
1078 for (b = 0; b < 64; b++) {
1079 vsd = (struct volume_structure_descriptor *)
1080 get_buffer(id, off + UDF_VSD_OFFSET + (b * bs), 0x800);
1084 dbg("vsd: %c%c%c%c%c",
1085 vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
1087 if (vsd->id[0] == '\0')
1089 if (strncmp(vsd->id, "NSR02", 5) == 0)
1091 if (strncmp(vsd->id, "NSR03", 5) == 0)
1097 /* read anchor volume descriptor */
1098 vd = (struct volume_descriptor *)
1099 get_buffer(id, off + (256 * bs), 0x200);
1103 type = le16_to_cpu(vd->tag.id);
1104 if (type != 2) /* TAG_ID_AVDP */
1107 /* get desriptor list address and block count */
1108 count = le32_to_cpu(vd->type.anchor.length) / bs;
1109 loc = le32_to_cpu(vd->type.anchor.location);
1110 dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
1112 /* pick the primary descriptor from the list */
1113 for (b = 0; b < count; b++) {
1114 vd = (struct volume_descriptor *)
1115 get_buffer(id, off + ((loc + b) * bs), 0x200);
1119 type = le16_to_cpu(vd->tag.id);
1120 dbg("descriptor type %i", type);
1122 /* check validity */
1125 if (le32_to_cpu(vd->tag.location) != loc + b)
1128 if (type == 1) /* TAG_ID_PVD */
1134 set_label_raw(id, &(vd->type.primary.ident.clen), 32);
1136 clen = vd->type.primary.ident.clen;
1137 dbg("label string charsize=%i bit", clen);
1139 set_label_string(id, vd->type.primary.ident.c, 31);
1140 else if (clen == 16)
1141 set_label_unicode16(id, vd->type.primary.ident.c, BE,31);
1144 id->usage_id = VOLUME_ID_FILESYSTEM;
1145 id->type_id = VOLUME_ID_UDF;
1151 #define ISO_SUPERBLOCK_OFFSET 0x8000
1152 static int probe_iso9660(struct volume_id *id, __u64 off)
1154 union iso_super_block {
1162 } __attribute__((__packed__)) iso;
1168 } __attribute__((__packed__)) hs;
1169 } __attribute__((__packed__)) *is;
1171 is = (union iso_super_block *)
1172 get_buffer(id, off + ISO_SUPERBLOCK_OFFSET, 0x200);
1176 if (strncmp(is->iso.id, "CD001", 5) == 0) {
1177 set_label_raw(id, is->iso.volume_id, 32);
1178 set_label_string(id, is->iso.volume_id, 32);
1181 if (strncmp(is->hs.id, "CDROM", 5) == 0)
1186 id->usage_id = VOLUME_ID_FILESYSTEM;
1187 id->type_id = VOLUME_ID_ISO9660;
1188 id->type = "iso9660";
1193 #define UFS_MAGIC 0x00011954
1194 #define UFS2_MAGIC 0x19540119
1195 #define UFS_MAGIC_FEA 0x00195612
1196 #define UFS_MAGIC_LFN 0x00095014
1199 static int probe_ufs(struct volume_id *id, __u64 off)
1201 struct ufs_super_block {
1235 __u32 fs_npsect_state;
1236 __u32 fs_interleave;
1254 } __attribute__((__packed__)) fs_cstotal;
1264 __u32 fs_maxcluster;
1266 __u16 fs_opostbl[16][8];
1267 } __attribute__((__packed__)) fs_u1;
1270 __u8 fs_volname[32];
1275 __u32 fs_contigdirs;
1277 __u32 fs_maxcluster;
1281 __s64 fs_sparecon64[17];
1283 struct ufs2_csum_total {
1288 __u64 cs_numclusters;
1290 } __attribute__((__packed__)) fs_cstotal;
1291 struct ufs_timeval {
1294 } __attribute__((__packed__)) fs_time;
1298 __s64 fs_pendingblocks;
1299 __s32 fs_pendinginodes;
1300 } __attribute__((__packed__)) fs_u2;
1304 __s32 fs_sparecon[53];
1306 __s32 fs_sparecon2[1];
1310 } __attribute__((__packed__)) fs_sun;
1312 __s32 fs_sparecon[53];
1314 __s32 fs_sparecon2[1];
1318 } __attribute__((__packed__)) fs_sunx86;
1320 __s32 fs_sparecon[50];
1321 __s32 fs_contigsumsize;
1322 __s32 fs_maxsymlinklen;
1324 __u32 fs_maxfilesize[2];
1328 } __attribute__((__packed__)) fs_44;
1330 __s32 fs_postblformat;
1336 } __attribute__((__packed__)) *ufs;
1340 int offsets[] = {0, 8, 64, 256, -1};
1342 for (i = 0; offsets[i] >= 0; i++) {
1343 ufs = (struct ufs_super_block *)
1344 get_buffer(id, off + (offsets[i] * 0x400), 0x800);
1348 dbg("offset 0x%x", offsets[i] * 0x400);
1349 magic = be32_to_cpu(ufs->fs_magic);
1350 if ((magic == UFS_MAGIC) ||
1351 (magic == UFS2_MAGIC) ||
1352 (magic == UFS_MAGIC_FEA) ||
1353 (magic == UFS_MAGIC_LFN)) {
1354 dbg("magic 0x%08x(be)", magic);
1357 magic = le32_to_cpu(ufs->fs_magic);
1358 if ((magic == UFS_MAGIC) ||
1359 (magic == UFS2_MAGIC) ||
1360 (magic == UFS_MAGIC_FEA) ||
1361 (magic == UFS_MAGIC_LFN)) {
1362 dbg("magic 0x%08x(le)", magic);
1369 id->usage_id = VOLUME_ID_FILESYSTEM;
1370 id->type_id = VOLUME_ID_UFS;
1376 static int probe_mac_partition_map(struct volume_id *id, __u64 off)
1378 struct mac_driver_desc {
1382 } __attribute__((__packed__)) *driver;
1384 struct mac_partition {
1392 } __attribute__((__packed__)) *part;
1396 buf = get_buffer(id, off, 0x200);
1400 part = (struct mac_partition *) buf;
1401 if ((strncmp(part->signature, "PM", 2) == 0) &&
1402 (strncmp(part->type, "Apple_partition_map", 19) == 0)) {
1403 /* linux creates an own subdevice for the map
1404 * just return the type if the drive header is missing */
1405 id->usage_id = VOLUME_ID_PARTITIONTABLE;
1406 id->type_id = VOLUME_ID_MACPARTMAP;
1407 id->type = "mac_partition_map";
1411 driver = (struct mac_driver_desc *) buf;
1412 if (strncmp(driver->signature, "ER", 2) == 0) {
1413 /* we are on a main device, like a CD
1414 * just try to probe the first partition from the map */
1415 unsigned int bsize = be16_to_cpu(driver->block_size);
1419 /* get first entry of partition table */
1420 buf = get_buffer(id, off + bsize, 0x200);
1424 part = (struct mac_partition *) buf;
1425 if (strncmp(part->signature, "PM", 2) != 0)
1428 part_count = be32_to_cpu(part->map_count);
1429 dbg("expecting %d partition entries", part_count);
1431 if (id->partitions != NULL)
1432 free(id->partitions);
1434 malloc(part_count * sizeof(struct volume_id_partition));
1435 if (id->partitions == NULL)
1437 memset(id->partitions, 0x00, sizeof(struct volume_id_partition));
1439 id->partition_count = part_count;
1441 for (i = 0; i < part_count; i++) {
1445 buf = get_buffer(id, off + ((i+1) * bsize), 0x200);
1449 part = (struct mac_partition *) buf;
1450 if (strncmp(part->signature, "PM", 2) != 0)
1453 poff = be32_to_cpu(part->start_block) * bsize;
1454 plen = be32_to_cpu(part->block_count) * bsize;
1455 dbg("found '%s' partition entry at 0x%llx, len 0x%llx",
1456 part->type, poff, plen);
1458 id->partitions[i].off = poff;
1459 id->partitions[i].len = plen;
1461 if (strncmp(part->type, "Apple_Free", 10) == 0) {
1462 id->partitions[i].usage_id = VOLUME_ID_UNUSED;
1463 } else if (strncmp(part->type, "Apple_partition_map", 19) == 0) {
1464 id->partitions[i].usage_id = VOLUME_ID_PARTITIONTABLE;
1465 id->partitions[i].type_id = VOLUME_ID_MACPARTMAP;
1467 id->partitions[i].usage_id = VOLUME_ID_UNPROBED;
1470 id->usage_id = VOLUME_ID_PARTITIONTABLE;
1471 id->type_id = VOLUME_ID_MACPARTMAP;
1472 id->type = "mac_partition_map";
1479 #define HFS_SUPERBLOCK_OFFSET 0x400
1480 #define HFS_NODE_LEAF 0xff
1481 #define HFSPLUS_POR_CNID 1
1482 static int probe_hfs_hfsplus(struct volume_id *id, __u64 off)
1484 struct finder_info {
1492 } __attribute__((__packed__));
1513 __u32 xt_clump_size;
1514 __u32 ct_clump_size;
1515 __u16 num_root_dirs;
1518 struct finder_info finfo;
1520 __u16 embed_startblock;
1521 __u16 embed_blockcount;
1522 } __attribute__((__packed__)) *hfs;
1524 struct hfsplus_bnode_descriptor {
1531 } __attribute__((__packed__));
1533 struct hfsplus_bheader_record {
1540 } __attribute__((__packed__));
1542 struct hfsplus_catalog_key {
1546 __u8 unicode[255 * 2];
1547 } __attribute__((__packed__));
1549 struct hfsplus_extent {
1552 } __attribute__((__packed__));
1554 struct hfsplus_fork {
1558 struct hfsplus_extent extents[8];
1559 } __attribute__((__packed__));
1561 struct hfsplus_vol_header {
1565 __u32 last_mount_vers;
1577 __u32 rsrc_clump_sz;
1578 __u32 data_clump_sz;
1581 __u64 encodings_bmp;
1582 struct finder_info finfo;
1583 struct hfsplus_fork alloc_file;
1584 struct hfsplus_fork ext_file;
1585 struct hfsplus_fork cat_file;
1586 struct hfsplus_fork attr_file;
1587 struct hfsplus_fork start_file;
1588 } __attribute__((__packed__)) *hfsplus;
1590 unsigned int blocksize;
1591 unsigned int cat_block;
1592 unsigned int cat_block_count;
1593 unsigned int cat_off;
1594 unsigned int cat_len;
1595 unsigned int leaf_node_head;
1596 unsigned int leaf_node_size;
1597 unsigned int alloc_block_size;
1598 unsigned int alloc_first_block;
1599 unsigned int embed_first_block;
1600 struct hfsplus_bnode_descriptor *descr;
1601 struct hfsplus_bheader_record *bnode;
1602 struct hfsplus_catalog_key *key;
1603 unsigned int label_len;
1606 buf = get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
1610 hfs = (struct hfs_mdb *) buf;
1611 if (strncmp(hfs->signature, "BD", 2) != 0)
1614 /* it may be just a hfs wrapper for hfs+ */
1615 if (strncmp(hfs->embed_sig, "H+", 2) == 0) {
1616 alloc_block_size = be32_to_cpu(hfs->al_blk_size);
1617 dbg("alloc_block_size 0x%x", alloc_block_size);
1619 alloc_first_block = be16_to_cpu(hfs->al_bl_st);
1620 dbg("alloc_first_block 0x%x", alloc_first_block);
1622 embed_first_block = be16_to_cpu(hfs->embed_startblock);
1623 dbg("embed_first_block 0x%x", embed_first_block);
1625 off += (alloc_first_block * 512) +
1626 (embed_first_block * alloc_block_size);
1627 dbg("hfs wrapped hfs+ found at offset 0x%llx", off);
1629 buf = get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
1635 if (hfs->label_len > 0 && hfs->label_len < 28) {
1636 set_label_raw(id, hfs->label, hfs->label_len);
1637 set_label_string(id, hfs->label, hfs->label_len) ;
1640 id->usage_id = VOLUME_ID_FILESYSTEM;
1641 id->type_id = VOLUME_ID_HFS;
1647 hfsplus = (struct hfsplus_vol_header *) buf;
1648 if (strncmp(hfsplus->signature, "H+", 2) == 0)
1650 if (strncmp(hfsplus->signature, "HX", 2) == 0)
1655 blocksize = be32_to_cpu(hfsplus->blocksize);
1656 cat_block = be32_to_cpu(hfsplus->cat_file.extents[0].start_block);
1657 cat_block_count = be32_to_cpu(hfsplus->cat_file.extents[0].block_count);
1658 cat_off = (cat_block * blocksize);
1659 cat_len = cat_block_count * blocksize;
1660 dbg("catalog start 0x%llx, len 0x%x", off + cat_off, cat_len);
1662 buf = get_buffer(id, off + cat_off, 0x2000);
1666 bnode = (struct hfsplus_bheader_record *)
1667 &buf[sizeof(struct hfsplus_bnode_descriptor)];
1669 leaf_node_head = be32_to_cpu(bnode->leaf_head);
1670 leaf_node_size = be16_to_cpu(bnode->node_size);
1672 dbg("catalog leaf node 0x%x, size 0x%x",
1673 leaf_node_head, leaf_node_size);
1675 buf = get_buffer(id, off + cat_off + (leaf_node_head * leaf_node_size),
1680 descr = (struct hfsplus_bnode_descriptor *) buf;
1681 dbg("descriptor type 0x%x", descr->type);
1682 if (descr->type != HFS_NODE_LEAF)
1685 key = (struct hfsplus_catalog_key *)
1686 &buf[sizeof(struct hfsplus_bnode_descriptor)];
1688 dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
1689 if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
1692 label_len = be16_to_cpu(key->unicode_len) * 2;
1693 dbg("label unicode16 len %i", label_len);
1694 set_label_raw(id, key->unicode, label_len);
1695 set_label_unicode16(id, key->unicode, BE, label_len);
1698 id->usage_id = VOLUME_ID_FILESYSTEM;
1699 id->type_id = VOLUME_ID_HFSPLUS;
1700 id->type = "hfsplus";
1705 #define MFT_RECORD_VOLUME 3
1706 #define MFT_RECORD_ATTR_VOLUME_NAME 0x60
1707 #define MFT_RECORD_ATTR_VOLUME_INFO 0x70
1708 #define MFT_RECORD_ATTR_OBJECT_ID 0x40
1709 #define MFT_RECORD_ATTR_END 0xffffffffu
1710 static int probe_ntfs(struct volume_id *id, __u64 off)
1712 struct ntfs_super_block {
1715 __u16 bytes_per_sector;
1716 __u8 sectors_per_cluster;
1717 __u16 reserved_sectors;
1722 __u16 sectors_per_fat;
1723 __u16 sectors_per_track;
1725 __u32 hidden_sectors;
1726 __u32 large_sectors;
1728 __u64 number_of_sectors;
1729 __u64 mft_cluster_location;
1730 __u64 mft_mirror_cluster_location;
1731 __s8 cluster_per_mft_record;
1733 __s8 cluster_per_index_record;
1735 __u8 volume_serial[8];
1737 } __attribute__((__packed__)) *ns;
1739 struct master_file_table_record {
1744 __u16 sequence_number;
1749 __u32 bytes_allocated;
1750 } __attribute__((__packed__)) *mftr;
1752 struct file_attribute {
1762 } __attribute__((__packed__)) *attr;
1764 struct volume_info {
1768 } __attribute__((__packed__)) *info;
1770 unsigned int sector_size;
1771 unsigned int cluster_size;
1774 unsigned int mft_record_size;
1775 unsigned int attr_type;
1776 unsigned int attr_off;
1777 unsigned int attr_len;
1778 unsigned int val_off;
1779 unsigned int val_len;
1783 ns = (struct ntfs_super_block *) get_buffer(id, off, 0x200);
1787 if (strncmp(ns->oem_id, "NTFS", 4) != 0)
1790 set_uuid(id, ns->volume_serial, 8);
1792 sector_size = le16_to_cpu(ns->bytes_per_sector);
1793 cluster_size = ns->sectors_per_cluster * sector_size;
1794 mft_cluster = le64_to_cpu(ns->mft_cluster_location);
1795 mft_off = mft_cluster * cluster_size;
1797 if (ns->cluster_per_mft_record < 0)
1798 /* size = -log2(mft_record_size); normally 1024 Bytes */
1799 mft_record_size = 1 << -ns->cluster_per_mft_record;
1801 mft_record_size = ns->cluster_per_mft_record * cluster_size;
1803 dbg("sectorsize 0x%x", sector_size);
1804 dbg("clustersize 0x%x", cluster_size);
1805 dbg("mftcluster %lli", mft_cluster);
1806 dbg("mftoffset 0x%llx", mft_off);
1807 dbg("cluster per mft_record %i", ns->cluster_per_mft_record);
1808 dbg("mft record size %i", mft_record_size);
1810 buf = get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size),
1815 mftr = (struct master_file_table_record*) buf;
1817 dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]);
1818 if (strncmp(mftr->magic, "FILE", 4) != 0)
1821 attr_off = le16_to_cpu(mftr->attrs_offset);
1822 dbg("file $Volume's attributes are at offset %i", attr_off);
1825 attr = (struct file_attribute*) &buf[attr_off];
1826 attr_type = le32_to_cpu(attr->type);
1827 attr_len = le16_to_cpu(attr->len);
1828 val_off = le16_to_cpu(attr->value_offset);
1829 val_len = le32_to_cpu(attr->value_len);
1830 attr_off += attr_len;
1835 if (attr_off >= mft_record_size)
1838 if (attr_type == MFT_RECORD_ATTR_END)
1841 dbg("found attribute type 0x%x, len %i, at offset %i",
1842 attr_type, attr_len, attr_off);
1844 if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) {
1845 dbg("found info, len %i", val_len);
1846 info = (struct volume_info*) (((__u8 *) attr) + val_off);
1847 snprintf(id->type_version, VOLUME_ID_FORMAT_SIZE-1,
1848 "%u.%u", info->major_ver, info->minor_ver);
1851 if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
1852 dbg("found label, len %i", val_len);
1853 if (val_len > VOLUME_ID_LABEL_SIZE)
1854 val_len = VOLUME_ID_LABEL_SIZE;
1856 val = ((__u8 *) attr) + val_off;
1857 set_label_raw(id, val, val_len);
1858 set_label_unicode16(id, val, LE, val_len);
1863 id->usage_id = VOLUME_ID_FILESYSTEM;
1864 id->type_id = VOLUME_ID_NTFS;
1870 #define LARGEST_PAGESIZE 0x4000
1871 static int probe_swap(struct volume_id *id, __u64 off)
1876 /* huhh, the swap signature is on the end of the PAGE_SIZE */
1877 for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
1878 sig = get_buffer(id, off + page-10, 10);
1882 if (strncmp(sig, "SWAP-SPACE", 10) == 0) {
1883 strcpy(id->type_version, "1");
1886 if (strncmp(sig, "SWAPSPACE2", 10) == 0) {
1887 strcpy(id->type_version, "2");
1894 id->usage_id = VOLUME_ID_OTHER;
1895 id->type_id = VOLUME_ID_SWAP;
1901 /* probe volume for filesystem type and try to read label+uuid */
1902 int volume_id_probe(struct volume_id *id,
1903 enum volume_id_type type,
1904 unsigned long long off,
1905 unsigned long long size)
1913 case VOLUME_ID_MSDOSPARTTABLE:
1914 rc = probe_msdos_part_table(id, off);
1916 case VOLUME_ID_EXT3:
1917 case VOLUME_ID_EXT2:
1918 rc = probe_ext(id, off);
1920 case VOLUME_ID_REISERFS:
1921 rc = probe_reiserfs(id, off);
1924 rc = probe_xfs(id, off);
1927 rc = probe_jfs(id, off);
1929 case VOLUME_ID_VFAT:
1930 rc = probe_vfat(id, off);
1933 rc = probe_udf(id, off);
1935 case VOLUME_ID_ISO9660:
1936 rc = probe_iso9660(id, off);
1938 case VOLUME_ID_MACPARTMAP:
1939 rc = probe_mac_partition_map(id, off);
1942 case VOLUME_ID_HFSPLUS:
1943 rc = probe_hfs_hfsplus(id, off);
1946 rc = probe_ufs(id, off);
1948 case VOLUME_ID_NTFS:
1949 rc = probe_ntfs(id, off);
1951 case VOLUME_ID_SWAP:
1952 rc = probe_swap(id, off);
1954 case VOLUME_ID_LINUX_RAID:
1955 rc = probe_linux_raid(id, off, size);
1957 case VOLUME_ID_LVM1:
1958 rc = probe_lvm1(id, off);
1960 case VOLUME_ID_LVM2:
1961 rc = probe_lvm2(id, off);
1965 rc = probe_linux_raid(id, off, size);
1969 /* signature in the first block */
1970 rc = probe_ntfs(id, off);
1973 rc = probe_vfat(id, off);
1976 rc = probe_msdos_part_table(id, off);
1979 rc = probe_mac_partition_map(id, off);
1982 rc = probe_xfs(id, off);
1986 /* fill buffer with maximum */
1987 get_buffer(id, 0, SB_BUFFER_SIZE);
1989 rc = probe_swap(id, off);
1992 rc = probe_ext(id, off);
1995 rc = probe_reiserfs(id, off);
1998 rc = probe_jfs(id, off);
2001 rc = probe_udf(id, off);
2004 rc = probe_iso9660(id, off);
2007 rc = probe_hfs_hfsplus(id, off);
2010 rc = probe_ufs(id, off);
2013 rc = probe_lvm1(id, off);
2016 rc = probe_lvm2(id, off);
2023 /* If the filestystem in recognized, we free the allocated buffers,
2024 otherwise they will stay in place for the possible next probe call */
2031 /* open volume by already open file descriptor */
2032 struct volume_id *volume_id_open_fd(int fd)
2034 struct volume_id *id;
2036 id = malloc(sizeof(struct volume_id));
2039 memset(id, 0x00, sizeof(struct volume_id));
2046 /* open volume by device node */
2047 struct volume_id *volume_id_open_node(const char *path)
2049 struct volume_id *id;
2052 fd = open(path, O_RDONLY | O_NONBLOCK);
2054 dbg("unable to open '%s'", path);
2058 id = volume_id_open_fd(fd);
2062 /* close fd on device close */
2068 /* open volume by major/minor */
2069 struct volume_id *volume_id_open_dev_t(dev_t devt)
2071 struct volume_id *id;
2072 __u8 tmp_node[VOLUME_ID_PATH_MAX];
2074 snprintf(tmp_node, VOLUME_ID_PATH_MAX,
2075 "/tmp/volume-%u-%u-%u", getpid(), major(devt), minor(devt));
2076 tmp_node[VOLUME_ID_PATH_MAX] = '\0';
2078 /* create tempory node to open the block device */
2080 if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
2083 id = volume_id_open_node(tmp_node);
2090 /* free allocated volume info */
2091 void volume_id_close(struct volume_id *id)
2096 if (id->fd_close != 0)
2101 if (id->partitions != NULL)
2102 free(id->partitions);