OSDN Git Service

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