| /* |
| libparted - a library for manipulating disk partitions |
| Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007 |
| 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 2 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <config.h> |
| |
| #include <sys/time.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 */ |
| |
| /* 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 |
| |
| #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_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_LDM 0x42 |
| #define PARTITION_LINUX_SWAP 0x82 |
| #define PARTITION_LINUX 0x83 |
| #define PARTITION_LINUX_EXT 0x85 |
| #define PARTITION_LINUX_LVM 0x8e |
| #define PARTITION_SUN_UFS 0xbf |
| #define PARTITION_DELL_DIAG 0xde |
| #define PARTITION_GPT 0xee |
| #define PARTITION_PALO 0xf0 |
| #define PARTITION_PREP 0x41 |
| #define PARTITION_LINUX_RAID 0xfd |
| #define PARTITION_LINUX_LVM_OLD 0xfe |
| |
| /* 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 |
| |
| 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 [4]; |
| 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 { |
| unsigned char system; |
| int boot; |
| int hidden; |
| int raid; |
| int lvm; |
| int lba; |
| int palo; |
| int prep; |
| OrigState* orig; /* used for CHS stuff */ |
| } DosPartitionData; |
| |
| static PedDiskType msdos_disk_type; |
| |
| static int |
| msdos_probe (const PedDevice *dev) |
| { |
| PedDiskType* disk_type; |
| DosRawTable part_table; |
| int i; |
| |
| PED_ASSERT (dev != NULL, return 0); |
| |
| if (dev->sector_size != 512) |
| return 0; |
| |
| if (!ped_device_read (dev, &part_table, 0, 1)) |
| return 0; |
| |
| /* check magic */ |
| if (PED_LE16_TO_CPU (part_table.magic) != MSDOS_MAGIC) |
| return 0; |
| |
| /* if this is a FAT fs, fail here. Note that the Smart Boot Manager |
| * Loader (SBML) signature indicates a partition table, not a file |
| * system. |
| */ |
| /* check below commented out to allow FAT fs images without a MBR */ |
| /* if ((!strncmp (part_table.boot_code + 0x36, "FAT", 3) |
| && strncmp (part_table.boot_code + 0x40, "SBML", 4) != 0) |
| || !strncmp (part_table.boot_code + 0x52, "FAT", 3)) |
| return 0; |
| */ |
| |
| /* If this is a GPT disk, fail here */ |
| for (i = 0; i < 4; i++) { |
| if (part_table.partitions[i].type == PARTITION_GPT) |
| return 0; |
| } |
| |
| /* 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) |
| return 0; |
| |
| #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)) |
| return 0; |
| #endif /* ENABLE_PC98 */ |
| |
| return 1; |
| } |
| |
| static PedDisk* |
| msdos_alloc (const PedDevice* dev) |
| { |
| PedDisk* disk; |
| PED_ASSERT (dev != NULL, return NULL); |
| |
| disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type); |
| if (disk) |
| disk->disk_specific = NULL; |
| 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; |
| new_disk->disk_specific = NULL; |
| return new_disk; |
| } |
| |
| static void |
| msdos_free (PedDisk* disk) |
| { |
| PED_ASSERT (disk != NULL, return); |
| |
| _ped_disk_free (disk); |
| } |
| |
| #ifndef DISCOVER_ONLY |
| static int |
| msdos_clobber (PedDevice* dev) |
| { |
| DosRawTable table; |
| |
| PED_ASSERT (dev != NULL, return 0); |
| PED_ASSERT (msdos_probe (dev), return 0); |
| |
| if (!ped_device_read (dev, &table, 0, 1)) |
| return 0; |
| table.magic = 0; |
| return ped_device_write (dev, (void*) &table, 0, 1); |
| } |
| #endif /* !DISCOVER_ONLY */ |
| |
| 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 |
| 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, return 0); |
| PED_ASSERT (chs != NULL, return 0); |
| |
| 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) |
| * (dev->sector_size / 512); |
| } |
| |
| 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, return); |
| PED_ASSERT (chs != NULL, return); |
| |
| if (!bios_geom) |
| bios_geom = &dev->bios_geom; |
| |
| sector /= (dev->sector_size / 512); |
| |
| 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 |
| legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom, |
| const DosRawPartition* raw_part) |
| { |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start); |
| } |
| |
| static PedSector |
| legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom, |
| const DosRawPartition* raw_part) |
| { |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end); |
| } |
| |
| static PedSector |
| linear_start (const PedDisk* disk, const DosRawPartition* raw_part, |
| PedSector offset) |
| { |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| return offset |
| + PED_LE32_TO_CPU (raw_part->start) |
| * (disk->dev->sector_size / 512); |
| } |
| |
| static PedSector |
| linear_end (const PedDisk* disk, const DosRawPartition* raw_part, |
| PedSector offset) |
| { |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| return linear_start (disk, raw_part, offset) |
| + (PED_LE32_TO_CPU (raw_part->length) - 1) |
| * (disk->dev->sector_size / 512); |
| } |
| |
| #ifndef DISCOVER_ONLY |
| static int |
| partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom) |
| { |
| PedSector leg_start, leg_end; |
| DosPartitionData* dos_data; |
| PedDisk* disk; |
| |
| PED_ASSERT (part != NULL, return 0); |
| PED_ASSERT (part->disk != NULL, return 0); |
| PED_ASSERT (part->disk_specific != NULL, return 0); |
| 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 |
| disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom) |
| { |
| PedPartition* part = NULL; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| |
| 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, return 0); |
| PED_ASSERT (part != NULL, return 0); |
| PED_ASSERT (part->disk != NULL, return 0); |
| PED_ASSERT (part->disk->dev != NULL, return 0); |
| PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, |
| return 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: |
| ped_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, return 0); |
| PED_ASSERT (part->disk_specific != NULL, return 0); |
| PED_ASSERT (bios_geom != NULL, return 0); |
| |
| 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; |
| |
| PED_ASSERT (cyl_size > 0, return 0); |
| PED_ASSERT (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 */ |
| head_size = 0; |
| PED_ASSERT (0, return 0); |
| } |
| |
| PED_ASSERT (head_size > 0, return 0); |
| PED_ASSERT (head_size <= 63, return 0); |
| |
| cylinders = part->disk->dev->length / cyl_size; |
| heads = cyl_size / head_size; |
| sectors = head_size; |
| |
| PED_ASSERT (heads > 0, return 0); |
| PED_ASSERT (heads < 256, return 0); |
| |
| PED_ASSERT (sectors > 0, return 0); |
| PED_ASSERT (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++; |
| |
| PED_ASSERT ((c * heads + h) * sectors + s == a, return 0); |
| PED_ASSERT ((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, return); |
| PED_ASSERT (part->disk != NULL, return); |
| PED_ASSERT (bios_geom != NULL, return); |
| |
| 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, return); |
| 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 |
| raw_part_is_extended (const DosRawPartition* raw_part) |
| { |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| switch (raw_part->type) { |
| case PARTITION_DOS_EXT: |
| case PARTITION_EXT_LBA: |
| case PARTITION_LINUX_EXT: |
| return 1; |
| |
| default: |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| raw_part_is_hidden (const DosRawPartition* raw_part) |
| { |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| switch (raw_part->type) { |
| case PARTITION_FAT12_H: |
| case PARTITION_FAT16_SM_H: |
| case PARTITION_FAT16_H: |
| case PARTITION_FAT32_H: |
| case PARTITION_NTFS_H: |
| case PARTITION_FAT32_LBA_H: |
| case PARTITION_FAT16_LBA_H: |
| return 1; |
| |
| default: |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| raw_part_is_lba (const DosRawPartition* raw_part) |
| { |
| PED_ASSERT (raw_part != NULL, return 0); |
| |
| switch (raw_part->type) { |
| case PARTITION_FAT32_LBA: |
| case PARTITION_FAT16_LBA: |
| case PARTITION_EXT_LBA: |
| case PARTITION_FAT32_LBA_H: |
| case PARTITION_FAT16_LBA_H: |
| 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, return NULL); |
| PED_ASSERT (raw_part != NULL, return 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->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 |
| || raw_part->type == PARTITION_LINUX_LVM; |
| dos_data->lba = raw_part_is_lba (raw_part); |
| dos_data->palo = raw_part->type == PARTITION_PALO; |
| dos_data->prep = raw_part->type == PARTITION_PREP; |
| 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; |
| PedConstraint* constraint_exact; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (disk->dev != NULL, return 0); |
| |
| if (!ped_device_read (disk->dev, (void*) &table, sector, 1)) |
| goto error; |
| |
| /* weird: empty extended partitions are filled with 0xf6 by PM */ |
| if (is_extended_table |
| && PED_LE16_TO_CPU (table.magic) == PARTITION_MAGIC_MAGIC) |
| return 1; |
| |
| #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; |
| return 1; |
| } |
| #endif |
| |
| /* parse the partitions from this table */ |
| for (i = 0; i < 4; 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); |
| |
| constraint_exact = ped_constraint_exact (&part->geom); |
| if (!ped_disk_add_partition (disk, part, constraint_exact)) |
| goto error; |
| ped_constraint_destroy (constraint_exact); |
| |
| /* 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 < 4; 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; |
| } |
| } |
| |
| return 1; |
| |
| error: |
| ped_disk_delete_all (disk); |
| return 0; |
| } |
| |
| static int |
| msdos_read (PedDisk* disk) |
| { |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (disk->dev != NULL, return 0); |
| |
| 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, return 0); |
| PED_ASSERT (part != NULL, return 0); |
| |
| 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) |
| / (part->disk->dev->sector_size / 512)); |
| raw_part->length = PED_CPU_TO_LE32 (part->geom.length |
| / (part->disk->dev->sector_size / 512)); |
| |
| 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, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| PED_ASSERT (geom->dev != NULL, return 0); |
| |
| raw_part->boot_ind = 0; |
| raw_part->type = PARTITION_DOS_EXT; |
| raw_part->start = PED_CPU_TO_LE32 ((geom->start - offset) |
| / (geom->dev->sector_size / 512)); |
| raw_part->length = PED_CPU_TO_LE32 (geom->length |
| / (geom->dev->sector_size / 512)); |
| |
| 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) |
| { |
| DosRawTable table; |
| PedPartition* part; |
| PedSector lba_offset; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (ped_disk_extended_partition (disk) != NULL, return 0); |
| PED_ASSERT (logical != NULL, return 0); |
| |
| lba_offset = ped_disk_extended_partition (disk)->geom.start; |
| |
| memset (&table, 0, sizeof (DosRawTable)); |
| table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); |
| |
| if (!fill_raw_part (&table.partitions[0], logical, sector)) |
| return 0; |
| |
| 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) |
| return 0; |
| 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)) |
| return 0; |
| } |
| |
| return ped_device_write (disk->dev, (void*) &table, sector, 1); |
| } |
| |
| static int |
| write_empty_table (const PedDisk* disk, PedSector sector) |
| { |
| DosRawTable table; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| |
| memset (&table, 0, sizeof (DosRawTable)); |
| 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, return 0); |
| |
| 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 inline uint32_t generate_random_id (void) |
| { |
| struct timeval tv; |
| int rc; |
| rc = gettimeofday(&tv, NULL); |
| if (rc == -1) |
| return 0; |
| return (uint32_t)(tv.tv_usec & 0xFFFFFFFFUL); |
| } |
| |
| static int |
| msdos_write (const PedDisk* disk) |
| { |
| DosRawTable table; |
| PedPartition* part; |
| int i; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (disk->dev != NULL, return 0); |
| |
| ped_device_read (disk->dev, &table, 0, 1); |
| |
| if (!table.boot_code[0]) { |
| memset (table.boot_code, 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_id(); |
| |
| memset (table.partitions, 0, sizeof (DosRawPartition) * 4); |
| table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC); |
| |
| for (i=1; i<=4; i++) { |
| part = ped_disk_get_partition (disk, i); |
| if (!part) |
| continue; |
| |
| if (!fill_raw_part (&table.partitions [i - 1], part, 0)) |
| return 0; |
| |
| if (part->type == PED_PARTITION_EXTENDED) { |
| if (!write_extended_partitions (disk)) |
| return 0; |
| } |
| } |
| |
| if (!ped_device_write (disk->dev, (void*) &table, 0, 1)) |
| return 0; |
| return ped_device_sync (disk->dev); |
| } |
| #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_malloc (sizeof (DosPartitionData)); |
| if (!dos_data) |
| goto error_free_part; |
| dos_data->orig = NULL; |
| dos_data->system = PARTITION_LINUX; |
| dos_data->hidden = 0; |
| dos_data->boot = 0; |
| dos_data->raid = 0; |
| dos_data->lvm = 0; |
| dos_data->lba = 0; |
| dos_data->palo = 0; |
| dos_data->prep = 0; |
| } else { |
| part->disk_specific = NULL; |
| } |
| return part; |
| |
| ped_free (dos_data); |
| error_free_part: |
| ped_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; |
| new_dos_data->hidden = old_dos_data->hidden; |
| new_dos_data->raid = old_dos_data->raid; |
| new_dos_data->lvm = old_dos_data->lvm; |
| new_dos_data->lba = old_dos_data->lba; |
| new_dos_data->palo = old_dos_data->palo; |
| new_dos_data->prep = old_dos_data->prep; |
| |
| 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, return); |
| |
| if (ped_partition_is_active (part)) { |
| DosPartitionData* dos_data; |
| dos_data = (DosPartitionData*) part->disk_specific; |
| if (dos_data->orig) |
| ped_free (dos_data->orig); |
| ped_free (part->disk_specific); |
| } |
| ped_free (part); |
| } |
| |
| static int |
| msdos_partition_set_system (PedPartition* part, |
| const PedFileSystemType* fs_type) |
| { |
| DosPartitionData* dos_data = part->disk_specific; |
| |
| part->fs_type = fs_type; |
| |
| if (dos_data->hidden |
| && fs_type |
| && strncmp (fs_type->name, "fat", 3) != 0 |
| && strcmp (fs_type->name, "ntfs") != 0) |
| dos_data->hidden = 0; |
| |
| if (part->type & PED_PARTITION_EXTENDED) { |
| dos_data->raid = 0; |
| dos_data->lvm = 0; |
| dos_data->palo = 0; |
| dos_data->prep = 0; |
| if (dos_data->lba) |
| dos_data->system = PARTITION_EXT_LBA; |
| else |
| dos_data->system = PARTITION_DOS_EXT; |
| return 1; |
| } |
| |
| if (dos_data->lvm) { |
| dos_data->system = PARTITION_LINUX_LVM; |
| return 1; |
| } |
| if (dos_data->raid) { |
| dos_data->system = PARTITION_LINUX_RAID; |
| return 1; |
| } |
| if (dos_data->palo) { |
| dos_data->system = PARTITION_PALO; |
| return 1; |
| } |
| if (dos_data->prep) { |
| dos_data->system = PARTITION_PREP; |
| return 1; |
| } |
| |
| if (!fs_type) |
| dos_data->system = PARTITION_LINUX; |
| else if (!strcmp (fs_type->name, "fat16")) { |
| dos_data->system = dos_data->lba |
| ? PARTITION_FAT16_LBA : PARTITION_FAT16; |
| dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; |
| } else if (!strcmp (fs_type->name, "fat32")) { |
| dos_data->system = dos_data->lba |
| ? PARTITION_FAT32_LBA : PARTITION_FAT32; |
| dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; |
| } else if (!strcmp (fs_type->name, "ntfs") |
| || !strcmp (fs_type->name, "hpfs")) { |
| dos_data->system = PARTITION_NTFS; |
| dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0; |
| } else if (!strcmp (fs_type->name, "sun-ufs")) |
| dos_data->system = PARTITION_SUN_UFS; |
| else if (!strcmp (fs_type->name, "linux-swap")) |
| 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) |
| { |
| PedDisk* disk; |
| PedPartition* walk; |
| DosPartitionData* dos_data; |
| |
| PED_ASSERT (part != NULL, return 0); |
| PED_ASSERT (part->disk_specific != NULL, return 0); |
| PED_ASSERT (part->disk != NULL, return 0); |
| |
| dos_data = part->disk_specific; |
| disk = part->disk; |
| |
| switch (flag) { |
| case PED_PARTITION_HIDDEN: |
| if (part->type == PED_PARTITION_EXTENDED) { |
| ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("Extended partitions cannot be hidden on " |
| "msdos disk labels.")); |
| return 0; |
| } |
| dos_data->hidden = state; |
| return ped_partition_set_system (part, part->fs_type); |
| |
| case PED_PARTITION_BOOT: |
| dos_data->boot = state; |
| if (!state) |
| return 1; |
| |
| 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; |
| |
| case PED_PARTITION_RAID: |
| if (state) { |
| dos_data->hidden = 0; |
| dos_data->lvm = 0; |
| dos_data->palo = 0; |
| dos_data->prep = 0; |
| } |
| 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; |
| } |
| dos_data->lvm = state; |
| return ped_partition_set_system (part, part->fs_type); |
| |
| case PED_PARTITION_LBA: |
| dos_data->lba = state; |
| 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; |
| } |
| 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; |
| } |
| dos_data->prep = state; |
| return ped_partition_set_system (part, part->fs_type); |
| |
| default: |
| return 0; |
| } |
| } |
| |
| static int |
| msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) |
| { |
| DosPartitionData* dos_data; |
| |
| PED_ASSERT (part != NULL, return 0); |
| PED_ASSERT (part->disk_specific != NULL, return 0); |
| |
| dos_data = part->disk_specific; |
| switch (flag) { |
| case PED_PARTITION_HIDDEN: |
| return dos_data->hidden; |
| |
| case PED_PARTITION_BOOT: |
| return dos_data->boot; |
| |
| case PED_PARTITION_RAID: |
| return dos_data->raid; |
| |
| case PED_PARTITION_LVM: |
| return dos_data->lvm; |
| |
| case PED_PARTITION_LBA: |
| return dos_data->lba; |
| |
| case PED_PARTITION_PALO: |
| return dos_data->palo; |
| |
| case PED_PARTITION_PREP: |
| return dos_data->prep; |
| |
| default: |
| return 0; |
| } |
| } |
| |
| static int |
| msdos_partition_is_flag_available (const PedPartition* part, |
| PedPartitionFlag flag) |
| { |
| switch (flag) { |
| case PED_PARTITION_HIDDEN: |
| case PED_PARTITION_BOOT: |
| case PED_PARTITION_RAID: |
| case PED_PARTITION_LVM: |
| case PED_PARTITION_LBA: |
| case PED_PARTITION_PALO: |
| case PED_PARTITION_PREP: |
| return 1; |
| |
| default: |
| return 0; |
| } |
| } |
| |
| 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 = abs (part->geom.start - a->start); |
| b_delta = abs (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 { |
| if (!ped_geometry_init (&start_geom, dev, cylinder_size, |
| dev->length - cylinder_size)) |
| 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, return 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; |
| ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size); |
| |
| 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 ride 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, return 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, return 0); |
| |
| 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, return 0); |
| PED_ASSERT (part->disk_specific != NULL, return 0); |
| |
| 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); |
| |
| if (_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, return 0); |
| |
| 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 0); |
| |
| return add_metadata_part (disk, PED_PARTITION_LOGICAL, |
| metadata_start, metadata_end); |
| } |
| |
| static PedPartition* |
| get_last_part (const PedDisk* disk) |
| { |
| PedPartition* first_part = disk->part_list; |
| PedPartition* walk; |
| |
| if (!first_part) |
| return NULL; |
| for (walk = first_part; walk->next; walk = walk->next); |
| return walk; |
| } |
| |
| /* 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; |
| PedPartition* first_part = disk->part_list; |
| PedPartition* last_part = get_last_part (disk); |
| PedSector start, end; |
| |
| if (!first_part) |
| return 1; |
| |
| start = 0; |
| end = PED_MIN (dev->bios_geom.sectors - 1, first_part->geom.start - 1); |
| if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end)) |
| return 0; |
| |
| start = PED_MAX (last_part->geom.end + 1, |
| ped_round_down_to (dev->length, cyl_size)); |
| end = dev->length - 1; |
| if (start < end) { |
| if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end)) |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| msdos_alloc_metadata (PedDisk* disk) |
| { |
| PedPartition* ext_part; |
| |
| PED_ASSERT (disk != NULL, return 0); |
| PED_ASSERT (disk->dev != NULL, return 0); |
| |
| 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<=4; i++) { |
| if (!ped_disk_get_partition (disk, i)) |
| return i; |
| } |
| return 0; |
| } |
| |
| static int |
| next_logical (const PedDisk* disk) |
| { |
| int i; |
| for (i=5; 1; i++) { |
| if (!ped_disk_get_partition (disk, i)) |
| return i; |
| } |
| } |
| |
| static int |
| msdos_partition_enumerate (PedPartition* part) |
| { |
| PED_ASSERT (part != NULL, return 0); |
| PED_ASSERT (part->disk != NULL, return 0); |
| |
| /* don't re-number a primary partition */ |
| if (part->num != -1 && part->num <= 4) |
| return 1; |
| |
| part->num = -1; |
| |
| if (part->type & PED_PARTITION_LOGICAL) |
| part->num = next_logical (part->disk); |
| else |
| part->num = next_primary (part->disk); |
| |
| return 1; |
| } |
| |
| static int |
| msdos_get_max_primary_partition_count (const PedDisk* disk) |
| { |
| return 4; |
| } |
| |
| static PedDiskOps msdos_disk_ops = { |
| probe: msdos_probe, |
| #ifndef DISCOVER_ONLY |
| clobber: msdos_clobber, |
| #else |
| clobber: NULL, |
| #endif |
| alloc: msdos_alloc, |
| duplicate: msdos_duplicate, |
| free: msdos_free, |
| read: msdos_read, |
| #ifndef DISCOVER_ONLY |
| write: msdos_write, |
| #else |
| write: NULL, |
| #endif |
| |
| partition_new: msdos_partition_new, |
| partition_duplicate: msdos_partition_duplicate, |
| partition_destroy: msdos_partition_destroy, |
| partition_set_system: msdos_partition_set_system, |
| partition_set_flag: msdos_partition_set_flag, |
| partition_get_flag: msdos_partition_get_flag, |
| partition_is_flag_available: msdos_partition_is_flag_available, |
| partition_set_name: NULL, |
| partition_get_name: NULL, |
| partition_align: msdos_partition_align, |
| partition_enumerate: msdos_partition_enumerate, |
| |
| alloc_metadata: msdos_alloc_metadata, |
| get_max_primary_partition_count: |
| msdos_get_max_primary_partition_count |
| }; |
| |
| static PedDiskType msdos_disk_type = { |
| next: NULL, |
| name: "msdos", |
| ops: &msdos_disk_ops, |
| features: PED_DISK_TYPE_EXTENDED |
| }; |
| |
| void |
| ped_disk_msdos_init () |
| { |
| PED_ASSERT (sizeof (DosRawPartition) == 16, return); |
| PED_ASSERT (sizeof (DosRawTable) == 512, return); |
| |
| ped_disk_type_register (&msdos_disk_type); |
| } |
| |
| void |
| ped_disk_msdos_done () |
| { |
| ped_disk_type_unregister (&msdos_disk_type); |
| } |