diff --git a/tests/sys/fs/tarfs/mktar.c b/tests/sys/fs/tarfs/mktar.c index 9b3d7910a12c..4e7d1acc1c82 100644 --- a/tests/sys/fs/tarfs/mktar.c +++ b/tests/sys/fs/tarfs/mktar.c @@ -1,275 +1,280 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Klara, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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 #include #include #include #include #include #include #include #include #include #include #define PROGNAME "mktar" #define SUBDIRNAME "directory" #define EMPTYDIRNAME "empty" #define NORMALFILENAME "file" #define SPARSEFILENAME "sparse_file" #define HARDLINKNAME "hard_link" #define SHORTLINKNAME "short_link" #define LONGLINKNAME "long_link" +static bool opt_g; static bool opt_v; static void verbose(const char *fmt, ...) { va_list ap; if (!opt_v) return; fprintf(stderr, "%s: ", PROGNAME); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } static void mknormalfile(const char *filename, mode_t mode) { char buf[512]; ssize_t res; int fd; if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) err(1, "%s", filename); for (unsigned int i = 0; i < sizeof(buf); i++) buf[i] = 32 + i % 64; res = write(fd, buf, sizeof(buf)); if (res < 0) err(1, "%s", filename); if (res != sizeof(buf)) errx(1, "%s: short write", filename); close(fd); } static void mksparsefile(const char *filename, mode_t mode) { char buf[511]; ssize_t res; int fd; if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) err(1, "%s", filename); for (unsigned int i = 33; i <= 126; i++) { memset(buf, i, sizeof(buf)); if (lseek(fd, 1048576LU * (i - 32), SEEK_SET) < 0) err(1, "%s", filename); res = write(fd, buf, sizeof(buf)); if (res < 0) err(1, "%s", filename); if (res != sizeof(buf)) errx(1, "%s: short write", filename); } close(fd); } static char * mklonglinktarget(const char *dirname, const char *filename) { char *piece, *target; if (asprintf(&piece, "%1$s/../%1$s/../%1$s/../%1$s/../", dirname) < 0) err(1, "asprintf()"); if (asprintf(&target, "%1$s%1$s%1$s%1$s%1$s%1$s%1$s%1$s%2$s", piece, filename) < 0) err(1, "asprintf()"); free(piece); return target; } static void mktar(void) { char *linktarget; /* create a subdirectory */ verbose("mkdir %s", SUBDIRNAME); if (mkdir(SUBDIRNAME, 0755) != 0) err(1, "%s", SUBDIRNAME); /* create a second subdirectory which will remain empty */ verbose("mkdir %s", EMPTYDIRNAME); if (mkdir(EMPTYDIRNAME, 0755) != 0) err(1, "%s", EMPTYDIRNAME); /* create a normal file */ verbose("creating %s", NORMALFILENAME); mknormalfile(NORMALFILENAME, 0644); /* create a sparse file */ verbose("creating %s", SPARSEFILENAME); mksparsefile(SPARSEFILENAME, 0644); chflags(SPARSEFILENAME, UF_NODUMP); /* create a hard link */ verbose("link %s %s", SPARSEFILENAME, HARDLINKNAME); if (link(SPARSEFILENAME, HARDLINKNAME) != 0) err(1, "%s", HARDLINKNAME); /* create a symbolic link with a short target */ verbose("symlink %s %s", SPARSEFILENAME, SHORTLINKNAME); if (symlink(SPARSEFILENAME, SHORTLINKNAME) != 0) err(1, "%s", SHORTLINKNAME); /* create a symbolic link with a long target */ linktarget = mklonglinktarget(SUBDIRNAME, SPARSEFILENAME); verbose("symlink %s %s", linktarget, LONGLINKNAME); if (symlink(linktarget, LONGLINKNAME) != 0) err(1, "%s", LONGLINKNAME); free(linktarget); } static void usage(void) { - fprintf(stderr, "usage: %s [-v] tarfile\n", PROGNAME); + fprintf(stderr, "usage: %s [-gv] tarfile\n", PROGNAME); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { const char *tarfilename; char *dirname; int opt, wstatus; pid_t pid; - while ((opt = getopt(argc, argv, "v")) != -1) + while ((opt = getopt(argc, argv, "gv")) != -1) switch (opt) { + case 'g': + opt_g = true; case 'v': opt_v = true; break; default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); tarfilename = *argv; if (asprintf(&dirname, "%s%s.XXXXXXXX", _PATH_TMP, PROGNAME) < 0) err(1, "asprintf()"); if (mkdtemp(dirname) == NULL) err(1, "%s", dirname); verbose("mkdir %s", dirname); /* fork a child to create the files */ if ((pid = fork()) < 0) err(1, "fork()"); if (pid == 0) { verbose("cd %s", dirname); if (chdir(dirname) != 0) err(1, "%s", dirname); verbose("umask 022"); umask(022); mktar(); verbose("cd -"); exit(0); } if (waitpid(pid, &wstatus, 0) < 0) err(1, "waitpid()"); if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) errx(1, "child failed"); /* fork a child to create the tarball */ if ((pid = fork()) < 0) err(1, "fork()"); if (pid == 0) { verbose("creating tarball"); - execlp("tar", "tar", + execlp(opt_g ? "gtar" : "tar", + "tar", "-c", "-f", tarfilename, "-C", dirname, + "--posix", "--zstd", #if 0 "--options", "zstd:frame-per-file", #endif "./" EMPTYDIRNAME "/../" NORMALFILENAME, "./" SPARSEFILENAME, "./" HARDLINKNAME, "./" SHORTLINKNAME, "./" SUBDIRNAME, "./" LONGLINKNAME, NULL); err(1, "execlp()"); } if (waitpid(pid, &wstatus, 0) < 0) err(1, "waitpid()"); if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) errx(1, "child failed"); /* fork a child to delete everything */ if ((pid = fork()) < 0) err(1, "fork()"); if (pid == 0) { verbose("cd %s", dirname); if (chdir(dirname) != 0) err(1, "%s", dirname); verbose("rm %s", LONGLINKNAME); (void)unlink(LONGLINKNAME); verbose("rm %s", SHORTLINKNAME); (void)unlink(SHORTLINKNAME); verbose("rm %s", HARDLINKNAME); (void)unlink(HARDLINKNAME); verbose("rm %s", SPARSEFILENAME); (void)unlink(SPARSEFILENAME); verbose("rmdir %s", EMPTYDIRNAME); (void)rmdir(EMPTYDIRNAME); verbose("rmdir %s", SUBDIRNAME); (void)rmdir(SUBDIRNAME); verbose("cd -"); exit(0); } if (waitpid(pid, &wstatus, 0) < 0) err(1, "waitpid()"); if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) errx(1, "child failed"); verbose("rmdir %s", dirname); (void)rmdir(dirname); exit(0); } diff --git a/tests/sys/fs/tarfs/tarfs_test.sh b/tests/sys/fs/tarfs/tarfs_test.sh index 32576cbf57b6..6e44a8081cb2 100644 --- a/tests/sys/fs/tarfs/tarfs_test.sh +++ b/tests/sys/fs/tarfs/tarfs_test.sh @@ -1,146 +1,232 @@ #!/bin/sh #- # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2023 Klara, Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. 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. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR 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. # -mktar="$(dirname $(realpath "$0"))"/mktar mnt="$(realpath ${TMPDIR:-/tmp})/mnt" # expected SHA256 checksum of file contained in test tarball sum=4da2143234486307bb44eaa610375301781a577d1172f362b88bb4b1643dee62 +tar() { + if [ -n "${TARFS_USE_GNU_TAR}" ] ; then + gtar --posix --absolute-names "$@" + else + bsdtar "$@" + fi +} + +mktar() { + "$(atf_get_srcdir)"/mktar ${TARFS_USE_GNU_TAR+-g} "$@" +} + atf_test_case tarfs_basic cleanup tarfs_basic_head() { atf_set "descr" "Basic function test" atf_set "require.user" "root" } tarfs_basic_body() { local tarball="${PWD}/tarfs_test.tar.zst" kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" mkdir "${mnt}" - "${mktar}" "${tarball}" + mktar "${tarball}" atf_check mount -rt tarfs "${tarball}" "${mnt}" atf_check -o match:"^${tarball} on ${mnt} \(tarfs," mount atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -f%d,%i "${mnt}"/hard_link)" atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/short_link)" atf_check_equal "$(stat -f%d,%i "${mnt}"/sparse_file)" "$(stat -L -f%d,%i "${mnt}"/long_link)" atf_check_equal "$(sha256 -q "${mnt}"/sparse_file)" ${sum} } tarfs_basic_cleanup() { umount "${mnt}" || true } +atf_test_case tarfs_basic_gnu cleanup +tarfs_basic_gnu_head() { + atf_set "descr" "Basic function test using GNU tar" + atf_set "require.user" "root" + atf_set "require.progs" "gtar" +} +tarfs_basic_gnu_body() { + TARFS_USE_GNU_TAR=true + tarfs_basic_body +} +tarfs_basic_gnu_cleanup() { + tarfs_basic_cleanup +} + atf_test_case tarfs_notdir_device cleanup tarfs_notdir_device_head() { atf_set "descr" "Regression test for PR 269519 and 269561" atf_set "require.user" "root" } tarfs_notdir_device_body() { kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" mkdir "${mnt}" atf_check mknod d b 0xdead 0xbeef - tar cf tarfs_notdir.tar d + tar -cf tarfs_notdir.tar d rm d mkdir d echo "boom" >d/f - tar rf tarfs_notdir.tar d/f + tar -rf tarfs_notdir.tar d/f atf_check -s not-exit:0 -e match:"Invalid" \ mount -rt tarfs tarfs_notdir.tar "${mnt}" } tarfs_notdir_device_cleanup() { umount "${mnt}" || true } +atf_test_case tarfs_notdir_device_gnu cleanup +tarfs_notdir_device_gnu_head() { + atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" + atf_set "require.user" "root" + atf_set "require.progs" "gtar" +} +tarfs_notdir_device_gnu_body() { + TARFS_USE_GNU_TAR=true + tarfs_notdir_device_body +} +tarfs_notdir_device_gnu_cleanup() { + tarfs_notdir_device_cleanup +} + atf_test_case tarfs_notdir_dot cleanup tarfs_notdir_dot_head() { atf_set "descr" "Regression test for PR 269519 and 269561" atf_set "require.user" "root" } tarfs_notdir_dot_body() { kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" mkdir "${mnt}" echo "hello" >d - tar cf tarfs_notdir.tar d + tar -cf tarfs_notdir.tar d rm d mkdir d echo "world" >d/f - tar rf tarfs_notdir.tar d/./f + tar -rf tarfs_notdir.tar d/./f atf_check -s not-exit:0 -e match:"Invalid" \ mount -rt tarfs tarfs_notdir.tar "${mnt}" } tarfs_notdir_dot_cleanup() { umount "${mnt}" || true } +atf_test_case tarfs_notdir_dot_gnu cleanup +tarfs_notdir_dot_gnu_head() { + atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" + atf_set "require.user" "root" + atf_set "require.progs" "gtar" +} +tarfs_notdir_dot_gnu_body() { + TARFS_USE_GNU_TAR=true + tarfs_notdir_dot_body +} +tarfs_notdir_dot_gnu_cleanup() { + tarfs_notdir_dot_cleanup +} + atf_test_case tarfs_notdir_dotdot cleanup tarfs_notdir_dotdot_head() { atf_set "descr" "Regression test for PR 269519 and 269561" atf_set "require.user" "root" } tarfs_notdir_dotdot_body() { kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" mkdir "${mnt}" echo "hello" >d - tar cf tarfs_notdir.tar d + tar -cf tarfs_notdir.tar d rm d mkdir d echo "world" >f - tar rf tarfs_notdir.tar d/../f + tar -rf tarfs_notdir.tar d/../f atf_check -s not-exit:0 -e match:"Invalid" \ mount -rt tarfs tarfs_notdir.tar "${mnt}" } tarfs_notdir_dotdot_cleanup() { umount "${mnt}" || true } +atf_test_case tarfs_notdir_dotdot_gnu cleanup +tarfs_notdir_dotdot_gnu_head() { + atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" + atf_set "require.user" "root" + atf_set "require.progs" "gtar" +} +tarfs_notdir_dotdot_gnu_body() { + TARFS_USE_GNU_TAR=true + tarfs_notdir_dotdot_body +} +tarfs_notdir_dotdot_gnu_cleanup() { + tarfs_notdir_dotdot_cleanup +} + atf_test_case tarfs_notdir_file cleanup tarfs_notdir_file_head() { atf_set "descr" "Regression test for PR 269519 and 269561" atf_set "require.user" "root" } tarfs_notdir_file_body() { kldload -n tarfs || atf_skip "This test requires tarfs and could not load it" mkdir "${mnt}" echo "hello" >d - tar cf tarfs_notdir.tar d + tar -cf tarfs_notdir.tar d rm d mkdir d echo "world" >d/f - tar rf tarfs_notdir.tar d/f + tar -rf tarfs_notdir.tar d/f atf_check -s not-exit:0 -e match:"Invalid" \ mount -rt tarfs tarfs_notdir.tar "${mnt}" } tarfs_notdir_file_cleanup() { umount "${mnt}" || true } +atf_test_case tarfs_notdir_file_gnu cleanup +tarfs_notdir_file_gnu_head() { + atf_set "descr" "Regression test for PR 269519 and 269561 using GNU tar" + atf_set "require.user" "root" + atf_set "require.progs" "gtar" +} +tarfs_notdir_file_gnu_body() { + TARFS_USE_GNU_TAR=true + tarfs_notdir_file_body +} +tarfs_notdir_file_gnu_cleanup() { + tarfs_notdir_file_cleanup +} + atf_init_test_cases() { atf_add_test_case tarfs_basic + atf_add_test_case tarfs_basic_gnu atf_add_test_case tarfs_notdir_device + atf_add_test_case tarfs_notdir_device_gnu atf_add_test_case tarfs_notdir_dot + atf_add_test_case tarfs_notdir_dot_gnu atf_add_test_case tarfs_notdir_dotdot + atf_add_test_case tarfs_notdir_dotdot_gnu atf_add_test_case tarfs_notdir_file + atf_add_test_case tarfs_notdir_file_gnu }