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