I'm impressed that I was able to whip this out in just one day of hacking. Below, I'll include a diff between the plugin and the filter as of patch 1, if it aids review. Eric Blake (3): filters: Add ext2 filter ext2: Deprecate ext2 plugin ext2: Add mode for letting client exportname choose file from image TODO | 5 - configure.ac | 8 +- filters/ext2/Makefile.am | 75 +++++ filters/ext2/ext2.c | 415 +++++++++++++++++++++++++ filters/ext2/io.c | 466 ++++++++++++++++++++++++++++ filters/ext2/io.h | 57 ++++ filters/ext2/nbdkit-ext2-filter.pod | 102 ++++++ plugins/ext2/nbdkit-ext2-plugin.pod | 12 +- tests/Makefile.am | 2 +- tests/test-ext2.c | 28 +- 10 files changed, 1153 insertions(+), 17 deletions(-) create mode 100644 filters/ext2/Makefile.am create mode 100644 filters/ext2/ext2.c create mode 100644 filters/ext2/io.c create mode 100644 filters/ext2/io.h create mode 100644 filters/ext2/nbdkit-ext2-filter.pod {plugins => filters}/ext2/ext2.c | 173 ++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 68 deletions(-) diff --git a/plugins/ext2/ext2.c b/filters/ext2/ext2.c index 6698d99f..d53743cd 100644 --- a/plugins/ext2/ext2.c +++ b/filters/ext2/ext2.c @@ -1,5 +1,5 @@ /* nbdkit - * Copyright (C) 2017-2019 Red Hat Inc. + * Copyright (C) 2017-2020 Red Hat Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -35,6 +35,7 @@ #include <stdio.h> #include <stdlib.h> #include <inttypes.h> +#include <errno.h> /* Inlining is broken in the ext2fs header file. Disable it by * defining the following: @@ -44,10 +45,12 @@ #define NBDKIT_API_VERSION 2 -#include <nbdkit-plugin.h> +#include <nbdkit-filter.h> -/* Disk image and filename parameters. */ -static char *disk; +#include "cleanup.h" +#include "io.h" + +/* Filename parameter. */ static char *file; static void @@ -59,25 +62,16 @@ ext2_load (void) static void ext2_unload (void) { - free (disk); free (file); } static int -ext2_config (const char *key, const char *value) +ext2_config (nbdkit_next_config *next, void *nxdata, + const char *key, const char *value) { - if (strcmp (key, "disk") == 0) { - if (disk != NULL) { - nbdkit_error ("disk parameter specified more than once"); - return -1; - } - disk = nbdkit_absolute_path (value); - if (disk == NULL) - return -1; - } - else if (strcmp (key, "file") == 0) { + if (strcmp (key, "ext2file") == 0) { if (file != NULL) { - nbdkit_error ("file parameter specified more than once"); + nbdkit_error ("ext2file parameter specified more than once"); return -1; } file = strdup (value); @@ -85,20 +79,17 @@ ext2_config (const char *key, const char *value) nbdkit_error ("strdup: %m"); return -1; } + return 0; } - else { - nbdkit_error ("unknown parameter '%s'", key); - return -1; - } - - return 0; + else + return next (nxdata, key, value); } static int -ext2_config_complete (void) +ext2_config_complete (nbdkit_next_config_complete *next, void *nxdata) { - if (disk == NULL || file == NULL) { - nbdkit_error ("you must supply disk=<DISK> and file=<FILE> parameters " + if (file == NULL) { + nbdkit_error ("you must supply ext2file=<FILE> parameter " "after the plugin name on the command line"); return -1; } @@ -108,46 +99,78 @@ ext2_config_complete (void) return -1; } - return 0; + return next (nxdata); } #define ext2_config_help \ - "disk=<FILENAME> (required) Raw ext2, ext3 or ext4 filesystem.\n" \ - "file=<FILENAME> (required) File to serve inside the disk image." + "ext2file=<FILENAME> (required) File to serve inside the disk image." /* The per-connection handle. */ struct handle { ext2_filsys fs; /* Filesystem handle. */ ext2_ino_t ino; /* Inode of open file. */ ext2_file_t file; /* File handle. */ + struct nbdkit_next next; /* "name" parameter to ext2fs_open. */ }; /* Create the per-connection handle. */ static void * -ext2_open (int readonly) +ext2_open (nbdkit_next_open *next, void *nxdata, int readonly) { struct handle *h; + + /* Request write access to the underlying plugin, for journal replay. */ + if (next (nxdata, 0) == -1) + return NULL; + + h = calloc (1, sizeof *h); + if (h == NULL) { + nbdkit_error ("calloc: %m"); + return NULL; + } + + return h; +} + +static int +ext2_prepare (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle, + int readonly) +{ + struct handle *h = handle; errcode_t err; int fs_flags; int file_flags; struct ext2_inode inode; - - h = malloc (sizeof *h); - if (h == NULL) { - nbdkit_error ("malloc: %m"); - return NULL; - } + int64_t r; + CLEANUP_FREE char *name = NULL; fs_flags = 0; #ifdef EXT2_FLAG_64BITS fs_flags |= EXT2_FLAG_64BITS; #endif + r = next_ops->get_size (nxdata); + if (r == -1) + return -1; + r = next_ops->can_write (nxdata); + if (r == -1) + return -1; + if (r == 0) + readonly = 1; + if (!readonly) fs_flags |= EXT2_FLAG_RW; - err = ext2fs_open (disk, fs_flags, 0, 0, unix_io_manager, &h->fs); + h->next.next_ops = next_ops; + h->next.nxdata = nxdata; + name = nbdkit_io_encode (&h->next); + if (!name) { + nbdkit_error ("nbdkit_io_encode: %m"); + return -1; + } + + err = ext2fs_open (name, fs_flags, 0, 0, nbdkit_io_manager, &h->fs); if (err != 0) { - nbdkit_error ("%s: open: %s", disk, error_message (err)); + nbdkit_error ("open: %s", error_message (err)); goto err0; } @@ -158,7 +181,7 @@ ext2_open (int readonly) err = ext2fs_namei (h->fs, EXT2_ROOT_INO, EXT2_ROOT_INO, &file[1], &h->ino); if (err != 0) { - nbdkit_error ("%s: %s: namei: %s", disk, file, error_message (err)); + nbdkit_error ("%s: namei: %s", file, error_message (err)); goto err1; } } @@ -168,12 +191,11 @@ ext2_open (int readonly) */ err = ext2fs_read_inode (h->fs, h->ino, &inode); if (err != 0) { - nbdkit_error ("%s: %s: inode: %s", disk, file, error_message (err)); + nbdkit_error ("%s: inode: %s", file, error_message (err)); goto err1; } if (!LINUX_S_ISREG (inode.i_mode)) { - nbdkit_error ("%s: %s: must be a regular file in the disk image", - disk, file); + nbdkit_error ("%s: must be a regular file in the disk image", file); goto err1; } @@ -182,17 +204,17 @@ ext2_open (int readonly) file_flags |= EXT2_FILE_WRITE; err = ext2fs_file_open2 (h->fs, h->ino, NULL, file_flags, &h->file); if (err != 0) { - nbdkit_error ("%s: %s: open: %s", disk, file, error_message (err)); + nbdkit_error ("%s: open: %s", file, error_message (err)); goto err1; } - return h; + return 0; err1: ext2fs_close (h->fs); + h->fs = NULL; err0: - free (h); - return NULL; + return -1; } /* Free up the per-connection handle. */ @@ -201,19 +223,21 @@ ext2_close (void *handle) { struct handle *h = handle; - ext2fs_file_close (h->file); - ext2fs_close (h->fs); + if (h->fs) { + ext2fs_file_close (h->file); + ext2fs_close (h->fs); + } free (h); } static int -ext2_can_fua (void *handle) +ext2_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle) { return NBDKIT_FUA_NATIVE; } static int -ext2_can_cache (void *handle) +ext2_can_cache (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle) { /* Let nbdkit call pread to populate the file system cache. */ return NBDKIT_CACHE_EMULATE; @@ -231,11 +255,14 @@ ext2_can_cache (void *handle) * but if we allowed parallel work on those handles then we would get * data corruption, so we need to serialize connections. */ -#define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS +static int ext2_thread_model (void) +{ + return NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS; +} /* Get the disk size. */ static int64_t -ext2_get_size (void *handle) +ext2_get_size (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle) { struct handle *h = handle; errcode_t err; @@ -243,7 +270,7 @@ ext2_get_size (void *handle) err = ext2fs_file_get_lsize (h->file, (__u64 *) &size); if (err != 0) { - nbdkit_error ("%s: %s: lsize: %s", disk, file, error_message (err)); + nbdkit_error ("%s: lsize: %s", file, error_message (err)); return -1; } return (int64_t) size; @@ -251,8 +278,9 @@ ext2_get_size (void *handle) /* Read data. */ static int -ext2_pread (void *handle, void *buf, uint32_t count, uint64_t offset, - uint32_t flags) +ext2_pread (struct nbdkit_next_ops *next_ops, void *nxdata, + void *handle, void *buf, uint32_t count, uint64_t offset, + uint32_t flags, int *errp) { struct handle *h = handle; errcode_t err; @@ -265,13 +293,15 @@ ext2_pread (void *handle, void *buf, uint32_t count, uint64_t offset, */ err = ext2fs_file_llseek (h->file, offset, EXT2_SEEK_SET, NULL); if (err != 0) { - nbdkit_error ("%s: %s: llseek: %s", disk, file, error_message (err)); + nbdkit_error ("%s: llseek: %s", file, error_message (err)); + *errp = errno; return -1; } err = ext2fs_file_read (h->file, buf, (unsigned int) count, &got); if (err != 0) { - nbdkit_error ("%s: %s: read: %s", disk, file, error_message (err)); + nbdkit_error ("%s: read: %s", file, error_message (err)); + *errp = errno; return -1; } @@ -285,8 +315,9 @@ ext2_pread (void *handle, void *buf, uint32_t count, uint64_t offset, /* Write data to the file. */ static int -ext2_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, - uint32_t flags) +ext2_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata, + void *handle, const void *buf, uint32_t count, uint64_t offset, + uint32_t flags, int *errp) { struct handle *h = handle; errcode_t err; @@ -295,13 +326,15 @@ ext2_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, while (count > 0) { err = ext2fs_file_llseek (h->file, offset, EXT2_SEEK_SET, NULL); if (err != 0) { - nbdkit_error ("%s: %s: llseek: %s", disk, file, error_message (err)); + nbdkit_error ("%s: llseek: %s", file, error_message (err)); + *errp = errno; return -1; } err = ext2fs_file_write (h->file, buf, (unsigned int) count, &written); if (err != 0) { - nbdkit_error ("%s: %s: write: %s", disk, file, error_message (err)); + nbdkit_error ("%s: write: %s", file, error_message (err)); + *errp = errno; return -1; } @@ -313,7 +346,8 @@ ext2_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, if ((flags & NBDKIT_FLAG_FUA) != 0) { err = ext2fs_file_flush (h->file); if (err != 0) { - nbdkit_error ("%s: %s: flush: %s", disk, file, error_message (err)); + nbdkit_error ("%s: flush: %s", file, error_message (err)); + *errp = errno; return -1; } } @@ -322,14 +356,16 @@ ext2_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset, } static int -ext2_flush (void *handle, uint32_t flags) +ext2_flush (struct nbdkit_next_ops *next_ops, void *nxdata, + void *handle, uint32_t flags, int *errp) { struct handle *h = handle; errcode_t err; err = ext2fs_file_flush (h->file); if (err != 0) { - nbdkit_error ("%s: %s: flush: %s", disk, file, error_message (err)); + nbdkit_error ("%s: flush: %s", file, error_message (err)); + *errp = errno; return -1; } @@ -341,15 +377,17 @@ ext2_flush (void *handle, uint32_t flags) * is very obscure. */ -static struct nbdkit_plugin plugin = { +static struct nbdkit_filter filter = { .name = "ext2", - .version = PACKAGE_VERSION, + .longname = "nbdkit ext2 filter", .load = ext2_load, .unload = ext2_unload, .config = ext2_config, .config_complete = ext2_config_complete, .config_help = ext2_config_help, + .thread_model = ext2_thread_model, .open = ext2_open, + .prepare = ext2_prepare, .close = ext2_close, .can_fua = ext2_can_fua, .can_cache = ext2_can_cache, @@ -357,7 +395,6 @@ static struct nbdkit_plugin plugin = { .pread = ext2_pread, .pwrite = ext2_pwrite, .flush = ext2_flush, - .errno_is_preserved = 1, }; -NBDKIT_REGISTER_PLUGIN(plugin) +NBDKIT_REGISTER_FILTER(filter) -- 2.24.1