OSDN Git Service

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