| /* |
| libparted - a library for manipulating disk partitions |
| Copyright (C) 1999, 2000, 2001, 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 filesys.c */ |
| |
| /** |
| * \addtogroup PedFileSystem |
| * |
| * \note File systems exist on a PedGeometry - NOT a PedPartition. |
| * |
| * @{ |
| */ |
| |
| #include <config.h> |
| |
| #include <parted/parted.h> |
| #include <parted/debug.h> |
| |
| #if ENABLE_NLS |
| # include <libintl.h> |
| # define _(String) dgettext (PACKAGE, String) |
| #else |
| # define _(String) (String) |
| #endif /* ENABLE_NLS */ |
| |
| #define BUFFER_SIZE 4096 /* in sectors */ |
| |
| static PedFileSystemType* fs_types = NULL; |
| |
| void |
| ped_file_system_type_register (PedFileSystemType* fs_type) |
| { |
| PED_ASSERT (fs_type != NULL, return); |
| PED_ASSERT (fs_type->ops != NULL, return); |
| PED_ASSERT (fs_type->name != NULL, return); |
| |
| /* pretend that "next" isn't part of the struct :-) */ |
| ((struct _PedFileSystemType*) fs_type)->next = fs_types; |
| fs_types = (struct _PedFileSystemType*) fs_type; |
| } |
| |
| void |
| ped_file_system_type_unregister (PedFileSystemType* fs_type) |
| { |
| PedFileSystemType* walk; |
| PedFileSystemType* last = NULL; |
| |
| PED_ASSERT (fs_types != NULL, return); |
| PED_ASSERT (fs_type != NULL, return); |
| |
| for (walk = fs_types; walk && walk != fs_type; |
| last = walk, walk = walk->next); |
| |
| PED_ASSERT (walk != NULL, return); |
| if (last) |
| ((struct _PedFileSystemType*) last)->next = fs_type->next; |
| else |
| fs_types = fs_type->next; |
| } |
| |
| /** |
| * Get a PedFileSystemType by its @p name. |
| * |
| * @return @c NULL if none found. |
| */ |
| PedFileSystemType* |
| ped_file_system_type_get (const char* name) |
| { |
| PedFileSystemType* walk; |
| |
| PED_ASSERT (name != NULL, return NULL); |
| |
| for (walk = fs_types; walk != NULL; walk = walk->next) { |
| if (!strcasecmp (walk->name, name)) |
| break; |
| } |
| return walk; |
| } |
| |
| /** |
| * Get the next PedFileSystemType after @p fs_type. |
| * |
| * @return @c NULL if @p fs_type is the last item in the list. |
| */ |
| PedFileSystemType* |
| ped_file_system_type_get_next (const PedFileSystemType* fs_type) |
| { |
| if (fs_type) |
| return fs_type->next; |
| else |
| return fs_types; |
| } |
| |
| /** |
| * Attempt to find a file system and return the region it occupies. |
| * |
| * @param fs_type The file system type to probe for. |
| * @param geom The region to be searched. |
| * |
| * @return @p NULL if @p fs_type file system wasn't detected |
| */ |
| PedGeometry* |
| ped_file_system_probe_specific ( |
| const PedFileSystemType* fs_type, PedGeometry* geom) |
| { |
| PedGeometry* result; |
| |
| PED_ASSERT (fs_type != NULL, return NULL); |
| PED_ASSERT (fs_type->ops->probe != NULL, return NULL); |
| PED_ASSERT (geom != NULL, return NULL); |
| |
| if (!ped_device_open (geom->dev)) |
| return 0; |
| result = fs_type->ops->probe (geom); |
| ped_device_close (geom->dev); |
| return result; |
| } |
| |
| static int |
| _test_open (PedFileSystemType* fs_type, PedGeometry* geom) |
| { |
| PedFileSystem* fs; |
| |
| ped_exception_fetch_all (); |
| fs = fs_type->ops->open (geom); |
| if (fs) |
| fs_type->ops->close (fs); |
| else |
| ped_exception_catch (); |
| ped_exception_leave_all (); |
| return fs != NULL; |
| } |
| |
| static PedFileSystemType* |
| _probe_with_open (PedGeometry* geom, int detected_count, |
| PedFileSystemType* detected[]) |
| { |
| int i; |
| PedFileSystemType* open_detected = NULL; |
| |
| ped_device_open (geom->dev); |
| |
| /* If one and only one file system that Parted is able to open |
| * can be successfully opened on this geometry, return it. |
| * If more than one can be, return NULL. |
| */ |
| for (i=0; i<detected_count; i++) { |
| if (!detected[i]->ops->open || !_test_open (detected [i], geom)) |
| continue; |
| |
| if (open_detected) { |
| ped_device_close (geom->dev); |
| return NULL; |
| } else { |
| open_detected = detected [i]; |
| } |
| } |
| |
| /* If no file system has been successfully opened, and |
| * if Parted has detected at most one unopenable file system, |
| * return it. |
| */ |
| if (!open_detected) |
| for (i=0; i<detected_count; i++) { |
| if (detected[i]->ops->open) |
| continue; |
| if (open_detected) { |
| ped_device_close (geom->dev); |
| return NULL; |
| } else { |
| open_detected = detected [i]; |
| } |
| } |
| |
| ped_device_close (geom->dev); |
| return open_detected; |
| } |
| |
| static int |
| _geometry_error (const PedGeometry* a, const PedGeometry* b) |
| { |
| PedSector start_delta = a->start - b->start; |
| PedSector end_delta = a->end - b->end; |
| |
| return abs (start_delta) + abs (end_delta); |
| } |
| |
| static PedFileSystemType* |
| _best_match (const PedGeometry* geom, PedFileSystemType* detected [], |
| const int detected_error [], int detected_count) |
| { |
| int best_match = 0; |
| int i; |
| PedSector min_error; |
| |
| min_error = PED_MAX (4096, geom->length / 100); |
| |
| for (i = 1; i < detected_count; i++) { |
| if (detected_error [i] < detected_error [best_match]) |
| best_match = i; |
| } |
| |
| /* make sure the best match is significantly better than all the |
| * other matches |
| */ |
| for (i = 0; i < detected_count; i++) { |
| if (i == best_match) |
| continue; |
| |
| if (abs (detected_error [best_match] - detected_error [i]) |
| < min_error) |
| return NULL; |
| } |
| |
| return detected [best_match]; |
| } |
| |
| |
| /** |
| * Attempt to detect a file system in region \p geom. |
| * This function tries to be clever at dealing with ambiguous |
| * situations, such as when one file system was not completely erased before a |
| * new file system was created on top of it. |
| * |
| * \return a new PedFileSystem on success, \c NULL on failure |
| */ |
| PedFileSystemType* |
| ped_file_system_probe (PedGeometry* geom) |
| { |
| PedFileSystemType* detected[32]; |
| int detected_error[32]; |
| int detected_count = 0; |
| PedFileSystemType* walk = NULL; |
| |
| PED_ASSERT (geom != NULL, return NULL); |
| |
| if (!ped_device_open (geom->dev)) |
| return NULL; |
| |
| ped_exception_fetch_all (); |
| while ( (walk = ped_file_system_type_get_next (walk)) ) { |
| PedGeometry* probed; |
| |
| probed = ped_file_system_probe_specific (walk, geom); |
| if (probed) { |
| detected [detected_count] = walk; |
| detected_error [detected_count] |
| = _geometry_error (geom, probed); |
| detected_count++; |
| ped_geometry_destroy (probed); |
| } else { |
| ped_exception_catch (); |
| } |
| } |
| ped_exception_leave_all (); |
| |
| ped_device_close (geom->dev); |
| |
| if (!detected_count) |
| return NULL; |
| walk = _best_match (geom, detected, detected_error, detected_count); |
| if (walk) |
| return walk; |
| return _probe_with_open (geom, detected_count, detected); |
| } |
| |
| /** |
| * This function erases all file system signatures that indicate that a |
| * file system occupies a given region described by \p geom. |
| * After this operation ped_file_system_probe() won't detect any file system. |
| * |
| * \note ped_file_system_create() calls this before creating a new file system. |
| * |
| * \return \c 1 on success, \c 0 on failure |
| */ |
| int |
| ped_file_system_clobber (PedGeometry* geom) |
| { |
| PedFileSystemType* fs_type = NULL; |
| |
| PED_ASSERT (geom != NULL, return 0); |
| |
| if (!ped_device_open (geom->dev)) |
| goto error; |
| |
| ped_exception_fetch_all (); |
| while ((fs_type = ped_file_system_type_get_next (fs_type))) { |
| PedGeometry* probed; |
| |
| if (!fs_type->ops->clobber) |
| continue; |
| |
| probed = ped_file_system_probe_specific (fs_type, geom); |
| if (!probed) { |
| ped_exception_catch (); |
| continue; |
| } |
| ped_geometry_destroy (probed); |
| |
| if (fs_type->ops->clobber && !fs_type->ops->clobber (geom)) { |
| ped_exception_leave_all (); |
| goto error_close_dev; |
| } |
| } |
| ped_device_close (geom->dev); |
| ped_exception_leave_all (); |
| return 1; |
| |
| error_close_dev: |
| ped_device_close (geom->dev); |
| error: |
| return 0; |
| } |
| |
| /* This function erases all signatures that indicate the presence of |
| * a file system in a particular region, without erasing any data |
| * contained inside the "exclude" region. |
| */ |
| static int |
| ped_file_system_clobber_exclude (PedGeometry* geom, |
| const PedGeometry* exclude) |
| { |
| PedGeometry* clobber_geom; |
| int status; |
| |
| if (ped_geometry_test_sector_inside (exclude, geom->start)) |
| return 1; |
| |
| clobber_geom = ped_geometry_duplicate (geom); |
| if (ped_geometry_test_overlap (clobber_geom, exclude)) |
| ped_geometry_set_end (clobber_geom, exclude->start - 1); |
| |
| status = ped_file_system_clobber (clobber_geom); |
| ped_geometry_destroy (clobber_geom); |
| return status; |
| } |
| |
| /** |
| * This function opens the file system stored on \p geom, if it |
| * can find one. |
| * It is often called in the following manner: |
| * \code |
| * fs = ped_file_system_open (&part.geom) |
| * \endcode |
| * |
| * \throws PED_EXCEPTION_ERROR if file system could not be detected |
| * \throws PED_EXCEPTION_ERROR if the file system is bigger than its volume |
| * \throws PED_EXCEPTION_NO_FEATURE if opening of a file system stored on |
| * \p geom is not implemented |
| * |
| * \return a PedFileSystem on success, \c NULL on failure. |
| */ |
| PedFileSystem* |
| ped_file_system_open (PedGeometry* geom) |
| { |
| PedFileSystemType* type; |
| PedFileSystem* fs; |
| PedGeometry* probed_geom; |
| |
| PED_ASSERT (geom != NULL, return NULL); |
| |
| if (!ped_device_open (geom->dev)) |
| goto error; |
| |
| type = ped_file_system_probe (geom); |
| if (!type) { |
| ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, |
| _("Could not detect file system.")); |
| goto error_close_dev; |
| } |
| |
| probed_geom = ped_file_system_probe_specific (type, geom); |
| if (!probed_geom) |
| goto error_close_dev; |
| if (!ped_geometry_test_inside (geom, probed_geom)) { |
| if (ped_exception_throw ( |
| PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_IGNORE_CANCEL, |
| _("The file system is bigger than its volume!")) |
| != PED_EXCEPTION_IGNORE) |
| goto error_destroy_probed_geom; |
| } |
| |
| if (!type->ops->open) { |
| ped_exception_throw (PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Support for opening %s file systems " |
| "is not implemented yet."), |
| type->name); |
| goto error_destroy_probed_geom; |
| } |
| |
| fs = type->ops->open (probed_geom); |
| if (!fs) |
| goto error_destroy_probed_geom; |
| ped_geometry_destroy (probed_geom); |
| return fs; |
| |
| error_destroy_probed_geom: |
| ped_geometry_destroy (probed_geom); |
| error_close_dev: |
| ped_device_close (geom->dev); |
| error: |
| return 0; |
| } |
| |
| /** |
| * This function initializes a new file system of type \p type on |
| * a region described by \p geom, writing out appropriate metadata and |
| * signatures. If \p timer is non-NULL, it is used as the progress meter. |
| * |
| * \throws PED_EXCEPTION_NO_FEATURE if creating file system type \p type |
| * is not implemented yet |
| * |
| * \return a PedFileSystem on success, \c NULL on failure |
| */ |
| PedFileSystem* |
| ped_file_system_create (PedGeometry* geom, const PedFileSystemType* type, |
| PedTimer* timer) |
| { |
| PedFileSystem* fs; |
| |
| PED_ASSERT (geom != NULL, return NULL); |
| PED_ASSERT (type != NULL, return NULL); |
| |
| if (!type->ops->create) { |
| ped_exception_throw (PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Support for creating %s file systems " |
| "is not implemented yet."), |
| type->name); |
| goto error; |
| } |
| |
| if (!ped_device_open (geom->dev)) |
| goto error; |
| |
| if (!ped_file_system_clobber (geom)) |
| goto error_close_dev; |
| fs = type->ops->create (geom, timer); |
| if (!fs) |
| goto error_close_dev; |
| return fs; |
| |
| error_close_dev: |
| ped_device_close (geom->dev); |
| error: |
| return 0; |
| } |
| |
| /** |
| * Close file system \p fs. |
| * |
| * \return \c 1 on success, \c 0 on failure |
| */ |
| int |
| ped_file_system_close (PedFileSystem* fs) |
| { |
| PedDevice* dev = fs->geom->dev; |
| |
| PED_ASSERT (fs != NULL, goto error_close_dev); |
| |
| if (!fs->type->ops->close (fs)) |
| goto error_close_dev; |
| ped_device_close (dev); |
| return 1; |
| |
| error_close_dev: |
| ped_device_close (dev); |
| return 0; |
| } |
| |
| /** |
| * Check \p fs file system for errors. |
| * |
| * \throws PED_EXCEPTION_NO_FEATURE if checking file system \p fs is |
| * not implemented yet |
| * |
| * \return \c 0 on failure (i.e. unfixed errors) |
| */ |
| int |
| ped_file_system_check (PedFileSystem* fs, PedTimer* timer) |
| { |
| PED_ASSERT (fs != NULL, return 0); |
| |
| if (!fs->type->ops->check) { |
| ped_exception_throw (PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Support for checking %s file systems " |
| "is not implemented yet."), |
| fs->type->name); |
| return 0; |
| } |
| return fs->type->ops->check (fs, timer); |
| } |
| |
| static int |
| _raw_copy (const PedGeometry* src, PedGeometry* dest, PedTimer* timer) |
| { |
| char* buf; |
| PedSector pos; |
| |
| PED_ASSERT (src != NULL, goto error); |
| PED_ASSERT (dest != NULL, goto error); |
| PED_ASSERT (src->length <= dest->length, goto error); |
| |
| buf = ped_malloc (BUFFER_SIZE * 512); /* FIXME */ |
| if (!buf) |
| goto error; |
| |
| if (!ped_device_open (src->dev)) |
| goto error_free_buf; |
| if (!ped_device_open (dest->dev)) |
| goto error_close_src; |
| |
| for (pos = 0; pos + BUFFER_SIZE < src->length; pos += BUFFER_SIZE) { |
| ped_timer_update (timer, 1.0 * pos / src->length); |
| if (!ped_geometry_read (src, buf, pos, BUFFER_SIZE)) |
| goto error_close_dest; |
| if (!ped_geometry_write (dest, buf, pos, BUFFER_SIZE)) |
| goto error_close_dest; |
| } |
| if (pos < src->length) { |
| ped_timer_update (timer, 1.0 * pos / src->length); |
| if (!ped_geometry_read (src, buf, pos, src->length - pos)) |
| goto error_close_dest; |
| if (!ped_geometry_write (dest, buf, pos, src->length - pos)) |
| goto error_close_dest; |
| } |
| ped_timer_update (timer, 1.0); |
| |
| ped_device_close (src->dev); |
| ped_device_close (dest->dev); |
| ped_free (buf); |
| return 1; |
| |
| error_close_dest: |
| ped_device_close (dest->dev); |
| error_close_src: |
| ped_device_close (src->dev); |
| error_free_buf: |
| ped_free (buf); |
| error: |
| return 0; |
| } |
| |
| static PedFileSystem* |
| _raw_copy_and_resize (const PedFileSystem* fs, PedGeometry* geom, |
| PedTimer* timer) |
| { |
| PedFileSystem* new_fs; |
| PedTimer* sub_timer = NULL; |
| |
| ped_timer_reset (timer); |
| ped_timer_set_state_name (timer, _("raw block copying")); |
| |
| sub_timer = ped_timer_new_nested (timer, 0.95); |
| if (!_raw_copy (fs->geom, geom, sub_timer)) |
| goto error; |
| ped_timer_destroy_nested (sub_timer); |
| |
| new_fs = ped_file_system_open (geom); |
| if (!new_fs) |
| goto error; |
| |
| ped_timer_set_state_name (timer, _("growing file system")); |
| |
| sub_timer = ped_timer_new_nested (timer, 0.05); |
| if (!ped_file_system_resize (new_fs, geom, sub_timer)) |
| goto error_close_new_fs; |
| ped_timer_destroy_nested (sub_timer); |
| return new_fs; |
| |
| error_close_new_fs: |
| ped_file_system_close (new_fs); |
| error: |
| ped_timer_destroy_nested (sub_timer); |
| return NULL; |
| } |
| |
| /** |
| * Create a new file system (of the same type) on \p geom, and |
| * copy the contents of \p fs into the new filesystem. |
| * If \p timer is non-NULL, it is used as the progress meter. |
| * |
| * \throws PED_EXCEPTION_ERROR when trying to copy onto an overlapping partition |
| * \throws PED_EXCEPTION_NO_FEATURE if copying of file system \p fs |
| * is not implemented yet |
| * |
| * \return a new PedFileSystem on success, \c NULL on failure |
| */ |
| PedFileSystem* |
| ped_file_system_copy (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) |
| { |
| PedFileSystem* new_fs; |
| |
| PED_ASSERT (fs != NULL, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| |
| if (!ped_device_open (geom->dev)) |
| goto error; |
| |
| if (ped_geometry_test_overlap (fs->geom, geom)) { |
| ped_exception_throw ( |
| PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, |
| _("Can't copy onto an overlapping partition.")); |
| goto error_close_dev; |
| } |
| |
| if (!fs->checked && fs->type->ops->check) { |
| if (!ped_file_system_check (fs, timer)) |
| goto error_close_dev; |
| } |
| |
| if (!ped_file_system_clobber_exclude (geom, fs->geom)) |
| goto error_close_dev; |
| |
| if (!fs->type->ops->copy) { |
| if (fs->type->ops->resize) { |
| if (fs->geom->length <= geom->length) |
| return _raw_copy_and_resize ( |
| fs, (PedGeometry*) geom, |
| timer); |
| |
| ped_exception_throw ( |
| PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Direct support for copying file systems is " |
| "not yet implemented for %s. However, " |
| "support for resizing is implemented. " |
| "Therefore, the file system can be copied if " |
| "the new partition is at least as big as the " |
| "old one. So, either shrink the partition " |
| "you are trying to copy, or copy to a bigger " |
| "partition."), |
| fs->type->name); |
| goto error_close_dev; |
| } else { |
| ped_exception_throw ( |
| PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Support for copying %s file systems is not " |
| "implemented yet."), |
| fs->type->name); |
| goto error_close_dev; |
| } |
| } |
| new_fs = fs->type->ops->copy (fs, geom, timer); |
| if (!new_fs) |
| goto error_close_dev; |
| return new_fs; |
| |
| error_close_dev: |
| ped_device_close (geom->dev); |
| error: |
| return NULL;; |
| } |
| |
| /** |
| * Resize \p fs to new geometry \p geom. |
| * |
| * \p geom should satisfy the ped_file_system_get_resize_constraint(). |
| * (This isn't asserted, so it's not a bug not to... just it's likely |
| * to fail ;) If \p timer is non-NULL, it is used as the progress meter. |
| * |
| * \throws PED_EXCEPTION_NO_FEATURE if resizing of file system \p fs |
| * is not implemented yet |
| * |
| * \return \c 0 on failure |
| */ |
| int |
| ped_file_system_resize (PedFileSystem* fs, PedGeometry* geom, PedTimer* timer) |
| { |
| PED_ASSERT (fs != NULL, return 0); |
| PED_ASSERT (geom != NULL, return 0); |
| |
| if (!fs->type->ops->resize) { |
| ped_exception_throw (PED_EXCEPTION_NO_FEATURE, |
| PED_EXCEPTION_CANCEL, |
| _("Support for resizing %s file systems " |
| "is not implemented yet."), |
| fs->type->name); |
| return 0; |
| } |
| if (!fs->checked && fs->type->ops->check) { |
| if (!ped_file_system_check (fs, timer)) |
| return 0; |
| } |
| if (!ped_file_system_clobber_exclude (geom, fs->geom)) |
| return 0; |
| |
| return fs->type->ops->resize (fs, geom, timer); |
| } |
| |
| /** |
| * This function returns a constraint on the region that all file systems |
| * of a particular type \p fs_type created on device \p dev with |
| * ped_file_system_create() must satisfy. For example, FAT16 file systems must |
| * be at least 32 megabytes. |
| * |
| * \return \c NULL on failure |
| */ |
| PedConstraint* |
| ped_file_system_get_create_constraint (const PedFileSystemType* fs_type, |
| const PedDevice* dev) |
| { |
| PED_ASSERT (fs_type != NULL, return NULL); |
| PED_ASSERT (dev != NULL, return NULL); |
| |
| if (!fs_type->ops->get_create_constraint) |
| return NULL; |
| return fs_type->ops->get_create_constraint (dev); |
| } |
| /** |
| * Return a constraint, that represents all of the possible ways the |
| * file system \p fs can be resized with ped_file_system_resize(). |
| * This takes into account the amount of used space on |
| * the filesystem \p fs and the capabilities of the resize algorithm. |
| * Hints: |
| * -# if constraint->start_align->grain_size == 0, or |
| * constraint->start_geom->length == 1, then the start can not be moved |
| * -# constraint->min_size is the minimum size you can resize the partition |
| * to. You might want to tell the user this ;-). |
| * |
| * \return a PedConstraint on success, \c NULL on failure |
| */ |
| PedConstraint* |
| ped_file_system_get_resize_constraint (const PedFileSystem* fs) |
| { |
| PED_ASSERT (fs != NULL, return 0); |
| |
| if (!fs->type->ops->get_resize_constraint) |
| return NULL; |
| return fs->type->ops->get_resize_constraint (fs); |
| } |
| |
| /** |
| * Get the constraint on copying \p fs with ped_file_system_copy() |
| * to somewhere on \p dev. |
| * |
| * \return a PedConstraint on success, \c NULL on failure |
| */ |
| PedConstraint* |
| ped_file_system_get_copy_constraint (const PedFileSystem* fs, |
| const PedDevice* dev) |
| { |
| PedGeometry full_dev; |
| |
| PED_ASSERT (fs != NULL, return NULL); |
| PED_ASSERT (dev != NULL, return NULL); |
| |
| if (fs->type->ops->get_copy_constraint) |
| return fs->type->ops->get_copy_constraint (fs, dev); |
| |
| if (fs->type->ops->resize) { |
| if (!ped_geometry_init (&full_dev, dev, 0, dev->length - 1)) |
| return NULL; |
| return ped_constraint_new ( |
| ped_alignment_any, ped_alignment_any, |
| &full_dev, &full_dev, |
| fs->geom->length, dev->length); |
| } |
| |
| return NULL; |
| } |
| |
| /** @} */ |