OSDN Git Service

maint: don't hard-code bug-reporting address
[android-x86/external-parted.git] / libparted / labels / dos.c
index b83bcc2..578180b 100644 (file)
@@ -81,6 +81,7 @@ static const char MBR_BOOT_CODE[] = {
 #define PARTITION_FAT16_LBA_H  (PARTITION_FAT16_LBA    | PART_FLAG_HIDDEN)
 
 #define PARTITION_COMPAQ_DIAG  0x12
+#define PARTITION_MSFT_RECOVERY        0x27
 #define PARTITION_LDM          0x42
 #define PARTITION_LINUX_SWAP   0x82
 #define PARTITION_LINUX                0x83
@@ -157,11 +158,79 @@ typedef struct {
        int             lba;
        int             palo;
        int             prep;
+       int             diag;
        OrigState*      orig;                   /* used for CHS stuff */
 } DosPartitionData;
 
 static PedDiskType msdos_disk_type;
 
+#if 0
+From http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html
+
+The 2-byte numbers are stored little endian (low order byte first).
+
+Here the FAT12 version, that is also the common part of the FAT12, FAT16 and FAT32 boot sectors. See further below.
+
+Bytes   Content
+0-2     Jump to bootstrap (E.g. eb 3c 90; on i86: JMP 003E NOP.
+        One finds either eb xx 90, or e9 xx xx.
+        The position of the bootstrap varies.)
+3-10    OEM name/version (E.g. "IBM  3.3", "IBM 20.0", "MSDOS5.0", "MSWIN4.0".
+        Various format utilities leave their own name, like "CH-FOR18".
+        Sometimes just garbage. Microsoft recommends "MSWIN4.1".)
+        /* BIOS Parameter Block starts here */
+11-12   Number of bytes per sector (512)
+        Must be one of 512, 1024, 2048, 4096.
+13      Number of sectors per cluster (1)
+        Must be one of 1, 2, 4, 8, 16, 32, 64, 128.
+        A cluster should have at most 32768 bytes. In rare cases 65536 is OK.
+14-15   Number of reserved sectors (1)
+        FAT12 and FAT16 use 1. FAT32 uses 32.
+16      Number of FAT copies (2)
+17-18   Number of root directory entries (224)
+        0 for FAT32. 512 is recommended for FAT16.
+19-20   Total number of sectors in the filesystem (2880)
+        (in case the partition is not FAT32 and smaller than 32 MB)
+21      Media descriptor type (f0: 1.4 MB floppy, f8: hard disk; see below)
+22-23   Number of sectors per FAT (9)
+        0 for FAT32.
+24-25   Number of sectors per track (12)
+26-27   Number of heads (2, for a double-sided diskette)
+28-29   Number of hidden sectors (0)
+        Hidden sectors are sectors preceding the partition.
+        /* BIOS Parameter Block ends here */
+30-509  Bootstrap
+510-511 Signature 55 aa
+#endif
+
+/* There is a significant risk of misclassifying (as msdos)
+   a disk that is composed solely of a single FAT partition.
+   Return false if sector S could not be a valid FAT boot sector.
+   Otherwise, return true.  */
+static bool
+maybe_FAT (unsigned char const *s)
+{
+  if (! (s[0] == 0xeb || s[0] == 0xe9))
+    return false;
+
+  unsigned int sector_size = PED_LE16_TO_CPU (*(uint16_t *) (s + 11));
+  switch (sector_size)
+    {
+    case 512:
+    case 1024:
+    case 2048:
+    case 4096:
+      break;
+    default:
+      return false;
+    }
+
+  if (! (s[21] == 0xf0 || s[21] == 0xf8))
+    return false;
+
+  return true;
+}
+
 static int
 msdos_probe (const PedDevice *dev)
 {
@@ -189,12 +258,20 @@ msdos_probe (const PedDevice *dev)
         * and ensure that each partition has a boot indicator that is
         * either 0 or 0x80.
         */
+       unsigned int n_active = 0;
        for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
+               if (part_table->partitions[i].boot_ind == 0x80)
+                       ++n_active;
                if (part_table->partitions[i].boot_ind != 0
                    && part_table->partitions[i].boot_ind != 0x80)
                        goto probe_fail;
        }
 
+       /* If there are no active partitions and this is probably
+          a FAT file system, do not classify it as msdos.  */
+       if (n_active == 0 && maybe_FAT (label))
+         goto probe_fail;
+
        /* If this is a GPT disk, fail here */
        for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
                if (part_table->partitions[i].type == PARTITION_GPT)
@@ -828,6 +905,9 @@ raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part,
        dos_data = part->disk_specific;
        dos_data->system = raw_part->type;
        dos_data->boot = raw_part->boot_ind != 0;
+       dos_data->diag = raw_part->type == PARTITION_COMPAQ_DIAG ||
+                        raw_part->type == PARTITION_MSFT_RECOVERY ||
+                        raw_part->type == PARTITION_DELL_DIAG;
        dos_data->hidden = raw_part_is_hidden (raw_part);
        dos_data->raid = raw_part->type == PARTITION_LINUX_RAID;
        dos_data->lvm = raw_part->type == PARTITION_LINUX_LVM_OLD
@@ -1231,6 +1311,7 @@ msdos_partition_new (const PedDisk* disk, PedPartitionType part_type,
                dos_data->system = PARTITION_LINUX;
                dos_data->hidden = 0;
                dos_data->boot = 0;
+               dos_data->diag = 0;
                dos_data->raid = 0;
                dos_data->lvm = 0;
                dos_data->lba = 0;
@@ -1264,6 +1345,7 @@ msdos_partition_duplicate (const PedPartition* part)
        new_dos_data = (DosPartitionData*) new_part->disk_specific;
        new_dos_data->system = old_dos_data->system;
        new_dos_data->boot = old_dos_data->boot;
+       new_dos_data->diag = old_dos_data->diag;
        new_dos_data->hidden = old_dos_data->hidden;
        new_dos_data->raid = old_dos_data->raid;
        new_dos_data->lvm = old_dos_data->lvm;
@@ -1313,6 +1395,7 @@ msdos_partition_set_system (PedPartition* part,
                dos_data->hidden = 0;
 
        if (part->type & PED_PARTITION_EXTENDED) {
+               dos_data->diag = 0;
                dos_data->raid = 0;
                dos_data->lvm = 0;
                dos_data->palo = 0;
@@ -1324,6 +1407,15 @@ msdos_partition_set_system (PedPartition* part,
                return 1;
        }
 
+       if (dos_data->diag) {
+               /* Don't change the system if it already is a diag type,
+                  otherwise use Compaq as almost all vendors use that. */
+               if (dos_data->system != PARTITION_COMPAQ_DIAG &&
+                   dos_data->system != PARTITION_MSFT_RECOVERY &&
+                   dos_data->system != PARTITION_DELL_DIAG)
+                       dos_data->system = PARTITION_COMPAQ_DIAG;
+               return 1;
+       }
        if (dos_data->lvm) {
                dos_data->system = PARTITION_LINUX_LVM;
                return 1;
@@ -1368,6 +1460,17 @@ msdos_partition_set_system (PedPartition* part,
        return 1;
 }
 
+static void
+clear_flags (DosPartitionData *dos_data)
+{
+  dos_data->diag = 0;
+  dos_data->hidden = 0;
+  dos_data->lvm = 0;
+  dos_data->palo = 0;
+  dos_data->prep = 0;
+  dos_data->raid = 0;
+}
+
 static int
 msdos_partition_set_flag (PedPartition* part,
                           PedPartitionFlag flag, int state)
@@ -1409,23 +1512,21 @@ msdos_partition_set_flag (PedPartition* part,
                }
                return 1;
 
+       case PED_PARTITION_DIAG:
+               if (state)
+                       clear_flags (dos_data);
+               dos_data->diag = state;
+               return ped_partition_set_system (part, part->fs_type);
+
        case PED_PARTITION_RAID:
-               if (state) {
-                       dos_data->hidden = 0;
-                       dos_data->lvm = 0;
-                       dos_data->palo = 0;
-                       dos_data->prep = 0;
-               }
+               if (state)
+                       clear_flags (dos_data);
                dos_data->raid = state;
                return ped_partition_set_system (part, part->fs_type);
 
        case PED_PARTITION_LVM:
-               if (state) {
-                       dos_data->hidden = 0;
-                       dos_data->raid = 0;
-                       dos_data->palo = 0;
-                       dos_data->prep = 0;
-               }
+               if (state)
+                       clear_flags (dos_data);
                dos_data->lvm = state;
                return ped_partition_set_system (part, part->fs_type);
 
@@ -1434,20 +1535,14 @@ msdos_partition_set_flag (PedPartition* part,
                return ped_partition_set_system (part, part->fs_type);
 
        case PED_PARTITION_PALO:
-               if (state) {
-                       dos_data->hidden = 0;
-                       dos_data->raid = 0;
-                       dos_data->lvm = 0;
-               }
+               if (state)
+                       clear_flags (dos_data);
                dos_data->palo = state;
                return ped_partition_set_system (part, part->fs_type);
 
        case PED_PARTITION_PREP:
-               if (state) {
-                       dos_data->hidden = 0;
-                       dos_data->raid = 0;
-                       dos_data->lvm = 0;
-               }
+               if (state)
+                       clear_flags (dos_data);
                dos_data->prep = state;
                return ped_partition_set_system (part, part->fs_type);
 
@@ -1467,11 +1562,17 @@ msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
        dos_data = part->disk_specific;
        switch (flag) {
        case PED_PARTITION_HIDDEN:
-               return dos_data->hidden;
+               if (part->type == PED_PARTITION_EXTENDED)
+                       return 0;
+               else
+                       return dos_data->hidden;
 
        case PED_PARTITION_BOOT:
                return dos_data->boot;
 
+       case PED_PARTITION_DIAG:
+               return dos_data->diag;
+
        case PED_PARTITION_RAID:
                return dos_data->raid;
 
@@ -1498,12 +1599,18 @@ msdos_partition_is_flag_available (const PedPartition* part,
 {
        switch (flag) {
        case PED_PARTITION_HIDDEN:
+               if (part->type == PED_PARTITION_EXTENDED)
+                       return 0;
+               else
+                       return 1;
+
        case PED_PARTITION_BOOT:
        case PED_PARTITION_RAID:
        case PED_PARTITION_LVM:
        case PED_PARTITION_LBA:
        case PED_PARTITION_PALO:
        case PED_PARTITION_PREP:
+       case PED_PARTITION_DIAG:
                return 1;
 
        default:
@@ -1606,8 +1713,13 @@ _primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
                                        dev->length - min_geom->end))
                        return NULL;
        } else {
-               if (!ped_geometry_init (&start_geom, dev, cylinder_size,
-                                       dev->length - cylinder_size))
+               /* Use cylinder_size as the starting sector number
+                  when the device is large enough to accommodate that.
+                  Otherwise, use sector 1.  */
+               PedSector start = (cylinder_size < dev->length
+                                  ? cylinder_size : 1);
+               if (!ped_geometry_init (&start_geom, dev, start,
+                                       dev->length - start))
                        return NULL;
                if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
                        return NULL;
@@ -1739,7 +1851,11 @@ _get_min_extended_part_geom (const PedPartition* ext_part,
        min_geom = ped_geometry_duplicate (&walk->geom);
        if (!min_geom)
                return NULL;
-       ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size);
+       /* We must always allow at least two sectors at the start, to leave
+        * room for LILO.  See linux/fs/partitions/msdos.c.
+        */
+       ped_geometry_set_start (min_geom,
+                               walk->geom.start - PED_MAX (1 * head_size, 2));
 
        for (walk = ext_part->part_list; walk; walk = walk->next) {
                if (!ped_partition_is_active (walk) || walk->num == 5)
@@ -1811,7 +1927,7 @@ _logical_min_start_head (const PedPartition* part,
 
 /* Shamelessly copied and adapted from _partition_get_overlap_constraint
  * (in disk.c)
- * This should get ride of the infamous Assertion (metadata_length > 0) failed
+ * This should get rid of the infamous Assertion (metadata_length > 0) failed
  * bug for extended msdos disklabels generated by Parted.
  * 1) There always is a partition table at the start of ext_part, so we leave
  *    a one sector gap there.