]> err.no Git - util-linux/commitdiff
libblkid: add MS-DOS partitions support
authorKarel Zak <kzak@redhat.com>
Wed, 16 Sep 2009 14:10:43 +0000 (16:10 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 16 Sep 2009 14:10:43 +0000 (16:10 +0200)
Signed-off-by: Karel Zak <kzak@redhat.com>
shlibs/blkid/src/partitions/Makefile.am
shlibs/blkid/src/partitions/dos.c [new file with mode: 0644]
shlibs/blkid/src/partitions/dos.h [new file with mode: 0644]
shlibs/blkid/src/partitions/partitions.c
shlibs/blkid/src/partitions/partitions.h

index ebf98fe0de553d9636942b9c1badec907b66496c..906ba68c7c66dec22e4e6683352c40874d9da979 100644 (file)
@@ -14,4 +14,6 @@ libblkid_partitions_la_SOURCES = partitions.c \
                                solaris_x86.c \
                                sun.c \
                                sgi.c \
-                               mac.c
+                               mac.c \
+                               dos.c \
+                               dos.h
diff --git a/shlibs/blkid/src/partitions/dos.c b/shlibs/blkid/src/partitions/dos.c
new file mode 100644 (file)
index 0000000..d03e18e
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * MS-DOS partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Inspired by fdisk, partx, Linux kernel and libparted.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "dos.h"
+#include "aix.h"
+
+static const struct dos_subtypes {
+       unsigned char type;
+       const struct blkid_idinfo *id;
+} dos_nested[] = {
+       { BLKID_FREEBSD_PARTITION, &bsd_pt_idinfo },
+       { BLKID_NETBSD_PARTITION, &bsd_pt_idinfo },
+       { BLKID_OPENBSD_PARTITION, &bsd_pt_idinfo },
+       { BLKID_UNIXWARE_PARTITION, &unixware_pt_idinfo },
+       { BLKID_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
+};
+
+static inline int is_extended(struct dos_partition *p)
+{
+       return (p->sys_type == BLKID_DOS_EXTENDED_PARTITION ||
+               p->sys_type == BLKID_W95_EXTENDED_PARTITION ||
+               p->sys_type == BLKID_LINUX_EXTENDED_PARTITION);
+}
+
+static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+               uint32_t ex_start, uint32_t ex_size, int ssf)
+{
+       blkid_partlist ls = blkid_probe_get_partlist(pr);
+       uint32_t cur_start = ex_start, cur_size = ex_size;
+       unsigned char *data;
+       int ct_nodata = 0;      /* count ext.partitions without data partitions */
+       int i;
+
+       while (1) {
+               struct dos_partition *p, *p0;
+               uint32_t start, size;
+
+               if (++ct_nodata > 100)
+                       return 0;
+               data = blkid_probe_get_sector(pr, cur_start);
+               if (!data)
+                       goto leave;     /* malformed partition? */
+
+               if (!is_valid_mbr_signature(data))
+                       goto leave;
+
+               p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+               /* Usually, the first entry is the real data partition,
+                * the 2nd entry is the next extended partition, or empty,
+                * and the 3rd and 4th entries are unused.
+                * However, DRDOS sometimes has the extended partition as
+                * the first entry (when the data partition is empty),
+                * and OS/2 seems to use all four entries.
+                * -- Linux kernel fs/partitions/dos.c
+                *
+                * See also http://en.wikipedia.org/wiki/Extended_boot_record
+                */
+
+               /* Parse data partition */
+               for (p = p0, i = 0; i < 4; i++, p++) {
+                       uint32_t abs_start;
+
+                       /* the start is relative to the parental ext.partition */
+                       start = dos_partition_start(p) * ssf;
+                       size = dos_partition_size(p) * ssf;
+                       abs_start = cur_start + start;  /* absolute start */
+
+                       if (!size || is_extended(p))
+                               continue;
+                       if (i >= 2) {
+                               /* extra checks to detect real data on
+                                * 3rd and 4th entries */
+                               if (start + size > cur_size)
+                                       continue;
+                               if (abs_start < ex_start)
+                                       continue;
+                               if (abs_start + size > ex_start + ex_size)
+                                       continue;
+                       }
+                       if (!blkid_partlist_add_partition(ls, tab, p->sys_type,
+                                               abs_start, size))
+                               goto err;
+
+                       ct_nodata = 0;
+               }
+               /* The first nested ext.partition should be a link to the next
+                * logical partition. Everything other (recursive ext.partitions)
+                * is junk.
+                */
+               for (p = p0, i = 0; i < 4; i++, p++) {
+                       start = dos_partition_start(p) * ssf;
+                       size = dos_partition_size(p) * ssf;
+
+                       if (size && is_extended(p))
+                               break;
+               }
+               if (i == 4)
+                       goto leave;
+
+               cur_start = ex_start + start;
+               cur_size = size;
+       }
+leave:
+       return 0;
+err:
+       return -1;
+}
+
+static int probe_dos_pt(blkid_probe pr, const struct blkid_idmag *mag)
+{
+       int i;
+       int ssf;
+       blkid_parttable tab = NULL;
+       blkid_partlist ls;
+       struct dos_partition *p0, *p;
+       unsigned char *data;
+       uint32_t start, size;
+
+       data = blkid_probe_get_sector(pr, 0);
+       if (!data)
+               goto nothing;
+
+       /* ignore disks with AIX magic number -- for more details see aix.c */
+       if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0)
+               goto nothing;
+
+       p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+       /*
+        * Now that the 55aa signature is present, this is probably
+        * either the boot sector of a FAT filesystem or a DOS-type
+        * partition table. Reject this in case the boot indicator
+        * is not 0 or 0x80.
+        */
+       for (p = p0, i = 0; i < 4; i++, p++) {
+               if (p->boot_ind != 0 && p->boot_ind != 0x80)
+                       goto nothing;
+       }
+
+       /*
+        * GPT uses valid MBR
+        */
+       for (p = p0, i = 0; i < 4; i++, p++) {
+               if (p->sys_type == BLKID_GPT_PARTITION)
+                       goto nothing;
+       }
+
+       /*
+        * Well, all checks pass, it's MS-DOS partiton table
+        */
+       if (blkid_partitions_need_typeonly(pr))
+               /* caller does not ask for details about partitions */
+               return 0;
+
+       ls = blkid_probe_get_partlist(pr);
+
+       /* sector size factor (the start and size are in the real sectors, but
+        * we need to convert all sizes to 512 logical sectors
+        */
+       ssf = blkid_probe_get_sectorsize(pr) / 512;
+
+       /* allocate a new partition table */
+       tab = blkid_partlist_new_parttable(ls, "dos", BLKID_MSDOS_PT_OFFSET);
+       if (!tab)
+               goto err;
+
+       /* Parse primary partitions */
+       for (p = p0, i = 0; i < 4; i++, p++) {
+               start = dos_partition_start(p) * ssf;
+               size = dos_partition_size(p) * ssf;
+
+               if (!size)
+                       continue;
+               if (!blkid_partlist_add_partition(ls, tab, p->sys_type,
+                                                       start, size))
+                       goto err;
+       }
+
+       /* Linux uses partition numbers greater than 4
+        * for all logical partition and all nested partition tables (bsd, ..)
+        */
+       blkid_partlist_set_partno(ls, 5);
+
+       /* Parse logical partitions */
+       for (p = p0, i = 0; i < 4; i++, p++) {
+               start = dos_partition_start(p) * ssf;
+               size = dos_partition_size(p) * ssf;
+
+               if (!size)
+                       continue;
+               if (is_extended(p) &&
+                   parse_dos_extended(pr, tab, start, size, ssf) == -1)
+                       goto err;
+       }
+
+       /* Parse subtypes (nested partitions) */
+       for (p = p0, i = 0; i < 4; i++, p++) {
+               int n;
+
+               if (!dos_partition_size(p) || is_extended(p))
+                       continue;
+
+               for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+                       if (dos_nested[n].type != p->sys_type)
+                               continue;
+
+                       if (blkid_partitions_do_subprobe(pr,
+                                       blkid_partlist_get_partition(ls, i),
+                                       dos_nested[n].id) == -1)
+                               goto err;
+                       break;
+               }
+       }
+
+       return 0;
+
+nothing:
+       return 1;
+err:
+       return -1;
+}
+
+
+const struct blkid_idinfo dos_pt_idinfo =
+{
+       .name           = "dos",
+       .probefunc      = probe_dos_pt,
+       .magics         =
+       {
+               /* DOS master boot sector:
+                *
+                *     0 | Code Area
+                *   440 | Optional Disk signature
+                *   446 | Partition table
+                *   510 | 0x55
+                *   511 | 0xAA
+                */
+               { .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+               { NULL }
+       }
+};
+
diff --git a/shlibs/blkid/src/partitions/dos.h b/shlibs/blkid/src/partitions/dos.h
new file mode 100644 (file)
index 0000000..130aa01
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef BLKID_PARTITIONS_DOS_H
+#define BLKID_PARTITIONS_DOS_H
+
+struct dos_partition {
+       unsigned char boot_ind;         /* 0x80 - active */
+       unsigned char bh, bs, bc;       /* begin CHS */
+       unsigned char sys_type;
+       unsigned char eh, es, ec;       /* end CHS */
+       unsigned char start_sect[4];
+       unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define BLKID_MSDOS_PT_OFFSET          0x1be
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int assemble4le(unsigned char *p)
+{
+       return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline unsigned int dos_partition_start(struct dos_partition *p)
+{
+       return assemble4le(&(p->start_sect[0]));
+}
+
+static inline unsigned int dos_partition_size(struct dos_partition *p)
+{
+       return assemble4le(&(p->nr_sects[0]));
+}
+
+static inline int is_valid_mbr_signature(const unsigned char *mbr)
+{
+       return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+#endif /* BLKID_PARTITIONS_DOS_H */
index 2fd9e2a4cd29c0b49248d08404c51ba7e4b2e401..064ec69a88c70d5b2615fd8203a689dba0d46fe3 100644 (file)
@@ -105,6 +105,7 @@ static const struct blkid_idinfo *idinfos[] =
        &aix_pt_idinfo,
        &sgi_pt_idinfo,
        &sun_pt_idinfo,
+       &dos_pt_idinfo,
        &mac_pt_idinfo,
        &bsd_pt_idinfo,
        &unixware_pt_idinfo,
index fd8465dfba6361ef1ee5d09f9f2f4acd9d8dfc0d..205311a5f874acec25ac5058608995a975e0866b 100644 (file)
@@ -42,5 +42,6 @@ extern const struct blkid_idinfo solaris_x86_pt_idinfo;
 extern const struct blkid_idinfo sun_pt_idinfo;
 extern const struct blkid_idinfo sgi_pt_idinfo;
 extern const struct blkid_idinfo mac_pt_idinfo;
+extern const struct blkid_idinfo dos_pt_idinfo;
 
 #endif /* BLKID_PARTITIONS_H */