[Libguestfs] [PATCH nbdkit 2/2] tar: Rewrite the tar plugin (again), this time in C.
Nir Soffer
nsoffer at redhat.com
Sun Jun 28 13:46:59 UTC 2020
On Sun, Jun 28, 2020 at 4:03 PM Richard W.M. Jones <rjones at redhat.com> wrote:
...
> +
> +static int
> +tar_get_ready (void)
> +{
> + FILE *fp;
> + CLEANUP_FREE char *cmd = NULL;
> + size_t len = 0;
> + bool scanned_ok;
> + char s[256];
> +
> + /* Construct the tar command to examine the tar file. */
> + fp = open_memstream (&cmd, &len);
> + if (fp == NULL) {
> + nbdkit_error ("open_memstream: %m");
> + return -1;
> + }
> + fprintf (fp, "LANG=C tar --no-auto-compress -tRvf ");
Using -R is nice, but is block size documented?
Also --block-number would be nicer.
> + shell_quote (tarfile, fp);
This is questionable. Why use string and quote the string instead of using
argv directly?
> + fputc (' ', fp);
> + shell_quote (file, fp);
> + if (fclose (fp) == EOF) {
> + nbdkit_error ("memstream failed: %m");
> + return -1;
> + }
> +
> + /* Run the command and read the first line of the output. */
> + nbdkit_debug ("%s", cmd);
> + fp = popen (cmd, "r");
> + if (fp == NULL) {
> + nbdkit_error ("tar: %m");
> + return -1;
> + }
> + scanned_ok = fscanf (fp, "block %" SCNu64 ": %*s %*s %" SCNu64,
> + &offset, &size) == 2;
> + /* We have to now read and discard the rest of the output until EOF. */
> + while (fread (s, sizeof s, 1, fp) > 0)
> + ;
> + if (pclose (fp) != 0) {
> + nbdkit_error ("tar subcommand failed, "
> + "check that the file really exists in the tarball");
> + return -1;
> + }
> +
> + if (!scanned_ok) {
> + nbdkit_error ("unexpected output from the tar subcommand");
> + return -1;
> + }
> +
> + /* Adjust the offset: Add 1 for the tar header, then multiply by the
> + * block size.
> + */
> + offset = (offset+1) * 512;
> +
> + nbdkit_debug ("tar: offset %" PRIu64 ", size %" PRIu64, offset, size);
> +
> + /* Check it looks sensible. XXX We ought to check it doesn't exceed
> + * the size of the tar file.
> + */
> + if (offset >= INT64_MAX || size >= INT64_MAX) {
> + nbdkit_error ("internal error: calculated offset and size are wrong");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +struct handle {
> + int fd;
> +};
> +
> +static void *
> +tar_open (int readonly)
> +{
> + struct handle *h;
> +
> + assert (offset > 0); /* Cannot be zero because of tar header. */
> +
> + h = calloc (1, sizeof *h);
> + if (h == NULL) {
> + nbdkit_error ("calloc: %m");
> + return NULL;
> + }
> + h->fd = open (tarfile, (readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC);
> + if (h->fd == -1) {
> + nbdkit_error ("%s: %m", tarfile);
> + free (h);
> + return NULL;
> + }
> +
> + return h;
> +}
> +
> +#define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
> +
> +/* Get the file size. */
> +static int64_t
> +tar_get_size (void *handle)
> +{
> + return size;
> +}
> +
> +/* Serves the same data over multiple connections. */
> +static int
> +tar_can_multi_conn (void *handle)
> +{
> + return 1;
> +}
> +
> +static int
> +tar_can_cache (void *handle)
> +{
> + /* Let nbdkit call pread to populate the file system cache. */
> + return NBDKIT_CACHE_EMULATE;
> +}
> +
> +/* Read data from the file. */
> +static int
> +tar_pread (void *handle, void *buf, uint32_t count, uint64_t offs)
This should be identical to file plugin, on? can we reuse the same code
for reading files?
> +{
> + struct handle *h = handle;
> +
> + offs += offset;
> +
> + while (count > 0) {
> + ssize_t r = pread (h->fd, buf, count, offs);
> + if (r == -1) {
> + nbdkit_error ("pread: %m");
> + return -1;
> + }
> + if (r == 0) {
> + nbdkit_error ("pread: unexpected end of file");
> + return -1;
> + }
> + buf += r;
> + count -= r;
> + offs += r;
> + }
> +
> + return 0;
> +}
> +
> +/* Write data to the file. */
> +static int
> +tar_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offs)
> +{
> + struct handle *h = handle;
> +
> + offs += offset;
> +
> + while (count > 0) {
> + ssize_t r = pwrite (h->fd, buf, count, offs);
> + if (r == -1) {
> + nbdkit_error ("pwrite: %m");
> + return -1;
> + }
> + buf += r;
> + count -= r;
> + offs += r;
> + }
> +
> + return 0;
> +}
> +
> +static struct nbdkit_plugin plugin = {
> + .name = "tar",
> + .longname = "nbdkit tar plugin",
> + .version = PACKAGE_VERSION,
> + .unload = tar_unload,
> + .config = tar_config,
> + .config_complete = tar_config_complete,
> + .config_help = tar_config_help,
> + .magic_config_key = "tar",
> + .get_ready = tar_get_ready,
> + .open = tar_open,
> + .get_size = tar_get_size,
> + .can_multi_conn = tar_can_multi_conn,
> + .can_cache = tar_can_cache,
> + .pread = tar_pread,
> + .pwrite = tar_pwrite,
> + .errno_is_preserved = 1,
> +};
> +
> +NBDKIT_REGISTER_PLUGIN(plugin)
> diff --git a/tests/test-dump-plugin.sh b/tests/test-dump-plugin.sh
> index 0b4c1ce1..6eb25a65 100755
> --- a/tests/test-dump-plugin.sh
> +++ b/tests/test-dump-plugin.sh
> @@ -46,7 +46,7 @@ do_test ()
> python-valgrind | ruby-valgrind | tcl-valgrind)
> echo "$0: skipping $1$vg because this language doesn't support valgrind"
> ;;
> - example4* | tar*)
> + example4*)
> # These tests are written in Perl so we have to check that
> # the Perl plugin was compiled.
> if nbdkit perl --version; then run_test $1; fi
> diff --git a/tests/test-help-plugin.sh b/tests/test-help-plugin.sh
> index 7dc26ece..f0dfa7df 100755
> --- a/tests/test-help-plugin.sh
> +++ b/tests/test-help-plugin.sh
> @@ -46,7 +46,7 @@ do_test ()
> python-valgrind | ruby-valgrind | tcl-valgrind)
> echo "$0: skipping $1$vg because this language doesn't support valgrind"
> ;;
> - example4* | tar*)
> + example4*)
> # These tests are written in Perl so we have to check that
> # the Perl plugin was compiled.
> if nbdkit perl --version; then run_test $1; fi
> diff --git a/tests/test-tar-info.sh b/tests/test-tar-info.sh
> new file mode 100755
> index 00000000..efaa8ec8
> --- /dev/null
> +++ b/tests/test-tar-info.sh
> @@ -0,0 +1,67 @@
> +#!/usr/bin/env bash
> +# nbdkit
> +# 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
> +# 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 that qemu-img info works on a qcow2 file in a tar file.
> +
> +source ./functions.sh
> +set -e
> +set -x
> +
> +requires test -f disk
> +requires guestfish --version
> +requires tar --version
> +requires qemu-img --version
> +requires qemu-img info --output=json /dev/null
> +requires jq --version
> +requires stat --version
> +
> +disk=tar-info-disk.qcow2
> +out=tar-info.out
> +tar=tar-info.tar
> +files="$disk $out $tar"
> +rm -f $files
> +cleanup_fn rm -f $files
> +
> +# Create a tar file containing a known qcow2 file.
> +qemu-img convert -f raw disk -O qcow2 $disk
> +tar cf $tar $disk
> +
> +# Run nbdkit.
> +nbdkit -U - tar $tar file=$disk --run 'qemu-img info --output=json $nbd' > $out
> +cat $out
> +
> +# Check various fields in the input.
> +# Virtual size must be the same as the size of the original raw disk.
> +test "$( jq -r -c '.["virtual-size"]' $out )" -eq "$( stat -c %s disk )"
> +
> +# Format must be qcow2.
> +test "$( jq -r -c '.["format"]' $out )" = "qcow2"
> diff --git a/tests/test-tar.sh b/tests/test-tar.sh
> index c6d726c4..3164b826 100755
> --- a/tests/test-tar.sh
> +++ b/tests/test-tar.sh
> @@ -38,23 +38,27 @@ requires test -f disk
> requires guestfish --version
> requires tar --version
>
> -# The tar plugin requires some Perl modules, this checks if they are
> -# installed.
> -requires perl -MCwd -e 1
> -requires perl -MIO::File -e 1
> -
> sock=`mktemp -u`
> files="tar.pid tar.tar $sock"
> rm -f $files
> cleanup_fn rm -f $files
>
> -# Create a tar file containing the disk image.
> -tar cf tar.tar disk
> +# Create a tar file containing the disk image plus some other random
> +# files that hopefully will be ignored.
> +tar cf tar.tar test-tar.sh Makefile disk Makefile.am
> +tar tvvf tar.tar
>
> # Run nbdkit.
> start_nbdkit -P tar.pid -U $sock tar tar=tar.tar file=disk
>
> -# Now see if we can open the disk from the tar file.
> -guestfish -x --ro --format=raw -a "nbd://?socket=$sock" -m /dev/sda1 <<EOF
> +# Now see if we can open, read and write the disk from the tar file.
> +guestfish -x --format=raw -a "nbd://?socket=$sock" -m /dev/sda1 <<EOF
> cat /hello.txt
> +
> + # Write a new file.
> + write /test.txt "hello"
> + cat /test.txt
> EOF
> +
> +# Check that the tar file isn't corrupt.
> +tar tvvf tar.tar
> diff --git a/tests/test-version-plugin.sh b/tests/test-version-plugin.sh
> index 7d2ab072..c397afc2 100755
> --- a/tests/test-version-plugin.sh
> +++ b/tests/test-version-plugin.sh
> @@ -46,7 +46,7 @@ do_test ()
> python-valgrind | ruby-valgrind | tcl-valgrind)
> echo "$0: skipping $1$vg because this language doesn't support valgrind"
> ;;
> - example4* | tar*)
> + example4*)
> # These tests are written in Perl so we have to check that
> # the Perl plugin was compiled.
> if nbdkit perl --version; then run_test $1; fi
> diff --git a/wrapper.c b/wrapper.c
> index b1e2ce2f..c27afae0 100644
> --- a/wrapper.c
> +++ b/wrapper.c
> @@ -85,7 +85,7 @@ static size_t len;
> static bool
> is_perl_plugin (const char *name)
> {
> - return strcmp (name, "example4") == 0 || strcmp (name, "tar") == 0;
> + return strcmp (name, "example4") == 0;
> }
>
> static void
> diff --git a/.gitignore b/.gitignore
> index f02d19dc..48a5302e 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -79,7 +79,6 @@ plugins/*/*.3
> /plugins/ocaml/nbdkit-ocamlexample-plugin.so
> /plugins/rust/Cargo.lock
> /plugins/rust/target
> -/plugins/tar/nbdkit-tar-plugin
> /plugins/tmpdisk/default-command.c
> /podwrapper.pl
> /server/local/nbdkit.pc
> diff --git a/README b/README
> index 4f584828..7733761e 100644
> --- a/README
> +++ b/README
> @@ -124,13 +124,13 @@ For the bittorrent plugin:
>
> - libtorrent-rasterbar (https://www.libtorrent.org)
>
> -For the Perl, example4 and tar plugins:
> +For the Perl and example4 plugins:
>
> - perl interpreter
>
> - perl development libraries
>
> - - perl modules ExtUtils::Embed, IO::File and Cwd
> + - perl modules ExtUtils::Embed
>
> For the Python plugin:
>
> --
> 2.25.0
>
More information about the Libguestfs
mailing list