[Libguestfs] [PATCH 3/3] New APIs: bmap-file, bmap-device, bmap.

Richard W.M. Jones
2014-11-23T22:16:41Z

Add support for a block mapping API, used by the 'virt-bmap' tool.
---
 daemon/Makefile.am   |   1 +
 daemon/bmap.c        | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++
 generator/actions.ml |  44 ++++++++++++++
 po/POTFILES          |   1 +
 src/MAX_PROC_NR      |   2 +-
 5 files changed, 215 insertions(+), 1 deletion(-)
 create mode 100644 daemon/bmap.c

diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 8ccf322..b6444fa 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -86,6 +86,7 @@ guestfsd_SOURCES = \
 	blkdiscard.c \
 	blkid.c \
 	blockdev.c \
+	bmap.c \
 	btrfs.c \
 	cap.c \
 	checksum.c \
diff --git a/daemon/bmap.c b/daemon/bmap.c
new file mode 100644
index 0000000..555f776
--- /dev/null
+++ b/daemon/bmap.c
@@ -0,0 +1,168 @@
+/* libguestfs - the guestfsd daemon
+ * Copyright (C) 2014 Red Hat 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 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 <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "daemon.h"
+#include "actions.h"
+
+static int fd = -1;
+static DIR *dir = NULL;
+static struct stat statbuf;
+
+static void bmap_finalize (void) __attribute__((destructor));
+static void
+bmap_finalize (void)
+{
+  if (fd >= 0) {
+    close (fd);
+    fd = -1;
+  }
+  if (dir != NULL) {
+    closedir (dir);
+    dir = NULL;
+  }
+}
+
+static int
+bmap_prepare (const char *path, const char *orig_path)
+{
+  bmap_finalize ();
+
+  if (stat (path, &statbuf) == -1) {
+    reply_with_perror ("%s", orig_path);
+    return -1;
+  }
+
+  if (S_ISDIR (statbuf.st_mode)) {
+    /* Open a directory. */
+    dir = opendir (path);
+    if (dir == NULL) {
+      reply_with_perror ("opendir: %s", orig_path);
+      return -1;
+    }
+  }
+  else {
+    /* Open a regular file. */
+    fd = open (path, O_RDONLY | O_CLOEXEC);
+    if (fd == -1) {
+      reply_with_perror ("%s", orig_path);
+      return -1;
+    }
+
+    posix_fadvise (fd, 0, 0,
+                   POSIX_FADV_SEQUENTIAL |
+                   POSIX_FADV_NOREUSE |
+                   POSIX_FADV_DONTNEED);
+  }
+
+  return 0;
+}
+
+int
+do_bmap_file (const char *path)
+{
+  CLEANUP_FREE char *buf = NULL;
+
+  buf = sysroot_path (path);
+  if (!buf) {
+    reply_with_perror ("malloc");
+    return -1;
+  }
+
+  return bmap_prepare (buf, path);
+}
+
+int
+do_bmap_device (const char *device)
+{
+  return bmap_prepare (device, device);
+}
+
+static char buffer[BUFSIZ];
+
+int
+do_bmap (void)
+{
+  size_t n;
+  ssize_t r;
+  struct dirent *d;
+
+  /* Drop caches before starting the read. */
+  if (do_drop_caches (3) == -1)
+    return -1;
+
+  if (fd >= 0) {
+    n = statbuf.st_size;
+
+    while (n > 0) {
+      r = read (fd, buffer, n > BUFSIZ ? BUFSIZ : n);
+      if (r == -1) {
+        reply_with_perror ("read");
+        close (fd);
+        fd = -1;
+        return -1;
+      }
+      n -= r;
+    }
+
+    if (close (fd) == -1) {
+      reply_with_perror ("close");
+      fd = -1;
+      return -1;
+    }
+    fd = -1;
+  }
+
+  if (dir != NULL) {
+    for (;;) {
+      errno = 0;
+      d = readdir (dir);
+      if (!d) break;
+    }
+
+    /* Check readdir didn't fail */
+    if (errno != 0) {
+      reply_with_perror ("readdir");
+      closedir (dir);
+      dir = NULL;
+      return -1;
+    }
+
+    /* Close the directory handle */
+    if (closedir (dir) == -1) {
+      reply_with_perror ("closedir");
+      dir = NULL;
+      return -1;
+    }
+    dir = NULL;
+  }
+
+  return 0;
+}
diff --git a/generator/actions.ml b/generator/actions.ml
index baa7679..0f2e040 100644
--- a/generator/actions.ml
+++ b/generator/actions.ml
@@ -12017,6 +12017,50 @@ Set readahead (in 512-byte sectors) for the device.

 This uses the L<blockdev(8)> command." };

+  { defaults with
+    name = "bmap_file";
+    style = RErr, [Pathname "path"], [];
+    proc_nr = Some 425;
+    shortdesc = "prepare a file for block mapping";
+    longdesc = "\
+This is a specialized function used by L<virt-bmap(1)> to map which
+blocks are used by a file.  It is unlikely to be useful outside
+virt-bmap.
+
+This call prepares C<path> (which may be a regular file or directory)
+to be mapped.  You have to call C<guestfs_bmap> immediately
afterwards." };
+
+  { defaults with
+    name = "bmap_device";
+    style = RErr, [Device "device"], [];
+    proc_nr = Some 426;
+    shortdesc = "prepare a device for block mapping";
+    longdesc = "\
+This is a specialized function used by L<virt-bmap(1)> to map which
+blocks are used by a device.  It is unlikely to be useful outside
+virt-bmap.
+
+This call prepares C<device> to be mapped.  You have to call
C<guestfs_bmap>
+immediately afterwards." };
+
+  { defaults with
+    name = "bmap";
+    style = RErr, [], [];
+    proc_nr = Some 427;
+    shortdesc = "block map file or device";
+    longdesc = "\
+This is a specialized function used by L<virt-bmap(1)> to map which
+blocks are used by a file or device.  It is unlikely to be useful outside
+virt-bmap.
+
+This performs the specialized read of the file or device which was
+previously prepared (see C<guestfs_bmap_file> and
C<guestfs_bmap_device>).
+The file/directory/device is read from start to end without any caching
+or readahead.
+
+L<virt-bmap(1)> uses this call in conjunction with an nbdkit plugin
+which monitors which disk blocks are read during the operation." };
+
 ]

 (* Non-API meta-commands available only in guestfish.
diff --git a/po/POTFILES b/po/POTFILES
index 1a088f5..b13cb86 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -22,6 +22,7 @@ daemon/base64.c
 daemon/blkdiscard.c
 daemon/blkid.c
 daemon/blockdev.c
+daemon/bmap.c
 daemon/btrfs.c
 daemon/cap.c
 daemon/checksum.c
diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR
index 5721413..d2a1e59 100644
--- a/src/MAX_PROC_NR
+++ b/src/MAX_PROC_NR
@@ -1 +1 @@
-423
+427
--
2.1.0