[Libguestfs] [PATCH nbdkit 1/4] Add truncate filter for truncating or extending the size of plugins.

Richard W.M. Jones rjones at redhat.com
Tue Jul 31 19:55:49 UTC 2018


This can truncate, extend, or round up/down to a multiple.
---
 common-rules.mk                               |   3 +-
 configure.ac                                  |   1 +
 filters/offset/nbdkit-offset-filter.pod       |   7 +-
 filters/partition/nbdkit-partition-filter.pod |   1 +
 filters/truncate/Makefile.am                  |  60 ++++
 filters/truncate/nbdkit-truncate-filter.pod   |  87 ++++++
 filters/truncate/truncate.c                   | 261 ++++++++++++++++++
 7 files changed, 417 insertions(+), 3 deletions(-)

diff --git a/common-rules.mk b/common-rules.mk
index 01beff5..f600293 100644
--- a/common-rules.mk
+++ b/common-rules.mk
@@ -68,7 +68,8 @@ filters = \
 	log \
 	nozero \
 	offset \
-	partition
+	partition \
+	truncate
 
 plugindir = $(libdir)/nbdkit/plugins
 filterdir = $(libdir)/nbdkit/filters
diff --git a/configure.ac b/configure.ac
index b87efb9..e8d0a38 100644
--- a/configure.ac
+++ b/configure.ac
@@ -578,6 +578,7 @@ AC_CONFIG_FILES([Makefile
                  filters/nozero/Makefile
                  filters/offset/Makefile
                  filters/partition/Makefile
+                 filters/truncate/Makefile
                  src/Makefile
                  src/nbdkit.pc
                  tests/Makefile])
diff --git a/filters/offset/nbdkit-offset-filter.pod b/filters/offset/nbdkit-offset-filter.pod
index ee8061b..6d8f9be 100644
--- a/filters/offset/nbdkit-offset-filter.pod
+++ b/filters/offset/nbdkit-offset-filter.pod
@@ -32,7 +32,9 @@ file/device.
 =back
 
 Note it is an error if the offset and/or range specify data which lies
-beyond the end of the underlying device.
+beyond the end of the underlying device.  Use
+L<nbdkit-truncate-filter(1)> to truncate or extend the size of
+plugins.
 
 =head1 EXAMPLES
 
@@ -65,7 +67,8 @@ You can then serve the partition only using:
 L<nbdkit(1)>,
 L<nbdkit-file-plugin(1)>,
 L<nbdkit-filter(3)>,
-L<nbdkit-partition-filter(1)>.
+L<nbdkit-partition-filter(1)>,
+L<nbdkit-truncate-filter(1)>.
 
 =head1 AUTHORS
 
diff --git a/filters/partition/nbdkit-partition-filter.pod b/filters/partition/nbdkit-partition-filter.pod
index bc5f346..71a7a3a 100644
--- a/filters/partition/nbdkit-partition-filter.pod
+++ b/filters/partition/nbdkit-partition-filter.pod
@@ -46,6 +46,7 @@ L<nbdkit(1)>,
 L<nbdkit-file-plugin(1)>,
 L<nbdkit-filter(3)>,
 L<nbdkit-offset-filter(1)>,
+L<nbdkit-truncate-filter(1)>,
 L<parted(8)>.
 
 =head1 AUTHORS
diff --git a/filters/truncate/Makefile.am b/filters/truncate/Makefile.am
new file mode 100644
index 0000000..04ed4de
--- /dev/null
+++ b/filters/truncate/Makefile.am
@@ -0,0 +1,60 @@
+# nbdkit
+# Copyright (C) 2018 Red Hat Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+include $(top_srcdir)/common-rules.mk
+
+EXTRA_DIST = nbdkit-truncate-filter.pod
+
+filter_LTLIBRARIES = nbdkit-truncate-filter.la
+
+nbdkit_truncate_filter_la_SOURCES = \
+	truncate.c \
+	$(top_srcdir)/include/nbdkit-filter.h
+
+nbdkit_truncate_filter_la_CPPFLAGS = \
+	-I$(top_srcdir)/include
+nbdkit_truncate_filter_la_CFLAGS = \
+	$(WARNINGS_CFLAGS)
+nbdkit_truncate_filter_la_LDFLAGS = \
+	-module -avoid-version -shared
+
+if HAVE_POD
+
+man_MANS = nbdkit-truncate-filter.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-truncate-filter.1: nbdkit-truncate-filter.pod
+	$(PODWRAPPER) --section=1 --man $@ \
+	    --html $(top_builddir)/html/$@.html \
+	    $<
+
+endif HAVE_POD
diff --git a/filters/truncate/nbdkit-truncate-filter.pod b/filters/truncate/nbdkit-truncate-filter.pod
new file mode 100644
index 0000000..d52a440
--- /dev/null
+++ b/filters/truncate/nbdkit-truncate-filter.pod
@@ -0,0 +1,87 @@
+=head1 NAME
+
+nbdkit-truncate-filter - change the size of plugins
+
+=head1 SYNOPSIS
+
+ nbdkit --filter=truncate plugin [truncate=SIZE]
+                                 [round-up=N] [round-down=N]
+
+=head1 DESCRIPTION
+
+C<nbdkit-truncate-filter> is a filter that changes the size of
+the underlying plugin.  It can:
+
+=over 4
+
+=item *
+
+Make the plugin smaller (truncate it).  Use the C<truncate=SIZE>
+parameter to set the smaller size.
+
+=item *
+
+Make the plugin larger (the additional bytes read back as zeroes).
+Use C<truncate=SIZE> to set the larger size.
+
+=item *
+
+Round the size of the plugin up or down to the next multiple of C<N>.
+Use either C<round-up=N> or C<round-down=N>.
+
+=back
+
+A common use for this filter is to handle NBD clients which have a
+problem dealing with device sizes which are not a multiple of 512
+bytes.  Use C<round-up=512> to round the size up to the next multiple
+of 512 bytes.  If the size is already a multiple of 512 bytes then
+this has no effect.
+
+=head1 PARAMETERS
+
+=over 4
+
+=item B<truncate=SIZE>
+
+Set the absolute size in bytes of the apparent device.  This may be
+smaller or larger or the same as the underlying plugin.
+
+If the size of larger than the underlying plugin, reading the extra
+space returns zeroes, and any non-zero writes will return an error
+back to the client.
+
+This parameter is optional.
+
+=item B<round-up=N>
+
+Round the size up to the next multiple of C<N> bytes.  If the size of
+the underlying plugin is already a multiple of C<N> bytes, this has no
+effect.
+
+This parameter is optional.
+
+=item B<round-down=N>
+
+Round the size down to a multiple of C<N> bytes.  If the size of the
+underlying plugin is already a multiple of C<N> bytes, this has no
+effect.
+
+This parameter is optional.
+
+=back
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-file-plugin(1)>,
+L<nbdkit-filter(3)>,
+L<nbdkit-offset-filter(1)>,
+L<nbdkit-partition-filter(1)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2018 Red Hat Inc.
diff --git a/filters/truncate/truncate.c b/filters/truncate/truncate.c
new file mode 100644
index 0000000..eccf8cd
--- /dev/null
+++ b/filters/truncate/truncate.c
@@ -0,0 +1,261 @@
+/* nbdkit
+ * Copyright (C) 2018 Red Hat Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#include <nbdkit-filter.h>
+
+#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
+
+/* These are the parameters. */
+static int64_t truncate = -1, round_up = -1, round_down = -1;
+
+/* The real size of the underlying plugin. */
+static int64_t real_size;
+
+/* The calculated size after applying the parameters. */
+static int64_t size;
+
+/* Called for each key=value passed on the command line. */
+static int
+truncate_config (nbdkit_next_config *next, void *nxdata,
+                 const char *key, const char *value)
+{
+  if (strcmp (key, "truncate") == 0) {
+    truncate = nbdkit_parse_size (value);
+    if (truncate == -1)
+      return -1;
+    return 0;
+  }
+  else if (strcmp (key, "round-up") == 0) {
+    round_up = nbdkit_parse_size (value);
+    if (round_up == -1)
+      return -1;
+    if (round_up == 0) {
+      nbdkit_error ("if set, the round-up parameter must be > 0");
+      return -1;
+    }
+    return 0;
+  }
+  else if (strcmp (key, "round-down") == 0) {
+    round_down = nbdkit_parse_size (value);
+    if (round_down == -1)
+      return -1;
+    if (round_down == 0) {
+      nbdkit_error ("if set, the round-down parameter must be > 0");
+      return -1;
+    }
+    return 0;
+  }
+  else
+    return next (nxdata, key, value);
+}
+
+#define truncate_config_help \
+  "truncate=<SIZE>                The new size.\n" \
+  "round-up=<N>                   Round up to next multiple of N.\n" \
+  "round-down=<N>                 Round down to multiple of N."
+
+static int64_t truncate_get_size (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle);
+
+/* In prepare, force a call to get_size which sets the real_size & size
+ * globals.
+ */
+static int
+truncate_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
+                  void *handle)
+{
+  int64_t r;
+
+  r = truncate_get_size (next_ops, nxdata, handle);
+  return r >= 0 ? 0 : -1;
+}
+
+/* Get the size.  As a side effect, calculate the size to serve. */
+static int64_t
+truncate_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
+                   void *handle)
+{
+  real_size = size = next_ops->get_size (nxdata);
+
+  /* The truncate, round-up and round-down parameters are treated as
+   * separate operations.  It's possible to specify more than one,
+   * although perhaps not very useful.
+   */
+  if (truncate >= 0)
+    size = truncate;
+  if (round_up > 0)
+    size = (size + round_up - 1) & ~(round_up-1);
+  if (round_down > 0)
+    size &= ~(round_down-1);
+  return size;
+}
+
+/* Read data. */
+static int
+truncate_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
+                void *handle, void *buf, uint32_t count, uint64_t offset,
+                uint32_t flags, int *err)
+{
+  int r;
+  uint32_t n;
+
+  if (offset < real_size) {
+    if (offset + count <= real_size)
+      n = count;
+    else
+      n = real_size - offset;
+    r = next_ops->pread (nxdata, buf, n, offset, flags, err);
+    if (r == -1)
+      return -1;
+    count -= n;
+    buf += n;
+  }
+
+  if (count > 0)
+    memset (buf, 0, count);
+
+  return 0;
+}
+
+/* Return true iff the buffer is all zero bytes.
+ *
+ * The clever approach here was suggested by Eric Blake.  See:
+ * https://www.redhat.com/archives/libguestfs/2017-April/msg00171.html
+ */
+static inline int
+is_zero (const char *buffer, size_t size)
+{
+  size_t i;
+  const size_t limit = size < 16 ? size : 16;
+
+  for (i = 0; i < limit; ++i)
+    if (buffer[i])
+      return 0;
+  if (size != limit)
+    return !memcmp (buffer, buffer + 16, size - 16);
+
+  return 1;
+}
+
+/* Write data. */
+static int
+truncate_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
+                 void *handle,
+                 const void *buf, uint32_t count, uint64_t offset,
+                 uint32_t flags, int *err)
+{
+  int r;
+  uint32_t n;
+
+  if (offset < real_size) {
+    if (offset + count <= real_size)
+      n = count;
+    else
+      n = real_size - offset;
+    r = next_ops->pwrite (nxdata, buf, n, offset, flags, err);
+    if (r == -1)
+      return -1;
+    count -= n;
+    buf += n;
+  }
+
+  if (count > 0) {
+    /* The caller must be writing zeroes, else it's an error. */
+    if (!is_zero (buf, count)) {
+      nbdkit_error ("truncate: write beyond end of underlying device");
+      *err = EIO;
+      return -1;
+    }
+  }
+
+  return 0;
+}
+
+/* Trim data. */
+static int
+truncate_trim (struct nbdkit_next_ops *next_ops, void *nxdata,
+               void *handle, uint32_t count, uint64_t offset,
+               uint32_t flags, int *err)
+{
+  uint32_t n;
+
+  if (offset < real_size) {
+    if (offset + count <= real_size)
+      n = count;
+    else
+      n = real_size - offset;
+    return next_ops->trim (nxdata, n, offset, flags, err);
+  }
+  return 0;
+}
+
+/* Zero data. */
+static int
+truncate_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
+               void *handle, uint32_t count, uint64_t offset,
+               uint32_t flags, int *err)
+{
+  uint32_t n;
+
+  if (offset < real_size) {
+    if (offset + count <= real_size)
+      n = count;
+    else
+      n = real_size - offset;
+    return next_ops->zero (nxdata, n, offset, flags, err);
+  }
+  return 0;
+}
+
+static struct nbdkit_filter filter = {
+  .name              = "truncate",
+  .longname          = "nbdkit truncate filter",
+  .version           = PACKAGE_VERSION,
+  .config            = truncate_config,
+  .config_help       = truncate_config_help,
+  .prepare           = truncate_prepare,
+  .get_size          = truncate_get_size,
+  .pread             = truncate_pread,
+  .pwrite            = truncate_pwrite,
+  .trim              = truncate_trim,
+  .zero              = truncate_zero,
+};
+
+NBDKIT_REGISTER_FILTER(filter)
-- 
2.18.0




More information about the Libguestfs mailing list