/* libparted - a library for manipulating disk partitions Copyright (C) 1999-2001, 2004-2005, 2007-2014, 2019-2022 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 . */ #include #include #include #include #include #include #if ENABLE_NLS # include # 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); }