[PATCH nbdkit] vddk: Suppress errors in can_extents test (RHBZ#1709211).
by Richard W.M. Jones
In can_extents we test whether the VDDK function
VixDiskLib_QueryAllocatedBlocks works before declaring that extents
are supported. This VDDK function can fail in several ways but most
notable if the server doesn't support querying extents.
In this case VDDK raises an error message through the error_function
callback, which we turn into a call to nbdkit_error. In most cases
this will cause something to be printed to stderr and/or logged
noisily to syslog, which is not desirable.
This commit adds a per-thread error suppression mechanism so that we
can temporarily hide errors, especially while doing this test, but I
guess it might be useful in future in other cases too.
Note this only affects a narrow range of versions of VMware ESXi and
VDDK. In particular to see this problem you would need
VMware ESXi (server) <= 5.5 and VDDK (client) >= 6.7.
---
plugins/vddk/vddk.c | 80 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 64 insertions(+), 16 deletions(-)
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
index 641c4d6d..94b3cbc9 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -43,8 +43,9 @@
#include <fcntl.h>
#include <libgen.h>
+#include <pthread.h>
+
#define NBDKIT_API_VERSION 2
-
#include <nbdkit-plugin.h>
#include "cleanup.h"
@@ -76,6 +77,7 @@ int vddk_debug_datapath = 1;
static void *dl; /* dlopen handle */
static bool init_called; /* was InitEx called */
static char *reexeced; /* orig LD_LIBRARY_PATH on reexec */
+static pthread_key_t error_suppression; /* threadlocal error suppression */
static char *config; /* config */
static const char *cookie; /* cookie */
@@ -108,6 +110,35 @@ static bool is_remote;
if (vddk_debug_datapath) \
nbdkit_debug ("VDDK call: %s (" fs ")", fn, ##__VA_ARGS__)
+/* Load the plugin. */
+static void
+vddk_load (void)
+{
+ int err;
+
+ err = pthread_key_create (&error_suppression, NULL);
+ if (err != 0) {
+ nbdkit_error ("vddk: pthread_key_create: %s\n", strerror (err));
+ exit (EXIT_FAILURE);
+ }
+}
+
+/* Unload the plugin. */
+static void
+vddk_unload (void)
+{
+ if (init_called) {
+ DEBUG_CALL ("VixDiskLib_Exit", "");
+ VixDiskLib_Exit ();
+ }
+ if (dl)
+ dlclose (dl);
+ free (config);
+ free (libdir);
+ free (password);
+ pthread_key_delete (error_suppression);
+}
+
static void
trim (char *str)
{
@@ -133,12 +164,37 @@ debug_function (const char *fs, va_list args)
nbdkit_debug ("%s", str);
}
+/* If the thread-local error_suppression pointer is non-NULL (we don't
+ * care about the actual value) then we will suppress error messages
+ * from VDDK in this thread.
+ */
+static void
+set_error_suppression (void)
+{
+ static const int one = 1; /* Something that gives us a non-NULL pointer. */
+ pthread_setspecific (error_suppression, &one);
+}
+
+static void
+clear_error_suppression (void)
+{
+ pthread_setspecific (error_suppression, NULL);
+}
+
+static bool
+are_errors_suppressed (void)
+{
+ return pthread_getspecific (error_suppression) != NULL;
+}
+
/* Turn error messages from the library into nbdkit_error. */
static void
error_function (const char *fs, va_list args)
{
CLEANUP_FREE char *str = NULL;
+ if (are_errors_suppressed ()) return;
+
if (vasprintf (&str, fs, args) == -1) {
nbdkit_error ("lost error message: %s", fs);
return;
@@ -149,21 +205,6 @@ error_function (const char *fs, va_list args)
nbdkit_error ("%s", str);
}
-/* Unload the plugin. */
-static void
-vddk_unload (void)
-{
- if (init_called) {
- DEBUG_CALL ("VixDiskLib_Exit", "");
- VixDiskLib_Exit ();
- }
- if (dl)
- dlclose (dl);
- free (config);
- free (libdir);
- free (password);
-}
-
/* Configuration. */
static int
vddk_config (const char *key, const char *value)
@@ -878,6 +919,11 @@ vddk_can_extents (void *handle)
return 0;
}
+ /* Suppress errors around this call. See:
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1709211#c7
+ */
+ set_error_suppression ();
+
/* However even when the call is available it rarely works well so
* the best thing we can do here is to try the call and if it's
* non-functional return false.
@@ -889,6 +935,7 @@ vddk_can_extents (void *handle)
0, VIXDISKLIB_MIN_CHUNK_SIZE,
VIXDISKLIB_MIN_CHUNK_SIZE,
&block_list);
+ clear_error_suppression ();
if (err == VIX_OK) {
DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
VixDiskLib_FreeBlockList (block_list);
@@ -1027,6 +1074,7 @@ static struct nbdkit_plugin plugin = {
.name = "vddk",
.longname = "VMware VDDK plugin",
.version = PACKAGE_VERSION,
+ .load = vddk_load,
.unload = vddk_unload,
.config = vddk_config,
.config_complete = vddk_config_complete,
--
2.25.0
4 years, 6 months
[PATCH v2 0/2] add FreeIPA offline unenrollment (RHBZ#1789592)
by Pino Toscano
This patch series adds a new virt-sysprep operation to offline unenroll
a guest from FreeIPA. It does so by removing some configuration files
and certificates.
Changes from v1:
- the other patches were pushed, as unrelated and approved
- created a new kerberos-hostkeytab operation
Pino Toscano (2):
sysprep: add IPA offline unenrollment (RHBZ#1789592)
sysprep: add Kerberos keytab file removal
sysprep/Makefile.am | 2 +
.../sysprep_operation_kerberos_hostkeytab.ml | 38 +++++++++++
sysprep/sysprep_operation_unenroll_ipa.ml | 66 +++++++++++++++++++
3 files changed, 106 insertions(+)
create mode 100644 sysprep/sysprep_operation_kerberos_hostkeytab.ml
create mode 100644 sysprep/sysprep_operation_unenroll_ipa.ml
--
2.25.4
4 years, 6 months
[common PATCH] mltools: add run_in_guest_command helper
by Pino Toscano
Add an helper function to run a command in the guest, checking for the
host/guest compatibility. This is mostly extracted from the internal
do_run helper currently in the Customize_run module of virt-customize.
---
mltools/tools_utils.ml | 50 +++++++++++++++++++++++++++++++++++++++++
mltools/tools_utils.mli | 10 +++++++++
2 files changed, 60 insertions(+)
diff --git a/mltools/tools_utils.ml b/mltools/tools_utils.ml
index 1271802..d54ec58 100644
--- a/mltools/tools_utils.ml
+++ b/mltools/tools_utils.ml
@@ -679,3 +679,53 @@ let with_timeout op timeout ?(sleep = 2) fn =
loop ()
in
loop ()
+
+let run_in_guest_command g root ?logfile ?incompatible_fn cmd =
+ (* Is the host_cpu compatible with the guest arch? ie. Can we
+ * run commands in this guest?
+ *)
+ let guest_arch = g#inspect_get_arch root in
+ let guest_arch_compatible = guest_arch_compatible guest_arch in
+ if not guest_arch_compatible then (
+ match incompatible_fn with
+ | None -> ()
+ | Some fn -> fn ()
+ )
+ else (
+ (* Add a prologue to the scripts:
+ * - Pass environment variables through from the host.
+ * - Optionally send stdout and stderr to a log file so we capture
+ * all output in error messages.
+ * - Use setarch when running x86_64 host + i686 guest.
+ *)
+ let env_vars =
+ List.filter_map (
+ fun name ->
+ try Some (sprintf "export %s=%s" name (quote (Sys.getenv name)))
+ with Not_found -> None
+ ) [ "http_proxy"; "https_proxy"; "ftp_proxy"; "no_proxy" ] in
+ let env_vars = String.concat "\n" env_vars ^ "\n" in
+
+ let cmd =
+ match Guestfs_config.host_cpu, guest_arch with
+ | "x86_64", ("i386"|"i486"|"i586"|"i686") ->
+ sprintf "setarch i686 <<\"__EOCMD\"
+%s
+__EOCMD
+" cmd
+ | _ -> cmd in
+
+ let logfile_redirect =
+ match logfile with
+ | None -> ""
+ | Some logfile -> sprintf "exec >>%s 2>&1" (quote logfile) in
+
+ let cmd = sprintf "\
+%s
+%s
+%s
+" (logfile_redirect) env_vars cmd in
+
+ debug "running command:\n%s" cmd;
+ ignore (g#sh cmd)
+ )
diff --git a/mltools/tools_utils.mli b/mltools/tools_utils.mli
index ab70f58..102abff 100644
--- a/mltools/tools_utils.mli
+++ b/mltools/tools_utils.mli
@@ -212,3 +212,13 @@ val with_timeout : string -> int -> ?sleep:int -> (unit -> 'a option) -> 'a
calls {!error} and the program exits. The error message will
contain the diagnostic string [op] to identify the operation
which timed out. *)
+
+val run_in_guest_command : Guestfs.guestfs -> string -> ?logfile:string -> ?incompatible_fn:(unit -> unit) -> string -> unit
+(** [run_in_guest_command g root ?incompatible_archs_fn cmd]
+ runs a command in the guest, which is already mounted for the
+ specified [root]. The command is run directly in case the
+ architecture of the host and the guest are compatible, optionally
+ calling [?incompatible_fn] in case they are not.
+
+ [?logfile] is an optional file in the guest to where redirect
+ stdout and stderr of the command. *)
--
2.25.4
4 years, 6 months
ANNOUNCE: nbdkit 1.20 - high performance NBD server
by Richard W.M. Jones
I'm pleased to announce the release of nbdkit 1.20, a high performance
plugin-based Network Block Device (NBD) server.
https://en.wikipedia.org/wiki/Network_block_device
Key features of nbdkit:
* Multithreaded NBD server written in C with good performance.
* Minimal dependencies for the basic server.
* Liberal license (BSD) allows nbdkit to be linked to proprietary
libraries or included in proprietary code.
* Well-documented, simple plugin API with a stable ABI guarantee.
Lets you export “unconventional” block devices easily.
* You can write plugins in C, [new!] Go, Lua, Perl, Python, OCaml,
Ruby, Rust, shell script or Tcl.
* Filters can be stacked in front of plugins to transform the output.
Git: https://github.com/libguestfs/nbdkit
Download: http://download.libguestfs.org/nbdkit/1.20-stable/
Fedora: https://koji.fedoraproject.org/koji/packageinfo?packageID=16469
Release notes (http://libguestfs.org/nbdkit-release-notes-1.20.1.html)
These are the release notes for nbdkit stable release 1.20. This
describes the major changes since 1.18.
nbdkit 1.20.0 was released on 2nd May 2020.
Security
There were no security issues found. All past security issues and
information about how to report new ones can be found in
nbdkit-security(1).
Plugins
New nbdkit-tmpdisk-plugin(1) is a scriptable “remote tmpfs” for
creating temporary filesystems (eg. for thin clients), and also for
blank or prepopulated temporary disks.
nbdkit-data-plugin(1) now has support for prepopulating disks with more
complex test patterns such as repeated sequences of bytes.
nbdkit-curl-plugin(1) now supports setting a proxy, enabling TCP
keepalives, and disabling Nagle’s algorithm.
nbdkit-perl-plugin(1) now supports API version 2. In particular the
full NBD client flags are visible to plugins, along with support for
the ".get_ready" callback. Also there is a new function
"Nbdkit::debug" which is a wrapper around the "nbdkit_debug" API.
nbdkit-vddk-plugin(1) drops support for VDDK 5.1.1. This version was
last updated in 2014 and is no longer supported by VMware. Since this
was the last version of VDDK to support i686, 32-bit support is also
dropped (Eric Blake).
Language bindings
Plugins may now be written in Golang, see nbdkit-golang-plugin(3)
(thanks Dan Berrangé, James Shubin).
OCaml plugins can now access "nbdkit_realpath", "nbdkit_nanosleep",
"nbdkit_export_name" and "nbdkit_shutdown".
Python plugins now transparently support fail-fast zero (Eric Blake).
Filters
New nbdkit-exitlast-filter(1) causes nbdkit to exit after the last
client connection.
New nbdkit-limit-filter(1) allows you to limit the number of clients
which can simultaneously connect to any plugin.
Server
The --run option now waits for the nbdkit plugin and nbdkit to exit
before returning to the caller. This allows for more predictable clean
up in shell scripts using this feature.
nbdkit --dump-config output now includes separate lines for
"version_major" and "version_minor", making it easier to find out from
shell scripts which version of nbdkit is installed.
nbdkit -s option (which connects to the client over stdin/stdout) now
rejects various options that would also try to read from or write to
stdin/stdout, for example --dump-plugin or "password=-" (Eric Blake).
API
New "nbdkit_shutdown" call which allows plugins to ask for nbdkit to
exit. This is used to implement the new "exitlast" filter.
New "nbdkit_stdio_safe" call allows plugins to check if reading from
stdin or writing to stdout is safe, eg. if it is safe to read passwords
interactively (Eric Blake).
"can_*" callbacks which return booleans can return any value ≥ 1 to
mean true. Previous versions of nbdkit had inconsistent behaviour if
plugins returned anything other than 1 for true (Eric Blake).
Bug fixes
nbdkit-tar-plugin(1) now works and there is a regression test for it.
nbdkit-curl-plugin(1) "-D curl.version=1" option now works.
Fixed a rare hang when closing a connection in nbdkit-nbd-plugin(1)
(Eric Blake).
Fix compilation on certain platforms with clang (Khem Raj).
Don’t leak $tmpdir from nbdkit-sh-plugin(1) into the --run subcommand.
nbdkit now correctly sets "FD_CLOEXEC" when using systemd socket
activation (Eric Blake).
Documentation
The nbdkit-plugin(3) man page has been overhauled completely to make it
easier to follow. Also we now have documentation for how to compile
plugins in various environments which was missing before.
Tests
All valgrind tests now pass.
“Old plugin” tests were added for v1.18.2 on x86-64, and a variety of
old plugins compiled on i686. The i686 plugins will allow us to test
for regressions in 32 bit support.
Tests of the nbd plugin should now be stable (Eric Blake).
There is an additional test combining the offset and truncate filters,
which tests several corner cases as well as providing tests of error
handling between layers.
Build
nbdkit-nbd-plugin(1) now requires libnbd. (If libnbd is not present at
build time then this plugin is not built). The fallback code in this
plugin which made NBD connections by constructing NBD command packets
without using libnbd has been removed (Eric Blake).
scripts/git.orderfile has been improved so that commands like
"git diff" and "git show" display OCaml sources in a natural order with
interface first followed by implementation.
Various fixes for MinGW. Note MinGW / MSYS support is a work in
progress and not finished yet (Frank Gu).
Multiple fixes to Haiku build.
awk(1) is no longer required to run the tests. It was a “hidden”
required dependency, but all use of it has now been eliminated.
Internals
There is now an internal utility library for creating vectors/lists of
objects, for example lists of strings (common/utils/vector.h). It is
widely used by the server, plugins and filters.
README discusses how to use lcov(1) for code coverage reports.
SEE ALSO
nbdkit(1).
AUTHORS
Authors of nbdkit 1.20:
Eric Blake
(33 commits)
Khem Raj
(1 commit)
Richard W.M. Jones
(134 commits)
Frank Gu
(6 commits)
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
Fedora Windows cross-compiler. Compile Windows programs, test, and
build Windows installers. Over 100 libraries supported.
http://fedoraproject.org/wiki/MinGW
4 years, 6 months
Anything for nbdkit 1.20? / ddrescue filter latest version?
by Richard W.M. Jones
Eric: Even though it's only been about 2 months, we have a very good
feature set for nbdkit 1.20. Is there anything you'd like to get in
or any patches I have missed?
Francois: you mentioned the ddrescue filter yesterday. While I don't
want to put in a new filter right before we do a release (new features
need to sit around in development so we can uncover bugs), if you do
have updated patches we could still review them.
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines. Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top
4 years, 6 months
[PATCH] WIP: ddrescue mapfile filter
by François Revol
This allows to overlay bad sectors according to the mapfile generated by
ddrescue, to then see where sectors are used using fsck and trying to
copy files around.
Signed-off-by: François Revol <revol(a)free.fr>
---
configure.ac | 2 +
filters/ddrescue/Makefile.am | 75 +++++++
filters/ddrescue/ddrescue.c | 218 ++++++++++++++++++++
filters/ddrescue/nbdkit-ddrescue-filter.pod | 76 +++++++
4 files changed, 371 insertions(+)
create mode 100644 filters/ddrescue/Makefile.am
create mode 100644 filters/ddrescue/ddrescue.c
create mode 100644 filters/ddrescue/nbdkit-ddrescue-filter.pod
diff --git a/configure.ac b/configure.ac
index 6c25226e..21e1013f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ filters="\
cache \
cacheextents \
cow \
+ ddrescue \
delay \
error \
exitlast \
@@ -1089,6 +1090,7 @@ AC_CONFIG_FILES([Makefile
filters/cache/Makefile
filters/cacheextents/Makefile
filters/cow/Makefile
+ filters/ddrescue/Makefile
filters/delay/Makefile
filters/error/Makefile
filters/exitlast/Makefile
diff --git a/filters/ddrescue/Makefile.am b/filters/ddrescue/Makefile.am
new file mode 100644
index 00000000..2498074c
--- /dev/null
+++ b/filters/ddrescue/Makefile.am
@@ -0,0 +1,75 @@
+# nbdkit
+# Copyright (C) 2018 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-ddrescue-filter.pod \
+ $(NULL)
+
+filter_LTLIBRARIES = nbdkit-ddrescue-filter.la
+
+nbdkit_ddrescue_filter_la_SOURCES = \
+ ddrescue.c \
+ $(top_srcdir)/include/nbdkit-filter.h \
+ $(NULL)
+
+nbdkit_ddrescue_filter_la_CPPFLAGS = \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/common/include \
+ -I$(top_srcdir)/common/sparse \
+ -I$(top_srcdir)/common/utils \
+ $(NULL)
+nbdkit_ddrescue_filter_la_CFLAGS = \
+ $(WARNINGS_CFLAGS) \
+ $(GNUTLS_CFLAGS) \
+ $(NULL)
+nbdkit_ddrescue_filter_la_LDFLAGS = \
+ -module -avoid-version -shared \
+ -Wl,--version-script=$(top_srcdir)/filters/filters.syms \
+ $(NULL)
+nbdkit_ddrescue_filter_la_LIBADD = \
+ $(top_builddir)/common/sparse/libsparse.la \
+ $(top_builddir)/common/utils/libutils.la \
+ $(GNUTLS_LIBS) \
+ $(NULL)
+
+if HAVE_POD
+
+man_MANS = nbdkit-ddrescue-filter.1
+CLEANFILES += $(man_MANS)
+
+nbdkit-ddrescue-filter.1: nbdkit-ddrescue-filter.pod
+ $(PODWRAPPER) --section=1 --man $@ \
+ --html $(top_builddir)/html/$@.html \
+ $<
+
+endif HAVE_POD
diff --git a/filters/ddrescue/ddrescue.c b/filters/ddrescue/ddrescue.c
new file mode 100644
index 00000000..a0e49e3c
--- /dev/null
+++ b/filters/ddrescue/ddrescue.c
@@ -0,0 +1,218 @@
+/* nbdkit
+ * Copyright (C) 2018-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 <pthread.h>
+
+#include <nbdkit-filter.h>
+
+#include "cleanup.h"
+
+struct range {
+ int64_t start;
+ int64_t end;
+ int64_t size;
+ char status;
+};
+
+struct mapfile {
+ int ranges_count;
+ struct range *ranges;
+};
+
+static struct mapfile map = { 0, NULL };
+
+static int
+parse_mapfile (const char *filename)
+{
+ FILE *fp = NULL;
+ CLEANUP_FREE char *line = NULL;
+ size_t linelen = 0;
+ ssize_t len;
+ int ret = -1;
+ int status_seen = 0;
+
+ fp = fopen (filename, "r");
+ if (!fp) {
+ nbdkit_error ("%s: ddrescue: fopen: %m", filename);
+ goto out;
+ }
+
+ while ((len = getline (&line, &linelen, fp)) != -1) {
+ const char *delim = " \t";
+ char *sp, *p;
+ int64_t offset, length;
+ char status;
+
+ if (len > 0 && line[len-1] == '\n') {
+ line[len-1] = '\0';
+ len--;
+ }
+
+ if (len > 0 && line[0] == '#')
+ continue;
+
+ if (len > 0 && !status_seen) {
+ /* status line, ignore it for now */
+ status_seen = 1;
+ nbdkit_debug ("%s: skipping status line: '%s'", filename, line);
+ continue;
+ }
+
+ if (sscanf (line, "%" SCNi64 "\t%" SCNi64 "\t%c", &offset, &length, &status) == 3) {
+ if (offset < 0) {
+ nbdkit_error ("block offset must not be negative");
+ return -1;
+ }
+ if (length < 0) {
+ nbdkit_error ("block length must not be negative");
+ return -1;
+ }
+ if (status == '+') {
+ int i = map.ranges_count++;
+ map.ranges = realloc(map.ranges, map.ranges_count * sizeof(struct range));
+ if (map.ranges == NULL) {
+ nbdkit_error ("%s: ddrescue: realloc: %m", filename);
+ goto out;
+ }
+ map.ranges[i].start = offset;
+ map.ranges[i].end = offset + length - 1;
+ map.ranges[i].size = length;
+ map.ranges[i].status = status;
+ }
+
+ nbdkit_debug ("%s: range: 0x%" PRIx64 " 0x%" PRIx64 " '%c'", filename, offset, length, status);
+ }
+ }
+
+ ret = 0;
+
+ out:
+ if (fp)
+ fclose (fp);
+ return ret;
+}
+
+
+static void
+ddrescue_load (void)
+{
+}
+
+/* On unload, free the sparse array. */
+static void
+ddrescue_unload (void)
+{
+ free (map.ranges);
+ map.ranges = NULL;
+ map.ranges_count = 0;
+}
+
+static int
+ddrescue_config (nbdkit_next_config *next, void *nxdata,
+ const char *key, const char *value)
+{
+ if (strcmp (key, "ddrescue-mapfile") == 0) {
+ if (parse_mapfile (value) == -1)
+ return -1;
+ return 0;
+ }
+
+ else
+ return next (nxdata, key, value);
+}
+
+#define ddrescue_config_help \
+ "ddrescue-mapfile=... Specify ddrescue mapfile to use"
+
+/* We need this because otherwise the layer below can_write is called
+ * and that might return true (eg. if the plugin has a pwrite method
+ * at all), resulting in writes being passed through to the layer
+ * below. This is possibly a bug in nbdkit.
+ */
+static int
+ddrescue_can_write (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle)
+{
+ return 0;
+}
+
+static int
+ddrescue_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata,
+ void *handle)
+{
+ return 0;
+}
+
+/* Read data. */
+static int
+ddrescue_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 i;
+
+ for (i = 0; i < map.ranges_count; i++) {
+ if (map.ranges[i].status != '+')
+ continue;
+ if (offset >= map.ranges[i].start && offset <= map.ranges[i].end) {
+ if (offset + count - 1 <= map.ranges[i].end) {
+ /* entirely contained within this range */
+ return next_ops->pread (nxdata, buf, count, offset, flags, err);
+ }
+ }
+ }
+ /* read was not fully covered */
+ *err = EIO;
+ return -1;
+}
+
+static struct nbdkit_filter filter = {
+ .name = "ddrescue",
+ .longname = "nbdkit ddrescue mapfile filter",
+ .load = ddrescue_load,
+ .unload = ddrescue_unload,
+ .config = ddrescue_config,
+ .config_help = ddrescue_config_help,
+ .can_write = ddrescue_can_write,
+ .can_cache = ddrescue_can_cache,
+ .pread = ddrescue_pread,
+};
+
+NBDKIT_REGISTER_FILTER(filter)
diff --git a/filters/ddrescue/nbdkit-ddrescue-filter.pod b/filters/ddrescue/nbdkit-ddrescue-filter.pod
new file mode 100644
index 00000000..3d9059bd
--- /dev/null
+++ b/filters/ddrescue/nbdkit-ddrescue-filter.pod
@@ -0,0 +1,76 @@
+=head1 NAME
+
+nbdkit-ddrescue-filter - nbdkit filter for serving from ddrescue dump
+
+=head1 SYNOPSIS
+
+ nbdkit --filter=ddrescue plugin [plugin-args...] ddrescue-mapfile=file.map
+
+ nbdkit --filter=ddrescue file file=file.img ddrescue-mapfile=file.map [plugin-args...]
+
+=head1 DESCRIPTION
+
+C<nbdkit-ddrescue-filter> is a filter for L<nbdkit(1)> which overlays
+bad blocks according to a GNU L<ddrescue(1)> mapfile. This is mainly useful
+for testing disk images recovered with ddrescue, to detect which files
+or filesystem structures are impacted, or attempting fsck on them.
+
+Note that the current implementation is read-only.
+
+=head1 EXAMPLES
+
+=over 4
+
+=item Expose a rescued disk image with detected bad sectors:
+
+ nbdkit --filter=ddrescue file file=disk.img ddrescue-mapfile=disk.map
+
+The above command serves the disk image disk.img and maps the bad
+sectors listed in disk.img so that read attempts on them do not return
+a valid block full of zeroes.
+
+=back
+
+=head1 PARAMETERS
+
+The C<ddrescue-mapfile> parameter must point to a valid GNU ddrescue
+mapfile.
+
+=head1 DATA FORMAT
+
+The file pointed to by the C<ddrescue-mapfile> parameter should
+conform to the format of a GNU L<ddrescue(1)> mapfile.
+
+=head1 FILES
+
+=over 4
+
+=item F<$filterdir/nbdkit-ddrescue-filter.so>
+
+The filter.
+
+Use C<nbdkit --dump-config> to find the location of C<$filterdir>.
+
+=back
+
+=head1 VERSION
+
+C<nbdkit-ddrescue-filter> first appeared in nbdkit 1.21.
+
+=head1 SEE ALSO
+
+L<nbdkit(1)>,
+L<nbdkit-file-plugin(1)>,
+L<nbdkit-filter(3)>,
+L<ddrescue(1)>,
+L<https://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html>.
+
+=head1 AUTHORS
+
+François Revol
+
+Richard W.M. Jones
+
+=head1 COPYRIGHT
+
+Copyright (C) 2020 Red Hat Inc.
--
2.26.2
4 years, 6 months