[Libguestfs] [nbdkit PATCH 3/3] ext2: Add mode for letting client exportname choose file from image

Eric Blake eblake at redhat.com
Wed Feb 12 02:19:02 UTC 2020


Not enabled by default, because of the potential security concern that
a client can use this to sniff what file names exist in the image; but
for an opt-in setting in the server, this lets a single nbdkit process
serve multiple files from an ext2 image (one at a time, unless we find
a way to allow a filter to open the plugin once without a current
connection, then share that opened plugin across multiple client
connections).

In testing this manually, I learned that qemu 4.0 fails to preserve
leading / in nbd://host:port//path/to/file (no matter how many slashes
you add), so the code here adds a leading slash to be nice to clients
like broken qemu.

Signed-off-by: Eric Blake <eblake at redhat.com>
---
 filters/ext2/ext2.c                 | 35 ++++++++++++++++++++---------
 filters/ext2/nbdkit-ext2-filter.pod | 10 +++++++++
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/filters/ext2/ext2.c b/filters/ext2/ext2.c
index d53743cd..92c8601a 100644
--- a/filters/ext2/ext2.c
+++ b/filters/ext2/ext2.c
@@ -50,7 +50,7 @@
 #include "cleanup.h"
 #include "io.h"

-/* Filename parameter. */
+/* Filename parameter, or NULL to honor export name. */
 static char *file;

 static void
@@ -94,7 +94,11 @@ ext2_config_complete (nbdkit_next_config_complete *next, void *nxdata)
     return -1;
   }

-  if (file[0] != '/') {
+  if (strcmp (file, "exportname") == 0) {
+    free (file);
+    file = NULL;
+  }
+  else if (file[0] != '/') {
     nbdkit_error ("the file parameter must refer to an absolute path");
     return -1;
   }
@@ -103,7 +107,8 @@ ext2_config_complete (nbdkit_next_config_complete *next, void *nxdata)
 }

 #define ext2_config_help \
-  "ext2file=<FILENAME>  (required) File to serve inside the disk image."
+  "ext2file=<FILENAME>  (required) Absolute name of file to serve inside the\n" \
+  "                     disk image, or 'exportname' for client choice."

 /* The per-connection handle. */
 struct handle {
@@ -143,6 +148,8 @@ ext2_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
   struct ext2_inode inode;
   int64_t r;
   CLEANUP_FREE char *name = NULL;
+  const char *fname = file ?: nbdkit_export_name ();
+  CLEANUP_FREE char *absname = NULL;

   fs_flags = 0;
 #ifdef EXT2_FLAG_64BITS
@@ -168,34 +175,42 @@ ext2_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
     return -1;
   }

+  if (fname[0] != '/') {
+    if (asprintf (&absname, "/%s", fname) < 0) {
+      nbdkit_error ("asprintf: %m");
+      return -1;
+    }
+    fname = absname;
+  }
+
   err = ext2fs_open (name, fs_flags, 0, 0, nbdkit_io_manager, &h->fs);
   if (err != 0) {
     nbdkit_error ("open: %s", error_message (err));
     goto err0;
   }

-  if (strcmp (file, "/") == 0)
+  if (strcmp (fname, "/") == 0)
     /* probably gonna fail, but we'll catch it later */
     h->ino = EXT2_ROOT_INO;
   else {
     err = ext2fs_namei (h->fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
-                        &file[1], &h->ino);
+                        &fname[1], &h->ino);
     if (err != 0) {
-      nbdkit_error ("%s: namei: %s", file, error_message (err));
+      nbdkit_error ("%s: namei: %s", fname, error_message (err));
       goto err1;
     }
   }

-  /* Check the file is a regular file.
+  /* Check that fname is a regular file.
    * XXX This won't follow symlinks, we'd have to do that manually.
    */
   err = ext2fs_read_inode (h->fs, h->ino, &inode);
   if (err != 0) {
-    nbdkit_error ("%s: inode: %s", file, error_message (err));
+    nbdkit_error ("%s: inode: %s", fname, error_message (err));
     goto err1;
   }
   if (!LINUX_S_ISREG (inode.i_mode)) {
-    nbdkit_error ("%s: must be a regular file in the disk image", file);
+    nbdkit_error ("%s: must be a regular file in the disk image", fname);
     goto err1;
   }

@@ -204,7 +219,7 @@ ext2_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
     file_flags |= EXT2_FILE_WRITE;
   err = ext2fs_file_open2 (h->fs, h->ino, NULL, file_flags, &h->file);
   if (err != 0) {
-    nbdkit_error ("%s: open: %s", file, error_message (err));
+    nbdkit_error ("%s: open: %s", fname, error_message (err));
     goto err1;
   }

diff --git a/filters/ext2/nbdkit-ext2-filter.pod b/filters/ext2/nbdkit-ext2-filter.pod
index c0927637..d621ee45 100644
--- a/filters/ext2/nbdkit-ext2-filter.pod
+++ b/filters/ext2/nbdkit-ext2-filter.pod
@@ -7,6 +7,9 @@ ext4 filesystems

  nbdkit --filter=ext2 file fs.img ext2file=/disks/disk.raw

+ nbdkit --filter=ext2 --filter=partition file fs.img \
+    partition=1 ext2file=exportname
+
 =head1 DESCRIPTION

 C<nbdkit-ext2-filter> is an nbdkit filter which can read and
@@ -55,6 +58,13 @@ e2fsprogs.
 The full path of the file within the filesystem that will be exposed
 over NBD.  The path must be absolute (starts with C</>).

+=item B<ext2file=exportname>
+
+The plugin will expose the path within the filesystem chosen by the
+exportname passed by the client.  Note that this mode allows the
+client to deduce which files exist within the disk image, which may be
+a security risk in some situations.
+
 =back

 =head1 FILES
-- 
2.24.1




More information about the Libguestfs mailing list