summaryrefslogtreecommitdiffstats
path: root/libparted/fs/r/filesys.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparted/fs/r/filesys.c')
-rw-r--r--libparted/fs/r/filesys.c320
1 files changed, 320 insertions, 0 deletions
diff --git a/libparted/fs/r/filesys.c b/libparted/fs/r/filesys.c
new file mode 100644
index 0000000..856238c
--- /dev/null
+++ b/libparted/fs/r/filesys.c
@@ -0,0 +1,320 @@
+/* libparted - a library for manipulating disk partitions
+ Copyright (C) 1999-2001, 2007-2014, 2019-2023 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/** \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 "pt-tools.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#define STREQ(a, b) (strcmp (a, b) == 0)
+
+#ifndef MIN
+# define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+typedef PedFileSystem * (*open_fn_t) (PedGeometry *);
+extern PedFileSystem *hfsplus_open (PedGeometry *);
+extern PedFileSystem *hfs_open (PedGeometry *);
+extern PedFileSystem *fat_open (PedGeometry *);
+
+typedef int (*close_fn_t) (PedFileSystem *);
+extern int hfsplus_close (PedFileSystem *);
+extern int hfs_close (PedFileSystem *);
+extern int fat_close (PedFileSystem *);
+
+typedef int (*resize_fn_t) (PedFileSystem *fs, PedGeometry *geom,
+ PedTimer *timer);
+extern int hfsplus_resize (PedFileSystem *fs, PedGeometry *geom,
+ PedTimer *timer);
+extern int hfs_resize (PedFileSystem *fs, PedGeometry *geom,
+ PedTimer *timer);
+extern int fat_resize (PedFileSystem *fs, PedGeometry *geom,
+ PedTimer *timer);
+
+typedef PedConstraint * (*resize_constraint_fn_t) (PedFileSystem const *fs);
+extern PedConstraint *hfsplus_get_resize_constraint (PedFileSystem const *fs);
+extern PedConstraint *hfs_get_resize_constraint (PedFileSystem const *fs);
+extern PedConstraint *fat_get_resize_constraint (PedFileSystem const *fs);
+
+static bool
+is_hfs_plus (char const *fs_type_name)
+{
+ return STREQ (fs_type_name, "hfsx") || STREQ (fs_type_name, "hfs+");
+}
+
+static open_fn_t
+open_fn (char const *fs_type_name)
+{
+ if (is_hfs_plus (fs_type_name))
+ return hfsplus_open;
+ if (STREQ (fs_type_name, "hfs"))
+ return hfs_open;
+ if (strncmp (fs_type_name, "fat", 3) == 0)
+ return fat_open;
+ return NULL;
+}
+
+static close_fn_t
+close_fn (char const *fs_type_name)
+{
+ if (is_hfs_plus (fs_type_name))
+ return hfsplus_close;
+ if (STREQ (fs_type_name, "hfs"))
+ return hfs_close;
+ if (strncmp (fs_type_name, "fat", 3) == 0)
+ return fat_close;
+ return NULL;
+}
+
+static resize_fn_t
+resize_fn (char const *fs_type_name)
+{
+ if (is_hfs_plus (fs_type_name))
+ return hfsplus_resize;
+ if (STREQ (fs_type_name, "hfs"))
+ return hfs_resize;
+ if (strncmp (fs_type_name, "fat", 3) == 0)
+ return fat_resize;
+ return NULL;
+}
+
+static resize_constraint_fn_t
+resize_constraint_fn (char const *fs_type_name)
+{
+ if (is_hfs_plus (fs_type_name))
+ return hfsplus_get_resize_constraint;
+ if (STREQ (fs_type_name, "hfs"))
+ return hfs_get_resize_constraint;
+ if (strncmp (fs_type_name, "fat", 3) == 0)
+ return fat_get_resize_constraint;
+ return NULL;
+}
+
+/**
+ * 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)
+{
+ PED_ASSERT (geom != NULL);
+
+ if (!ped_device_open (geom->dev))
+ goto error;
+
+ PedFileSystemType *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;
+ }
+
+ open_fn_t open_f = open_fn (type->name);
+ if (open_f == NULL) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("resizing %s file systems is not supported"),
+ type->name);
+ goto error_close_dev;
+ }
+
+ PedGeometry *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;
+ }
+
+ PedFileSystem *fs = (*open_f) (probed_geom);
+ if (!fs)
+ goto error_destroy_probed_geom;
+ ped_geometry_destroy (probed_geom);
+ fs->type = type;
+ return fs;
+
+error_destroy_probed_geom:
+ ped_geometry_destroy (probed_geom);
+error_close_dev:
+ ped_device_close (geom->dev);
+error:
+ return NULL;
+}
+
+/**
+ * Close file system \p fs.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+int
+ped_file_system_close (PedFileSystem* fs)
+{
+ PED_ASSERT (fs != NULL);
+ PedDevice *dev = fs->geom->dev;
+ close_fn_t fn = close_fn (fs->type->name);
+
+ if (!fn || !(fn (fs)))
+ goto error_close_dev;
+ ped_device_close (dev);
+ return 1;
+
+error_close_dev:
+ ped_device_close (dev);
+ return 0;
+}
+
+/**
+ * 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.
+ *
+ * \return \c 1 on success, \c 0 on failure
+ */
+static int
+ped_file_system_clobber (PedGeometry* geom)
+{
+ PED_ASSERT (geom != NULL);
+
+ if (!ped_device_open (geom->dev))
+ return 0;
+
+ /* Clear the first three and the last two sectors, albeit fewer
+ when GEOM is too small. */
+ PedSector len = MIN (geom->length, geom->dev->length);
+
+ int ok = (len <= 5
+ ? ptt_geom_clear_sectors (geom, 0, len)
+ : (ptt_geom_clear_sectors (geom, 0, 3)
+ && ptt_geom_clear_sectors (geom, geom->dev->length - 2, 2)));
+
+ ped_device_close (geom->dev);
+ return !!ok;
+}
+
+/* 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;
+}
+
+/**
+ * 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);
+ PED_ASSERT (geom != NULL);
+
+ resize_fn_t resize_f = resize_fn (fs->type->name);
+ if (resize_f == NULL) {
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("resizing %s file systems is not supported"),
+ fs->type->name);
+ return 0;
+ }
+
+ if (!ped_file_system_clobber_exclude (geom, fs->geom))
+ return 0;
+
+ return resize_f (fs, geom, timer);
+}
+
+/**
+ * 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 cannot 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);
+
+ resize_constraint_fn_t resize_constraint_f =
+ resize_constraint_fn (fs->type->name);
+ if (resize_constraint_f == NULL)
+ return NULL;
+
+ return resize_constraint_f (fs);
+}