1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007
   4     Free Software Foundation, Inc.
   5 
   6     This program is free software; you can redistribute it and/or modify
   7     it under the terms of the GNU General Public License as published by
   8     the Free Software Foundation; either version 3 of the License, or
   9     (at your option) any later version.
  10 
  11     This program is distributed in the hope that it will be useful,
  12     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14     GNU General Public License for more details.
  15 
  16     You should have received a copy of the GNU General Public License
  17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19 
  20 #include <config.h>
  21 
  22 #include <sys/time.h>
  23 #include <stdbool.h>
  24 #include <parted/parted.h>
  25 #include <parted/debug.h>
  26 #include <parted/endian.h>
  27 
  28 #if ENABLE_NLS
  29 #  include <libintl.h>
  30 #  define _(String) dgettext (PACKAGE, String)
  31 #else
  32 #  define _(String) (String)
  33 #endif /* ENABLE_NLS */
  34 
  35 /* this MBR boot code is loaded into 0000:7c00 by the BIOS.  See mbr.s for
  36  * the source, and how to build it
  37  */
  38 
  39 static const unsigned char MBR_BOOT_CODE[] = {
  40         0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00,
  41         0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
  42         0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9,
  43         0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00,
  44         0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b,
  45         0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75,
  46         0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb,
  47         0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b,
  48         0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00,
  49         0x00, 0xeb, 0xfe
  50 };
  51 
  52 #define MSDOS_MAGIC             0xAA55
  53 #define PARTITION_MAGIC_MAGIC   0xf6f6
  54 
  55 #define PARTITION_EMPTY         0x00
  56 #define PARTITION_FAT12         0x01
  57 #define PARTITION_FAT16_SM      0x04
  58 #define PARTITION_DOS_EXT       0x05
  59 #define PARTITION_FAT16         0x06
  60 #define PARTITION_NTFS          0x07
  61 #define PARTITION_HPFS          0x07
  62 #define PARTITION_FAT32         0x0b
  63 #define PARTITION_FAT32_LBA     0x0c
  64 #define PARTITION_FAT16_LBA     0x0e
  65 #define PARTITION_EXT_LBA       0x0f
  66 
  67 #define PART_FLAG_HIDDEN        0x10    /* Valid for FAT/NTFS only */
  68 #define PARTITION_FAT12_H       (PARTITION_FAT12        | PART_FLAG_HIDDEN)
  69 #define PARTITION_FAT16_SM_H    (PARTITION_FAT16_SM     | PART_FLAG_HIDDEN)
  70 #define PARTITION_DOS_EXT_H     (PARTITION_DOS_EXT      | PART_FLAG_HIDDEN)
  71 #define PARTITION_FAT16_H       (PARTITION_FAT16        | PART_FLAG_HIDDEN)
  72 #define PARTITION_NTFS_H        (PARTITION_NTFS         | PART_FLAG_HIDDEN)
  73 #define PARTITION_FAT32_H       (PARTITION_FAT32        | PART_FLAG_HIDDEN)
  74 #define PARTITION_FAT32_LBA_H   (PARTITION_FAT32_LBA    | PART_FLAG_HIDDEN)
  75 #define PARTITION_FAT16_LBA_H   (PARTITION_FAT16_LBA    | PART_FLAG_HIDDEN)
  76 
  77 #define PARTITION_COMPAQ_DIAG   0x12
  78 #define PARTITION_LDM           0x42
  79 #define PARTITION_LINUX_SWAP    0x82
  80 #define PARTITION_LINUX         0x83
  81 #define PARTITION_LINUX_EXT     0x85
  82 #define PARTITION_LINUX_LVM     0x8e
  83 #define PARTITION_SUN_UFS       0xbf
  84 #define PARTITION_DELL_DIAG     0xde
  85 #define PARTITION_GPT           0xee
  86 #define PARTITION_PALO          0xf0
  87 #define PARTITION_PREP          0x41
  88 #define PARTITION_LINUX_RAID    0xfd
  89 #define PARTITION_LINUX_LVM_OLD 0xfe
  90 
  91 /* This constant contains the maximum cylinder number that can be represented
  92  * in (C,H,S) notation.  Higher cylinder numbers are reserved for
  93  * "too big" indicators (in which case only LBA addressing can be used).
  94  *      Some partition tables in the wild indicate this number is 1021.
  95  * (i.e. 1022 is sometimes used to indicate "use LBA").
  96  */
  97 #define MAX_CHS_CYLINDER        1021
  98 
  99 typedef struct _DosRawPartition         DosRawPartition;
 100 typedef struct _DosRawTable             DosRawTable;
 101 
 102 #ifdef __sun
 103 #define __attribute__(X)        /*nothing*/
 104 #endif /* __sun */
 105 
 106 /* note: lots of bit-bashing here, thus, you shouldn't look inside it.
 107  * Use chs_to_sector() and sector_to_chs() instead.
 108  */
 109 #ifdef __sun
 110 #pragma pack(1)
 111 #endif
 112 typedef struct {
 113         uint8_t         head;
 114         uint8_t         sector;
 115         uint8_t         cylinder;
 116 } __attribute__((packed)) RawCHS;
 117 
 118 /* ripped from Linux source */
 119 struct _DosRawPartition {
 120         uint8_t         boot_ind;       /* 00:  0x80 - active */
 121         RawCHS          chs_start;      /* 01: */
 122         uint8_t         type;           /* 04: partition type */
 123         RawCHS          chs_end;        /* 05: */
 124         uint32_t        start;          /* 08: starting sector counting from 0 */
 125         uint32_t        length;         /* 0c: nr of sectors in partition */
 126 } __attribute__((packed));
 127 
 128 struct _DosRawTable {
 129         char                    boot_code [440];
 130         uint32_t                mbr_signature;  /* really a unique ID */
 131         uint16_t                Unknown;
 132         DosRawPartition         partitions [4];
 133         uint16_t                magic;
 134 } __attribute__((packed));
 135 #ifdef __sun
 136 #pragma pack()
 137 #endif
 138 
 139 
 140 /* OrigState is information we want to preserve about the partition for
 141  * dealing with CHS issues
 142  */
 143 typedef struct {
 144         PedGeometry     geom;
 145         DosRawPartition raw_part;
 146         PedSector       lba_offset;     /* needed for computing start/end for
 147                                          * logical partitions */
 148 } OrigState;
 149 
 150 typedef struct {
 151         unsigned char   system;
 152         int             boot;
 153         int             hidden;
 154         int             raid;
 155         int             lvm;
 156         int             lba;
 157         int             palo;
 158         int             prep;
 159         OrigState*      orig;                   /* used for CHS stuff */
 160 } DosPartitionData;
 161 
 162 static PedDiskType msdos_disk_type;
 163 
 164 /* FIXME: factor out this function: copied from aix.c, with changes to
 165    the description, and an added sector number argument.
 166    Read sector, SECTOR_NUM (which has length DEV->sector_size) into malloc'd
 167    storage.  If the read fails, free the memory and return zero without
 168    modifying *BUF.  Otherwise, set *BUF to the new buffer and return 1.  */
 169 static int
 170 read_sector (const PedDevice *dev, PedSector sector_num, char **buf)
 171 {
 172         char *b = ped_malloc (dev->sector_size);
 173         PED_ASSERT (b != NULL, return 0);
 174         if (!ped_device_read (dev, b, sector_num, 1)) {
 175                 ped_free (b);
 176                 return 0;
 177         }
 178         *buf = b;
 179         return 1;
 180 }
 181 
 182 static int
 183 msdos_probe (const PedDevice *dev)
 184 {
 185         PedDiskType*    disk_type;
 186         DosRawTable*    part_table;
 187         int             i;
 188 
 189         PED_ASSERT (dev != NULL, return 0);
 190 
 191         if (dev->sector_size < sizeof *part_table)
 192                 return 0;
 193 
 194         char *label;
 195         if (!read_sector (dev, 0, &label))
 196                 return 0;
 197 
 198         part_table = (DosRawTable *) label;
 199 
 200         /* check magic */
 201         if (PED_LE16_TO_CPU (part_table->magic) != MSDOS_MAGIC)
 202                 goto probe_fail;
 203 
 204         /* if this is a FAT fs, fail here.  Note that the Smart Boot Manager
 205          * Loader (SBML) signature indicates a partition table, not a file
 206          * system.
 207          */
 208         if ((!strncmp (part_table->boot_code + 0x36, "FAT", 3)
 209             && strncmp (part_table->boot_code + 0x40, "SBML", 4) != 0)
 210             || !strncmp (part_table->boot_code + 0x52, "FAT", 3))
 211                 goto probe_fail;
 212 
 213         /* If this is a GPT disk, fail here */
 214         for (i = 0; i < 4; i++) {
 215                 if (part_table->partitions[i].type == PARTITION_GPT)
 216                         goto probe_fail;
 217         }
 218 
 219         /* If this is an AIX Physical Volume, fail here.  IBMA in EBCDIC */
 220         if (part_table->boot_code[0] == (char) 0xc9 &&
 221             part_table->boot_code[1] == (char) 0xc2 &&
 222             part_table->boot_code[2] == (char) 0xd4 &&
 223             part_table->boot_code[3] == (char) 0xc1)
 224                 goto probe_fail;
 225 
 226 #ifdef ENABLE_PC98
 227         /* HACK: it's impossible to tell PC98 and msdos disk labels apart.
 228          * Someone made the signatures the same (very clever).  Since
 229          * PC98 has some idiosyncracies with it's boot-loader, it's detection
 230          * is more reliable */
 231         disk_type = ped_disk_type_get ("pc98");
 232         if (disk_type && disk_type->ops->probe (dev))
 233                 goto probe_fail;
 234 #endif /* ENABLE_PC98 */
 235 
 236         free (label);
 237         return 1;
 238 
 239  probe_fail:
 240         free (label);
 241         return 0;
 242 }
 243 
 244 static PedDisk*
 245 msdos_alloc (const PedDevice* dev)
 246 {
 247         PedDisk* disk;
 248         PED_ASSERT (dev != NULL, return NULL);
 249 
 250         disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type);
 251         if (disk)
 252                 disk->disk_specific = NULL;
 253         return disk;
 254 }
 255 
 256 static PedDisk*
 257 msdos_duplicate (const PedDisk* disk)
 258 {
 259         PedDisk*        new_disk;
 260        
 261         new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type);
 262         if (!new_disk)
 263                 return NULL;
 264         new_disk->disk_specific = NULL;
 265         return new_disk;
 266 }
 267 
 268 static void
 269 msdos_free (PedDisk* disk)
 270 {
 271         PED_ASSERT (disk != NULL, return);
 272 
 273         _ped_disk_free (disk);
 274 }
 275 
 276 #ifndef DISCOVER_ONLY
 277 static int
 278 msdos_clobber (PedDevice* dev)
 279 {
 280         DosRawTable             table;
 281 
 282         PED_ASSERT (dev != NULL, return 0);
 283         PED_ASSERT (msdos_probe (dev), return 0);
 284 
 285         if (!ped_device_read (dev, &table, 0, 1))
 286                 return 0;
 287         table.magic = 0;
 288         return ped_device_write (dev, (void*) &table, 0, 1);
 289 }
 290 #endif /* !DISCOVER_ONLY */
 291 
 292 static int
 293 chs_get_cylinder (const RawCHS* chs)
 294 {
 295         return chs->cylinder + ((chs->sector >> 6) << 8);
 296 }
 297 
 298 static int
 299 chs_get_head (const RawCHS* chs)
 300 {
 301         return chs->head;
 302 }
 303 
 304 /* counts from 0 */
 305 static int
 306 chs_get_sector (const RawCHS* chs)
 307 {
 308         return (chs->sector & 0x3f) - 1;
 309 }
 310 
 311 static PedSector
 312 chs_to_sector (const PedDevice* dev, const PedCHSGeometry *bios_geom,
 313                const RawCHS* chs)
 314 {
 315         PedSector       c;              /* not measured in sectors, but need */
 316         PedSector       h;              /* lots of bits */
 317         PedSector       s;
 318 
 319         PED_ASSERT (bios_geom != NULL, return 0);
 320         PED_ASSERT (chs != NULL, return 0);
 321 
 322         c = chs_get_cylinder (chs);
 323         h = chs_get_head (chs);
 324         s = chs_get_sector (chs);
 325 
 326         if (c > MAX_CHS_CYLINDER)            /* MAGIC: C/H/S is irrelevant */
 327                 return 0;
 328         if (s < 0)
 329                 return 0;
 330         return ((c * bios_geom->heads + h) * bios_geom->sectors + s)
 331                 * (dev->sector_size / 512);
 332 }
 333 
 334 static void
 335 sector_to_chs (const PedDevice* dev, const PedCHSGeometry* bios_geom,
 336                PedSector sector, RawCHS* chs)
 337 {
 338         PedSector       real_c, real_h, real_s;
 339 
 340         PED_ASSERT (dev != NULL, return);
 341         PED_ASSERT (chs != NULL, return);
 342         
 343         if (!bios_geom)
 344                 bios_geom = &dev->bios_geom;
 345 
 346         sector /= (dev->sector_size / 512);
 347 
 348         real_c = sector / (bios_geom->heads * bios_geom->sectors);
 349         real_h = (sector / bios_geom->sectors) % bios_geom->heads;
 350         real_s = sector % bios_geom->sectors;
 351 
 352         if (real_c > MAX_CHS_CYLINDER) {
 353                 real_c = 1023;
 354                 real_h = bios_geom->heads - 1;
 355                 real_s = bios_geom->sectors - 1;
 356         }
 357 
 358         chs->cylinder = real_c % 0x100;
 359         chs->head = real_h;
 360         chs->sector = real_s + 1 + (real_c >> 8 << 6);
 361 }
 362 
 363 static PedSector
 364 legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom,
 365               const DosRawPartition* raw_part)
 366 {
 367         PED_ASSERT (disk != NULL, return 0);
 368         PED_ASSERT (raw_part != NULL, return 0);
 369 
 370         return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start);
 371 }
 372 
 373 static PedSector
 374 legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom,
 375             const DosRawPartition* raw_part)
 376 {
 377         PED_ASSERT (disk != NULL, return 0);
 378         PED_ASSERT (raw_part != NULL, return 0);
 379 
 380         return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end);
 381 }
 382 
 383 static PedSector
 384 linear_start (const PedDisk* disk, const DosRawPartition* raw_part,
 385               PedSector offset)
 386 {
 387         PED_ASSERT (disk != NULL, return 0);
 388         PED_ASSERT (raw_part != NULL, return 0);
 389 
 390         return offset
 391                + PED_LE32_TO_CPU (raw_part->start)
 392                         * (disk->dev->sector_size / 512);
 393 }
 394 
 395 static PedSector
 396 linear_end (const PedDisk* disk, const DosRawPartition* raw_part,
 397             PedSector offset)
 398 {
 399         PED_ASSERT (disk != NULL, return 0);
 400         PED_ASSERT (raw_part != NULL, return 0);
 401 
 402         return linear_start (disk, raw_part, offset)
 403                + (PED_LE32_TO_CPU (raw_part->length) - 1)
 404                         * (disk->dev->sector_size / 512);
 405 }
 406 
 407 #ifndef DISCOVER_ONLY
 408 static int
 409 partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom)
 410 {
 411         PedSector               leg_start, leg_end;
 412         DosPartitionData*       dos_data;
 413         PedDisk*                disk;
 414 
 415         PED_ASSERT (part != NULL, return 0);
 416         PED_ASSERT (part->disk != NULL, return 0);
 417         PED_ASSERT (part->disk_specific != NULL, return 0);
 418         dos_data = part->disk_specific;
 419 
 420         if (!dos_data->orig)
 421                 return 1;
 422 
 423         disk = part->disk;
 424         leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part);
 425         leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part);
 426 
 427         if (leg_start && leg_start != dos_data->orig->geom.start)
 428                 return 0;
 429         if (leg_end && leg_end != dos_data->orig->geom.end)
 430                 return 0;
 431         return 1;
 432 }
 433 
 434 static int
 435 disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
 436 {
 437         PedPartition* part = NULL;
 438 
 439         PED_ASSERT (disk != NULL, return 0);
 440 
 441         while ((part = ped_disk_next_partition (disk, part))) {
 442                 if (ped_partition_is_active (part)) {
 443                         if (!partition_check_bios_geometry (part, bios_geom))
 444                                 return 0;
 445                 }
 446         }
 447 
 448         return 1;
 449 }
 450 
 451 static int
 452 probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
 453 {
 454         const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL};
 455         int i;
 456         int found;
 457         unsigned char* buf;
 458         int sectors;
 459         int heads;
 460         int res = 0;
 461 
 462         PED_ASSERT (bios_geom        != NULL, return 0);
 463         PED_ASSERT (part             != NULL, return 0);
 464         PED_ASSERT (part->disk       != NULL, return 0);
 465         PED_ASSERT (part->disk->dev  != NULL, return 0);
 466         PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
 467                     return 0);
 468 
 469         buf = ped_malloc (part->disk->dev->sector_size);
 470         
 471         if (!buf)
 472                 return 0;
 473 
 474         if (!part->fs_type)
 475                 goto end;
 476 
 477         found = 0;
 478         for (i = 0; ms_types[i]; i++) {
 479                 if (!strcmp(ms_types[i], part->fs_type->name))
 480                         found = 1;
 481         }
 482         if (!found)
 483                 goto end;
 484 
 485         if (!ped_geometry_read(&part->geom, buf, 0, 1))
 486                 goto end;
 487 
 488         /* shared by the start of all Microsoft file systems */
 489         sectors = buf[0x18] + (buf[0x19] << 8);
 490         heads = buf[0x1a] + (buf[0x1b] << 8);
 491 
 492         if (sectors < 1 || sectors > 63)
 493                 goto end;
 494         if (heads > 255 || heads < 1)
 495                 goto end;
 496 
 497         bios_geom->sectors = sectors;
 498         bios_geom->heads = heads;
 499         bios_geom->cylinders = part->disk->dev->length / (sectors * heads);
 500         res = 1;
 501 end:
 502         ped_free(buf);
 503         return res;
 504 }
 505 
 506 /* This function attempts to infer the BIOS CHS geometry of the hard disk
 507  * from the CHS + LBA information contained in the partition table from
 508  * a single partition's entry.
 509  *
 510  * This involves some maths.  Let (c,h,s,a) be the starting cylinder,
 511  * starting head, starting sector and LBA start address of the partition.
 512  * Likewise, (C,H,S,A) the end addresses.  Using both of these pieces
 513  * of information, we want to deduce cyl_sectors and head_sectors which
 514  * are the sizes of a single cylinder and a single head, respectively.
 515  *
 516  * The relationships are:
 517  * c*cyl_sectors + h * head_sectors + s = a
 518  * C*cyl_sectors + H * head_sectors + S = A
 519  *
 520  * We can rewrite this in matrix form:
 521  *
 522  * [ c h ] [ cyl_sectors  ]  =  [ s - a ]  =  [ a_ ]
 523  * [ C H ] [ head_sectors ]     [ S - A ]     [ A_ ].
 524  * 
 525  * (s - a is abbreviated to a_to simplify the notation.)
 526  *
 527  * This can be abbreviated into augmented matrix form:
 528  *
 529  * [ c h | a_ ]
 530  * [ C H | A_ ].
 531  * 
 532  * Solving these equations requires following the row reduction algorithm.  We
 533  * need to be careful about a few things though:
 534  *      - the equations might be linearly dependent, in which case there
 535  *      are many solutions.
 536  *      - the equations might be inconsistent, in which case there
 537  *      are no solutions.  (Inconsistent partition table entry!)
 538  *      - there might be zeros, so we need to be careful about applying
 539  *      the algorithm.  We know, however, that C > 0.
 540  */
 541 static int
 542 probe_partition_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
 543 {
 544         DosPartitionData* dos_data;
 545         RawCHS* start_chs;
 546         RawCHS* end_chs;
 547         PedSector c, h, s, a, a_;       /* start */
 548         PedSector C, H, S, A, A_;       /* end */
 549         PedSector dont_overflow, denum;
 550         PedSector cyl_size, head_size;
 551         PedSector cylinders, heads, sectors;
 552 
 553         PED_ASSERT (part != NULL, return 0);
 554         PED_ASSERT (part->disk_specific != NULL, return 0);
 555         PED_ASSERT (bios_geom != NULL, return 0);
 556 
 557         dos_data = part->disk_specific;
 558 
 559         if (!dos_data->orig)
 560                 return 0;
 561 
 562         start_chs = &dos_data->orig->raw_part.chs_start;
 563         c = chs_get_cylinder (start_chs);
 564         h = chs_get_head (start_chs);
 565         s = chs_get_sector (start_chs);
 566         a = dos_data->orig->geom.start;
 567         a_ = a - s;
 568 
 569         end_chs = &dos_data->orig->raw_part.chs_end;
 570         C = chs_get_cylinder (end_chs);
 571         H = chs_get_head (end_chs);
 572         S = chs_get_sector (end_chs);
 573         A = dos_data->orig->geom.end;
 574         A_ = A - S;
 575 
 576         if (h < 0 || H < 0 || h > 254 || H > 254)
 577                 return 0;
 578         if (c > C)
 579                 return 0;
 580 
 581         /* If no geometry is feasible, then don't even bother.
 582          * Useful for eliminating assertions for broken partition
 583          * tables generated by Norton Ghost et al.
 584          */
 585         if (A > (C+1) * 255 * 63)
 586                 return 0;
 587 
 588         /* Not enough information.  In theory, we can do better.  Should we? */
 589         if (C > MAX_CHS_CYLINDER)
 590                 return 0;
 591         if (C == 0)
 592                 return 0;
 593 
 594         /* Calculate the maximum number that can be multiplied by
 595          * any head count without overflowing a PedSector
 596          * 2^8 = 256, 8 bits + 1(sign bit) = 9
 597          */
 598         dont_overflow = 1;
 599         dont_overflow <<= (8*sizeof(dont_overflow)) - 9;
 600         dont_overflow--;
 601 
 602         if (a_ > dont_overflow || A_ > dont_overflow)
 603                 return 0;
 604 
 605         /* The matrix is solved by :
 606          *
 607          * [ c h | a_]                  R1
 608          * [ C H | A_]                  R2
 609          *
 610          * (cH - Ch) cyl_size = a_H - A_h               H R1 - h R2
 611          * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch)
 612          *
 613          * (Hc - hC) head_size = A_c - a_C              c R2 - C R1
 614          * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch)
 615          *
 616          *   But this calculation of head_size would need
 617          *   not overflowing A_c or a_C
 618          *   So substitution is use instead, to minimize dimension
 619          *   of temporary results :
 620          *
 621          * If h != 0 : head_size = ( a_ - c cyl_size ) / h
 622          * If H != 0 : head_size = ( A_ - C cyl_size ) / H
 623          *
 624          */
 625         denum = c * H - C * h;
 626         if (denum == 0)
 627                 return 0;
 628 
 629         cyl_size = (a_*H - A_*h) / denum;
 630         /* Check for non integer result */
 631         if (cyl_size * denum != a_*H - A_*h)
 632                 return 0;
 633 
 634         PED_ASSERT (cyl_size > 0, return 0);
 635         PED_ASSERT (cyl_size <= 255 * 63, return 0);
 636 
 637         if (h > 0)
 638                 head_size = ( a_ - c * cyl_size ) / h;
 639         else if (H > 0)
 640                 head_size = ( A_ - C * cyl_size ) / H;
 641         else { 
 642                 /* should not happen because denum != 0 */
 643                 head_size = 0;
 644                 PED_ASSERT (0, return 0);
 645         }
 646 
 647         PED_ASSERT (head_size > 0, return 0);
 648         PED_ASSERT (head_size <= 63, return 0);
 649 
 650         cylinders = part->disk->dev->length / cyl_size;
 651         heads = cyl_size / head_size;
 652         sectors = head_size;
 653 
 654         PED_ASSERT (heads > 0, return 0);
 655         PED_ASSERT (heads < 256, return 0);
 656 
 657         PED_ASSERT (sectors > 0, return 0);
 658         PED_ASSERT (sectors <= 63, return 0);
 659 
 660         /* Some broken OEM partitioning program(s) seem to have an out-by-one
 661          * error on the end of partitions.  We should offer to fix the
 662          * partition table...
 663          */
 664         if (((C + 1) * heads + H) * sectors + S == A)
 665                 C++;
 666 
 667         PED_ASSERT ((c * heads + h) * sectors + s == a, return 0);
 668         PED_ASSERT ((C * heads + H) * sectors + S == A, return 0);
 669 
 670         bios_geom->cylinders = cylinders;
 671         bios_geom->heads = heads;
 672         bios_geom->sectors = sectors;
 673 
 674         return 1;
 675 }
 676 
 677 static void
 678 partition_probe_bios_geometry (const PedPartition* part,
 679                                PedCHSGeometry* bios_geom)
 680 {
 681         PED_ASSERT (part != NULL, return);
 682         PED_ASSERT (part->disk != NULL, return);
 683         PED_ASSERT (bios_geom != NULL, return);
 684 
 685         if (ped_partition_is_active (part)) {
 686                 if (probe_partition_for_geom (part, bios_geom))
 687                         return;
 688                 if (part->type & PED_PARTITION_EXTENDED) {
 689                         if (probe_filesystem_for_geom (part, bios_geom))
 690                                 return;
 691                 }
 692         }
 693         if (part->type & PED_PARTITION_LOGICAL) {
 694                 PedPartition* ext_part;
 695                 ext_part = ped_disk_extended_partition (part->disk);
 696                 PED_ASSERT (ext_part != NULL, return);
 697                 partition_probe_bios_geometry (ext_part, bios_geom);
 698         } else {
 699                 *bios_geom = part->disk->dev->bios_geom;
 700         }
 701 }
 702 
 703 static void
 704 disk_probe_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
 705 {
 706         PedPartition*   part;
 707 
 708         /* first look at the boot partition */
 709         part = NULL;
 710         while ((part = ped_disk_next_partition (disk, part))) {
 711                 if (!ped_partition_is_active (part))
 712                         continue;
 713                 if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) {
 714                         if (probe_filesystem_for_geom (part, bios_geom))
 715                                 return;
 716                         if (probe_partition_for_geom (part, bios_geom))
 717                                 return;
 718                 }
 719         }
 720 
 721         /* that didn't work... try all partition table entries */
 722         part = NULL;
 723         while ((part = ped_disk_next_partition (disk, part))) {
 724                 if (ped_partition_is_active (part)) {
 725                         if (probe_partition_for_geom (part, bios_geom))
 726                                 return;
 727                 }
 728         }
 729 
 730         /* that didn't work... look at all file systems */
 731         part = NULL;
 732         while ((part = ped_disk_next_partition (disk, part))) {
 733                 if (ped_partition_is_active (part)) {
 734                         if (probe_filesystem_for_geom (part, bios_geom))
 735                                 return;
 736                 }
 737         }
 738 }
 739 #endif /* !DISCOVER_ONLY */
 740 
 741 static int
 742 raw_part_is_extended (const DosRawPartition* raw_part)
 743 {
 744         PED_ASSERT (raw_part != NULL, return 0);
 745 
 746         switch (raw_part->type) {
 747         case PARTITION_DOS_EXT:
 748         case PARTITION_EXT_LBA:
 749         case PARTITION_LINUX_EXT:
 750                 return 1;
 751 
 752         default:
 753                 return 0;
 754         }
 755 
 756         return 0;
 757 }
 758 
 759 static int
 760 raw_part_is_hidden (const DosRawPartition* raw_part)
 761 {
 762         PED_ASSERT (raw_part != NULL, return 0);
 763 
 764         switch (raw_part->type) {
 765         case PARTITION_FAT12_H:
 766         case PARTITION_FAT16_SM_H:
 767         case PARTITION_FAT16_H:
 768         case PARTITION_FAT32_H:
 769         case PARTITION_NTFS_H:
 770         case PARTITION_FAT32_LBA_H:
 771         case PARTITION_FAT16_LBA_H:
 772                 return 1;
 773 
 774         default:
 775                 return 0;
 776         }
 777 
 778         return 0;
 779 }
 780 
 781 static int
 782 raw_part_is_lba (const DosRawPartition* raw_part)
 783 {
 784         PED_ASSERT (raw_part != NULL, return 0);
 785 
 786         switch (raw_part->type) {
 787         case PARTITION_FAT32_LBA:
 788         case PARTITION_FAT16_LBA:
 789         case PARTITION_EXT_LBA:
 790         case PARTITION_FAT32_LBA_H:
 791         case PARTITION_FAT16_LBA_H:
 792                 return 1;
 793 
 794         default:
 795                 return 0;
 796         }
 797 
 798         return 0;
 799 }
 800 
 801 static PedPartition*
 802 raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part,
 803                 PedSector lba_offset, PedPartitionType type)
 804 {
 805         PedPartition* part;
 806         DosPartitionData* dos_data;
 807 
 808         PED_ASSERT (disk != NULL, return NULL);
 809         PED_ASSERT (raw_part != NULL, return NULL);
 810 
 811         part = ped_partition_new (
 812                 disk, type, NULL,
 813                 linear_start (disk, raw_part, lba_offset),
 814                 linear_end (disk, raw_part, lba_offset));
 815         if (!part)
 816                 return NULL;
 817         dos_data = part->disk_specific;
 818         dos_data->system = raw_part->type;
 819         dos_data->boot = raw_part->boot_ind != 0;
 820         dos_data->hidden = raw_part_is_hidden (raw_part);
 821         dos_data->raid = raw_part->type == PARTITION_LINUX_RAID;
 822         dos_data->lvm = raw_part->type == PARTITION_LINUX_LVM_OLD
 823                         || raw_part->type == PARTITION_LINUX_LVM;
 824         dos_data->lba = raw_part_is_lba (raw_part);
 825         dos_data->palo = raw_part->type == PARTITION_PALO;
 826         dos_data->prep = raw_part->type == PARTITION_PREP;
 827         dos_data->orig = ped_malloc (sizeof (OrigState));
 828         if (!dos_data->orig) {
 829                 ped_partition_destroy (part);
 830                 return NULL;
 831         }
 832         dos_data->orig->geom = part->geom;
 833         dos_data->orig->raw_part = *raw_part;
 834         dos_data->orig->lba_offset = lba_offset;
 835         return part;
 836 }
 837 
 838 static int
 839 read_table (PedDisk* disk, PedSector sector, int is_extended_table)
 840 {
 841         int                     i;
 842         DosRawTable*            table;
 843         DosRawPartition*        raw_part;
 844         PedPartition*           part;
 845         PedPartitionType        type;
 846         PedSector               lba_offset;
 847         PedConstraint*          constraint_exact;
 848 
 849         PED_ASSERT (disk != NULL, return 0);
 850         PED_ASSERT (disk->dev != NULL, return 0);
 851 
 852         char *label = NULL;
 853         if (!read_sector (disk->dev, sector, &label))
 854                 goto error;
 855 
 856         table = (DosRawTable *) label;
 857 
 858         /* weird: empty extended partitions are filled with 0xf6 by PM */
 859         if (is_extended_table
 860             && PED_LE16_TO_CPU (table->magic) == PARTITION_MAGIC_MAGIC)
 861                 goto read_ok;
 862 
 863 #ifndef DISCOVER_ONLY
 864         if (PED_LE16_TO_CPU (table->magic) != MSDOS_MAGIC) {
 865                 if (ped_exception_throw (
 866                         PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
 867                         _("Invalid partition table on %s "
 868                           "-- wrong signature %x."),
 869                         disk->dev->path,
 870                         PED_LE16_TO_CPU (table->magic))
 871                                 != PED_EXCEPTION_IGNORE)
 872                         goto error;
 873                 goto read_ok;
 874         }
 875 #endif
 876 
 877         /* parse the partitions from this table */
 878         for (i = 0; i < 4; i++) {
 879                 raw_part = &table->partitions [i];
 880                 if (raw_part->type == PARTITION_EMPTY || !raw_part->length)
 881                         continue;
 882 
 883                 /* process nested extended partitions after normal logical
 884                  * partitions, to make sure we get the order right.
 885                  */
 886                 if (is_extended_table && raw_part_is_extended (raw_part))
 887                         continue;       
 888 
 889                 lba_offset = is_extended_table ? sector : 0;
 890 
 891                 if (linear_start (disk, raw_part, lba_offset) == sector) {
 892                         if (ped_exception_throw (
 893                                 PED_EXCEPTION_ERROR,
 894                                 PED_EXCEPTION_IGNORE_CANCEL,
 895                                 _("Invalid partition table - recursive "
 896                                 "partition on %s."),
 897                                 disk->dev->path)
 898                                         != PED_EXCEPTION_IGNORE)
 899                                 goto error;
 900                         continue;       /* avoid infinite recursion */
 901                 }
 902 
 903                 if (is_extended_table)
 904                         type = PED_PARTITION_LOGICAL;
 905                 else if (raw_part_is_extended (raw_part))
 906                         type = PED_PARTITION_EXTENDED;
 907                 else
 908                         type = PED_PARTITION_NORMAL;
 909 
 910                 part = raw_part_parse (disk, raw_part, lba_offset, type);
 911                 if (!part)
 912                         goto error;
 913                 if (!is_extended_table)
 914                         part->num = i + 1;
 915                 if (type != PED_PARTITION_EXTENDED)
 916                         part->fs_type = ped_file_system_probe (&part->geom);
 917 
 918                 constraint_exact = ped_constraint_exact (&part->geom);
 919                 if (!ped_disk_add_partition (disk, part, constraint_exact))
 920                         goto error;
 921                 ped_constraint_destroy (constraint_exact);
 922 
 923                 /* non-nested extended partition */
 924                 if (part->type == PED_PARTITION_EXTENDED) {
 925                         if (!read_table (disk, part->geom.start, 1))
 926                                 goto error;
 927                 }
 928         }
 929 
 930         if (is_extended_table) {
 931                 /* process the nested extended partitions */
 932                 for (i = 0; i < 4; i++) {
 933                         PedSector part_start;
 934 
 935                         raw_part = &table->partitions [i];
 936                         if (!raw_part_is_extended (raw_part))
 937                                 continue;
 938 
 939                         lba_offset = ped_disk_extended_partition
 940                                         (disk)->geom.start;
 941                         part_start = linear_start (disk, raw_part, lba_offset);
 942                         if (part_start == sector) {
 943                                 /* recursive table - already threw an
 944                                  * exception above.
 945                                  */
 946                                 continue;
 947                         }
 948                         if (!read_table (disk, part_start, 1))
 949                                 goto error;
 950                 }
 951         }
 952 
 953 read_ok:
 954         free (label);
 955         return 1;
 956 
 957 error:
 958         free (label);
 959         ped_disk_delete_all (disk);
 960         return 0;
 961 }
 962 
 963 static int
 964 msdos_read (PedDisk* disk)
 965 {
 966         PED_ASSERT (disk != NULL, return 0);
 967         PED_ASSERT (disk->dev != NULL, return 0);
 968 
 969         ped_disk_delete_all (disk);
 970         if (!read_table (disk, 0, 0))
 971                 return 0;
 972 
 973 #ifndef DISCOVER_ONLY
 974         /* try to figure out the correct BIOS CHS values */
 975         if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) {
 976                 PedCHSGeometry bios_geom = disk->dev->bios_geom;
 977                 disk_probe_bios_geometry (disk, &bios_geom);
 978 
 979                 /* if the geometry was wrong, then we should reread, to
 980                  * make sure the metadata is allocated in the right places.
 981                  */
 982                 if (disk->dev->bios_geom.cylinders != bios_geom.cylinders
 983                     || disk->dev->bios_geom.heads != bios_geom.heads
 984                     || disk->dev->bios_geom.sectors != bios_geom.sectors) {
 985                         disk->dev->bios_geom = bios_geom;
 986                         return msdos_read (disk);
 987                 }
 988         }
 989 #endif
 990 
 991         return 1;
 992 }
 993 
 994 #ifndef DISCOVER_ONLY
 995 static int
 996 fill_raw_part (DosRawPartition* raw_part,
 997                const PedPartition* part, PedSector offset)
 998 {
 999         DosPartitionData*       dos_data;
1000         PedCHSGeometry          bios_geom;
1001 
1002         PED_ASSERT (raw_part != NULL, return 0);
1003         PED_ASSERT (part != NULL, return 0);
1004 
1005         partition_probe_bios_geometry (part, &bios_geom);
1006 
1007         dos_data = part->disk_specific;
1008 
1009         raw_part->boot_ind = 0x80 * dos_data->boot;
1010         raw_part->type = dos_data->system;
1011         raw_part->start = PED_CPU_TO_LE32 ((part->geom.start - offset)
1012                                 / (part->disk->dev->sector_size / 512));
1013         raw_part->length = PED_CPU_TO_LE32 (part->geom.length
1014                                 / (part->disk->dev->sector_size / 512));
1015 
1016         sector_to_chs (part->disk->dev, &bios_geom, part->geom.start,
1017                        &raw_part->chs_start);
1018         sector_to_chs (part->disk->dev, &bios_geom, part->geom.end,
1019                        &raw_part->chs_end);
1020 
1021         if (dos_data->orig) {
1022                 DosRawPartition* orig_raw_part = &dos_data->orig->raw_part;
1023                 if (dos_data->orig->geom.start == part->geom.start)
1024                         raw_part->chs_start = orig_raw_part->chs_start;
1025                 if (dos_data->orig->geom.end == part->geom.end)
1026                         raw_part->chs_end = orig_raw_part->chs_end;
1027         }
1028 
1029         return 1;
1030 }
1031 
1032 static int
1033 fill_ext_raw_part_geom (DosRawPartition* raw_part,
1034                         const PedCHSGeometry* bios_geom,
1035                         const PedGeometry* geom, PedSector offset)
1036 {
1037         PED_ASSERT (raw_part != NULL, return 0);
1038         PED_ASSERT (geom != NULL, return 0);
1039         PED_ASSERT (geom->dev != NULL, return 0);
1040 
1041         raw_part->boot_ind = 0;
1042         raw_part->type = PARTITION_DOS_EXT;
1043         raw_part->start = PED_CPU_TO_LE32 ((geom->start - offset)
1044                                 / (geom->dev->sector_size / 512));
1045         raw_part->length = PED_CPU_TO_LE32 (geom->length
1046                                 / (geom->dev->sector_size / 512));
1047 
1048         sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start);
1049         sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1,
1050                        &raw_part->chs_end);
1051 
1052         return 1;
1053 }
1054 
1055 static int
1056 write_ext_table (const PedDisk* disk,
1057                  PedSector sector, const PedPartition* logical)
1058 {
1059         DosRawTable             table;
1060         PedPartition*           part;
1061         PedSector               lba_offset;
1062 
1063         PED_ASSERT (disk != NULL, return 0);
1064         PED_ASSERT (ped_disk_extended_partition (disk) != NULL, return 0);
1065         PED_ASSERT (logical != NULL, return 0);
1066 
1067         lba_offset = ped_disk_extended_partition (disk)->geom.start;
1068 
1069         memset (&table, 0, sizeof (DosRawTable));
1070         table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1071 
1072         if (!fill_raw_part (&table.partitions[0], logical, sector))
1073                 return 0;
1074 
1075         part = ped_disk_get_partition (disk, logical->num + 1);
1076         if (part) {
1077                 PedGeometry*            geom;
1078                 PedCHSGeometry          bios_geom;
1079 
1080                 geom = ped_geometry_new (disk->dev, part->prev->geom.start,
1081                                 part->geom.end - part->prev->geom.start + 1);
1082                 if (!geom)
1083                         return 0;
1084                 partition_probe_bios_geometry (part, &bios_geom);
1085                 fill_ext_raw_part_geom (&table.partitions[1], &bios_geom,
1086                                         geom, lba_offset);
1087                 ped_geometry_destroy (geom);
1088 
1089                 if (!write_ext_table (disk, part->prev->geom.start, part))
1090                         return 0;
1091         }
1092 
1093         return ped_device_write (disk->dev, (void*) &table, sector, 1);
1094 }
1095 
1096 static int
1097 write_empty_table (const PedDisk* disk, PedSector sector)
1098 {
1099         DosRawTable             table;
1100 
1101         PED_ASSERT (disk != NULL, return 0);
1102 
1103         memset (&table, 0, sizeof (DosRawTable));
1104         table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1105 
1106         return ped_device_write (disk->dev, (void*) &table, sector, 1);
1107 }
1108 
1109 /* Find the first logical partition, and write the partition table for it.
1110  */
1111 static int
1112 write_extended_partitions (const PedDisk* disk)
1113 {
1114         PedPartition*           ext_part;
1115         PedPartition*           part;
1116         PedCHSGeometry          bios_geom;
1117 
1118         PED_ASSERT (disk != NULL, return 0);
1119 
1120         ext_part = ped_disk_extended_partition (disk);
1121         partition_probe_bios_geometry (ext_part, &bios_geom);
1122         part = ped_disk_get_partition (disk, 5);
1123         if (part)
1124                 return write_ext_table (disk, ext_part->geom.start, part);
1125         else
1126                 return write_empty_table (disk, ext_part->geom.start);
1127 }
1128 
1129 static inline uint32_t generate_random_id (void)
1130 {
1131         struct timeval tv;
1132         int rc;
1133         rc = gettimeofday(&tv, NULL);
1134         if (rc == -1)
1135                 return 0;
1136         return (uint32_t)(tv.tv_usec & 0xFFFFFFFFUL);
1137 }
1138 
1139 static int
1140 msdos_write (const PedDisk* disk)
1141 {
1142         DosRawTable             table;
1143         PedPartition*           part;
1144         int                     i;
1145 
1146         PED_ASSERT (disk != NULL, return 0);
1147         PED_ASSERT (disk->dev != NULL, return 0);
1148 
1149         ped_device_read (disk->dev, &table, 0, 1);
1150 
1151         if (!table.boot_code[0]) {
1152                 memset (table.boot_code, 0, 512);
1153                 memcpy (table.boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE));
1154         }
1155 
1156         /* If there is no unique identifier, generate a random one */
1157         if (!table.mbr_signature)
1158                 table.mbr_signature = generate_random_id();
1159 
1160         memset (table.partitions, 0, sizeof (DosRawPartition) * 4);
1161         table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1162 
1163         for (i=1; i<=4; i++) {
1164                 part = ped_disk_get_partition (disk, i);
1165                 if (!part)
1166                         continue;
1167 
1168                 if (!fill_raw_part (&table.partitions [i - 1], part, 0))
1169                         return 0;
1170 
1171                 if (part->type == PED_PARTITION_EXTENDED) {
1172                         if (!write_extended_partitions (disk))
1173                                 return 0;
1174                 }
1175         }
1176 
1177         if (!ped_device_write (disk->dev, (void*) &table, 0, 1))
1178                 return 0;
1179         return ped_device_sync (disk->dev);
1180 }
1181 #endif /* !DISCOVER_ONLY */
1182 
1183 static PedPartition*
1184 msdos_partition_new (const PedDisk* disk, PedPartitionType part_type,
1185                      const PedFileSystemType* fs_type,
1186                      PedSector start, PedSector end)
1187 {
1188         PedPartition*           part;
1189         DosPartitionData*       dos_data;
1190 
1191         part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
1192         if (!part)
1193                 goto error;
1194 
1195         if (ped_partition_is_active (part)) {
1196                 part->disk_specific
1197                         = dos_data = ped_malloc (sizeof (DosPartitionData));
1198                 if (!dos_data)
1199                         goto error_free_part;
1200                 dos_data->orig = NULL;
1201                 dos_data->system = PARTITION_LINUX;
1202                 dos_data->hidden = 0;
1203                 dos_data->boot = 0;
1204                 dos_data->raid = 0;
1205                 dos_data->lvm = 0;
1206                 dos_data->lba = 0;
1207                 dos_data->palo = 0;
1208                 dos_data->prep = 0;
1209         } else {
1210                 part->disk_specific = NULL;
1211         }
1212         return part;
1213 
1214         ped_free (dos_data);
1215 error_free_part:
1216         ped_free (part);
1217 error:
1218         return 0;
1219 }
1220 
1221 static PedPartition*
1222 msdos_partition_duplicate (const PedPartition* part)
1223 {
1224         PedPartition*           new_part;
1225         DosPartitionData*       new_dos_data;
1226         DosPartitionData*       old_dos_data;
1227 
1228         new_part = ped_partition_new (part->disk, part->type, part->fs_type,
1229                                       part->geom.start, part->geom.end);
1230         if (!new_part)
1231                 return NULL;
1232         new_part->num = part->num;
1233 
1234         old_dos_data = (DosPartitionData*) part->disk_specific;
1235         new_dos_data = (DosPartitionData*) new_part->disk_specific;
1236         new_dos_data->system = old_dos_data->system;
1237         new_dos_data->boot = old_dos_data->boot;
1238         new_dos_data->hidden = old_dos_data->hidden;
1239         new_dos_data->raid = old_dos_data->raid;
1240         new_dos_data->lvm = old_dos_data->lvm;
1241         new_dos_data->lba = old_dos_data->lba;
1242         new_dos_data->palo = old_dos_data->palo;
1243         new_dos_data->prep = old_dos_data->prep;
1244 
1245         if (old_dos_data->orig) {
1246                 new_dos_data->orig = ped_malloc (sizeof (OrigState));
1247                 if (!new_dos_data->orig) {
1248                         ped_partition_destroy (new_part);
1249                         return NULL;
1250                 }
1251                 new_dos_data->orig->geom = old_dos_data->orig->geom;
1252                 new_dos_data->orig->raw_part = old_dos_data->orig->raw_part;
1253                 new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset;
1254         }
1255         return new_part;
1256 }
1257 
1258 static void
1259 msdos_partition_destroy (PedPartition* part)
1260 {
1261         PED_ASSERT (part != NULL, return);
1262 
1263         if (ped_partition_is_active (part)) {
1264                 DosPartitionData* dos_data;
1265                 dos_data = (DosPartitionData*) part->disk_specific;
1266                 if (dos_data->orig)
1267                         ped_free (dos_data->orig);
1268                 ped_free (part->disk_specific);
1269         }
1270         ped_free (part);
1271 }
1272 
1273 static int
1274 msdos_partition_set_system (PedPartition* part,
1275                             const PedFileSystemType* fs_type)
1276 {
1277         DosPartitionData* dos_data = part->disk_specific;
1278 
1279         part->fs_type = fs_type;
1280 
1281         if (dos_data->hidden
1282                     && fs_type
1283                     && strncmp (fs_type->name, "fat", 3) != 0
1284                     && strcmp (fs_type->name, "ntfs") != 0)
1285                 dos_data->hidden = 0;
1286 
1287         if (part->type & PED_PARTITION_EXTENDED) {
1288                 dos_data->raid = 0;
1289                 dos_data->lvm = 0;
1290                 dos_data->palo = 0;
1291                 dos_data->prep = 0;
1292                 if (dos_data->lba)
1293                         dos_data->system = PARTITION_EXT_LBA;
1294                 else
1295                         dos_data->system = PARTITION_DOS_EXT;
1296                 return 1;
1297         }
1298 
1299         if (dos_data->lvm) {
1300                 dos_data->system = PARTITION_LINUX_LVM;
1301                 return 1;
1302         }
1303         if (dos_data->raid) {
1304                 dos_data->system = PARTITION_LINUX_RAID;
1305                 return 1;
1306         }
1307         if (dos_data->palo) {
1308                 dos_data->system = PARTITION_PALO;
1309                 return 1;
1310         }
1311         if (dos_data->prep) {
1312                 dos_data->system = PARTITION_PREP;
1313                 return 1;
1314         }
1315 
1316         if (!fs_type)
1317                 dos_data->system = PARTITION_LINUX;
1318         else if (!strcmp (fs_type->name, "fat16")) {
1319                 dos_data->system = dos_data->lba
1320                                    ? PARTITION_FAT16_LBA : PARTITION_FAT16;
1321                 dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1322         } else if (!strcmp (fs_type->name, "fat32")) {
1323                 dos_data->system = dos_data->lba
1324                                    ? PARTITION_FAT32_LBA : PARTITION_FAT32;
1325                 dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1326         } else if (!strcmp (fs_type->name, "ntfs")
1327                    || !strcmp (fs_type->name, "hpfs")) {
1328                 dos_data->system = PARTITION_NTFS;
1329                 dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1330         } else if (!strcmp (fs_type->name, "sun-ufs"))
1331                 dos_data->system = PARTITION_SUN_UFS;
1332         else if (!strcmp (fs_type->name, "solaris"))
1333                 dos_data->system = PARTITION_SUN_UFS;
1334         else if (!strcmp (fs_type->name, "linux-swap"))
1335                 dos_data->system = PARTITION_LINUX_SWAP;
1336         else
1337                 dos_data->system = PARTITION_LINUX;
1338 
1339         return 1;
1340 }
1341 
1342 static int
1343 msdos_partition_set_flag (PedPartition* part,
1344                           PedPartitionFlag flag, int state)
1345 {
1346         PedDisk*                        disk;
1347         PedPartition*                   walk;
1348         DosPartitionData*               dos_data;
1349 
1350         PED_ASSERT (part != NULL, return 0);
1351         PED_ASSERT (part->disk_specific != NULL, return 0);
1352         PED_ASSERT (part->disk != NULL, return 0);
1353 
1354         dos_data = part->disk_specific;
1355         disk = part->disk;
1356 
1357         switch (flag) {
1358         case PED_PARTITION_HIDDEN:
1359                 if (part->type == PED_PARTITION_EXTENDED) {
1360                         ped_exception_throw (
1361                                 PED_EXCEPTION_ERROR,
1362                                 PED_EXCEPTION_CANCEL,
1363                                 _("Extended partitions cannot be hidden on "
1364                                   "msdos disk labels."));
1365                         return 0;
1366                 }
1367                 dos_data->hidden = state;
1368                 return ped_partition_set_system (part, part->fs_type);
1369 
1370         case PED_PARTITION_BOOT:
1371                 dos_data->boot = state;
1372                 if (!state)
1373                         return 1;
1374 
1375                 walk = ped_disk_next_partition (disk, NULL);
1376                 for (; walk; walk = ped_disk_next_partition (disk, walk)) {
1377                         if (walk == part || !ped_partition_is_active (walk))
1378                                 continue;
1379                         msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0);
1380                 }
1381                 return 1;
1382 
1383         case PED_PARTITION_RAID:
1384                 if (state) {
1385                         dos_data->hidden = 0;
1386                         dos_data->lvm = 0;
1387                         dos_data->palo = 0;
1388                         dos_data->prep = 0;
1389                 }
1390                 dos_data->raid = state;
1391                 return ped_partition_set_system (part, part->fs_type);
1392 
1393         case PED_PARTITION_LVM:
1394                 if (state) {
1395                         dos_data->hidden = 0;
1396                         dos_data->raid = 0;
1397                         dos_data->palo = 0;
1398                         dos_data->prep = 0;
1399                 }
1400                 dos_data->lvm = state;
1401                 return ped_partition_set_system (part, part->fs_type);
1402 
1403         case PED_PARTITION_LBA:
1404                 dos_data->lba = state;
1405                 return ped_partition_set_system (part, part->fs_type);
1406 
1407         case PED_PARTITION_PALO:
1408                 if (state) {
1409                         dos_data->hidden = 0;
1410                         dos_data->raid = 0;
1411                         dos_data->lvm = 0;
1412                 }
1413                 dos_data->palo = state;
1414                 return ped_partition_set_system (part, part->fs_type);
1415 
1416         case PED_PARTITION_PREP:
1417                 if (state) {
1418                         dos_data->hidden = 0;
1419                         dos_data->raid = 0;
1420                         dos_data->lvm = 0;
1421                 }
1422                 dos_data->prep = state;
1423                 return ped_partition_set_system (part, part->fs_type);
1424 
1425         default:
1426                 return 0;
1427         }
1428 }
1429 
1430 static int
1431 msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
1432 {
1433         DosPartitionData*       dos_data;
1434 
1435         PED_ASSERT (part != NULL, return 0);
1436         PED_ASSERT (part->disk_specific != NULL, return 0);
1437 
1438         dos_data = part->disk_specific;
1439         switch (flag) {
1440         case PED_PARTITION_HIDDEN:
1441                 return dos_data->hidden;
1442 
1443         case PED_PARTITION_BOOT:
1444                 return dos_data->boot;
1445 
1446         case PED_PARTITION_RAID:
1447                 return dos_data->raid;
1448 
1449         case PED_PARTITION_LVM:
1450                 return dos_data->lvm;
1451 
1452         case PED_PARTITION_LBA:
1453                 return dos_data->lba;
1454 
1455         case PED_PARTITION_PALO:
1456                 return dos_data->palo;
1457 
1458         case PED_PARTITION_PREP:
1459                 return dos_data->prep;
1460 
1461         default:
1462                 return 0;
1463         }
1464 }
1465 
1466 static int
1467 msdos_partition_is_flag_available (const PedPartition* part,
1468                                    PedPartitionFlag flag)
1469 {
1470         switch (flag) {
1471         case PED_PARTITION_HIDDEN:
1472         case PED_PARTITION_BOOT:
1473         case PED_PARTITION_RAID:
1474         case PED_PARTITION_LVM:
1475         case PED_PARTITION_LBA:
1476         case PED_PARTITION_PALO:
1477         case PED_PARTITION_PREP:
1478                 return 1;
1479 
1480         default:
1481                 return 0;
1482         }
1483 }
1484 
1485 static PedGeometry*
1486 _try_constraint (const PedPartition* part, const PedConstraint* external,
1487                  PedConstraint* internal)
1488 {
1489         PedConstraint*          intersection;
1490         PedGeometry*            solution;
1491 
1492         intersection = ped_constraint_intersect (external, internal);
1493         ped_constraint_destroy (internal);
1494         if (!intersection)
1495                 return NULL;
1496 
1497         solution = ped_constraint_solve_nearest (intersection, &part->geom);
1498         ped_constraint_destroy (intersection);
1499         return solution;
1500 }
1501 
1502 static PedGeometry*
1503 _best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom,
1504                 PedGeometry* a, PedGeometry* b)
1505 {
1506         PedSector       cyl_size = bios_geom->heads * bios_geom->sectors;
1507         int             a_cylinder;
1508         int             b_cylinder;
1509 
1510         if (!a)
1511                 return b;
1512         if (!b)
1513                 return a;
1514 
1515         a_cylinder = a->start / cyl_size;
1516         b_cylinder = b->start / cyl_size;
1517 
1518         if (a_cylinder == b_cylinder) {
1519                 if ( (a->start / bios_geom->sectors) % bios_geom->heads
1520                           < (b->start / bios_geom->sectors) % bios_geom->heads)
1521                         goto choose_a;
1522                 else
1523                         goto choose_b;
1524         } else {
1525                 PedSector       a_delta;
1526                 PedSector       b_delta;
1527 
1528                 a_delta = abs (part->geom.start - a->start);
1529                 b_delta = abs (part->geom.start - b->start);
1530 
1531                 if (a_delta < b_delta)
1532                         goto choose_a;
1533                 else
1534                         goto choose_b;
1535         }
1536 
1537         return NULL;    /* never get here! */
1538 
1539 choose_a:
1540         ped_geometry_destroy (b);
1541         return a;
1542 
1543 choose_b:
1544         ped_geometry_destroy (a);
1545         return b;
1546 }
1547 
1548 /* This constraint is for "normal" primary partitions, that start at the
1549  * beginning of a cylinder, and end at the end of a cylinder.
1550  *      Note: you can't start a partition at the beginning of the 1st
1551  * cylinder, because that's where the partition table is!  There are different
1552  * rules for that - see the _primary_start_constraint.
1553  */
1554 static PedConstraint*
1555 _primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
1556                      PedGeometry* min_geom)
1557 {
1558         PedDevice*      dev = disk->dev;
1559         PedSector       cylinder_size = bios_geom->sectors * bios_geom->heads;
1560         PedAlignment    start_align;
1561         PedAlignment    end_align;
1562         PedGeometry     start_geom;
1563         PedGeometry     end_geom;
1564 
1565         if (!ped_alignment_init (&start_align, 0, cylinder_size))
1566                 return NULL;
1567         if (!ped_alignment_init (&end_align, -1, cylinder_size))
1568                 return NULL;
1569 
1570         if (min_geom) {
1571                 if (min_geom->start < cylinder_size)
1572                         return NULL;
1573                 if (!ped_geometry_init (&start_geom, dev, cylinder_size,
1574                                         min_geom->start + 1 - cylinder_size))
1575                         return NULL;
1576                 if (!ped_geometry_init (&end_geom, dev, min_geom->end,
1577                                         dev->length - min_geom->end))
1578                         return NULL;
1579         } else {
1580                 if (!ped_geometry_init (&start_geom, dev, cylinder_size,
1581                                         dev->length - cylinder_size))
1582                         return NULL;
1583                 if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
1584                         return NULL;
1585         }
1586 
1587         return ped_constraint_new (&start_align, &end_align, &start_geom,
1588                                    &end_geom, 1, dev->length);
1589 }
1590 
1591 /* This constraint is for partitions starting on the first cylinder.  They
1592  * must start on the 2nd head of the 1st cylinder.
1593  *
1594  * NOTE: We don't always start on the 2nd head of the 1st cylinder.  Windows
1595  * Vista aligns starting partitions at sector 2048 (0x800) by default.  See:
1596  * http://support.microsoft.com/kb/923332
1597  */
1598 static PedConstraint*
1599 _primary_start_constraint (const PedDisk* disk,
1600                            const PedPartition *part,
1601                            const PedCHSGeometry* bios_geom,
1602                            const PedGeometry* min_geom)
1603 {
1604         PedDevice*      dev = disk->dev;
1605         PedSector       cylinder_size = bios_geom->sectors * bios_geom->heads;
1606         PedAlignment    start_align;
1607         PedAlignment    end_align;
1608         PedGeometry     start_geom;
1609         PedGeometry     end_geom;
1610         PedSector start_pos;
1611 
1612         if (part->geom.start == 2048)
1613                 /* check for known Windows Vista (NTFS >= 3.1) alignments */
1614                 /* sector 0x800 == 2048                                   */
1615                 start_pos = 2048;
1616         else
1617                 /* all other primary partitions on a DOS label align to   */
1618                 /* the 2nd head of the first cylinder (0x3F == 63)        */
1619                 start_pos = bios_geom->sectors;
1620 
1621         if (!ped_alignment_init (&start_align, start_pos, 0))
1622                 return NULL;
1623         if (!ped_alignment_init (&end_align, -1, cylinder_size))
1624                 return NULL;
1625         if (min_geom) {
1626                 if (!ped_geometry_init (&start_geom, dev, start_pos, 1))
1627                         return NULL;
1628                 if (!ped_geometry_init (&end_geom, dev, min_geom->end,
1629                                         dev->length - min_geom->end))
1630                         return NULL;
1631         } else {
1632                 if (!ped_geometry_init (&start_geom, dev, start_pos,
1633                         dev->length - start_pos))
1634                         return NULL;
1635                 if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
1636                         return NULL;
1637         }
1638 
1639         return ped_constraint_new (&start_align, &end_align, &start_geom,
1640                                    &end_geom, 1, dev->length);
1641 }
1642 
1643 /* constraints for logical partitions:
1644  *      - start_offset is the offset in the start alignment.  "normally",
1645  * this is bios_geom->sectors.  exceptions: MINOR > 5 at the beginning of the
1646  * extended partition, or MINOR == 5 in the middle of the extended partition
1647  *      - is_start_part == 1 if the constraint is for the first cylinder of
1648  * the extended partition, or == 0 if the constraint is for the second cylinder
1649  * onwards of the extended partition.
1650  */
1651 static PedConstraint*
1652 _logical_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
1653                      PedSector start_offset, int is_start_part)
1654 {
1655         PedPartition*   ext_part = ped_disk_extended_partition (disk);
1656         PedDevice*      dev = disk->dev;
1657         PedSector       cylinder_size = bios_geom->sectors * bios_geom->heads;
1658         PedAlignment    start_align;
1659         PedAlignment    end_align;
1660         PedGeometry     max_geom;
1661 
1662         PED_ASSERT (ext_part != NULL, return NULL);
1663 
1664         if (!ped_alignment_init (&start_align, start_offset, cylinder_size))
1665                 return NULL;
1666         if (!ped_alignment_init (&end_align, -1, cylinder_size))
1667                 return NULL;
1668         if (is_start_part) {
1669                 if (!ped_geometry_init (&max_geom, dev,
1670                                         ext_part->geom.start,
1671                                         ext_part->geom.length))
1672                         return NULL;
1673         } else {
1674                 PedSector       min_start;
1675                 PedSector       max_length;
1676 
1677                 min_start = ped_round_up_to (ext_part->geom.start + 1,
1678                                              cylinder_size);
1679                 max_length = ext_part->geom.end - min_start + 1;
1680                 if (min_start >= ext_part->geom.end)
1681                         return NULL;
1682 
1683                 if (!ped_geometry_init (&max_geom, dev, min_start, max_length))
1684                         return NULL;
1685         }
1686 
1687         return ped_constraint_new (&start_align, &end_align, &max_geom,
1688                                    &max_geom, 1, dev->length);
1689 }
1690 
1691 /* returns the minimum geometry for the extended partition, given that the
1692  * extended partition must contain:
1693  *   * all logical partitions
1694  *   * all partition tables for all logical partitions (except the first)
1695  *   * the extended partition table
1696  */
1697 static PedGeometry*
1698 _get_min_extended_part_geom (const PedPartition* ext_part,
1699                              const PedCHSGeometry* bios_geom)
1700 {
1701         PedDisk*                disk = ext_part->disk;
1702         PedSector               head_size = bios_geom ? bios_geom->sectors : 1;
1703         PedPartition*           walk;
1704         PedGeometry*            min_geom;
1705 
1706         walk = ped_disk_get_partition (disk, 5);
1707         if (!walk)
1708                 return NULL;
1709 
1710         min_geom = ped_geometry_duplicate (&walk->geom);
1711         if (!min_geom)
1712                 return NULL;
1713         ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size);
1714 
1715         for (walk = ext_part->part_list; walk; walk = walk->next) {
1716                 if (!ped_partition_is_active (walk) || walk->num == 5)
1717                         continue;
1718                 if (walk->geom.start < min_geom->start)
1719                         ped_geometry_set_start (min_geom,
1720                                         walk->geom.start - 2 * head_size);
1721                 if (walk->geom.end > min_geom->end)
1722                         ped_geometry_set_end (min_geom, walk->geom.end);
1723         }
1724 
1725         return min_geom;
1726 }
1727 
1728 static int
1729 _align_primary (PedPartition* part, const PedCHSGeometry* bios_geom,
1730                 const PedConstraint* constraint)
1731 {
1732         PedDisk*        disk = part->disk;
1733         PedGeometry*    min_geom = NULL;
1734         PedGeometry*    solution = NULL;
1735 
1736         if (part->type == PED_PARTITION_EXTENDED)
1737                 min_geom = _get_min_extended_part_geom (part, bios_geom);
1738 
1739         solution = _best_solution (part, bios_geom, solution,
1740                         _try_constraint (part, constraint,
1741                                          _primary_start_constraint (disk, part,
1742                                                  bios_geom, min_geom)));
1743 
1744         solution = _best_solution (part, bios_geom, solution,
1745                         _try_constraint (part, constraint,
1746                                 _primary_constraint (disk, bios_geom,
1747                                 min_geom)));
1748 
1749         if (min_geom)
1750                 ped_geometry_destroy (min_geom);
1751 
1752         if (solution) {
1753                 ped_geometry_set (&part->geom, solution->start,
1754                                   solution->length);
1755                 ped_geometry_destroy (solution);
1756                 return 1;
1757         }
1758 
1759         return 0;
1760 }
1761 
1762 static int
1763 _logical_min_start_head (const PedPartition* part,
1764                          const PedCHSGeometry* bios_geom,
1765                          const PedPartition* ext_part,
1766                          int is_start_ext_part)
1767 {
1768         PedSector       cylinder_size = bios_geom->sectors * bios_geom->heads;
1769         PedSector       base_head;
1770 
1771         if (is_start_ext_part)
1772                 base_head = 1 + (ext_part->geom.start % cylinder_size)
1773                                         / bios_geom->sectors;
1774         else
1775                 base_head = 0;
1776 
1777         if (part->num == 5)
1778                 return base_head + 0;
1779         else
1780                 return base_head + 1;
1781 }
1782 
1783 /* Shamelessly copied and adapted from _partition_get_overlap_constraint
1784  * (in disk.c)
1785  * This should get ride of the infamous Assertion (metadata_length > 0) failed
1786  * bug for extended msdos disklabels generated by Parted.
1787  * 1) There always is a partition table at the start of ext_part, so we leave
1788  *    a one sector gap there.
1789  * 2)*The partition table of part5 is always at the beginning of the ext_part
1790  *    so there is no need to leave a one sector gap before part5.
1791  *   *There always is a partition table at the beginning of each partition != 5.
1792  * We don't need to worry to much about consistency with 
1793  * _partition_get_overlap_constraint because missing it means we are in edge
1794  * cases anyway, and we don't lose anything by just refusing to do the job in
1795  * those cases.
1796  */
1797 static PedConstraint*
1798 _log_meta_overlap_constraint (PedPartition* part, const PedGeometry* geom)
1799 {
1800         PedGeometry     safe_space;
1801         PedSector       min_start;
1802         PedSector       max_end;
1803         PedPartition*   ext_part = ped_disk_extended_partition (part->disk);
1804         PedPartition*   walk;
1805         int             not_5 = (part->num != 5);
1806 
1807         PED_ASSERT (ext_part != NULL, return NULL);
1808 
1809         walk = ext_part->part_list;
1810 
1811         /*                                 1)  2)     */
1812         min_start = ext_part->geom.start + 1 + not_5;
1813         max_end = ext_part->geom.end;
1814 
1815         while (walk != NULL             /*      2)                         2) */
1816                 && (   walk->geom.start - (walk->num != 5) < geom->start - not_5
1817                     || walk->geom.start - (walk->num != 5) <= min_start )) {
1818                 if (walk != part && ped_partition_is_active (walk))
1819                         min_start = walk->geom.end + 1 + not_5; /* 2) */
1820                 walk = walk->next;
1821         }
1822 
1823         while (walk && (walk == part || !ped_partition_is_active (walk)))
1824                 walk = walk->next;
1825 
1826         if (walk)
1827                 max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */
1828 
1829         if (min_start >= max_end)
1830                 return NULL;
1831 
1832         ped_geometry_init (&safe_space, part->disk->dev,
1833                            min_start, max_end - min_start + 1);
1834         return ped_constraint_new_from_max (&safe_space);
1835 }
1836 
1837 static int
1838 _align_logical (PedPartition* part, const PedCHSGeometry* bios_geom,
1839                 const PedConstraint* constraint)
1840 {
1841         PedDisk*        disk = part->disk;
1842         PedPartition*   ext_part = ped_disk_extended_partition (disk);
1843         PedSector       cyl_size = bios_geom->sectors * bios_geom->heads;
1844         PedSector       start_base;
1845         int             head;
1846         PedGeometry*    solution = NULL;
1847         PedConstraint   *intersect, *log_meta_overlap;
1848 
1849         PED_ASSERT (ext_part != NULL, return 0);
1850 
1851         log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom);
1852         intersect = ped_constraint_intersect (constraint, log_meta_overlap);
1853         ped_constraint_destroy (log_meta_overlap);
1854         if (!intersect)
1855                 return 0;
1856 
1857         start_base = ped_round_down_to (part->geom.start, cyl_size);
1858 
1859         for (head = _logical_min_start_head (part, bios_geom, ext_part, 0);
1860              head < PED_MIN (5, bios_geom->heads); head++) {
1861                 PedConstraint*  disk_constraint;
1862                 PedSector       start = start_base + head * bios_geom->sectors;
1863 
1864                 if (head >= _logical_min_start_head (part, bios_geom,
1865                                                      ext_part, 1))
1866                         disk_constraint =
1867                                 _logical_constraint (disk, bios_geom, start, 1);
1868                 else
1869                         disk_constraint =
1870                                 _logical_constraint (disk, bios_geom, start, 0);
1871 
1872                 solution = _best_solution (part, bios_geom, solution,
1873                                 _try_constraint (part, intersect,
1874                                                  disk_constraint));
1875         }
1876 
1877         ped_constraint_destroy (intersect);
1878 
1879         if (solution) {
1880                 ped_geometry_set (&part->geom, solution->start,
1881                                   solution->length);
1882                 ped_geometry_destroy (solution);
1883                 return 1;
1884         }
1885 
1886         return 0;
1887 }
1888 
1889 static int
1890 _align (PedPartition* part, const PedCHSGeometry* bios_geom,
1891         const PedConstraint* constraint)
1892 {
1893         if (part->type == PED_PARTITION_LOGICAL)
1894                 return _align_logical (part, bios_geom, constraint);
1895         else
1896                 return _align_primary (part, bios_geom, constraint);
1897 }
1898 
1899 static PedConstraint*
1900 _no_geom_constraint (const PedDisk* disk, PedSector start, PedSector end)
1901 {
1902         PedGeometry      max;
1903 
1904         ped_geometry_init (&max, disk->dev, start, end - start + 1);
1905         return ped_constraint_new_from_max (&max);
1906 }
1907 
1908 static PedConstraint*
1909 _no_geom_extended_constraint (const PedPartition* part)
1910 {
1911         PedDevice*      dev = part->disk->dev;
1912         PedGeometry*    min = _get_min_extended_part_geom (part, NULL);
1913         PedGeometry     start_range;
1914         PedGeometry     end_range;
1915         PedConstraint*  constraint;
1916 
1917         if (min) {
1918                 ped_geometry_init (&start_range, dev, 1, min->start);
1919                 ped_geometry_init (&end_range, dev, min->end,
1920                                    dev->length - min->end);
1921                 ped_geometry_destroy (min);
1922         } else {
1923                 ped_geometry_init (&start_range, dev, 1, dev->length - 1);
1924                 ped_geometry_init (&end_range, dev, 1, dev->length - 1);
1925         }
1926         constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any,
1927                         &start_range, &end_range, 1, dev->length);
1928         return constraint;
1929 }
1930 
1931 static int
1932 _align_primary_no_geom (PedPartition* part, const PedConstraint* constraint)
1933 {
1934         PedDisk*        disk = part->disk;
1935         PedGeometry*    solution;
1936 
1937         if (part->type == PED_PARTITION_EXTENDED) {
1938                 solution = _try_constraint (part, constraint,
1939                                 _no_geom_extended_constraint (part));
1940         } else {
1941                 solution = _try_constraint (part, constraint,
1942                                 _no_geom_constraint (disk, 1,
1943                                                      disk->dev->length - 1));
1944         }
1945 
1946         if (solution) {
1947                 ped_geometry_set (&part->geom, solution->start,
1948                                   solution->length);
1949                 ped_geometry_destroy (solution);
1950                 return 1;
1951         }
1952         return 0;
1953 }
1954 
1955 static int
1956 _align_logical_no_geom (PedPartition* part, const PedConstraint* constraint)
1957 {
1958         PedGeometry*    solution;
1959 
1960         solution = _try_constraint (part, constraint,
1961                         _log_meta_overlap_constraint (part, &part->geom));
1962 
1963         if (solution) {
1964                 ped_geometry_set (&part->geom, solution->start,
1965                                   solution->length);
1966                 ped_geometry_destroy (solution);
1967                 return 1;
1968         }
1969         return 0;
1970 }
1971 
1972 static int
1973 _align_no_geom (PedPartition* part, const PedConstraint* constraint)
1974 {
1975         if (part->type == PED_PARTITION_LOGICAL)
1976                 return _align_logical_no_geom (part, constraint);
1977         else
1978                 return _align_primary_no_geom (part, constraint);
1979 }
1980 
1981 static int
1982 msdos_partition_align (PedPartition* part, const PedConstraint* constraint)
1983 {
1984         PedCHSGeometry  bios_geom;
1985         DosPartitionData* dos_data;
1986 
1987         PED_ASSERT (part != NULL, return 0);
1988         PED_ASSERT (part->disk_specific != NULL, return 0);
1989 
1990         dos_data = part->disk_specific;
1991         if (dos_data->system == PARTITION_LDM && dos_data->orig) {
1992                 PedGeometry *orig_geom = &dos_data->orig->geom;
1993 
1994                 if (ped_geometry_test_equal (&part->geom, orig_geom)
1995                     && ped_constraint_is_solution (constraint, &part->geom))
1996                         return 1;
1997 
1998                 ped_geometry_set (&part->geom, orig_geom->start,
1999                                   orig_geom->length);
2000                 ped_exception_throw (
2001                         PED_EXCEPTION_ERROR,
2002                         PED_EXCEPTION_CANCEL,
2003                         _("Parted can't resize partitions managed by "
2004                           "Windows Dynamic Disk."));
2005                 return 0;
2006         }
2007 
2008         partition_probe_bios_geometry (part, &bios_geom);
2009 
2010         if (_align (part, &bios_geom, constraint))
2011                 return 1;
2012         if (_align_no_geom (part, constraint))
2013                 return 1;
2014  
2015 #ifndef DISCOVER_ONLY
2016         ped_exception_throw (
2017                 PED_EXCEPTION_ERROR,
2018                 PED_EXCEPTION_CANCEL,
2019                 _("Unable to satisfy all constraints on the partition."));
2020 #endif
2021         return 0;
2022 }
2023 
2024 static int
2025 add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start,
2026                    PedSector end)
2027 {
2028         PedPartition*           new_part;
2029 
2030         PED_ASSERT (disk != NULL, return 0);
2031 
2032         new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL,
2033                                       start, end);
2034         if (!new_part)
2035                 goto error;
2036         if (!ped_disk_add_partition (disk, new_part, NULL))
2037                 goto error_destroy_new_part;
2038 
2039         return 1;
2040 
2041 error_destroy_new_part:
2042         ped_partition_destroy (new_part);
2043 error:
2044         return 0;
2045 }
2046 
2047 /* There are a few objectives here:
2048  *      - avoid having lots of "free space" partitions lying around, to confuse
2049  * the front end.
2050  *      - ensure that there's enough room to put in the extended partition
2051  * tables, etc.
2052  */
2053 static int
2054 add_logical_part_metadata (PedDisk* disk, const PedPartition* log_part)
2055 {
2056         PedPartition*   ext_part = ped_disk_extended_partition (disk);
2057         PedPartition*   prev = log_part->prev;
2058         PedCHSGeometry  bios_geom;
2059         PedSector       cyl_size;
2060         PedSector       metadata_start;
2061         PedSector       metadata_end;
2062         PedSector       metadata_length;
2063 
2064         partition_probe_bios_geometry (ext_part, &bios_geom);
2065         cyl_size = bios_geom.sectors * bios_geom.heads;
2066 
2067         /* if there's metadata shortly before the partition (on the same
2068          * cylinder), then make this new metadata partition touch the end of
2069          * the other.  No point having 63 bytes (or whatever) of free space
2070          * partition - just confuses front-ends, etc.
2071          *      Otherwise, start the metadata at the start of the cylinder
2072          */
2073 
2074         metadata_end = log_part->geom.start - 1;
2075         metadata_start = ped_round_down_to (metadata_end, cyl_size);
2076         if (prev)
2077                 metadata_start = PED_MAX (metadata_start, prev->geom.end + 1);
2078         else
2079                 metadata_start = PED_MAX (metadata_start,
2080                                           ext_part->geom.start + 1);
2081         metadata_length = metadata_end - metadata_start + 1;
2082 
2083         /* partition 5 doesn't need to have any metadata */
2084         if (log_part->num == 5 && metadata_length < bios_geom.sectors)
2085                 return 1;
2086 
2087         PED_ASSERT (metadata_length > 0, return 0);
2088 
2089         return add_metadata_part (disk, PED_PARTITION_LOGICAL,
2090                                   metadata_start, metadata_end);
2091 }
2092 
2093 static PedPartition*
2094 get_last_part (const PedDisk* disk)
2095 {
2096         PedPartition* first_part = disk->part_list;
2097         PedPartition* walk;
2098 
2099         if (!first_part)
2100                 return NULL;
2101         for (walk = first_part; walk->next; walk = walk->next);
2102         return walk;
2103 }
2104 
2105 /* Adds metadata placeholder partitions to cover the partition table (and
2106  * "free" space after it that often has bootloader stuff), and the last
2107  * incomplete cylinder at the end of the disk.
2108  *      Parted has to be mindful of the uncertainty of dev->bios_geom.
2109  * It therefore makes sure this metadata doesn't overlap with partitions.
2110  */
2111 static int
2112 add_startend_metadata (PedDisk* disk)
2113 {
2114         PedDevice* dev = disk->dev;
2115         PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads;
2116         PedPartition* first_part = disk->part_list;
2117         PedPartition* last_part = get_last_part (disk);
2118         PedSector start, end;
2119 
2120         if (!first_part)
2121                 return 1;
2122 
2123         start = 0;
2124         end = PED_MIN (dev->bios_geom.sectors - 1, first_part->geom.start - 1);
2125         if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
2126                 return 0;
2127 
2128         start = PED_MAX (last_part->geom.end + 1,
2129                          ped_round_down_to (dev->length, cyl_size));
2130         end = dev->length - 1;
2131         if (start < end) {
2132                 if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
2133                         return 0;
2134         }
2135 
2136         return 1;
2137 }
2138 
2139 static int
2140 msdos_alloc_metadata (PedDisk* disk)
2141 {
2142         PedPartition*           ext_part;
2143 
2144         PED_ASSERT (disk != NULL, return 0);
2145         PED_ASSERT (disk->dev != NULL, return 0);
2146 
2147         if (!add_startend_metadata (disk))
2148                 return 0;
2149 
2150         ext_part = ped_disk_extended_partition (disk);
2151         if (ext_part) {
2152                 int             i;
2153                 PedSector       start, end;
2154                 PedCHSGeometry  bios_geom;
2155                 
2156                 for (i=5; 1; i++) {
2157                         PedPartition* log_part;
2158                         log_part = ped_disk_get_partition (disk, i);
2159                         if (!log_part)
2160                                 break;
2161                         if (!add_logical_part_metadata (disk, log_part))
2162                                 return 0;
2163                 }
2164 
2165                 partition_probe_bios_geometry (ext_part, &bios_geom);
2166                 start = ext_part->geom.start;
2167                 end = start + bios_geom.sectors - 1;
2168                 if (ext_part->part_list)
2169                         end = PED_MIN (end,
2170                                        ext_part->part_list->geom.start - 1);
2171                 if (!add_metadata_part (disk, PED_PARTITION_LOGICAL,
2172                                         start, end))
2173                         return 0;
2174         }
2175 
2176         return 1;
2177 }
2178 
2179 static int
2180 next_primary (const PedDisk* disk)
2181 {
2182         int     i;
2183         for (i=1; i<=4; i++) {
2184                 if (!ped_disk_get_partition (disk, i))
2185                         return i;
2186         }
2187         return 0;
2188 }
2189 
2190 static int
2191 next_logical (const PedDisk* disk)
2192 {
2193         int     i;
2194         for (i=5; 1; i++) {
2195                 if (!ped_disk_get_partition (disk, i))
2196                         return i;
2197         }
2198 }
2199 
2200 static int
2201 msdos_partition_enumerate (PedPartition* part)
2202 {
2203         PED_ASSERT (part != NULL, return 0);
2204         PED_ASSERT (part->disk != NULL, return 0);
2205 
2206         /* don't re-number a primary partition */
2207         if (part->num != -1 && part->num <= 4)
2208                 return 1;
2209 
2210         part->num = -1;
2211 
2212         if (part->type & PED_PARTITION_LOGICAL)
2213                 part->num = next_logical (part->disk);
2214         else
2215                 part->num = next_primary (part->disk);
2216 
2217         return 1;
2218 }
2219 
2220 static int
2221 msdos_get_max_primary_partition_count (const PedDisk* disk)
2222 {
2223         return 4;
2224 }
2225 
2226 static PedDiskOps msdos_disk_ops = {
2227         .probe =                msdos_probe,
2228 #ifndef DISCOVER_ONLY
2229         .clobber =              msdos_clobber,
2230 #else
2231         .clobber =              NULL,
2232 #endif
2233         .alloc =                msdos_alloc,
2234         .duplicate =            msdos_duplicate,
2235         .free =                 msdos_free,
2236         .read =                 msdos_read,
2237 #ifndef DISCOVER_ONLY
2238         .write =                msdos_write,
2239 #else
2240         .write =                NULL,
2241 #endif
2242 
2243         .partition_new =        msdos_partition_new,
2244         .partition_duplicate =  msdos_partition_duplicate,
2245         .partition_destroy =    msdos_partition_destroy,
2246         .partition_set_system = msdos_partition_set_system,
2247         .partition_set_flag =   msdos_partition_set_flag,
2248         .partition_get_flag =   msdos_partition_get_flag,
2249         .partition_is_flag_available =  msdos_partition_is_flag_available,
2250         .partition_set_name =   NULL,
2251         .partition_get_name =   NULL,
2252         .partition_align =      msdos_partition_align,
2253         .partition_enumerate =  msdos_partition_enumerate,
2254 
2255         .alloc_metadata =       msdos_alloc_metadata,
2256         .get_max_primary_partition_count =
2257                                 msdos_get_max_primary_partition_count
2258 };
2259 
2260 static PedDiskType msdos_disk_type = {
2261         .next =                 NULL,
2262         .name =                 "msdos",
2263         .ops =                  &msdos_disk_ops,
2264         .features =             PED_DISK_TYPE_EXTENDED
2265 };
2266 
2267 void
2268 ped_disk_msdos_init ()
2269 {
2270         PED_ASSERT (sizeof (DosRawPartition) == 16, return);
2271         PED_ASSERT (sizeof (DosRawTable) == 512, return);
2272 
2273         ped_disk_type_register (&msdos_disk_type);
2274 }
2275 
2276 void
2277 ped_disk_msdos_done ()
2278 {
2279         ped_disk_type_unregister (&msdos_disk_type);
2280 }