aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorChunwei Chen <[email protected]>2016-01-26 12:29:46 -0800
committerBrian Behlendorf <[email protected]>2016-11-04 10:46:40 -0700
commitace1eae84cca8579596f46262d99df19f6d7e963 (patch)
tree22e2bf44dfc906cf6cb75567d993a86f64bf1ef8 /tests
parent987014903f9d36783547188b6ad00f01d9a076bd (diff)
Add support for O_TMPFILE
Linux 3.11 add O_TMPFILE to open(2), which allow creating an unlinked file on supported filesystem. It's basically doing open(2) and unlink(2) atomically. The filesystem support is added through i_op->tmpfile. We basically copy the create operation except we get rid of the link and name related stuff and add the new node to unlinked set. We also add support for linkat(2) to link tmpfile. However, since all previous file operation will skip ZIL, we force a txg_wait_synced to make sure we are sync safe. Signed-off-by: Chunwei Chen <[email protected]>
Diffstat (limited to 'tests')
-rw-r--r--tests/runfiles/linux.run3
-rw-r--r--tests/zfs-tests/tests/functional/Makefile.am1
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/.gitignore3
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/Makefile.am15
-rwxr-xr-xtests/zfs-tests/tests/functional/tmpfile/cleanup.ksh34
-rwxr-xr-xtests/zfs-tests/tests/functional/tmpfile/setup.ksh39
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/tmpfile_001_pos.c109
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c98
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/tmpfile_003_pos.c68
-rw-r--r--tests/zfs-tests/tests/functional/tmpfile/tmpfile_test.c52
10 files changed, 422 insertions, 0 deletions
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 9d63d6853..429098435 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -586,6 +586,9 @@ tests = ['sparse_001_pos']
#[tests/functional/threadsappend]
#tests = ['threadsappend_001_pos']
+[tests/functional/tmpfile]
+tests = ['tmpfile_001_pos', 'tmpfile_002_pos', 'tmpfile_003_pos']
+
[tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am
index ffba71b51..670f994fe 100644
--- a/tests/zfs-tests/tests/functional/Makefile.am
+++ b/tests/zfs-tests/tests/functional/Makefile.am
@@ -50,6 +50,7 @@ SUBDIRS = \
snapused \
sparse \
threadsappend \
+ tmpfile \
truncate \
upgrade \
userquota \
diff --git a/tests/zfs-tests/tests/functional/tmpfile/.gitignore b/tests/zfs-tests/tests/functional/tmpfile/.gitignore
new file mode 100644
index 000000000..591158154
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/.gitignore
@@ -0,0 +1,3 @@
+/tmpfile_test
+/tmpfile_001_pos
+/tmpfile_002_pos
diff --git a/tests/zfs-tests/tests/functional/tmpfile/Makefile.am b/tests/zfs-tests/tests/functional/tmpfile/Makefile.am
new file mode 100644
index 000000000..411445217
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/config/Rules.am
+
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/tmpfile
+
+dist_pkgdata_SCRIPTS = \
+ cleanup.ksh \
+ setup.ksh
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/tmpfile
+
+pkgexec_PROGRAMS = tmpfile_test tmpfile_001_pos tmpfile_002_pos tmpfile_003_pos
+tmpfile_test_SOURCES= tmpfile_test.c
+tmpfile_001_pos_SOURCES = tmpfile_001_pos.c
+tmpfile_002_pos_SOURCES = tmpfile_002_pos.c
+tmpfile_003_pos_SOURCES = tmpfile_003_pos.c
diff --git a/tests/zfs-tests/tests/functional/tmpfile/cleanup.ksh b/tests/zfs-tests/tests/functional/tmpfile/cleanup.ksh
new file mode 100755
index 000000000..3166bd6ec
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/tmpfile/setup.ksh b/tests/zfs-tests/tests/functional/tmpfile/setup.ksh
new file mode 100755
index 000000000..243a5b779
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/setup.ksh
@@ -0,0 +1,39 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+if ! $STF_SUITE/tests/functional/tmpfile/tmpfile_test /tmp; then
+ log_unsupported "The kernel doesn't support O_TMPFILE."
+fi
+
+DISK=${DISKS%% *}
+default_setup $DISK
diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_001_pos.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_001_pos.c
new file mode 100644
index 000000000..c2c02c5d4
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_001_pos.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+/* backward compat in case it's not defined */
+#ifndef O_TMPFILE
+#define O_TMPFILE (020000000|O_DIRECTORY)
+#endif
+
+/*
+ * DESCRIPTION:
+ * Verify we can create tmpfile.
+ *
+ * STRATEGY:
+ * 1. open(2) with O_TMPFILE.
+ * 2. write(2) random data to it, then read(2) and compare.
+ * 3. fsetxattr(2) random data, then fgetxattr(2) and compare.
+ * 4. Verify the above operations run successfully.
+ *
+ */
+
+#define BSZ 64
+
+void
+fill_random(char *buf, int len)
+{
+ int i;
+ srand(time(NULL));
+ for (i = 0; i < len; i++) {
+ buf[i] = (char)rand();
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i, fd;
+ char buf1[BSZ], buf2[BSZ] = {};
+ char *penv[] = {"TESTDIR"};
+
+ (void) fprintf(stdout, "Verify O_TMPFILE is working properly.\n");
+
+ /*
+ * Get the environment variable values.
+ */
+ for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
+ if ((penv[i] = getenv(penv[i])) == NULL) {
+ (void) fprintf(stderr, "getenv(penv[%d])\n", i);
+ exit(1);
+ }
+ }
+
+ fill_random(buf1, BSZ);
+
+ fd = open(penv[0], O_RDWR|O_TMPFILE, 0666);
+ if (fd < 0) {
+ perror("open");
+ exit(2);
+ }
+
+ if (write(fd, buf1, BSZ) < 0) {
+ perror("write");
+ close(fd);
+ exit(3);
+ }
+
+ if (pread(fd, buf2, BSZ, 0) < 0) {
+ perror("pread");
+ close(fd);
+ exit(4);
+ }
+
+ if (memcmp(buf1, buf2, BSZ) != 0) {
+ fprintf(stderr, "data corrupted\n");
+ close(fd);
+ exit(5);
+ }
+
+ memset(buf2, 0, BSZ);
+
+ if (fsetxattr(fd, "user.test", buf1, BSZ, 0) < 0) {
+ perror("fsetxattr");
+ close(fd);
+ exit(6);
+ }
+
+ if (fgetxattr(fd, "user.test", buf2, BSZ) < 0) {
+ perror("fgetxattr");
+ close(fd);
+ exit(7);
+ }
+
+ if (memcmp(buf1, buf2, BSZ) != 0) {
+ fprintf(stderr, "xattr corrupted\n");
+ close(fd);
+ exit(8);
+ }
+
+ close(fd);
+
+ return (0);
+}
diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c
new file mode 100644
index 000000000..c92e6127d
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_002_pos.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/* backward compat in case it's not defined */
+#ifndef O_TMPFILE
+#define O_TMPFILE (020000000|O_DIRECTORY)
+#endif
+
+/*
+ * DESCRIPTION:
+ * Verify we can link tmpfile.
+ *
+ * STRATEGY:
+ * 1. open(2) with O_TMPFILE.
+ * 2. linkat(2).
+ * 3. freeze the pool, export and re-import the pool.
+ * 3. stat(2) the path to verify it has been created.
+ *
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int i, fd, ret;
+ char spath[1024], dpath[1024];
+ char *penv[] = {"TESTDIR", "TESTFILE0"};
+ struct stat sbuf;
+
+ (void) fprintf(stdout, "Verify O_TMPFILE file can be linked.\n");
+
+ /*
+ * Get the environment variable values.
+ */
+ for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
+ if ((penv[i] = getenv(penv[i])) == NULL) {
+ (void) fprintf(stderr, "getenv(penv[%d])\n", i);
+ exit(1);
+ }
+ }
+
+ fd = open(penv[0], O_RDWR|O_TMPFILE, 0666);
+ if (fd < 0) {
+ perror("open");
+ exit(2);
+ }
+
+ snprintf(spath, 1024, "/proc/self/fd/%d", fd);
+ snprintf(dpath, 1024, "%s/%s", penv[0], penv[1]);
+ if (linkat(AT_FDCWD, spath, AT_FDCWD, dpath, AT_SYMLINK_FOLLOW) < 0) {
+ perror("linkat");
+ close(fd);
+ exit(3);
+ }
+
+ if ((ret = system("sudo zpool freeze $TESTPOOL"))) {
+ if (ret == -1)
+ perror("system \"zpool freeze\"");
+ else
+ fprintf(stderr, "zpool freeze exits with %d\n",
+ WEXITSTATUS(ret));
+ exit(4);
+ }
+
+ close(fd);
+
+ if ((ret = system("sudo zpool export $TESTPOOL"))) {
+ if (ret == -1)
+ perror("system \"zpool export\"");
+ else
+ fprintf(stderr, "zpool export exits with %d\n",
+ WEXITSTATUS(ret));
+ exit(4);
+ }
+
+ if ((ret = system("sudo zpool import $TESTPOOL"))) {
+ if (ret == -1)
+ perror("system \"zpool import\"");
+ else
+ fprintf(stderr, "zpool import exits with %d\n",
+ WEXITSTATUS(ret));
+ exit(4);
+ }
+
+ if (stat(dpath, &sbuf) < 0) {
+ perror("stat");
+ unlink(dpath);
+ exit(5);
+ }
+ unlink(dpath);
+
+ return (0);
+}
diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_003_pos.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_003_pos.c
new file mode 100644
index 000000000..477ef3f81
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_003_pos.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+/* backward compat in case it's not defined */
+#ifndef O_TMPFILE
+#define O_TMPFILE (020000000|O_DIRECTORY)
+#endif
+
+/*
+ * DESCRIPTION:
+ * Verify O_EXCL tmpfile cannot be linked.
+ *
+ * STRATEGY:
+ * 1. open(2) with O_TMPFILE|O_EXCL.
+ * 2. linkat(2).
+ * 3. stat(2) the path to verify it wasn't created.
+ *
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int i, fd;
+ char spath[1024], dpath[1024];
+ char *penv[] = {"TESTDIR", "TESTFILE0"};
+ struct stat sbuf;
+
+ (void) fprintf(stdout, "Verify O_EXCL tmpfile cannot be linked.\n");
+
+ /*
+ * Get the environment variable values.
+ */
+ for (i = 0; i < sizeof (penv) / sizeof (char *); i++) {
+ if ((penv[i] = getenv(penv[i])) == NULL) {
+ (void) fprintf(stderr, "getenv(penv[%d])\n", i);
+ exit(1);
+ }
+ }
+
+ fd = open(penv[0], O_RDWR|O_TMPFILE|O_EXCL, 0666);
+ if (fd < 0) {
+ perror("open");
+ exit(2);
+ }
+
+ snprintf(spath, 1024, "/proc/self/fd/%d", fd);
+ snprintf(dpath, 1024, "%s/%s", penv[0], penv[1]);
+ if (linkat(AT_FDCWD, spath, AT_FDCWD, dpath, AT_SYMLINK_FOLLOW) == 0) {
+ fprintf(stderr, "linkat returns successfully\n");
+ close(fd);
+ exit(3);
+ }
+
+ if (stat(dpath, &sbuf) == 0) {
+ fprintf(stderr, "stat returns successfully\n");
+ close(fd);
+ exit(4);
+ }
+ close(fd);
+
+ return (0);
+}
diff --git a/tests/zfs-tests/tests/functional/tmpfile/tmpfile_test.c b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_test.c
new file mode 100644
index 000000000..5fb67b47f
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/tmpfile/tmpfile_test.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* backward compat in case it's not defined */
+#ifndef O_TMPFILE
+#define O_TMPFILE (020000000|O_DIRECTORY)
+#endif
+
+/*
+ * DESCRIPTION:
+ * Check if the kernel support O_TMPFILE.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+ struct stat buf;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s dir\n", argv[0]);
+ return (2);
+ }
+ if (stat(argv[1], &buf) < 0) {
+ perror("stat");
+ return (2);
+ }
+ if (!S_ISDIR(buf.st_mode)) {
+ fprintf(stderr, "\"%s\" is not a directory\n", argv[1]);
+ return (2);
+ }
+
+ fd = open(argv[1], O_TMPFILE | O_WRONLY, 0666);
+ if (fd < 0) {
+ /*
+ * Only fail on EISDIR. If we get EOPNOTSUPP, that means
+ * kernel support O_TMPFILE, but the path at argv[1] doesn't.
+ */
+ if (errno == EISDIR) {
+ fprintf(stderr, "kernel doesn't support O_TMPFILE\n");
+ return (1);
+ }
+ perror("open");
+ } else {
+ close(fd);
+ }
+ return (0);
+}