From b8d4ca686b8ed6ef0863f317a5a80995c87aface Mon Sep 17 00:00:00 2001 From: Maxim Perevedentsev Date: Tue, 20 Oct 2015 18:45:55 +0300 Subject: [PATCH] New API: vfs_minimum_size This call provides the way to get minimum size of filesystem. This is needed for shrinking. The return units are bytes. --- daemon/Makefile.am | 1 + daemon/daemon.h | 1 + daemon/fs-min-size.c | 46 +++++++++++++++++++++++ daemon/ntfs.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ generator/actions.ml | 19 ++++++++++ po/POTFILES | 1 + src/MAX_PROC_NR | 2 +- 7 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 daemon/fs-min-size.c diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 4ea3c8834..0a01a241b 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -116,6 +116,7 @@ guestfsd_SOURCES = \ findfs.c \ fill.c \ find.c \ + fs-min-size.c \ fsck.c \ fstrim.c \ glob.c \ diff --git a/daemon/daemon.h b/daemon/daemon.h index 508691a01..3b0266dc9 100644 --- a/daemon/daemon.h +++ b/daemon/daemon.h @@ -283,6 +283,7 @@ extern int btrfs_set_uuid_random (const char *device); /*-- in ntfs.c --*/ extern char *ntfs_get_label (const char *device); extern int ntfs_set_label (const char *device, const char *label); +extern int64_t ntfs_minimum_size (const char *device); /*-- in swap.c --*/ extern int swap_set_uuid (const char *device, const char *uuid); diff --git a/daemon/fs-min-size.c b/daemon/fs-min-size.c new file mode 100644 index 000000000..652eb0ee5 --- /dev/null +++ b/daemon/fs-min-size.c @@ -0,0 +1,46 @@ +/* libguestfs - the guestfsd daemon + * Copyright (C) 2015 Maxim Perevedentsev mperevedentsev@virtuozzo.com + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include +#include + +#include "daemon.h" +#include "actions.h" + +int64_t +do_vfs_minimum_size (const mountable_t *mountable) +{ + int64_t r; + + /* How we set the label depends on the filesystem type. */ + CLEANUP_FREE char *vfs_type = do_vfs_type (mountable); + if (vfs_type == NULL) + return -1; + + else if (STREQ (vfs_type, "ntfs")) + r = ntfs_minimum_size (mountable->device); + + else + NOT_SUPPORTED (-1, "don't know how to get minimum size of '%s' filesystems", + vfs_type); + + return r; +} diff --git a/daemon/ntfs.c b/daemon/ntfs.c index 1ead15922..ea0844f92 100644 --- a/daemon/ntfs.c +++ b/daemon/ntfs.c @@ -27,6 +27,7 @@ #include "daemon.h" #include "actions.h" #include "optgroups.h" +#include "xstrtol.h" #define MAX_ARGS 64 @@ -153,6 +154,92 @@ do_ntfsresize_size (const char *device, int64_t size) return do_ntfsresize (device, size, 0); } +int64_t +ntfs_minimum_size (const char *device) +{ + CLEANUP_FREE char *err = NULL, *out = NULL; + CLEANUP_FREE_STRING_LIST char **lines = NULL; + int r; + size_t i; + int64_t volume_size = 0; + const char *size_pattern = "You might resize at ", + *full_pattern = "Volume is full", + *cluster_size_pattern = "Cluster size", + *volume_size_pattern = "Current volume size:"; + int is_full = 0; + int32_t cluster_size = 0; + + /* FS may be marked for check, so force ntfsresize */ + r = command (&out, &err, str_ntfsresize, "--info", "-ff", device, NULL); + + lines = split_lines (out); + if (lines == NULL) + return -1; + + if (verbose) { + for (i = 0; lines[i] != NULL; ++i) + fprintf (stderr, "ntfs_minimum_size: lines[%zu] = \"%s\"\n", i, lines[i]); + } + +#if __WORDSIZE == 64 +#define XSTRTOD64 xstrtol +#else +#define XSTRTOD64 xstrtoll +#endif + + if (r == -1) { + /* If volume is full, ntfsresize returns error. */ + for (i = 0; lines[i] != NULL; ++i) { + if (strstr (lines[i], full_pattern)) + is_full = 1; + else if (STRPREFIX (lines[i], cluster_size_pattern)) { + if (sscanf (lines[i] + strlen (cluster_size_pattern), + "%*[ ]:%" SCNd32, &cluster_size) != 1) { + reply_with_error ("cannot parse cluster size"); + return -1; + } + } + else if (STRPREFIX (lines[i], volume_size_pattern)) { + if (XSTRTOD64 (lines[i] + strlen (volume_size_pattern), + NULL, 20, &volume_size, NULL) != LONGINT_OK) { + reply_with_error ("cannot parse volume size"); + return -1; + } + } + } + if (is_full) { + if (cluster_size == 0) { + reply_with_error ("bad cluster size"); + return -1; + } + /* In case of a full filesystem, we estimate minimum size + * as volume size rounded up to cluster size. + */ + return (volume_size + cluster_size - 1) / cluster_size * cluster_size; + } + + reply_with_error ("%s", err); + return -1; + } + + for (i = 0; lines[i] != NULL; ++i) { + if (STRPREFIX (lines[i], size_pattern)) { + int64_t ret; + if (XSTRTOD64 (lines[i] + strlen (size_pattern), + NULL, 20, &ret, NULL) != LONGINT_OK) { + reply_with_error ("cannot parse minimum size"); + return -1; + } + return ret; + } + } + +#undef XSTRTOD64 + + reply_with_error ("minimum size not found. Check output format:\n%s", out); + return -1; +} + /* Takes optional arguments, consult optargs_bitmask. */ int do_ntfsfix (const char *device, int clearbadsectors) diff --git a/generator/actions.ml b/generator/actions.ml index 274ef3f11..e0459c067 100644 --- a/generator/actions.ml +++ b/generator/actions.ml @@ -12765,6 +12765,25 @@ Get the estimated minimum filesystem size of an ext2/3/4 filesystem in blocks. See also L." }; + { defaults with + name = "vfs_minimum_size"; added = (1, 31, 18); + style = RInt64 "sizeinbytes", [Mountable "mountable"], []; + proc_nr = Some 458; + tests = [ + InitPartition, IfAvailable "ntfsprogs", TestRun( + [["mkfs"; "ntfs"; "/dev/sda1"; ""; "NOARG"; ""; ""; "NOARG"]; + ["vfs_minimum_size"; "/dev/sda1"]]), []; + ]; + shortdesc = "get minimum filesystem size"; + longdesc = "\ +Get the minimum size of filesystem in bytes. +This is the minimum possible size for filesystem shrinking. + +If getting minimum size of specified filesystem is not supported, +this will fail and set errno as ENOTSUP. + +See also L." }; + ] (* Non-API meta-commands available only in guestfish. diff --git a/po/POTFILES b/po/POTFILES index cd2c437f4..d90772a6c 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -49,6 +49,7 @@ daemon/file.c daemon/fill.c daemon/find.c daemon/findfs.c +daemon/fs-min-size.c daemon/fsck.c daemon/fstrim.c daemon/glob.c diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index de2a00c7d..c92ddb6af 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -457 +458