| /* |
| 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); |
| } |
| |
| /** @} */ |