chiark / gitweb /
[PATCH] udev callout for reading filesystem labels
[elogind.git] / extras / volume_id / volume_id.c
1 /*
2  * volume_id - reads filesystem label and uuid
3  *
4  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
5  *
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
11  *      a look at:
12  *              http://e2fsprogs.sourceforge.net.
13  *
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.
18  *
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.
23  *
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
27  */
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <fcntl.h>
36 #include <sys/stat.h>
37 #include <asm/types.h>
38
39 #include "volume_id.h"
40
41 #ifdef DEBUG
42 #define dbg(format, arg...)                                             \
43         do {                                                            \
44                 printf("%s: " format "\n", __FUNCTION__ , ## arg);      \
45         } while (0)
46 #else
47 #define dbg(format, arg...)     do {} while (0)
48 #endif
49
50 #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
51                            (((__u32)(x) & 0xff00u) >> 8))
52
53 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
54                            (((__u32)(x) & 0x00ff0000u) >>  8) | \
55                            (((__u32)(x) & 0x0000ff00u) <<  8) | \
56                            (((__u32)(x) & 0x000000ffu) << 24))
57
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)
64 #endif
65
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
70
71
72 static void set_label_raw(struct volume_id *id, char *buf, int count)
73 {
74         memcpy(id->label_raw, buf, count);
75         id->label_raw_len = count;
76 }
77
78 static void set_label_string(struct volume_id *id, char *buf, int count)
79 {
80         int i;
81
82         memcpy(id->label_string, buf, count);
83
84         /* remove trailing whitespace */
85         i = strlen(id->label_string);
86         while (i--) {
87                 if (! isspace(id->label_string[i]))
88                         break;
89         }
90         id->label_string[i+1] = '\0';
91 }
92
93 static void set_uuid(struct volume_id *id, unsigned char *buf, int count)
94 {
95         int i;
96
97         memcpy(id->uuid, buf, count);
98
99         /* create string if uuid is set */
100         for (i = 0; i < count; i++) 
101                 if (buf[i] != 0)
102                         goto set;
103         return;
104
105 set:
106         switch(count) {
107         case 4:
108                 sprintf(id->uuid_string, "%02X%02X-%02X%02X",
109                         buf[3], buf[2], buf[1], buf[0]);
110                 break;
111         case 16:
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],
116                         buf[4], buf[5],
117                         buf[6], buf[7],
118                         buf[8], buf[9],
119                         buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
120                 break;
121         }
122 }
123
124 static char *get_buffer(struct volume_id *id, size_t off, size_t len)
125 {
126         size_t buf_len;
127
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)
133                                 return NULL;
134                 }
135
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)
143                                 return NULL;
144                 }
145
146                 return &(id->sbbuf[off]);
147         } else {
148                 if (len > SEEK_BUFFER_SIZE)
149                         len = SEEK_BUFFER_SIZE;
150
151                 /* get seek buffer */
152                 if (id->seekbuf == NULL) {
153                         id->seekbuf = malloc(SEEK_BUFFER_SIZE);
154                         if (id->seekbuf == NULL)
155                                 return NULL;
156                 }
157
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;
166                         if (buf_len < len)
167                                 return NULL;
168                 }
169
170                 return &(id->seekbuf[off - id->seekbuf_off]);
171         }
172 }
173
174 static void free_buffer(struct volume_id *id)
175 {
176         if (id->sbbuf != NULL) {
177                 free(id->sbbuf);
178                 id->sbbuf = NULL;
179                 id->sbbuf_len = 0;
180         }
181         if (id->seekbuf != NULL) {
182                 free(id->seekbuf);
183                 id->seekbuf = NULL;
184                 id->seekbuf_len = 0;
185         }
186 }
187
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)
192 {
193         struct ext2_super_block {
194                 __u32           inodes_count;
195                 __u32           blocks_count;
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;
201                 __u32           dummy3[7];
202                 unsigned char   magic[2];
203                 __u16           state;
204                 __u32           dummy5[8];
205                 __u32           feature_compat;
206                 __u32           feature_incompat;
207                 __u32           feature_ro_compat;
208                 unsigned char   uuid[16];
209                 char            volume_name[16];
210         } *es;
211
212         es = (struct ext2_super_block *)
213              get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
214         if (es == NULL)
215                 return -1;
216
217         if (es->magic[0] != 0123 ||
218             es->magic[1] != 0357)
219                 return -1;
220
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);
224
225         if ((le32_to_cpu(es->feature_compat) &
226              EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
227                 id->fs_type = EXT3;
228                 id->fs_name = "ext3";
229         } else {
230                 id->fs_type = EXT2;
231                 id->fs_name = "ext2";
232         }
233
234         return 0;
235 }
236
237 #define REISER1_SUPERBLOCK_OFFSET               0x2000
238 #define REISER_SUPERBLOCK_OFFSET                0x10000
239 static int probe_reiser(struct volume_id *id)
240 {
241         struct reiser_super_block {
242                 __u32           blocks_count;
243                 __u32           free_blocks;
244                 __u32           root_block;
245                 __u32           journal_block;
246                 __u32           journal_dev;
247                 __u32           orig_journal_size;
248                 __u32           dummy2[5];
249                 __u16           blocksize;
250                 __u16           dummy3[3];
251                 unsigned char   magic[12];
252                 __u32           dummy4[5];
253                 unsigned char   uuid[16];
254                 char            label[16];
255         } *rs;
256
257         rs = (struct reiser_super_block *)
258              get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
259         if (rs == NULL)
260                 return -1;
261
262         if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
263                 goto found;
264         if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
265                 goto found;
266
267         rs = (struct reiser_super_block *)
268              get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
269         if (rs == NULL)
270                 return -1;
271
272         if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
273                 goto found;
274
275         return -1;
276
277 found:
278         set_label_raw(id, rs->label, 16);
279         set_label_string(id, rs->label, 16);
280         set_uuid(id, rs->uuid, 16);
281
282         id->fs_type = REISER;
283         id->fs_name = "reiser";
284
285         return 0;
286 }
287
288 static int probe_xfs(struct volume_id *id)
289 {
290         struct xfs_super_block {
291                 unsigned char   magic[4];
292                 __u32           blocksize;
293                 __u64           dblocks;
294                 __u64           rblocks;
295                 __u32           dummy1[2];
296                 unsigned char   uuid[16];
297                 __u32           dummy2[15];
298                 char            fname[12];
299                 __u32           dummy3[2];
300                 __u64           icount;
301                 __u64           ifree;
302                 __u64           fdblocks;
303         } *xs;
304
305         xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
306         if (xs == NULL)
307                 return -1;
308
309         if (strncmp(xs->magic, "XFSB", 4) != 0)
310                 return -1;
311
312         set_label_raw(id, xs->fname, 12);
313         set_label_string(id, xs->fname, 12);
314         set_uuid(id, xs->uuid, 16);
315
316         id->fs_type = XFS;
317         id->fs_name = "xfs";
318
319         return 0;
320 }
321
322 #define JFS_SUPERBLOCK_OFFSET                   0x8000
323 static int probe_jfs(struct volume_id *id)
324 {
325         struct jfs_super_block {
326                 unsigned char   magic[4];
327                 __u32           version;
328                 __u64           size;
329                 __u32           bsize;
330                 __u32           dummy1;
331                 __u32           pbsize;
332                 __u32           dummy2[27];
333                 unsigned char   uuid[16];
334                 unsigned char   label[16];
335                 unsigned char   loguuid[16];
336         } *js;
337
338         js = (struct jfs_super_block *)
339              get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
340         if (js == NULL)
341                 return -1;
342
343         if (strncmp(js->magic, "JFS1", 4) != 0)
344                 return -1;
345
346         set_label_raw(id, js->label, 16);
347         set_label_string(id, js->label, 16);
348         set_uuid(id, js->uuid, 16);
349
350         id->fs_type = JFS;
351         id->fs_name = "jfs";
352
353         return 0;
354 }
355
356 static int probe_vfat(struct volume_id *id)
357 {
358         struct vfat_super_block {
359                 unsigned char   ignored[3];
360                 unsigned char   sysid[8];
361                 unsigned char   sector_size[2];
362                 __u8            cluster_size;
363                 __u16           reserved;
364                 __u8            fats;
365                 unsigned char   dir_entries[2];
366                 unsigned char   sectors[2];
367                 unsigned char   media;
368                 __u16           fat_length;
369                 __u16           secs_track;
370                 __u16           heads;
371                 __u32           hidden;
372                 __u32           total_sect;
373                 __u32           fat32_length;
374                 __u16           flags;
375                 __u8            version[2];
376                 __u32           root_cluster;
377                 __u16           insfo_sector;
378                 __u16           backup_boot;
379                 __u16           reserved2[6];
380                 unsigned char   unknown[3];
381                 unsigned char   serno[4];
382                 char            label[11];
383                 unsigned char   magic[8];
384                 unsigned char   dummy2[164];
385                 unsigned char   pmagic[2];
386         } *vs;
387
388         vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
389         if (vs == NULL)
390                 return -1;
391
392         if (strncmp(vs->magic, "MSWIN", 5) == 0)
393                 goto found;
394         if (strncmp(vs->magic, "FAT32   ", 8) == 0)
395                 goto found;
396         return -1;
397
398 found:
399         set_label_raw(id, vs->label, 11);
400         set_label_string(id, vs->label, 11);
401         set_uuid(id, vs->serno, 4);
402
403         id->fs_type = VFAT;
404         id->fs_name = "vfat";
405
406         return 0;
407 }
408
409 static int probe_msdos(struct volume_id *id)
410 {
411         struct msdos_super_block {
412                 unsigned char   ignored[3];
413                 unsigned char   sysid[8];
414                 unsigned char   sector_size[2];
415                 __u8            cluster_size;
416                 __u16           reserved;
417                 __u8            fats;
418                 unsigned char   dir_entries[2];
419                 unsigned char   sectors[2];
420                 unsigned char   media;
421                 __u16           fat_length;
422                 __u16           secs_track;
423                 __u16           heads;
424                 __u32           hidden;
425                 __u32           total_sect;
426                 unsigned char   unknown[3];
427                 unsigned char   serno[4];
428                 char            label[11];
429                 unsigned char   magic[8];
430                 unsigned char   dummy2[192];
431                 unsigned char   pmagic[2];
432         } *ms;
433
434         ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
435         if (ms == NULL)
436                 return -1;
437
438         if (strncmp(ms->magic, "MSDOS", 5) == 0)
439                 goto found;
440         if (strncmp(ms->magic, "FAT16   ", 8) == 0)
441                 goto found;
442         if (strncmp(ms->magic, "FAT12   ", 8) == 0)
443                 goto found;
444         return -1;
445
446 found:
447         set_label_raw(id, ms->label, 11);
448         set_label_string(id, ms->label, 11);
449         set_uuid(id, ms->serno, 4);
450
451         id->fs_type = MSDOS;
452         id->fs_name = "msdos";
453
454         return 0;
455 }
456
457 #define UDF_VSD_OFFSET                  0x8000
458 static int probe_udf(struct volume_id *id)
459 {
460         struct volume_descriptor {
461                 struct descriptor_tag {
462                         __u16           id;
463                         __u16           version;
464                         unsigned char   checksum;
465                         unsigned char   reserved;
466                         __u16           serial;
467                         __u16           crc;
468                         __u16           crc_len;
469                         __u32           location;
470                 } tag;
471                 union {
472                         struct anchor_descriptor {
473                                 __u32           length;
474                                 __u32           location;
475                         } anchor;
476                         struct primary_descriptor {
477                                 __u32           seq_num;
478                                 __u32           desc_num;
479                                 struct dstring {
480                                         char            clen;
481                                         char            c[31];
482                                 } ident;
483                         } primary;
484                 } type;
485         } *vd;
486
487         struct volume_structure_descriptor {
488                 unsigned char   type;
489                 char            id[5];
490                 unsigned char   version;
491         } *vsd;
492
493         size_t bs;
494         size_t b;
495         int type;
496         int count;
497         int loc;
498         int clen;
499         int i,j;
500         int c;
501
502         vsd = (struct volume_structure_descriptor *)
503               get_buffer(id, UDF_VSD_OFFSET, 0x200);
504         if (vsd == NULL)
505                 return -1;
506
507         if (strncmp(vsd->id, "NSR02", 5) == 0)
508                 goto blocksize;
509         if (strncmp(vsd->id, "NSR03", 5) == 0)
510                 goto blocksize;
511         if (strncmp(vsd->id, "BEA01", 5) == 0)
512                 goto blocksize;
513         if (strncmp(vsd->id, "BOOT2", 5) == 0)
514                 goto blocksize;
515         if (strncmp(vsd->id, "CD001", 5) == 0)
516                 goto blocksize;
517         if (strncmp(vsd->id, "CDW02", 5) == 0)
518                 goto blocksize;
519         if (strncmp(vsd->id, "TEA03", 5) == 0)
520                 goto blocksize;
521         return -1;
522
523 blocksize:
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);
528                 if (vsd == NULL)
529                         return -1;
530                 dbg("test for blocksize: 0x%x", bs);
531                 if (vsd->id[0] != '\0')
532                         goto nsr;
533         }
534         return -1;
535
536 nsr:
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);
541                 if (vsd == NULL)
542                         return -1;
543
544                 dbg("vsd: %c%c%c%c%c",
545                     vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
546
547                 if (vsd->id[0] == '\0')
548                         return -1;
549                 if (strncmp(vsd->id, "NSR02", 5) == 0)
550                         goto anchor;
551                 if (strncmp(vsd->id, "NSR03", 5) == 0)
552                         goto anchor;
553         }
554         return -1;
555
556 anchor:
557         /* read anchor volume descriptor */
558         vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
559         if (vd == NULL)
560                 return -1;
561
562         type = le16_to_cpu(vd->tag.id);
563         if (type != 2) /* TAG_ID_AVDP */
564                 goto found;
565
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);
570
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);
575                 if (vd == NULL)
576                         return -1;
577
578                 type = le16_to_cpu(vd->tag.id);
579                 dbg("descriptor type %i", type);
580
581                 /* check validity */
582                 if (type == 0)
583                         goto found;
584                 if (le32_to_cpu(vd->tag.location) != loc + b)
585                         goto found;
586
587                 if (type == 1) /* TAG_ID_PVD */
588                         goto pvd;
589         }
590         goto found;
591
592 pvd:
593         set_label_raw(id, &(vd->type.primary.ident.clen), 32);
594
595         clen = vd->type.primary.ident.clen;
596         dbg("label string charsize=%i bit", clen);
597         if (clen == 8) {
598                 set_label_string(id, vd->type.primary.ident.c, 31);
599         } else if (clen == 16) {
600                 /* convert unicode OSTA dstring to UTF-8 */
601                 j = 0;
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];
605                         if (c == 0) {
606                                 id->label_string[j] = '\0';
607                                 break;
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));
613                         } else {
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));
617                         }
618                 }
619         }
620
621 found:
622         id->fs_type = UDF;
623         id->fs_name = "udf";
624
625         return 0;
626 }
627
628 #define ISO_SUPERBLOCK_OFFSET           0x8000
629 static int probe_iso9660(struct volume_id *id)
630 {
631         union iso_super_block {
632                 struct iso_header {
633                         unsigned char   type;
634                         char            id[5];
635                         unsigned char   version;
636                         unsigned char   unused1;
637                         char            system_id[32];
638                         char            volume_id[32];
639                 } iso;
640                 struct hs_header {
641                         char            foo[8];
642                         unsigned char   type;
643                         char            id[4];
644                         unsigned char   version;
645                 } hs;
646         } *is;
647
648         is = (union iso_super_block *)
649              get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
650         if (is == NULL)
651                 return -1;
652
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);
656                 goto found;
657         }
658         if (strncmp(is->hs.id, "CDROM", 5) == 0)
659                 goto found;
660         return -1;
661
662 found:
663         id->fs_type = ISO9660;
664         id->fs_name = "iso9660";
665
666         return 0;
667 }
668
669 static int probe_ntfs(struct volume_id *id)
670 {
671         struct ntfs_super_block {
672                 char jump[3];
673                 char oem_id[4];
674         } *ns;
675
676         ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
677         if (ns == NULL)
678                 return -1;
679
680         if (strncmp(ns->oem_id, "NTFS", 4) != 0)
681                 return -1;
682
683         id->fs_type = NTFS;
684         id->fs_name = "ntfs";
685
686         return 0;
687 }
688
689 #define LARGEST_PAGESIZE                        0x4000
690 static int probe_swap(struct volume_id *id)
691 {
692         char *sig;
693         size_t page;
694
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);
698                         if (sig == NULL)
699                                 return -1;
700
701                         if (strncmp(sig, "SWAP-SPACE", 10) == 0)
702                                 goto found;
703                         if (strncmp(sig, "SWAPSPACE2", 10) == 0)
704                                 goto found;
705         }
706         return -1;
707
708 found:
709         id->fs_type = SWAP;
710         id->fs_name = "swap";
711
712         return 0;
713 }
714
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)
717 {
718         int rc;
719
720         if (id == NULL)
721                 return -EINVAL;
722
723         switch (fs_type) {
724         case EXT3:
725         case EXT2:
726                 rc = probe_ext(id);
727                 break;
728         case REISER:
729                 rc = probe_reiser(id);
730                 break;
731         case XFS:
732                 rc = probe_xfs(id);
733                 break;
734         case JFS:
735                 rc = probe_jfs(id);
736                 break;
737         case MSDOS:
738                 rc = probe_msdos(id);
739                 break;
740         case VFAT:
741                 rc = probe_vfat(id);
742                 break;
743         case UDF:
744                 rc = probe_udf(id);
745                 break;
746         case ISO9660:
747                 rc = probe_iso9660(id);
748                 break;
749         case NTFS:
750                 rc = probe_ntfs(id);
751                 break;
752         case SWAP:
753                 rc = probe_swap(id);
754                 break;
755         case ALL:
756         default:
757                 /* fill buffer with maximum */
758                 get_buffer(id, 0, SB_BUFFER_SIZE);
759                 rc = probe_ext(id);
760                 if (rc == 0)
761                         break;
762                 rc = probe_reiser(id);
763                 if (rc == 0)
764                         break;
765                 rc = probe_xfs(id);
766                 if (rc == 0)
767                         break;
768                 rc = probe_jfs(id);
769                 if (rc == 0)
770                         break;
771                 rc = probe_msdos(id);
772                 if (rc == 0)
773                         break;
774                 rc = probe_vfat(id);
775                 if (rc == 0)
776                         break;
777                 rc = probe_udf(id);
778                 if (rc == 0)
779                         break;
780                 rc = probe_iso9660(id);
781                 if (rc == 0)
782                         break;
783                 rc = probe_ntfs(id);
784                 if (rc == 0)
785                         break;
786                 rc = probe_swap(id);
787                 if (rc == 0)
788                         break;
789                 rc = -1;
790         }
791
792         /* If the filestystem in recognized, we free the allocated buffers,
793            otherwise they will stay in place for the possible next probe call */
794         if (rc == 0)
795                 free_buffer(id);
796
797         return rc;
798 }
799
800 /* open volume by already open file descriptor */
801 struct volume_id *volume_id_open_fd(int fd)
802 {
803         struct volume_id *id;
804
805         id = malloc(sizeof(struct volume_id));
806         if (id == NULL)
807                 return NULL;
808         memset(id, 0x00, sizeof(struct volume_id));
809
810         id->fd = fd;
811
812         return id;
813 }
814
815 /* open volume by device node */
816 struct volume_id *volume_id_open_node(const char *path)
817 {
818         struct volume_id *id;
819         int fd;
820
821         fd = open(path, O_RDONLY);
822         if (fd < 0)
823                 return NULL;
824
825         id = volume_id_open_fd(fd);
826         if (id == NULL)
827                 return NULL;
828
829         /* close fd on device close */
830         id->fd_close = 1;
831
832         return id;
833 }
834
835 /* open volume by major/minor */
836 struct volume_id *volume_id_open_dev_t(dev_t devt)
837 {
838         struct volume_id *id;
839         char tmp_node[VOLUME_ID_PATH_MAX];
840
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';
844
845         /* create tempory node to open the block device */
846         if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
847                 return NULL;
848
849         id = volume_id_open_node(tmp_node);
850
851         unlink(tmp_node);
852
853         return id;
854 }
855
856 /* free allocated volume info */
857 void volume_id_close(struct volume_id *id)
858 {
859         if (id == NULL)
860                 return;
861
862         if (id->fd_close != 0)
863                 close(id->fd);
864
865         free_buffer(id);
866
867         free(id);
868 }