chiark / gitweb /
[PATCH] udev callout for reading filesystem labels
authorkay.sievers@vrfy.org <kay.sievers@vrfy.org>
Thu, 6 May 2004 05:21:48 +0000 (22:21 -0700)
committerGreg KH <gregkh@suse.de>
Wed, 27 Apr 2005 04:35:17 +0000 (21:35 -0700)
On Fri, Apr 30, 2004 at 03:29:54PM -0700, Greg KH wrote:
> On Thu, Apr 29, 2004 at 11:04:46PM +0200, Kay Sievers wrote:
> > Hi,
> > here is a small udev toy, which enables udev to name partitions by
> > its filesystem label or uuid's.
> >
> > The following udev rule:
> >
> >   KERNEL="sd*", PROGRAM="/sbin/udev_volume_id -M%M -m%m -u", SYMLINK="%c"
> >
> > creates a symlink with the uuid read from the filesystem. If no label or
> > uuid is found the program exits with nonzero and the rule will fail.
> >
> > ext2, ext3, reiserfs, xfs, jfs, vfat, msdos volume labels are supported,
> > ntfs and swap partitions can be recognized.
> >
> > It's possible to compile with klibc and the static binary takes 13kb.
>
> Very nice, I was wondering who was going to use that library to make
> such a tool.  This is even better as we can use klibc for it.

Here is a update, which supports iso9660 and udf labels.
Not very useful in the udev case, but I've added it for hal,
so we just catch up with the latest version.

extras/volume_id/volume_id.c
extras/volume_id/volume_id.h

index cc5475fc8dda2f24fe37dcf23297ed6269e8abed..8eba4b22f25f9689f81e7d394cfb38f1ee138d2e 100644 (file)
  *     a look at:
  *             http://e2fsprogs.sourceforge.net.
  *
- *     This program is free software; you can redistribute it and/or modify it
- *     under the terms of the GNU General Public License as published by the
- *     Free Software Foundation version 2 of the License.
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation; either
+ *     version 2.1 of the License, or (at your option) any later version.
  *
- *     This program is distributed in the hope that it will be useful, but
- *     WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *     General Public License for more details.
- *
- *     You should have received a copy of the GNU General Public License along
- *     with this program; if not, write to the Free Software Foundation, Inc.,
- *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *     Lesser General Public License for more details.
  *
+ *     You should have received a copy of the GNU Lesser General Public
+ *     License along with this library; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #include <stdlib.h>
 
 #include "volume_id.h"
 
+#ifdef DEBUG
+#define dbg(format, arg...)                                            \
+       do {                                                            \
+               printf("%s: " format "\n", __FUNCTION__ , ## arg);      \
+       } while (0)
+#else
+#define dbg(format, arg...)    do {} while (0)
+#endif
+
+#define bswap16(x) (__u16)((((__u16)(x) & 0x00ffu) << 8) | \
+                          (((__u32)(x) & 0xff00u) >> 8))
 
 #define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
                           (((__u32)(x) & 0x00ff0000u) >>  8) | \
                           (((__u32)(x) & 0x000000ffu) << 24))
 
 #if (__BYTE_ORDER == __LITTLE_ENDIAN) 
-#define cpu_to_le32(x) (x)
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
 #elif (__BYTE_ORDER == __BIG_ENDIAN)
-#define cpu_to_le32(x) bswap32(x)
+#define le16_to_cpu(x) bswap16(x)
+#define le32_to_cpu(x) bswap32(x)
 #endif
 
-#define VOLUME_ID_BUFFER_SIZE          0x11000 /* reiser offset is 64k */
+/* size of superblock buffer, reiser block is at 64k */
+#define SB_BUFFER_SIZE                         0x11000
+/* size of seek buffer 2k */
+#define SEEK_BUFFER_SIZE                       0x800
 
 
-static void set_label(struct volume_id *id, char *buf, int count)
+static void set_label_raw(struct volume_id *id, char *buf, int count)
 {
-       int i;
+       memcpy(id->label_raw, buf, count);
+       id->label_raw_len = count;
+}
 
-       memcpy(id->label, buf, count);
+static void set_label_string(struct volume_id *id, char *buf, int count)
+{
+       int i;
 
        memcpy(id->label_string, buf, count);
 
@@ -90,7 +110,8 @@ set:
                break;
        case 16:
                sprintf(id->uuid_string,
-                       "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+                       "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+                       "%02x%02x%02x%02x%02x%02x",
                        buf[0], buf[1], buf[2], buf[3],
                        buf[4], buf[5],
                        buf[6], buf[7],
@@ -100,29 +121,67 @@ set:
        }
 }
 
-static int open_superblock(struct volume_id *id)
+static char *get_buffer(struct volume_id *id, size_t off, size_t len)
 {
-       /* get buffer to read the first block */
-       if (id->buf == NULL) {
-               id->buf = malloc(VOLUME_ID_BUFFER_SIZE);
-               if (id->buf == NULL)
-                       return -1;
+       size_t buf_len;
+
+       /* check if requested area fits in superblock buffer */
+       if (off + len <= SB_BUFFER_SIZE) {
+               if (id->sbbuf == NULL) {
+                       id->sbbuf = malloc(SB_BUFFER_SIZE);
+                       if (id->sbbuf == NULL)
+                               return NULL;
+               }
+
+               /* check if we need to read */
+               if ((off + len) > id->sbbuf_len) {
+                       dbg("read sbbuf len:0x%x", off + len);
+                       lseek(id->fd, 0, SEEK_SET);
+                       buf_len = read(id->fd, id->sbbuf, off + len);
+                       id->sbbuf_len = buf_len;
+                       if (buf_len < off + len)
+                               return NULL;
+               }
+
+               return &(id->sbbuf[off]);
+       } else {
+               if (len > SEEK_BUFFER_SIZE)
+                       len = SEEK_BUFFER_SIZE;
+
+               /* get seek buffer */
+               if (id->seekbuf == NULL) {
+                       id->seekbuf = malloc(SEEK_BUFFER_SIZE);
+                       if (id->seekbuf == NULL)
+                               return NULL;
+               }
+
+               /* check if we need to read */
+               if ((off < id->seekbuf_off) ||
+                   ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
+                       dbg("read seekbuf off:0x%x len:0x%x", off, len);
+                       lseek(id->fd, off, SEEK_SET);
+                       buf_len = read(id->fd, id->seekbuf, len);
+                       id->seekbuf_off = off;
+                       id->seekbuf_len = buf_len;
+                       if (buf_len < len)
+                               return NULL;
+               }
+
+               return &(id->seekbuf[off - id->seekbuf_off]);
        }
-
-       /* try to read the first 64k, but at least the first block */
-       memset(id->buf, 0x00, VOLUME_ID_BUFFER_SIZE);
-       lseek(id->fd, 0, SEEK_SET);
-       if (read(id->fd, id->buf, VOLUME_ID_BUFFER_SIZE) < 0x200)
-               return -1;
-
-       return 0;
 }
 
-static void close_superblock(struct volume_id *id)
+static void free_buffer(struct volume_id *id)
 {
-       if (id->buf != NULL) {
-               free(id->buf);
-               id->buf = NULL;
+       if (id->sbbuf != NULL) {
+               free(id->sbbuf);
+               id->sbbuf = NULL;
+               id->sbbuf_len = 0;
+       }
+       if (id->seekbuf != NULL) {
+               free(id->seekbuf);
+               id->seekbuf = NULL;
+               id->seekbuf_len = 0;
        }
 }
 
@@ -132,34 +191,39 @@ static void close_superblock(struct volume_id *id)
 static int probe_ext(struct volume_id *id)
 {
        struct ext2_super_block {
-               __u32           s_inodes_count;
-               __u32           s_blocks_count;
-               __u32           s_r_blocks_count;
-               __u32           s_free_blocks_count;
-               __u32           s_free_inodes_count;
-               __u32           s_first_data_block;
-               __u32           s_log_block_size;
-               __u32           s_dummy3[7];
-               unsigned char   s_magic[2];
-               __u16           s_state;
-               __u32           s_dummy5[8];
-               __u32           s_feature_compat;
-               __u32           s_feature_incompat;
-               __u32           s_feature_ro_compat;
-               unsigned char   s_uuid[16];
-               char            s_volume_name[16];
+               __u32           inodes_count;
+               __u32           blocks_count;
+               __u32           r_blocks_count;
+               __u32           free_blocks_count;
+               __u32           free_inodes_count;
+               __u32           first_data_block;
+               __u32           log_block_size;
+               __u32           dummy3[7];
+               unsigned char   magic[2];
+               __u16           state;
+               __u32           dummy5[8];
+               __u32           feature_compat;
+               __u32           feature_incompat;
+               __u32           feature_ro_compat;
+               unsigned char   uuid[16];
+               char            volume_name[16];
        } *es;
 
-       es = (struct ext2_super_block *) (id->buf + EXT_SUPERBLOCK_OFFSET);
+       es = (struct ext2_super_block *)
+            get_buffer(id, EXT_SUPERBLOCK_OFFSET, 0x200);
+       if (es == NULL)
+               return -1;
 
-       if (es->s_magic[0] != 0123 ||
-           es->s_magic[1] != 0357)
+       if (es->magic[0] != 0123 ||
+           es->magic[1] != 0357)
                return -1;
 
-       set_label(id, es->s_volume_name, 16);
-       set_uuid(id, es->s_uuid, 16);
+       set_label_raw(id, es->volume_name, 16);
+       set_label_string(id, es->volume_name, 16);
+       set_uuid(id, es->uuid, 16);
 
-       if ((cpu_to_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
+       if ((le32_to_cpu(es->feature_compat) &
+            EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
                id->fs_type = EXT3;
                id->fs_name = "ext3";
        } else {
@@ -175,38 +239,45 @@ static int probe_ext(struct volume_id *id)
 static int probe_reiser(struct volume_id *id)
 {
        struct reiser_super_block {
-               __u32           rs_blocks_count;
-               __u32           rs_free_blocks;
-               __u32           rs_root_block;
-               __u32           rs_journal_block;
-               __u32           rs_journal_dev;
-               __u32           rs_orig_journal_size;
-               __u32           rs_dummy2[5];
-               __u16           rs_blocksize;
-               __u16           rs_dummy3[3];
-               unsigned char   rs_magic[12];
-               __u32           rs_dummy4[5];
-               unsigned char   rs_uuid[16];
-               char            rs_label[16];
+               __u32           blocks_count;
+               __u32           free_blocks;
+               __u32           root_block;
+               __u32           journal_block;
+               __u32           journal_dev;
+               __u32           orig_journal_size;
+               __u32           dummy2[5];
+               __u16           blocksize;
+               __u16           dummy3[3];
+               unsigned char   magic[12];
+               __u32           dummy4[5];
+               unsigned char   uuid[16];
+               char            label[16];
        } *rs;
 
-       rs = (struct reiser_super_block *) &(id->buf[REISER1_SUPERBLOCK_OFFSET]);
+       rs = (struct reiser_super_block *)
+            get_buffer(id, REISER_SUPERBLOCK_OFFSET, 0x200);
+       if (rs == NULL)
+               return -1;
 
-       if (strncmp(rs->rs_magic, "ReIsErFs", 8) == 0)
+       if (strncmp(rs->magic, "ReIsEr2Fs", 9) == 0)
+               goto found;
+       if (strncmp(rs->magic, "ReIsEr3Fs", 9) == 0)
                goto found;
 
-       rs = (struct reiser_super_block *) &(id->buf[REISER_SUPERBLOCK_OFFSET]);
+       rs = (struct reiser_super_block *)
+            get_buffer(id, REISER1_SUPERBLOCK_OFFSET, 0x200);
+       if (rs == NULL)
+               return -1;
 
-       if (strncmp(rs->rs_magic, "ReIsEr2Fs", 9) == 0)
-               goto found;
-       if (strncmp(rs->rs_magic, "ReIsEr3Fs", 9) == 0)
+       if (strncmp(rs->magic, "ReIsErFs", 8) == 0)
                goto found;
 
        return -1;
 
 found:
-       set_label(id, rs->rs_label, 16);
-       set_uuid(id, rs->rs_uuid, 16);
+       set_label_raw(id, rs->label, 16);
+       set_label_string(id, rs->label, 16);
+       set_uuid(id, rs->uuid, 16);
 
        id->fs_type = REISER;
        id->fs_name = "reiser";
@@ -217,27 +288,30 @@ found:
 static int probe_xfs(struct volume_id *id)
 {
        struct xfs_super_block {
-               unsigned char   xs_magic[4];
-               __u32           xs_blocksize;
-               __u64           xs_dblocks;
-               __u64           xs_rblocks;
-               __u32           xs_dummy1[2];
-               unsigned char   xs_uuid[16];
-               __u32           xs_dummy2[15];
-               char            xs_fname[12];
-               __u32           xs_dummy3[2];
-               __u64           xs_icount;
-               __u64           xs_ifree;
-               __u64           xs_fdblocks;
+               unsigned char   magic[4];
+               __u32           blocksize;
+               __u64           dblocks;
+               __u64           rblocks;
+               __u32           dummy1[2];
+               unsigned char   uuid[16];
+               __u32           dummy2[15];
+               char            fname[12];
+               __u32           dummy3[2];
+               __u64           icount;
+               __u64           ifree;
+               __u64           fdblocks;
        } *xs;
 
-       xs = (struct xfs_super_block *) id->buf;
+       xs = (struct xfs_super_block *) get_buffer(id, 0, 0x200);
+       if (xs == NULL)
+               return -1;
 
-       if (strncmp(xs->xs_magic, "XFSB", 4) != 0)
+       if (strncmp(xs->magic, "XFSB", 4) != 0)
                return -1;
 
-       set_label(id, xs->xs_fname, 12);
-       set_uuid(id, xs->xs_uuid, 16);
+       set_label_raw(id, xs->fname, 12);
+       set_label_string(id, xs->fname, 12);
+       set_uuid(id, xs->uuid, 16);
 
        id->fs_type = XFS;
        id->fs_name = "xfs";
@@ -249,25 +323,29 @@ static int probe_xfs(struct volume_id *id)
 static int probe_jfs(struct volume_id *id)
 {
        struct jfs_super_block {
-               unsigned char   js_magic[4];
-               __u32           js_version;
-               __u64           js_size;
-               __u32           js_bsize;
-               __u32           js_dummy1;
-               __u32           js_pbsize;
-               __u32           js_dummy2[27];
-               unsigned char   js_uuid[16];
-               unsigned char   js_label[16];
-               unsigned char   js_loguuid[16];
+               unsigned char   magic[4];
+               __u32           version;
+               __u64           size;
+               __u32           bsize;
+               __u32           dummy1;
+               __u32           pbsize;
+               __u32           dummy2[27];
+               unsigned char   uuid[16];
+               unsigned char   label[16];
+               unsigned char   loguuid[16];
        } *js;
 
-       js = (struct jfs_super_block *) &(id->buf[JFS_SUPERBLOCK_OFFSET]);
+       js = (struct jfs_super_block *)
+            get_buffer(id, JFS_SUPERBLOCK_OFFSET, 0x200);
+       if (js == NULL)
+               return -1;
 
-       if (strncmp(js->js_magic, "JFS1", 4) != 0)
+       if (strncmp(js->magic, "JFS1", 4) != 0)
                return -1;
 
-       set_label(id, js->js_label, 16);
-       set_uuid(id, js->js_uuid, 16);
+       set_label_raw(id, js->label, 16);
+       set_label_string(id, js->label, 16);
+       set_uuid(id, js->uuid, 16);
 
        id->fs_type = JFS;
        id->fs_name = "jfs";
@@ -278,46 +356,49 @@ static int probe_jfs(struct volume_id *id)
 static int probe_vfat(struct volume_id *id)
 {
        struct vfat_super_block {
-               unsigned char   vs_ignored[3];
-               unsigned char   vs_sysid[8];
-               unsigned char   vs_sector_size[2];
-               __u8            vs_cluster_size;
-               __u16           vs_reserved;
-               __u8            vs_fats;
-               unsigned char   vs_dir_entries[2];
-               unsigned char   vs_sectors[2];
-               unsigned char   vs_media;
-               __u16           vs_fat_length;
-               __u16           vs_secs_track;
-               __u16           vs_heads;
-               __u32           vs_hidden;
-               __u32           vs_total_sect;
-               __u32           vs_fat32_length;
-               __u16           vs_flags;
-               __u8            vs_version[2];
-               __u32           vs_root_cluster;
-               __u16           vs_insfo_sector;
-               __u16           vs_backup_boot;
-               __u16           vs_reserved2[6];
-               unsigned char   vs_unknown[3];
-               unsigned char   vs_serno[4];
-               char            vs_label[11];
-               unsigned char   vs_magic[8];
-               unsigned char   vs_dummy2[164];
-               unsigned char   vs_pmagic[2];
+               unsigned char   ignored[3];
+               unsigned char   sysid[8];
+               unsigned char   sector_size[2];
+               __u8            cluster_size;
+               __u16           reserved;
+               __u8            fats;
+               unsigned char   dir_entries[2];
+               unsigned char   sectors[2];
+               unsigned char   media;
+               __u16           fat_length;
+               __u16           secs_track;
+               __u16           heads;
+               __u32           hidden;
+               __u32           total_sect;
+               __u32           fat32_length;
+               __u16           flags;
+               __u8            version[2];
+               __u32           root_cluster;
+               __u16           insfo_sector;
+               __u16           backup_boot;
+               __u16           reserved2[6];
+               unsigned char   unknown[3];
+               unsigned char   serno[4];
+               char            label[11];
+               unsigned char   magic[8];
+               unsigned char   dummy2[164];
+               unsigned char   pmagic[2];
        } *vs;
 
-       vs = (struct vfat_super_block *) id->buf;
+       vs = (struct vfat_super_block *) get_buffer(id, 0, 0x200);
+       if (vs == NULL)
+               return -1;
 
-       if (strncmp(vs->vs_magic, "MSWIN", 5) == 0)
+       if (strncmp(vs->magic, "MSWIN", 5) == 0)
                goto found;
-       if (strncmp(vs->vs_magic, "FAT32   ", 8) == 0)
+       if (strncmp(vs->magic, "FAT32   ", 8) == 0)
                goto found;
        return -1;
 
 found:
-       memcpy(id->label, vs->vs_label, 11);
-       memcpy(id->uuid, vs->vs_serno, 4);
+       set_label_raw(id, vs->label, 11);
+       set_label_string(id, vs->label, 11);
+       set_uuid(id, vs->serno, 4);
 
        id->fs_type = VFAT;
        id->fs_name = "vfat";
@@ -328,41 +409,44 @@ found:
 static int probe_msdos(struct volume_id *id)
 {
        struct msdos_super_block {
-               unsigned char   ms_ignored[3];
-               unsigned char   ms_sysid[8];
-               unsigned char   ms_sector_size[2];
-               __u8            ms_cluster_size;
-               __u16           ms_reserved;
-               __u8            ms_fats;
-               unsigned char   ms_dir_entries[2];
-               unsigned char   ms_sectors[2];
-               unsigned char   ms_media;
-               __u16           ms_fat_length;
-               __u16           ms_secs_track;
-               __u16           ms_heads;
-               __u32           ms_hidden;
-               __u32           ms_total_sect;
-               unsigned char   ms_unknown[3];
-               unsigned char   ms_serno[4];
-               char            ms_label[11];
-               unsigned char   ms_magic[8];
-               unsigned char   ms_dummy2[192];
-               unsigned char   ms_pmagic[2];
+               unsigned char   ignored[3];
+               unsigned char   sysid[8];
+               unsigned char   sector_size[2];
+               __u8            cluster_size;
+               __u16           reserved;
+               __u8            fats;
+               unsigned char   dir_entries[2];
+               unsigned char   sectors[2];
+               unsigned char   media;
+               __u16           fat_length;
+               __u16           secs_track;
+               __u16           heads;
+               __u32           hidden;
+               __u32           total_sect;
+               unsigned char   unknown[3];
+               unsigned char   serno[4];
+               char            label[11];
+               unsigned char   magic[8];
+               unsigned char   dummy2[192];
+               unsigned char   pmagic[2];
        } *ms;
 
-       ms = (struct msdos_super_block *) id->buf;
+       ms = (struct msdos_super_block *) get_buffer(id, 0, 0x200);
+       if (ms == NULL)
+               return -1;
 
-       if (strncmp(ms->ms_magic, "MSDOS", 5) == 0)
+       if (strncmp(ms->magic, "MSDOS", 5) == 0)
                goto found;
-       if (strncmp(ms->ms_magic, "FAT16   ", 8) == 0)
+       if (strncmp(ms->magic, "FAT16   ", 8) == 0)
                goto found;
-       if (strncmp(ms->ms_magic, "FAT12   ", 8) == 0)
+       if (strncmp(ms->magic, "FAT12   ", 8) == 0)
                goto found;
        return -1;
 
 found:
-       set_label(id, ms->ms_label, 11);
-       set_uuid(id, ms->ms_serno, 4);
+       set_label_raw(id, ms->label, 11);
+       set_label_string(id, ms->label, 11);
+       set_uuid(id, ms->serno, 4);
 
        id->fs_type = MSDOS;
        id->fs_name = "msdos";
@@ -370,6 +454,218 @@ found:
        return 0;
 }
 
+#define UDF_VSD_OFFSET                 0x8000
+static int probe_udf(struct volume_id *id)
+{
+       struct volume_descriptor {
+               struct descriptor_tag {
+                       __u16           id;
+                       __u16           version;
+                       unsigned char   checksum;
+                       unsigned char   reserved;
+                       __u16           serial;
+                       __u16           crc;
+                       __u16           crc_len;
+                       __u32           location;
+               } tag;
+               union {
+                       struct anchor_descriptor {
+                               __u32           length;
+                               __u32           location;
+                       } anchor;
+                       struct primary_descriptor {
+                               __u32           seq_num;
+                               __u32           desc_num;
+                               struct dstring {
+                                       char            clen;
+                                       char            c[31];
+                               } ident;
+                       } primary;
+               } type;
+       } *vd;
+
+       struct volume_structure_descriptor {
+               unsigned char   type;
+               char            id[5];
+               unsigned char   version;
+       } *vsd;
+
+       size_t bs;
+       size_t b;
+       int type;
+       int count;
+       int loc;
+       int clen;
+       int i,j;
+       int c;
+
+       vsd = (struct volume_structure_descriptor *)
+             get_buffer(id, UDF_VSD_OFFSET, 0x200);
+       if (vsd == NULL)
+               return -1;
+
+       if (strncmp(vsd->id, "NSR02", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "NSR03", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "BEA01", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "BOOT2", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "CD001", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "CDW02", 5) == 0)
+               goto blocksize;
+       if (strncmp(vsd->id, "TEA03", 5) == 0)
+               goto blocksize;
+       return -1;
+
+blocksize:
+       /* search the next VSD to get the logical block size of the volume */
+       for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+               vsd = (struct volume_structure_descriptor *)
+                     get_buffer(id, UDF_VSD_OFFSET + bs, 0x800);
+               if (vsd == NULL)
+                       return -1;
+               dbg("test for blocksize: 0x%x", bs);
+               if (vsd->id[0] != '\0')
+                       goto nsr;
+       }
+       return -1;
+
+nsr:
+       /* search the list of VSDs for a NSR descriptor */
+       for (b = 0; b < 64; b++) {
+               vsd = (struct volume_structure_descriptor *)
+                     get_buffer(id, UDF_VSD_OFFSET + (b * bs), 0x800);
+               if (vsd == NULL)
+                       return -1;
+
+               dbg("vsd: %c%c%c%c%c",
+                   vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
+
+               if (vsd->id[0] == '\0')
+                       return -1;
+               if (strncmp(vsd->id, "NSR02", 5) == 0)
+                       goto anchor;
+               if (strncmp(vsd->id, "NSR03", 5) == 0)
+                       goto anchor;
+       }
+       return -1;
+
+anchor:
+       /* read anchor volume descriptor */
+       vd = (struct volume_descriptor *) get_buffer(id, 256 * bs, 0x200);
+       if (vd == NULL)
+               return -1;
+
+       type = le16_to_cpu(vd->tag.id);
+       if (type != 2) /* TAG_ID_AVDP */
+               goto found;
+
+       /* get desriptor list address and block count */
+       count = le32_to_cpu(vd->type.anchor.length) / bs;
+       loc = le32_to_cpu(vd->type.anchor.location);
+       dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
+
+       /* pick the primary descriptor from the list */
+       for (b = 0; b < count; b++) {
+               vd = (struct volume_descriptor *)
+                    get_buffer(id, (loc + b) * bs, 0x200);
+               if (vd == NULL)
+                       return -1;
+
+               type = le16_to_cpu(vd->tag.id);
+               dbg("descriptor type %i", type);
+
+               /* check validity */
+               if (type == 0)
+                       goto found;
+               if (le32_to_cpu(vd->tag.location) != loc + b)
+                       goto found;
+
+               if (type == 1) /* TAG_ID_PVD */
+                       goto pvd;
+       }
+       goto found;
+
+pvd:
+       set_label_raw(id, &(vd->type.primary.ident.clen), 32);
+
+       clen = vd->type.primary.ident.clen;
+       dbg("label string charsize=%i bit", clen);
+       if (clen == 8) {
+               set_label_string(id, vd->type.primary.ident.c, 31);
+       } else if (clen == 16) {
+               /* convert unicode OSTA dstring to UTF-8 */
+               j = 0;
+               for (i = 0; i < 32; i += 2) {
+                       c = (vd->type.primary.ident.c[i] << 8) |
+                           vd->type.primary.ident.c[i+1];
+                       if (c == 0) {
+                               id->label_string[j] = '\0';
+                               break;
+                       }else if (c < 0x80U) {
+                               id->label_string[j++] = (char) c;
+                       } else if (c < 0x800U) {
+                               id->label_string[j++] = (char) (0xc0 | (c >> 6));
+                               id->label_string[j++] = (char) (0x80 | (c & 0x3f));
+                       } else {
+                               id->label_string[j++] = (char) (0xe0 | (c >> 12));
+                               id->label_string[j++] = (char) (0x80 | ((c >> 6) & 0x3f));
+                               id->label_string[j++] = (char) (0x80 | (c & 0x3f));
+                       }
+               }
+       }
+
+found:
+       id->fs_type = UDF;
+       id->fs_name = "udf";
+
+       return 0;
+}
+
+#define ISO_SUPERBLOCK_OFFSET          0x8000
+static int probe_iso9660(struct volume_id *id)
+{
+       union iso_super_block {
+               struct iso_header {
+                       unsigned char   type;
+                       char            id[5];
+                       unsigned char   version;
+                       unsigned char   unused1;
+                       char            system_id[32];
+                       char            volume_id[32];
+               } iso;
+               struct hs_header {
+                       char            foo[8];
+                       unsigned char   type;
+                       char            id[4];
+                       unsigned char   version;
+               } hs;
+       } *is;
+
+       is = (union iso_super_block *)
+            get_buffer(id, ISO_SUPERBLOCK_OFFSET, 0x200);
+       if (is == NULL)
+               return -1;
+
+       if (strncmp(is->iso.id, "CD001", 5) == 0) {
+               set_label_raw(id, is->iso.volume_id, 32);
+               set_label_string(id, is->iso.volume_id, 32);
+               goto found;
+       }
+       if (strncmp(is->hs.id, "CDROM", 5) == 0)
+               goto found;
+       return -1;
+
+found:
+       id->fs_type = ISO9660;
+       id->fs_name = "iso9660";
+
+       return 0;
+}
+
 static int probe_ntfs(struct volume_id *id)
 {
        struct ntfs_super_block {
@@ -377,7 +673,9 @@ static int probe_ntfs(struct volume_id *id)
                char oem_id[4];
        } *ns;
 
-       ns = (struct ntfs_super_block *) id->buf;
+       ns = (struct ntfs_super_block *) get_buffer(id, 0, 0x200);
+       if (ns == NULL)
+               return -1;
 
        if (strncmp(ns->oem_id, "NTFS", 4) != 0)
                return -1;
@@ -388,15 +686,21 @@ static int probe_ntfs(struct volume_id *id)
        return 0;
 }
 
+#define LARGEST_PAGESIZE                       0x4000
 static int probe_swap(struct volume_id *id)
 {
-       int magic;
+       char *sig;
+       size_t page;
 
        /* huhh, the swap signature is on the end of the PAGE_SIZE */
-       for (magic = 0x1000; magic <= 0x4000; magic <<= 1) {
-                       if (strncmp(&(id->buf[magic -10]), "SWAP-SPACE", 10) == 0)
+       for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
+                       sig = get_buffer(id, page-10, 10);
+                       if (sig == NULL)
+                               return -1;
+
+                       if (strncmp(sig, "SWAP-SPACE", 10) == 0)
                                goto found;
-                       if (strncmp(&(id->buf[magic -10]), "SWAPSPACE2", 10) == 0)
+                       if (strncmp(sig, "SWAPSPACE2", 10) == 0)
                                goto found;
        }
        return -1;
@@ -416,9 +720,6 @@ int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
        if (id == NULL)
                return -EINVAL;
 
-       if (open_superblock(id) != 0)
-               return -EACCES;
-
        switch (fs_type) {
        case EXT3:
        case EXT2:
@@ -439,13 +740,22 @@ int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
        case VFAT:
                rc = probe_vfat(id);
                break;
+       case UDF:
+               rc = probe_udf(id);
+               break;
+       case ISO9660:
+               rc = probe_iso9660(id);
+               break;
        case NTFS:
                rc = probe_ntfs(id);
                break;
        case SWAP:
                rc = probe_swap(id);
                break;
+       case ALL:
        default:
+               /* fill buffer with maximum */
+               get_buffer(id, 0, SB_BUFFER_SIZE);
                rc = probe_ext(id);
                if (rc == 0)
                        break;
@@ -462,6 +772,12 @@ int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
                if (rc == 0)
                        break;
                rc = probe_vfat(id);
+               if (rc == 0)
+                       break;
+               rc = probe_udf(id);
+               if (rc == 0)
+                       break;
+               rc = probe_iso9660(id);
                if (rc == 0)
                        break;
                rc = probe_ntfs(id);
@@ -473,8 +789,10 @@ int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
                rc = -1;
        }
 
+       /* If the filestystem in recognized, we free the allocated buffers,
+          otherwise they will stay in place for the possible next probe call */
        if (rc == 0)
-               close_superblock(id);
+               free_buffer(id);
 
        return rc;
 }
@@ -544,7 +862,7 @@ void volume_id_close(struct volume_id *id)
        if (id->fd_close != 0)
                close(id->fd);
 
-       close_superblock(id);
+       free_buffer(id);
 
        free(id);
 }
index a0d2acda225b01fa329687c61595f0f7658ef7af..2f20244c7c7de615a7cf8e4169c5870429149438 100644 (file)
@@ -3,27 +3,27 @@
  *
  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
  *
- *     This program is free software; you can redistribute it and/or modify it
- *     under the terms of the GNU General Public License as published by the
- *     Free Software Foundation version 2 of the License.
- * 
- *     This program is distributed in the hope that it will be useful, but
- *     WITHOUT ANY WARRANTY; without even the implied warranty of
- *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *     General Public License for more details.
- * 
- *     You should have received a copy of the GNU General Public License along
- *     with this program; if not, write to the Free Software Foundation, Inc.,
- *     675 Mass Ave, Cambridge, MA 02139, USA.
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation; either
+ *     version 2.1 of the License, or (at your option) any later version.
  *
+ *     This library is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *     Lesser General Public License for more details.
+ *
+ *     You should have received a copy of the GNU Lesser General Public
+ *     License along with this library; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #ifndef _VOLUME_ID_H_
 #define _VOLUME_ID_H_
 
-#define VOLUME_ID_VERSION              001
+#define VOLUME_ID_VERSION              002
 
-#define VOLUME_ID_LABEL_SIZE           16
+#define VOLUME_ID_LABEL_SIZE           32
 #define VOLUME_ID_UUID_SIZE            16
 #define VOLUME_ID_UUID_STRING_SIZE     37
 #define VOLUME_ID_PATH_MAX             255
@@ -38,20 +38,27 @@ enum filesystem_type {
        JFS,
        MSDOS,
        VFAT,
+       UDF,
+       ISO9660,
        NTFS,
        SWAP
 };
 
 struct volume_id {
-       char label[VOLUME_ID_LABEL_SIZE];
-       char label_string[VOLUME_ID_LABEL_SIZE+1];
-       unsigned char uuid[VOLUME_ID_UUID_SIZE];
-       char uuid_string[VOLUME_ID_UUID_STRING_SIZE];
-       enum filesystem_type fs_type;
-       char *fs_name;
-       int fd;
-       char *buf;
-       int fd_close;
+       char            label_raw[VOLUME_ID_LABEL_SIZE];
+       size_t          label_raw_len;
+       char            label_string[VOLUME_ID_LABEL_SIZE+1];
+       unsigned char   uuid[VOLUME_ID_UUID_SIZE];
+       char            uuid_string[VOLUME_ID_UUID_STRING_SIZE];
+       enum            filesystem_type fs_type;
+       char            *fs_name;
+       int             fd;
+       char            *sbbuf;
+       size_t          sbbuf_len;
+       char            *seekbuf;
+       size_t          seekbuf_off;
+       size_t          seekbuf_len;
+       int             fd_close;
 };
 
 /* open volume by already open file descriptor */