chiark / gitweb /
[PATCH] update udev_volume_id
[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 disk 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 #ifndef _GNU_SOURCE
30 #define _GNU_SOURCE
31 #endif
32
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40 #include <sys/stat.h>
41 #include <asm/types.h>
42
43 #include "volume_id.h"
44
45 #ifdef DEBUG
46 #define dbg(format, arg...)                                             \
47         do {                                                            \
48                 printf("%s: " format "\n", __FUNCTION__ , ## arg);      \
49         } while (0)
50 #else
51 #define dbg(format, arg...)     do {} while (0)
52 #endif
53
54 #define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
55                            (((__u32)(x) & 0xff00u) >> 8))
56
57 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
58                            (((__u32)(x) & 0x00ff0000u) >>  8) | \
59                            (((__u32)(x) & 0x0000ff00u) <<  8) | \
60                            (((__u32)(x) & 0x000000ffu) << 24))
61
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))
70
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)
79 #endif
80
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
85
86
87 static void set_label_raw(struct volume_id *id,
88                           const __u8 *buf, unsigned int count)
89 {
90         memcpy(id->label_raw, buf, count);
91         id->label_raw_len = count;
92 }
93
94 static void set_label_string(struct volume_id *id,
95                              const __u8 *buf, unsigned int count)
96 {
97         unsigned int i;
98
99         memcpy(id->label_string, buf, count);
100
101         /* remove trailing whitespace */
102         i = strnlen(id->label_string, count);
103         while (i--) {
104                 if (! isspace(id->label_string[i]))
105                         break;
106         }
107         id->label_string[i+1] = '\0';
108 }
109
110 #define LE              0
111 #define BE              1
112 static void set_label_unicode16(struct volume_id *id,
113                                 const __u8 *buf,
114                                 unsigned int endianess,
115                                 unsigned int count)
116 {
117         unsigned int i, j;
118         __u16 c;
119
120         j = 0;
121         for (i = 0; i <= count-2; i += 2) {
122                 if (endianess == LE)
123                         c = (buf[i+1] << 8) | buf[i];
124                 else
125                         c = (buf[i] << 8) | buf[i+1];
126                 if (c == 0) {
127                         id->label_string[j] = '\0';
128                         break;
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));
134                 } else {
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));
138                 }
139         }
140 }
141
142 static void set_uuid(struct volume_id *id,
143                      const __u8 *buf, unsigned int count)
144 {
145         unsigned int i;
146
147         memcpy(id->uuid, buf, count);
148
149         /* create string if uuid is set */
150         for (i = 0; i < count; i++) 
151                 if (buf[i] != 0)
152                         goto set;
153         return;
154
155 set:
156         switch(count) {
157         case 4:
158                 sprintf(id->uuid_string, "%02X%02X-%02X%02X",
159                         buf[3], buf[2], buf[1], buf[0]);
160                 break;
161         case 16:
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],
166                         buf[4], buf[5],
167                         buf[6], buf[7],
168                         buf[8], buf[9],
169                         buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
170                 break;
171         }
172 }
173
174 static __u8 *get_buffer(struct volume_id *id,
175                         unsigned long off, unsigned int len)
176 {
177         unsigned int buf_len;
178
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)
184                                 return NULL;
185                 }
186
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)
194                                 return NULL;
195                 }
196
197                 return &(id->sbbuf[off]);
198         } else {
199                 if (len > SEEK_BUFFER_SIZE)
200                         len = SEEK_BUFFER_SIZE;
201
202                 /* get seek buffer */
203                 if (id->seekbuf == NULL) {
204                         id->seekbuf = malloc(SEEK_BUFFER_SIZE);
205                         if (id->seekbuf == NULL)
206                                 return NULL;
207                 }
208
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;
218                         if (buf_len < len)
219                                 return NULL;
220                 }
221
222                 return &(id->seekbuf[off - id->seekbuf_off]);
223         }
224 }
225
226 static void free_buffer(struct volume_id *id)
227 {
228         if (id->sbbuf != NULL) {
229                 free(id->sbbuf);
230                 id->sbbuf = NULL;
231                 id->sbbuf_len = 0;
232         }
233         if (id->seekbuf != NULL) {
234                 free(id->seekbuf);
235                 id->seekbuf = NULL;
236                 id->seekbuf_len = 0;
237         }
238 }
239
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)
244 {
245         struct ext2_super_block {
246                 __u32   inodes_count;
247                 __u32   blocks_count;
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;
253                 __u32   dummy3[7];
254                 __u8    magic[2];
255                 __u16   state;
256                 __u32   dummy5[8];
257                 __u32   feature_compat;
258                 __u32   feature_incompat;
259                 __u32   feature_ro_compat;
260                 __u8    uuid[16];
261                 __u8    volume_name[16];
262         } __attribute__((__packed__)) *es;
263
264         es = (struct ext2_super_block *)
265              get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
266         if (es == NULL)
267                 return -1;
268
269         if (es->magic[0] != 0123 ||
270             es->magic[1] != 0357)
271                 return -1;
272
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);
276
277         if ((le32_to_cpu(es->feature_compat) &
278              EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
279                 id->fs_type = EXT3;
280                 id->fs_name = "ext3";
281         } else {
282                 id->fs_type = EXT2;
283                 id->fs_name = "ext2";
284         }
285
286         return 0;
287 }
288
289 #define REISER1_SUPERBLOCK_OFFSET               0x2000
290 #define REISER_SUPERBLOCK_OFFSET                0x10000
291 static int probe_reiser(struct volume_id *id)
292 {
293         struct reiser_super_block {
294                 __u32   blocks_count;
295                 __u32   free_blocks;
296                 __u32   root_block;
297                 __u32   journal_block;
298                 __u32   journal_dev;
299                 __u32   orig_journal_size;
300                 __u32   dummy2[5];
301                 __u16   blocksize;
302                 __u16   dummy3[3];
303                 __u8    magic[12];
304                 __u32   dummy4[5];
305                 __u8    uuid[16];
306                 __u8    label[16];
307         } __attribute__((__packed__)) *rs;
308
309         rs = (struct reiser_super_block *)
310              get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
311         if (rs == NULL)
312                 return -1;
313
314         if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
315                 goto found;
316         if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
317                 goto found;
318
319         rs = (struct reiser_super_block *)
320              get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
321         if (rs == NULL)
322                 return -1;
323
324         if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
325                 goto found;
326
327         return -1;
328
329 found:
330         set_label_raw(id, rs->label, 16);
331         set_label_string(id, rs->label, 16);
332         set_uuid(id, rs->uuid, 16);
333
334         id->fs_type = REISER;
335         id->fs_name = "reiser";
336
337         return 0;
338 }
339
340 static int probe_xfs(struct volume_id *id)
341 {
342         struct xfs_super_block {
343                 __u8    magic[4];
344                 __u32   blocksize;
345                 __u64   dblocks;
346                 __u64   rblocks;
347                 __u32   dummy1[2];
348                 __u8    uuid[16];
349                 __u32   dummy2[15];
350                 __u8    fname[12];
351                 __u32   dummy3[2];
352                 __u64   icount;
353                 __u64   ifree;
354                 __u64   fdblocks;
355         } __attribute__((__packed__)) *xs;
356
357         xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
358         if (xs == NULL)
359                 return -1;
360
361         if (strncmp(xs->magic, "XFSB", 4) != 0)
362                 return -1;
363
364         set_label_raw(id, xs->fname, 12);
365         set_label_string(id, xs->fname, 12);
366         set_uuid(id, xs->uuid, 16);
367
368         id->fs_type = XFS;
369         id->fs_name = "xfs";
370
371         return 0;
372 }
373
374 #define JFS_SUPERBLOCK_OFFSET                   0x8000
375 static int probe_jfs(struct volume_id *id)
376 {
377         struct jfs_super_block {
378                 __u8    magic[4];
379                 __u32   version;
380                 __u64   size;
381                 __u32   bsize;
382                 __u32   dummy1;
383                 __u32   pbsize;
384                 __u32   dummy2[27];
385                 __u8    uuid[16];
386                 __u8    label[16];
387                 __u8    loguuid[16];
388         } __attribute__((__packed__)) *js;
389
390         js = (struct jfs_super_block *)
391              get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
392         if (js == NULL)
393                 return -1;
394
395         if (strncmp(js->magic, "JFS1", 4) != 0)
396                 return -1;
397
398         set_label_raw(id, js->label, 16);
399         set_label_string(id, js->label, 16);
400         set_uuid(id, js->uuid, 16);
401
402         id->fs_type = JFS;
403         id->fs_name = "jfs";
404
405         return 0;
406 }
407
408 static int probe_vfat(struct volume_id *id)
409 {
410         struct vfat_super_block {
411                 __u8    ignored[3];
412                 __u8    sysid[8];
413                 __u8    sector_size[2];
414                 __u8    cluster_size;
415                 __u16   reserved;
416                 __u8    fats;
417                 __u8    dir_entries[2];
418                 __u8    sectors[2];
419                 __u8    media;
420                 __u16   fat_length;
421                 __u16   secs_track;
422                 __u16   heads;
423                 __u32   hidden;
424                 __u32   total_sect;
425                 __u32   fat32_length;
426                 __u16   flags;
427                 __u8    version[2];
428                 __u32   root_cluster;
429                 __u16   insfo_sector;
430                 __u16   backup_boot;
431                 __u16   reserved2[6];
432                 __u8    unknown[3];
433                 __u8    serno[4];
434                 __u8    label[11];
435                 __u8    magic[8];
436                 __u8    dummy2[164];
437                 __u8    pmagic[2];
438         } __attribute__((__packed__)) *vs;
439
440         vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
441         if (vs == NULL)
442                 return -1;
443
444         if (strncmp(vs->magic, "MSWIN", 5) == 0)
445                 goto found;
446         if (strncmp(vs->magic, "FAT32   ", 8) == 0)
447                 goto found;
448         return -1;
449
450 found:
451         set_label_raw(id, vs->label, 11);
452         set_label_string(id, vs->label, 11);
453         set_uuid(id, vs->serno, 4);
454
455         id->fs_type = VFAT;
456         id->fs_name = "vfat";
457
458         return 0;
459 }
460
461 static int probe_msdos(struct volume_id *id)
462 {
463         struct msdos_super_block {
464                 __u8    ignored[3];
465                 __u8    sysid[8];
466                 __u8    sector_size[2];
467                 __u8    cluster_size;
468                 __u16   reserved;
469                 __u8    fats;
470                 __u8    dir_entries[2];
471                 __u8    sectors[2];
472                 __u8    media;
473                 __u16   fat_length;
474                 __u16   secs_track;
475                 __u16   heads;
476                 __u32   hidden;
477                 __u32   total_sect;
478                 __u8    unknown[3];
479                 __u8    serno[4];
480                 __u8    label[11];
481                 __u8    magic[8];
482                 __u8    dummy2[192];
483                 __u8    pmagic[2];
484         } __attribute__((__packed__)) *ms;
485
486         ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
487         if (ms == NULL)
488                 return -1;
489
490         if (strncmp(ms->magic, "MSDOS", 5) == 0)
491                 goto found;
492         if (strncmp(ms->magic, "FAT16   ", 8) == 0)
493                 goto found;
494         if (strncmp(ms->magic, "FAT12   ", 8) == 0)
495                 goto found;
496         return -1;
497
498 found:
499         set_label_raw(id, ms->label, 11);
500         set_label_string(id, ms->label, 11);
501         set_uuid(id, ms->serno, 4);
502
503         id->fs_type = MSDOS;
504         id->fs_name = "msdos";
505
506         return 0;
507 }
508
509 #define UDF_VSD_OFFSET                  0x8000
510 static int probe_udf(struct volume_id *id)
511 {
512         struct volume_descriptor {
513                 struct descriptor_tag {
514                         __u16   id;
515                         __u16   version;
516                         __u8    checksum;
517                         __u8    reserved;
518                         __u16   serial;
519                         __u16   crc;
520                         __u16   crc_len;
521                         __u32   location;
522                 } __attribute__((__packed__)) tag;
523                 union {
524                         struct anchor_descriptor {
525                                 __u32   length;
526                                 __u32   location;
527                         } __attribute__((__packed__)) anchor;
528                         struct primary_descriptor {
529                                 __u32   seq_num;
530                                 __u32   desc_num;
531                                 struct dstring {
532                                         __u8    clen;
533                                         __u8    c[31];
534                                 } __attribute__((__packed__)) ident;
535                         } __attribute__((__packed__)) primary;
536                 } __attribute__((__packed__)) type;
537         } __attribute__((__packed__)) *vd;
538
539         struct volume_structure_descriptor {
540                 __u8    type;
541                 __u8    id[5];
542                 __u8    version;
543         } *vsd;
544
545         unsigned int bs;
546         unsigned int b;
547         unsigned int type;
548         unsigned int count;
549         unsigned int loc;
550         unsigned int clen;
551
552         vsd = (struct volume_structure_descriptor *)
553               get_buffer(id, UDF_VSD_OFFSET, 0x200);
554         if (vsd == NULL)
555                 return -1;
556
557         if (strncmp(vsd->id, "NSR02", 5) == 0)
558                 goto blocksize;
559         if (strncmp(vsd->id, "NSR03", 5) == 0)
560                 goto blocksize;
561         if (strncmp(vsd->id, "BEA01", 5) == 0)
562                 goto blocksize;
563         if (strncmp(vsd->id, "BOOT2", 5) == 0)
564                 goto blocksize;
565         if (strncmp(vsd->id, "CD001", 5) == 0)
566                 goto blocksize;
567         if (strncmp(vsd->id, "CDW02", 5) == 0)
568                 goto blocksize;
569         if (strncmp(vsd->id, "TEA03", 5) == 0)
570                 goto blocksize;
571         return -1;
572
573 blocksize:
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);
578                 if (vsd == NULL)
579                         return -1;
580                 dbg("test for blocksize: 0x%x", bs);
581                 if (vsd->id[0] != '\0')
582                         goto nsr;
583         }
584         return -1;
585
586 nsr:
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);
591                 if (vsd == NULL)
592                         return -1;
593
594                 dbg("vsd: %c%c%c%c%c",
595                     vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
596
597                 if (vsd->id[0] == '\0')
598                         return -1;
599                 if (strncmp(vsd->id, "NSR02", 5) == 0)
600                         goto anchor;
601                 if (strncmp(vsd->id, "NSR03", 5) == 0)
602                         goto anchor;
603         }
604         return -1;
605
606 anchor:
607         /* read anchor volume descriptor */
608         vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
609         if (vd == NULL)
610                 return -1;
611
612         type = le16_to_cpu(vd->tag.id);
613         if (type != 2) /* TAG_ID_AVDP */
614                 goto found;
615
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);
620
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);
625                 if (vd == NULL)
626                         return -1;
627
628                 type = le16_to_cpu(vd->tag.id);
629                 dbg("descriptor type %i", type);
630
631                 /* check validity */
632                 if (type == 0)
633                         goto found;
634                 if (le32_to_cpu(vd->tag.location) != loc + b)
635                         goto found;
636
637                 if (type == 1) /* TAG_ID_PVD */
638                         goto pvd;
639         }
640         goto found;
641
642 pvd:
643         set_label_raw(id, &(vd->type.primary.ident.clen), 32);
644
645         clen = vd->type.primary.ident.clen;
646         dbg("label string charsize=%i bit", clen);
647         if (clen == 8) 
648                 set_label_string(id, vd->type.primary.ident.c, 31);
649         else if (clen == 16)
650                 set_label_unicode16(id, vd->type.primary.ident.c, BE,31);
651
652 found:
653         id->fs_type = UDF;
654         id->fs_name = "udf";
655
656         return 0;
657 }
658
659 #define ISO_SUPERBLOCK_OFFSET           0x8000
660 static int probe_iso9660(struct volume_id *id)
661 {
662         union iso_super_block {
663                 struct iso_header {
664                         __u8    type;
665                         __u8    id[5];
666                         __u8    version;
667                         __u8    unused1;
668                         __u8            system_id[32];
669                         __u8            volume_id[32];
670                 } __attribute__((__packed__)) iso;
671                 struct hs_header {
672                         __u8    foo[8];
673                         __u8    type;
674                         __u8    id[4];
675                         __u8    version;
676                 } __attribute__((__packed__)) hs;
677         } __attribute__((__packed__)) *is;
678
679         is = (union iso_super_block *)
680              get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
681         if (is == NULL)
682                 return -1;
683
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);
687                 goto found;
688         }
689         if (strncmp(is->hs.id, "CDROM", 5) == 0)
690                 goto found;
691         return -1;
692
693 found:
694         id->fs_type = ISO9660;
695         id->fs_name = "iso9660";
696
697         return 0;
698 }
699
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)
705 {
706         struct ntfs_super_block {
707                 __u8    jump[3];
708                 __u8    oem_id[8];
709                 struct bios_param_block {
710                         __u16   bytes_per_sector;
711                         __u8    sectors_per_cluster;
712                         __u16   reserved_sectors;
713                         __u8    fats;
714                         __u16   root_entries;
715                         __u16   sectors;
716                         __u8    media_type;             /* 0xf8 = hard disk */
717                         __u16   sectors_per_fat;
718                         __u16   sectors_per_track;
719                         __u16   heads;
720                         __u32   hidden_sectors;
721                         __u32   large_sectors;
722                 } __attribute__((__packed__)) bpb;
723                 __u8 unused[4];
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;
729
730         struct master_file_table_record {
731                 __u8    magic[4];
732                 __u16   usa_ofs;
733                 __u16   usa_count;
734                 __u64   lsn;
735                 __u16   sequence_number;
736                 __u16   link_count;
737                 __u16   attrs_offset;
738                 __u16   flags;
739                 __u32   bytes_in_use;
740                 __u32   bytes_allocated;
741         } __attribute__((__packed__)) *mftr;
742
743         struct file_attribute {
744                 __u32   type;
745                 __u32   len;
746                 __u8    non_resident;
747                 __u8    name_len;
748                 __u16   name_offset;
749                 __u16   flags;
750                 __u16   instance;
751                 __u32   value_len;
752                 __u16   value_offset;
753         } __attribute__((__packed__)) *attr;
754
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;
765         const __u8 *buf;
766         const __u8 *val;
767
768         ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
769         if (ns == NULL)
770                 return -1;
771
772         if (strncmp(ns->oem_id, "NTFS", 4) != 0)
773                 return -1;
774
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;
779
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;
783         else
784                 mft_record_size = ns->cluster_per_mft_record * cluster_size;
785
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);
792
793         buf = get_buffer(id, mft_off + (MFT_RECORD_VOLUME * mft_record_size),
794                          mft_record_size);
795         if (buf == NULL)
796                 goto found;
797
798         mftr = (struct master_file_table_record*) buf;
799
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)
805                 goto found;
806
807         attr_off = le16_to_cpu(mftr->attrs_offset);
808         dbg("file $Volume's attributes are at offset %i", attr_off);
809
810         while (1) {
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);
816
817                 if (attr_type == MFT_RECORD_ATTR_END)
818                         break;
819
820                 dbg("found attribute type 0x%x, len %i, at offset %i",
821                     attr_type, attr_len, attr_off);
822
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;
827
828                         val = &((__u8 *) attr)[val_off];
829                         set_label_raw(id, val, val_len);
830                         set_label_unicode16(id, val, LE, val_len);
831                 }
832
833                 if (attr_type == MFT_RECORD_ATTR_OBJECT_ID) {
834                         dbg("found uuid");
835                         val = &((__u8 *) attr)[val_off];
836                         set_uuid(id, val, 16);
837                 }
838
839                 if (attr_len == 0)
840                         break;
841                 attr_off += attr_len;
842                 if (attr_off >= mft_record_size)
843                         break;
844         }
845
846 found:
847         id->fs_type = NTFS;
848         id->fs_name = "ntfs";
849
850         return 0;
851 }
852
853 #define LARGEST_PAGESIZE                        0x4000
854 static int probe_swap(struct volume_id *id)
855 {
856         const __u8 *sig;
857         unsigned int page;
858
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);
862                         if (sig == NULL)
863                                 return -1;
864
865                         if (strncmp(sig, "SWAP-SPACE", 10) == 0)
866                                 goto found;
867                         if (strncmp(sig, "SWAPSPACE2", 10) == 0)
868                                 goto found;
869         }
870         return -1;
871
872 found:
873         id->fs_type = SWAP;
874         id->fs_name = "swap";
875
876         return 0;
877 }
878
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)
881 {
882         int rc;
883
884         if (id == NULL)
885                 return -EINVAL;
886
887         switch (fs_type) {
888         case EXT3:
889         case EXT2:
890                 rc = probe_ext(id);
891                 break;
892         case REISER:
893                 rc = probe_reiser(id);
894                 break;
895         case XFS:
896                 rc = probe_xfs(id);
897                 break;
898         case JFS:
899                 rc = probe_jfs(id);
900                 break;
901         case MSDOS:
902                 rc = probe_msdos(id);
903                 break;
904         case VFAT:
905                 rc = probe_vfat(id);
906                 break;
907         case UDF:
908                 rc = probe_udf(id);
909                 break;
910         case ISO9660:
911                 rc = probe_iso9660(id);
912                 break;
913         case NTFS:
914                 rc = probe_ntfs(id);
915                 break;
916         case SWAP:
917                 rc = probe_swap(id);
918                 break;
919         case ALL:
920         default:
921                 /* fill buffer with maximum */
922                 get_buffer(id, 0, SB_BUFFER_SIZE);
923                 rc = probe_ext(id);
924                 if (rc == 0)
925                         break;
926                 rc = probe_reiser(id);
927                 if (rc == 0)
928                         break;
929                 rc = probe_xfs(id);
930                 if (rc == 0)
931                         break;
932                 rc = probe_jfs(id);
933                 if (rc == 0)
934                         break;
935                 rc = probe_msdos(id);
936                 if (rc == 0)
937                         break;
938                 rc = probe_vfat(id);
939                 if (rc == 0)
940                         break;
941                 rc = probe_udf(id);
942                 if (rc == 0)
943                         break;
944                 rc = probe_iso9660(id);
945                 if (rc == 0)
946                         break;
947                 rc = probe_ntfs(id);
948                 if (rc == 0)
949                         break;
950                 rc = probe_swap(id);
951                 if (rc == 0)
952                         break;
953                 rc = -1;
954         }
955
956         /* If the filestystem in recognized, we free the allocated buffers,
957            otherwise they will stay in place for the possible next probe call */
958         if (rc == 0)
959                 free_buffer(id);
960
961         return rc;
962 }
963
964 /* open volume by already open file descriptor */
965 struct volume_id *volume_id_open_fd(int fd)
966 {
967         struct volume_id *id;
968
969         id = malloc(sizeof(struct volume_id));
970         if (id == NULL)
971                 return NULL;
972         memset(id, 0x00, sizeof(struct volume_id));
973
974         id->fd = fd;
975
976         return id;
977 }
978
979 /* open volume by device node */
980 struct volume_id *volume_id_open_node(const char *path)
981 {
982         struct volume_id *id;
983         int fd;
984
985         fd = open(path, O_RDONLY);
986         if (fd < 0)
987                 return NULL;
988
989         id = volume_id_open_fd(fd);
990         if (id == NULL)
991                 return NULL;
992
993         /* close fd on device close */
994         id->fd_close = 1;
995
996         return id;
997 }
998
999 /* open volume by major/minor */
1000 struct volume_id *volume_id_open_dev_t(dev_t devt)
1001 {
1002         struct volume_id *id;
1003         __u8 tmp_node[VOLUME_ID_PATH_MAX];
1004
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';
1008
1009         /* create tempory node to open the block device */
1010         unlink(tmp_node);
1011         if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
1012                 return NULL;
1013
1014         id = volume_id_open_node(tmp_node);
1015
1016         unlink(tmp_node);
1017
1018         return id;
1019 }
1020
1021 /* free allocated volume info */
1022 void volume_id_close(struct volume_id *id)
1023 {
1024         if (id == NULL)
1025                 return;
1026
1027         if (id->fd_close != 0)
1028                 close(id->fd);
1029
1030         free_buffer(id);
1031
1032         free(id);
1033 }