[Libguestfs] [PATCHv4 1/2] New API: vfs_minimum_size

Maxim Perevedentsev
Tue, 20 Oct 2015 15:45:55 -0000

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 4ea3c88..0a01a24 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 508691a..3b0266d 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 0000000..652eb0e
--- /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 <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#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 1ead159..7a74c9b 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 274ef3f..e0459c0 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<resize2fs(8)>." };

+  { 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<ntfsresize(8)>." };
+
 ]

 (* Non-API meta-commands available only in guestfish.
diff --git a/po/POTFILES b/po/POTFILES
index cd2c437..d90772a 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 de2a00c..c92ddb6 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-457
+458
--
1.8.3.1