chiark / gitweb /
[PATCH] udev_volume_id: fix -d option
[elogind.git] / extras / volume_id / volume_id.c
index 0b43bc8efb84ffd6afd263abce483b840c538ded..3bf7886aaa65c8375133226e6b08f35167d28be8 100644 (file)
@@ -78,7 +78,7 @@
 /* size of superblock buffer, reiserfs block is at 64k */
 #define SB_BUFFER_SIZE                         0x11000
 /* size of seek buffer 4k */
-#define SEEK_BUFFER_SIZE                       0x1000
+#define SEEK_BUFFER_SIZE                       0x10000
 
 
 static void set_label_raw(struct volume_id *id,
@@ -221,8 +221,10 @@ static __u8 *get_buffer(struct volume_id *id, __u64 off, unsigned int len)
 
                return &(id->sbbuf[off]);
        } else {
-               if (len > SEEK_BUFFER_SIZE)
-                       len = SEEK_BUFFER_SIZE;
+               if (len > SEEK_BUFFER_SIZE) {
+                       dbg("seek buffer too small %d", SEEK_BUFFER_SIZE);
+                       return NULL;
+               }
 
                /* get seek buffer */
                if (id->seekbuf == NULL) {
@@ -232,8 +234,7 @@ static __u8 *get_buffer(struct volume_id *id, __u64 off, unsigned int len)
                }
 
                /* check if we need to read */
-               if ((off < id->seekbuf_off) ||
-                   ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
+               if ((off < id->seekbuf_off) || ((off + len) > (id->seekbuf_off + id->seekbuf_len))) {
                        dbg("read seekbuf off:0x%llx len:0x%x", off, len);
                        if (lseek(id->fd, off, SEEK_SET) == -1)
                                return NULL;
@@ -241,8 +242,10 @@ static __u8 *get_buffer(struct volume_id *id, __u64 off, unsigned int len)
                        dbg("got 0x%x (%i) bytes", buf_len, buf_len);
                        id->seekbuf_off = off;
                        id->seekbuf_len = buf_len;
-                       if (buf_len < len)
+                       if (buf_len < len) {
+                               dbg("requested 0x%x bytes, got only 0x%x bytes", len, buf_len);
                                return NULL;
+                       }
                }
 
                return &(id->seekbuf[off - id->seekbuf_off]);
@@ -263,6 +266,36 @@ static void free_buffer(struct volume_id *id)
        }
 }
 
+#define HPT37X_CONFIG_OFF              0x1200
+#define HPT37X_MAGIC_OK                        0x5a7816f0
+#define HPT37X_MAGIC_BAD               0x5a7816fd
+static int probe_highpoint_ataraid(struct volume_id *id, __u64 off)
+{
+       struct hpt37x {
+               __u8    filler1[32];
+               __u32   magic;
+               __u32   magic_0;
+               __u32   magic_1;
+       } __attribute__((packed)) *hpt;
+
+       const __u8 *buf;
+
+       buf = get_buffer(id, off + HPT37X_CONFIG_OFF, 0x200);
+       if (buf == NULL)
+               return -1;
+
+       hpt = (struct hpt37x *) buf;
+
+       if (hpt->magic != HPT37X_MAGIC_OK && hpt->magic != HPT37X_MAGIC_BAD)
+               return -1;
+
+       id->usage_id = VOLUME_ID_RAID;
+       id->type_id = VOLUME_ID_HPTRAID;
+       id->type = "hpt_ataraid_member";
+
+       return 0;
+}
+
 #define LVM1_SB_OFF                    0x400
 #define LVM1_MAGIC                     "HM"
 static int probe_lvm1(struct volume_id *id, __u64 off)
@@ -541,7 +574,7 @@ static int probe_msdos_part_table(struct volume_id *id, __u64 off)
                                p->partition_type_raw = part[i].sys_ind;
 
                                if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) {
-                                       dbg("to many partitions");
+                                       dbg("too many partitions");
                                        next = 0;
                                }
                        }
@@ -739,7 +772,11 @@ static int probe_jfs(struct volume_id *id, __u64 off)
 
 #define FAT12_MAX                      0xff5
 #define FAT16_MAX                      0xfff5
-#define FAT_ATTR_VOLUME                        0x08
+#define FAT_ATTR_VOLUME_ID             0x08
+#define FAT_ATTR_DIR                   0x10
+#define FAT_ATTR_LONG_NAME             0x0f
+#define FAT_ATTR_MASK                  0x3f
+#define FAT_ENTRY_FREE                 0xe5
 static int probe_vfat(struct volume_id *id, __u64 off)
 {
        struct vfat_super_block {
@@ -802,7 +839,7 @@ static int probe_vfat(struct volume_id *id, __u64 off)
        __u16 dir_entries;
        __u32 sect_count;
        __u16 reserved;
-       __u16 fat_size;
+       __u32 fat_size;
        __u32 root_cluster;
        __u32 dir_size;
        __u32 cluster_count;
@@ -824,6 +861,9 @@ static int probe_vfat(struct volume_id *id, __u64 off)
        /* believe only that's fat, don't trust the version
         * the cluster_count will tell us
         */
+       if (strncmp(vs->sysid, "NTFS", 4) == 0)
+               return -1;
+
        if (strncmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
                goto valid;
 
@@ -921,7 +961,7 @@ valid:
 
        dir = (struct vfat_dir_entry*) buf;
 
-       for (i = 0; i <= root_dir_entries; i++) {
+       for (i = 0; i < root_dir_entries; i++) {
                /* end marker */
                if (dir[i].name[0] == 0x00) {
                        dbg("end of dir");
@@ -929,17 +969,30 @@ valid:
                }
 
                /* empty entry */
-               if (dir[i].name[0] == 0xe5)
+               if (dir[i].name[0] == FAT_ENTRY_FREE)
                        continue;
 
-               if (dir[i].attr == FAT_ATTR_VOLUME) {
-                       dbg("found ATTR_VOLUME id in root dir");
+               /* long name */
+               if ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
+                       continue;
+
+               if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
+                       /* labels do not have file data */
+                       if (dir[i].cluster_high != 0 || dir[i].cluster_low != 0)
+                               continue;
+
+                       dbg("found ATTR_VOLUME_ID id in root dir");
                        label = dir[i].name;
+                       break;
                }
 
                dbg("skip dir entry");
        }
 
+       vs = (struct vfat_super_block *) get_buffer(id, off, 0x200);
+       if (vs == NULL)
+               return -1;
+
        if (label != NULL && strncmp(label, "NO NAME    ", 11) != 0) {
                set_label_raw(id, label, 11);
                set_label_string(id, label, 11);
@@ -979,7 +1032,7 @@ fat32:
                count = buf_size / sizeof(struct vfat_dir_entry);
                dbg("expected entries 0x%x", count);
 
-               for (i = 0; i <= count; i++) {
+               for (i = 0; i < count; i++) {
                        /* end marker */
                        if (dir[i].name[0] == 0x00) {
                                dbg("end of dir");
@@ -987,11 +1040,19 @@ fat32:
                        }
 
                        /* empty entry */
-                       if (dir[i].name[0] == 0xe5)
+                       if (dir[i].name[0] == FAT_ENTRY_FREE)
+                               continue;
+
+                       /* long name */
+                       if ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
                                continue;
 
-                       if (dir[i].attr == FAT_ATTR_VOLUME) {
-                               dbg("found ATTR_VOLUME id in root dir");
+                       if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
+                               /* labels do not have file data */
+                               if (dir[i].cluster_high != 0 || dir[i].cluster_low != 0)
+                                       continue;
+
+                               dbg("found ATTR_VOLUME_ID id in root dir");
                                label = dir[i].name;
                                goto fat32_label;
                        }
@@ -1014,10 +1075,14 @@ fat32:
                dbg("reached maximum follow count of root cluster chain, give up");
 
 fat32_label:
+       vs = (struct vfat_super_block *) get_buffer(id, off, 0x200);
+       if (vs == NULL)
+               return -1;
+
        if (label != NULL && strncmp(label, "NO NAME    ", 11) != 0) {
                set_label_raw(id, label, 11);
                set_label_string(id, label, 11);
-       } else if (strncmp(vs->type.fat32.label, "NO NAME    ", 11) == 0) {
+       } else if (strncmp(vs->type.fat32.label, "NO NAME    ", 11) != 0) {
                set_label_raw(id, vs->type.fat32.label, 11);
                set_label_string(id, vs->type.fat32.label, 11);
        }
@@ -1184,6 +1249,12 @@ found:
 }
 
 #define ISO_SUPERBLOCK_OFFSET          0x8000
+#define ISO_SECTOR_SIZE                        0x800
+#define ISO_VD_OFFSET                  (ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_PRIMARY                 0x1
+#define ISO_VD_SUPPLEMENTARY           0x2
+#define ISO_VD_END                     0xff
+#define ISO_VD_MAX                     16
 static int probe_iso9660(struct volume_id *id, __u64 off)
 {
        union iso_super_block {
@@ -1209,8 +1280,37 @@ static int probe_iso9660(struct volume_id *id, __u64 off)
                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);
+               char root_label[VOLUME_ID_LABEL_SIZE+1];
+               int vd_offset;
+               int i;
+               int found_svd;
+
+               memset(root_label, 0, sizeof(root_label));
+               strncpy(root_label, is->iso.volume_id, sizeof(root_label)-1);
+
+               found_svd = 0;
+               vd_offset = ISO_VD_OFFSET;
+               for (i = 0; i < ISO_VD_MAX; i++) {
+                       is = (union iso_super_block *) 
+                            get_buffer (id, off + vd_offset, 0x200);
+                       if (is == NULL || is->iso.type == ISO_VD_END)
+                               break;
+                       if (is->iso.type == ISO_VD_SUPPLEMENTARY) {
+                               dbg("found ISO supplementary VD at offset 0x%llx", off + vd_offset);
+                               set_label_raw(id, is->iso.volume_id, 32);
+                               set_label_unicode16(id, is->iso.volume_id, BE, 32);
+                               found_svd = 1;
+                               break;
+                       }
+                       vd_offset += ISO_SECTOR_SIZE;
+               }
+
+               if (!found_svd ||
+                   (found_svd && !strncmp(root_label, id->label, 16)))
+               {
+                       set_label_raw(id, root_label, 32);
+                       set_label_string(id, root_label, 32);
+               }
                goto found;
        }
        if (strncmp(is->hs.id, "CDROM", 5) == 0)
@@ -1948,21 +2048,37 @@ found:
 #define LARGEST_PAGESIZE                       0x4000
 static int probe_swap(struct volume_id *id, __u64 off)
 {
-       const __u8 *sig;
+       struct swap_header_v1_2 {
+               __u8    bootbits[1024];
+               __u32   version;
+               __u32   last_page;
+               __u32   nr_badpages;
+               __u8    uuid[16];
+               __u8    volume_name[16];
+       } __attribute__((__packed__)) *sw;
+
+       const __u8 *buf;
        unsigned int page;
 
-       /* huhh, the swap signature is on the end of the PAGE_SIZE */
+       /* the swap signature is at the end of the PAGE_SIZE */
        for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
-                       sig = get_buffer(id, off + page-10, 10);
-                       if (sig == NULL)
+                       buf = get_buffer(id, off + page-10, 10);
+                       if (buf == NULL)
                                return -1;
 
-                       if (strncmp(sig, "SWAP-SPACE", 10) == 0) {
+                       if (strncmp(buf, "SWAP-SPACE", 10) == 0) {
                                strcpy(id->type_version, "1");
                                goto found;
                        }
-                       if (strncmp(sig, "SWAPSPACE2", 10) == 0) {
+
+                       if (strncmp(buf, "SWAPSPACE2", 10) == 0) {
+                               sw = (struct swap_header_v1_2 *) get_buffer(id, off, sizeof(struct swap_header_v1_2));
+                               if (sw == NULL)
+                                       return -1;
                                strcpy(id->type_version, "2");
+                               set_label_raw(id, sw->volume_name, 16);
+                               set_label_string(id, sw->volume_name, 16);
+                               set_uuid(id, sw->uuid, UUID_DCE);
                                goto found;
                        }
        }
@@ -1984,6 +2100,8 @@ int volume_id_probe(struct volume_id *id,
 {
        int rc;
 
+       dbg("called with size=0x%llx", size);
+
        if (id == NULL)
                return -EINVAL;
 
@@ -2038,6 +2156,9 @@ int volume_id_probe(struct volume_id *id,
        case VOLUME_ID_LVM2:
                rc = probe_lvm2(id, off);
                break;
+       case VOLUME_ID_HPTRAID:
+               rc = probe_highpoint_ataraid(id, off);
+               break;
        case VOLUME_ID_ALL:
        default:
                /* probe for raid first, cause fs probes may be successful on raid members */
@@ -2050,14 +2171,11 @@ int volume_id_probe(struct volume_id *id,
                rc = probe_lvm2(id, off);
                if (rc == 0)
                        break;
-
-               /* signature in the first block, only small buffer needed */
-               rc = probe_msdos_part_table(id, off);
-               if (rc == 0)
-                       break;
-               rc = probe_ntfs(id, off);
+               rc = probe_highpoint_ataraid(id, off);
                if (rc == 0)
                        break;
+
+               /* signature in the first block, only small buffer needed */
                rc = probe_vfat(id, off);
                if (rc == 0)
                        break;
@@ -2093,6 +2211,9 @@ int volume_id_probe(struct volume_id *id,
                if (rc == 0)
                        break;
                rc = probe_ufs(id, off);
+               if (rc == 0)
+                       break;
+               rc = probe_ntfs(id, off);
                if (rc == 0)
                        break;