summaryrefslogtreecommitdiffstats
path: root/libparted/fs/linux_swap/linux_swap.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparted/fs/linux_swap/linux_swap.c')
-rw-r--r--libparted/fs/linux_swap/linux_swap.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/libparted/fs/linux_swap/linux_swap.c b/libparted/fs/linux_swap/linux_swap.c
new file mode 100644
index 0000000..60100b0
--- /dev/null
+++ b/libparted/fs/linux_swap/linux_swap.c
@@ -0,0 +1,396 @@
+/*
+ libparted - a library for manipulating disk partitions
+ Copyright (C) 1999-2000, 2002, 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/>.
+*/
+
+/* It's a bit silly calling a swap partition a file system. Oh well... */
+
+#include <config.h>
+
+#include <parted/parted.h>
+#include <parted/endian.h>
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(String) dgettext (PACKAGE, String)
+#else
+# define _(String) (String)
+#endif /* ENABLE_NLS */
+
+#include <unistd.h>
+#include <uuid/uuid.h>
+
+#define SWAP_SPECIFIC(fs) ((SwapSpecific*) (fs->type_specific))
+#define BUFFER_SIZE 128
+
+#define LINUXSWAP_BLOCK_SIZES ((int[2]){512, 0})
+
+typedef struct {
+ char page_map[1];
+} SwapOldHeader;
+
+/* ripped from mkswap */
+typedef struct {
+ char bootbits[1024]; /* Space for disklabel etc. */
+ uint32_t version;
+ uint32_t last_page;
+ uint32_t nr_badpages;
+ unsigned char sws_uuid[16];
+ unsigned char sws_volume[16];
+ uint32_t padding[117];
+ uint32_t badpages[1];
+} SwapNewHeader;
+
+typedef struct {
+ union {
+ SwapNewHeader new;
+ SwapOldHeader old;
+ }* header;
+
+ void* buffer;
+ int buffer_size;
+
+ PedSector page_sectors;
+ unsigned int page_count;
+ unsigned int version;
+ unsigned int max_bad_pages;
+} SwapSpecific;
+
+static PedFileSystemType _swap_v0_type;
+static PedFileSystemType _swap_v1_type;
+static PedFileSystemType _swap_swsusp_type;
+
+static PedFileSystem* _swap_v0_open (PedGeometry* geom);
+static PedFileSystem* _swap_v1_open (PedGeometry* geom);
+static PedFileSystem* _swap_swsusp_open (PedGeometry* geom);
+static int swap_close (PedFileSystem* fs);
+
+static PedGeometry*
+_generic_swap_probe (PedGeometry* geom, int kind)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ PedGeometry* probed_geom;
+ PedSector length;
+
+ switch (kind) {
+ /* Check for old style swap partitions. */
+ case 0:
+ fs = _swap_v0_open(geom);
+ break;
+ /* Check for new style swap partitions. */
+ case 1:
+ fs = _swap_v1_open(geom);
+ break;
+ /* Check for swap partitions containing swsusp data. */
+ case -1:
+ fs = _swap_swsusp_open(geom);
+ break;
+ /* Not reached. */
+ default:
+ goto error;
+ }
+
+ if (!fs)
+ goto error;
+ fs_info = SWAP_SPECIFIC (fs);
+
+ if (fs_info->version)
+ length = fs_info->page_sectors * fs_info->page_count;
+ else
+ length = geom->length;
+
+ probed_geom = ped_geometry_new (geom->dev, geom->start, length);
+ if (!probed_geom)
+ goto error_close_fs;
+ swap_close (fs);
+ return probed_geom;
+
+error_close_fs:
+ swap_close (fs);
+error:
+ return NULL;
+}
+
+
+static int
+swap_init (PedFileSystem* fs)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+
+ fs_info->page_sectors = getpagesize () / fs->geom->dev->sector_size;
+ fs_info->page_count = fs->geom->length / fs_info->page_sectors;
+ fs_info->version = 1;
+ fs_info->max_bad_pages = (getpagesize()
+ - sizeof (SwapNewHeader)) / 4;
+
+ return ped_geometry_read (fs->geom, fs_info->header,
+ 0, fs_info->page_sectors);
+}
+
+
+static PedFileSystem*
+swap_alloc (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+
+ fs = (PedFileSystem*) ped_malloc (sizeof (PedFileSystem));
+ if (!fs)
+ goto error;
+
+ fs->type_specific = (SwapSpecific*) ped_malloc (sizeof (SwapSpecific));
+ if (!fs->type_specific)
+ goto error_free_fs;
+
+ fs_info = SWAP_SPECIFIC (fs);
+ fs_info->header = ped_malloc (PED_MAX(getpagesize(), geom->dev->sector_size));
+ if (!fs_info->header)
+ goto error_free_type_specific;
+
+ fs_info = SWAP_SPECIFIC (fs);
+ fs_info->buffer_size = getpagesize() * BUFFER_SIZE;
+ fs_info->buffer = ped_malloc (fs_info->buffer_size);
+ if (!fs_info->buffer)
+ goto error_free_header;
+
+ fs->geom = ped_geometry_duplicate (geom);
+ if (!fs->geom)
+ goto error_free_buffer;
+ fs->type = &_swap_v1_type;
+ return fs;
+
+error_free_buffer:
+ free (fs_info->buffer);
+error_free_header:
+ free (fs_info->header);
+error_free_type_specific:
+ free (fs->type_specific);
+error_free_fs:
+ free (fs);
+error:
+ return NULL;
+}
+
+static void
+swap_free (PedFileSystem* fs)
+{
+ SwapSpecific* fs_info = SWAP_SPECIFIC (fs);
+
+ free (fs_info->buffer);
+ free (fs_info->header);
+ free (fs->type_specific);
+
+ ped_geometry_destroy (fs->geom);
+ free (fs);
+}
+
+static PedFileSystem*
+_swap_v0_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ const char* sig;
+
+ fs = swap_alloc (geom);
+ if (!fs)
+ goto error;
+ swap_init (fs);
+
+ fs_info = SWAP_SPECIFIC (fs);
+ if (!ped_geometry_read (fs->geom, fs_info->header, 0,
+ fs_info->page_sectors))
+ goto error_free_fs;
+
+ sig = ((char*) fs_info->header) + getpagesize() - 10;
+ if (strncmp (sig, "SWAP-SPACE", 10) == 0) {
+ fs_info->version = 0;
+ fs_info->page_count
+ = PED_MIN (fs->geom->length / fs_info->page_sectors,
+ 8 * (getpagesize() - 10));
+ } else {
+ char _sig [11];
+
+ memcpy (_sig, sig, 10);
+ _sig [10] = 0;
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unrecognised old style linux swap signature '%10s'."), _sig);
+ goto error_free_fs;
+ }
+
+ fs->checked = 1;
+ return fs;
+
+error_free_fs:
+ swap_free (fs);
+error:
+ return NULL;
+}
+
+static PedFileSystem*
+_swap_v1_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ const char* sig;
+
+ fs = swap_alloc (geom);
+ if (!fs)
+ goto error;
+ if (!swap_init(fs))
+ goto error_free_fs;
+
+ fs_info = SWAP_SPECIFIC (fs);
+
+ sig = ((char*) fs_info->header) + getpagesize() - 10;
+ if (strncmp (sig, "SWAPSPACE2", 10) == 0) {
+ fs_info->version = 1;
+ fs_info->page_count = fs_info->header->new.last_page;
+ } else {
+ char _sig [11];
+
+ memcpy (_sig, sig, 10);
+ _sig [10] = 0;
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unrecognised new style linux swap signature '%10s'."), _sig);
+ goto error_free_fs;
+ }
+
+ fs->checked = 1;
+ return fs;
+
+error_free_fs:
+ swap_free (fs);
+error:
+ return NULL;
+}
+
+static PedFileSystem*
+_swap_swsusp_open (PedGeometry* geom)
+{
+ PedFileSystem* fs;
+ SwapSpecific* fs_info;
+ const char* sig;
+
+ fs = swap_alloc (geom);
+ if (!fs)
+ goto error;
+ fs->type = &_swap_swsusp_type;
+ swap_init (fs);
+
+ fs_info = SWAP_SPECIFIC (fs);
+ if (!ped_geometry_read (fs->geom, fs_info->header, 0,
+ fs_info->page_sectors))
+ goto error_free_fs;
+
+ sig = ((char*) fs_info->header) + getpagesize() - 10;
+ if (strncmp (sig, "S1SUSPEND", 9) == 0) {
+ fs_info->version = -1;
+ } else {
+ char _sig [10];
+
+ memcpy (_sig, sig, 9);
+ _sig [9] = 0;
+ ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
+ _("Unrecognised swsusp linux swap signature '%9s'."), _sig);
+ goto error_free_fs;
+ }
+
+ fs->checked = 1;
+ return fs;
+
+error_free_fs:
+ swap_free (fs);
+error:
+ return NULL;
+}
+
+static int
+swap_close (PedFileSystem* fs)
+{
+ swap_free (fs);
+ return 1;
+}
+
+static PedGeometry*
+_swap_v0_probe (PedGeometry* geom) {
+ return _generic_swap_probe (geom, 0);
+}
+
+static PedGeometry*
+_swap_v1_probe (PedGeometry* geom) {
+ return _generic_swap_probe (geom, 1);
+}
+
+static PedGeometry*
+_swap_swsusp_probe (PedGeometry* geom) {
+ return _generic_swap_probe (geom, -1);
+}
+
+static PedFileSystemOps _swap_v0_ops = {
+ probe: _swap_v0_probe,
+};
+
+static PedFileSystemOps _swap_v1_ops = {
+ probe: _swap_v1_probe,
+};
+
+static PedFileSystemOps _swap_swsusp_ops = {
+ probe: _swap_swsusp_probe,
+};
+
+static PedFileSystemType _swap_v0_type = {
+ next: NULL,
+ ops: &_swap_v0_ops,
+ name: "linux-swap(v0)",
+};
+
+static PedFileSystemType _swap_v1_type = {
+ next: NULL,
+ ops: &_swap_v1_ops,
+ name: "linux-swap(v1)",
+};
+
+static PedFileSystemType _swap_swsusp_type = {
+ next: NULL,
+ ops: &_swap_swsusp_ops,
+ name: "swsusp",
+};
+
+void
+ped_file_system_linux_swap_init ()
+{
+ ped_file_system_type_register (&_swap_v0_type);
+ ped_file_system_type_register (&_swap_v1_type);
+ ped_file_system_type_register (&_swap_swsusp_type);
+
+ ped_file_system_alias_register (&_swap_v0_type, "linux-swap(old)", 1);
+ ped_file_system_alias_register (&_swap_v1_type, "linux-swap(new)", 1);
+ ped_file_system_alias_register (&_swap_v1_type, "linux-swap", 0);
+}
+
+void
+ped_file_system_linux_swap_done ()
+{
+ ped_file_system_alias_unregister (&_swap_v0_type, "linux-swap(old)");
+ ped_file_system_alias_unregister (&_swap_v1_type, "linux-swap(new)");
+ ped_file_system_alias_unregister (&_swap_v1_type, "linux-swap");
+
+ ped_file_system_type_unregister (&_swap_v0_type);
+ ped_file_system_type_unregister (&_swap_v1_type);
+ ped_file_system_type_unregister (&_swap_swsusp_type);
+}