/* 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 .
*/
/** \file filesys.c */
/**
* \addtogroup PedFileSystem
*
* \note File systems exist on a PedGeometry - NOT a PedPartition.
*
* @{
*/
#include
#include
#include
#include "pt-tools.h"
#if ENABLE_NLS
# include
# 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);
}