dos.c revision 7e7bd3dccbfe8f79e25e5c1554b5bc3a9aaca321
/*
libparted - a library for manipulating disk partitions
Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007
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 */
/* 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
};
#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 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;
#ifdef __sun
#define __attribute__(X) /*nothing*/
#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 {
char boot_code [440];
} __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 {
unsigned char system;
int boot;
int hidden;
int raid;
int lvm;
int lba;
int palo;
int prep;
static PedDiskType msdos_disk_type;
/* FIXME: factor out this function: copied from aix.c, with changes to
the description, and an added sector number argument.
Read sector, SECTOR_NUM (which has length DEV->sector_size) into malloc'd
storage. If the read fails, free the memory and return zero without
modifying *BUF. Otherwise, set *BUF to the new buffer and return 1. */
static int
{
PED_ASSERT (b != NULL, return 0);
ped_free (b);
return 0;
}
*buf = b;
return 1;
}
static int
{
int i;
return 0;
char *label;
return 0;
/* check magic */
goto probe_fail;
/* 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.
*/
goto probe_fail;
/* If this is a GPT disk, fail here */
for (i = 0; i < 4; 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)
return disk;
}
static PedDisk*
{
if (!new_disk)
return NULL;
return new_disk;
}
static void
{
}
#ifndef DISCOVER_ONLY
static int
{
return 0;
}
#endif /* !DISCOVER_ONLY */
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
{
return offset
}
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 */
head_size = 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 < 4; 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)
goto error;
/* non-nested extended partition */
goto error;
}
}
if (is_extended_table) {
/* process the nested extended partitions */
for (i = 0; i < 4; 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
{
return 0;
if (part) {
if (!geom)
return 0;
geom, lba_offset);
return 0;
}
}
static int
{
}
/* Find the first logical partition, and write the partition table for it.
*/
static int
{
if (part)
else
}
static inline uint32_t generate_random_id (void)
{
int rc;
if (rc == -1)
return 0;
}
static int
{
int i;
}
/* If there is no unique identifier, generate a random one */
if (!table.mbr_signature)
for (i=1; i<=4; i++) {
if (!part)
continue;
return 0;
if (!write_extended_partitions (disk))
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;
}
return 1;
}
return 1;
}
return 1;
}
return 1;
}
if (!fs_type)
else
return 1;
}
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_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:
case PED_PARTITION_BOOT:
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:
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*
{
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 {
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;
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 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*
{
/* 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);
}
static PedPartition*
{
if (!first_part)
return NULL;
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
{
if (!first_part)
return 1;
start = 0;
return 0;
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<=4; 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 4;
}
static PedDiskOps msdos_disk_ops = {
.probe = msdos_probe,
#ifndef DISCOVER_ONLY
.clobber = msdos_clobber,
#else
#endif
.alloc = msdos_alloc,
.free = msdos_free,
.read = msdos_read,
#ifndef DISCOVER_ONLY
.write = msdos_write,
#else
#endif
};
static PedDiskType msdos_disk_type = {
.name = "msdos",
.ops = &msdos_disk_ops,
};
void
{
}
void
{
}