1  /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 1999, 2000, 2001, 2002, 2003, 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 /** \file disk.c */
  21 
  22 /**
  23  * \addtogroup PedDisk
  24  *
  25  * \brief Disk label access.
  26  * 
  27  * Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get
  28  * anything done.  A PedDisk is always associated with a device and has a
  29  * partition table.  There are different types of partition tables (or disk
  30  * labels).  These are represented by the PedDiskType enumeration.
  31  *
  32  * @{
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <parted/parted.h>
  38 #include <parted/debug.h>
  39 
  40 #if ENABLE_NLS
  41 #  include <libintl.h>
  42 #  define _(String) dgettext (PACKAGE, String)
  43 #  define N_(String) (String)
  44 #else
  45 #  define _(String) (String)
  46 #  define N_(String) (String)
  47 #endif /* ENABLE_NLS */
  48 
  49 /* UPDATE MODE functions */
  50 #ifdef DEBUG
  51 static int _disk_check_sanity (PedDisk* disk);
  52 #endif
  53 static void _disk_push_update_mode (PedDisk* disk);
  54 static void _disk_pop_update_mode (PedDisk* disk);
  55 static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc,
  56                                     PedPartition* part);
  57 static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc,
  58                                    PedPartition* part);
  59 static int _disk_raw_remove (PedDisk* disk, PedPartition* part);
  60 static int _disk_raw_add (PedDisk* disk, PedPartition* part);
  61 
  62 static PedDiskType*     disk_types = NULL;
  63 
  64 void
  65 ped_disk_type_register (PedDiskType* disk_type)
  66 {
  67         PED_ASSERT (disk_type != NULL, return);
  68         PED_ASSERT (disk_type->ops != NULL, return);
  69         PED_ASSERT (disk_type->name != NULL, return);
  70         
  71         /* pretend that "next" isn't part of the struct :-) */
  72         ((struct _PedDiskType*) disk_type)->next = disk_types;
  73         disk_types = (struct _PedDiskType*) disk_type;
  74 }
  75 
  76 void
  77 ped_disk_type_unregister (PedDiskType* disk_type)
  78 {
  79         PedDiskType*    walk;
  80         PedDiskType*    last = NULL;
  81 
  82         PED_ASSERT (disk_types != NULL, return);
  83         PED_ASSERT (disk_type != NULL, return);
  84 
  85         for (walk = disk_types; walk && walk != disk_type;
  86                 last = walk, walk = walk->next);
  87 
  88         PED_ASSERT (walk != NULL, return);
  89         if (last)
  90                 ((struct _PedDiskType*) last)->next = disk_type->next;
  91         else
  92                 disk_types = disk_type->next;
  93 }
  94 
  95 /**
  96  * Deprecated: use ped_disk_type_regiser.
  97  */
  98 void
  99 ped_register_disk_type (PedDiskType* disk_type)
 100 {
 101         ped_disk_type_register (disk_type);
 102 }
 103 
 104 /**
 105  * Deprecated: use ped_disk_type_unregiser.
 106  */
 107 void
 108 ped_unregister_disk_type (PedDiskType* disk_type)
 109 {
 110         ped_disk_type_unregister (disk_type);
 111 }
 112 
 113 /**
 114  * Return the next disk type registers, after "type".  If "type" is
 115  * NULL, returns the first disk type.
 116  *
 117  * \return Next disk; NULL if "type" is the last registered disk type.
 118  */
 119 PedDiskType*
 120 ped_disk_type_get_next (PedDiskType* type)
 121 {
 122         if (type)
 123                 return type->next;
 124         else
 125                 return disk_types;
 126 }
 127 
 128 /**
 129  * Return the disk type with a name of "name".
 130  *
 131  * \return Disk type; NULL if no match.
 132  */
 133 PedDiskType*
 134 ped_disk_type_get (const char* name)
 135 {
 136         PedDiskType*    walk = NULL;
 137 
 138         PED_ASSERT (name != NULL, return NULL);
 139 
 140         for (walk = ped_disk_type_get_next (NULL); walk;
 141              walk = ped_disk_type_get_next (walk))
 142                         if (strcasecmp (walk->name, name) == 0)
 143                                         break;
 144 
 145         return walk;
 146 }
 147 
 148 /**
 149  * Return the type of partition table detected on "dev".
 150  *
 151  * \return Type; NULL if none was detected.
 152  */
 153 PedDiskType*
 154 ped_disk_probe (PedDevice* dev)
 155 {
 156         PedDiskType *walk = NULL;
 157 
 158         PED_ASSERT (dev != NULL, return NULL);
 159 
 160         if (!ped_device_open (dev))
 161                 return NULL;
 162 
 163         ped_exception_fetch_all ();
 164         for (walk = ped_disk_type_get_next (NULL); walk;
 165              walk = ped_disk_type_get_next (walk)) {
 166                 if (walk->ops->probe (dev))
 167                         break;
 168         }
 169 
 170         if (ped_exception)
 171                 ped_exception_catch ();
 172         ped_exception_leave_all ();
 173 
 174         ped_device_close (dev);
 175         return walk;
 176 }
 177 
 178 /**
 179  * Read the partition table off a device (if one is found). 
 180  * 
 181  * \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors
 182  *      if the partition table indicates that the existing values
 183  *      are incorrect.
 184  * 
 185  * \return A new \link _PedDisk PedDisk \endlink object;
 186  *         NULL on failure (e.g. partition table not detected).
 187  */
 188 PedDisk*
 189 ped_disk_new (PedDevice* dev)
 190 {
 191         PedDiskType*    type;
 192         PedDisk*        disk;
 193 
 194         PED_ASSERT (dev != NULL, return NULL);
 195 
 196         if (!ped_device_open (dev))
 197                 goto error;
 198 
 199         type = ped_disk_probe (dev);
 200         if (!type) {
 201                 ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
 202                         _("%s: unrecognised disk label"),
 203                         dev->path);
 204                 goto error_close_dev;
 205         }
 206         disk = ped_disk_new_fresh (dev, type);
 207         if (!disk)
 208                 goto error_close_dev;
 209         if (!type->ops->read (disk))
 210                 goto error_destroy_disk;
 211         disk->needs_clobber = 0;
 212         ped_device_close (dev);
 213         return disk;
 214 
 215 error_destroy_disk:
 216         ped_disk_destroy (disk);
 217 error_close_dev:
 218         ped_device_close (dev);
 219 error:
 220         return NULL;
 221 }
 222 
 223 static int
 224 _add_duplicate_part (PedDisk* disk, PedPartition* old_part)
 225 {
 226         PedPartition*   new_part;
 227         PedConstraint*  constraint_exact;
 228 
 229         new_part = disk->type->ops->partition_duplicate (old_part);
 230         if (!new_part)
 231                 goto error;
 232         new_part->disk = disk;
 233 
 234         constraint_exact = ped_constraint_exact (&new_part->geom);
 235         if (!constraint_exact)
 236                 goto error_destroy_new_part;
 237         if (!ped_disk_add_partition (disk, new_part, constraint_exact))
 238                 goto error_destroy_constraint_exact;
 239         ped_constraint_destroy (constraint_exact);
 240         return 1;
 241 
 242 error_destroy_constraint_exact:
 243         ped_constraint_destroy (constraint_exact);
 244 error_destroy_new_part:
 245         ped_partition_destroy (new_part);
 246 error:
 247         return 0;
 248 }
 249 
 250 /**
 251  * Clone a \link _PedDisk PedDisk \endlink object.
 252  *
 253  * \return Deep copy of \p old_disk, NULL on failure.
 254  */
 255 PedDisk*
 256 ped_disk_duplicate (const PedDisk* old_disk)
 257 {
 258         PedDisk*        new_disk;
 259         PedPartition*   old_part;
 260 
 261         PED_ASSERT (old_disk != NULL, return NULL);
 262         PED_ASSERT (!old_disk->update_mode, return NULL);
 263         PED_ASSERT (old_disk->type->ops->duplicate != NULL, return NULL);
 264         PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL,
 265                     return NULL);
 266 
 267         new_disk = old_disk->type->ops->duplicate (old_disk);
 268         if (!new_disk)
 269                 goto error;
 270 
 271         _disk_push_update_mode (new_disk);
 272         for (old_part = ped_disk_next_partition (old_disk, NULL); old_part;
 273              old_part = ped_disk_next_partition (old_disk, old_part)) {
 274                 if (ped_partition_is_active (old_part)) {
 275                         if (!_add_duplicate_part (new_disk, old_part))
 276                                 goto error_destroy_new_disk;
 277                 }
 278         }
 279         _disk_pop_update_mode (new_disk);
 280         return new_disk;
 281 
 282 error_destroy_new_disk:
 283         ped_disk_destroy (new_disk);
 284 error:
 285         return NULL;
 286 }
 287 
 288 /**
 289  * Remove all identifying signatures of a partition table,
 290  * except for partition tables of a given type.
 291  * 
 292  * \return 0 on error, 1 otherwise.
 293  * 
 294  * \sa ped_disk_clobber()
 295  */
 296 int
 297 ped_disk_clobber_exclude (PedDevice* dev, const PedDiskType* exclude)
 298 {
 299         PedDiskType*    walk;
 300 
 301         PED_ASSERT (dev != NULL, goto error);
 302 
 303         if (!ped_device_open (dev))
 304                 goto error;
 305 
 306         for (walk = ped_disk_type_get_next (NULL); walk;
 307              walk = ped_disk_type_get_next (walk)) {
 308                 int     probed;
 309 
 310                 if (walk == exclude)
 311                         continue;
 312 
 313                 ped_exception_fetch_all ();
 314                 probed = walk->ops->probe (dev);
 315                 if (!probed)
 316                         ped_exception_catch ();
 317                 ped_exception_leave_all ();
 318 
 319                 if (probed && walk->ops->clobber) {
 320                         if (!walk->ops->clobber (dev))
 321                                 goto error_close_dev;
 322                 }
 323         }
 324         ped_device_close (dev);
 325         return 1;
 326 
 327 error_close_dev:
 328         ped_device_close (dev);
 329 error:
 330         return 0;
 331 }
 332 
 333 /** 
 334  * Remove all identifying signatures of a partition table,
 335  * 
 336  * \return 0 on error, 1 otherwise.
 337  * 
 338  * \sa ped_disk_clobber_exclude()
 339  */
 340 int
 341 ped_disk_clobber (PedDevice* dev)
 342 {
 343         return ped_disk_clobber_exclude (dev, NULL);
 344 }
 345 
 346 /**
 347  * Create a new partition table on \p dev.
 348  *
 349  * This new partition table is only created in-memory, and nothing is written
 350  * to disk until ped_disk_commit_to_dev() is called.
 351  * 
 352  * \return The newly constructed \link _PedDisk PedDisk \endlink,
 353  *      NULL on failure.
 354  */
 355 PedDisk*
 356 ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type)
 357 {
 358         PedDisk*        disk;
 359 
 360         PED_ASSERT (dev != NULL, return NULL);
 361         PED_ASSERT (type != NULL, return NULL);
 362         PED_ASSERT (type->ops->alloc != NULL, return NULL);
 363 
 364         disk = type->ops->alloc (dev);
 365         if (!disk)
 366                 goto error;
 367         _disk_pop_update_mode (disk);
 368         PED_ASSERT (disk->update_mode == 0, goto error_destroy_disk);
 369 
 370         disk->needs_clobber = 1;
 371         return disk;
 372 
 373 error_destroy_disk:
 374         ped_disk_destroy (disk);
 375 error:
 376         return NULL;
 377 }
 378 
 379 PedDisk*
 380 _ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type)
 381 {
 382         PedDisk*        disk;
 383 
 384         disk = (PedDisk*) ped_malloc (sizeof (PedDisk));
 385         if (!disk)
 386                 goto error;
 387 
 388         disk->dev = (PedDevice*)dev;
 389         disk->type = disk_type;
 390         disk->update_mode = 1;
 391         disk->part_list = NULL;
 392         return disk;
 393 
 394         ped_free (disk);
 395 error:
 396         return NULL;
 397 }
 398 
 399 void
 400 _ped_disk_free (PedDisk* disk)
 401 {
 402         _disk_push_update_mode (disk);
 403         ped_disk_delete_all (disk);
 404         ped_free (disk);
 405 }
 406 
 407 /**
 408  * Close \p disk.
 409  *
 410  * What this function does depends on the PedDiskType of \p disk,
 411  * but you can generally assume that outstanding writes are flushed
 412  * (this mainly means that _ped_disk_free is called).
 413  */
 414 void
 415 ped_disk_destroy (PedDisk* disk)
 416 {
 417         PED_ASSERT (disk != NULL, return);
 418         PED_ASSERT (!disk->update_mode, return);
 419 
 420         disk->type->ops->free (disk);
 421 }
 422 
 423 /**
 424  * Tell the operating system kernel about the partition table layout
 425  * of \p disk.
 426  *
 427  * This is rather loosely defined: for example, on old versions of Linux,
 428  * it simply calls the BLKRRPART ioctl, which tells the kernel to
 429  * reread the partition table. On newer versions (2.4.x), it will
 430  * use the new blkpg interface to tell Linux where each partition
 431  * starts/ends, etc. In this case, Linux does not need to have support for
 432  * a specific type of partition table.
 433  * 
 434  * \return 0 on failure, 1 otherwise.
 435  */
 436 int
 437 ped_disk_commit_to_os (PedDisk* disk)
 438 {
 439         PED_ASSERT (disk != NULL, return 0);
 440 
 441         if (!ped_device_open (disk->dev))
 442                 goto error;
 443         if (!ped_architecture->disk_ops->disk_commit (disk))
 444                 goto error_close_dev;
 445         ped_device_close (disk->dev);
 446         return 1;
 447 
 448 error_close_dev:
 449         ped_device_close (disk->dev);
 450 error:
 451         return 0;
 452 }
 453 
 454 /**
 455  * Write the changes made to the in-memory description
 456  * of a partition table to the device.
 457  *
 458  * \return 0 on failure, 1 otherwise.
 459  */
 460 int
 461 ped_disk_commit_to_dev (PedDisk* disk)
 462 {
 463         PED_ASSERT (disk != NULL, goto error);
 464         PED_ASSERT (!disk->update_mode, goto error);
 465 
 466         if (!disk->type->ops->write) {
 467                 ped_exception_throw (
 468                         PED_EXCEPTION_ERROR,
 469                         PED_EXCEPTION_CANCEL,
 470                         _("This libparted doesn't have write support for "
 471                           "%s.  Perhaps it was compiled read-only."),
 472                         disk->type->name);
 473                 goto error;
 474         }
 475 
 476         if (!ped_device_open (disk->dev))
 477                 goto error;
 478 
 479         if (disk->needs_clobber) {
 480                 if (!ped_disk_clobber_exclude (disk->dev, disk->type))
 481                         goto error_close_dev;
 482                 disk->needs_clobber = 0;
 483         }
 484         if (!disk->type->ops->write (disk))
 485                 goto error_close_dev;
 486         ped_device_close (disk->dev);
 487         return 1;
 488 
 489 error_close_dev:
 490         ped_device_close (disk->dev);
 491 error:
 492         return 0;
 493 }
 494 
 495 /*
 496  * This function writes the in-memory changes to a partition table to
 497  * disk and informs the operating system of the changes.
 498  *
 499  * \note Equivalent to calling first ped_disk_commit_to_dev(), then
 500  *      ped_disk_commit_to_os().
 501  *
 502  * \return 0 on failure, 1 otherwise.
 503  */
 504 int
 505 ped_disk_commit (PedDisk* disk)
 506 {
 507         if (!ped_disk_commit_to_dev (disk))
 508                 return 0;
 509         return ped_disk_commit_to_os (disk);
 510 }
 511 
 512 /**
 513  * \addtogroup PedPartition
 514  *
 515  * @{
 516  */
 517 
 518 /**
 519  * Check whether a partition is mounted or busy in some
 520  * other way.
 521  *
 522  * \note An extended partition is busy if any logical partitions are mounted.
 523  *
 524  * \return \c 1 if busy.
 525  */
 526 int
 527 ped_partition_is_busy (const PedPartition* part)
 528 {
 529         PED_ASSERT (part != NULL, return 1);
 530 
 531         return ped_architecture->disk_ops->partition_is_busy (part);
 532 }
 533 
 534 /**
 535  * Return a path that can be used to address the partition in the
 536  * operating system.
 537  */
 538 char*
 539 ped_partition_get_path (const PedPartition* part)
 540 {
 541         PED_ASSERT (part != NULL, return NULL);
 542 
 543         return ped_architecture->disk_ops->partition_get_path (part);
 544 }
 545 
 546 /** @} */
 547 
 548 /**
 549  * \addtogroup PedDisk
 550  *
 551  * @{
 552  */
 553 
 554 /**
 555  * Perform a sanity check on a partition table.
 556  *
 557  * \note The check performed is generic (i.e. it does not depends on the label
 558  *      type of the disk.
 559  *
 560  * \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file
 561  *      system on it.
 562  *
 563  * \return 0 if the check fails, 1 otherwise.
 564  */
 565 int
 566 ped_disk_check (const PedDisk* disk)
 567 {
 568         PedPartition*   walk;
 569 
 570         PED_ASSERT (disk != NULL, return 0);
 571 
 572         for (walk = disk->part_list; walk;
 573              walk = ped_disk_next_partition (disk, walk)) {
 574                 const PedFileSystemType*        fs_type = walk->fs_type;
 575                 PedGeometry*                    geom;
 576                 PedSector                       length_error;
 577                 PedSector                       max_length_error;
 578 
 579                 if (!ped_partition_is_active (walk) || !fs_type)
 580                         continue;
 581 
 582                 geom = ped_file_system_probe_specific (fs_type, &walk->geom);
 583                 if (!geom)
 584                         continue;
 585 
 586                 length_error = abs (walk->geom.length - geom->length);
 587                 max_length_error = PED_MAX (4096, walk->geom.length / 100);
 588                 if (!ped_geometry_test_inside (&walk->geom, geom)
 589                     || length_error > max_length_error) {
 590                         char* part_size = ped_unit_format (disk->dev, walk->geom.length);
 591                         char* fs_size = ped_unit_format (disk->dev, geom->length);
 592                         PedExceptionOption choice;
 593 
 594                         choice = ped_exception_throw (
 595                                 PED_EXCEPTION_WARNING,
 596                                 PED_EXCEPTION_IGNORE_CANCEL,
 597                                 _("Partition %d is %s, but the file system is "
 598                                   "%s."),
 599                                 walk->num, part_size, fs_size);
 600 
 601                         ped_free (part_size);
 602                         ped_free (fs_size);
 603 
 604                         if (choice != PED_EXCEPTION_IGNORE)
 605                                 return 0;
 606                 }
 607         }
 608 
 609         return 1;
 610 }
 611 
 612 /**
 613  * This function checks if a particular type of partition table supports
 614  * a feature.
 615  *
 616  * \return 1 if \p disk_type supports \p feature, 0 otherwise.
 617  */
 618 int
 619 ped_disk_type_check_feature (const PedDiskType* disk_type,
 620                              PedDiskTypeFeature feature)
 621 {
 622         return (disk_type->features & feature) != 0;
 623 }
 624 
 625 /**
 626  * Get the number of primary partitions.
 627  */
 628 int
 629 ped_disk_get_primary_partition_count (const PedDisk* disk)
 630 {
 631         PedPartition*   walk;
 632         int             count = 0;
 633 
 634         PED_ASSERT (disk != NULL, return 0);
 635 
 636         for (walk = disk->part_list; walk;
 637              walk = ped_disk_next_partition (disk, walk)) {
 638                 if (ped_partition_is_active (walk)
 639                                 && ! (walk->type & PED_PARTITION_LOGICAL))
 640                         count++;
 641         }
 642 
 643         return count;
 644 }
 645 
 646 /**
 647  * Get the highest partition number on \p disk.
 648  */
 649 int
 650 ped_disk_get_last_partition_num (const PedDisk* disk)
 651 {
 652         PedPartition*   walk;
 653         int             highest = -1;
 654 
 655         PED_ASSERT (disk != NULL, return 0);
 656 
 657         for (walk = disk->part_list; walk;
 658              walk = ped_disk_next_partition (disk, walk)) {
 659                 if (walk->num > highest)
 660                         highest = walk->num;
 661         }
 662 
 663         return highest;
 664 }
 665 
 666 /**
 667  * Get the maximum number of (primary) partitions the disk label supports.
 668  * 
 669  * For example, MacIntosh partition maps can have different sizes,
 670  * and accordingly support a different number of partitions.
 671  */
 672 int
 673 ped_disk_get_max_primary_partition_count (const PedDisk* disk)
 674 {
 675         PED_ASSERT (disk->type != NULL, return 0);
 676         PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL,
 677                     return 0);
 678 
 679         return disk->type->ops->get_max_primary_partition_count (disk);
 680 }
 681 
 682 /**
 683  * \internal We turned a really nasty bureaucracy problem into an elegant maths
 684  * problem :-)  Basically, there are some constraints to a partition's
 685  * geometry:
 686  *
 687  * (1) it must start and end on a "disk" block, determined by the disk label
 688  * (not the hardware).  (constraint represented by a PedAlignment)
 689  *
 690  * (2) if we're resizing a partition, we MIGHT need to keep each block aligned.
 691  * Eg: if an ext2 file system has 4k blocks, then we can only move the start
 692  * by a multiple of 4k.  (constraint represented by a PedAlignment)
 693  *
 694  * (3) we need to keep the start and end within the device's physical
 695  * boundaries.  (constraint represented by a PedGeometry)
 696  *
 697  * Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-)  See
 698  * ped_alignment_intersect()
 699  *
 700  * The application of these constraints is in disk_*.c's *_partition_align()
 701  * function.
 702  */
 703 static int
 704 _partition_align (PedPartition* part, const PedConstraint* constraint)
 705 {
 706         const PedDiskType*      disk_type;
 707 
 708         PED_ASSERT (part != NULL, return 0);
 709         PED_ASSERT (part->num != -1, return 0);
 710         PED_ASSERT (part->disk != NULL, return 0);
 711         disk_type = part->disk->type;
 712         PED_ASSERT (disk_type != NULL, return 0);
 713         PED_ASSERT (disk_type->ops->partition_align != NULL, return 0);
 714         PED_ASSERT (part->disk->update_mode, return 0);
 715 
 716         return disk_type->ops->partition_align (part, constraint);
 717 }
 718 
 719 static int
 720 _partition_enumerate (PedPartition* part)
 721 {
 722         const PedDiskType*      disk_type;
 723 
 724         PED_ASSERT (part != NULL, return 0);
 725         PED_ASSERT (part->disk != NULL, return 0);
 726         disk_type = part->disk->type;
 727         PED_ASSERT (disk_type != NULL, return 0);
 728         PED_ASSERT (disk_type->ops->partition_enumerate != NULL, return 0);
 729 
 730         return disk_type->ops->partition_enumerate (part);
 731 }
 732 
 733 /**
 734  * Gives all the (active) partitions a number.  It should preserve the numbers
 735  * and orders as much as possible.
 736  */
 737 static int
 738 ped_disk_enumerate_partitions (PedDisk* disk)
 739 {
 740         PedPartition*   walk;
 741         int             i;
 742         int             end;
 743 
 744         PED_ASSERT (disk != NULL, return 0);
 745 
 746 /* first "sort" already-numbered partitions.  (e.g. if a logical partition
 747  * is removed, then all logical partitions that were number higher MUST be
 748  * renumbered)
 749  */
 750         end = ped_disk_get_last_partition_num (disk);
 751         for (i=1; i<=end; i++) {
 752                 walk = ped_disk_get_partition (disk, i);
 753                 if (walk) {
 754                         if (!_partition_enumerate (walk))
 755                                 return 0;
 756                 }
 757         }
 758 
 759 /* now, number un-numbered partitions */
 760         for (walk = disk->part_list; walk;
 761              walk = ped_disk_next_partition (disk, walk)) {
 762                 if (ped_partition_is_active (walk) && walk->num == -1) {
 763                         if (!_partition_enumerate (walk))
 764                                 return 0;
 765                 }
 766         }
 767 
 768         return 1;
 769 }
 770 
 771 static int
 772 _disk_remove_metadata (PedDisk* disk)
 773 {
 774         PedPartition*   walk = NULL;
 775         PedPartition*   next;
 776 
 777         PED_ASSERT (disk != NULL, return 0);
 778 
 779         next = ped_disk_next_partition (disk, walk);
 780 
 781         while (next) {
 782                 walk = next;
 783                 while (1) {
 784                         next = ped_disk_next_partition (disk, next);
 785                         if (!next || next->type & PED_PARTITION_METADATA)
 786                                 break;
 787                 }
 788                 if (walk->type & PED_PARTITION_METADATA)
 789                         ped_disk_delete_partition (disk, walk);
 790         }
 791         return 1;
 792 }
 793 
 794 static int
 795 _disk_alloc_metadata (PedDisk* disk)
 796 {
 797         PED_ASSERT (disk != NULL, return 0);
 798 
 799         if (!disk->update_mode)
 800                 _disk_remove_metadata (disk);
 801 
 802         return disk->type->ops->alloc_metadata (disk);
 803 }
 804 
 805 static int
 806 _disk_remove_freespace (PedDisk* disk)
 807 {
 808         PedPartition*   walk;
 809         PedPartition*   next;
 810 
 811         walk = ped_disk_next_partition (disk, NULL);
 812         for (; walk; walk = next) {
 813                 next = ped_disk_next_partition (disk, walk);
 814 
 815                 if (walk->type & PED_PARTITION_FREESPACE) {
 816                         _disk_raw_remove (disk, walk);
 817                         ped_partition_destroy (walk);
 818                 }
 819         }
 820 
 821         return 1;
 822 }
 823 
 824 static int
 825 _alloc_extended_freespace (PedDisk* disk)
 826 {
 827         PedSector       last_end;
 828         PedPartition*   walk;
 829         PedPartition*   last;
 830         PedPartition*   free_space;
 831         PedPartition*   extended_part;
 832 
 833         extended_part = ped_disk_extended_partition (disk);
 834         if (!extended_part)
 835                 return 1;
 836 
 837         last_end = extended_part->geom.start;
 838         last = NULL;
 839         
 840         for (walk = extended_part->part_list; walk; walk = walk->next) {
 841                 if (walk->geom.start > last_end + 1) {
 842                         free_space = ped_partition_new (
 843                                         disk,
 844                                         PED_PARTITION_FREESPACE
 845                                                 | PED_PARTITION_LOGICAL,
 846                                         NULL,
 847                                         last_end + 1, walk->geom.start - 1);
 848                         _disk_raw_insert_before (disk, walk, free_space);
 849                 }
 850 
 851                 last = walk;
 852                 last_end = last->geom.end;
 853         }
 854 
 855         if (last_end < extended_part->geom.end) {
 856                 free_space = ped_partition_new (
 857                                 disk,
 858                                 PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL,
 859                                 NULL,
 860                                 last_end + 1, extended_part->geom.end);
 861 
 862                 if (last)
 863                         return _disk_raw_insert_after (disk, last, free_space);
 864                 else
 865                         extended_part->part_list = free_space;
 866         }
 867 
 868         return 1;
 869 }
 870 
 871 static int
 872 _disk_alloc_freespace (PedDisk* disk)
 873 {
 874         PedSector       last_end;
 875         PedPartition*   walk;
 876         PedPartition*   last;
 877         PedPartition*   free_space;
 878 
 879         if (!_disk_remove_freespace (disk))
 880                 return 0;
 881         if (!_alloc_extended_freespace (disk))
 882                 return 0;
 883 
 884         last = NULL;
 885         last_end = -1;
 886 
 887         for (walk = disk->part_list; walk; walk = walk->next) {
 888                 if (walk->geom.start > last_end + 1) {
 889                         free_space = ped_partition_new (disk,
 890                                         PED_PARTITION_FREESPACE, NULL,
 891                                         last_end + 1, walk->geom.start - 1);
 892                         _disk_raw_insert_before (disk, walk, free_space);
 893                 }
 894 
 895                 last = walk;
 896                 last_end = last->geom.end;
 897         }
 898 
 899         if (last_end < disk->dev->length - 1) {
 900                 free_space = ped_partition_new (disk,
 901                                         PED_PARTITION_FREESPACE, NULL,
 902                                         last_end + 1, disk->dev->length - 1);
 903                 if (last)
 904                         return _disk_raw_insert_after (disk, last, free_space);
 905                 else
 906                         disk->part_list = free_space;
 907         }
 908 
 909         return 1;
 910 }
 911 
 912 /**
 913  * Update mode: used when updating the internal representation of the partition
 914  * table.  In update mode, the metadata and freespace placeholder/virtual
 915  * partitions are removed, making it much easier for various manipulation
 916  * routines...
 917  */
 918 static void
 919 _disk_push_update_mode (PedDisk* disk)
 920 {
 921         if (!disk->update_mode) {
 922 #ifdef DEBUG
 923                 _disk_check_sanity (disk);
 924 #endif
 925 
 926                 _disk_remove_freespace (disk);
 927                 disk->update_mode++;
 928                 _disk_remove_metadata (disk);
 929 
 930 #ifdef DEBUG
 931                 _disk_check_sanity (disk);
 932 #endif
 933         } else {
 934                 disk->update_mode++;
 935         }
 936 }
 937 
 938 static void
 939 _disk_pop_update_mode (PedDisk* disk)
 940 {
 941         PED_ASSERT (disk->update_mode, return);
 942 
 943         if (disk->update_mode == 1) {
 944         /* re-allocate metadata BEFORE leaving update mode, to prevent infinite
 945          * recursion (metadata allocation requires update mode)
 946          */
 947 #ifdef DEBUG
 948                 _disk_check_sanity (disk);
 949 #endif
 950 
 951                 _disk_alloc_metadata (disk);
 952                 disk->update_mode--;
 953                 _disk_alloc_freespace (disk);
 954 
 955 #ifdef DEBUG
 956                 _disk_check_sanity (disk);
 957 #endif
 958         } else {
 959                 disk->update_mode--;
 960         }
 961 }
 962 
 963 /** @} */
 964 
 965 /**
 966  * \addtogroup PedPartition
 967  *
 968  * \brief Partition access.
 969  * 
 970  * @{
 971  */
 972 
 973 PedPartition*
 974 _ped_partition_alloc (const PedDisk* disk, PedPartitionType type,
 975                       const PedFileSystemType* fs_type,
 976                       PedSector start, PedSector end)
 977 {
 978         PedPartition*   part;
 979 
 980         PED_ASSERT (disk != NULL, return 0);
 981 
 982         part = (PedPartition*) ped_malloc (sizeof (PedPartition));
 983         if (!part)
 984                 goto error;
 985 
 986         part->prev = NULL;
 987         part->next = NULL;
 988 
 989         part->disk = (PedDisk*) disk;
 990         if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1))
 991                 goto error_free_part;
 992 
 993         part->num = -1;
 994         part->type = type;
 995         part->part_list = NULL;
 996         part->fs_type = fs_type;
 997 
 998         return part;
 999 
1000 error_free_part:
1001         ped_free (part);
1002 error:
1003         return NULL;
1004 }
1005 
1006 void
1007 _ped_partition_free (PedPartition* part)
1008 {
1009         ped_free (part);
1010 }
1011 
1012 int
1013 _ped_partition_attempt_align (PedPartition* part,
1014                               const PedConstraint* external,
1015                               PedConstraint* internal)
1016 {
1017         PedConstraint*          intersection;
1018         PedGeometry*            solution;
1019 
1020         intersection = ped_constraint_intersect (external, internal);
1021         ped_constraint_destroy (internal);
1022         if (!intersection)
1023                 goto fail;
1024 
1025         solution = ped_constraint_solve_nearest (intersection, &part->geom);
1026         if (!solution)
1027                 goto fail_free_intersection;
1028         ped_geometry_set (&part->geom, solution->start, solution->length);
1029         ped_geometry_destroy (solution);
1030         ped_constraint_destroy (intersection);
1031         return 1;
1032 
1033 fail_free_intersection:
1034         ped_constraint_destroy (intersection);
1035 fail:
1036         return 0;
1037 }
1038 
1039 /**
1040  * Create a new \link _PedPartition PedPartition \endlink on \p disk.
1041  *
1042  * \param type One of \p PED_PARTITION_NORMAL, \p PED_PARTITION_EXTENDED,
1043  *      \p PED_PARTITION_LOGICAL.
1044  *
1045  * \note The constructed partition is not added to <tt>disk</tt>'s
1046  *      partition table. Use ped_disk_add_partition() to do this.
1047  * 
1048  * \return A new \link _PedPartition PedPartition \endlink object,
1049  *      NULL on failure.
1050  *
1051  * \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the
1052  *      label does not support this concept.
1053  */ 
1054 PedPartition*
1055 ped_partition_new (const PedDisk* disk, PedPartitionType type,
1056                    const PedFileSystemType* fs_type, PedSector start,
1057                    PedSector end)
1058 {
1059         int             supports_extended;
1060         PedPartition*   part;
1061 
1062         PED_ASSERT (disk != NULL, return NULL);
1063         PED_ASSERT (disk->type->ops->partition_new != NULL, return NULL);
1064 
1065         supports_extended = ped_disk_type_check_feature (disk->type,
1066                                 PED_DISK_TYPE_EXTENDED);
1067 
1068         if (!supports_extended
1069             && (type == PED_PARTITION_EXTENDED
1070                         || type == PED_PARTITION_LOGICAL)) {
1071                 ped_exception_throw (
1072                         PED_EXCEPTION_ERROR,
1073                         PED_EXCEPTION_CANCEL,
1074                         _("%s disk labels do not support extended "
1075                           "partitions."),
1076                         disk->type->name);
1077                 goto error;
1078         }
1079 
1080         part = disk->type->ops->partition_new (disk, type, fs_type, start, end);
1081         if (!part)
1082                 goto error;
1083 
1084         if (fs_type || part->type == PED_PARTITION_EXTENDED) {
1085                 if (!ped_partition_set_system (part, fs_type))
1086                         goto error_destroy_part;
1087         }
1088         return part;
1089 
1090 error_destroy_part:
1091         ped_partition_destroy (part);
1092 error:
1093         return NULL;
1094 }
1095 
1096 /**
1097  * Destroy a \link _PedPartition PedPartition \endlink object.
1098  *
1099  * \note Should not be called on a partition that is in a partition table.
1100  *      Use ped_disk_delete_partition() instead.
1101  */
1102 void
1103 ped_partition_destroy (PedPartition* part)
1104 {
1105         PED_ASSERT (part != NULL, return);
1106         PED_ASSERT (part->disk != NULL, return);
1107         PED_ASSERT (part->disk->type->ops->partition_new != NULL, return);
1108 
1109         part->disk->type->ops->partition_destroy (part);
1110 }
1111 
1112 
1113 /**
1114  * Return whether or not the partition is "active".
1115  *
1116  * A partition is active if \p part->type is neither \p PED_PARTITION_METADATA
1117  * nor \p PED_PARTITION_FREE.
1118  */
1119 int
1120 ped_partition_is_active (const PedPartition* part)
1121 {
1122         PED_ASSERT (part != NULL, return 0);
1123 
1124         return !(part->type & PED_PARTITION_FREESPACE
1125                  || part->type & PED_PARTITION_METADATA);
1126 }
1127 
1128 /**
1129  * Set the state (\c 1 or \c 0) of a flag on a partition.
1130  * 
1131  * Flags are disk label specific, although they have a global
1132  * "namespace": the flag PED_PARTITION_BOOT, for example, roughly means
1133  * "this" partition is bootable". But this means different things on different
1134  * disk labels (and may not be defined on some disk labels). For example,
1135  * on MS-DOS disk labels, there can only be one boot partition, and this
1136  * refers to the partition that will be booted from on startup. On PC98
1137  * disk labels, the user can choose from any bootable partition on startup.
1138  * 
1139  * \note It is an error to call this on an unavailable flag -- use
1140  * ped_partition_is_flag_available() to determine which flags are available
1141  * for a given disk label.
1142  *
1143  * \throws PED_EXCEPTION_ERROR if the requested flag is not available for this
1144  *      label.
1145  */
1146 int
1147 ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
1148 {
1149         PedDiskOps*     ops;
1150 
1151         PED_ASSERT (part != NULL, return 0);
1152         PED_ASSERT (part->disk != NULL, return 0);
1153         PED_ASSERT (ped_partition_is_active (part), return 0);
1154 
1155         ops = part->disk->type->ops;
1156         PED_ASSERT (ops->partition_set_flag != NULL, return 0);
1157         PED_ASSERT (ops->partition_is_flag_available != NULL, return 0);
1158 
1159         if (!ops->partition_is_flag_available (part, flag)) {
1160                 ped_exception_throw (
1161                         PED_EXCEPTION_ERROR,
1162                         PED_EXCEPTION_CANCEL,
1163                         "The flag '%s' is not available for %s disk labels.",
1164                         ped_partition_flag_get_name (flag),
1165                         part->disk->type->name);
1166                 return 0;
1167         }
1168 
1169         return ops->partition_set_flag (part, flag, state);
1170 }
1171 
1172 /**
1173  * Get the state (\c 1 or \c 0) of a flag on a partition.
1174  *
1175  * See ped_partition_set_flag() for conditions that must hold.
1176  *
1177  * \todo Where's the check for flag availability?
1178  */
1179 int
1180 ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
1181 {
1182         PED_ASSERT (part != NULL, return 0);
1183         PED_ASSERT (part->disk != NULL, return 0);
1184         PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL,
1185                     return 0);
1186         PED_ASSERT (ped_partition_is_active (part), return 0);
1187 
1188         return part->disk->type->ops->partition_get_flag (part, flag);
1189 }
1190 
1191 /**
1192  * Check whether a given flag is available on a partition.
1193  *
1194  * \return \c 1 if the flag is available.
1195  */
1196 int
1197 ped_partition_is_flag_available (const PedPartition* part,
1198                                  PedPartitionFlag flag)
1199 {
1200         PED_ASSERT (part != NULL, return 0);
1201         PED_ASSERT (part->disk != NULL, return 0);
1202         PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL,
1203                     return 0);
1204         PED_ASSERT (ped_partition_is_active (part), return 0);
1205 
1206         return part->disk->type->ops->partition_is_flag_available (part, flag);
1207 }
1208 
1209 /**
1210  * Sets the system type on the partition to \p fs_type.
1211  *
1212  * \note The file system may be opened, to get more information about the
1213  * file system, e.g. to determine if it's FAT16 or FAT32.
1214  *
1215  * \return \c 0 on failure.
1216  */
1217 int
1218 ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
1219 {
1220         const PedDiskType*      disk_type;
1221 
1222         PED_ASSERT (part != NULL, return 0);
1223         PED_ASSERT (ped_partition_is_active (part), return 0);
1224         PED_ASSERT (part->disk != NULL, return 0);
1225         disk_type = part->disk->type;
1226         PED_ASSERT (disk_type != NULL, return 0);
1227         PED_ASSERT (disk_type->ops != NULL, return 0);
1228         PED_ASSERT (disk_type->ops->partition_set_system != NULL, return 0);
1229 
1230         return disk_type->ops->partition_set_system (part, fs_type);
1231 }
1232 
1233 static int
1234 _assert_partition_name_feature (const PedDiskType* disk_type)
1235 {
1236         if (!ped_disk_type_check_feature (
1237                         disk_type, PED_DISK_TYPE_PARTITION_NAME)) {
1238                 ped_exception_throw (
1239                         PED_EXCEPTION_ERROR,
1240                         PED_EXCEPTION_CANCEL,
1241                         "%s disk labels do not support partition names.",
1242                         disk_type->name);
1243                 return 0;
1244         }
1245         return 1;
1246 }
1247 
1248 /**
1249  * Sets the name of a partition.
1250  *
1251  * \note This will only work if the disk label supports it.
1252  *      You can use
1253  *      \code
1254  * ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME);
1255  *      \endcode
1256  *      to check whether this feature is enabled for a label.
1257  *      
1258  * \note \p name will not be modified by libparted. It can be freed
1259  *      by the caller immediately after ped_partition_set_name() is called.
1260  *
1261  * \return \c 1 on success, \c 0 otherwise.
1262  */
1263 int
1264 ped_partition_set_name (PedPartition* part, const char* name)
1265 {
1266         PED_ASSERT (part != NULL, return 0);
1267         PED_ASSERT (part->disk != NULL, return 0);
1268         PED_ASSERT (ped_partition_is_active (part), return 0);
1269         PED_ASSERT (name != NULL, return 0);
1270 
1271         if (!_assert_partition_name_feature (part->disk->type))
1272                 return 0;
1273 
1274         PED_ASSERT (part->disk->type->ops->partition_set_name != NULL,
1275                     return 0);
1276         part->disk->type->ops->partition_set_name (part, name);
1277         return 1;
1278 }
1279 
1280 /**
1281  * Returns the name of a partition \p part.  This will only work if the disk
1282  * label supports it.
1283  *
1284  * \note The returned string should not be modified.  It should
1285  *      not be referenced after the partition is destroyed.
1286  */
1287 const char*
1288 ped_partition_get_name (const PedPartition* part)
1289 {
1290         PED_ASSERT (part != NULL, return NULL);
1291         PED_ASSERT (part->disk != NULL, return 0);
1292         PED_ASSERT (ped_partition_is_active (part), return 0);
1293 
1294         if (!_assert_partition_name_feature (part->disk->type))
1295                 return NULL;
1296 
1297         PED_ASSERT (part->disk->type->ops->partition_get_name != NULL,
1298                     return NULL);
1299         return part->disk->type->ops->partition_get_name (part);
1300 }
1301 
1302 /** @} */
1303 
1304 /**
1305  * \addtogroup PedDisk
1306  *
1307  * @{
1308  */
1309 
1310 PedPartition*
1311 ped_disk_extended_partition (const PedDisk* disk)
1312 {
1313         PedPartition*           walk;
1314 
1315         PED_ASSERT (disk != NULL, return 0);
1316 
1317         for (walk = disk->part_list; walk; walk = walk->next) {
1318                 if (walk->type == PED_PARTITION_EXTENDED)
1319                         break;
1320         }
1321         return walk;
1322 }
1323 
1324 /** 
1325  * Return the next partition after \p part on \p disk. If \p part is \c NULL,
1326  * return the first partition. If \p part is the last partition, returns
1327  * \c NULL. If \p part is an extended partition, returns the first logical
1328  * partition. If this is called repeatedly passing the return value as \p part,
1329  * a depth-first traversal is executed.
1330  *
1331  * \return The next partition, \c NULL if no more partitions left.
1332  */
1333 PedPartition*
1334 ped_disk_next_partition (const PedDisk* disk, const PedPartition* part)
1335 {
1336         PED_ASSERT (disk != NULL, return 0);
1337 
1338         if (!part)
1339                 return disk->part_list;
1340         if (part->type == PED_PARTITION_EXTENDED)
1341                 return part->part_list ? part->part_list : part->next;
1342         if (part->next)
1343                 return part->next;
1344         if (part->type & PED_PARTITION_LOGICAL)
1345                 return ped_disk_extended_partition (disk)->next;
1346         return NULL;
1347 }
1348 
1349 /** @} */
1350 
1351 #ifdef DEBUG
1352 static int
1353 _disk_check_sanity (PedDisk* disk)
1354 {
1355         PedPartition*   walk;
1356 
1357         PED_ASSERT (disk != NULL, return 0);
1358 
1359         for (walk = disk->part_list; walk; walk = walk->next) {
1360                 PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL), return 0);
1361                 PED_ASSERT (!walk->prev || walk->prev->next == walk, return 0);
1362         }
1363 
1364         if (!ped_disk_extended_partition (disk))
1365                 return 1;
1366 
1367         for (walk = ped_disk_extended_partition (disk)->part_list; walk;
1368              walk = walk->next) {
1369                 PED_ASSERT (walk->type & PED_PARTITION_LOGICAL, return 0);
1370                 if (walk->prev)
1371                         PED_ASSERT (walk->prev->next == walk, return 0);
1372         }
1373         return 1;
1374 }
1375 #endif
1376 
1377 /**
1378  * Returns the partition numbered \p num. 
1379  *
1380  * \return \c NULL if the specified partition does not exist.
1381  */
1382 PedPartition*
1383 ped_disk_get_partition (const PedDisk* disk, int num)
1384 {
1385         PedPartition*   walk;
1386 
1387         PED_ASSERT (disk != NULL, return 0);
1388 
1389         for (walk = disk->part_list; walk;
1390              walk = ped_disk_next_partition (disk, walk)) {
1391                 if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE))
1392                         return walk;
1393         }
1394 
1395         return NULL;
1396 }
1397 
1398 /**
1399  * Returns the partition that contains sect.  If sect lies within a logical
1400  * partition, then the logical partition is returned (not the extended
1401  * partition).
1402  */
1403 PedPartition*
1404 ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect)
1405 {
1406         PedPartition*   walk;
1407 
1408         PED_ASSERT (disk != NULL, return 0);
1409 
1410         for (walk = disk->part_list; walk;
1411              walk = ped_disk_next_partition (disk, walk)) {
1412                 if (ped_geometry_test_sector_inside (&walk->geom, sect)
1413                     && walk->type != PED_PARTITION_EXTENDED)
1414                         return walk;
1415         }
1416 
1417         /* should never get here, unless sect is outside of disk's useable
1418          * part, or we're in "update mode", and the free space place-holders
1419          * have been removed with _disk_remove_freespace()
1420          */
1421         return NULL;
1422 }
1423 
1424 /* I'm beginning to agree with Sedgewick :-/ */
1425 static int
1426 _disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part)
1427 {
1428         PED_ASSERT (disk != NULL, return 0);
1429         PED_ASSERT (loc != NULL, return 0);
1430         PED_ASSERT (part != NULL, return 0);
1431 
1432         part->prev = loc->prev;
1433         part->next = loc;
1434         if (part->prev) {
1435                 part->prev->next = part;
1436         } else {
1437                 if (loc->type & PED_PARTITION_LOGICAL)
1438                         ped_disk_extended_partition (disk)->part_list = part;
1439                 else
1440                         disk->part_list = part;
1441         }
1442         loc->prev = part;
1443 
1444         return 1;
1445 }
1446 
1447 static int
1448 _disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part)
1449 {
1450         PED_ASSERT (disk != NULL, return 0);
1451         PED_ASSERT (loc != NULL, return 0);
1452         PED_ASSERT (part != NULL, return 0);
1453 
1454         part->prev = loc;
1455         part->next = loc->next;
1456         if (loc->next)
1457                 loc->next->prev = part;
1458         loc->next = part;
1459 
1460         return 1;
1461 }
1462 
1463 static int
1464 _disk_raw_remove (PedDisk* disk, PedPartition* part)
1465 {
1466         PED_ASSERT (disk != NULL, return 0);
1467         PED_ASSERT (part != NULL, return 0);
1468 
1469         if (part->prev) {
1470                 part->prev->next = part->next;
1471                 if (part->next)
1472                         part->next->prev = part->prev;
1473         } else {
1474                 if (part->type & PED_PARTITION_LOGICAL) {
1475                         ped_disk_extended_partition (disk)->part_list
1476                                 = part->next;
1477                 } else {
1478                         disk->part_list = part->next;
1479                 }
1480                 if (part->next)
1481                         part->next->prev = NULL;
1482         }
1483 
1484         return 1;
1485 }
1486 
1487 /*
1488  *UPDATE MODE ONLY
1489  */
1490 static int
1491 _disk_raw_add (PedDisk* disk, PedPartition* part)
1492 {
1493         PedPartition*   walk;
1494         PedPartition*   last;
1495         PedPartition*   ext_part;
1496 
1497         PED_ASSERT (disk->update_mode, return 0);
1498 
1499         ext_part = ped_disk_extended_partition (disk);
1500 
1501         last = NULL;
1502         walk = (part->type & PED_PARTITION_LOGICAL) ?
1503                         ext_part->part_list : disk->part_list;
1504 
1505         for (; walk; last = walk, walk = walk->next) {
1506                 if (walk->geom.start > part->geom.end)
1507                         break;
1508         }
1509 
1510         if (walk) {
1511                 return _disk_raw_insert_before (disk, walk, part);
1512         } else {
1513                 if (last) {
1514                         return _disk_raw_insert_after (disk, last, part);
1515                 } else {
1516                         if (part->type & PED_PARTITION_LOGICAL)
1517                                 ext_part->part_list = part;
1518                         else
1519                                 disk->part_list = part;
1520                 } 
1521         }
1522 
1523         return 1;
1524 }
1525 
1526 static PedConstraint*
1527 _partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom)
1528 {
1529         PedSector       min_start;
1530         PedSector       max_end;
1531         PedPartition*   walk;
1532         PedGeometry     free_space;
1533 
1534         PED_ASSERT (part->disk->update_mode, return NULL);
1535         PED_ASSERT (part->geom.dev == geom->dev, return NULL);
1536 
1537         if (part->type & PED_PARTITION_LOGICAL) {
1538                 PedPartition* ext_part;
1539                 
1540                 ext_part = ped_disk_extended_partition (part->disk);
1541                 PED_ASSERT (ext_part != NULL, return NULL);
1542 
1543                 min_start = ext_part->geom.start;
1544                 max_end = ext_part->geom.end;
1545                 walk = ext_part->part_list;
1546         } else {
1547                 min_start = 0;
1548                 max_end = part->disk->dev->length - 1;
1549                 walk = part->disk->part_list;
1550         }
1551 
1552         while (walk != NULL
1553                && (walk->geom.start < geom->start
1554                             || min_start >= walk->geom.start)) {
1555                 if (walk != part)
1556                         min_start = walk->geom.end + 1;
1557                 walk = walk->next;
1558         }
1559 
1560         if (walk == part)
1561                 walk = walk->next;
1562 
1563         if (walk)
1564                 max_end = walk->geom.start - 1;
1565 
1566         if (min_start >= max_end)
1567                 return NULL;
1568 
1569         ped_geometry_init (&free_space, part->disk->dev,
1570                            min_start, max_end - min_start + 1);
1571         return ped_constraint_new_from_max (&free_space);
1572 }
1573 
1574 /*
1575  * Returns \c 0 if the partition, \p part overlaps with any partitions on the
1576  * \p disk.  The geometry of \p part is taken to be \p geom, NOT \p part->geom
1577  * (the idea here is to check if \p geom is valid, before changing \p part).
1578  * 
1579  * This is useful for seeing if a resized partitions new geometry is going to
1580  * fit, without the existing geomtry getting in the way.
1581  *
1582  * Note: overlap with an extended partition is also allowed, provided that
1583  * \p geom lies completely inside the extended partition.
1584  */
1585 static int
1586 _disk_check_part_overlaps (PedDisk* disk, PedPartition* part)
1587 {
1588         PedPartition*   walk;
1589 
1590         PED_ASSERT (disk != NULL, return 0);
1591         PED_ASSERT (part != NULL, return 0);
1592 
1593         for (walk = ped_disk_next_partition (disk, NULL); walk;
1594              walk = ped_disk_next_partition (disk, walk)) {
1595                 if (walk->type & PED_PARTITION_FREESPACE)
1596                         continue;
1597                 if (walk == part)
1598                         continue;
1599                 if (part->type & PED_PARTITION_EXTENDED
1600                     && walk->type & PED_PARTITION_LOGICAL)
1601                         continue;
1602 
1603                 if (ped_geometry_test_overlap (&walk->geom, &part->geom)) {
1604                         if (walk->type & PED_PARTITION_EXTENDED
1605                             && part->type & PED_PARTITION_LOGICAL
1606                             && ped_geometry_test_inside (&walk->geom,
1607                                                          &part->geom))
1608                                 continue;
1609                         return 0;
1610                 }
1611         }
1612 
1613         return 1;
1614 }
1615 
1616 static int
1617 _partition_check_basic_sanity (PedDisk* disk, PedPartition* part)
1618 {
1619         PedPartition*   ext_part = ped_disk_extended_partition (disk);
1620 
1621         PED_ASSERT (part->disk == disk, return 0);
1622 
1623         PED_ASSERT (part->geom.start >= 0, return 0);
1624         PED_ASSERT (part->geom.end < disk->dev->length, return 0);
1625         PED_ASSERT (part->geom.start <= part->geom.end, return 0);
1626 
1627         if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED)
1628             && (part->type == PED_PARTITION_EXTENDED
1629                     || part->type == PED_PARTITION_LOGICAL)) {
1630                 ped_exception_throw (
1631                         PED_EXCEPTION_ERROR,
1632                         PED_EXCEPTION_CANCEL,
1633                         _("%s disk labels don't support logical or extended "
1634                           "partitions."),
1635                         disk->type->name);
1636                 return 0;
1637         }
1638 
1639         if (ped_partition_is_active (part)
1640                         && ! (part->type & PED_PARTITION_LOGICAL)) {
1641                 if (ped_disk_get_primary_partition_count (disk) + 1
1642                     > ped_disk_get_max_primary_partition_count (disk)) {
1643                         ped_exception_throw (
1644                                 PED_EXCEPTION_ERROR,
1645                                 PED_EXCEPTION_CANCEL,
1646                                 _("Too many primary partitions."));
1647                         return 0;
1648                 }
1649         }
1650 
1651         if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) {
1652                 ped_exception_throw (
1653                         PED_EXCEPTION_ERROR,
1654                         PED_EXCEPTION_CANCEL,
1655                         _("Can't add a logical partition to %s, because "
1656                         "there is no extended partition."),
1657                         disk->dev->path);
1658                 return 0;
1659         }
1660 
1661         return 1;
1662 }
1663 
1664 static int
1665 _check_extended_partition (PedDisk* disk, PedPartition* part)
1666 {
1667         PedPartition*           walk;
1668         PedPartition*           ext_part;
1669 
1670         PED_ASSERT (disk != NULL, return 0);
1671         ext_part = ped_disk_extended_partition (disk);
1672         if (!ext_part) ext_part = part;
1673         PED_ASSERT (ext_part != NULL, return 0);
1674 
1675         if (part != ext_part) {
1676                 ped_exception_throw (
1677                         PED_EXCEPTION_ERROR,
1678                         PED_EXCEPTION_CANCEL,
1679                         _("Can't have more than one extended partition on %s."),
1680                         disk->dev->path);
1681                 return 0;
1682         }
1683 
1684         for (walk = ext_part->part_list; walk; walk = walk->next) {
1685                 if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) {
1686                         ped_exception_throw (
1687                                 PED_EXCEPTION_ERROR,
1688                                 PED_EXCEPTION_CANCEL,
1689                                 _("Can't have logical partitions outside of "
1690                                   "the extended partition."));
1691                         return 0;
1692                 }
1693         }
1694         return 1;
1695 }
1696 
1697 static int
1698 _check_partition (PedDisk* disk, PedPartition* part)
1699 {
1700         PedPartition*   ext_part = ped_disk_extended_partition (disk);
1701 
1702         PED_ASSERT (part->geom.start <= part->geom.end, return 0);
1703 
1704         if (part->type == PED_PARTITION_EXTENDED) {
1705                 if (!_check_extended_partition (disk, part))
1706                         return 0;
1707         }
1708 
1709         if (part->type & PED_PARTITION_LOGICAL
1710             && !ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
1711                 ped_exception_throw (
1712                         PED_EXCEPTION_ERROR,
1713                         PED_EXCEPTION_CANCEL,
1714                         _("Can't have a logical partition outside of the "
1715                           "extended partition on %s."),
1716                         disk->dev->path);
1717                 return 0;
1718         }
1719 
1720         if (!_disk_check_part_overlaps (disk, part)) {
1721                 ped_exception_throw (
1722                         PED_EXCEPTION_ERROR,
1723                         PED_EXCEPTION_CANCEL,
1724                         _("Can't have overlapping partitions."));
1725                 return 0;
1726         }
1727 
1728         if (! (part->type & PED_PARTITION_LOGICAL)
1729             && ext_part && ext_part != part
1730             && ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
1731                 ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
1732                         _("Can't have a primary partition inside an extended "
1733                          "partition."));
1734                 return 0;
1735         }
1736 
1737         return 1;
1738 }
1739 
1740 /**
1741  * Adds PedPartition \p part to PedPartition \p disk.
1742  * 
1743  * \warning The partition's geometry may be changed, subject to \p constraint.
1744  * You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>,
1745  * but many partition table schemes have special requirements on the start
1746  * and end of partitions.  Therefore, having an overly strict constraint
1747  * will probably mean that this function will fail (in which
1748  * case \p part will be left unmodified)
1749  * \p part is assigned a number (\p part->num) in this process.
1750  * 
1751  * \return \c 0 on failure.
1752  */
1753 int
1754 ped_disk_add_partition (PedDisk* disk, PedPartition* part,
1755                         const PedConstraint* constraint)
1756 {
1757         PedConstraint*  overlap_constraint = NULL;
1758         PedConstraint*  constraints = NULL;
1759 
1760         PED_ASSERT (disk != NULL, return 0);
1761         PED_ASSERT (part != NULL, return 0);
1762 
1763         if (!_partition_check_basic_sanity (disk, part))
1764                 return 0;
1765 
1766         _disk_push_update_mode (disk);
1767 
1768         if (ped_partition_is_active (part)) {
1769                 overlap_constraint
1770                         = _partition_get_overlap_constraint (part, &part->geom);
1771                 constraints = ped_constraint_intersect (overlap_constraint,
1772                                                         constraint);
1773 
1774                 if (!constraints && constraint) {
1775                         ped_exception_throw (
1776                                 PED_EXCEPTION_ERROR,
1777                                 PED_EXCEPTION_CANCEL,
1778                                 _("Can't have overlapping partitions."));
1779                         goto error;
1780                 }
1781 
1782                 if (!_partition_enumerate (part))
1783                         goto error;
1784                 if (!_partition_align (part, constraints))
1785                         goto error;
1786         }
1787         if (!_check_partition (disk, part))
1788                 goto error;
1789         if (!_disk_raw_add (disk, part))
1790                 goto error;
1791 
1792         ped_constraint_destroy (overlap_constraint);
1793         ped_constraint_destroy (constraints);
1794         _disk_pop_update_mode (disk);
1795 #ifdef DEBUG
1796         if (!_disk_check_sanity (disk))
1797                 return 0;
1798 #endif
1799         return 1;
1800 
1801 error:
1802         ped_constraint_destroy (overlap_constraint);
1803         ped_constraint_destroy (constraints);
1804         _disk_pop_update_mode (disk);
1805         return 0;
1806 }
1807 
1808 /**
1809  * Removes PedPartition \p part from PedDisk \p disk.
1810  *
1811  * If \p part is an extended partition, it must not contain any logical
1812  * partitions. \p part is *NOT* destroyed. The caller must call
1813  * ped_partition_destroy(), or use ped_disk_delete_partition() instead.
1814  *
1815  * \return \c 0 on error.
1816  */
1817 int
1818 ped_disk_remove_partition (PedDisk* disk, PedPartition* part)
1819 {
1820         PED_ASSERT (disk != NULL, return 0);
1821         PED_ASSERT (part != NULL, return 0);
1822 
1823         _disk_push_update_mode (disk);
1824         PED_ASSERT (part->part_list == NULL, goto error);
1825         _disk_raw_remove (disk, part);
1826         _disk_pop_update_mode (disk);
1827         ped_disk_enumerate_partitions (disk);
1828         return 1;
1829 
1830 error:
1831         _disk_pop_update_mode (disk);
1832         return 0;
1833 }
1834 
1835 static int
1836 ped_disk_delete_all_logical (PedDisk* disk);
1837 
1838 /**
1839  * Removes \p part from \p disk, and destroys \p part.
1840  *
1841  * \return \c 0 on failure.
1842  */
1843 int
1844 ped_disk_delete_partition (PedDisk* disk, PedPartition* part)
1845 {
1846         PED_ASSERT (disk != NULL, return 0);
1847         PED_ASSERT (part != NULL, return 0);
1848 
1849         _disk_push_update_mode (disk);
1850         if (part->type == PED_PARTITION_EXTENDED)
1851                 ped_disk_delete_all_logical (disk);
1852         ped_disk_remove_partition (disk, part);
1853         ped_partition_destroy (part);
1854         _disk_pop_update_mode (disk);
1855 
1856         return 1;
1857 }
1858 
1859 static int
1860 ped_disk_delete_all_logical (PedDisk* disk)
1861 {
1862         PedPartition*           walk;
1863         PedPartition*           next;
1864         PedPartition*           ext_part;
1865 
1866         PED_ASSERT (disk != NULL, return 0);
1867         ext_part = ped_disk_extended_partition (disk);
1868         PED_ASSERT (ext_part != NULL, return 0);
1869 
1870         for (walk = ext_part->part_list; walk; walk = next) {
1871                 next = walk->next;
1872 
1873                 if (!ped_disk_delete_partition (disk, walk))
1874                         return 0;
1875         }
1876         return 1;
1877 }
1878 
1879 /**
1880  * Removes and destroys all partitions on \p disk.
1881  *
1882  * \return \c 0 on failure.
1883  */
1884 int
1885 ped_disk_delete_all (PedDisk* disk)
1886 {
1887         PedPartition*           walk;
1888         PedPartition*           next;
1889 
1890         PED_ASSERT (disk != NULL, return 0);
1891 
1892         _disk_push_update_mode (disk);
1893 
1894         for (walk = disk->part_list; walk; walk = next) {
1895                 next = walk->next;
1896 
1897                 if (!ped_disk_delete_partition (disk, walk))
1898                         return 0;
1899         }
1900 
1901         _disk_pop_update_mode (disk);
1902 
1903         return 1;
1904 }
1905 
1906 /**
1907  * Sets the geometry of \p part (i.e. change a partitions location). This can
1908  * fail for many reasons, e.g. can't overlap with other partitions. If it
1909  * does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's
1910  * geometry may be set to something different from \p start and \p end subject
1911  * to \p constraint.
1912  *
1913  * \warning The constraint warning from ped_disk_add_partition() applies.
1914  * 
1915  * \note this function does not modify the contents of the partition.  You need
1916  *       to call ped_file_system_resize() separately.
1917  */
1918 int
1919 ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part,
1920                              const PedConstraint* constraint,
1921                              PedSector start, PedSector end)
1922 {
1923         PedConstraint*  overlap_constraint = NULL;
1924         PedConstraint*  constraints = NULL;
1925         PedGeometry     old_geom;
1926         PedGeometry     new_geom;
1927 
1928         PED_ASSERT (disk != NULL, return 0);
1929         PED_ASSERT (part != NULL, return 0);
1930         PED_ASSERT (part->disk == disk, return 0);
1931 
1932         old_geom = part->geom;
1933         ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1);
1934 
1935         _disk_push_update_mode (disk);
1936 
1937         overlap_constraint
1938                 = _partition_get_overlap_constraint (part, &new_geom);
1939         constraints = ped_constraint_intersect (overlap_constraint, constraint);
1940         if (!constraints && constraint) {
1941                 ped_exception_throw (
1942                         PED_EXCEPTION_ERROR,
1943                         PED_EXCEPTION_CANCEL,
1944                         _("Can't have overlapping partitions."));
1945                 goto error_pop_update_mode;
1946         }
1947 
1948         part->geom = new_geom;
1949         if (!_partition_align (part, constraints))
1950                 goto error_pop_update_mode;
1951         if (!_check_partition (disk, part))
1952                 goto error_pop_update_mode;
1953 
1954         /* remove and add, to ensure the ordering gets updated if necessary */
1955         _disk_raw_remove (disk, part);
1956         _disk_raw_add (disk, part);
1957 
1958         _disk_pop_update_mode (disk);
1959 
1960         ped_constraint_destroy (overlap_constraint);
1961         ped_constraint_destroy (constraints);
1962         return 1;
1963 
1964 error_pop_update_mode:
1965         _disk_pop_update_mode (disk);
1966         ped_constraint_destroy (overlap_constraint);
1967         ped_constraint_destroy (constraints);
1968         part->geom = old_geom;
1969         return 0;
1970 }
1971 
1972 /**
1973  * Grow PedPartition \p part geometry to the maximum possible subject to
1974  * \p constraint.  The new geometry will be a superset of the old geometry.
1975  * 
1976  * \return 0 on failure
1977  */
1978 int
1979 ped_disk_maximize_partition (PedDisk* disk, PedPartition* part,
1980                              const PedConstraint* constraint)
1981 {
1982         PedGeometry     old_geom;
1983         PedSector       global_min_start;
1984         PedSector       global_max_end;
1985         PedSector       new_start;
1986         PedSector       new_end;
1987         PedPartition*   ext_part = ped_disk_extended_partition (disk);
1988         PedConstraint*  constraint_any;
1989 
1990         PED_ASSERT (disk != NULL, return 0);
1991         PED_ASSERT (part != NULL, return 0);
1992 
1993         if (part->type & PED_PARTITION_LOGICAL) {
1994                 PED_ASSERT (ext_part != NULL, return 0);
1995                 global_min_start = ext_part->geom.start;
1996                 global_max_end = ext_part->geom.end;
1997         } else {
1998                 global_min_start = 0;
1999                 global_max_end = disk->dev->length - 1;
2000         }
2001 
2002         old_geom = part->geom;
2003 
2004         _disk_push_update_mode (disk);
2005 
2006         if (part->prev)
2007                 new_start = part->prev->geom.end + 1;
2008         else
2009                 new_start = global_min_start;
2010 
2011         if (part->next)
2012                 new_end = part->next->geom.start - 1;
2013         else
2014                 new_end = global_max_end;
2015 
2016         if (!ped_disk_set_partition_geom (disk, part, constraint, new_start,
2017                                           new_end))
2018                 goto error;
2019 
2020         _disk_pop_update_mode (disk);
2021         return 1;
2022 
2023 error:
2024         constraint_any = ped_constraint_any (disk->dev);
2025         ped_disk_set_partition_geom (disk, part, constraint_any,
2026                                      old_geom.start, old_geom.end);
2027         ped_constraint_destroy (constraint_any);
2028         _disk_pop_update_mode (disk);
2029         return 0;
2030 }
2031 
2032 /**
2033  * Get the maximum geometry \p part can be grown to, subject to
2034  * \p constraint.
2035  *
2036  * \return \c NULL on failure.
2037  */
2038 PedGeometry*
2039 ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part,
2040                                      const PedConstraint* constraint)
2041 {
2042         PedGeometry     old_geom;
2043         PedGeometry*    max_geom;
2044         PedConstraint*  constraint_exact;
2045 
2046         PED_ASSERT(disk != NULL, return NULL);
2047         PED_ASSERT(part != NULL, return NULL);
2048         PED_ASSERT(ped_partition_is_active (part), return NULL);
2049 
2050         old_geom = part->geom;
2051         if (!ped_disk_maximize_partition (disk, part, constraint))
2052                 return NULL;
2053         max_geom = ped_geometry_duplicate (&part->geom);
2054 
2055         constraint_exact = ped_constraint_exact (&old_geom);
2056         ped_disk_set_partition_geom (disk, part, constraint_exact,
2057                                      old_geom.start, old_geom.end);
2058         ped_constraint_destroy (constraint_exact);
2059 
2060         /* this assertion should never fail, because the old
2061          * geometry was valid
2062          */
2063         PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom),
2064                     return NULL);
2065 
2066         return max_geom;
2067 }
2068 
2069 /** 
2070  * Reduce the size of the extended partition to a minimum while still wrapping
2071  * its logical partitions.  If there are no logical partitions, remove the
2072  * extended partition.
2073  *
2074  * \return 0 on failure.
2075  */
2076 int
2077 ped_disk_minimize_extended_partition (PedDisk* disk)
2078 {
2079         PedPartition*           first_logical;
2080         PedPartition*           last_logical;
2081         PedPartition*           walk;
2082         PedPartition*           ext_part;
2083         PedConstraint*          constraint;
2084         int                     status;
2085 
2086         PED_ASSERT (disk != NULL, return 0);
2087 
2088         ext_part = ped_disk_extended_partition (disk);
2089         if (!ext_part)
2090                 return 1;
2091 
2092         _disk_push_update_mode (disk);
2093 
2094         first_logical = ext_part->part_list;
2095         if (!first_logical) {
2096                 _disk_pop_update_mode (disk);
2097                 return ped_disk_delete_partition (disk, ext_part);
2098         }
2099 
2100         for (walk = first_logical; walk->next; walk = walk->next);
2101         last_logical = walk;
2102 
2103         constraint = ped_constraint_any (disk->dev);
2104         status = ped_disk_set_partition_geom (disk, ext_part, constraint,
2105                                               first_logical->geom.start,
2106                                               last_logical->geom.end);
2107         ped_constraint_destroy (constraint);
2108 
2109         _disk_pop_update_mode (disk);
2110         return status;
2111 }
2112 
2113 /**
2114  * @}
2115  */
2116 
2117 /**
2118  * \addtogroup PedPartition
2119  *
2120  * @{
2121  */
2122 
2123 /**
2124  * Returns a name that seems mildly appropriate for a partition type \p type.
2125  * 
2126  * Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it
2127  * will return "free".  This isn't to be taken too seriously - it's just
2128  * useful for user interfaces, so you can show the user something ;-)
2129  * 
2130  * \note The returned string will be in English.  However,
2131  * translations are provided, so the caller can call
2132  * dgettext("parted", RESULT) on the result.
2133  *
2134  */
2135 const char*
2136 ped_partition_type_get_name (PedPartitionType type)
2137 {
2138         if (type & PED_PARTITION_METADATA)
2139                 return N_("metadata");
2140         else if (type & PED_PARTITION_FREESPACE)
2141                 return N_("free");
2142         else if (type & PED_PARTITION_EXTENDED)
2143                 return N_("extended");
2144         else if (type & PED_PARTITION_LOGICAL)
2145                 return N_("logical");
2146         else
2147                 return N_("primary");
2148 }
2149 
2150 
2151 /**
2152  * Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot".
2153  * 
2154  * \note The returned string will be in English.  However,
2155  * translations are provided, so the caller can call
2156  * dgettext("parted", RESULT) on the result.
2157  */
2158 const char*
2159 ped_partition_flag_get_name (PedPartitionFlag flag)
2160 {
2161         switch (flag) {
2162         case PED_PARTITION_BOOT:
2163                 return N_("boot");
2164         case PED_PARTITION_ROOT:
2165                 return N_("root");
2166         case PED_PARTITION_SWAP:
2167                 return N_("swap");
2168         case PED_PARTITION_HIDDEN:
2169                 return N_("hidden");
2170         case PED_PARTITION_RAID:
2171                 return N_("raid");
2172         case PED_PARTITION_LVM:
2173                 return N_("lvm");
2174         case PED_PARTITION_LBA:
2175                 return N_("lba");
2176         case PED_PARTITION_HPSERVICE:
2177                 return N_("hp-service");
2178         case PED_PARTITION_PALO:
2179                 return N_("palo");
2180         case PED_PARTITION_PREP:
2181                 return N_("prep");
2182         case PED_PARTITION_MSFT_RESERVED:
2183                 return N_("msftres");
2184 
2185         default:
2186                 ped_exception_throw (
2187                         PED_EXCEPTION_BUG,
2188                         PED_EXCEPTION_CANCEL,
2189                         _("Unknown partition flag, %d."),
2190                         flag);
2191                 return NULL;
2192         }
2193 }
2194 
2195 /**
2196  * Iterates through all flags. 
2197  * 
2198  * ped_partition_flag_next(0) returns the first flag
2199  *
2200  * \return the next flag, or 0 if there are no more flags
2201  */
2202 PedPartitionFlag
2203 ped_partition_flag_next (PedPartitionFlag flag)
2204 {
2205         return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1);
2206 }
2207 
2208 /**
2209  * Returns the flag associated with \p name.  
2210  *
2211  * \p name can be the English
2212  * string, or the translation for the native language.
2213  */
2214 PedPartitionFlag
2215 ped_partition_flag_get_by_name (const char* name)
2216 {
2217         PedPartitionFlag        flag;
2218         const char*             flag_name;
2219 
2220         for (flag = ped_partition_flag_next (0); flag;
2221                         flag = ped_partition_flag_next (flag)) {
2222                 flag_name = ped_partition_flag_get_name (flag);
2223                 if (strcasecmp (name, flag_name) == 0
2224                     || strcasecmp (name, _(flag_name)) == 0)
2225                         return flag;
2226         }
2227 
2228         return 0;
2229 }
2230 
2231 static void
2232 ped_partition_print (const PedPartition* part)
2233 {
2234         PED_ASSERT (part != NULL, return);
2235 
2236         printf ("  %-10s %02d  (%d->%d)\n",
2237                 ped_partition_type_get_name (part->type),
2238                 part->num,
2239                 (int) part->geom.start, (int) part->geom.end);
2240 }
2241 
2242 /** @} */
2243 
2244 /**
2245  * \addtogroup PedDisk
2246  *
2247  * @{
2248  */
2249 
2250 /**
2251  * Prints a summary of disk's partitions.  Useful for debugging.
2252  */
2253 void
2254 ped_disk_print (const PedDisk* disk)
2255 {
2256         PedPartition*   part;
2257 
2258         PED_ASSERT (disk != NULL, return);
2259 
2260         for (part = disk->part_list; part;
2261              part = ped_disk_next_partition (disk, part))
2262                 ped_partition_print (part);
2263 }
2264 
2265 /** @} */