diff options
author | Anant Narayanan <anant@kix.in> | 2006-09-14 15:18:45 +0000 |
---|---|---|
committer | Anant Narayanan <anant@kix.in> | 2006-09-14 15:18:45 +0000 |
commit | 232dbda915dfcfec99e5983b7f53d57d4498a6aa (patch) | |
tree | 4d54060e75f7f2df07de6e83004551b610ac9865 /libparted/filesys.c | |
download | parted-232dbda915dfcfec99e5983b7f53d57d4498a6aa.tar.gz |
Fix ChangeLog
git-svn-id: svn://svn.debian.org/svn/parted/upstream/trunk@820 2d424fd7-7fe2-0310-af74-8bc65edeb173
Diffstat (limited to 'libparted/filesys.c')
-rw-r--r-- | libparted/filesys.c | 786 |
1 files changed, 786 insertions, 0 deletions
diff --git a/libparted/filesys.c b/libparted/filesys.c new file mode 100644 index 0000000..cf33f96 --- /dev/null +++ b/libparted/filesys.c @@ -0,0 +1,786 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 1999, 2000, 2001 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> +#include <string.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_type != NULL, return); + + for (walk = fs_types; walk != fs_type; walk = walk->next) { + if (!walk) return; + last = walk; + } + + if (last) { + ((struct _PedFileSystemType*) last)->next = fs_type->next; + } else { + fs_types = fs_types->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); +error: + 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; +} + +/** @} */ |