diff options
Diffstat (limited to 'fs/vfs')
-rw-r--r-- | fs/vfs/Makefile | 47 | ||||
-rw-r--r-- | fs/vfs/t_full.c | 101 | ||||
-rw-r--r-- | fs/vfs/t_io.c | 250 | ||||
-rw-r--r-- | fs/vfs/t_renamerace.c | 190 | ||||
-rw-r--r-- | fs/vfs/t_rmdirrace.c | 106 | ||||
-rw-r--r-- | fs/vfs/t_ro.c | 203 | ||||
-rw-r--r-- | fs/vfs/t_union.c | 204 | ||||
-rw-r--r-- | fs/vfs/t_unpriv.c | 241 | ||||
-rw-r--r-- | fs/vfs/t_vfsops.c | 211 | ||||
-rw-r--r-- | fs/vfs/t_vnops.c | 1001 |
10 files changed, 2554 insertions, 0 deletions
diff --git a/fs/vfs/Makefile b/fs/vfs/Makefile new file mode 100644 index 000000000000..13f4413ce75c --- /dev/null +++ b/fs/vfs/Makefile @@ -0,0 +1,47 @@ +# $NetBSD: Makefile,v 1.19 2013/06/28 13:04:06 reinoud Exp $ +# + +.include <bsd.own.mk> + +TESTSDIR= ${TESTSBASE}/fs/vfs +WARNS= 4 + +TESTS_C+= t_full +TESTS_C+= t_io +TESTS_C+= t_renamerace +TESTS_C+= t_ro +TESTS_C+= t_rmdirrace +TESTS_C+= t_union +TESTS_C+= t_unpriv +TESTS_C+= t_vfsops +TESTS_C+= t_vnops + +LDADD+=-lrumpfs_ext2fs # ext2fs +LDADD+=-lrumpfs_ffs # ffs +LDADD+=-lrumpfs_lfs # lfs +LDADD+=-lrumpfs_msdos # msdos +LDADD+=-lrumpfs_syspuffs # puffs +LDADD+=-lrumpdev_putter -lrumpdev # \ putter +LDADD+=-lrumpfs_sysvbfs # sysvbfs +LDADD+=-lrumpfs_tmpfs # tmpfs +LDADD+=-lrumpfs_udf # udf +LDADD+=-lrumpfs_union # union +LDADD+=-lrumpfs_v7fs # v7fs +LDADD+=-lrumpdev_disk -lrumpdev # disk device + + +VFSTESTDIR != cd ${.CURDIR}/../common && ${PRINTOBJDIR} +LDADD+=-L${VFSTESTDIR} -lvfstest + +LDADD+=-lrumpfs_nfs # NFS +LDADD+=-lrumpnet_shmif -lrumpnet_netinet -lrumpnet_net -lrumpnet +LDADD+=-lrumpvfs -lrump -lrumpuser -lpthread # base + +LDADD+=-lutil + +.if (${MKZFS} != "no") +LDADD+=-lrumpfs_zfs -lrumpkern_solaris -lrumpdev_rnd +CPPFLAGS+=-DWANT_ZFS_TESTS +.endif + +.include <bsd.test.mk> diff --git a/fs/vfs/t_full.c b/fs/vfs/t_full.c new file mode 100644 index 000000000000..f338a0221f0a --- /dev/null +++ b/fs/vfs/t_full.c @@ -0,0 +1,101 @@ +/* $NetBSD: t_full.c,v 1.8 2013/03/16 05:45:37 jmmv Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/statvfs.h> + +#include <atf-c.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +/* + * Write this much over the image size. This is to force an NFS commit, + * since we might just stuff data into the cache and miss the problem. + */ +#define NFSBONUS (1<<16) + +static void +fillfs(const atf_tc_t *tc, const char *mp) +{ + char buf[8192]; + size_t written; + ssize_t n = 0; /* xxxgcc */ + size_t bonus; + int fd, i = 0; + + if (FSTYPE_P2K_FFS(tc) || FSTYPE_PUFFS(tc) || FSTYPE_RUMPFS(tc)) { + atf_tc_skip("fs does not support explicit block allocation " + "(GOP_ALLOC)"); + } + + bonus = 0; + if (FSTYPE_NFS(tc)) + bonus = NFSBONUS; + + if (rump_sys_chdir(mp) == -1) + atf_tc_fail_errno("chdir mountpoint"); + fd = rump_sys_open("afile", O_CREAT | O_RDWR); + if (fd == -1) + atf_tc_fail_errno("create file"); + + for (written = 0; written < FSTEST_IMGSIZE + bonus; written +=n) { + memset(buf, i++, sizeof(buf)); /* known garbage */ + n = rump_sys_write(fd, buf, sizeof(buf)); + if (n == -1) + break; + } + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (n == -1) { + if (errno != ENOSPC) + atf_tc_fail_errno("write"); + } else { + atf_tc_fail("filled file system over size limit"); + } + + rump_sys_close(fd); + rump_sys_chdir("/"); +} + +ATF_TC_FSAPPLY(fillfs, "fills file system, expects ENOSPC"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(fillfs); + + return atf_no_error(); +} diff --git a/fs/vfs/t_io.c b/fs/vfs/t_io.c new file mode 100644 index 000000000000..67d865706ee8 --- /dev/null +++ b/fs/vfs/t_io.c @@ -0,0 +1,250 @@ +/* $NetBSD: t_io.c,v 1.12 2013/08/04 11:02:02 pooka Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/statvfs.h> + +#include <atf-c.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +#define TESTSTR "this is a string. collect enough and you'll have Em" +#define TESTSZ sizeof(TESTSTR) + +static void +holywrite(const atf_tc_t *tc, const char *mp) +{ + char buf[1024]; + char *b2, *b3; + size_t therange = getpagesize()+1; + int fd; + + FSTEST_ENTER(); + + RL(fd = rump_sys_open("file", O_RDWR|O_CREAT|O_TRUNC, 0666)); + + memset(buf, 'A', sizeof(buf)); + RL(rump_sys_pwrite(fd, buf, 1, getpagesize())); + + memset(buf, 'B', sizeof(buf)); + RL(rump_sys_pwrite(fd, buf, 2, getpagesize()-1)); + + REQUIRE_LIBC(b2 = malloc(2 * getpagesize()), NULL); + REQUIRE_LIBC(b3 = malloc(2 * getpagesize()), NULL); + + RL(rump_sys_pread(fd, b2, therange, 0)); + + memset(b3, 0, therange); + memset(b3 + getpagesize() - 1, 'B', 2); + + ATF_REQUIRE_EQ(memcmp(b2, b3, therange), 0); + + rump_sys_close(fd); + FSTEST_EXIT(); +} + +static void +extendbody(const atf_tc_t *tc, off_t seekcnt) +{ + char buf[TESTSZ+1]; + struct stat sb; + int fd; + + FSTEST_ENTER(); + RL(fd = rump_sys_open("testfile", + O_CREAT | O_RDWR | (seekcnt ? O_APPEND : 0))); + RL(rump_sys_ftruncate(fd, seekcnt)); + RL(rump_sys_fstat(fd, &sb)); + ATF_REQUIRE_EQ(sb.st_size, seekcnt); + + ATF_REQUIRE_EQ(rump_sys_write(fd, TESTSTR, TESTSZ), TESTSZ); + ATF_REQUIRE_EQ(rump_sys_pread(fd, buf, TESTSZ, seekcnt), TESTSZ); + ATF_REQUIRE_STREQ(buf, TESTSTR); + + RL(rump_sys_fstat(fd, &sb)); + ATF_REQUIRE_EQ(sb.st_size, (off_t)TESTSZ + seekcnt); + RL(rump_sys_close(fd)); + FSTEST_EXIT(); +} + +static void +extendfile(const atf_tc_t *tc, const char *mp) +{ + + extendbody(tc, 0); +} + +static void +extendfile_append(const atf_tc_t *tc, const char *mp) +{ + + extendbody(tc, 37); +} + +static void +overwritebody(const atf_tc_t *tc, off_t count, bool dotrunc) +{ + char *buf; + int fd; + + REQUIRE_LIBC(buf = malloc(count), NULL); + FSTEST_ENTER(); + RL(fd = rump_sys_open("testi", O_CREAT | O_RDWR, 0666)); + ATF_REQUIRE_EQ(rump_sys_write(fd, buf, count), count); + RL(rump_sys_close(fd)); + + RL(fd = rump_sys_open("testi", O_RDWR)); + if (dotrunc) + RL(rump_sys_ftruncate(fd, 0)); + ATF_REQUIRE_EQ(rump_sys_write(fd, buf, count), count); + RL(rump_sys_close(fd)); + FSTEST_EXIT(); +} + +static void +overwrite512(const atf_tc_t *tc, const char *mp) +{ + + overwritebody(tc, 512, false); +} + +static void +overwrite64k(const atf_tc_t *tc, const char *mp) +{ + + overwritebody(tc, 1<<16, false); +} + +static void +overwrite_trunc(const atf_tc_t *tc, const char *mp) +{ + + overwritebody(tc, 1<<16, true); +} + +static void +shrinkfile(const atf_tc_t *tc, const char *mp) +{ + int fd; + + FSTEST_ENTER(); + RL(fd = rump_sys_open("file", O_RDWR|O_CREAT|O_TRUNC, 0666)); + RL(rump_sys_ftruncate(fd, 2)); + RL(rump_sys_ftruncate(fd, 1)); + rump_sys_close(fd); + FSTEST_EXIT(); +} + +#define TBSIZE 9000 +static void +read_after_unlink(const atf_tc_t *tc, const char *mp) +{ + char buf[TBSIZE], buf2[TBSIZE]; + int fd; + + FSTEST_ENTER(); + + /* create file and put some content into it */ + RL(fd = rump_sys_open("file", O_RDWR|O_CREAT, 0666)); + memset(buf, 'D', TBSIZE); + ATF_REQUIRE_EQ(rump_sys_write(fd, buf, TBSIZE), TBSIZE); + rump_sys_close(fd); + + /* flush buffers from UBC to file system */ + ATF_REQUIRE_ERRNO(EBUSY, rump_sys_unmount(mp, 0) == -1); + + RL(fd = rump_sys_open("file", O_RDWR)); + RL(rump_sys_unlink("file")); + + ATF_REQUIRE_EQ(rump_sys_read(fd, buf2, TBSIZE), TBSIZE); + ATF_REQUIRE_EQ(memcmp(buf, buf2, TBSIZE), 0); + rump_sys_close(fd); + + FSTEST_EXIT(); +} + +static void +wrrd_after_unlink(const atf_tc_t *tc, const char *mp) +{ + int value = 0x11; + int v2; + int fd; + + FSTEST_ENTER(); + + RL(fd = rump_sys_open("file", O_RDWR|O_CREAT, 0666)); + RL(rump_sys_unlink("file")); + + RL(rump_sys_pwrite(fd, &value, sizeof(value), 654321)); + + /* + * We can't easily invalidate the buffer since we hold a + * reference, but try to get them to flush anyway. + */ + RL(rump_sys_fsync(fd)); + RL(rump_sys_pread(fd, &v2, sizeof(v2), 654321)); + rump_sys_close(fd); + + ATF_REQUIRE_EQ(value, v2); + FSTEST_EXIT(); +} + +ATF_TC_FSAPPLY(holywrite, "create a sparse file and fill hole"); +ATF_TC_FSAPPLY(extendfile, "check that extending a file works"); +ATF_TC_FSAPPLY(extendfile_append, "check that extending a file works " + "with a append-only fd (PR kern/44307)"); +ATF_TC_FSAPPLY(overwrite512, "write a 512 byte file twice"); +ATF_TC_FSAPPLY(overwrite64k, "write a 64k byte file twice"); +ATF_TC_FSAPPLY(overwrite_trunc, "write 64k + truncate + rewrite"); +ATF_TC_FSAPPLY(shrinkfile, "shrink file"); +ATF_TC_FSAPPLY(read_after_unlink, "contents can be read off disk after unlink"); +ATF_TC_FSAPPLY(wrrd_after_unlink, "file can be written and read after unlink"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(holywrite); + ATF_TP_FSAPPLY(extendfile); + ATF_TP_FSAPPLY(extendfile_append); + ATF_TP_FSAPPLY(overwrite512); + ATF_TP_FSAPPLY(overwrite64k); + ATF_TP_FSAPPLY(overwrite_trunc); + ATF_TP_FSAPPLY(shrinkfile); + ATF_TP_FSAPPLY(read_after_unlink); + ATF_TP_FSAPPLY(wrrd_after_unlink); + + return atf_no_error(); +} diff --git a/fs/vfs/t_renamerace.c b/fs/vfs/t_renamerace.c new file mode 100644 index 000000000000..70e02f3ce107 --- /dev/null +++ b/fs/vfs/t_renamerace.c @@ -0,0 +1,190 @@ +/* $NetBSD: t_renamerace.c,v 1.32 2014/07/29 09:15:48 gson Exp $ */ + +/* + * Modified for rump and atf from a program supplied + * by Nicolas Joly in kern/40948 + */ + +#include <sys/types.h> +#include <sys/mount.h> +#include <sys/utsname.h> + +#include <atf-c.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +/* Bump the size of the test file system image to a larger value. + * + * These tests cause a lot of churn in the file system by creating and + * deleting files/directories in quick succession. A faster CPU will cause + * more churn because the tests are capped by a run time period in seconds, + * not number of operations. + * + * This is all fine except for LFS, because the lfs_cleanerd cannot keep up + * with the churn and thus causes the test to fail on fast machines. Hence + * the reason for this hack. */ +#define FSTEST_IMGSIZE (50000 * 512) + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +static volatile int quittingtime; +pid_t wrkpid; + +static void * +w1(void *arg) +{ + int fd; + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + fd = rump_sys_open("rename.test1", + O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (fd == -1 && errno != EEXIST) + atf_tc_fail_errno("create"); + rump_sys_unlink("rename.test1"); + rump_sys_close(fd); + } + + return NULL; +} + +static void * +w1_dirs(void *arg) +{ + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + if (rump_sys_mkdir("rename.test1", 0777) == -1) + atf_tc_fail_errno("mkdir"); + rump_sys_rmdir("rename.test1"); + } + + return NULL; +} + +static void * +w2(void *arg) +{ + + rump_pub_lwproc_newlwp(wrkpid); + + while (!quittingtime) { + rump_sys_rename("rename.test1", "rename.test2"); + } + + return NULL; +} + +#define NWRK 8 +static void +renamerace(const atf_tc_t *tc, const char *mp) +{ + pthread_t pt1[NWRK], pt2[NWRK]; + int i; + + /* + * Sysvbfs supports only 8 inodes so this test would exhaust + * the inode table and creating files would fail with ENOSPC. + */ + if (FSTYPE_SYSVBFS(tc)) + atf_tc_skip("filesystem has not enough inodes"); + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + if (FSTYPE_UDF(tc)) + atf_tc_expect_fail("PR kern/49046"); + + RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); + RL(wrkpid = rump_sys_getpid()); + + RL(rump_sys_chdir(mp)); + for (i = 0; i < NWRK; i++) + pthread_create(&pt1[i], NULL, w1, NULL); + + for (i = 0; i < NWRK; i++) + pthread_create(&pt2[i], NULL, w2, NULL); + + sleep(5); + quittingtime = 1; + + for (i = 0; i < NWRK; i++) + pthread_join(pt1[i], NULL); + for (i = 0; i < NWRK; i++) + pthread_join(pt2[i], NULL); + RL(rump_sys_chdir("/")); + + if (FSTYPE_UDF(tc)) + atf_tc_fail("race did not trigger this time"); + + if (FSTYPE_MSDOS(tc)) { + atf_tc_expect_fail("PR kern/44661"); + /* + * XXX: race does not trigger every time at least + * on amd64/qemu. + */ + if (msdosfs_fstest_unmount(tc, mp, 0) != 0) { + rump_pub_vfs_mount_print(mp, 1); + atf_tc_fail_errno("unmount failed"); + } + atf_tc_fail("race did not trigger this time"); + } +} + +static void +renamerace_dirs(const atf_tc_t *tc, const char *mp) +{ + pthread_t pt1, pt2; + + if (FSTYPE_SYSVBFS(tc)) + atf_tc_skip("directories not supported by file system"); + + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + + /* XXX: msdosfs also sometimes hangs */ + if (FSTYPE_MSDOS(tc)) + atf_tc_expect_signal(-1, "PR kern/43626"); + + RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); + RL(wrkpid = rump_sys_getpid()); + + RL(rump_sys_chdir(mp)); + pthread_create(&pt1, NULL, w1_dirs, NULL); + pthread_create(&pt2, NULL, w2, NULL); + + sleep(5); + quittingtime = 1; + + pthread_join(pt1, NULL); + pthread_join(pt2, NULL); + RL(rump_sys_chdir("/")); + + /* + * Doesn't always trigger when run on a slow backend + * (i.e. not on tmpfs/mfs). So do the usual kludge. + */ + if (FSTYPE_MSDOS(tc)) + abort(); +} + +ATF_TC_FSAPPLY(renamerace, "rename(2) race with file unlinked mid-operation"); +ATF_TC_FSAPPLY(renamerace_dirs, "rename(2) race with directories"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(renamerace); /* PR kern/41128 */ + ATF_TP_FSAPPLY(renamerace_dirs); + + return atf_no_error(); +} diff --git a/fs/vfs/t_rmdirrace.c b/fs/vfs/t_rmdirrace.c new file mode 100644 index 000000000000..839dbad73352 --- /dev/null +++ b/fs/vfs/t_rmdirrace.c @@ -0,0 +1,106 @@ +/* $NetBSD: t_rmdirrace.c,v 1.9 2012/02/16 02:47:56 perseant Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nicolas Joly. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> + +#include <atf-c.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" + +#define DIRNAME "rmdir.test" + +static void *func1(void *arg) +{ + + while (*(int *)arg != 1) + rump_sys_mkdir(DIRNAME, 0755); + + return NULL; +} + +static void *func2(void *arg) +{ + + while (*(int *)arg != 1) + rump_sys_rmdir(DIRNAME); + + return NULL; +} + +static void +race(const atf_tc_t *tc, const char *path) +{ + int res, fd, quit; + pthread_t th1, th2; + + if (FSTYPE_SYSVBFS(tc)) + atf_tc_skip("directories not supported by file system"); + + fd = rump_sys_open(".", O_RDONLY, 0666); + if (fd == -1) + atf_tc_fail("open failed"); + res = rump_sys_chdir(path); + if (res == -1) + atf_tc_fail("chdir failed"); + + quit = 0; + + res = pthread_create(&th1, NULL, func1, &quit); + if (res != 0) + atf_tc_fail("pthread_create1 failed"); + res = pthread_create(&th2, NULL, func2, &quit); + if (res != 0) + atf_tc_fail("pthread_create2 failed"); + + sleep(10); + + quit = 1; + + res = pthread_join(th2, NULL); + if (res != 0) + atf_tc_fail("pthread_join2 failed"); + res = pthread_join(th1, NULL); + if (res != 0) + atf_tc_fail("pthread_join1 failed"); + + res = rump_sys_fchdir(fd); + if (res == -1) + atf_tc_fail("fchdir failed"); +} + +ATF_FSAPPLY(race, "rmdir(2) race"); diff --git a/fs/vfs/t_ro.c b/fs/vfs/t_ro.c new file mode 100644 index 000000000000..b1e1179956b0 --- /dev/null +++ b/fs/vfs/t_ro.c @@ -0,0 +1,203 @@ +/* $NetBSD: t_ro.c,v 1.5 2011/02/22 21:23:19 yamt Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/statvfs.h> + +#include <atf-c.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +#define AFILE "testfile" +#define ADIR "testdir" +#define AFIFO "testfifo" +#define ASYMLINK "testsymlink" +#define ALINK "testlink" +#define FUNTEXT "this is some non-humppa text" +#define FUNSIZE (sizeof(FUNTEXT)-1) + +static void +nullgen(const atf_tc_t *tc, const char *mp) +{ + + return; +} + +static void +filegen(const atf_tc_t *tc, const char *mp) +{ + int fd; + + FSTEST_ENTER(); + RL(fd = rump_sys_open(AFILE, O_CREAT | O_RDWR, 0777)); + ATF_REQUIRE_EQ(rump_sys_write(fd, FUNTEXT, FUNSIZE), FUNSIZE); + RL(rump_sys_close(fd)); + FSTEST_EXIT(); +} + +/* + * + * BEGIN tests + * + */ + +static void +create(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_open(AFILE, O_CREAT|O_RDONLY) == -1); + FSTEST_EXIT(); +} + +static void +rmfile(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_unlink(AFILE) == -1); + FSTEST_EXIT(); +} + +static void +fileio(const atf_tc_t *tc, const char *mp) +{ + int fd; + char buf[FUNSIZE+1]; + int expected; + + if (FSTYPE_NFSRO(tc)) + expected = EACCES; + else + expected = EROFS; + + FSTEST_ENTER(); + RL(fd = rump_sys_open(AFILE, O_RDONLY)); + ATF_REQUIRE_EQ(rump_sys_read(fd, buf, FUNSIZE), FUNSIZE); + buf[FUNSIZE] = '\0'; + ATF_REQUIRE_STREQ(buf, FUNTEXT); + RL(rump_sys_close(fd)); + + ATF_REQUIRE_ERRNO(expected, rump_sys_open(AFILE, O_WRONLY) == -1); + ATF_REQUIRE_ERRNO(expected, rump_sys_open(AFILE, O_RDWR) == -1); + FSTEST_EXIT(); +} + +static void +attrs(const atf_tc_t *tc, const char *mp) +{ + struct timeval sometvs[2]; + struct stat sb; + int fd; + + FSTEST_ENTER(); + + RL(rump_sys_stat(AFILE, &sb)); + + ATF_REQUIRE_ERRNO(EROFS, rump_sys_chmod(AFILE, 0775) == -1); + if (!FSTYPE_MSDOS(tc)) + ATF_REQUIRE_ERRNO(EROFS, rump_sys_chown(AFILE, 1, 1) == -1); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_utimes(AFILE, sometvs) == -1); + + RL(fd = rump_sys_open(AFILE, O_RDONLY)); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_fchmod(fd, 0775) == -1); + if (!FSTYPE_MSDOS(tc)) + ATF_REQUIRE_ERRNO(EROFS, rump_sys_fchown(fd, 1, 1) == -1); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_futimes(fd, sometvs) == -1); + RL(rump_sys_close(fd)); + + FSTEST_EXIT(); +} + +static void +createdir(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_mkdir(ADIR, 0775) == -1); + FSTEST_EXIT(); +} + +static void +createfifo(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_mkfifo(AFIFO, 0775) == -1); + FSTEST_EXIT(); +} + +static void +createsymlink(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_symlink("hoge", ASYMLINK) == -1); + FSTEST_EXIT(); +} + +static void +createlink(const atf_tc_t *tc, const char *mp) +{ + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(EROFS, rump_sys_link(AFILE, ALINK) == -1); + FSTEST_EXIT(); +} + +ATF_TC_FSAPPLY_RO(create, "create file on r/o mount", nullgen); +ATF_TC_FSAPPLY_RO(rmfile, "remove file from r/o mount", filegen); +ATF_TC_FSAPPLY_RO(fileio, "can read a file but not write it", filegen); +ATF_TC_FSAPPLY_RO(attrs, "can query but not change attributes", filegen); +ATF_TC_FSAPPLY_RO(createdir, "create directory on r/o mount", nullgen); +ATF_TC_FSAPPLY_RO(createfifo, "create fifo on r/o mount", nullgen); +ATF_TC_FSAPPLY_RO(createsymlink, "create symlink on r/o mount", nullgen); +ATF_TC_FSAPPLY_RO(createlink, "create hardlink on r/o mount", filegen); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY_RO(create); + ATF_TP_FSAPPLY_RO(rmfile); + ATF_TP_FSAPPLY_RO(fileio); + ATF_TP_FSAPPLY_RO(attrs); + ATF_TP_FSAPPLY_RO(createdir); + ATF_TP_FSAPPLY_RO(createfifo); + ATF_TP_FSAPPLY_RO(createsymlink); + ATF_TP_FSAPPLY_RO(createlink); + + return atf_no_error(); +} diff --git a/fs/vfs/t_union.c b/fs/vfs/t_union.c new file mode 100644 index 000000000000..bdcef939354e --- /dev/null +++ b/fs/vfs/t_union.c @@ -0,0 +1,204 @@ +/* $NetBSD: t_union.c,v 1.8 2011/08/07 06:01:51 hannken Exp $ */ + +#include <sys/types.h> +#include <sys/mount.h> + +#include <atf-c.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> + +#include <miscfs/union/union.h> + +#include "../../h_macros.h" +#include "../common/h_fsmacros.h" + +#define MSTR "magic bus" + +static void +xput_tfile(const char *mp, const char *path) +{ + char pathb[MAXPATHLEN]; + int fd; + + strcpy(pathb, mp); + strcat(pathb, "/"); + strcat(pathb, path); + + RL(fd = rump_sys_open(pathb, O_CREAT | O_RDWR, 0777)); + if (rump_sys_write(fd, MSTR, sizeof(MSTR)) != sizeof(MSTR)) + atf_tc_fail_errno("write to testfile"); + RL(rump_sys_close(fd)); +} + +static int +xread_tfile(const char *mp, const char *path) +{ + char pathb[MAXPATHLEN]; + char buf[128]; + int fd; + + strcpy(pathb, mp); + strcat(pathb, "/"); + strcat(pathb, path); + + fd = rump_sys_open(pathb, O_RDONLY); + if (fd == -1) + return errno; + if (rump_sys_read(fd, buf, sizeof(buf)) == -1) + atf_tc_fail_errno("read tfile"); + RL(rump_sys_close(fd)); + if (strcmp(buf, MSTR) == 0) + return 0; + return EPROGMISMATCH; +} + +/* + * Mount a unionfs for testing. Before calling, "mp" contains + * the upper layer. Lowerpath is constructed so that the directory + * contains rumpfs. + */ +static void +mountunion(const char *mp, char *lowerpath) +{ + struct union_args unionargs; + + sprintf(lowerpath, "/lower"); + rump_sys_mkdir(lowerpath, 0777); + + /* mount the union with our testfs as the upper layer */ + memset(&unionargs, 0, sizeof(unionargs)); + unionargs.target = lowerpath; + unionargs.mntflags = UNMNT_BELOW; + + if (rump_sys_mount(MOUNT_UNION, mp, 0, + &unionargs, sizeof(unionargs)) == -1) { + if (errno == EOPNOTSUPP) { + atf_tc_skip("fs does not support VOP_WHITEOUT"); + } else { + atf_tc_fail_errno("union mount"); + } + } +} + +#if 0 +static void +toggleroot(void) +{ + static int status; + + status ^= MNT_RDONLY; + + printf("0x%x\n", status); + RL(rump_sys_mount(MOUNT_RUMPFS, "/", status | MNT_UPDATE, NULL, 0)); +} +#endif + +#define TFILE "tensti" +#define TDIR "testdir" +#define TDFILE TDIR "/indir" + +static void +basic(const atf_tc_t *tc, const char *mp) +{ + char lowerpath[MAXPATHLEN]; + char dbuf[8192]; + struct stat sb; + struct dirent *dp; + int error, fd, dsize; + + mountunion(mp, lowerpath); + + /* create a file in the lower layer */ + xput_tfile(lowerpath, TFILE); + + /* first, test we can read the old file from the new namespace */ + error = xread_tfile(mp, TFILE); + if (error != 0) + atf_tc_fail("union compare failed: %d (%s)", + error, strerror(error)); + + /* then, test upper layer writes don't affect the lower layer */ + xput_tfile(mp, "kiekko"); + if ((error = xread_tfile(lowerpath, "kiekko")) != ENOENT) + atf_tc_fail("invisibility failed: %d (%s)", + error, strerror(error)); + + /* check that we can whiteout stuff in the upper layer */ + FSTEST_ENTER(); + RL(rump_sys_unlink(TFILE)); + ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TFILE, &sb) == -1); + FSTEST_EXIT(); + + /* check that the removed node is not in the directory listing */ + RL(fd = rump_sys_open(mp, O_RDONLY)); + RL(dsize = rump_sys_getdents(fd, dbuf, sizeof(dbuf))); + for (dp = (struct dirent *)dbuf; + (char *)dp < dbuf + dsize; + dp = _DIRENT_NEXT(dp)) { + if (strcmp(dp->d_name, TFILE) == 0 && dp->d_type != DT_WHT) + atf_tc_fail("removed file non-white-outed"); + } + RL(rump_sys_close(fd)); + + RL(rump_sys_unmount(mp, 0)); +} + +static void +whiteout(const atf_tc_t *tc, const char *mp) +{ + char lower[MAXPATHLEN]; + struct stat sb; + void *fsarg; + + /* + * XXX: use ffs here to make sure any screwups in rumpfs don't + * affect the test + */ + RL(ffs_fstest_newfs(tc, &fsarg, "daimage", 1024*1024*5, NULL)); + RL(ffs_fstest_mount(tc, fsarg, "/lower", 0)); + + /* create a file in the lower layer */ + RL(rump_sys_chdir("/lower")); + RL(rump_sys_mkdir(TDIR, 0777)); + RL(rump_sys_mkdir(TDFILE, 0777)); + RL(rump_sys_chdir("/")); + + RL(ffs_fstest_unmount(tc, "/lower", 0)); + RL(ffs_fstest_mount(tc, fsarg, "/lower", MNT_RDONLY)); + + mountunion(mp, lower); + + FSTEST_ENTER(); + ATF_REQUIRE_ERRNO(ENOTEMPTY, rump_sys_rmdir(TDIR) == -1); + RL(rump_sys_rmdir(TDFILE)); + RL(rump_sys_rmdir(TDIR)); + ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1); + ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDIR, &sb) == -1); + + RL(rump_sys_mkdir(TDIR, 0777)); + RL(rump_sys_stat(TDIR, &sb)); + ATF_REQUIRE_ERRNO(ENOENT, rump_sys_stat(TDFILE, &sb) == -1); + FSTEST_EXIT(); + + RL(rump_sys_unmount(mp, 0)); +} + +ATF_TC_FSAPPLY(basic, "check basic union functionality"); +ATF_TC_FSAPPLY(whiteout, "create whiteout in upper layer"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(basic); + ATF_TP_FSAPPLY(whiteout); + + return atf_no_error(); +} diff --git a/fs/vfs/t_unpriv.c b/fs/vfs/t_unpriv.c new file mode 100644 index 000000000000..fffffe412de9 --- /dev/null +++ b/fs/vfs/t_unpriv.c @@ -0,0 +1,241 @@ +/* $NetBSD: t_unpriv.c,v 1.11 2014/08/29 17:39:18 gson Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/time.h> + +#include <atf-c.h> +#include <libgen.h> +#include <limits.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +#define USES_OWNER \ + if (FSTYPE_MSDOS(tc)) \ + atf_tc_skip("owner not supported by file system") + +static void +owner(const atf_tc_t *tc, const char *mp) +{ + + USES_OWNER; + + FSTEST_ENTER(); + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rump_sys_chown(".", 1, -1) != -1 || errno != EPERM) + atf_tc_fail_errno("chown"); + if (rump_sys_chmod(".", 0000) != -1 || errno != EPERM) + atf_tc_fail_errno("chmod"); + rump_pub_lwproc_releaselwp(); + + if (rump_sys_chown(".", 1, -1) == -1) + atf_tc_fail_errno("chown"); + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (rump_sys_chown(".", 1, -1) == -1) + atf_tc_fail_errno("chown"); + if (rump_sys_chmod(".", 0000) == -1) + atf_tc_fail_errno("chmod"); + rump_pub_lwproc_releaselwp(); + + FSTEST_EXIT(); +} + +static void +dirperms(const atf_tc_t *tc, const char *mp) +{ + char name[] = "dir.test/file.test"; + char *dir = dirname(name); + int fd; + + if (FSTYPE_SYSVBFS(tc)) + atf_tc_skip("directories not supported by file system"); + + FSTEST_ENTER(); + + if (rump_sys_mkdir(dir, 0777) == -1) + atf_tc_fail_errno("mkdir"); + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rump_sys_open(name, O_RDWR|O_CREAT, 0666) != -1 || errno != EACCES) + atf_tc_fail_errno("open"); + rump_pub_lwproc_releaselwp(); + + if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1) + atf_tc_fail_errno("open"); + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (rump_sys_unlink(name) != -1 || errno != EACCES) + atf_tc_fail_errno("unlink"); + rump_pub_lwproc_releaselwp(); + + if (rump_sys_unlink(name) == -1) + atf_tc_fail_errno("unlink"); + + if (rump_sys_rmdir(dir) == -1) + atf_tc_fail_errno("rmdir"); + + FSTEST_EXIT(); +} + +static void +times(const atf_tc_t *tc, const char *mp) +{ + const char *name = "file.test"; + int fd; + unsigned int i, j; + struct timeval tmv[2]; + static struct timeval tmvs[] = { + { QUAD_MIN, 0 }, + { 0, 0 }, + { QUAD_MAX, 999999 } + }; + + FSTEST_ENTER(); + + if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1) + atf_tc_fail_errno("open"); + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rump_sys_utimes(name, NULL) != -1 || errno != EACCES) + atf_tc_fail_errno("utimes"); + rump_pub_lwproc_releaselwp(); + + if (rump_sys_utimes(name, NULL) == -1) + atf_tc_fail_errno("utimes"); + + for (i = 0; i < sizeof(tmvs) / sizeof(tmvs[0]); i++) { + for (j = 0; j < sizeof(tmvs) / sizeof(tmvs[0]); j++) { + tmv[0] = tmvs[i]; + tmv[1] = tmvs[j]; + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + if (rump_sys_utimes(name, tmv) != -1 || errno != EPERM) + atf_tc_fail_errno("utimes"); + rump_pub_lwproc_releaselwp(); + + if (rump_sys_utimes(name, tmv) == -1) + atf_tc_fail_errno("utimes"); + } + } + + if (rump_sys_unlink(name) == -1) + atf_tc_fail_errno("unlink"); + + FSTEST_EXIT(); +} + +static void +flags(const atf_tc_t *tc, const char *mp) +{ + const char *name = "file.test"; + int fd, fflags; + struct stat st; + + FSTEST_ENTER(); + + if ((fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)) == -1) + atf_tc_fail_errno("open"); + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + + if (rump_sys_stat(name, &st) == -1) + atf_tc_fail_errno("stat"); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rump_sys_chflags(name, st.st_flags) == -1) { + if (errno == EOPNOTSUPP) + atf_tc_skip("file flags not supported by file system"); + atf_tc_fail_errno("chflags"); + } + + fflags = st.st_flags | UF_IMMUTABLE; + + rump_pub_lwproc_rfork(RUMP_RFCFDG); + if (rump_sys_setuid(1) == -1) + atf_tc_fail_errno("setuid"); + fflags |= UF_IMMUTABLE; + if (rump_sys_chflags(name, fflags) != -1 || errno != EPERM) + atf_tc_fail_errno("chflags"); + rump_pub_lwproc_releaselwp(); + + if (rump_sys_chflags(name, fflags) == -1) + atf_tc_fail_errno("chflags"); + + fflags &= ~UF_IMMUTABLE; + if (rump_sys_chflags(name, fflags) == -1) + atf_tc_fail_errno("chflags"); + + if (rump_sys_unlink(name) == -1) + atf_tc_fail_errno("unlink"); + + FSTEST_EXIT(); +} + +ATF_TC_FSAPPLY(owner, "owner unprivileged checks"); +ATF_TC_FSAPPLY(dirperms, "directory permission checks"); +ATF_TC_FSAPPLY(times, "time set checks"); +ATF_TC_FSAPPLY(flags, "file flags checks"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(owner); + ATF_TP_FSAPPLY(dirperms); + ATF_TP_FSAPPLY(times); + ATF_TP_FSAPPLY(flags); + + return atf_no_error(); +} diff --git a/fs/vfs/t_vfsops.c b/fs/vfs/t_vfsops.c new file mode 100644 index 000000000000..1bcee89e573d --- /dev/null +++ b/fs/vfs/t_vfsops.c @@ -0,0 +1,211 @@ +/* $NetBSD: t_vfsops.c,v 1.11 2011/04/04 19:16:58 hannken Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/statvfs.h> + +#include <atf-c.h> +#include <dirent.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +static void +tmount(const atf_tc_t *tc, const char *path) +{ + + return; +} + +static void +tstatvfs(const atf_tc_t *tc, const char *path) +{ + const char *fstype = atf_tc_get_md_var(tc, "X-fs.mntname"); + struct statvfs svb; + + if (rump_sys_statvfs1(path, &svb, ST_WAIT) == -1) + atf_tc_fail_errno("statvfs"); + + ATF_REQUIRE(svb.f_namemax > 0 && svb.f_namemax <= MAXNAMLEN); + if (!(FSTYPE_PUFFS(tc) || FSTYPE_P2K_FFS(tc))) + ATF_REQUIRE_STREQ(svb.f_fstypename, fstype); + ATF_REQUIRE_STREQ(svb.f_mntonname, path); +} + +static void +tsync(const atf_tc_t *tc, const char *path) +{ + + rump_sys_sync(); +} + +#define MAGICSTR "just a string, I like A" +static void +tfilehandle(const atf_tc_t *tc, const char *path) +{ + char fpath[MAXPATHLEN]; + char buf[sizeof(MAGICSTR)]; + size_t fhsize; + void *fhp; + int fd; + + sprintf(fpath, "%s/file", path); + fd = rump_sys_open(fpath, O_RDWR | O_CREAT, 0777); + if (fd == -1) + atf_tc_fail_errno("open"); + + if (rump_sys_write(fd, MAGICSTR, sizeof(MAGICSTR)) != sizeof(MAGICSTR)) + atf_tc_fail("write to file"); + rump_sys_close(fd); + + /* + * Get file handle size. + * This also weeds out unsupported file systems. + */ + fhsize = 0; + if (rump_sys_getfh(fpath, NULL, &fhsize) == -1) { + if (errno == EOPNOTSUPP) { + atf_tc_skip("file handles not supported"); + } else if (errno != E2BIG) { + atf_tc_fail_errno("getfh size"); + } + } + + fhp = malloc(fhsize); + if (rump_sys_getfh(fpath, fhp, &fhsize) == -1) + atf_tc_fail_errno("getfh"); + + /* open file based on file handle */ + fd = rump_sys_fhopen(fhp, fhsize, O_RDONLY); + if (fd == -1) { + atf_tc_fail_errno("fhopen"); + } + + /* check that we got the same file */ + if (rump_sys_read(fd, buf, sizeof(buf)) != sizeof(MAGICSTR)) + atf_tc_fail("read fhopened file"); + + ATF_REQUIRE_STREQ(buf, MAGICSTR); + + rump_sys_close(fd); +} + +#define FNAME "a_file" +static void +tfhremove(const atf_tc_t *tc, const char *path) +{ + size_t fhsize; + void *fhp; + int fd; + + RL(rump_sys_chdir(path)); + RL(fd = rump_sys_open(FNAME, O_RDWR | O_CREAT, 0777)); + RL(rump_sys_close(fd)); + + fhsize = 0; + if (rump_sys_getfh(FNAME, NULL, &fhsize) == -1) { + if (errno == EOPNOTSUPP) { + atf_tc_skip("file handles not supported"); + } else if (errno != E2BIG) { + atf_tc_fail_errno("getfh size"); + } + } + + fhp = malloc(fhsize); + RL(rump_sys_getfh(FNAME, fhp, &fhsize)); + RL(rump_sys_unlink(FNAME)); + + if (FSTYPE_LFS(tc)) + atf_tc_expect_fail("fhopen() for removed file succeeds " + "(PR kern/43745)"); + ATF_REQUIRE_ERRNO(ESTALE, rump_sys_fhopen(fhp, fhsize, O_RDONLY) == -1); + atf_tc_expect_pass(); + + RL(rump_sys_chdir("/")); +} +#undef FNAME + +/* + * This test only checks the file system doesn't crash. We *might* + * try a valid file handle. + */ +static void +tfhinval(const atf_tc_t *tc, const char *path) +{ + size_t fhsize; + void *fhp; + unsigned long seed; + int fd; + + srandom(seed = time(NULL)); + printf("RNG seed %lu\n", seed); + + RL(rump_sys_chdir(path)); + fhsize = 0; + if (rump_sys_getfh(".", NULL, &fhsize) == -1) { + if (errno == EOPNOTSUPP) { + atf_tc_skip("file handles not supported"); + } else if (errno != E2BIG) { + atf_tc_fail_errno("getfh size"); + } + } + + fhp = malloc(fhsize); + tests_makegarbage(fhp, fhsize); + fd = rump_sys_fhopen(fhp, fhsize, O_RDWR); + if (fd != -1) + rump_sys_close(fd); + + RL(rump_sys_chdir("/")); +} + +ATF_TC_FSAPPLY(tmount, "mount/unmount"); +ATF_TC_FSAPPLY(tstatvfs, "statvfs"); +ATF_TC_FSAPPLY(tsync, "sync"); +ATF_TC_FSAPPLY(tfilehandle, "file handles"); +ATF_TC_FSAPPLY(tfhremove, "fhtovp for removed file"); +ATF_TC_FSAPPLY(tfhinval, "fhopen invalid filehandle"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(tmount); + ATF_TP_FSAPPLY(tstatvfs); + ATF_TP_FSAPPLY(tsync); + ATF_TP_FSAPPLY(tfilehandle); + ATF_TP_FSAPPLY(tfhremove); + ATF_TP_FSAPPLY(tfhinval); + + return atf_no_error(); +} diff --git a/fs/vfs/t_vnops.c b/fs/vfs/t_vnops.c new file mode 100644 index 000000000000..66b550538bdd --- /dev/null +++ b/fs/vfs/t_vnops.c @@ -0,0 +1,1001 @@ +/* $NetBSD: t_vnops.c,v 1.43 2014/09/09 06:51:00 gson Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, 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: + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/stat.h> +#include <sys/statvfs.h> + +#include <assert.h> +#include <atf-c.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <rump/rump_syscalls.h> +#include <rump/rump.h> + +#include "../common/h_fsmacros.h" +#include "../../h_macros.h" + +#define TESTFILE "afile" + +#define USES_DIRS \ + if (FSTYPE_SYSVBFS(tc)) \ + atf_tc_skip("directories not supported by file system") + +#define USES_SYMLINKS \ + if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc)) \ + atf_tc_skip("symlinks not supported by file system") + +static char * +md(char *buf, const char *base, const char *tail) +{ + + sprintf(buf, "%s/%s", base, tail); + return buf; +} + +static void +lookup_simple(const atf_tc_t *tc, const char *mountpath) +{ + char pb[MAXPATHLEN], final[MAXPATHLEN]; + struct stat sb1, sb2; + + strcpy(final, mountpath); + sprintf(pb, "%s/../%s", mountpath, basename(final)); + if (rump_sys_stat(pb, &sb1) == -1) + atf_tc_fail_errno("stat 1"); + + sprintf(pb, "%s/./../%s", mountpath, basename(final)); + if (rump_sys_stat(pb, &sb2) == -1) + atf_tc_fail_errno("stat 2"); + + ATF_REQUIRE(memcmp(&sb1, &sb2, sizeof(sb1)) == 0); +} + +static void +lookup_complex(const atf_tc_t *tc, const char *mountpath) +{ + char pb[MAXPATHLEN]; + struct stat sb1, sb2; + + USES_DIRS; + + if (FSTYPE_UDF(tc)) + atf_tc_expect_fail("PR kern/49033"); + + sprintf(pb, "%s/dir", mountpath); + if (rump_sys_mkdir(pb, 0777) == -1) + atf_tc_fail_errno("mkdir"); + if (rump_sys_stat(pb, &sb1) == -1) + atf_tc_fail_errno("stat 1"); + + sprintf(pb, "%s/./dir/../././dir/.", mountpath); + if (rump_sys_stat(pb, &sb2) == -1) + atf_tc_fail_errno("stat 2"); + + if (memcmp(&sb1, &sb2, sizeof(sb1)) != 0) { + printf("what\tsb1\t\tsb2\n"); + +#define FIELD(FN) \ + printf(#FN "\t%lld\t%lld\n", \ + (long long)sb1.FN, (long long)sb2.FN) +#define TIME(FN) \ + printf(#FN "\t%lld.%ld\t%lld.%ld\n", \ + (long long)sb1.FN.tv_sec, sb1.FN.tv_nsec, \ + (long long)sb2.FN.tv_sec, sb2.FN.tv_nsec) + + FIELD(st_dev); + FIELD(st_mode); + FIELD(st_ino); + FIELD(st_nlink); + FIELD(st_uid); + FIELD(st_gid); + FIELD(st_rdev); + TIME(st_atim); + TIME(st_mtim); + TIME(st_ctim); + TIME(st_birthtim); + FIELD(st_size); + FIELD(st_blocks); + FIELD(st_flags); + FIELD(st_gen); + +#undef FIELD +#undef TIME + + atf_tc_fail("stat results differ, see ouput for more details"); + } + if (FSTYPE_UDF(tc)) + atf_tc_fail("random failure of PR kern/49033 " + "did not happen this time"); +} + +static void +dir_simple(const atf_tc_t *tc, const char *mountpath) +{ + char pb[MAXPATHLEN]; + struct stat sb; + + USES_DIRS; + + /* check we can create directories */ + sprintf(pb, "%s/dir", mountpath); + if (rump_sys_mkdir(pb, 0777) == -1) + atf_tc_fail_errno("mkdir"); + if (rump_sys_stat(pb, &sb) == -1) + atf_tc_fail_errno("stat new directory"); + + /* check we can remove then and that it makes them unreachable */ + if (rump_sys_rmdir(pb) == -1) + atf_tc_fail_errno("rmdir"); + if (rump_sys_stat(pb, &sb) != -1 || errno != ENOENT) + atf_tc_fail("ENOENT expected from stat"); +} + +static void +dir_notempty(const atf_tc_t *tc, const char *mountpath) +{ + char pb[MAXPATHLEN], pb2[MAXPATHLEN]; + int fd, rv; + + USES_DIRS; + + /* check we can create directories */ + sprintf(pb, "%s/dir", mountpath); + if (rump_sys_mkdir(pb, 0777) == -1) + atf_tc_fail_errno("mkdir"); + + sprintf(pb2, "%s/dir/file", mountpath); + fd = rump_sys_open(pb2, O_RDWR | O_CREAT, 0777); + if (fd == -1) + atf_tc_fail_errno("create file"); + rump_sys_close(fd); + + rv = rump_sys_rmdir(pb); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rv != -1 || errno != ENOTEMPTY) + atf_tc_fail("non-empty directory removed succesfully"); + + if (rump_sys_unlink(pb2) == -1) + atf_tc_fail_errno("cannot remove dir/file"); + + if (rump_sys_rmdir(pb) == -1) + atf_tc_fail_errno("remove directory"); +} + +static void +dir_rmdirdotdot(const atf_tc_t *tc, const char *mp) +{ + char pb[MAXPATHLEN]; + int xerrno; + + USES_DIRS; + + FSTEST_ENTER(); + RL(rump_sys_mkdir("test", 0777)); + RL(rump_sys_chdir("test")); + + RL(rump_sys_mkdir("subtest", 0777)); + RL(rump_sys_chdir("subtest")); + + md(pb, mp, "test/subtest"); + RL(rump_sys_rmdir(pb)); + md(pb, mp, "test"); + RL(rump_sys_rmdir(pb)); + + if (FSTYPE_NFS(tc)) + xerrno = ESTALE; + else + xerrno = ENOENT; + ATF_REQUIRE_ERRNO(xerrno, rump_sys_chdir("..") == -1); + FSTEST_EXIT(); +} + +static void +checkfile(const char *path, struct stat *refp) +{ + char buf[MAXPATHLEN]; + struct stat sb; + static int n = 1; + + md(buf, path, "file"); + if (rump_sys_stat(buf, &sb) == -1) + atf_tc_fail_errno("cannot stat file %d (%s)", n, buf); + if (memcmp(&sb, refp, sizeof(sb)) != 0) + atf_tc_fail("stat mismatch %d", n); + n++; +} + +static void +rename_dir(const atf_tc_t *tc, const char *mp) +{ + char pb1[MAXPATHLEN], pb2[MAXPATHLEN], pb3[MAXPATHLEN]; + struct stat ref, sb; + + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + + USES_DIRS; + + md(pb1, mp, "dir1"); + if (rump_sys_mkdir(pb1, 0777) == -1) + atf_tc_fail_errno("mkdir 1"); + + md(pb2, mp, "dir2"); + if (rump_sys_mkdir(pb2, 0777) == -1) + atf_tc_fail_errno("mkdir 2"); + md(pb2, mp, "dir2/subdir"); + if (rump_sys_mkdir(pb2, 0777) == -1) + atf_tc_fail_errno("mkdir 3"); + + md(pb3, mp, "dir1/file"); + if (rump_sys_mknod(pb3, S_IFREG | 0777, -1) == -1) + atf_tc_fail_errno("create file"); + if (rump_sys_stat(pb3, &ref) == -1) + atf_tc_fail_errno("stat of file"); + + /* + * First try ops which should succeed. + */ + + /* rename within directory */ + md(pb3, mp, "dir3"); + if (rump_sys_rename(pb1, pb3) == -1) + atf_tc_fail_errno("rename 1"); + checkfile(pb3, &ref); + + /* rename directory onto itself (two ways, should fail) */ + md(pb1, mp, "dir3/."); + if (rump_sys_rename(pb1, pb3) != -1 || errno != EINVAL) + atf_tc_fail_errno("rename 2"); + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (rump_sys_rename(pb3, pb1) != -1 || errno != EISDIR) + atf_tc_fail_errno("rename 3"); + + checkfile(pb3, &ref); + + /* rename father of directory into directory */ + md(pb1, mp, "dir2/dir"); + md(pb2, mp, "dir2"); + if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL) + atf_tc_fail_errno("rename 4"); + + /* same for grandfather */ + md(pb1, mp, "dir2/subdir/dir2"); + if (rump_sys_rename(pb2, pb1) != -1 || errno != EINVAL) + atf_tc_fail("rename 5"); + + checkfile(pb3, &ref); + + /* rename directory over a non-empty directory */ + if (rump_sys_rename(pb2, pb3) != -1 || errno != ENOTEMPTY) + atf_tc_fail("rename 6"); + + /* cross-directory rename */ + md(pb1, mp, "dir3"); + md(pb2, mp, "dir2/somedir"); + if (rump_sys_rename(pb1, pb2) == -1) + atf_tc_fail_errno("rename 7"); + checkfile(pb2, &ref); + + /* move to parent directory */ + md(pb1, mp, "dir2/somedir/../../dir3"); + if (rump_sys_rename(pb2, pb1) == -1) + atf_tc_fail_errno("rename 8"); + md(pb1, mp, "dir2/../dir3"); + checkfile(pb1, &ref); + + /* atomic cross-directory rename */ + md(pb3, mp, "dir2/subdir"); + if (rump_sys_rename(pb1, pb3) == -1) + atf_tc_fail_errno("rename 9"); + checkfile(pb3, &ref); + + /* rename directory over an empty directory */ + md(pb1, mp, "parent"); + md(pb2, mp, "parent/dir1"); + md(pb3, mp, "parent/dir2"); + RL(rump_sys_mkdir(pb1, 0777)); + RL(rump_sys_mkdir(pb2, 0777)); + RL(rump_sys_mkdir(pb3, 0777)); + RL(rump_sys_rename(pb2, pb3)); + + RL(rump_sys_stat(pb1, &sb)); + if (! FSTYPE_MSDOS(tc)) + ATF_CHECK_EQ(sb.st_nlink, 3); + RL(rump_sys_rmdir(pb3)); + RL(rump_sys_rmdir(pb1)); +} + +static void +rename_dotdot(const atf_tc_t *tc, const char *mp) +{ + + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + + USES_DIRS; + + if (rump_sys_chdir(mp) == -1) + atf_tc_fail_errno("chdir mountpoint"); + + if (rump_sys_mkdir("dir1", 0777) == -1) + atf_tc_fail_errno("mkdir 1"); + if (rump_sys_mkdir("dir2", 0777) == -1) + atf_tc_fail_errno("mkdir 2"); + + if (rump_sys_rename("dir1", "dir1/..") != -1 || errno != EINVAL) + atf_tc_fail_errno("self-dotdot to"); + + if (rump_sys_rename("dir1/..", "sometarget") != -1 || errno != EINVAL) + atf_tc_fail_errno("self-dotdot from"); + + if (rump_sys_rename("dir1", "dir2/..") != -1 || errno != EINVAL) + atf_tc_fail("other-dotdot"); + + rump_sys_chdir("/"); +} + +static void +rename_reg_nodir(const atf_tc_t *tc, const char *mp) +{ + bool haslinks; + struct stat sb; + ino_t f1ino; + + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + + if (rump_sys_chdir(mp) == -1) + atf_tc_fail_errno("chdir mountpoint"); + + if (FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc)) + haslinks = false; + else + haslinks = true; + + if (rump_sys_mknod("file1", S_IFREG | 0777, -1) == -1) + atf_tc_fail_errno("create file"); + if (rump_sys_mknod("file2", S_IFREG | 0777, -1) == -1) + atf_tc_fail_errno("create file"); + + if (rump_sys_stat("file1", &sb) == -1) + atf_tc_fail_errno("stat"); + f1ino = sb.st_ino; + + if (haslinks) { + if (rump_sys_link("file1", "file_link") == -1) + atf_tc_fail_errno("link"); + if (rump_sys_stat("file_link", &sb) == -1) + atf_tc_fail_errno("stat"); + ATF_REQUIRE_EQ(sb.st_ino, f1ino); + ATF_REQUIRE_EQ(sb.st_nlink, 2); + } + + if (rump_sys_stat("file2", &sb) == -1) + atf_tc_fail_errno("stat"); + + if (rump_sys_rename("file1", "file3") == -1) + atf_tc_fail_errno("rename 1"); + if (rump_sys_stat("file3", &sb) == -1) + atf_tc_fail_errno("stat 1"); + if (haslinks) { + ATF_REQUIRE_EQ(sb.st_ino, f1ino); + } + if (rump_sys_stat("file1", &sb) != -1 || errno != ENOENT) + atf_tc_fail_errno("source 1"); + + if (rump_sys_rename("file3", "file2") == -1) + atf_tc_fail_errno("rename 2"); + if (rump_sys_stat("file2", &sb) == -1) + atf_tc_fail_errno("stat 2"); + if (haslinks) { + ATF_REQUIRE_EQ(sb.st_ino, f1ino); + } + + if (rump_sys_stat("file3", &sb) != -1 || errno != ENOENT) + atf_tc_fail_errno("source 2"); + + if (haslinks) { + if (rump_sys_rename("file2", "file_link") == -1) + atf_tc_fail_errno("rename hardlink"); + if (rump_sys_stat("file2", &sb) != -1 || errno != ENOENT) + atf_tc_fail_errno("source 3"); + if (rump_sys_stat("file_link", &sb) == -1) + atf_tc_fail_errno("stat 2"); + ATF_REQUIRE_EQ(sb.st_ino, f1ino); + ATF_REQUIRE_EQ(sb.st_nlink, 1); + } + + ATF_CHECK_ERRNO(EFAULT, rump_sys_rename("file2", NULL) == -1); + ATF_CHECK_ERRNO(EFAULT, rump_sys_rename(NULL, "file2") == -1); + + rump_sys_chdir("/"); +} + +static void +create_nametoolong(const atf_tc_t *tc, const char *mp) +{ + char *name; + int fd; + long val; + size_t len; + + if (rump_sys_chdir(mp) == -1) + atf_tc_fail_errno("chdir mountpoint"); + + val = rump_sys_pathconf(".", _PC_NAME_MAX); + if (val == -1) + atf_tc_fail_errno("pathconf"); + + len = val + 1; + name = malloc(len+1); + if (name == NULL) + atf_tc_fail_errno("malloc"); + + memset(name, 'a', len); + *(name+len) = '\0'; + + val = rump_sys_pathconf(".", _PC_NO_TRUNC); + if (val == -1) + atf_tc_fail_errno("pathconf"); + + fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666); + if (val != 0 && (fd != -1 || errno != ENAMETOOLONG)) + atf_tc_fail_errno("open"); + + if (val == 0 && rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + if (val == 0 && rump_sys_unlink(name) == -1) + atf_tc_fail_errno("unlink"); + + free(name); + + rump_sys_chdir("/"); +} + +static void +create_exist(const atf_tc_t *tc, const char *mp) +{ + const char *name = "hoge"; + int fd; + + RL(rump_sys_chdir(mp)); + RL(fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666)); + RL(rump_sys_close(fd)); + RL(rump_sys_unlink(name)); + RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)); + RL(rump_sys_close(fd)); + RL(fd = rump_sys_open(name, O_RDWR|O_CREAT, 0666)); + RL(rump_sys_close(fd)); + ATF_REQUIRE_ERRNO(EEXIST, + (fd = rump_sys_open(name, O_RDWR|O_CREAT|O_EXCL, 0666))); + RL(rump_sys_unlink(name)); + RL(rump_sys_chdir("/")); +} + +static void +rename_nametoolong(const atf_tc_t *tc, const char *mp) +{ + char *name; + int res, fd; + long val; + size_t len; + + if (FSTYPE_RUMPFS(tc)) + atf_tc_skip("rename not supported by file system"); + + if (rump_sys_chdir(mp) == -1) + atf_tc_fail_errno("chdir mountpoint"); + + val = rump_sys_pathconf(".", _PC_NAME_MAX); + if (val == -1) + atf_tc_fail_errno("pathconf"); + + len = val + 1; + name = malloc(len+1); + if (name == NULL) + atf_tc_fail_errno("malloc"); + + memset(name, 'a', len); + *(name+len) = '\0'; + + fd = rump_sys_open("dummy", O_RDWR|O_CREAT, 0666); + if (fd == -1) + atf_tc_fail_errno("open"); + if (rump_sys_close(fd) == -1) + atf_tc_fail_errno("close"); + + val = rump_sys_pathconf(".", _PC_NO_TRUNC); + if (val == -1) + atf_tc_fail_errno("pathconf"); + + res = rump_sys_rename("dummy", name); + if (val != 0 && (res != -1 || errno != ENAMETOOLONG)) + atf_tc_fail_errno("rename"); + + if (val == 0 && rump_sys_unlink(name) == -1) + atf_tc_fail_errno("unlink"); + + free(name); + + rump_sys_chdir("/"); +} + +/* + * Test creating a symlink whose length is "len" bytes, not including + * the terminating NUL. + */ +static void +symlink_len(const atf_tc_t *tc, const char *mp, size_t len) +{ + char *buf; + int r; + + USES_SYMLINKS; + + RL(rump_sys_chdir(mp)); + + buf = malloc(len + 1); + ATF_REQUIRE(buf); + memset(buf, 'a', len); + buf[len] = '\0'; + r = rump_sys_symlink(buf, "afile"); + if (r == -1) { + ATF_REQUIRE_ERRNO(ENAMETOOLONG, r); + } else { + RL(rump_sys_unlink("afile")); + } + free(buf); + + RL(rump_sys_chdir("/")); +} + +static void +symlink_zerolen(const atf_tc_t *tc, const char *mp) +{ + symlink_len(tc, mp, 0); +} + +static void +symlink_long(const atf_tc_t *tc, const char *mp) +{ + /* + * Test lengths close to powers of two, as those are likely + * to be edge cases. + */ + size_t len; + int fuzz; + for (len = 2; len <= 65536; len *= 2) { + for (fuzz = -1; fuzz <= 1; fuzz++) { + symlink_len(tc, mp, len + fuzz); + } + } +} + +static void +symlink_root(const atf_tc_t *tc, const char *mp) +{ + + USES_SYMLINKS; + + RL(rump_sys_chdir(mp)); + RL(rump_sys_symlink("/", "foo")); + RL(rump_sys_chdir("foo")); +} + +static void +attrs(const atf_tc_t *tc, const char *mp) +{ + struct stat sb, sb2; + struct timeval tv[2]; + int fd; + + FSTEST_ENTER(); + RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755)); + RL(rump_sys_close(fd)); + RL(rump_sys_stat(TESTFILE, &sb)); + if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) { + RL(rump_sys_chown(TESTFILE, 1, 2)); + sb.st_uid = 1; + sb.st_gid = 2; + RL(rump_sys_chmod(TESTFILE, 0123)); + sb.st_mode = (sb.st_mode & ~ACCESSPERMS) | 0123; + } + + tv[0].tv_sec = 1000000000; /* need something >1980 for msdosfs */ + tv[0].tv_usec = 1; + tv[1].tv_sec = 1000000002; /* need even seconds for msdosfs */ + tv[1].tv_usec = 3; + RL(rump_sys_utimes(TESTFILE, tv)); + RL(rump_sys_utimes(TESTFILE, tv)); /* XXX: utimes & birthtime */ + sb.st_atimespec.tv_sec = 1000000000; + sb.st_atimespec.tv_nsec = 1000; + sb.st_mtimespec.tv_sec = 1000000002; + sb.st_mtimespec.tv_nsec = 3000; + + RL(rump_sys_stat(TESTFILE, &sb2)); +#define CHECK(a) ATF_REQUIRE_EQ(sb.a, sb2.a) + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + if (!(FSTYPE_MSDOS(tc) || FSTYPE_SYSVBFS(tc))) { + CHECK(st_uid); + CHECK(st_gid); + CHECK(st_mode); + } + if (!FSTYPE_MSDOS(tc)) { + /* msdosfs has only access date, not time */ + CHECK(st_atimespec.tv_sec); + } + CHECK(st_mtimespec.tv_sec); + if (!(FSTYPE_EXT2FS(tc) || FSTYPE_MSDOS(tc) || + FSTYPE_SYSVBFS(tc) || FSTYPE_V7FS(tc))) { + CHECK(st_atimespec.tv_nsec); + CHECK(st_mtimespec.tv_nsec); + } +#undef CHECK + + FSTEST_EXIT(); +} + +static void +fcntl_lock(const atf_tc_t *tc, const char *mp) +{ + int fd, fd2; + struct flock l; + struct lwp *lwp1, *lwp2; + + FSTEST_ENTER(); + l.l_pid = 0; + l.l_start = l.l_len = 1024; + l.l_type = F_RDLCK | F_WRLCK; + l.l_whence = SEEK_END; + + lwp1 = rump_pub_lwproc_curlwp(); + RL(fd = rump_sys_open(TESTFILE, O_RDWR | O_CREAT, 0755)); + RL(rump_sys_ftruncate(fd, 8192)); + + /* PR kern/43321 */ + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be broken"); + RL(rump_sys_fcntl(fd, F_SETLK, &l)); + + /* Next, we fork and try to lock the same area */ + RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); + lwp2 = rump_pub_lwproc_curlwp(); + RL(fd2 = rump_sys_open(TESTFILE, O_RDWR, 0)); + ATF_REQUIRE_ERRNO(EAGAIN, rump_sys_fcntl(fd2, F_SETLK, &l)); + + /* Switch back and unlock... */ + rump_pub_lwproc_switch(lwp1); + l.l_type = F_UNLCK; + RL(rump_sys_fcntl(fd, F_SETLK, &l)); + + /* ... and try to lock again */ + rump_pub_lwproc_switch(lwp2); + l.l_type = F_RDLCK | F_WRLCK; + RL(rump_sys_fcntl(fd2, F_SETLK, &l)); + + RL(rump_sys_close(fd2)); + rump_pub_lwproc_releaselwp(); + + RL(rump_sys_close(fd)); + + FSTEST_EXIT(); +} + +static int +flock_compare(const void *p, const void *q) +{ + int a = ((const struct flock *)p)->l_start; + int b = ((const struct flock *)q)->l_start; + return a < b ? -1 : (a > b ? 1 : 0); +} + +/* + * Find all locks set by fcntl_getlock_pids test + * using GETLK for a range [start, start+end], and, + * if there is a blocking lock, recursively find + * all locks to the left (toward the beginning of + * a file) and to the right of the lock. + * The function also understands "until end of file" + * convention when len==0. + */ +static unsigned int +fcntl_getlocks(int fildes, off_t start, off_t len, + struct flock *lock, struct flock *end) +{ + unsigned int rv = 0; + const struct flock l = { start, len, 0, F_RDLCK, SEEK_SET }; + + if (lock == end) + return rv; + + RL(rump_sys_fcntl(fildes, F_GETLK, &l)); + + if (l.l_type == F_UNLCK) + return rv; + + *lock++ = l; + rv += 1; + + ATF_REQUIRE(l.l_whence == SEEK_SET); + + if (l.l_start > start) { + unsigned int n = + fcntl_getlocks(fildes, start, l.l_start - start, lock, end); + rv += n; + lock += n; + if (lock == end) + return rv; + } + + if (l.l_len == 0) /* does l spans until the end? */ + return rv; + + if (len == 0) /* are we looking for locks until the end? */ { + rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end); + } else if (l.l_start + l.l_len < start + len) { + len -= l.l_start + l.l_len - start; + rv += fcntl_getlocks(fildes, l.l_start + l.l_len, len, lock, end); + } + + return rv; +} + +static void +fcntl_getlock_pids(const atf_tc_t *tc, const char *mp) +{ + /* test non-overlaping ranges */ + struct flock expect[4]; + const struct flock lock[4] = { + { 0, 2, 0, F_WRLCK, SEEK_SET }, + { 2, 1, 0, F_WRLCK, SEEK_SET }, + { 7, 5, 0, F_WRLCK, SEEK_SET }, + { 4, 3, 0, F_WRLCK, SEEK_SET }, + }; + + /* Add extra element to make sure recursion does't stop at array end */ + struct flock result[5]; + + /* Add 5th process */ + int fd[5]; + pid_t pid[5]; + struct lwp *lwp[5]; + + unsigned int i, j; + const off_t sz = 8192; + int omode = 0755; + int oflags = O_RDWR | O_CREAT; + + memcpy(expect, lock, sizeof(lock)); + + FSTEST_ENTER(); + + /* + * First, we create 4 processes and let each lock a range of the + * file. Note that the third and fourth processes lock in + * "reverse" order, i.e. the greater pid locks a range before + * the lesser pid. + * Then, we create 5th process which doesn't lock anything. + */ + for (i = 0; i < __arraycount(lwp); i++) { + RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG)); + + lwp[i] = rump_pub_lwproc_curlwp(); + pid[i] = rump_sys_getpid(); + + RL(fd[i] = rump_sys_open(TESTFILE, oflags, omode)); + oflags = O_RDWR; + omode = 0; + + RL(rump_sys_ftruncate(fd[i], sz)); + + if (FSTYPE_ZFS(tc)) + atf_tc_expect_fail("PR kern/47656: Test known to be " + "broken"); + if (i < __arraycount(lock)) { + RL(rump_sys_fcntl(fd[i], F_SETLK, &lock[i])); + expect[i].l_pid = pid[i]; + } + } + + qsort(expect, __arraycount(expect), sizeof(expect[0]), &flock_compare); + + /* + * In the context of each process, recursively find all locks + * that would block the current process. Processes 1-4 don't + * see their own lock, we insert it to simplify checks. + * Process 5 sees all 4 locks. + */ + for (i = 0; i < __arraycount(lwp); i++) { + unsigned int nlocks; + + rump_pub_lwproc_switch(lwp[i]); + + memset(result, 0, sizeof(result)); + nlocks = fcntl_getlocks(fd[i], 0, sz, + result, result + __arraycount(result)); + + if (i < __arraycount(lock)) { + ATF_REQUIRE(nlocks < __arraycount(result)); + result[nlocks] = lock[i]; + result[nlocks].l_pid = pid[i]; + nlocks++; + } + + ATF_CHECK_EQ(nlocks, __arraycount(expect)); + + qsort(result, nlocks, sizeof(result[0]), &flock_compare); + + for (j = 0; j < nlocks; j++) { + ATF_CHECK_EQ(result[j].l_start, expect[j].l_start ); + ATF_CHECK_EQ(result[j].l_len, expect[j].l_len ); + ATF_CHECK_EQ(result[j].l_pid, expect[j].l_pid ); + ATF_CHECK_EQ(result[j].l_type, expect[j].l_type ); + ATF_CHECK_EQ(result[j].l_whence, expect[j].l_whence); + } + } + + /* + * Release processes. This also releases the fds and locks + * making fs unmount possible + */ + for (i = 0; i < __arraycount(lwp); i++) { + rump_pub_lwproc_switch(lwp[i]); + rump_pub_lwproc_releaselwp(); + } + + FSTEST_EXIT(); +} + +static void +access_simple(const atf_tc_t *tc, const char *mp) +{ + int fd; + int tmode; + + FSTEST_ENTER(); + RL(fd = rump_sys_open("tfile", O_CREAT | O_RDWR, 0777)); + RL(rump_sys_close(fd)); + +#define ALLACC (F_OK | X_OK | W_OK | R_OK) + if (FSTYPE_SYSVBFS(tc) || FSTYPE_MSDOS(tc)) + tmode = F_OK; + else + tmode = ALLACC; + + RL(rump_sys_access("tfile", tmode)); + + /* PR kern/44648 */ + ATF_REQUIRE_ERRNO(EINVAL, rump_sys_access("tfile", ALLACC+1) == -1); +#undef ALLACC + FSTEST_EXIT(); +} + +static void +read_directory(const atf_tc_t *tc, const char *mp) +{ + char buf[1024]; + int fd, res; + ssize_t size; + + FSTEST_ENTER(); + fd = rump_sys_open(".", O_DIRECTORY | O_RDONLY, 0777); + ATF_REQUIRE(fd != -1); + + size = rump_sys_pread(fd, buf, sizeof(buf), 0); + ATF_CHECK(size != -1 || errno == EISDIR); + size = rump_sys_read(fd, buf, sizeof(buf)); + ATF_CHECK(size != -1 || errno == EISDIR); + + res = rump_sys_close(fd); + ATF_REQUIRE(res != -1); + FSTEST_EXIT(); +} + +static void +lstat_symlink(const atf_tc_t *tc, const char *mp) +{ + const char *src, *dst; + int res; + struct stat st; + + USES_SYMLINKS; + + if (FSTYPE_V7FS(tc)) + atf_tc_expect_fail("PR kern/48864"); + + FSTEST_ENTER(); + + src = "source"; + dst = "destination"; + + res = rump_sys_symlink(src, dst); + ATF_REQUIRE(res != -1); + res = rump_sys_lstat(dst, &st); + ATF_REQUIRE(res != -1); + + ATF_CHECK(S_ISLNK(st.st_mode) != 0); + ATF_CHECK(st.st_size == (off_t)strlen(src)); + + FSTEST_EXIT(); +} + +ATF_TC_FSAPPLY(lookup_simple, "simple lookup (./.. on root)"); +ATF_TC_FSAPPLY(lookup_complex, "lookup of non-dot entries"); +ATF_TC_FSAPPLY(dir_simple, "mkdir/rmdir"); +ATF_TC_FSAPPLY(dir_notempty, "non-empty directories cannot be removed"); +ATF_TC_FSAPPLY(dir_rmdirdotdot, "remove .. and try to cd out (PR kern/44657)"); +ATF_TC_FSAPPLY(rename_dir, "exercise various directory renaming ops " +"(PR kern/44288)"); +ATF_TC_FSAPPLY(rename_dotdot, "rename dir .. (PR kern/43617)"); +ATF_TC_FSAPPLY(rename_reg_nodir, "rename regular files, no subdirectories"); +ATF_TC_FSAPPLY(create_nametoolong, "create file with name too long"); +ATF_TC_FSAPPLY(create_exist, "create with O_EXCL"); +ATF_TC_FSAPPLY(rename_nametoolong, "rename to file with name too long"); +ATF_TC_FSAPPLY(symlink_zerolen, "symlink with target of length 0"); +ATF_TC_FSAPPLY(symlink_long, "symlink with target of length > 0"); +ATF_TC_FSAPPLY(symlink_root, "symlink to root directory"); +ATF_TC_FSAPPLY(attrs, "check setting attributes works"); +ATF_TC_FSAPPLY(fcntl_lock, "check fcntl F_SETLK"); +ATF_TC_FSAPPLY(fcntl_getlock_pids,"fcntl F_GETLK w/ many procs, PR kern/44494"); +ATF_TC_FSAPPLY(access_simple, "access(2)"); +ATF_TC_FSAPPLY(read_directory, "read(2) on directories"); +ATF_TC_FSAPPLY(lstat_symlink, "lstat(2) values for symbolic links"); + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_FSAPPLY(lookup_simple); + ATF_TP_FSAPPLY(lookup_complex); + ATF_TP_FSAPPLY(dir_simple); + ATF_TP_FSAPPLY(dir_notempty); + ATF_TP_FSAPPLY(dir_rmdirdotdot); + ATF_TP_FSAPPLY(rename_dir); + ATF_TP_FSAPPLY(rename_dotdot); + ATF_TP_FSAPPLY(rename_reg_nodir); + ATF_TP_FSAPPLY(create_nametoolong); + ATF_TP_FSAPPLY(create_exist); + ATF_TP_FSAPPLY(rename_nametoolong); + ATF_TP_FSAPPLY(symlink_zerolen); + ATF_TP_FSAPPLY(symlink_long); + ATF_TP_FSAPPLY(symlink_root); + ATF_TP_FSAPPLY(attrs); + ATF_TP_FSAPPLY(fcntl_lock); + ATF_TP_FSAPPLY(fcntl_getlock_pids); + ATF_TP_FSAPPLY(access_simple); + ATF_TP_FSAPPLY(read_directory); + ATF_TP_FSAPPLY(lstat_symlink); + + return atf_no_error(); +} |