summaryrefslogtreecommitdiffstats
path: root/libparted/labels/dos.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--libparted/labels/dos.c2612
1 files changed, 2612 insertions, 0 deletions
diff --git a/libparted/labels/dos.c b/libparted/labels/dos.c
new file mode 100644
index 0000000..e6a0105
--- /dev/null
+++ b/libparted/labels/dos.c
@@ -0,0 +1,2612 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999-2001, 2004-2005, 2007-2014, 2019-2023 Free Software
+ Foundation, Inc.
+
+ 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; either version 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+
+#include <sys/time.h>
+#include <stdbool.h>
+#include <parted/parted.h>
+#include <parted/debug.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include "misc.h"
+#include "pt-tools.h"
+
+/* this MBR boot code is loaded into 0000:7c00 by the BIOS. See mbr.s for
+ * the source, and how to build it
+ */
+
+static const char MBR_BOOT_CODE[] = {
+ 0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00,
+ 0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
+ 0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9,
+ 0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00,
+ 0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b,
+ 0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75,
+ 0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb,
+ 0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b,
+ 0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00,
+ 0x00, 0xeb, 0xfe
+};
+
+#define MSDOS_MAGIC 0xAA55
+#define PARTITION_MAGIC_MAGIC 0xf6f6
+
+/* The maximum number of DOS primary partitions. */
+#define DOS_N_PRI_PARTITIONS 4
+
+#define PARTITION_EMPTY 0x00
+#define PARTITION_FAT12 0x01
+#define PARTITION_FAT16_SM 0x04
+#define PARTITION_DOS_EXT 0x05
+#define PARTITION_FAT16 0x06
+#define PARTITION_NTFS 0x07
+#define PARTITION_HPFS 0x07
+#define PARTITION_UDF 0x07
+#define PARTITION_FAT32 0x0b
+#define PARTITION_FAT32_LBA 0x0c
+#define PARTITION_FAT16_LBA 0x0e
+#define PARTITION_EXT_LBA 0x0f
+
+#define PART_FLAG_HIDDEN 0x10 /* Valid for FAT/NTFS only */
+#define PARTITION_FAT12_H (PARTITION_FAT12 | PART_FLAG_HIDDEN)
+#define PARTITION_FAT16_SM_H (PARTITION_FAT16_SM | PART_FLAG_HIDDEN)
+#define PARTITION_DOS_EXT_H (PARTITION_DOS_EXT | PART_FLAG_HIDDEN)
+#define PARTITION_FAT16_H (PARTITION_FAT16 | PART_FLAG_HIDDEN)
+#define PARTITION_NTFS_H (PARTITION_NTFS | PART_FLAG_HIDDEN)
+#define PARTITION_FAT32_H (PARTITION_FAT32 | PART_FLAG_HIDDEN)
+#define PARTITION_FAT32_LBA_H (PARTITION_FAT32_LBA | PART_FLAG_HIDDEN)
+#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
+#define PARTITION_IRST 0x84
+#define PARTITION_LINUX_EXT 0x85
+#define PARTITION_LINUX_LVM 0x8e
+#define PARTITION_HFS 0xaf
+#define PARTITION_SUN_UFS 0xbf
+#define PARTITION_DELL_DIAG 0xde
+#define PARTITION_BLS_BOOT 0xea
+#define PARTITION_GPT 0xee
+#define PARTITION_ESP 0xef
+#define PARTITION_PALO 0xf0
+#define PARTITION_PREP 0x41
+#define PARTITION_LINUX_RAID 0xfd
+#define PARTITION_LINUX_LVM_OLD 0xfe
+
+struct flag_id_mapping_t
+{
+ enum _PedPartitionFlag flag;
+ unsigned char type_id;
+ unsigned char alt_type_id;
+};
+
+static const struct flag_id_mapping_t flag_id_mapping[] =
+{
+ { PED_PARTITION_BLS_BOOT, PARTITION_BLS_BOOT },
+ { PED_PARTITION_DIAG, PARTITION_COMPAQ_DIAG, PARTITION_DELL_DIAG },
+ { PED_PARTITION_ESP, PARTITION_ESP },
+ { PED_PARTITION_IRST, PARTITION_IRST },
+ { PED_PARTITION_LVM, PARTITION_LINUX_LVM, PARTITION_LINUX_LVM_OLD },
+ { PED_PARTITION_MSFT_RESERVED, PARTITION_MSFT_RECOVERY },
+ { PED_PARTITION_PALO, PARTITION_PALO },
+ { PED_PARTITION_PREP, PARTITION_PREP },
+ { PED_PARTITION_RAID, PARTITION_LINUX_RAID },
+ { PED_PARTITION_SWAP, PARTITION_LINUX_SWAP },
+};
+
+static const unsigned char skip_set_system_types[] =
+{
+ PARTITION_EXT_LBA,
+ PARTITION_DOS_EXT,
+ PARTITION_COMPAQ_DIAG,
+ PARTITION_MSFT_RECOVERY,
+ PARTITION_LINUX_LVM,
+ PARTITION_LINUX_SWAP,
+ PARTITION_LINUX_RAID,
+ PARTITION_PALO,
+ PARTITION_PREP,
+ PARTITION_IRST,
+ PARTITION_ESP,
+ PARTITION_BLS_BOOT
+};
+
+static const struct flag_id_mapping_t* _GL_ATTRIBUTE_CONST
+dos_find_flag_id_mapping (PedPartitionFlag flag)
+{
+ int n = sizeof(flag_id_mapping) / sizeof(flag_id_mapping[0]);
+
+ for (int i = 0; i < n; ++i)
+ if (flag_id_mapping[i].flag == flag)
+ return &flag_id_mapping[i];
+
+ return NULL;
+}
+
+/**
+ * Check whether the type_id supports the hidden flag. Returns true for both hidden and
+ * non-hidden id.
+ */
+static bool
+dos_type_id_supports_hidden(unsigned char type_id)
+{
+ switch (type_id)
+ {
+ case PARTITION_DOS_EXT:
+ case PARTITION_DOS_EXT_H:
+ case PARTITION_FAT12:
+ case PARTITION_FAT12_H:
+ case PARTITION_FAT16:
+ case PARTITION_FAT16_H:
+ case PARTITION_FAT16_LBA:
+ case PARTITION_FAT16_LBA_H:
+ case PARTITION_FAT16_SM:
+ case PARTITION_FAT16_SM_H:
+ case PARTITION_FAT32:
+ case PARTITION_FAT32_H:
+ case PARTITION_FAT32_LBA:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_NTFS:
+ case PARTITION_NTFS_H:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Check whether the type_id has the hidden flag set.
+ */
+static bool
+dos_type_id_is_hidden(unsigned char type_id)
+{
+ switch (type_id)
+ {
+ case PARTITION_DOS_EXT_H:
+ case PARTITION_FAT12_H:
+ case PARTITION_FAT16_H:
+ case PARTITION_FAT16_LBA_H:
+ case PARTITION_FAT16_SM_H:
+ case PARTITION_FAT32_H:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_NTFS_H:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Sets the hidden flag on type_id.
+ */
+static bool
+dos_type_id_set_hidden(unsigned char* type_id, bool state)
+{
+ PED_ASSERT (type_id);
+
+ if (!dos_type_id_supports_hidden(*type_id))
+ return false;
+
+ if (state)
+ *type_id |= PART_FLAG_HIDDEN;
+ else
+ *type_id &= ~PART_FLAG_HIDDEN;
+
+ return 1;
+}
+
+/**
+ * Check whether the type_id supports the lba flag. Returns true for both lba and non-lba
+ * id.
+ */
+static bool
+dos_type_id_supports_lba(unsigned char type_id)
+{
+ switch (type_id)
+ {
+ case PARTITION_FAT16:
+ case PARTITION_FAT16_H:
+ case PARTITION_FAT16_LBA:
+ case PARTITION_FAT16_LBA_H:
+ case PARTITION_FAT32:
+ case PARTITION_FAT32_H:
+ case PARTITION_FAT32_LBA:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_DOS_EXT:
+ case PARTITION_EXT_LBA:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Check whether the type_id has the lba flag set.
+ */
+static bool
+dos_type_id_is_lba(unsigned char type_id)
+{
+ switch (type_id)
+ {
+ case PARTITION_FAT16_LBA:
+ case PARTITION_FAT16_LBA_H:
+ case PARTITION_FAT32_LBA:
+ case PARTITION_FAT32_LBA_H:
+ case PARTITION_EXT_LBA:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
+ * Sets the lba flag on type_id.
+ */
+static bool
+dos_type_id_set_lba(unsigned char* type_id, bool state)
+{
+ PED_ASSERT (type_id);
+
+ if (!dos_type_id_supports_lba(*type_id))
+ return false;
+
+ if (state)
+ {
+ switch (*type_id)
+ {
+ case PARTITION_FAT16:
+ *type_id = PARTITION_FAT16_LBA;
+ break;
+
+ case PARTITION_FAT32:
+ *type_id = PARTITION_FAT32_LBA;
+ break;
+
+ case PARTITION_DOS_EXT:
+ *type_id = PARTITION_EXT_LBA;
+ break;
+ }
+ }
+ else
+ {
+ switch (*type_id)
+ {
+ case PARTITION_FAT16_LBA:
+ *type_id = PARTITION_FAT16;
+ break;
+
+ case PARTITION_FAT32_LBA:
+ *type_id = PARTITION_FAT32;
+ break;
+
+ case PARTITION_EXT_LBA:
+ *type_id = PARTITION_DOS_EXT;
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+/* This constant contains the maximum cylinder number that can be represented
+ * in (C,H,S) notation. Higher cylinder numbers are reserved for
+ * "too big" indicators (in which case only LBA addressing can be used).
+ * Some partition tables in the wild indicate this number is 1021.
+ * (i.e. 1022 is sometimes used to indicate "use LBA").
+ */
+#define MAX_CHS_CYLINDER 1021
+#define MAX_TOTAL_PART 64
+
+typedef struct _DosRawPartition DosRawPartition;
+typedef struct _DosRawTable DosRawTable;
+
+/* note: lots of bit-bashing here, thus, you shouldn't look inside it.
+ * Use chs_to_sector() and sector_to_chs() instead.
+ */
+typedef struct {
+ uint8_t head;
+ uint8_t sector;
+ uint8_t cylinder;
+} __attribute__((packed)) RawCHS;
+
+/* ripped from Linux source */
+struct _DosRawPartition {
+ uint8_t boot_ind; /* 00: 0x80 - active */
+ RawCHS chs_start; /* 01: */
+ uint8_t type; /* 04: partition type */
+ RawCHS chs_end; /* 05: */
+ uint32_t start; /* 08: starting sector counting from 0 */
+ uint32_t length; /* 0c: nr of sectors in partition */
+} __attribute__((packed));
+
+struct _DosRawTable {
+ char boot_code [440];
+ uint32_t mbr_signature; /* really a unique ID */
+ uint16_t Unknown;
+ DosRawPartition partitions [DOS_N_PRI_PARTITIONS];
+ uint16_t magic;
+} __attribute__((packed));
+
+/* OrigState is information we want to preserve about the partition for
+ * dealing with CHS issues
+ */
+typedef struct {
+ PedGeometry geom;
+ DosRawPartition raw_part;
+ PedSector lba_offset; /* needed for computing start/end for
+ * logical partitions */
+} OrigState;
+
+typedef struct {
+ int cylinder_alignment;
+} DosDiskData;
+
+typedef struct {
+ unsigned char system;
+ int boot;
+ 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;
+
+ uint16_t sector_size = (s[12] << 8) | 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;
+}
+
+PedGeometry*
+fat_probe_fat16 (PedGeometry* geom);
+
+PedGeometry*
+fat_probe_fat32 (PedGeometry* geom);
+
+PedGeometry*
+ntfs_probe (PedGeometry* geom);
+
+static int
+msdos_probe (const PedDevice *dev)
+{
+ PedDiskType* disk_type;
+ DosRawTable* part_table;
+ int i;
+ PedGeometry *geom = NULL;
+ PedGeometry *fsgeom = NULL;
+
+ PED_ASSERT (dev != NULL);
+
+ if (dev->sector_size < sizeof *part_table)
+ return 0;
+
+ void *label;
+ if (!ptt_read_sector (dev, 0, &label))
+ return 0;
+
+ part_table = (DosRawTable *) label;
+
+ /* check magic */
+ if (PED_LE16_TO_CPU (part_table->magic) != MSDOS_MAGIC)
+ goto probe_fail;
+
+ geom = ped_geometry_new (dev, 0, dev->length);
+ PED_ASSERT (geom);
+ fsgeom = fat_probe_fat16 (geom);
+ if (fsgeom)
+ goto probe_fail; /* fat fs looks like dos mbr */
+ fsgeom = fat_probe_fat32 (geom);
+ if (fsgeom)
+ goto probe_fail; /* fat fs looks like dos mbr */
+ fsgeom = ntfs_probe (geom);
+ if (fsgeom)
+ goto probe_fail; /* ntfs fs looks like dos mbr */
+ ped_geometry_destroy (geom);
+ geom = NULL;
+
+ /* If this is a FAT fs, fail here. Checking for the FAT signature
+ * has some false positives; instead, do what the Linux kernel does
+ * 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)
+ goto probe_fail;
+ }
+
+ /* If this is an AIX Physical Volume, fail here. IBMA in EBCDIC */
+ if (part_table->boot_code[0] == (char) 0xc9 &&
+ part_table->boot_code[1] == (char) 0xc2 &&
+ part_table->boot_code[2] == (char) 0xd4 &&
+ part_table->boot_code[3] == (char) 0xc1)
+ goto probe_fail;
+
+#ifdef ENABLE_PC98
+ /* HACK: it's impossible to tell PC98 and msdos disk labels apart.
+ * Someone made the signatures the same (very clever). Since
+ * PC98 has some idiosyncracies with it's boot-loader, it's detection
+ * is more reliable */
+ disk_type = ped_disk_type_get ("pc98");
+ if (disk_type && disk_type->ops->probe (dev))
+ goto probe_fail;
+#endif /* ENABLE_PC98 */
+
+ free (label);
+ return 1;
+
+ probe_fail:
+ if (geom)
+ ped_geometry_destroy (geom);
+ if (fsgeom)
+ ped_geometry_destroy (fsgeom);
+ free (label);
+ return 0;
+}
+
+static PedDisk*
+msdos_alloc (const PedDevice* dev)
+{
+ PedDisk* disk;
+ PED_ASSERT (dev != NULL);
+
+ disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type);
+ if (disk) {
+ DosDiskData *disk_specific = ped_malloc(sizeof *disk_specific);
+ if (!disk_specific) {
+ free (disk);
+ return NULL;
+ }
+ disk_specific->cylinder_alignment = 1;
+ disk->disk_specific = disk_specific;
+ }
+
+ return disk;
+}
+
+static PedDisk*
+msdos_duplicate (const PedDisk* disk)
+{
+ PedDisk* new_disk;
+
+ new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type);
+ if (!new_disk)
+ return NULL;
+
+ memcpy(new_disk->disk_specific, disk->disk_specific,
+ sizeof(DosDiskData));
+
+ return new_disk;
+}
+
+static void
+msdos_free (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL);
+
+ DosDiskData *disk_specific = disk->disk_specific;
+ _ped_disk_free (disk);
+ free(disk_specific);
+}
+
+static int
+msdos_disk_set_flag (PedDisk *disk, PedDiskFlag flag, int state)
+{
+ DosDiskData *disk_specific = disk->disk_specific;
+ switch (flag) {
+ case PED_DISK_CYLINDER_ALIGNMENT:
+ disk_specific->cylinder_alignment = !!state;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+msdos_disk_get_flag (const PedDisk *disk, PedDiskFlag flag)
+{
+ DosDiskData *disk_specific = disk->disk_specific;
+ switch (flag) {
+ case PED_DISK_CYLINDER_ALIGNMENT:
+ return disk_specific->cylinder_alignment;
+ default:
+ return 0;
+ }
+}
+
+static int
+msdos_disk_is_flag_available (const PedDisk *disk, PedDiskFlag flag)
+{
+ switch (flag) {
+ case PED_DISK_CYLINDER_ALIGNMENT:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+chs_get_cylinder (const RawCHS* chs)
+{
+ return chs->cylinder + ((chs->sector >> 6) << 8);
+}
+
+static int
+chs_get_head (const RawCHS* chs)
+{
+ return chs->head;
+}
+
+/* counts from 0 */
+static int
+chs_get_sector (const RawCHS* chs)
+{
+ return (chs->sector & 0x3f) - 1;
+}
+
+static PedSector _GL_ATTRIBUTE_PURE
+chs_to_sector (const PedDevice* dev, const PedCHSGeometry *bios_geom,
+ const RawCHS* chs)
+{
+ PedSector c; /* not measured in sectors, but need */
+ PedSector h; /* lots of bits */
+ PedSector s;
+
+ PED_ASSERT (bios_geom != NULL);
+ PED_ASSERT (chs != NULL);
+
+ c = chs_get_cylinder (chs);
+ h = chs_get_head (chs);
+ s = chs_get_sector (chs);
+
+ if (c > MAX_CHS_CYLINDER) /* MAGIC: C/H/S is irrelevant */
+ return 0;
+ if (s < 0)
+ return 0;
+ return (c * bios_geom->heads + h) * bios_geom->sectors + s;
+}
+
+static void
+sector_to_chs (const PedDevice* dev, const PedCHSGeometry* bios_geom,
+ PedSector sector, RawCHS* chs)
+{
+ PedSector real_c, real_h, real_s;
+
+ PED_ASSERT (dev != NULL);
+ PED_ASSERT (chs != NULL);
+
+ if (!bios_geom)
+ bios_geom = &dev->bios_geom;
+
+ real_c = sector / (bios_geom->heads * bios_geom->sectors);
+ real_h = (sector / bios_geom->sectors) % bios_geom->heads;
+ real_s = sector % bios_geom->sectors;
+
+ if (real_c > MAX_CHS_CYLINDER) {
+ real_c = 1023;
+ real_h = bios_geom->heads - 1;
+ real_s = bios_geom->sectors - 1;
+ }
+
+ chs->cylinder = real_c % 0x100;
+ chs->head = real_h;
+ chs->sector = real_s + 1 + (real_c >> 8 << 6);
+}
+
+static PedSector _GL_ATTRIBUTE_PURE
+legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ const DosRawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (raw_part != NULL);
+
+ return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start);
+}
+
+static PedSector _GL_ATTRIBUTE_PURE
+legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ const DosRawPartition* raw_part)
+{
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (raw_part != NULL);
+
+ return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end);
+}
+
+static PedSector _GL_ATTRIBUTE_PURE
+linear_start (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector offset)
+{
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (raw_part != NULL);
+
+ return offset + PED_LE32_TO_CPU (raw_part->start);
+}
+
+static PedSector _GL_ATTRIBUTE_PURE
+linear_end (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector offset)
+{
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (raw_part != NULL);
+
+ return (linear_start (disk, raw_part, offset)
+ + (PED_LE32_TO_CPU (raw_part->length) - 1));
+}
+
+#ifndef DISCOVER_ONLY
+static int _GL_ATTRIBUTE_PURE
+partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ PedSector leg_start, leg_end;
+ DosPartitionData* dos_data;
+ PedDisk* disk;
+
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+ dos_data = part->disk_specific;
+
+ if (!dos_data->orig)
+ return 1;
+
+ disk = part->disk;
+ leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part);
+ leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part);
+
+ if (leg_start && leg_start != dos_data->orig->geom.start)
+ return 0;
+ if (leg_end && leg_end != dos_data->orig->geom.end)
+ return 0;
+ return 1;
+}
+
+static int _GL_ATTRIBUTE_PURE
+disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
+{
+ PedPartition* part = NULL;
+
+ PED_ASSERT (disk != NULL);
+
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (!partition_check_bios_geometry (part, bios_geom))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL};
+ int i;
+ int found;
+ unsigned char* buf;
+ int sectors;
+ int heads;
+ int res = 0;
+
+ PED_ASSERT (bios_geom != NULL);
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk != NULL);
+ PED_ASSERT (part->disk->dev != NULL);
+ PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0);
+
+ buf = ped_malloc (part->disk->dev->sector_size);
+
+ if (!buf)
+ return 0;
+
+ if (!part->fs_type)
+ goto end;
+
+ found = 0;
+ for (i = 0; ms_types[i]; i++) {
+ if (!strcmp(ms_types[i], part->fs_type->name))
+ found = 1;
+ }
+ if (!found)
+ goto end;
+
+ if (!ped_geometry_read(&part->geom, buf, 0, 1))
+ goto end;
+
+ /* shared by the start of all Microsoft file systems */
+ sectors = buf[0x18] + (buf[0x19] << 8);
+ heads = buf[0x1a] + (buf[0x1b] << 8);
+
+ if (sectors < 1 || sectors > 63)
+ goto end;
+ if (heads > 255 || heads < 1)
+ goto end;
+
+ bios_geom->sectors = sectors;
+ bios_geom->heads = heads;
+ bios_geom->cylinders = part->disk->dev->length / (sectors * heads);
+ res = 1;
+end:
+ free(buf);
+ return res;
+}
+
+/* This function attempts to infer the BIOS CHS geometry of the hard disk
+ * from the CHS + LBA information contained in the partition table from
+ * a single partition's entry.
+ *
+ * This involves some maths. Let (c,h,s,a) be the starting cylinder,
+ * starting head, starting sector and LBA start address of the partition.
+ * Likewise, (C,H,S,A) the end addresses. Using both of these pieces
+ * of information, we want to deduce cyl_sectors and head_sectors which
+ * are the sizes of a single cylinder and a single head, respectively.
+ *
+ * The relationships are:
+ * c*cyl_sectors + h * head_sectors + s = a
+ * C*cyl_sectors + H * head_sectors + S = A
+ *
+ * We can rewrite this in matrix form:
+ *
+ * [ c h ] [ cyl_sectors ] = [ s - a ] = [ a_ ]
+ * [ C H ] [ head_sectors ] [ S - A ] [ A_ ].
+ *
+ * (s - a is abbreviated to a_to simplify the notation.)
+ *
+ * This can be abbreviated into augmented matrix form:
+ *
+ * [ c h | a_ ]
+ * [ C H | A_ ].
+ *
+ * Solving these equations requires following the row reduction algorithm. We
+ * need to be careful about a few things though:
+ * - the equations might be linearly dependent, in which case there
+ * are many solutions.
+ * - the equations might be inconsistent, in which case there
+ * are no solutions. (Inconsistent partition table entry!)
+ * - there might be zeros, so we need to be careful about applying
+ * the algorithm. We know, however, that C > 0.
+ */
+static int
+probe_partition_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
+{
+ DosPartitionData* dos_data;
+ RawCHS* start_chs;
+ RawCHS* end_chs;
+ PedSector c, h, s, a, a_; /* start */
+ PedSector C, H, S, A, A_; /* end */
+ PedSector dont_overflow, denum;
+ PedSector cyl_size, head_size;
+ PedSector cylinders, heads, sectors;
+
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+ PED_ASSERT (bios_geom != NULL);
+
+ dos_data = part->disk_specific;
+
+ if (!dos_data->orig)
+ return 0;
+
+ start_chs = &dos_data->orig->raw_part.chs_start;
+ c = chs_get_cylinder (start_chs);
+ h = chs_get_head (start_chs);
+ s = chs_get_sector (start_chs);
+ a = dos_data->orig->geom.start;
+ a_ = a - s;
+
+ end_chs = &dos_data->orig->raw_part.chs_end;
+ C = chs_get_cylinder (end_chs);
+ H = chs_get_head (end_chs);
+ S = chs_get_sector (end_chs);
+ A = dos_data->orig->geom.end;
+ A_ = A - S;
+
+ if (h < 0 || H < 0 || h > 254 || H > 254)
+ return 0;
+ if (c > C)
+ return 0;
+
+ /* If no geometry is feasible, then don't even bother.
+ * Useful for eliminating assertions for broken partition
+ * tables generated by Norton Ghost et al.
+ */
+ if (A > (C+1) * 255 * 63)
+ return 0;
+
+ /* Not enough information. In theory, we can do better. Should we? */
+ if (C > MAX_CHS_CYLINDER)
+ return 0;
+ if (C == 0)
+ return 0;
+
+ /* Calculate the maximum number that can be multiplied by
+ * any head count without overflowing a PedSector
+ * 2^8 = 256, 8 bits + 1(sign bit) = 9
+ */
+ dont_overflow = 1;
+ dont_overflow <<= (8*sizeof(dont_overflow)) - 9;
+ dont_overflow--;
+
+ if (a_ > dont_overflow || A_ > dont_overflow)
+ return 0;
+
+ /* The matrix is solved by :
+ *
+ * [ c h | a_] R1
+ * [ C H | A_] R2
+ *
+ * (cH - Ch) cyl_size = a_H - A_h H R1 - h R2
+ * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch)
+ *
+ * (Hc - hC) head_size = A_c - a_C c R2 - C R1
+ * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch)
+ *
+ * But this calculation of head_size would need
+ * not overflowing A_c or a_C
+ * So substitution is use instead, to minimize dimension
+ * of temporary results :
+ *
+ * If h != 0 : head_size = ( a_ - c cyl_size ) / h
+ * If H != 0 : head_size = ( A_ - C cyl_size ) / H
+ *
+ */
+ denum = c * H - C * h;
+ if (denum == 0)
+ return 0;
+
+ cyl_size = (a_*H - A_*h) / denum;
+ /* Check for non integer result */
+ if (cyl_size * denum != a_*H - A_*h)
+ return 0;
+
+ if (!(cyl_size > 0))
+ return 0;
+ if (!(cyl_size <= 255 * 63))
+ return 0;
+
+ if (h > 0)
+ head_size = ( a_ - c * cyl_size ) / h;
+ else if (H > 0)
+ head_size = ( A_ - C * cyl_size ) / H;
+ else {
+ /* should not happen because denum != 0 */
+ PED_ASSERT (0);
+ }
+
+ if (!(head_size > 0))
+ return 0;
+ if (!(head_size <= 63))
+ return 0;
+
+ cylinders = part->disk->dev->length / cyl_size;
+ heads = cyl_size / head_size;
+ sectors = head_size;
+
+ if (!(heads > 0))
+ return 0;
+ if (!(heads < 256))
+ return 0;
+
+ if (!(sectors > 0))
+ return 0;
+ if (!(sectors <= 63))
+ return 0;
+
+ /* Some broken OEM partitioning program(s) seem to have an out-by-one
+ * error on the end of partitions. We should offer to fix the
+ * partition table...
+ */
+ if (((C + 1) * heads + H) * sectors + S == A)
+ C++;
+
+ if (!((c * heads + h) * sectors + s == a))
+ return 0;
+ if (!((C * heads + H) * sectors + S == A))
+ return 0;
+
+ bios_geom->cylinders = cylinders;
+ bios_geom->heads = heads;
+ bios_geom->sectors = sectors;
+
+ return 1;
+}
+
+static void
+partition_probe_bios_geometry (const PedPartition* part,
+ PedCHSGeometry* bios_geom)
+{
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk != NULL);
+ PED_ASSERT (bios_geom != NULL);
+
+ if (ped_partition_is_active (part)) {
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ if (part->type & PED_PARTITION_EXTENDED) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ }
+ }
+ if (part->type & PED_PARTITION_LOGICAL) {
+ PedPartition* ext_part;
+ ext_part = ped_disk_extended_partition (part->disk);
+ PED_ASSERT (ext_part != NULL);
+ partition_probe_bios_geometry (ext_part, bios_geom);
+ } else {
+ *bios_geom = part->disk->dev->bios_geom;
+ }
+}
+
+static void
+disk_probe_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
+{
+ PedPartition* part;
+
+ /* first look at the boot partition */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (!ped_partition_is_active (part))
+ continue;
+ if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ }
+ }
+
+ /* that didn't work... try all partition table entries */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (probe_partition_for_geom (part, bios_geom))
+ return;
+ }
+ }
+
+ /* that didn't work... look at all file systems */
+ part = NULL;
+ while ((part = ped_disk_next_partition (disk, part))) {
+ if (ped_partition_is_active (part)) {
+ if (probe_filesystem_for_geom (part, bios_geom))
+ return;
+ }
+ }
+}
+#endif /* !DISCOVER_ONLY */
+
+static int _GL_ATTRIBUTE_PURE
+raw_part_is_extended (const DosRawPartition* raw_part)
+{
+ PED_ASSERT (raw_part != NULL);
+
+ switch (raw_part->type) {
+ case PARTITION_DOS_EXT:
+ case PARTITION_EXT_LBA:
+ case PARTITION_LINUX_EXT:
+ return 1;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static PedPartition*
+raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part,
+ PedSector lba_offset, PedPartitionType type)
+{
+ PedPartition* part;
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (raw_part != NULL);
+
+ part = ped_partition_new (
+ disk, type, NULL,
+ linear_start (disk, raw_part, lba_offset),
+ linear_end (disk, raw_part, lba_offset));
+ if (!part)
+ return NULL;
+ dos_data = part->disk_specific;
+ dos_data->system = raw_part->type;
+ dos_data->boot = raw_part->boot_ind != 0;
+ dos_data->orig = ped_malloc (sizeof (OrigState));
+ if (!dos_data->orig) {
+ ped_partition_destroy (part);
+ return NULL;
+ }
+ dos_data->orig->geom = part->geom;
+ dos_data->orig->raw_part = *raw_part;
+ dos_data->orig->lba_offset = lba_offset;
+ return part;
+}
+
+static int
+read_table (PedDisk* disk, PedSector sector, int is_extended_table)
+{
+ int i;
+ DosRawTable* table;
+ DosRawPartition* raw_part;
+ PedPartition* part;
+ PedPartitionType type;
+ PedSector lba_offset;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+
+ void *label = NULL;
+ if (!ptt_read_sector (disk->dev, sector, &label))
+ goto error;
+
+ table = (DosRawTable *) label;
+
+ /* weird: empty extended partitions are filled with 0xf6 by PM */
+ if (is_extended_table
+ && PED_LE16_TO_CPU (table->magic) == PARTITION_MAGIC_MAGIC)
+ goto read_ok;
+
+#ifndef DISCOVER_ONLY
+ if (PED_LE16_TO_CPU (table->magic) != MSDOS_MAGIC) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid partition table on %s "
+ "-- wrong signature %x."),
+ disk->dev->path,
+ PED_LE16_TO_CPU (table->magic))
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ goto read_ok;
+ }
+#endif
+
+ /* parse the partitions from this table */
+ for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
+ raw_part = &table->partitions [i];
+ if (raw_part->type == PARTITION_EMPTY || !raw_part->length)
+ continue;
+
+ /* process nested extended partitions after normal logical
+ * partitions, to make sure we get the order right.
+ */
+ if (is_extended_table && raw_part_is_extended (raw_part))
+ continue;
+
+ lba_offset = is_extended_table ? sector : 0;
+
+ if (linear_start (disk, raw_part, lba_offset) == sector) {
+ if (ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_IGNORE_CANCEL,
+ _("Invalid partition table - recursive "
+ "partition on %s."),
+ disk->dev->path)
+ != PED_EXCEPTION_IGNORE)
+ goto error;
+ continue; /* avoid infinite recursion */
+ }
+
+ if (is_extended_table)
+ type = PED_PARTITION_LOGICAL;
+ else if (raw_part_is_extended (raw_part))
+ type = PED_PARTITION_EXTENDED;
+ else
+ type = PED_PARTITION_NORMAL;
+
+ part = raw_part_parse (disk, raw_part, lba_offset, type);
+ if (!part)
+ goto error;
+ if (!is_extended_table)
+ part->num = i + 1;
+ if (type != PED_PARTITION_EXTENDED)
+ part->fs_type = ped_file_system_probe (&part->geom);
+
+ PedConstraint *constraint_exact
+ = ped_constraint_exact (&part->geom);
+ bool ok = ped_disk_add_partition (disk, part, constraint_exact);
+ ped_constraint_destroy (constraint_exact);
+ if (!ok)
+ goto error;
+
+ /* non-nested extended partition */
+ if (part->type == PED_PARTITION_EXTENDED) {
+ if (!read_table (disk, part->geom.start, 1))
+ goto error;
+ }
+ }
+
+ if (is_extended_table) {
+ /* process the nested extended partitions */
+ for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
+ PedSector part_start;
+
+ raw_part = &table->partitions [i];
+ if (!raw_part_is_extended (raw_part))
+ continue;
+
+ lba_offset = ped_disk_extended_partition
+ (disk)->geom.start;
+ part_start = linear_start (disk, raw_part, lba_offset);
+ if (part_start == sector) {
+ /* recursive table - already threw an
+ * exception above.
+ */
+ continue;
+ }
+ if (!read_table (disk, part_start, 1))
+ goto error;
+ }
+ }
+
+read_ok:
+ free (label);
+ return 1;
+
+error:
+ free (label);
+ ped_disk_delete_all (disk);
+ return 0;
+}
+
+static int
+msdos_read (PedDisk* disk)
+{
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+
+ ped_disk_delete_all (disk);
+ if (!read_table (disk, 0, 0))
+ return 0;
+
+#ifndef DISCOVER_ONLY
+ /* try to figure out the correct BIOS CHS values */
+ if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) {
+ PedCHSGeometry bios_geom = disk->dev->bios_geom;
+ disk_probe_bios_geometry (disk, &bios_geom);
+
+ /* if the geometry was wrong, then we should reread, to
+ * make sure the metadata is allocated in the right places.
+ */
+ if (disk->dev->bios_geom.cylinders != bios_geom.cylinders
+ || disk->dev->bios_geom.heads != bios_geom.heads
+ || disk->dev->bios_geom.sectors != bios_geom.sectors) {
+ disk->dev->bios_geom = bios_geom;
+ return msdos_read (disk);
+ }
+ }
+#endif
+
+ return 1;
+}
+
+#ifndef DISCOVER_ONLY
+static int
+fill_raw_part (DosRawPartition* raw_part,
+ const PedPartition* part, PedSector offset)
+{
+ DosPartitionData* dos_data;
+ PedCHSGeometry bios_geom;
+
+ PED_ASSERT (raw_part != NULL);
+ PED_ASSERT (part != NULL);
+
+ partition_probe_bios_geometry (part, &bios_geom);
+
+ dos_data = part->disk_specific;
+
+ raw_part->boot_ind = 0x80 * dos_data->boot;
+ raw_part->type = dos_data->system;
+ raw_part->start = PED_CPU_TO_LE32 (part->geom.start - offset);
+ raw_part->length = PED_CPU_TO_LE32 (part->geom.length);
+
+ sector_to_chs (part->disk->dev, &bios_geom, part->geom.start,
+ &raw_part->chs_start);
+ sector_to_chs (part->disk->dev, &bios_geom, part->geom.end,
+ &raw_part->chs_end);
+
+ if (dos_data->orig) {
+ DosRawPartition* orig_raw_part = &dos_data->orig->raw_part;
+ if (dos_data->orig->geom.start == part->geom.start)
+ raw_part->chs_start = orig_raw_part->chs_start;
+ if (dos_data->orig->geom.end == part->geom.end)
+ raw_part->chs_end = orig_raw_part->chs_end;
+ }
+
+ return 1;
+}
+
+static int
+fill_ext_raw_part_geom (DosRawPartition* raw_part,
+ const PedCHSGeometry* bios_geom,
+ const PedGeometry* geom, PedSector offset)
+{
+ PED_ASSERT (raw_part != NULL);
+ PED_ASSERT (geom != NULL);
+ PED_ASSERT (geom->dev != NULL);
+
+ raw_part->boot_ind = 0;
+ raw_part->type = PARTITION_DOS_EXT;
+ raw_part->start = PED_CPU_TO_LE32 (geom->start - offset);
+ raw_part->length = PED_CPU_TO_LE32 (geom->length);
+
+ sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start);
+ sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1,
+ &raw_part->chs_end);
+
+ return 1;
+}
+
+static int
+write_ext_table (const PedDisk* disk,
+ PedSector sector, const PedPartition* logical)
+{
+ PedPartition* part;
+ PedSector lba_offset;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (ped_disk_extended_partition (disk) != NULL);
+ PED_ASSERT (logical != NULL);
+
+ lba_offset = ped_disk_extended_partition (disk)->geom.start;
+
+ void* s;
+ if (!ptt_read_sector (disk->dev, sector, &s))
+ return 0;
+
+ DosRawTable *table = s;
+ memset(&(table->partitions), 0, sizeof (table->partitions));
+ table->magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ int ok = 0;
+ if (!fill_raw_part (&table->partitions[0], logical, sector))
+ goto cleanup;
+
+ part = ped_disk_get_partition (disk, logical->num + 1);
+ if (part) {
+ PedGeometry* geom;
+ PedCHSGeometry bios_geom;
+
+ geom = ped_geometry_new (disk->dev, part->prev->geom.start,
+ part->geom.end - part->prev->geom.start + 1);
+ if (!geom)
+ goto cleanup;
+ partition_probe_bios_geometry (part, &bios_geom);
+ fill_ext_raw_part_geom (&table->partitions[1], &bios_geom,
+ geom, lba_offset);
+ ped_geometry_destroy (geom);
+
+ if (!write_ext_table (disk, part->prev->geom.start, part))
+ goto cleanup;
+ }
+
+ ok = ped_device_write (disk->dev, table, sector, 1);
+ cleanup:
+ free (s);
+ return ok;
+}
+
+static int
+write_empty_table (const PedDisk* disk, PedSector sector)
+{
+ DosRawTable table;
+ void* table_sector;
+
+ PED_ASSERT (disk != NULL);
+
+ if (ptt_read_sector (disk->dev, sector, &table_sector)) {
+ memcpy (&table, table_sector, sizeof (table));
+ free(table_sector);
+ }
+ memset (&(table.partitions), 0, sizeof (table.partitions));
+ table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ return ped_device_write (disk->dev, (void*) &table, sector, 1);
+}
+
+/* Find the first logical partition, and write the partition table for it.
+ */
+static int
+write_extended_partitions (const PedDisk* disk)
+{
+ PedPartition* ext_part;
+ PedPartition* part;
+ PedCHSGeometry bios_geom;
+
+ PED_ASSERT (disk != NULL);
+
+ ext_part = ped_disk_extended_partition (disk);
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ part = ped_disk_get_partition (disk, 5);
+ if (part)
+ return write_ext_table (disk, ext_part->geom.start, part);
+ else
+ return write_empty_table (disk, ext_part->geom.start);
+}
+
+static int
+msdos_write (const PedDisk* disk)
+{
+ PedPartition* part;
+ int i;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+
+ void *s0;
+ if (!ptt_read_sector (disk->dev, 0, &s0))
+ return 0;
+ DosRawTable *table = (DosRawTable *) s0;
+
+ if (!table->boot_code[0]) {
+ memset (table, 0, 512);
+ memcpy (table->boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE));
+ }
+
+ /* If there is no unique identifier, generate a random one */
+ if (!table->mbr_signature)
+ table->mbr_signature = generate_random_uint32 ();
+
+ memset (table->partitions, 0, sizeof (table->partitions));
+ table->magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
+
+ for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) {
+ part = ped_disk_get_partition (disk, i);
+ if (!part)
+ continue;
+
+ if (!fill_raw_part (&table->partitions [i - 1], part, 0))
+ goto write_fail;
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ if (!write_extended_partitions (disk))
+ goto write_fail;
+ }
+ }
+
+ int write_ok = ped_device_write (disk->dev, (void*) table, 0, 1);
+ free (s0);
+ if (!write_ok)
+ return 0;
+ return ped_device_sync (disk->dev);
+
+ write_fail:
+ free (s0);
+ return 0;
+
+}
+#endif /* !DISCOVER_ONLY */
+
+static PedPartition*
+msdos_partition_new (const PedDisk* disk, PedPartitionType part_type,
+ const PedFileSystemType* fs_type,
+ PedSector start, PedSector end)
+{
+ PedPartition* part;
+ DosPartitionData* dos_data;
+
+ part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
+ if (!part)
+ goto error;
+
+ if (ped_partition_is_active (part)) {
+ part->disk_specific
+ = dos_data = ped_calloc (sizeof (DosPartitionData));
+ if (!dos_data)
+ goto error_free_part;
+ dos_data->system = PARTITION_LINUX;
+ } else {
+ part->disk_specific = NULL;
+ }
+ return part;
+
+error_free_part:
+ free (part);
+error:
+ return 0;
+}
+
+static PedPartition*
+msdos_partition_duplicate (const PedPartition* part)
+{
+ PedPartition* new_part;
+ DosPartitionData* new_dos_data;
+ DosPartitionData* old_dos_data;
+
+ new_part = ped_partition_new (part->disk, part->type, part->fs_type,
+ part->geom.start, part->geom.end);
+ if (!new_part)
+ return NULL;
+ new_part->num = part->num;
+
+ old_dos_data = (DosPartitionData*) part->disk_specific;
+ new_dos_data = (DosPartitionData*) new_part->disk_specific;
+ new_dos_data->system = old_dos_data->system;
+ new_dos_data->boot = old_dos_data->boot;
+
+ if (old_dos_data->orig) {
+ new_dos_data->orig = ped_malloc (sizeof (OrigState));
+ if (!new_dos_data->orig) {
+ ped_partition_destroy (new_part);
+ return NULL;
+ }
+ new_dos_data->orig->geom = old_dos_data->orig->geom;
+ new_dos_data->orig->raw_part = old_dos_data->orig->raw_part;
+ new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset;
+ }
+ return new_part;
+}
+
+static void
+msdos_partition_destroy (PedPartition* part)
+{
+ PED_ASSERT (part != NULL);
+
+ if (ped_partition_is_active (part)) {
+ DosPartitionData* dos_data;
+ dos_data = (DosPartitionData*) part->disk_specific;
+ free (dos_data->orig);
+ free (part->disk_specific);
+ }
+ free (part);
+}
+
+/* is_skip_type checks the type against the list of types that should not be
+ * overridden by set_system. It returns a 1 if it is in the list.
+*/
+static bool
+is_skip_type(unsigned char type_id) {
+ int n = sizeof(skip_set_system_types) / sizeof(skip_set_system_types[0]);
+ for (int i = 0; i < n; ++i) {
+ if (type_id == skip_set_system_types[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int
+msdos_partition_set_system (PedPartition* part,
+ const PedFileSystemType* fs_type)
+{
+ DosPartitionData* dos_data = part->disk_specific;
+
+ part->fs_type = fs_type;
+
+ // Is this a type that should skip fs_type checking?
+ if (is_skip_type(dos_data->system)) {
+ return 1;
+ }
+
+ if (part->type & PED_PARTITION_EXTENDED) {
+ dos_data->system = PARTITION_EXT_LBA;
+ return 1;
+ }
+
+ if (!fs_type)
+ dos_data->system = PARTITION_LINUX;
+ else if (!strcmp (fs_type->name, "fat16"))
+ dos_data->system = PARTITION_FAT16;
+ else if (!strcmp (fs_type->name, "fat32"))
+ dos_data->system = PARTITION_FAT32;
+ else if (!strcmp (fs_type->name, "ntfs")
+ || !strcmp (fs_type->name, "hpfs"))
+ dos_data->system = PARTITION_NTFS;
+ else if (!strcmp (fs_type->name, "hfs")
+ || !strcmp (fs_type->name, "hfs+"))
+ dos_data->system = PARTITION_HFS;
+ else if (!strcmp (fs_type->name, "udf"))
+ dos_data->system = PARTITION_UDF;
+ else if (!strcmp (fs_type->name, "sun-ufs"))
+ dos_data->system = PARTITION_SUN_UFS;
+ else if (is_linux_swap (fs_type->name))
+ dos_data->system = PARTITION_LINUX_SWAP;
+ else
+ dos_data->system = PARTITION_LINUX;
+
+ return 1;
+}
+
+static int
+msdos_partition_set_flag (PedPartition* part,
+ PedPartitionFlag flag, int state)
+{
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+ PED_ASSERT (part->disk != NULL);
+
+ DosPartitionData* dos_data = part->disk_specific;
+
+ const struct flag_id_mapping_t* p = dos_find_flag_id_mapping (flag);
+ if (p)
+ {
+ if (part->type & PED_PARTITION_EXTENDED)
+ return 0;
+
+ if (state) {
+ dos_data->system = p->type_id;
+ } else if (dos_data->system == p->type_id || dos_data->system == p->alt_type_id) {
+ // Clear the type so that fs_type will be used to return it to the default
+ dos_data->system = PARTITION_LINUX;
+ return ped_partition_set_system (part, part->fs_type);
+ }
+ return 1;
+ }
+
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ {
+ return dos_type_id_set_hidden(&dos_data->system, state);
+ }
+
+ case PED_PARTITION_LBA:
+ {
+ return dos_type_id_set_lba(&dos_data->system, state);
+ }
+
+ case PED_PARTITION_BOOT:
+ {
+ dos_data->boot = state;
+
+ if (state)
+ {
+ PedDisk* disk = part->disk;
+ PedPartition* walk = ped_disk_next_partition (disk, NULL);
+ for (; walk; walk = ped_disk_next_partition (disk, walk)) {
+ if (walk == part || !ped_partition_is_active (walk))
+ continue;
+ msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0);
+ }
+ }
+
+ return 1;
+ }
+
+ default:
+ return 0;
+ }
+}
+
+static int _GL_ATTRIBUTE_PURE
+msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
+{
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+
+ DosPartitionData* dos_data = part->disk_specific;
+
+ const struct flag_id_mapping_t* p = dos_find_flag_id_mapping (flag);
+ if (p)
+ return dos_data->system == p->type_id || dos_data->system == p->alt_type_id;
+
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ return dos_type_id_is_hidden(dos_data->system);
+
+ case PED_PARTITION_LBA:
+ return dos_type_id_is_lba(dos_data->system);
+
+ case PED_PARTITION_BOOT:
+ return dos_data->boot;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+msdos_partition_is_flag_available (const PedPartition* part,
+ PedPartitionFlag flag)
+{
+ if (dos_find_flag_id_mapping (flag))
+ return part->type != PED_PARTITION_EXTENDED;
+
+ DosPartitionData* dos_data = part->disk_specific;
+
+ switch (flag) {
+ case PED_PARTITION_HIDDEN:
+ return dos_type_id_supports_hidden(dos_data->system);
+
+ case PED_PARTITION_LBA:
+ return dos_type_id_supports_lba(dos_data->system);
+
+ case PED_PARTITION_BOOT:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+int
+msdos_partition_set_type_id (PedPartition* part, uint8_t id)
+{
+ DosPartitionData* dos_data = part->disk_specific;
+
+ dos_data->system = id;
+
+ return 1;
+}
+
+
+uint8_t _GL_ATTRIBUTE_PURE
+msdos_partition_get_type_id (const PedPartition* part)
+{
+ const DosPartitionData* dos_data = part->disk_specific;
+
+ return dos_data->system;
+}
+
+
+static PedGeometry*
+_try_constraint (const PedPartition* part, const PedConstraint* external,
+ PedConstraint* internal)
+{
+ PedConstraint* intersection;
+ PedGeometry* solution;
+
+ intersection = ped_constraint_intersect (external, internal);
+ ped_constraint_destroy (internal);
+ if (!intersection)
+ return NULL;
+
+ solution = ped_constraint_solve_nearest (intersection, &part->geom);
+ ped_constraint_destroy (intersection);
+ return solution;
+}
+
+static PedGeometry*
+_best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom,
+ PedGeometry* a, PedGeometry* b)
+{
+ PedSector cyl_size = bios_geom->heads * bios_geom->sectors;
+ int a_cylinder;
+ int b_cylinder;
+
+ if (!a)
+ return b;
+ if (!b)
+ return a;
+
+ a_cylinder = a->start / cyl_size;
+ b_cylinder = b->start / cyl_size;
+
+ if (a_cylinder == b_cylinder) {
+ if ( (a->start / bios_geom->sectors) % bios_geom->heads
+ < (b->start / bios_geom->sectors) % bios_geom->heads)
+ goto choose_a;
+ else
+ goto choose_b;
+ } else {
+ PedSector a_delta;
+ PedSector b_delta;
+
+ a_delta = llabs (part->geom.start - a->start);
+ b_delta = llabs (part->geom.start - b->start);
+
+ if (a_delta < b_delta)
+ goto choose_a;
+ else
+ goto choose_b;
+ }
+
+ return NULL; /* never get here! */
+
+choose_a:
+ ped_geometry_destroy (b);
+ return a;
+
+choose_b:
+ ped_geometry_destroy (a);
+ return b;
+}
+
+/* This constraint is for "normal" primary partitions, that start at the
+ * beginning of a cylinder, and end at the end of a cylinder.
+ * Note: you can't start a partition at the beginning of the 1st
+ * cylinder, because that's where the partition table is! There are different
+ * rules for that - see the _primary_start_constraint.
+ */
+static PedConstraint*
+_primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ PedGeometry* min_geom)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry start_geom;
+ PedGeometry end_geom;
+
+ if (!ped_alignment_init (&start_align, 0, cylinder_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+
+ if (min_geom) {
+ if (min_geom->start < cylinder_size)
+ return NULL;
+ if (!ped_geometry_init (&start_geom, dev, cylinder_size,
+ min_geom->start + 1 - cylinder_size))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, min_geom->end,
+ dev->length - min_geom->end))
+ return NULL;
+ } else {
+ /* 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;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &start_geom,
+ &end_geom, 1, dev->length);
+}
+
+/* This constraint is for partitions starting on the first cylinder. They
+ * must start on the 2nd head of the 1st cylinder.
+ *
+ * NOTE: We don't always start on the 2nd head of the 1st cylinder. Windows
+ * Vista aligns starting partitions at sector 2048 (0x800) by default. See:
+ * http://support.microsoft.com/kb/923332
+ */
+static PedConstraint*
+_primary_start_constraint (const PedDisk* disk,
+ const PedPartition *part,
+ const PedCHSGeometry* bios_geom,
+ const PedGeometry* min_geom)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry start_geom;
+ PedGeometry end_geom;
+ PedSector start_pos;
+
+ if (part->geom.start == 2048)
+ /* check for known Windows Vista (NTFS >= 3.1) alignments */
+ /* sector 0x800 == 2048 */
+ start_pos = 2048;
+ else
+ /* all other primary partitions on a DOS label align to */
+ /* the 2nd head of the first cylinder (0x3F == 63) */
+ start_pos = bios_geom->sectors;
+
+ if (!ped_alignment_init (&start_align, start_pos, 0))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+ if (min_geom) {
+ if (!ped_geometry_init (&start_geom, dev, start_pos, 1))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, min_geom->end,
+ dev->length - min_geom->end))
+ return NULL;
+ } else {
+ if (!ped_geometry_init (&start_geom, dev, start_pos,
+ dev->length - start_pos))
+ return NULL;
+ if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
+ return NULL;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &start_geom,
+ &end_geom, 1, dev->length);
+}
+
+/* constraints for logical partitions:
+ * - start_offset is the offset in the start alignment. "normally",
+ * this is bios_geom->sectors. exceptions: MINOR > 5 at the beginning of the
+ * extended partition, or MINOR == 5 in the middle of the extended partition
+ * - is_start_part == 1 if the constraint is for the first cylinder of
+ * the extended partition, or == 0 if the constraint is for the second cylinder
+ * onwards of the extended partition.
+ */
+static PedConstraint*
+_logical_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
+ PedSector start_offset, int is_start_part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedDevice* dev = disk->dev;
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedAlignment start_align;
+ PedAlignment end_align;
+ PedGeometry max_geom;
+
+ PED_ASSERT (ext_part != NULL);
+
+ if (!ped_alignment_init (&start_align, start_offset, cylinder_size))
+ return NULL;
+ if (!ped_alignment_init (&end_align, -1, cylinder_size))
+ return NULL;
+ if (is_start_part) {
+ if (!ped_geometry_init (&max_geom, dev,
+ ext_part->geom.start,
+ ext_part->geom.length))
+ return NULL;
+ } else {
+ PedSector min_start;
+ PedSector max_length;
+
+ min_start = ped_round_up_to (ext_part->geom.start + 1,
+ cylinder_size);
+ max_length = ext_part->geom.end - min_start + 1;
+ if (min_start >= ext_part->geom.end)
+ return NULL;
+
+ if (!ped_geometry_init (&max_geom, dev, min_start, max_length))
+ return NULL;
+ }
+
+ return ped_constraint_new (&start_align, &end_align, &max_geom,
+ &max_geom, 1, dev->length);
+}
+
+/* returns the minimum geometry for the extended partition, given that the
+ * extended partition must contain:
+ * * all logical partitions
+ * * all partition tables for all logical partitions (except the first)
+ * * the extended partition table
+ */
+static PedGeometry*
+_get_min_extended_part_geom (const PedPartition* ext_part,
+ const PedCHSGeometry* bios_geom)
+{
+ PedDisk* disk = ext_part->disk;
+ PedSector head_size = bios_geom ? bios_geom->sectors : 1;
+ PedPartition* walk;
+ PedGeometry* min_geom;
+
+ walk = ped_disk_get_partition (disk, 5);
+ if (!walk)
+ return NULL;
+
+ min_geom = ped_geometry_duplicate (&walk->geom);
+ if (!min_geom)
+ return NULL;
+ /* 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)
+ continue;
+ if (walk->geom.start < min_geom->start)
+ ped_geometry_set_start (min_geom,
+ walk->geom.start - 2 * head_size);
+ if (walk->geom.end > min_geom->end)
+ ped_geometry_set_end (min_geom, walk->geom.end);
+ }
+
+ return min_geom;
+}
+
+static int
+_align_primary (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedGeometry* min_geom = NULL;
+ PedGeometry* solution = NULL;
+
+ if (part->type == PED_PARTITION_EXTENDED)
+ min_geom = _get_min_extended_part_geom (part, bios_geom);
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, constraint,
+ _primary_start_constraint (disk, part,
+ bios_geom, min_geom)));
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, constraint,
+ _primary_constraint (disk, bios_geom,
+ min_geom)));
+
+ if (min_geom)
+ ped_geometry_destroy (min_geom);
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+_logical_min_start_head (const PedPartition* part,
+ const PedCHSGeometry* bios_geom,
+ const PedPartition* ext_part,
+ int is_start_ext_part)
+{
+ PedSector cylinder_size = bios_geom->sectors * bios_geom->heads;
+ PedSector base_head;
+
+ if (is_start_ext_part)
+ base_head = 1 + (ext_part->geom.start % cylinder_size)
+ / bios_geom->sectors;
+ else
+ base_head = 0;
+
+ if (part->num == 5)
+ return base_head + 0;
+ else
+ return base_head + 1;
+}
+
+/* Shamelessly copied and adapted from _partition_get_overlap_constraint
+ * (in disk.c)
+ * 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.
+ * 2)*The partition table of part5 is always at the beginning of the ext_part
+ * so there is no need to leave a one sector gap before part5.
+ * *There always is a partition table at the beginning of each partition != 5.
+ * We don't need to worry to much about consistency with
+ * _partition_get_overlap_constraint because missing it means we are in edge
+ * cases anyway, and we don't lose anything by just refusing to do the job in
+ * those cases.
+ */
+static PedConstraint*
+_log_meta_overlap_constraint (PedPartition* part, const PedGeometry* geom)
+{
+ PedGeometry safe_space;
+ PedSector min_start;
+ PedSector max_end;
+ PedPartition* ext_part = ped_disk_extended_partition (part->disk);
+ PedPartition* walk;
+ int not_5 = (part->num != 5);
+
+ PED_ASSERT (ext_part != NULL);
+
+ walk = ext_part->part_list;
+
+ /* 1) 2) */
+ min_start = ext_part->geom.start + 1 + not_5;
+ max_end = ext_part->geom.end;
+
+ while (walk != NULL /* 2) 2) */
+ && ( walk->geom.start - (walk->num != 5) < geom->start - not_5
+ || walk->geom.start - (walk->num != 5) <= min_start )) {
+ if (walk != part && ped_partition_is_active (walk))
+ min_start = walk->geom.end + 1 + not_5; /* 2) */
+ walk = walk->next;
+ }
+
+ while (walk && (walk == part || !ped_partition_is_active (walk)))
+ walk = walk->next;
+
+ if (walk)
+ max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */
+
+ if (min_start >= max_end)
+ return NULL;
+
+ ped_geometry_init (&safe_space, part->disk->dev,
+ min_start, max_end - min_start + 1);
+ return ped_constraint_new_from_max (&safe_space);
+}
+
+static int
+_align_logical (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedSector cyl_size = bios_geom->sectors * bios_geom->heads;
+ PedSector start_base;
+ int head;
+ PedGeometry* solution = NULL;
+ PedConstraint *intersect, *log_meta_overlap;
+
+ PED_ASSERT (ext_part != NULL);
+
+ log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom);
+ intersect = ped_constraint_intersect (constraint, log_meta_overlap);
+ ped_constraint_destroy (log_meta_overlap);
+ if (!intersect)
+ return 0;
+
+ start_base = ped_round_down_to (part->geom.start, cyl_size);
+
+ for (head = _logical_min_start_head (part, bios_geom, ext_part, 0);
+ head < PED_MIN (5, bios_geom->heads); head++) {
+ PedConstraint* disk_constraint;
+ PedSector start = start_base + head * bios_geom->sectors;
+
+ if (head >= _logical_min_start_head (part, bios_geom,
+ ext_part, 1))
+ disk_constraint =
+ _logical_constraint (disk, bios_geom, start, 1);
+ else
+ disk_constraint =
+ _logical_constraint (disk, bios_geom, start, 0);
+
+ solution = _best_solution (part, bios_geom, solution,
+ _try_constraint (part, intersect,
+ disk_constraint));
+ }
+
+ ped_constraint_destroy (intersect);
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+_align (PedPartition* part, const PedCHSGeometry* bios_geom,
+ const PedConstraint* constraint)
+{
+ if (part->type == PED_PARTITION_LOGICAL)
+ return _align_logical (part, bios_geom, constraint);
+ else
+ return _align_primary (part, bios_geom, constraint);
+}
+
+static PedConstraint*
+_no_geom_constraint (const PedDisk* disk, PedSector start, PedSector end)
+{
+ PedGeometry max;
+
+ ped_geometry_init (&max, disk->dev, start, end - start + 1);
+ return ped_constraint_new_from_max (&max);
+}
+
+static PedConstraint*
+_no_geom_extended_constraint (const PedPartition* part)
+{
+ PedDevice* dev = part->disk->dev;
+ PedGeometry* min = _get_min_extended_part_geom (part, NULL);
+ PedGeometry start_range;
+ PedGeometry end_range;
+ PedConstraint* constraint;
+
+ if (min) {
+ ped_geometry_init (&start_range, dev, 1, min->start);
+ ped_geometry_init (&end_range, dev, min->end,
+ dev->length - min->end);
+ ped_geometry_destroy (min);
+ } else {
+ ped_geometry_init (&start_range, dev, 1, dev->length - 1);
+ ped_geometry_init (&end_range, dev, 1, dev->length - 1);
+ }
+ constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any,
+ &start_range, &end_range, 1, dev->length);
+ return constraint;
+}
+
+static int
+_align_primary_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ PedDisk* disk = part->disk;
+ PedGeometry* solution;
+
+ if (part->type == PED_PARTITION_EXTENDED) {
+ solution = _try_constraint (part, constraint,
+ _no_geom_extended_constraint (part));
+ } else {
+ solution = _try_constraint (part, constraint,
+ _no_geom_constraint (disk, 1,
+ disk->dev->length - 1));
+ }
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_align_logical_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ PedGeometry* solution;
+
+ solution = _try_constraint (part, constraint,
+ _log_meta_overlap_constraint (part, &part->geom));
+
+ if (solution) {
+ ped_geometry_set (&part->geom, solution->start,
+ solution->length);
+ ped_geometry_destroy (solution);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+_align_no_geom (PedPartition* part, const PedConstraint* constraint)
+{
+ if (part->type == PED_PARTITION_LOGICAL)
+ return _align_logical_no_geom (part, constraint);
+ else
+ return _align_primary_no_geom (part, constraint);
+}
+
+static int
+msdos_partition_align (PedPartition* part, const PedConstraint* constraint)
+{
+ PedCHSGeometry bios_geom;
+ DosPartitionData* dos_data;
+
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk_specific != NULL);
+
+ dos_data = part->disk_specific;
+
+ if (dos_data->system == PARTITION_LDM && dos_data->orig) {
+ PedGeometry *orig_geom = &dos_data->orig->geom;
+
+ if (ped_geometry_test_equal (&part->geom, orig_geom)
+ && ped_constraint_is_solution (constraint, &part->geom))
+ return 1;
+
+ ped_geometry_set (&part->geom, orig_geom->start,
+ orig_geom->length);
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Parted can't resize partitions managed by "
+ "Windows Dynamic Disk."));
+ return 0;
+ }
+
+ partition_probe_bios_geometry (part, &bios_geom);
+
+ DosDiskData *disk_specific = part->disk->disk_specific;
+ if (disk_specific->cylinder_alignment
+ && _align(part, &bios_geom, constraint))
+ return 1;
+ if (_align_no_geom (part, constraint))
+ return 1;
+
+#ifndef DISCOVER_ONLY
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR,
+ PED_EXCEPTION_CANCEL,
+ _("Unable to satisfy all constraints on the partition."));
+#endif
+ return 0;
+}
+
+static int
+add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start,
+ PedSector end)
+{
+ PedPartition* new_part;
+
+ PED_ASSERT (disk != NULL);
+
+ new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL,
+ start, end);
+ if (!new_part)
+ goto error;
+ if (!ped_disk_add_partition (disk, new_part, NULL))
+ goto error_destroy_new_part;
+
+ return 1;
+
+error_destroy_new_part:
+ ped_partition_destroy (new_part);
+error:
+ return 0;
+}
+
+/* There are a few objectives here:
+ * - avoid having lots of "free space" partitions lying around, to confuse
+ * the front end.
+ * - ensure that there's enough room to put in the extended partition
+ * tables, etc.
+ */
+static int
+add_logical_part_metadata (PedDisk* disk, const PedPartition* log_part)
+{
+ PedPartition* ext_part = ped_disk_extended_partition (disk);
+ PedPartition* prev = log_part->prev;
+ PedCHSGeometry bios_geom;
+ PedSector cyl_size;
+ PedSector metadata_start;
+ PedSector metadata_end;
+ PedSector metadata_length;
+
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ cyl_size = bios_geom.sectors * bios_geom.heads;
+
+ /* if there's metadata shortly before the partition (on the same
+ * cylinder), then make this new metadata partition touch the end of
+ * the other. No point having 63 bytes (or whatever) of free space
+ * partition - just confuses front-ends, etc.
+ * Otherwise, start the metadata at the start of the cylinder
+ */
+
+ metadata_end = log_part->geom.start - 1;
+ metadata_start = ped_round_down_to (metadata_end, cyl_size);
+ if (prev)
+ metadata_start = PED_MAX (metadata_start, prev->geom.end + 1);
+ else
+ metadata_start = PED_MAX (metadata_start,
+ ext_part->geom.start + 1);
+ metadata_length = metadata_end - metadata_start + 1;
+
+ /* partition 5 doesn't need to have any metadata */
+ if (log_part->num == 5 && metadata_length < bios_geom.sectors)
+ return 1;
+
+ PED_ASSERT (metadata_length > 0);
+
+ return add_metadata_part (disk, PED_PARTITION_LOGICAL,
+ metadata_start, metadata_end);
+}
+
+/*
+ * Find the starting sector number of the first non-free partition,
+ * set *SECTOR to that value, and return 1.
+ * If there is no non-free partition, don't modify *SECTOR and return 0.
+ */
+static int
+get_start_first_nonfree_part (const PedDisk* disk, PedSector *sector)
+{
+ PedPartition* walk;
+
+ // disk->part_list is the first partition on the disk.
+ if (!disk->part_list)
+ return 0;
+
+ for (walk = disk->part_list; walk; walk = walk->next) {
+ if (walk->type == PED_PARTITION_NORMAL ||
+ walk->type == PED_PARTITION_EXTENDED) {
+ *sector = walk->geom.start;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find the ending sector number of the last non-free partition,
+ * set *SECTOR to that value, and return 1.
+ * If there is no non-free partition, don't modify *SECTOR and return 0.
+ */
+static int
+get_end_last_nonfree_part (const PedDisk* disk, PedSector *sector)
+{
+ PedPartition* last_part = NULL;
+ PedPartition* walk;
+
+ // disk->part_list is the first partition on the disk.
+ if (!disk->part_list)
+ return 0;
+
+ for (walk = disk->part_list; walk; walk = walk->next) {
+ if (walk->type == PED_PARTITION_NORMAL ||
+ walk->type == PED_PARTITION_EXTENDED) {
+ last_part = walk;
+ }
+ }
+
+ if (!last_part)
+ return 0;
+ else {
+ *sector = last_part->geom.end;
+ return 1;
+ }
+}
+
+/* Adds metadata placeholder partitions to cover the partition table (and
+ * "free" space after it that often has bootloader stuff), and the last
+ * incomplete cylinder at the end of the disk.
+ * Parted has to be mindful of the uncertainty of dev->bios_geom.
+ * It therefore makes sure this metadata doesn't overlap with partitions.
+ */
+static int
+add_startend_metadata (PedDisk* disk)
+{
+ PedDevice* dev = disk->dev;
+ PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads;
+ PedSector init_start, init_end, final_start, final_end;
+
+ // Ranges for the initial and final metadata partition.
+ init_start = 0;
+ if (!get_start_first_nonfree_part(disk, &init_end))
+ init_end = dev->bios_geom.sectors - 1;
+ else
+ init_end = PED_MIN (dev->bios_geom.sectors - 1, init_end - 1);
+
+ DosDiskData *disk_specific = disk->disk_specific;
+ if (!disk_specific->cylinder_alignment)
+ final_start = dev->length - 1;
+ else if (!get_end_last_nonfree_part(disk, &final_start))
+ final_start = ped_round_down_to (dev->length, cyl_size);
+ else
+ final_start = PED_MAX (final_start + 1,
+ ped_round_down_to (dev->length, cyl_size));
+ final_end = dev->length - 1;
+
+ // Create the metadata partitions.
+ // init_end <= dev->length for devices that are _real_ small.
+ if (init_start < init_end &&
+ init_end <= dev->length &&
+ !add_metadata_part (disk, PED_PARTITION_NORMAL,
+ init_start, init_end))
+ return 0;
+
+ // init_end < final_start so they dont overlap. For very small devs.
+ if (final_start < final_end &&
+ init_end < final_start &&
+ final_end <= dev->length &&
+ !add_metadata_part (disk, PED_PARTITION_NORMAL,
+ final_start, final_end))
+ return 0;
+
+ return 1;
+}
+
+static int
+msdos_alloc_metadata (PedDisk* disk)
+{
+ PedPartition* ext_part;
+
+ PED_ASSERT (disk != NULL);
+ PED_ASSERT (disk->dev != NULL);
+
+ if (!add_startend_metadata (disk))
+ return 0;
+
+ ext_part = ped_disk_extended_partition (disk);
+ if (ext_part) {
+ int i;
+ PedSector start, end;
+ PedCHSGeometry bios_geom;
+
+ for (i=5; 1; i++) {
+ PedPartition* log_part;
+ log_part = ped_disk_get_partition (disk, i);
+ if (!log_part)
+ break;
+ if (!add_logical_part_metadata (disk, log_part))
+ return 0;
+ }
+
+ partition_probe_bios_geometry (ext_part, &bios_geom);
+ start = ext_part->geom.start;
+ end = start + bios_geom.sectors - 1;
+ if (ext_part->part_list)
+ end = PED_MIN (end,
+ ext_part->part_list->geom.start - 1);
+ if (!add_metadata_part (disk, PED_PARTITION_LOGICAL,
+ start, end))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+next_primary (const PedDisk* disk)
+{
+ int i;
+ for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) {
+ if (!ped_disk_get_partition (disk, i))
+ return i;
+ }
+ return -1;
+}
+
+static int _GL_ATTRIBUTE_PURE
+next_logical (const PedDisk* disk)
+{
+ int i;
+ for (i=5; i<=MAX_TOTAL_PART; i++) {
+ if (!ped_disk_get_partition (disk, i))
+ return i;
+ }
+ ped_exception_throw (
+ PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("cannot create any more partitions"),
+ disk->dev->path);
+ return -1;
+}
+
+static int
+msdos_partition_enumerate (PedPartition* part)
+{
+ PED_ASSERT (part != NULL);
+ PED_ASSERT (part->disk != NULL);
+
+ /* don't re-number a primary partition */
+ if (part->num != -1 && part->num <= DOS_N_PRI_PARTITIONS)
+ return 1;
+
+ part->num = -1;
+
+ if (part->type & PED_PARTITION_LOGICAL)
+ part->num = next_logical (part->disk);
+ else
+ part->num = next_primary (part->disk);
+ if (part->num == -1)
+ return 0;
+ return 1;
+}
+
+static int
+msdos_get_max_primary_partition_count (const PedDisk* disk)
+{
+ return DOS_N_PRI_PARTITIONS;
+}
+
+static bool
+msdos_get_max_supported_partition_count(const PedDisk* disk, int *max_n)
+{
+ *max_n = MAX_TOTAL_PART;
+ return true;
+}
+
+#include "pt-common.h"
+PT_define_limit_functions (msdos)
+
+static PedDiskOps msdos_disk_ops = {
+ clobber: NULL,
+ write: NULL_IF_DISCOVER_ONLY (msdos_write),
+
+ disk_set_flag: msdos_disk_set_flag,
+ disk_get_flag: msdos_disk_get_flag,
+ disk_is_flag_available: msdos_disk_is_flag_available,
+
+ partition_set_name: NULL,
+ partition_get_name: NULL,
+ partition_set_type_id: msdos_partition_set_type_id,
+ partition_get_type_id: msdos_partition_get_type_id,
+ partition_set_type_uuid: NULL,
+ partition_get_type_uuid: NULL,
+
+ PT_op_function_initializers (msdos)
+};
+
+static PedDiskType msdos_disk_type = {
+ next: NULL,
+ name: "msdos",
+ ops: &msdos_disk_ops,
+ features: PED_DISK_TYPE_EXTENDED | PED_DISK_TYPE_PARTITION_TYPE_ID
+};
+
+void
+ped_disk_msdos_init ()
+{
+ PED_ASSERT (sizeof (DosRawPartition) == 16);
+ PED_ASSERT (sizeof (DosRawTable) == 512);
+
+ ped_disk_type_register (&msdos_disk_type);
+}
+
+void
+ped_disk_msdos_done ()
+{
+ ped_disk_type_unregister (&msdos_disk_type);
+}