Richard W.M. Jones
2018-Aug-20 12:49 UTC
[Libguestfs] [PATCH nbdkit] tests: Add a root only test of the file plugin with
In libguestfs we have a few tests that require root privileges and they are skipped by default (normally you should not build or test as root), but you can do this to run them: sudo make check-root In nbdkit I wanted to check that the file plugin works with block devices (this is not tested), and the only way I can sensibly think to do this is using a loopback device and root. This commit therefore adds a similar mechanism for running root tests and a test of the file plugin using loop. It's somewhat hard to say whether this is testing the full fallocate, trim and zero range paths that might be exercised by Nir's forthcoming patch. With the current codebase of nbdkit we can see that fallocate seems to do the right thing: commandrvf: stdout=e stderr=y flags=0x10000 commandrvf: /bin/sh -c "fallocate -nzl 64k /dev/sda" nbdkit: file.8: debug: zero count=65536 offset=0 may_trim=0 fua=0 nbdkit: file.7: debug: flush but fstrim possibly does not: commandrvf: fstrim -v /sysroot/ nbdkit: file.2: debug: pread count=1024 offset=336896 nbdkit: file.6: debug: pread count=1024 offset=343040 /sysroot/: 91.2 MiB (95629312 bytes) trimmed Another question is whether the Linux loop device supports all these ioctls (and indeed whether the Linux -> qemu -> loop path that libguestfs uses does too). I admit I did not yet look at the kernel and qemu code to find out. An easier way is probably to add debug messages to Nir's patch. (An alternative might have been to use scsi_debug, but that is a rather specialized device.) Rich.
Richard W.M. Jones
2018-Aug-20 12:49 UTC
[Libguestfs] [PATCH nbdkit] tests: Add a root only test of the file plugin with block devices.
You can also run only the root tests using: sudo make check-root --- .gitignore | 1 + Makefile.am | 3 + README | 7 ++ tests/Makefile.am | 10 +- tests/test-file-block.c | 217 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d6686ea..a3d504e 100644 --- a/.gitignore +++ b/.gitignore @@ -69,6 +69,7 @@ Makefile.in /tests/test-exit-with-parent /tests/test-ext2 /tests/test-file +/tests/test-file-block /tests/test-gzip /tests/test-layers /tests/test-lua diff --git a/Makefile.am b/Makefile.am index 0a79e56..c411ed6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,6 +64,9 @@ SUBDIRS += tests check-valgrind: $(MAKE) -C tests check-valgrind +check-root: + $(MAKE) -C tests check-root + # Maintainer only: check no files are missing from EXTRA_DIST rules, # and that all generated files have been included in the tarball. # (Note you must have done 'make dist') diff --git a/README b/README index 473eea8..145cc49 100644 --- a/README +++ b/README @@ -125,6 +125,8 @@ To test for memory leaks (‘make check-valgrind’): For non-essential enhancements to the test suite: + - losetup (from util-linux package) + - qemu-io (usually shipped with qemu) - socat @@ -175,6 +177,11 @@ nbdkit + plugins as a captive process, and tests them using libguestfs. If there is a failure, look at the corresponding ‘tests/*.log’ file for debug information. +A few tests require root privileges, and are skipped by default. To +run them you must do: + + sudo make check-root + DOWNLOAD TARBALLS ================ diff --git a/tests/Makefile.am b/tests/Makefile.am index ce383c2..e0ae6b3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -95,6 +95,10 @@ EXTRA_DIST = \ check-valgrind: NBDKIT_VALGRIND=1 $(MAKE) check +# To run only tests which require root, use: +check-root: + $(MAKE) check TESTS="test-file-block" + #---------------------------------------------------------------------- # Basic server command line and start-up tests. @@ -307,12 +311,16 @@ endif HAVE_GUESTFISH endif HAVE_EXT2 # file plugin test. -LIBGUESTFS_TESTS += test-file +LIBGUESTFS_TESTS += test-file test-file-block test_file_SOURCES = test-file.c test.h test_file_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS) test_file_LDADD = libtest.la $(LIBGUESTFS_LIBS) +test_file_block_SOURCES = test-file-block.c test.h +test_file_block_CFLAGS = $(WARNINGS_CFLAGS) $(LIBGUESTFS_CFLAGS) +test_file_block_LDADD = libtest.la $(LIBGUESTFS_LIBS) + # gzip plugin test. if HAVE_ZLIB if HAVE_GUESTFISH diff --git a/tests/test-file-block.c b/tests/test-file-block.c new file mode 100644 index 0000000..75c2c61 --- /dev/null +++ b/tests/test-file-block.c @@ -0,0 +1,217 @@ +/* nbdkit + * Copyright (C) 2013-2018 Red Hat Inc. + * All rights reserved. + * + * 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 <unistd.h> +#include <sys/types.h> + +#include <guestfs.h> + +#include "test.h" + +static char *loopdev; /* Name of the loop device. */ +static void detach_loopdev (void); + +int +main (int argc, char *argv[]) +{ + guestfs_h *g; + int r; + int fd; + char cmd[64], buf[64]; + char disk[] = "/tmp/diskXXXXXX"; /* Backing disk. */ + FILE *pp; + char *data; + size_t len; + char *s; + + /* This test can only be run as root, and will be skipped otherwise. */ + if (geteuid () != 0) { + fprintf (stderr, "%s: this test has to be run as root.\n", + program_name); + exit (77); + } + + /* losetup must be available. */ + r = system ("losetup --version"); + if (r != 0) { + fprintf (stderr, "%s: losetup program must be installed.\n", + program_name); + exit (77); + } + + /* Create the temporary backing disk. */ + fd = mkstemp (disk); + if (fd == -1) { + perror ("mkstemp"); + exit (EXIT_FAILURE); + } + if (ftruncate (fd, 100 * 1024 * 1024) == -1) { + perror ("ftruncate"); + unlink (disk); + exit (EXIT_FAILURE); + } + + /* Create the loopback device. */ + snprintf (cmd, sizeof cmd, "losetup -f --show %s", disk); + pp = popen (cmd, "r"); + if (pp == NULL) { + perror ("popen: losetup"); + unlink (disk); + exit (EXIT_FAILURE); + } + if (fgets (buf, sizeof buf, pp) == NULL) { + fprintf (stderr, "%s: could not read loop device name from losetup\n", + program_name); + unlink (disk); + exit (EXIT_FAILURE); + } + len = strlen (buf); + if (len > 0 && buf[len-1] == '\n') { + buf[len-1] = '\0'; + len--; + } + pclose (pp); + + /* We can delete the backing disk. The loop device will hold it open. */ + unlink (disk); + + /* If we get to this point, set up an atexit handler to detach the + * loop device. + */ + loopdev = malloc (len+1); + if (loopdev == NULL) { + perror ("malloc"); + exit (EXIT_FAILURE); + } + strcpy (loopdev, buf); + atexit (detach_loopdev); + + /* Start nbdkit. */ + snprintf (buf, sizeof buf, "file=%s", loopdev); + if (test_start_nbdkit ("file", buf, NULL) == -1) + exit (EXIT_FAILURE); + + g = guestfs_create (); + if (g == NULL) { + perror ("guestfs_create"); + exit (EXIT_FAILURE); + } + + r = guestfs_add_drive_opts (g, "", + GUESTFS_ADD_DRIVE_OPTS_FORMAT, "raw", + GUESTFS_ADD_DRIVE_OPTS_PROTOCOL, "nbd", + GUESTFS_ADD_DRIVE_OPTS_SERVER, server, + -1); + if (r == -1) + exit (EXIT_FAILURE); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + /* Partition the disk. */ + if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1) + exit (EXIT_FAILURE); + if (guestfs_mkfs (g, "ext4", "/dev/sda1") == -1) + exit (EXIT_FAILURE); + + if (guestfs_mount (g, "/dev/sda1", "/") == -1) + exit (EXIT_FAILURE); + +#define filename "/hello.txt" +#define content "hello, people of the world" + + if (guestfs_write (g, filename, content, strlen (content)) == -1) + exit (EXIT_FAILURE); + + data = guestfs_cat (g, filename); + if (!data) + exit (EXIT_FAILURE); + + if (strcmp (data, content) != 0) { + fprintf (stderr, "%s FAILED: unexpected content of %s file (actual: %s, expected: %s)\n", + program_name, filename, data, content); + exit (EXIT_FAILURE); + } + + /* Run sync to test flush path. */ + if (guestfs_sync (g) == -1) + exit (EXIT_FAILURE); + + /* Run fstrim to test trim path. However only recent versions of + * libguestfs have this, and it probably only works in recent + * versions of qemu. + */ +#ifdef GUESTFS_HAVE_FSTRIM + if (guestfs_fstrim (g, "/", -1) == -1) + exit (EXIT_FAILURE); +#endif + + /* Run fallocate(1) on the device to test zero path. */ + if (guestfs_umount (g, "/") == -1) + exit (EXIT_FAILURE); + const char *sh[] = { "fallocate", "-nzl", "64k", "/dev/sda", NULL }; + s = guestfs_debug (g, "sh", (char **) sh); + free (s); + + if (guestfs_shutdown (g) == -1) + exit (EXIT_FAILURE); + + guestfs_close (g); + + /* The atexit handler should detach the loop device and clean up + * the backing disk. + */ + exit (EXIT_SUCCESS); +} + +/* atexit handler. */ +static void +detach_loopdev (void) +{ + char cmd[64]; + + if (loopdev == NULL) + return; + + snprintf (cmd, sizeof cmd, "losetup -d %s", loopdev); + system (cmd); + free (loopdev); + loopdev = NULL; +} -- 2.18.0