/*
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
#if ENABLE_NLS
# include
# define _(String) dgettext (PACKAGE, String)
#else
# define _(String) (String)
#endif /* ENABLE_NLS */
#define BUFFER_SIZE 4096 /* in sectors */
static PedFileSystemType* fs_types = NULL;
static PedFileSystemAlias* fs_aliases = NULL;
void
ped_file_system_type_register (PedFileSystemType* fs_type)
{
PED_ASSERT (fs_type != NULL);
PED_ASSERT (fs_type->ops != NULL);
PED_ASSERT (fs_type->name != NULL);
fs_type->next = fs_types;
fs_types = fs_type;
}
void
ped_file_system_type_unregister (PedFileSystemType* fs_type)
{
PedFileSystemType* walk;
PedFileSystemType* last = NULL;
PED_ASSERT (fs_types != NULL);
PED_ASSERT (fs_type != NULL);
for (walk = fs_types; walk && walk != fs_type;
last = walk, walk = walk->next);
PED_ASSERT (walk != NULL);
if (last)
((struct _PedFileSystemType*) last)->next = fs_type->next;
else
fs_types = fs_type->next;
}
void
ped_file_system_alias_register (PedFileSystemType* fs_type, const char* alias,
int deprecated)
{
PedFileSystemAlias* fs_alias;
PED_ASSERT (fs_type != NULL);
PED_ASSERT (alias != NULL);
fs_alias = ped_malloc (sizeof *fs_alias);
if (!fs_alias)
return;
fs_alias->next = fs_aliases;
fs_alias->fs_type = fs_type;
fs_alias->alias = alias;
fs_alias->deprecated = deprecated;
fs_aliases = fs_alias;
}
void
ped_file_system_alias_unregister (PedFileSystemType* fs_type,
const char* alias)
{
PedFileSystemAlias* walk;
PedFileSystemAlias* last = NULL;
PED_ASSERT (fs_aliases != NULL);
PED_ASSERT (fs_type != NULL);
PED_ASSERT (alias != NULL);
for (walk = fs_aliases; walk; last = walk, walk = walk->next) {
if (walk->fs_type == fs_type && !strcmp (walk->alias, alias))
break;
}
PED_ASSERT (walk != NULL);
if (last)
last->next = walk->next;
else
fs_aliases = walk->next;
free (walk);
}
/**
* Get a PedFileSystemType by its @p name.
*
* @return @c NULL if none found.
*/
PedFileSystemType*
ped_file_system_type_get (const char* name)
{
PedFileSystemType* walk;
PedFileSystemAlias* alias_walk;
PED_ASSERT (name != NULL);
for (walk = fs_types; walk != NULL; walk = walk->next) {
if (!strcasecmp (walk->name, name))
break;
}
if (walk != NULL)
return walk;
for (alias_walk = fs_aliases; alias_walk != NULL;
alias_walk = alias_walk->next) {
if (!strcasecmp (alias_walk->alias, name))
break;
}
if (alias_walk != NULL) {
if (alias_walk->deprecated)
PED_DEBUG (0, "File system alias %s is deprecated",
name);
return alias_walk->fs_type;
}
return NULL;
}
/**
* 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;
}
/**
* Get the next PedFileSystemAlias after @p fs_alias.
*
* @return @c NULL if @p fs_alias is the last item in the list.
*/
PedFileSystemAlias*
ped_file_system_alias_get_next (const PedFileSystemAlias* fs_alias)
{
if (fs_alias)
return fs_alias->next;
else
return fs_aliases;
}
/**
* 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);
PED_ASSERT (fs_type->ops->probe != NULL);
PED_ASSERT (geom != NULL);
if (!ped_device_open (geom->dev))
return 0;
result = fs_type->ops->probe (geom);
ped_device_close (geom->dev);
return result;
}
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 llabs (start_delta) + llabs (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);
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 NULL;
}