blob: 9831702c971854834f13018ff3fd317a7a1c1a8b [file] [log] [blame] [edit]
/*
libparted - a library for manipulating disk partitions
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2005, 2007
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/** \file disk.c */
/**
* \addtogroup PedDisk
*
* \brief Disk label access.
*
* Most programs will need to use ped_disk_new() or ped_disk_new_fresh() to get
* anything done. A PedDisk is always associated with a device and has a
* partition table. There are different types of partition tables (or disk
* labels). These are represented by the PedDiskType enumeration.
*
* @{
*/
#include <config.h>
#include <parted/parted.h>
#include <parted/debug.h>
#if ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (PACKAGE, String)
# define N_(String) (String)
#else
# define _(String) (String)
# define N_(String) (String)
#endif /* ENABLE_NLS */
/* UPDATE MODE functions */
#ifdef DEBUG
static int _disk_check_sanity (PedDisk* disk);
#endif
static void _disk_push_update_mode (PedDisk* disk);
static void _disk_pop_update_mode (PedDisk* disk);
static int _disk_raw_insert_before (PedDisk* disk, PedPartition* loc,
PedPartition* part);
static int _disk_raw_insert_after (PedDisk* disk, PedPartition* loc,
PedPartition* part);
static int _disk_raw_remove (PedDisk* disk, PedPartition* part);
static int _disk_raw_add (PedDisk* disk, PedPartition* part);
static PedDiskType* disk_types = NULL;
void
ped_disk_type_register (PedDiskType* disk_type)
{
PED_ASSERT (disk_type != NULL, return);
PED_ASSERT (disk_type->ops != NULL, return);
PED_ASSERT (disk_type->name != NULL, return);
/* pretend that "next" isn't part of the struct :-) */
((struct _PedDiskType*) disk_type)->next = disk_types;
disk_types = (struct _PedDiskType*) disk_type;
}
void
ped_disk_type_unregister (PedDiskType* disk_type)
{
PedDiskType* walk;
PedDiskType* last = NULL;
PED_ASSERT (disk_types != NULL, return);
PED_ASSERT (disk_type != NULL, return);
for (walk = disk_types; walk && walk != disk_type;
last = walk, walk = walk->next);
PED_ASSERT (walk != NULL, return);
if (last)
((struct _PedDiskType*) last)->next = disk_type->next;
else
disk_types = disk_type->next;
}
/**
* Deprecated: use ped_disk_type_regiser.
*/
void
ped_register_disk_type (PedDiskType* disk_type)
{
ped_disk_type_register (disk_type);
}
/**
* Deprecated: use ped_disk_type_unregiser.
*/
void
ped_unregister_disk_type (PedDiskType* disk_type)
{
ped_disk_type_unregister (disk_type);
}
/**
* Return the next disk type registers, after "type". If "type" is
* NULL, returns the first disk type.
*
* \return Next disk; NULL if "type" is the last registered disk type.
*/
PedDiskType*
ped_disk_type_get_next (PedDiskType* type)
{
if (type)
return type->next;
else
return disk_types;
}
/**
* Return the disk type with a name of "name".
*
* \return Disk type; NULL if no match.
*/
PedDiskType*
ped_disk_type_get (const char* name)
{
PedDiskType* walk = NULL;
PED_ASSERT (name != NULL, return NULL);
for (walk = ped_disk_type_get_next (NULL); walk;
walk = ped_disk_type_get_next (walk))
if (strcasecmp (walk->name, name) == 0)
break;
return walk;
}
/**
* Return the type of partition table detected on "dev".
*
* \return Type; NULL if none was detected.
*/
PedDiskType*
ped_disk_probe (PedDevice* dev)
{
PedDiskType *walk = NULL;
PED_ASSERT (dev != NULL, return NULL);
if (!ped_device_open (dev))
return NULL;
ped_exception_fetch_all ();
for (walk = ped_disk_type_get_next (NULL); walk;
walk = ped_disk_type_get_next (walk)) {
if (walk->ops->probe (dev))
break;
}
if (ped_exception)
ped_exception_catch ();
ped_exception_leave_all ();
ped_device_close (dev);
return walk;
}
/**
* Read the partition table off a device (if one is found).
*
* \warning May modify \p dev->cylinders, \p dev->heads and \p dev->sectors
* if the partition table indicates that the existing values
* are incorrect.
*
* \return A new \link _PedDisk PedDisk \endlink object;
* NULL on failure (e.g. partition table not detected).
*/
PedDisk*
ped_disk_new (PedDevice* dev)
{
PedDiskType* type;
PedDisk* disk;
PED_ASSERT (dev != NULL, return NULL);
if (!ped_device_open (dev))
goto error;
type = ped_disk_probe (dev);
if (!type) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Unable to open %s - unrecognised disk label."),
dev->path);
goto error_close_dev;
}
disk = ped_disk_new_fresh (dev, type);
if (!disk)
goto error_close_dev;
if (!type->ops->read (disk))
goto error_destroy_disk;
disk->needs_clobber = 0;
ped_device_close (dev);
return disk;
error_destroy_disk:
ped_disk_destroy (disk);
error_close_dev:
ped_device_close (dev);
error:
return NULL;
}
static int
_add_duplicate_part (PedDisk* disk, PedPartition* old_part)
{
PedPartition* new_part;
PedConstraint* constraint_exact;
new_part = disk->type->ops->partition_duplicate (old_part);
if (!new_part)
goto error;
new_part->disk = disk;
constraint_exact = ped_constraint_exact (&new_part->geom);
if (!constraint_exact)
goto error_destroy_new_part;
if (!ped_disk_add_partition (disk, new_part, constraint_exact))
goto error_destroy_constraint_exact;
ped_constraint_destroy (constraint_exact);
return 1;
error_destroy_constraint_exact:
ped_constraint_destroy (constraint_exact);
error_destroy_new_part:
ped_partition_destroy (new_part);
error:
return 0;
}
/**
* Clone a \link _PedDisk PedDisk \endlink object.
*
* \return Deep copy of \p old_disk, NULL on failure.
*/
PedDisk*
ped_disk_duplicate (const PedDisk* old_disk)
{
PedDisk* new_disk;
PedPartition* old_part;
PED_ASSERT (old_disk != NULL, return NULL);
PED_ASSERT (!old_disk->update_mode, return NULL);
PED_ASSERT (old_disk->type->ops->duplicate != NULL, return NULL);
PED_ASSERT (old_disk->type->ops->partition_duplicate != NULL,
return NULL);
new_disk = old_disk->type->ops->duplicate (old_disk);
if (!new_disk)
goto error;
_disk_push_update_mode (new_disk);
for (old_part = ped_disk_next_partition (old_disk, NULL); old_part;
old_part = ped_disk_next_partition (old_disk, old_part)) {
if (ped_partition_is_active (old_part)) {
if (!_add_duplicate_part (new_disk, old_part))
goto error_destroy_new_disk;
}
}
_disk_pop_update_mode (new_disk);
return new_disk;
error_destroy_new_disk:
ped_disk_destroy (new_disk);
error:
return NULL;
}
/**
* Remove all identifying signatures of a partition table,
* except for partition tables of a given type.
*
* \return 0 on error, 1 otherwise.
*
* \sa ped_disk_clobber()
*/
int
ped_disk_clobber_exclude (PedDevice* dev, const PedDiskType* exclude)
{
PedDiskType* walk;
PED_ASSERT (dev != NULL, goto error);
if (!ped_device_open (dev))
goto error;
for (walk = ped_disk_type_get_next (NULL); walk;
walk = ped_disk_type_get_next (walk)) {
int probed;
if (walk == exclude)
continue;
ped_exception_fetch_all ();
probed = walk->ops->probe (dev);
if (!probed)
ped_exception_catch ();
ped_exception_leave_all ();
if (probed && walk->ops->clobber) {
if (!walk->ops->clobber (dev))
goto error_close_dev;
}
}
ped_device_close (dev);
return 1;
error_close_dev:
ped_device_close (dev);
error:
return 0;
}
/**
* Remove all identifying signatures of a partition table,
*
* \return 0 on error, 1 otherwise.
*
* \sa ped_disk_clobber_exclude()
*/
int
ped_disk_clobber (PedDevice* dev)
{
return ped_disk_clobber_exclude (dev, NULL);
}
/**
* Create a new partition table on \p dev.
*
* This new partition table is only created in-memory, and nothing is written
* to disk until ped_disk_commit_to_dev() is called.
*
* \return The newly constructed \link _PedDisk PedDisk \endlink,
* NULL on failure.
*/
PedDisk*
ped_disk_new_fresh (PedDevice* dev, const PedDiskType* type)
{
PedDisk* disk;
PED_ASSERT (dev != NULL, return NULL);
PED_ASSERT (type != NULL, return NULL);
PED_ASSERT (type->ops->alloc != NULL, return NULL);
disk = type->ops->alloc (dev);
if (!disk)
goto error;
_disk_pop_update_mode (disk);
PED_ASSERT (disk->update_mode == 0, goto error_destroy_disk);
disk->needs_clobber = 1;
return disk;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return NULL;
}
PedDisk*
_ped_disk_alloc (const PedDevice* dev, const PedDiskType* disk_type)
{
PedDisk* disk;
disk = (PedDisk*) ped_malloc (sizeof (PedDisk));
if (!disk)
goto error;
disk->dev = (PedDevice*)dev;
disk->type = disk_type;
disk->update_mode = 1;
disk->part_list = NULL;
return disk;
ped_free (disk);
error:
return NULL;
}
void
_ped_disk_free (PedDisk* disk)
{
_disk_push_update_mode (disk);
ped_disk_delete_all (disk);
ped_free (disk);
}
/**
* Close \p disk.
*
* What this function does depends on the PedDiskType of \p disk,
* but you can generally assume that outstanding writes are flushed
* (this mainly means that _ped_disk_free is called).
*/
void
ped_disk_destroy (PedDisk* disk)
{
PED_ASSERT (disk != NULL, return);
PED_ASSERT (!disk->update_mode, return);
disk->type->ops->free (disk);
}
/**
* Tell the operating system kernel about the partition table layout
* of \p disk.
*
* This is rather loosely defined: for example, on old versions of Linux,
* it simply calls the BLKRRPART ioctl, which tells the kernel to
* reread the partition table. On newer versions (2.4.x), it will
* use the new blkpg interface to tell Linux where each partition
* starts/ends, etc. In this case, Linux does not need to have support for
* a specific type of partition table.
*
* \return 0 on failure, 1 otherwise.
*/
int
ped_disk_commit_to_os (PedDisk* disk)
{
PED_ASSERT (disk != NULL, return 0);
if (!ped_device_open (disk->dev))
goto error;
if (!ped_architecture->disk_ops->disk_commit (disk))
goto error_close_dev;
ped_device_close (disk->dev);
return 1;
error_close_dev:
ped_device_close (disk->dev);
error:
return 0;
}
/**
* Write the changes made to the in-memory description
* of a partition table to the device.
*
* \return 0 on failure, 1 otherwise.
*/
int
ped_disk_commit_to_dev (PedDisk* disk)
{
PED_ASSERT (disk != NULL, goto error);
PED_ASSERT (!disk->update_mode, goto error);
if (!disk->type->ops->write) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("This libparted doesn't have write support for "
"%s. Perhaps it was compiled read-only."),
disk->type->name);
goto error;
}
if (!ped_device_open (disk->dev))
goto error;
if (disk->needs_clobber) {
if (!ped_disk_clobber_exclude (disk->dev, disk->type))
goto error_close_dev;
disk->needs_clobber = 0;
}
if (!disk->type->ops->write (disk))
goto error_close_dev;
ped_device_close (disk->dev);
return 1;
error_close_dev:
ped_device_close (disk->dev);
error:
return 0;
}
/*
* This function writes the in-memory changes to a partition table to
* disk and informs the operating system of the changes.
*
* \note Equivalent to calling first ped_disk_commit_to_dev(), then
* ped_disk_commit_to_os().
*
* \return 0 on failure, 1 otherwise.
*/
int
ped_disk_commit (PedDisk* disk)
{
if (!ped_disk_commit_to_dev (disk))
return 0;
return ped_disk_commit_to_os (disk);
}
/**
* \addtogroup PedPartition
*
* @{
*/
/**
* Check whether a partition is mounted or busy in some
* other way.
*
* \note An extended partition is busy if any logical partitions are mounted.
*
* \return \c 1 if busy.
*/
int
ped_partition_is_busy (const PedPartition* part)
{
PED_ASSERT (part != NULL, return 1);
return ped_architecture->disk_ops->partition_is_busy (part);
}
/**
* Return a path that can be used to address the partition in the
* operating system.
*/
char*
ped_partition_get_path (const PedPartition* part)
{
PED_ASSERT (part != NULL, return NULL);
return ped_architecture->disk_ops->partition_get_path (part);
}
/** @} */
/**
* \addtogroup PedDisk
*
* @{
*/
/**
* Perform a sanity check on a partition table.
*
* \note The check performed is generic (i.e. it does not depends on the label
* type of the disk.
*
* \throws PED_EXCEPTION_WARNING if a partition type ID does not match the file
* system on it.
*
* \return 0 if the check fails, 1 otherwise.
*/
int
ped_disk_check (const PedDisk* disk)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
const PedFileSystemType* fs_type = walk->fs_type;
PedGeometry* geom;
PedSector length_error;
PedSector max_length_error;
if (!ped_partition_is_active (walk) || !fs_type)
continue;
geom = ped_file_system_probe_specific (fs_type, &walk->geom);
if (!geom)
continue;
length_error = abs (walk->geom.length - geom->length);
max_length_error = PED_MAX (4096, walk->geom.length / 100);
if (!ped_geometry_test_inside (&walk->geom, geom)
|| length_error > max_length_error) {
char* part_size = ped_unit_format (disk->dev, walk->geom.length);
char* fs_size = ped_unit_format (disk->dev, geom->length);
PedExceptionOption choice;
choice = ped_exception_throw (
PED_EXCEPTION_WARNING,
PED_EXCEPTION_IGNORE_CANCEL,
_("Partition %d is %s, but the file system is "
"%s."),
walk->num, part_size, fs_size);
ped_free (part_size);
ped_free (fs_size);
if (choice != PED_EXCEPTION_IGNORE)
return 0;
}
}
return 1;
}
/**
* This function checks if a particular type of partition table supports
* a feature.
*
* \return 1 if \p disk_type supports \p feature, 0 otherwise.
*/
int
ped_disk_type_check_feature (const PedDiskType* disk_type,
PedDiskTypeFeature feature)
{
return (disk_type->features & feature) != 0;
}
/**
* Get the number of primary partitions.
*/
int
ped_disk_get_primary_partition_count (const PedDisk* disk)
{
PedPartition* walk;
int count = 0;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
if (ped_partition_is_active (walk)
&& ! (walk->type & PED_PARTITION_LOGICAL))
count++;
}
return count;
}
/**
* Get the highest partition number on \p disk.
*/
int
ped_disk_get_last_partition_num (const PedDisk* disk)
{
PedPartition* walk;
int highest = -1;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
if (walk->num > highest)
highest = walk->num;
}
return highest;
}
/**
* Get the maximum number of (primary) partitions the disk label supports.
*
* For example, MacIntosh partition maps can have different sizes,
* and accordingly support a different number of partitions.
*/
int
ped_disk_get_max_primary_partition_count (const PedDisk* disk)
{
PED_ASSERT (disk->type != NULL, return 0);
PED_ASSERT (disk->type->ops->get_max_primary_partition_count != NULL,
return 0);
return disk->type->ops->get_max_primary_partition_count (disk);
}
/**
* \internal We turned a really nasty bureaucracy problem into an elegant maths
* problem :-) Basically, there are some constraints to a partition's
* geometry:
*
* (1) it must start and end on a "disk" block, determined by the disk label
* (not the hardware). (constraint represented by a PedAlignment)
*
* (2) if we're resizing a partition, we MIGHT need to keep each block aligned.
* Eg: if an ext2 file system has 4k blocks, then we can only move the start
* by a multiple of 4k. (constraint represented by a PedAlignment)
*
* (3) we need to keep the start and end within the device's physical
* boundaries. (constraint represented by a PedGeometry)
*
* Satisfying (1) and (2) simultaneously required a bit of fancy maths ;-) See
* ped_alignment_intersect()
*
* The application of these constraints is in disk_*.c's *_partition_align()
* function.
*/
static int
_partition_align (PedPartition* part, const PedConstraint* constraint)
{
const PedDiskType* disk_type;
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->num != -1, return 0);
PED_ASSERT (part->disk != NULL, return 0);
disk_type = part->disk->type;
PED_ASSERT (disk_type != NULL, return 0);
PED_ASSERT (disk_type->ops->partition_align != NULL, return 0);
PED_ASSERT (part->disk->update_mode, return 0);
return disk_type->ops->partition_align (part, constraint);
}
static int
_partition_enumerate (PedPartition* part)
{
const PedDiskType* disk_type;
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk != NULL, return 0);
disk_type = part->disk->type;
PED_ASSERT (disk_type != NULL, return 0);
PED_ASSERT (disk_type->ops->partition_enumerate != NULL, return 0);
return disk_type->ops->partition_enumerate (part);
}
/**
* Gives all the (active) partitions a number. It should preserve the numbers
* and orders as much as possible.
*/
static int
ped_disk_enumerate_partitions (PedDisk* disk)
{
PedPartition* walk;
int i;
int end;
PED_ASSERT (disk != NULL, return 0);
/* first "sort" already-numbered partitions. (e.g. if a logical partition
* is removed, then all logical partitions that were number higher MUST be
* renumbered)
*/
end = ped_disk_get_last_partition_num (disk);
for (i=1; i<=end; i++) {
walk = ped_disk_get_partition (disk, i);
if (walk) {
if (!_partition_enumerate (walk))
return 0;
}
}
/* now, number un-numbered partitions */
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
if (ped_partition_is_active (walk) && walk->num == -1) {
if (!_partition_enumerate (walk))
return 0;
}
}
return 1;
}
static int
_disk_remove_metadata (PedDisk* disk)
{
PedPartition* walk = NULL;
PedPartition* next;
PED_ASSERT (disk != NULL, return 0);
next = ped_disk_next_partition (disk, walk);
while (next) {
walk = next;
while (1) {
next = ped_disk_next_partition (disk, next);
if (!next || next->type & PED_PARTITION_METADATA)
break;
}
if (walk->type & PED_PARTITION_METADATA)
ped_disk_delete_partition (disk, walk);
}
return 1;
}
static int
_disk_alloc_metadata (PedDisk* disk)
{
PED_ASSERT (disk != NULL, return 0);
if (!disk->update_mode)
_disk_remove_metadata (disk);
return disk->type->ops->alloc_metadata (disk);
}
static int
_disk_remove_freespace (PedDisk* disk)
{
PedPartition* walk;
PedPartition* next;
walk = ped_disk_next_partition (disk, NULL);
for (; walk; walk = next) {
next = ped_disk_next_partition (disk, walk);
if (walk->type & PED_PARTITION_FREESPACE) {
_disk_raw_remove (disk, walk);
ped_partition_destroy (walk);
}
}
return 1;
}
static int
_alloc_extended_freespace (PedDisk* disk)
{
PedSector last_end;
PedPartition* walk;
PedPartition* last;
PedPartition* free_space;
PedPartition* extended_part;
extended_part = ped_disk_extended_partition (disk);
if (!extended_part)
return 1;
last_end = extended_part->geom.start;
last = NULL;
for (walk = extended_part->part_list; walk; walk = walk->next) {
if (walk->geom.start > last_end + 1) {
free_space = ped_partition_new (
disk,
PED_PARTITION_FREESPACE
| PED_PARTITION_LOGICAL,
NULL,
last_end + 1, walk->geom.start - 1);
_disk_raw_insert_before (disk, walk, free_space);
}
last = walk;
last_end = last->geom.end;
}
if (last_end < extended_part->geom.end) {
free_space = ped_partition_new (
disk,
PED_PARTITION_FREESPACE | PED_PARTITION_LOGICAL,
NULL,
last_end + 1, extended_part->geom.end);
if (last)
return _disk_raw_insert_after (disk, last, free_space);
else
extended_part->part_list = free_space;
}
return 1;
}
static int
_disk_alloc_freespace (PedDisk* disk)
{
PedSector last_end;
PedPartition* walk;
PedPartition* last;
PedPartition* free_space;
if (!_disk_remove_freespace (disk))
return 0;
if (!_alloc_extended_freespace (disk))
return 0;
last = NULL;
last_end = -1;
for (walk = disk->part_list; walk; walk = walk->next) {
if (walk->geom.start > last_end + 1) {
free_space = ped_partition_new (disk,
PED_PARTITION_FREESPACE, NULL,
last_end + 1, walk->geom.start - 1);
_disk_raw_insert_before (disk, walk, free_space);
}
last = walk;
last_end = last->geom.end;
}
if (last_end < disk->dev->length - 1) {
free_space = ped_partition_new (disk,
PED_PARTITION_FREESPACE, NULL,
last_end + 1, disk->dev->length - 1);
if (last)
return _disk_raw_insert_after (disk, last, free_space);
else
disk->part_list = free_space;
}
return 1;
}
/**
* Update mode: used when updating the internal representation of the partition
* table. In update mode, the metadata and freespace placeholder/virtual
* partitions are removed, making it much easier for various manipulation
* routines...
*/
static void
_disk_push_update_mode (PedDisk* disk)
{
if (!disk->update_mode) {
#ifdef DEBUG
_disk_check_sanity (disk);
#endif
_disk_remove_freespace (disk);
disk->update_mode++;
_disk_remove_metadata (disk);
#ifdef DEBUG
_disk_check_sanity (disk);
#endif
} else {
disk->update_mode++;
}
}
static void
_disk_pop_update_mode (PedDisk* disk)
{
PED_ASSERT (disk->update_mode, return);
if (disk->update_mode == 1) {
/* re-allocate metadata BEFORE leaving update mode, to prevent infinite
* recursion (metadata allocation requires update mode)
*/
#ifdef DEBUG
_disk_check_sanity (disk);
#endif
_disk_alloc_metadata (disk);
disk->update_mode--;
_disk_alloc_freespace (disk);
#ifdef DEBUG
_disk_check_sanity (disk);
#endif
} else {
disk->update_mode--;
}
}
/** @} */
/**
* \addtogroup PedPartition
*
* \brief Partition access.
*
* @{
*/
PedPartition*
_ped_partition_alloc (const PedDisk* disk, PedPartitionType type,
const PedFileSystemType* fs_type,
PedSector start, PedSector end)
{
PedPartition* part;
PED_ASSERT (disk != NULL, return 0);
part = (PedPartition*) ped_malloc (sizeof (PedPartition));
if (!part)
goto error;
part->prev = NULL;
part->next = NULL;
part->disk = (PedDisk*) disk;
if (!ped_geometry_init (&part->geom, disk->dev, start, end - start + 1))
goto error_free_part;
part->num = -1;
part->type = type;
part->part_list = NULL;
part->fs_type = fs_type;
return part;
error_free_part:
ped_free (part);
error:
return NULL;
}
void
_ped_partition_free (PedPartition* part)
{
ped_free (part);
}
int
_ped_partition_attempt_align (PedPartition* part,
const PedConstraint* external,
PedConstraint* internal)
{
PedConstraint* intersection;
PedGeometry* solution;
intersection = ped_constraint_intersect (external, internal);
ped_constraint_destroy (internal);
if (!intersection)
goto fail;
solution = ped_constraint_solve_nearest (intersection, &part->geom);
if (!solution)
goto fail_free_intersection;
ped_geometry_set (&part->geom, solution->start, solution->length);
ped_geometry_destroy (solution);
ped_constraint_destroy (intersection);
return 1;
fail_free_intersection:
ped_constraint_destroy (intersection);
fail:
return 0;
}
/**
* Create a new \link _PedPartition PedPartition \endlink on \p disk.
*
* \param type One of \p PED_PARTITION_NORMAL, \p PED_PARTITION_EXTENDED,
* \p PED_PARTITION_LOGICAL.
*
* \note The constructed partition is not added to <tt>disk</tt>'s
* partition table. Use ped_disk_add_partition() to do this.
*
* \return A new \link _PedPartition PedPartition \endlink object,
* NULL on failure.
*
* \throws PED_EXCEPTION_ERROR if \p type is \p EXTENDED or \p LOGICAL but the
* label does not support this concept.
*/
PedPartition*
ped_partition_new (const PedDisk* disk, PedPartitionType type,
const PedFileSystemType* fs_type, PedSector start,
PedSector end)
{
int supports_extended;
PedPartition* part;
PED_ASSERT (disk != NULL, return NULL);
PED_ASSERT (disk->type->ops->partition_new != NULL, return NULL);
supports_extended = ped_disk_type_check_feature (disk->type,
PED_DISK_TYPE_EXTENDED);
if (!supports_extended
&& (type == PED_PARTITION_EXTENDED
|| type == PED_PARTITION_LOGICAL)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("%s disk labels do not support extended "
"partitions."),
disk->type->name);
goto error;
}
part = disk->type->ops->partition_new (disk, type, fs_type, start, end);
if (!part)
goto error;
if (fs_type || part->type == PED_PARTITION_EXTENDED) {
if (!ped_partition_set_system (part, fs_type))
goto error_destroy_part;
}
return part;
error_destroy_part:
ped_partition_destroy (part);
error:
return NULL;
}
/**
* Destroy a \link _PedPartition PedPartition \endlink object.
*
* \note Should not be called on a partition that is in a partition table.
* Use ped_disk_delete_partition() instead.
*/
void
ped_partition_destroy (PedPartition* part)
{
PED_ASSERT (part != NULL, return);
PED_ASSERT (part->disk != NULL, return);
PED_ASSERT (part->disk->type->ops->partition_new != NULL, return);
part->disk->type->ops->partition_destroy (part);
}
/**
* Return whether or not the partition is "active".
*
* A partition is active if \p part->type is neither \p PED_PARTITION_METADATA
* nor \p PED_PARTITION_FREE.
*/
int
ped_partition_is_active (const PedPartition* part)
{
PED_ASSERT (part != NULL, return 0);
return !(part->type & PED_PARTITION_FREESPACE
|| part->type & PED_PARTITION_METADATA);
}
/**
* Set the state (\c 1 or \c 0) of a flag on a partition.
*
* Flags are disk label specific, although they have a global
* "namespace": the flag PED_PARTITION_BOOT, for example, roughly means
* "this" partition is bootable". But this means different things on different
* disk labels (and may not be defined on some disk labels). For example,
* on MS-DOS disk labels, there can only be one boot partition, and this
* refers to the partition that will be booted from on startup. On PC98
* disk labels, the user can choose from any bootable partition on startup.
*
* \note It is an error to call this on an unavailable flag -- use
* ped_partition_is_flag_available() to determine which flags are available
* for a given disk label.
*
* \throws PED_EXCEPTION_ERROR if the requested flag is not available for this
* label.
*/
int
ped_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
{
PedDiskOps* ops;
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk != NULL, return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
ops = part->disk->type->ops;
PED_ASSERT (ops->partition_set_flag != NULL, return 0);
PED_ASSERT (ops->partition_is_flag_available != NULL, return 0);
if (!ops->partition_is_flag_available (part, flag)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
"The flag '%s' is not available for %s disk labels.",
ped_partition_flag_get_name (flag),
part->disk->type->name);
return 0;
}
return ops->partition_set_flag (part, flag, state);
}
/**
* Get the state (\c 1 or \c 0) of a flag on a partition.
*
* See ped_partition_set_flag() for conditions that must hold.
*
* \todo Where's the check for flag availability?
*/
int
ped_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
{
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk != NULL, return 0);
PED_ASSERT (part->disk->type->ops->partition_get_flag != NULL,
return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
return part->disk->type->ops->partition_get_flag (part, flag);
}
/**
* Check whether a given flag is available on a partition.
*
* \return \c 1 if the flag is available.
*/
int
ped_partition_is_flag_available (const PedPartition* part,
PedPartitionFlag flag)
{
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk != NULL, return 0);
PED_ASSERT (part->disk->type->ops->partition_is_flag_available != NULL,
return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
return part->disk->type->ops->partition_is_flag_available (part, flag);
}
/**
* Sets the system type on the partition to \p fs_type.
*
* \note The file system may be opened, to get more information about the
* file system, e.g. to determine if it's FAT16 or FAT32.
*
* \return \c 0 on failure.
*/
int
ped_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
{
const PedDiskType* disk_type;
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
PED_ASSERT (part->disk != NULL, return 0);
disk_type = part->disk->type;
PED_ASSERT (disk_type != NULL, return 0);
PED_ASSERT (disk_type->ops != NULL, return 0);
PED_ASSERT (disk_type->ops->partition_set_system != NULL, return 0);
return disk_type->ops->partition_set_system (part, fs_type);
}
static int
_assert_partition_name_feature (const PedDiskType* disk_type)
{
if (!ped_disk_type_check_feature (
disk_type, PED_DISK_TYPE_PARTITION_NAME)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
"%s disk labels do not support partition names.",
disk_type->name);
return 0;
}
return 1;
}
/**
* Sets the name of a partition.
*
* \note This will only work if the disk label supports it.
* You can use
* \code
* ped_disk_type_check_feature (part->disk->type, PED_DISK_TYPE_PARTITION_NAME);
* \endcode
* to check whether this feature is enabled for a label.
*
* \note \p name will not be modified by libparted. It can be freed
* by the caller immediately after ped_partition_set_name() is called.
*
* \return \c 1 on success, \c 0 otherwise.
*/
int
ped_partition_set_name (PedPartition* part, const char* name)
{
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk != NULL, return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
PED_ASSERT (name != NULL, return 0);
if (!_assert_partition_name_feature (part->disk->type))
return 0;
PED_ASSERT (part->disk->type->ops->partition_set_name != NULL,
return 0);
part->disk->type->ops->partition_set_name (part, name);
return 1;
}
/**
* Returns the name of a partition \p part. This will only work if the disk
* label supports it.
*
* \note The returned string should not be modified. It should
* not be referenced after the partition is destroyed.
*/
const char*
ped_partition_get_name (const PedPartition* part)
{
PED_ASSERT (part != NULL, return NULL);
PED_ASSERT (part->disk != NULL, return 0);
PED_ASSERT (ped_partition_is_active (part), return 0);
if (!_assert_partition_name_feature (part->disk->type))
return NULL;
PED_ASSERT (part->disk->type->ops->partition_get_name != NULL,
return NULL);
return part->disk->type->ops->partition_get_name (part);
}
/** @} */
/**
* \addtogroup PedDisk
*
* @{
*/
PedPartition*
ped_disk_extended_partition (const PedDisk* disk)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk; walk = walk->next) {
if (walk->type == PED_PARTITION_EXTENDED)
break;
}
return walk;
}
/**
* Return the next partition after \p part on \p disk. If \p part is \c NULL,
* return the first partition. If \p part is the last partition, returns
* \c NULL. If \p part is an extended partition, returns the first logical
* partition. If this is called repeatedly passing the return value as \p part,
* a depth-first traversal is executed.
*
* \return The next partition, \c NULL if no more partitions left.
*/
PedPartition*
ped_disk_next_partition (const PedDisk* disk, const PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
if (!part)
return disk->part_list;
if (part->type == PED_PARTITION_EXTENDED)
return part->part_list ? part->part_list : part->next;
if (part->next)
return part->next;
if (part->type & PED_PARTITION_LOGICAL)
return ped_disk_extended_partition (disk)->next;
return NULL;
}
/** @} */
#ifdef DEBUG
static int
_disk_check_sanity (PedDisk* disk)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk; walk = walk->next) {
PED_ASSERT (!(walk->type & PED_PARTITION_LOGICAL), return 0);
PED_ASSERT (!walk->prev || walk->prev->next == walk, return 0);
}
if (!ped_disk_extended_partition (disk))
return 1;
for (walk = ped_disk_extended_partition (disk)->part_list; walk;
walk = walk->next) {
PED_ASSERT (walk->type & PED_PARTITION_LOGICAL, return 0);
if (walk->prev)
PED_ASSERT (walk->prev->next == walk, return 0);
}
return 1;
}
#endif
/**
* Returns the partition numbered \p num.
*
* \return \c NULL if the specified partition does not exist.
*/
PedPartition*
ped_disk_get_partition (const PedDisk* disk, int num)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
if (walk->num == num && !(walk->type & PED_PARTITION_FREESPACE))
return walk;
}
return NULL;
}
/**
* Returns the partition that contains sect. If sect lies within a logical
* partition, then the logical partition is returned (not the extended
* partition).
*/
PedPartition*
ped_disk_get_partition_by_sector (const PedDisk* disk, PedSector sect)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
for (walk = disk->part_list; walk;
walk = ped_disk_next_partition (disk, walk)) {
if (ped_geometry_test_sector_inside (&walk->geom, sect)
&& walk->type != PED_PARTITION_EXTENDED)
return walk;
}
/* should never get here, unless sect is outside of disk's useable
* part, or we're in "update mode", and the free space place-holders
* have been removed with _disk_remove_freespace()
*/
return NULL;
}
/* I'm beginning to agree with Sedgewick :-/ */
static int
_disk_raw_insert_before (PedDisk* disk, PedPartition* loc, PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (loc != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
part->prev = loc->prev;
part->next = loc;
if (part->prev) {
part->prev->next = part;
} else {
if (loc->type & PED_PARTITION_LOGICAL)
ped_disk_extended_partition (disk)->part_list = part;
else
disk->part_list = part;
}
loc->prev = part;
return 1;
}
static int
_disk_raw_insert_after (PedDisk* disk, PedPartition* loc, PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (loc != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
part->prev = loc;
part->next = loc->next;
if (loc->next)
loc->next->prev = part;
loc->next = part;
return 1;
}
static int
_disk_raw_remove (PedDisk* disk, PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
if (part->prev) {
part->prev->next = part->next;
if (part->next)
part->next->prev = part->prev;
} else {
if (part->type & PED_PARTITION_LOGICAL) {
ped_disk_extended_partition (disk)->part_list
= part->next;
} else {
disk->part_list = part->next;
}
if (part->next)
part->next->prev = NULL;
}
return 1;
}
/*
*UPDATE MODE ONLY
*/
static int
_disk_raw_add (PedDisk* disk, PedPartition* part)
{
PedPartition* walk;
PedPartition* last;
PedPartition* ext_part;
PED_ASSERT (disk->update_mode, return 0);
ext_part = ped_disk_extended_partition (disk);
last = NULL;
walk = (part->type & PED_PARTITION_LOGICAL) ?
ext_part->part_list : disk->part_list;
for (; walk; last = walk, walk = walk->next) {
if (walk->geom.start > part->geom.end)
break;
}
if (walk) {
return _disk_raw_insert_before (disk, walk, part);
} else {
if (last) {
return _disk_raw_insert_after (disk, last, part);
} else {
if (part->type & PED_PARTITION_LOGICAL)
ext_part->part_list = part;
else
disk->part_list = part;
}
}
return 1;
}
static PedConstraint*
_partition_get_overlap_constraint (PedPartition* part, PedGeometry* geom)
{
PedSector min_start;
PedSector max_end;
PedPartition* walk;
PedGeometry free_space;
PED_ASSERT (part->disk->update_mode, return NULL);
PED_ASSERT (part->geom.dev == geom->dev, return NULL);
if (part->type & PED_PARTITION_LOGICAL) {
PedPartition* ext_part;
ext_part = ped_disk_extended_partition (part->disk);
PED_ASSERT (ext_part != NULL, return NULL);
min_start = ext_part->geom.start;
max_end = ext_part->geom.end;
walk = ext_part->part_list;
} else {
min_start = 0;
max_end = part->disk->dev->length - 1;
walk = part->disk->part_list;
}
while (walk != NULL
&& (walk->geom.start < geom->start
|| min_start >= walk->geom.start)) {
if (walk != part)
min_start = walk->geom.end + 1;
walk = walk->next;
}
if (walk == part)
walk = walk->next;
if (walk)
max_end = walk->geom.start - 1;
if (min_start >= max_end)
return NULL;
ped_geometry_init (&free_space, part->disk->dev,
min_start, max_end - min_start + 1);
return ped_constraint_new_from_max (&free_space);
}
/*
* Returns \c 0 if the partition, \p part overlaps with any partitions on the
* \p disk. The geometry of \p part is taken to be \p geom, NOT \p part->geom
* (the idea here is to check if \p geom is valid, before changing \p part).
*
* This is useful for seeing if a resized partitions new geometry is going to
* fit, without the existing geomtry getting in the way.
*
* Note: overlap with an extended partition is also allowed, provided that
* \p geom lies completely inside the extended partition.
*/
static int
_disk_check_part_overlaps (PedDisk* disk, PedPartition* part)
{
PedPartition* walk;
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
for (walk = ped_disk_next_partition (disk, NULL); walk;
walk = ped_disk_next_partition (disk, walk)) {
if (walk->type & PED_PARTITION_FREESPACE)
continue;
if (walk == part)
continue;
if (part->type & PED_PARTITION_EXTENDED
&& walk->type & PED_PARTITION_LOGICAL)
continue;
if (ped_geometry_test_overlap (&walk->geom, &part->geom)) {
if (walk->type & PED_PARTITION_EXTENDED
&& part->type & PED_PARTITION_LOGICAL
&& ped_geometry_test_inside (&walk->geom,
&part->geom))
continue;
return 0;
}
}
return 1;
}
static int
_partition_check_basic_sanity (PedDisk* disk, PedPartition* part)
{
PedPartition* ext_part = ped_disk_extended_partition (disk);
PED_ASSERT (part->disk == disk, return 0);
PED_ASSERT (part->geom.start >= 0, return 0);
PED_ASSERT (part->geom.end < disk->dev->length, return 0);
PED_ASSERT (part->geom.start <= part->geom.end, return 0);
if (!ped_disk_type_check_feature (disk->type, PED_DISK_TYPE_EXTENDED)
&& (part->type == PED_PARTITION_EXTENDED
|| part->type == PED_PARTITION_LOGICAL)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("%s disk labels don't support logical or extended "
"partitions."),
disk->type->name);
return 0;
}
if (ped_partition_is_active (part)
&& ! (part->type & PED_PARTITION_LOGICAL)) {
if (ped_disk_get_primary_partition_count (disk) + 1
> ped_disk_get_max_primary_partition_count (disk)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Too many primary partitions."));
return 0;
}
}
if ((part->type & PED_PARTITION_LOGICAL) && !ext_part) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't add a logical partition to %s, because "
"there is no extended partition."),
disk->dev->path);
return 0;
}
return 1;
}
static int
_check_extended_partition (PedDisk* disk, PedPartition* part)
{
PedPartition* walk;
PedPartition* ext_part;
PED_ASSERT (disk != NULL, return 0);
ext_part = ped_disk_extended_partition (disk);
if (!ext_part) ext_part = part;
PED_ASSERT (ext_part != NULL, return 0);
if (part != ext_part) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have more than one extended partition on %s."),
disk->dev->path);
return 0;
}
for (walk = ext_part->part_list; walk; walk = walk->next) {
if (!ped_geometry_test_inside (&ext_part->geom, &walk->geom)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have logical partitions outside of "
"the extended partition."));
return 0;
}
}
return 1;
}
static int
_check_partition (PedDisk* disk, PedPartition* part)
{
PedPartition* ext_part = ped_disk_extended_partition (disk);
PED_ASSERT (part->geom.start <= part->geom.end, return 0);
if (part->type == PED_PARTITION_EXTENDED) {
if (!_check_extended_partition (disk, part))
return 0;
}
if (part->type & PED_PARTITION_LOGICAL
&& !ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have a logical partition outside of the "
"extended partition on %s."),
disk->dev->path);
return 0;
}
if (!_disk_check_part_overlaps (disk, part)) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have overlapping partitions."));
return 0;
}
if (! (part->type & PED_PARTITION_LOGICAL)
&& ext_part && ext_part != part
&& ped_geometry_test_inside (&ext_part->geom, &part->geom)) {
ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
_("Can't have a primary partition inside an extended "
"partition."));
return 0;
}
return 1;
}
/**
* Adds PedPartition \p part to PedPartition \p disk.
*
* \warning The partition's geometry may be changed, subject to \p constraint.
* You could set \p constraint to <tt>ped_constraint_exact(&part->geom)</tt>,
* but many partition table schemes have special requirements on the start
* and end of partitions. Therefore, having an overly strict constraint
* will probably mean that this function will fail (in which
* case \p part will be left unmodified)
* \p part is assigned a number (\p part->num) in this process.
*
* \return \c 0 on failure.
*/
int
ped_disk_add_partition (PedDisk* disk, PedPartition* part,
const PedConstraint* constraint)
{
PedConstraint* overlap_constraint = NULL;
PedConstraint* constraints = NULL;
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
if (!_partition_check_basic_sanity (disk, part))
return 0;
_disk_push_update_mode (disk);
if (ped_partition_is_active (part)) {
overlap_constraint
= _partition_get_overlap_constraint (part, &part->geom);
constraints = ped_constraint_intersect (overlap_constraint,
constraint);
if (!constraints && constraint) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have overlapping partitions."));
goto error;
}
if (!_partition_enumerate (part))
goto error;
if (!_partition_align (part, constraints))
goto error;
}
if (!_check_partition (disk, part))
goto error;
if (!_disk_raw_add (disk, part))
goto error;
ped_constraint_destroy (overlap_constraint);
ped_constraint_destroy (constraints);
_disk_pop_update_mode (disk);
#ifdef DEBUG
if (!_disk_check_sanity (disk))
return 0;
#endif
return 1;
error:
ped_constraint_destroy (overlap_constraint);
ped_constraint_destroy (constraints);
_disk_pop_update_mode (disk);
return 0;
}
/**
* Removes PedPartition \p part from PedDisk \p disk.
*
* If \p part is an extended partition, it must not contain any logical
* partitions. \p part is *NOT* destroyed. The caller must call
* ped_partition_destroy(), or use ped_disk_delete_partition() instead.
*
* \return \c 0 on error.
*/
int
ped_disk_remove_partition (PedDisk* disk, PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
_disk_push_update_mode (disk);
PED_ASSERT (part->part_list == NULL, goto error);
_disk_raw_remove (disk, part);
_disk_pop_update_mode (disk);
ped_disk_enumerate_partitions (disk);
return 1;
error:
_disk_pop_update_mode (disk);
return 0;
}
static int
ped_disk_delete_all_logical (PedDisk* disk);
/**
* Removes \p part from \p disk, and destroys \p part.
*
* \return \c 0 on failure.
*/
int
ped_disk_delete_partition (PedDisk* disk, PedPartition* part)
{
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
_disk_push_update_mode (disk);
if (part->type == PED_PARTITION_EXTENDED)
ped_disk_delete_all_logical (disk);
ped_disk_remove_partition (disk, part);
ped_partition_destroy (part);
_disk_pop_update_mode (disk);
return 1;
}
static int
ped_disk_delete_all_logical (PedDisk* disk)
{
PedPartition* walk;
PedPartition* next;
PedPartition* ext_part;
PED_ASSERT (disk != NULL, return 0);
ext_part = ped_disk_extended_partition (disk);
PED_ASSERT (ext_part != NULL, return 0);
for (walk = ext_part->part_list; walk; walk = next) {
next = walk->next;
if (!ped_disk_delete_partition (disk, walk))
return 0;
}
return 1;
}
/**
* Removes and destroys all partitions on \p disk.
*
* \return \c 0 on failure.
*/
int
ped_disk_delete_all (PedDisk* disk)
{
PedPartition* walk;
PedPartition* next;
PED_ASSERT (disk != NULL, return 0);
_disk_push_update_mode (disk);
for (walk = disk->part_list; walk; walk = next) {
next = walk->next;
if (!ped_disk_delete_partition (disk, walk))
return 0;
}
_disk_pop_update_mode (disk);
return 1;
}
/**
* Sets the geometry of \p part (i.e. change a partitions location). This can
* fail for many reasons, e.g. can't overlap with other partitions. If it
* does fail, \p part will remain unchanged. Returns \c 0 on failure. \p part's
* geometry may be set to something different from \p start and \p end subject
* to \p constraint.
*
* \warning The constraint warning from ped_disk_add_partition() applies.
*
* \note this function does not modify the contents of the partition. You need
* to call ped_file_system_resize() separately.
*/
int
ped_disk_set_partition_geom (PedDisk* disk, PedPartition* part,
const PedConstraint* constraint,
PedSector start, PedSector end)
{
PedConstraint* overlap_constraint = NULL;
PedConstraint* constraints = NULL;
PedGeometry old_geom;
PedGeometry new_geom;
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
PED_ASSERT (part->disk == disk, return 0);
old_geom = part->geom;
ped_geometry_init (&new_geom, part->geom.dev, start, end - start + 1);
_disk_push_update_mode (disk);
overlap_constraint
= _partition_get_overlap_constraint (part, &new_geom);
constraints = ped_constraint_intersect (overlap_constraint, constraint);
if (!constraints && constraint) {
ped_exception_throw (
PED_EXCEPTION_ERROR,
PED_EXCEPTION_CANCEL,
_("Can't have overlapping partitions."));
goto error_pop_update_mode;
}
part->geom = new_geom;
if (!_partition_align (part, constraints))
goto error_pop_update_mode;
if (!_check_partition (disk, part))
goto error_pop_update_mode;
/* remove and add, to ensure the ordering gets updated if necessary */
_disk_raw_remove (disk, part);
_disk_raw_add (disk, part);
_disk_pop_update_mode (disk);
ped_constraint_destroy (overlap_constraint);
ped_constraint_destroy (constraints);
return 1;
error_pop_update_mode:
_disk_pop_update_mode (disk);
ped_constraint_destroy (overlap_constraint);
ped_constraint_destroy (constraints);
part->geom = old_geom;
return 0;
}
/**
* Grow PedPartition \p part geometry to the maximum possible subject to
* \p constraint. The new geometry will be a superset of the old geometry.
*
* \return 0 on failure
*/
int
ped_disk_maximize_partition (PedDisk* disk, PedPartition* part,
const PedConstraint* constraint)
{
PedGeometry old_geom;
PedSector global_min_start;
PedSector global_max_end;
PedSector new_start;
PedSector new_end;
PedPartition* ext_part = ped_disk_extended_partition (disk);
PedConstraint* constraint_any;
PED_ASSERT (disk != NULL, return 0);
PED_ASSERT (part != NULL, return 0);
if (part->type & PED_PARTITION_LOGICAL) {
PED_ASSERT (ext_part != NULL, return 0);
global_min_start = ext_part->geom.start;
global_max_end = ext_part->geom.end;
} else {
global_min_start = 0;
global_max_end = disk->dev->length - 1;
}
old_geom = part->geom;
_disk_push_update_mode (disk);
if (part->prev)
new_start = part->prev->geom.end + 1;
else
new_start = global_min_start;
if (part->next)
new_end = part->next->geom.start - 1;
else
new_end = global_max_end;
if (!ped_disk_set_partition_geom (disk, part, constraint, new_start,
new_end))
goto error;
_disk_pop_update_mode (disk);
return 1;
error:
constraint_any = ped_constraint_any (disk->dev);
ped_disk_set_partition_geom (disk, part, constraint_any,
old_geom.start, old_geom.end);
ped_constraint_destroy (constraint_any);
_disk_pop_update_mode (disk);
return 0;
}
/**
* Get the maximum geometry \p part can be grown to, subject to
* \p constraint.
*
* \return \c NULL on failure.
*/
PedGeometry*
ped_disk_get_max_partition_geometry (PedDisk* disk, PedPartition* part,
const PedConstraint* constraint)
{
PedGeometry old_geom;
PedGeometry* max_geom;
PedConstraint* constraint_exact;
PED_ASSERT(disk != NULL, return NULL);
PED_ASSERT(part != NULL, return NULL);
PED_ASSERT(ped_partition_is_active (part), return NULL);
old_geom = part->geom;
if (!ped_disk_maximize_partition (disk, part, constraint))
return NULL;
max_geom = ped_geometry_duplicate (&part->geom);
constraint_exact = ped_constraint_exact (&old_geom);
ped_disk_set_partition_geom (disk, part, constraint_exact,
old_geom.start, old_geom.end);
ped_constraint_destroy (constraint_exact);
/* this assertion should never fail, because the old
* geometry was valid
*/
PED_ASSERT (ped_geometry_test_equal (&part->geom, &old_geom),
return NULL);
return max_geom;
}
/**
* Reduce the size of the extended partition to a minimum while still wrapping
* its logical partitions. If there are no logical partitions, remove the
* extended partition.
*
* \return 0 on failure.
*/
int
ped_disk_minimize_extended_partition (PedDisk* disk)
{
PedPartition* first_logical;
PedPartition* last_logical;
PedPartition* walk;
PedPartition* ext_part;
PedConstraint* constraint;
int status;
PED_ASSERT (disk != NULL, return 0);
ext_part = ped_disk_extended_partition (disk);
if (!ext_part)
return 1;
_disk_push_update_mode (disk);
first_logical = ext_part->part_list;
if (!first_logical) {
_disk_pop_update_mode (disk);
return ped_disk_delete_partition (disk, ext_part);
}
for (walk = first_logical; walk->next; walk = walk->next);
last_logical = walk;
constraint = ped_constraint_any (disk->dev);
status = ped_disk_set_partition_geom (disk, ext_part, constraint,
first_logical->geom.start,
last_logical->geom.end);
ped_constraint_destroy (constraint);
_disk_pop_update_mode (disk);
return status;
}
/**
* @}
*/
/**
* \addtogroup PedPartition
*
* @{
*/
/**
* Returns a name that seems mildly appropriate for a partition type \p type.
*
* Eg, if you pass (PED_PARTITION_LOGICAL & PED_PARTITION_FREESPACE), it
* will return "free". This isn't to be taken too seriously - it's just
* useful for user interfaces, so you can show the user something ;-)
*
* \note The returned string will be in English. However,
* translations are provided, so the caller can call
* dgettext("parted", RESULT) on the result.
*
*/
const char*
ped_partition_type_get_name (PedPartitionType type)
{
if (type & PED_PARTITION_METADATA)
return N_("metadata");
else if (type & PED_PARTITION_FREESPACE)
return N_("free");
else if (type & PED_PARTITION_EXTENDED)
return N_("extended");
else if (type & PED_PARTITION_LOGICAL)
return N_("logical");
else
return N_("primary");
}
/**
* Returns a name for a \p flag, e.g. PED_PARTITION_BOOT will return "boot".
*
* \note The returned string will be in English. However,
* translations are provided, so the caller can call
* dgettext("parted", RESULT) on the result.
*/
const char*
ped_partition_flag_get_name (PedPartitionFlag flag)
{
switch (flag) {
case PED_PARTITION_BOOT:
return N_("boot");
case PED_PARTITION_ROOT:
return N_("root");
case PED_PARTITION_SWAP:
return N_("swap");
case PED_PARTITION_HIDDEN:
return N_("hidden");
case PED_PARTITION_RAID:
return N_("raid");
case PED_PARTITION_LVM:
return N_("lvm");
case PED_PARTITION_LBA:
return N_("lba");
case PED_PARTITION_HPSERVICE:
return N_("hp-service");
case PED_PARTITION_PALO:
return N_("palo");
case PED_PARTITION_PREP:
return N_("prep");
case PED_PARTITION_MSFT_RESERVED:
return N_("msftres");
default:
ped_exception_throw (
PED_EXCEPTION_BUG,
PED_EXCEPTION_CANCEL,
_("Unknown partition flag, %d."),
flag);
return NULL;
}
}
/**
* Iterates through all flags.
*
* ped_partition_flag_next(0) returns the first flag
*
* \return the next flag, or 0 if there are no more flags
*/
PedPartitionFlag
ped_partition_flag_next (PedPartitionFlag flag)
{
return (flag + 1) % (PED_PARTITION_LAST_FLAG + 1);
}
/**
* Returns the flag associated with \p name.
*
* \p name can be the English
* string, or the translation for the native language.
*/
PedPartitionFlag
ped_partition_flag_get_by_name (const char* name)
{
PedPartitionFlag flag;
const char* flag_name;
for (flag = ped_partition_flag_next (0); flag;
flag = ped_partition_flag_next (flag)) {
flag_name = ped_partition_flag_get_name (flag);
if (strcasecmp (name, flag_name) == 0
|| strcasecmp (name, _(flag_name)) == 0)
return flag;
}
return 0;
}
static void
ped_partition_print (const PedPartition* part)
{
PED_ASSERT (part != NULL, return);
printf (" %-10s %02d (%d->%d)\n",
ped_partition_type_get_name (part->type),
part->num,
(int) part->geom.start, (int) part->geom.end);
}
/** @} */
/**
* \addtogroup PedDisk
*
* @{
*/
/**
* Prints a summary of disk's partitions. Useful for debugging.
*/
void
ped_disk_print (const PedDisk* disk)
{
PedPartition* part;
PED_ASSERT (disk != NULL, return);
for (part = disk->part_list; part;
part = ped_disk_next_partition (disk, part))
ped_partition_print (part);
}
/** @} */