791 lines
23 KiB
Diff
791 lines
23 KiB
Diff
From e744dcb38cc52cbe64977efcdd4bc60e802d1b17 Mon Sep 17 00:00:00 2001
|
||
From: "Richard W.M. Jones" <rjones@redhat.com>
|
||
Date: Thu, 23 Jan 2020 19:52:00 +0000
|
||
Subject: [PATCH 11/19] New filter: extentlist.
|
||
|
||
Allows a list of extents to be placed on top of an existing plugin.
|
||
|
||
(cherry picked from commit 3e770b6d6620a62546849a2863638041c0b00640)
|
||
---
|
||
TODO | 4 +
|
||
configure.ac | 2 +
|
||
.../nbdkit-cacheextents-filter.pod | 1 +
|
||
filters/extentlist/Makefile.am | 67 ++++
|
||
filters/extentlist/extentlist.c | 326 ++++++++++++++++++
|
||
.../extentlist/nbdkit-extentlist-filter.pod | 90 +++++
|
||
filters/noextents/nbdkit-noextents-filter.pod | 1 +
|
||
tests/Makefile.am | 4 +
|
||
tests/test-extentlist.sh | 175 ++++++++++
|
||
9 files changed, 670 insertions(+)
|
||
create mode 100644 filters/extentlist/Makefile.am
|
||
create mode 100644 filters/extentlist/extentlist.c
|
||
create mode 100644 filters/extentlist/nbdkit-extentlist-filter.pod
|
||
create mode 100755 tests/test-extentlist.sh
|
||
|
||
diff --git a/TODO b/TODO
|
||
index d2aca440..2a3e89dc 100644
|
||
--- a/TODO
|
||
+++ b/TODO
|
||
@@ -187,6 +187,10 @@ Suggestions for filters
|
||
MBs of extra data)
|
||
https://github.com/facebook/zstd/issues/395#issuecomment-535875379
|
||
|
||
+* nbdkit-extentlist-filter could read the extents generated by
|
||
+ qemu-img map, allowing extents to be ported from a qemu block
|
||
+ device.
|
||
+
|
||
nbdkit-rate-filter:
|
||
|
||
* allow other kinds of traffic shaping such as VBR
|
||
diff --git a/configure.ac b/configure.ac
|
||
index fde498b8..41e68de3 100644
|
||
--- a/configure.ac
|
||
+++ b/configure.ac
|
||
@@ -896,6 +896,7 @@ filters="\
|
||
cow \
|
||
delay \
|
||
error \
|
||
+ extentlist \
|
||
fua \
|
||
log \
|
||
nocache \
|
||
@@ -979,6 +980,7 @@ AC_CONFIG_FILES([Makefile
|
||
filters/cow/Makefile
|
||
filters/delay/Makefile
|
||
filters/error/Makefile
|
||
+ filters/extentlist/Makefile
|
||
filters/fua/Makefile
|
||
filters/log/Makefile
|
||
filters/nocache/Makefile
|
||
diff --git a/filters/cacheextents/nbdkit-cacheextents-filter.pod b/filters/cacheextents/nbdkit-cacheextents-filter.pod
|
||
index fdd2285a..bb2514a4 100644
|
||
--- a/filters/cacheextents/nbdkit-cacheextents-filter.pod
|
||
+++ b/filters/cacheextents/nbdkit-cacheextents-filter.pod
|
||
@@ -52,6 +52,7 @@ C<nbdkit-cacheextents-filter> first appeared in nbdkit 1.14.
|
||
|
||
L<nbdkit(1)>,
|
||
L<nbdkit-cache-filter(1)>,
|
||
+L<nbdkit-extentlist-filter(1)>,
|
||
L<nbdkit-readahead-filter(1)>,
|
||
L<nbdkit-vddk-plugin(1)>,
|
||
L<nbdkit-filter(3)>,
|
||
diff --git a/filters/extentlist/Makefile.am b/filters/extentlist/Makefile.am
|
||
new file mode 100644
|
||
index 00000000..88a9afe1
|
||
--- /dev/null
|
||
+++ b/filters/extentlist/Makefile.am
|
||
@@ -0,0 +1,67 @@
|
||
+# nbdkit
|
||
+# Copyright (C) 2019-2020 Red Hat Inc.
|
||
+#
|
||
+# 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-extentlist-filter.pod
|
||
+
|
||
+filter_LTLIBRARIES = nbdkit-extentlist-filter.la
|
||
+
|
||
+nbdkit_extentlist_filter_la_SOURCES = \
|
||
+ extentlist.c \
|
||
+ $(top_srcdir)/include/nbdkit-filter.h \
|
||
+ $(NULL)
|
||
+
|
||
+nbdkit_extentlist_filter_la_CPPFLAGS = \
|
||
+ -I$(top_srcdir)/include \
|
||
+ -I$(top_srcdir)/common/include \
|
||
+ -I$(top_srcdir)/common/utils \
|
||
+ $(NULL)
|
||
+nbdkit_extentlist_filter_la_CFLAGS = $(WARNINGS_CFLAGS)
|
||
+nbdkit_extentlist_filter_la_LDFLAGS = \
|
||
+ -module -avoid-version -shared \
|
||
+ -Wl,--version-script=$(top_srcdir)/filters/filters.syms \
|
||
+ $(NULL)
|
||
+nbdkit_extentlist_filter_la_LIBADD = \
|
||
+ $(top_builddir)/common/utils/libutils.la \
|
||
+ $(NULL)
|
||
+
|
||
+if HAVE_POD
|
||
+
|
||
+man_MANS = nbdkit-extentlist-filter.1
|
||
+CLEANFILES += $(man_MANS)
|
||
+
|
||
+nbdkit-extentlist-filter.1: nbdkit-extentlist-filter.pod
|
||
+ $(PODWRAPPER) --section=1 --man $@ \
|
||
+ --html $(top_builddir)/html/$@.html \
|
||
+ $<
|
||
+
|
||
+endif HAVE_POD
|
||
diff --git a/filters/extentlist/extentlist.c b/filters/extentlist/extentlist.c
|
||
new file mode 100644
|
||
index 00000000..5f4990b3
|
||
--- /dev/null
|
||
+++ b/filters/extentlist/extentlist.c
|
||
@@ -0,0 +1,326 @@
|
||
+/* nbdkit
|
||
+ * Copyright (C) 2019-2020 Red Hat Inc.
|
||
+ *
|
||
+ * 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 <inttypes.h>
|
||
+#include <string.h>
|
||
+#include <errno.h>
|
||
+#include <assert.h>
|
||
+
|
||
+#include <nbdkit-filter.h>
|
||
+
|
||
+#include "cleanup.h"
|
||
+#include "minmax.h"
|
||
+
|
||
+#define HOLE (NBDKIT_EXTENT_HOLE|NBDKIT_EXTENT_ZERO)
|
||
+
|
||
+static const char *extentlist;
|
||
+
|
||
+/* List of extents. Once we've finally parsed them this will be
|
||
+ * ordered, non-overlapping and have no gaps.
|
||
+ */
|
||
+struct extent {
|
||
+ uint64_t offset, length;
|
||
+ uint32_t type;
|
||
+};
|
||
+static struct extent *extents;
|
||
+static size_t nr_extents, allocated;
|
||
+
|
||
+/* Insert an extent before i. If i = nr_extents, inserts at the end. */
|
||
+static void
|
||
+insert_extent (size_t i, struct extent new_extent)
|
||
+{
|
||
+ if (nr_extents >= allocated) {
|
||
+ allocated = allocated == 0 ? 1 : allocated * 2;
|
||
+ extents = realloc (extents, (sizeof (struct extent) * allocated));
|
||
+ if (extents == NULL) {
|
||
+ nbdkit_error ("realloc: %m");
|
||
+ exit (EXIT_FAILURE);
|
||
+ }
|
||
+ }
|
||
+ memmove (&extents[i+1], &extents[i],
|
||
+ sizeof (struct extent) * (nr_extents-i));
|
||
+ extents[i] = new_extent;
|
||
+ nr_extents++;
|
||
+}
|
||
+
|
||
+static void
|
||
+extentlist_unload (void)
|
||
+{
|
||
+ free (extents);
|
||
+}
|
||
+
|
||
+/* Called for each key=value passed on the command line. */
|
||
+static int
|
||
+extentlist_config (nbdkit_next_config *next, void *nxdata,
|
||
+ const char *key, const char *value)
|
||
+{
|
||
+ if (strcmp (key, "extentlist") == 0) {
|
||
+ if (extentlist != NULL) {
|
||
+ nbdkit_error ("extentlist cannot appear twice");
|
||
+ exit (EXIT_FAILURE);
|
||
+ }
|
||
+ extentlist = value;
|
||
+ return 0;
|
||
+ }
|
||
+ else
|
||
+ return next (nxdata, key, value);
|
||
+}
|
||
+
|
||
+static int
|
||
+compare_offsets (const void *ev1, const void *ev2)
|
||
+{
|
||
+ const struct extent *e1 = ev1;
|
||
+ const struct extent *e2 = ev2;
|
||
+
|
||
+ if (e1->offset < e2->offset)
|
||
+ return -1;
|
||
+ else if (e1->offset > e2->offset)
|
||
+ return 1;
|
||
+ else
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int
|
||
+compare_ranges (const void *ev1, const void *ev2)
|
||
+{
|
||
+ const struct extent *e1 = ev1;
|
||
+ const struct extent *e2 = ev2;
|
||
+
|
||
+ if (e1->offset < e2->offset)
|
||
+ return -1;
|
||
+ else if (e1->offset >= e2->offset + e2->length)
|
||
+ return 1;
|
||
+ else
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+/* Similar to parse_extents in plugins/sh/methods.c */
|
||
+static void
|
||
+parse_extentlist (void)
|
||
+{
|
||
+ FILE *fp;
|
||
+ CLEANUP_FREE char *line = NULL;
|
||
+ size_t linelen = 0;
|
||
+ ssize_t len;
|
||
+ size_t i;
|
||
+ uint64_t end;
|
||
+
|
||
+ assert (extentlist != NULL);
|
||
+ assert (extents == NULL);
|
||
+ assert (nr_extents == 0);
|
||
+
|
||
+ fp = fopen (extentlist, "r");
|
||
+ if (!fp) {
|
||
+ nbdkit_error ("open: %s: %m", extentlist);
|
||
+ exit (EXIT_FAILURE);
|
||
+ }
|
||
+
|
||
+ while ((len = getline (&line, &linelen, fp)) != -1) {
|
||
+ const char *delim = " \t";
|
||
+ char *sp, *p;
|
||
+ int64_t offset, length;
|
||
+ uint32_t type;
|
||
+
|
||
+ if (len > 0 && line[len-1] == '\n') {
|
||
+ line[len-1] = '\0';
|
||
+ len--;
|
||
+ }
|
||
+
|
||
+ if ((p = strtok_r (line, delim, &sp)) == NULL) {
|
||
+ parse_error:
|
||
+ nbdkit_error ("%s: cannot parse %s", extentlist, line);
|
||
+ exit (EXIT_FAILURE);
|
||
+ }
|
||
+ offset = nbdkit_parse_size (p);
|
||
+ if (offset == -1)
|
||
+ exit (EXIT_FAILURE);
|
||
+
|
||
+ if ((p = strtok_r (NULL, delim, &sp)) == NULL)
|
||
+ goto parse_error;
|
||
+ length = nbdkit_parse_size (p);
|
||
+ if (length == -1)
|
||
+ exit (EXIT_FAILURE);
|
||
+
|
||
+ /* Skip zero length extents. Makes the rest of the code easier. */
|
||
+ if (length == 0)
|
||
+ continue;
|
||
+
|
||
+ if ((p = strtok_r (NULL, delim, &sp)) == NULL)
|
||
+ /* empty type field means allocated data (0) */
|
||
+ type = 0;
|
||
+ else if (sscanf (p, "%" SCNu32, &type) == 1)
|
||
+ ;
|
||
+ else {
|
||
+ type = 0;
|
||
+ if (strstr (p, "hole") != NULL)
|
||
+ type |= NBDKIT_EXTENT_HOLE;
|
||
+ if (strstr (p, "zero") != NULL)
|
||
+ type |= NBDKIT_EXTENT_ZERO;
|
||
+ }
|
||
+
|
||
+ insert_extent (nr_extents,
|
||
+ (struct extent){.offset = offset, .length=length,
|
||
+ .type=type});
|
||
+ }
|
||
+
|
||
+ fclose (fp);
|
||
+
|
||
+ /* Sort the extents by offset. */
|
||
+ qsort (extents, nr_extents, sizeof (struct extent), compare_offsets);
|
||
+
|
||
+ /* There must not be overlaps at this point. */
|
||
+ end = 0;
|
||
+ for (i = 0; i < nr_extents; ++i) {
|
||
+ if (extents[i].offset < end ||
|
||
+ extents[i].offset + extents[i].length < extents[i].offset) {
|
||
+ nbdkit_error ("extents in the extent list are overlapping");
|
||
+ exit (EXIT_FAILURE);
|
||
+ }
|
||
+ end = extents[i].offset + extents[i].length;
|
||
+ }
|
||
+
|
||
+ /* If there's a gap at the beginning, insert a hole|zero extent. */
|
||
+ if (nr_extents == 0 || extents[0].offset > 0) {
|
||
+ end = nr_extents == 0 ? UINT64_MAX : extents[0].offset;
|
||
+ insert_extent (0, (struct extent){.offset = 0, .length = end,
|
||
+ .type = HOLE});
|
||
+ }
|
||
+
|
||
+ /* Now insert hole|zero extents after every extent where there
|
||
+ * is a gap between that extent and the next one.
|
||
+ */
|
||
+ for (i = 0; i < nr_extents-1; ++i) {
|
||
+ end = extents[i].offset + extents[i].length;
|
||
+ if (end < extents[i+1].offset)
|
||
+ insert_extent (i+1, (struct extent){.offset = end,
|
||
+ .length = extents[i+1].offset - end,
|
||
+ .type = HOLE});
|
||
+ }
|
||
+
|
||
+ /* If there's a gap at the end, insert a hole|zero extent. */
|
||
+ end = extents[nr_extents-1].offset + extents[nr_extents-1].length;
|
||
+ if (end < UINT64_MAX)
|
||
+ insert_extent (nr_extents, (struct extent){.offset = end,
|
||
+ .length = UINT64_MAX-end,
|
||
+ .type = HOLE});
|
||
+
|
||
+ /* Debug the final list. */
|
||
+ for (i = 0; i < nr_extents; ++i) {
|
||
+ nbdkit_debug ("extentlist: "
|
||
+ "extent[%zu] = %" PRIu64 "-%" PRIu64 " (length %" PRIu64 ")"
|
||
+ " type %" PRIu32,
|
||
+ i, extents[i].offset,
|
||
+ extents[i].offset + extents[i].length - 1,
|
||
+ extents[i].length,
|
||
+ extents[i].type);
|
||
+ }
|
||
+}
|
||
+
|
||
+static int
|
||
+extentlist_config_complete (nbdkit_next_config_complete *next, void *nxdata)
|
||
+{
|
||
+ if (extentlist == NULL) {
|
||
+ nbdkit_error ("you must supply the extentlist parameter "
|
||
+ "on the command line");
|
||
+ return -1;
|
||
+ }
|
||
+
|
||
+ parse_extentlist ();
|
||
+
|
||
+ return next (nxdata);
|
||
+}
|
||
+
|
||
+static int
|
||
+extentlist_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
|
||
+ void *handle)
|
||
+{
|
||
+ return 1;
|
||
+}
|
||
+
|
||
+/* Use ‘-D extentlist.lookup=1’ to debug the function below. */
|
||
+int extentlist_debug_lookup = 0;
|
||
+
|
||
+/* Read extents. */
|
||
+static int
|
||
+extentlist_extents (struct nbdkit_next_ops *next_ops, void *nxdata,
|
||
+ void *handle, uint32_t count, uint64_t offset,
|
||
+ uint32_t flags,
|
||
+ struct nbdkit_extents *ret_extents,
|
||
+ int *err)
|
||
+{
|
||
+ const struct extent eoffset = { .offset = offset };
|
||
+ struct extent *p;
|
||
+ ssize_t i;
|
||
+ uint64_t end;
|
||
+
|
||
+ /* Find the starting point in the extents list. */
|
||
+ p = bsearch (&eoffset, extents,
|
||
+ nr_extents, sizeof (struct extent), compare_ranges);
|
||
+ assert (p != NULL);
|
||
+ i = p - extents;
|
||
+
|
||
+ /* Add extents to the output. */
|
||
+ while (count > 0) {
|
||
+ if (extentlist_debug_lookup)
|
||
+ nbdkit_debug ("extentlist lookup: "
|
||
+ "loop i=%zd count=%" PRIu32 " offset=%" PRIu64,
|
||
+ i, count, offset);
|
||
+
|
||
+ end = extents[i].offset + extents[i].length;
|
||
+ if (nbdkit_add_extent (ret_extents, offset, end - offset,
|
||
+ extents[i].type) == -1)
|
||
+ return -1;
|
||
+
|
||
+ count -= MIN (count, end-offset);
|
||
+ offset = end;
|
||
+ i++;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static struct nbdkit_filter filter = {
|
||
+ .name = "extentlist",
|
||
+ .longname = "nbdkit extentlist filter",
|
||
+ .unload = extentlist_unload,
|
||
+ .config = extentlist_config,
|
||
+ .config_complete = extentlist_config_complete,
|
||
+ .can_extents = extentlist_can_extents,
|
||
+ .extents = extentlist_extents,
|
||
+};
|
||
+
|
||
+NBDKIT_REGISTER_FILTER(filter)
|
||
diff --git a/filters/extentlist/nbdkit-extentlist-filter.pod b/filters/extentlist/nbdkit-extentlist-filter.pod
|
||
new file mode 100644
|
||
index 00000000..adfb4ad8
|
||
--- /dev/null
|
||
+++ b/filters/extentlist/nbdkit-extentlist-filter.pod
|
||
@@ -0,0 +1,90 @@
|
||
+=head1 NAME
|
||
+
|
||
+nbdkit-extentlist-filter - place extent list over a plugin
|
||
+
|
||
+=head1 SYNOPSIS
|
||
+
|
||
+ nbdkit --filter=extentlist plugin extentlist=FILENAME
|
||
+
|
||
+=head1 DESCRIPTION
|
||
+
|
||
+C<nbdkit-extentlist-filter> is an nbdkit filter lets you place a
|
||
+static list of extents on top of an existing plugin. Extents record
|
||
+whether or not specific parts of the disk are allocated or sparse.
|
||
+
|
||
+You can use this with plugins which cannot get extent information
|
||
+themselves, but you can get this information from another source. One
|
||
+place where it is useful is with L<nbdkit-ssh-plugin(1)> because the
|
||
+sftp protocol does not support reading sparseness information, but you
|
||
+may be able to get this information directly from the source disk on
|
||
+the remote server.
|
||
+
|
||
+=head1 FILE FORMAT
|
||
+
|
||
+The list of extents is specified in a text file. There is one extent
|
||
+specified per line. Each line has the format:
|
||
+
|
||
+ offset length type
|
||
+
|
||
+The C<offset> and C<length> fields may use any format understood by
|
||
+C<nbdkit_parse_size>. The optional C<type> field may be an integer,
|
||
+missing (same as 0), or a comma-separated list of the words C<hole>
|
||
+and C<zero>. (The fields correspond to the inputs of the
|
||
+C<nbdkit_add_extent> function, see L<nbdkit-plugin(3)>).
|
||
+
|
||
+An example of a valid set of extents covering a C<10M> disk where the
|
||
+first megabyte only is allocated data:
|
||
+
|
||
+ 0 1M
|
||
+ 1M 9M hole,zero
|
||
+
|
||
+Or you could omit the C<hole,zero> extent since any gaps are assumed
|
||
+to be holes with that type:
|
||
+
|
||
+ 0 1M
|
||
+
|
||
+The extent list need not cover the whole disk, and does not need to be
|
||
+in ascending order, but it must I<not> contain overlapping extents.
|
||
+
|
||
+=head1 PARAMETERS
|
||
+
|
||
+=over 4
|
||
+
|
||
+=item B<extentlist=>FILENAME
|
||
+
|
||
+Specify the file containing the extent list, in the format described
|
||
+in L</FILE FORMAT> above.
|
||
+
|
||
+=back
|
||
+
|
||
+=head1 FILES
|
||
+
|
||
+=over 4
|
||
+
|
||
+=item F<$filterdir/nbdkit-extentlist-filter.so>
|
||
+
|
||
+The filter.
|
||
+
|
||
+Use C<nbdkit --dump-config> to find the location of C<$filterdir>.
|
||
+
|
||
+=back
|
||
+
|
||
+=head1 VERSION
|
||
+
|
||
+C<nbdkit-extentlist-filter> first appeared in nbdkit 1.18.
|
||
+
|
||
+=head1 SEE ALSO
|
||
+
|
||
+L<nbdkit(1)>,
|
||
+L<nbdkit-cacheextents-filter(1)>,
|
||
+L<nbdkit-noextents-filter(1)>,
|
||
+L<nbdkit-filter(3)>,
|
||
+L<nbdkit-plugin(3)>.
|
||
+
|
||
+=head1 AUTHORS
|
||
+
|
||
+Richard W.M. Jones
|
||
+
|
||
+=head1 COPYRIGHT
|
||
+
|
||
+Copyright (C) 2020 Red Hat Inc.
|
||
diff --git a/filters/noextents/nbdkit-noextents-filter.pod b/filters/noextents/nbdkit-noextents-filter.pod
|
||
index 991ecfe8..0260a5cf 100644
|
||
--- a/filters/noextents/nbdkit-noextents-filter.pod
|
||
+++ b/filters/noextents/nbdkit-noextents-filter.pod
|
||
@@ -47,6 +47,7 @@ C<nbdkit-noextents-filter> first appeared in nbdkit 1.14.
|
||
|
||
L<nbdkit(1)>,
|
||
L<nbdkit-filter(3)>,
|
||
+L<nbdkit-extentlist-filter(1)>,
|
||
L<nbdkit-fua-filter(1)>,
|
||
L<nbdkit-nocache-filter(1)>,
|
||
L<nbdkit-noparallel-filter(1)>,
|
||
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
||
index 09103fbb..b99952f4 100644
|
||
--- a/tests/Makefile.am
|
||
+++ b/tests/Makefile.am
|
||
@@ -110,6 +110,7 @@ EXTRA_DIST = \
|
||
test-error100.sh \
|
||
test-error-triggered.sh \
|
||
test-export-name.sh \
|
||
+ test-extentlist.sh \
|
||
test-file-extents.sh \
|
||
test-floppy.sh \
|
||
test-foreground.sh \
|
||
@@ -1009,6 +1010,9 @@ TESTS += \
|
||
test-error-triggered.sh \
|
||
$(NULL)
|
||
|
||
+# extentlist filter test.
|
||
+TESTS += test-extentlist.sh
|
||
+
|
||
# fua filter test.
|
||
TESTS += test-fua.sh
|
||
|
||
diff --git a/tests/test-extentlist.sh b/tests/test-extentlist.sh
|
||
new file mode 100755
|
||
index 00000000..7d05de4f
|
||
--- /dev/null
|
||
+++ b/tests/test-extentlist.sh
|
||
@@ -0,0 +1,175 @@
|
||
+#!/usr/bin/env bash
|
||
+# nbdkit
|
||
+# Copyright (C) 2016-2020 Red Hat Inc.
|
||
+#
|
||
+# 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.
|
||
+
|
||
+# Test the extentlist filter.
|
||
+
|
||
+source ./functions.sh
|
||
+set -e
|
||
+set -x
|
||
+
|
||
+requires jq --version
|
||
+requires qemu-img --version
|
||
+requires qemu-img map --help
|
||
+
|
||
+out=test-extentlist.out
|
||
+input=test-extentlist.in
|
||
+expected=test-extentlist.expected
|
||
+files="$out $input $expected"
|
||
+rm -f $files
|
||
+cleanup_fn rm $files
|
||
+
|
||
+test ()
|
||
+{
|
||
+ nbdkit -v -D extentlist.lookup=1 \
|
||
+ -U - \
|
||
+ --filter=extentlist \
|
||
+ null size=$1 extentlist=$input \
|
||
+ --run 'qemu-img map -f raw --output=json $nbd' |
|
||
+ jq -c '.[] | {start:.start, length:.length, data:.data, zero:.zero}' \
|
||
+ > $out
|
||
+ diff -u $out $expected
|
||
+}
|
||
+
|
||
+# Empty extent list.
|
||
+cat > $input <<'EOF'
|
||
+EOF
|
||
+
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":0,"data":false,"zero":false}
|
||
+EOF
|
||
+test 0
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 1M
|
||
+
|
||
+# Extent list covering 0-1M with data.
|
||
+cat > $input <<'EOF'
|
||
+0 1M
|
||
+EOF
|
||
+
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":0,"data":false,"zero":false}
|
||
+EOF
|
||
+test 0
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":true,"zero":false}
|
||
+EOF
|
||
+test 1M
|
||
+
|
||
+# Extent list covering 1-2M with data.
|
||
+cat > $input <<'EOF'
|
||
+1M 1M
|
||
+EOF
|
||
+
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":0,"data":false,"zero":false}
|
||
+EOF
|
||
+test 0
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 1M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+{"start":1048576,"length":1048576,"data":true,"zero":false}
|
||
+EOF
|
||
+test 2M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+{"start":1048576,"length":1048576,"data":true,"zero":false}
|
||
+{"start":2097152,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 3M
|
||
+
|
||
+# Extent list covering 1-2M with data, but in a more fragmented
|
||
+# way than the above.
|
||
+cat > $input <<'EOF'
|
||
+1024K 512K
|
||
+1536K 512K
|
||
+EOF
|
||
+
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":0,"data":false,"zero":false}
|
||
+EOF
|
||
+test 0
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 1M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+{"start":1048576,"length":1048576,"data":true,"zero":false}
|
||
+EOF
|
||
+test 2M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":false,"zero":true}
|
||
+{"start":1048576,"length":1048576,"data":true,"zero":false}
|
||
+{"start":2097152,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 3M
|
||
+
|
||
+# Adjacent data and holes.
|
||
+cat > $input <<'EOF'
|
||
+0 1M
|
||
+2M 1M
|
||
+4M 1M
|
||
+EOF
|
||
+
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":0,"data":false,"zero":false}
|
||
+EOF
|
||
+test 0
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":true,"zero":false}
|
||
+EOF
|
||
+test 1M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":true,"zero":false}
|
||
+{"start":1048576,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 2M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":true,"zero":false}
|
||
+{"start":1048576,"length":1048576,"data":false,"zero":true}
|
||
+{"start":2097152,"length":1048576,"data":true,"zero":false}
|
||
+EOF
|
||
+test 3M
|
||
+cat > $expected <<'EOF'
|
||
+{"start":0,"length":1048576,"data":true,"zero":false}
|
||
+{"start":1048576,"length":1048576,"data":false,"zero":true}
|
||
+{"start":2097152,"length":1048576,"data":true,"zero":false}
|
||
+{"start":3145728,"length":1048576,"data":false,"zero":true}
|
||
+{"start":4194304,"length":1048576,"data":true,"zero":false}
|
||
+{"start":5242880,"length":1048576,"data":false,"zero":true}
|
||
+EOF
|
||
+test 6M
|
||
--
|
||
2.18.2
|
||
|