/*
libparted - a library for manipulating disk partitions
Copyright (C) 1999-2001, 2004-2005, 2007-2010 Free Software
Foundation, Inc.
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdbool.h>
#if ENABLE_NLS
# include <libintl.h>
#else
#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 unsigned 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
};
/* The maximum number of DOS primary partitions. */
/* 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").
*/
#ifdef __sun
#endif /* __sun */
/* note: lots of bit-bashing here, thus, you shouldn't look inside it.
* Use chs_to_sector() and sector_to_chs() instead.
*/
#ifdef __sun
#pragma pack(1)
#endif
typedef struct {
/* ripped from Linux source */
struct _DosRawPartition {
} __attribute__((packed));
struct _DosRawTable {
} __attribute__((packed));
#ifdef __sun
#pragma pack()
#endif
/* OrigState is information we want to preserve about the partition for
* dealing with CHS issues
*/
typedef struct {
* logical partitions */
} OrigState;
typedef struct {
int cylinder_alignment;
} DosDiskData;
typedef struct {
unsigned char system;
int boot;
int hidden;
int raid;
int lvm;
int lba;
int palo;
int prep;
int diag;
static int
{
int i;
return 0;
void *label;
return 0;
/* check magic */
goto probe_fail;
/* 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.
*/
for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
goto probe_fail;
}
/* If this is a GPT disk, fail here */
for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
goto probe_fail;
}
/* If this is an AIX Physical Volume, fail here. IBMA in EBCDIC */
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 */
goto probe_fail;
#endif /* ENABLE_PC98 */
return 1;
return 0;
}
static PedDisk*
{
if (disk) {
if (!disk_specific) {
return NULL;
}
}
return disk;
}
static PedDisk*
{
if (!new_disk)
return NULL;
sizeof(DosDiskData));
return new_disk;
}
static void
{
}
static int
{
switch (flag) {
return 1;
default:
return 0;
}
}
static int
{
switch (flag) {
return disk_specific->cylinder_alignment;
default:
return 0;
}
}
static int
{
switch (flag) {
return 1;
default:
return 0;
}
}
static int
{
}
static int
{
}
/* counts from 0 */
static int
{
}
static PedSector
{
PedSector c; /* not measured in sectors, but need */
PedSector h; /* lots of bits */
PedSector s;
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;
}
static void
{
if (!bios_geom)
if (real_c > MAX_CHS_CYLINDER) {
real_c = 1023;
}
}
static PedSector
const DosRawPartition* raw_part)
{
}
static PedSector
const DosRawPartition* raw_part)
{
}
static PedSector
{
}
static PedSector
{
}
#ifndef DISCOVER_ONLY
static int
{
return 1;
return 0;
return 0;
return 1;
}
static int
{
if (ped_partition_is_active (part)) {
return 0;
}
}
return 1;
}
static int
{
int i;
int found;
unsigned char* buf;
int sectors;
int heads;
int res = 0;
return 0);
if (!buf)
return 0;
goto end;
found = 0;
for (i = 0; ms_types[i]; i++) {
found = 1;
}
if (!found)
goto end;
goto end;
/* shared by the start of all Microsoft file systems */
goto end;
goto end;
res = 1;
end:
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
{
return 0;
c = chs_get_cylinder (start_chs);
h = chs_get_head (start_chs);
s = chs_get_sector (start_chs);
a_ = a - s;
C = chs_get_cylinder (end_chs);
H = chs_get_head (end_chs);
S = chs_get_sector (end_chs);
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;
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;
/* Check for non integer result */
return 0;
PED_ASSERT (cyl_size > 0, return 0);
if (h > 0)
else if (H > 0)
else {
/* should not happen because denum != 0 */
PED_ASSERT (0, return 0);
}
PED_ASSERT (head_size > 0, return 0);
PED_ASSERT (heads > 0, return 0);
PED_ASSERT (sectors > 0, 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...
*/
C++;
return 1;
}
static void
{
if (ped_partition_is_active (part)) {
return;
return;
}
}
} else {
}
}
static void
{
/* first look at the boot partition */
if (!ped_partition_is_active (part))
continue;
return;
return;
}
}
/* that didn't work... try all partition table entries */
if (ped_partition_is_active (part)) {
return;
}
}
/* that didn't work... look at all file systems */
if (ped_partition_is_active (part)) {
return;
}
}
}
#endif /* !DISCOVER_ONLY */
static int
{
case PARTITION_DOS_EXT:
case PARTITION_EXT_LBA:
case PARTITION_LINUX_EXT:
return 1;
default:
return 0;
}
return 0;
}
static int
{
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
{
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*
{
if (!part)
return NULL;
return NULL;
}
return part;
}
static int
{
int i;
goto error;
/* weird: empty extended partitions are filled with 0xf6 by PM */
goto read_ok;
#ifndef DISCOVER_ONLY
if (ped_exception_throw (
_("Invalid partition table on %s "
"-- wrong signature %x."),
goto error;
goto read_ok;
}
#endif
/* parse the partitions from this table */
for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
continue;
/* process nested extended partitions after normal logical
* partitions, to make sure we get the order right.
*/
continue;
if (ped_exception_throw (
_("Invalid partition table - recursive "
"partition on %s."),
goto error;
continue; /* avoid infinite recursion */
}
if (is_extended_table)
else if (raw_part_is_extended (raw_part))
else
if (!part)
goto error;
if (!is_extended_table)
if (type != PED_PARTITION_EXTENDED)
if (!ok)
goto error;
/* non-nested extended partition */
goto error;
}
}
if (is_extended_table) {
/* process the nested extended partitions */
for (i = 0; i < DOS_N_PRI_PARTITIONS; i++) {
if (!raw_part_is_extended (raw_part))
continue;
if (part_start == sector) {
/* recursive table - already threw an
* exception above.
*/
continue;
}
goto error;
}
}
return 1;
return 0;
}
static int
{
if (!read_table (disk, 0, 0))
return 0;
#ifndef DISCOVER_ONLY
/* try to figure out the correct BIOS CHS values */
/* if the geometry was wrong, then we should reread, to
* make sure the metadata is allocated in the right places.
*/
return msdos_read (disk);
}
}
#endif
return 1;
}
#ifndef DISCOVER_ONLY
static int
{
}
return 1;
}
static int
const PedCHSGeometry* bios_geom,
{
return 1;
}
static int
{
void* s;
return 0;
DosRawTable *table = s;
int ok = 0;
goto cleanup;
if (part) {
if (!geom)
goto cleanup;
geom, lba_offset);
goto cleanup;
}
free (s);
return ok;
}
static int
{
void* table_sector;
}
}
/* Find the first logical partition, and write the partition table for it.
*/
static int
{
if (part)
else
}
{
int rc;
if (rc == -1)
return 0;
}
static int
{
int i;
void *s0;
return 0;
}
/* If there is no unique identifier, generate a random one */
if (!table->mbr_signature)
for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) {
if (!part)
continue;
goto write_fail;
if (!write_extended_partitions (disk))
goto write_fail;
}
}
if (!write_ok)
return 0;
return 0;
}
#endif /* !DISCOVER_ONLY */
static PedPartition*
const PedFileSystemType* fs_type,
{
if (!part)
goto error;
if (ped_partition_is_active (part)) {
if (!dos_data)
goto error_free_part;
} else {
}
return part;
return 0;
}
static PedPartition*
{
if (!new_part)
return NULL;
if (old_dos_data->orig) {
if (!new_dos_data->orig) {
return NULL;
}
}
return new_part;
}
static void
{
if (ped_partition_is_active (part)) {
}
}
static int
const PedFileSystemType* fs_type)
{
&& fs_type
else
return 1;
}
/* Don't change the system if it already is a diag type,
otherwise use Compaq as almost all vendors use that. */
return 1;
}
return 1;
}
return 1;
}
return 1;
}
return 1;
}
if (!fs_type)
else
return 1;
}
static void
{
}
static int
{
switch (flag) {
case PED_PARTITION_HIDDEN:
_("Extended partitions cannot be hidden on "
"msdos disk labels."));
return 0;
}
case PED_PARTITION_BOOT:
if (!state)
return 1;
continue;
}
return 1;
case PED_PARTITION_DIAG:
if (state)
case PED_PARTITION_RAID:
if (state)
case PED_PARTITION_LVM:
if (state)
case PED_PARTITION_LBA:
case PED_PARTITION_PALO:
if (state)
case PED_PARTITION_PREP:
if (state)
default:
return 0;
}
}
static int
{
switch (flag) {
case PED_PARTITION_HIDDEN:
return 0;
else
case PED_PARTITION_BOOT:
case PED_PARTITION_DIAG:
case PED_PARTITION_RAID:
case PED_PARTITION_LVM:
case PED_PARTITION_LBA:
case PED_PARTITION_PALO:
case PED_PARTITION_PREP:
default:
return 0;
}
}
static int
{
switch (flag) {
case PED_PARTITION_HIDDEN:
return 0;
else
return 1;
case PED_PARTITION_BOOT:
case PED_PARTITION_RAID:
case PED_PARTITION_LVM:
case PED_PARTITION_LBA:
case PED_PARTITION_PALO:
case PED_PARTITION_PREP:
case PED_PARTITION_DIAG:
return 1;
default:
return 0;
}
}
static PedGeometry*
{
if (!intersection)
return NULL;
return solution;
}
static PedGeometry*
PedGeometry* a, PedGeometry* b)
{
int a_cylinder;
int b_cylinder;
if (!a)
return b;
if (!b)
return a;
if (a_cylinder == b_cylinder) {
goto choose_a;
else
goto choose_b;
} else {
goto choose_a;
else
goto choose_b;
}
return NULL; /* never get here! */
ped_geometry_destroy (b);
return a;
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*
{
return NULL;
return NULL;
if (min_geom) {
return NULL;
return NULL;
return NULL;
} else {
/* Do not assume that length is larger than 1 cylinder's
worth of sectors. This is useful when testing with
a memory-mapped "disk" (a la scsi_debug) that is say,
2048 sectors long. */
return NULL;
return NULL;
}
}
/* 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:
*/
static PedConstraint*
const PedPartition *part,
const PedCHSGeometry* bios_geom,
const PedGeometry* min_geom)
{
/* 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) */
return NULL;
return NULL;
if (min_geom) {
return NULL;
return NULL;
} else {
return NULL;
return NULL;
}
}
/* 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*
{
return NULL;
return NULL;
if (is_start_part) {
return NULL;
} else {
return NULL;
return NULL;
}
}
/* 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*
const PedCHSGeometry* bios_geom)
{
if (!walk)
return NULL;
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.
*/
continue;
}
return min_geom;
}
static int
const PedConstraint* constraint)
{
min_geom)));
if (min_geom)
if (solution) {
return 1;
}
return 0;
}
static int
const PedCHSGeometry* bios_geom,
const PedPartition* ext_part,
int is_start_ext_part)
{
if (is_start_ext_part)
else
base_head = 0;
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*
{
/* 1) 2) */
}
if (walk)
return NULL;
return ped_constraint_new_from_max (&safe_space);
}
static int
const PedConstraint* constraint)
{
int head;
if (!intersect)
return 0;
ext_part, 1))
else
}
if (solution) {
return 1;
}
return 0;
}
static int
const PedConstraint* constraint)
{
else
}
static PedConstraint*
{
return ped_constraint_new_from_max (&max);
}
static PedConstraint*
{
if (min) {
} else {
}
return constraint;
}
static int
{
} else {
}
if (solution) {
return 1;
}
return 0;
}
static int
{
if (solution) {
return 1;
}
return 0;
}
static int
{
else
}
static int
{
return 1;
_("Parted can't resize partitions managed by "
"Windows Dynamic Disk."));
return 0;
}
return 1;
return 1;
#ifndef DISCOVER_ONLY
_("Unable to satisfy all constraints on the partition."));
#endif
return 0;
}
static int
{
if (!new_part)
goto error;
goto error_destroy_new_part;
return 1;
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
{
/* 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
*/
if (prev)
else
/* partition 5 doesn't need to have any metadata */
return 1;
PED_ASSERT (metadata_length > 0, return 0);
}
/*
* 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
{
// disk->part_list is the first partition on the disk.
return 0;
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
{
// disk->part_list is the first partition on the disk.
return 0;
}
}
if (!last_part)
return 0;
else {
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
{
// Ranges for the initial and final metadata partition.
init_start = 0;
else
if (!disk_specific->cylinder_alignment)
else
// Create the metadata partitions.
// init_end <= dev->length for devices that are _real_ small.
if (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 &&
return 0;
return 1;
}
static int
{
if (!add_startend_metadata (disk))
return 0;
if (ext_part) {
int i;
for (i=5; 1; i++) {
if (!log_part)
break;
return 0;
}
return 0;
}
return 1;
}
static int
{
int i;
for (i=1; i<=DOS_N_PRI_PARTITIONS; i++) {
if (!ped_disk_get_partition (disk, i))
return i;
}
return 0;
}
static int
{
int i;
for (i=5; 1; i++) {
if (!ped_disk_get_partition (disk, i))
return i;
}
}
static int
{
/* don't re-number a primary partition */
return 1;
else
return 1;
}
static int
{
return DOS_N_PRI_PARTITIONS;
}
static bool
{
*max_n = MAX_TOTAL_PART;
return true;
}
#include "pt-common.h"
};
.name = "msdos",
.ops = &msdos_disk_ops,
};
void
{
}
void
{
}