aboutsummaryrefslogtreecommitdiff
path: root/fs/vfs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/vfs')
-rw-r--r--fs/vfs/Makefile47
-rw-r--r--fs/vfs/t_full.c101
-rw-r--r--fs/vfs/t_io.c250
-rw-r--r--fs/vfs/t_renamerace.c190
-rw-r--r--fs/vfs/t_rmdirrace.c106
-rw-r--r--fs/vfs/t_ro.c203
-rw-r--r--fs/vfs/t_union.c204
-rw-r--r--fs/vfs/t_unpriv.c241
-rw-r--r--fs/vfs/t_vfsops.c211
-rw-r--r--fs/vfs/t_vnops.c1001
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();
+}