summaryrefslogtreecommitdiff
path: root/usr/src/test
diff options
context:
space:
mode:
authorJason King <jasonbking@users.noreply.github.com>2020-06-03 15:03:49 -0500
committerGitHub <noreply@github.com>2020-06-03 15:03:49 -0500
commit71b43f2a12f58ef8bc5a1965a3b742749bb49231 (patch)
tree4369300a9f43681a8ea759142d919982a0db2a31 /usr/src/test
parent1522aad90cd52cad5f5eb1fae5521a9630ae60f0 (diff)
downloadillumos-joyent-release-20200604.tar.gz
OS-8054 inotify watches lead to EBUSY during zfs mount (#305)release-20200604
Portions contributed by: Mike Gerdts <mike.gerdts@joyent.com> Reviewed by: John Levon <john.levon@joyent.com> Reviewed by: Dan McDonald <danmcd@joyent.com> Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/test')
-rw-r--r--usr/src/test/zfs-tests/cmd/watch_dir/Makefile19
-rw-r--r--usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c151
-rw-r--r--usr/src/test/zfs-tests/include/commands.cfg3
-rw-r--r--usr/src/test/zfs-tests/runfiles/smartos.run3
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh74
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh74
6 files changed, 322 insertions, 2 deletions
diff --git a/usr/src/test/zfs-tests/cmd/watch_dir/Makefile b/usr/src/test/zfs-tests/cmd/watch_dir/Makefile
new file mode 100644
index 0000000000..517dff64af
--- /dev/null
+++ b/usr/src/test/zfs-tests/cmd/watch_dir/Makefile
@@ -0,0 +1,19 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+PROG = watch_dir
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.subdirs
diff --git a/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c b/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c
new file mode 100644
index 0000000000..f6d8445162
--- /dev/null
+++ b/usr/src/test/zfs-tests/cmd/watch_dir/watch_dir.c
@@ -0,0 +1,151 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Joyent, Inc.
+ */
+
+/*
+ * This program watches a directory with portfs or inotify, exiting when the
+ * directory is removed. It is useful in tests that ensure that watching a
+ * directory does not prevent it from being used as a mount point.
+ */
+#include <limits.h>
+#include <port.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+
+void
+fail_usage(void)
+{
+ (void) fprintf(stderr, "Usage: watch <portfs|inotify> directory\n");
+ exit(1);
+}
+
+#define MAX_PES 8
+
+void
+watch_port(const char *path)
+{
+ int port;
+ struct file_obj fobj = {0};
+
+ if ((port = port_create()) < 0) {
+ perror("port_create");
+ exit(1);
+ }
+
+ fobj.fo_name = (char *)path;
+ for (;;) {
+ timespec_t ts = {300, 0};
+ port_event_t pe;
+
+ if (port_associate(port, PORT_SOURCE_FILE, (uintptr_t)&fobj,
+ 0, (char *)path) != 0) {
+ perror("port_associate");
+ exit(1);
+ }
+
+ if (port_get(port, &pe, &ts) != 0) {
+ perror("port_get");
+ exit(1);
+ }
+
+ if (pe.portev_events & FILE_DELETE) {
+ (void) printf("DELETE\t%s\n", path);
+ exit(0);
+ }
+ if (pe.portev_events & MOUNTEDOVER) {
+ (void) printf("MOUNTEDOVER\t%s\n", path);
+ }
+ }
+}
+
+void
+watch_inotify(const char *path)
+{
+ int in, wd;
+ struct inotify_event ev;
+
+ if ((in = inotify_init()) < 0) {
+ perror("inotify_init");
+ exit(1);
+ }
+ if ((wd = inotify_add_watch(in, path, IN_DELETE_SELF)) == -1) {
+ perror("inotify_add_watch");
+ exit(1);
+ }
+
+ for (;;) {
+ ssize_t cnt;
+ char evpath[PATH_MAX];
+
+ cnt = read(in, &ev, sizeof (ev));
+ if (cnt != sizeof (ev)) {
+ (void) fprintf(stderr,
+ "read: expected %ld bytes got %ld\n",
+ sizeof (ev), cnt);
+ exit(1);
+ }
+ if (ev.len != 0) {
+ if (ev.len > sizeof (evpath)) {
+ (void) fprintf(stderr, "read: oversize "
+ "path (%u bytes)\n", ev.len);
+ exit(1);
+ }
+ cnt = read(in, evpath, ev.len);
+ if (cnt != ev.len) {
+ (void) fprintf(stderr, "read: expected %ld "
+ "bytes for path, got %ld\n", ev.len, cnt);
+ exit(1);
+ }
+ evpath[ev.len - 1] = '\0';
+ } else {
+ evpath[0] = '\0';
+ }
+ if (ev.mask & IN_DELETE_SELF) {
+ /*
+ * IN_DELETE_SELF events don't appear to include
+ * the path in the event.
+ */
+ (void) printf("DELETE_SELF\n");
+ exit(0);
+ } else {
+ (void) printf("EVENT_%08x\t%s\n", ev.mask, evpath);
+ }
+
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *watcher, *path;
+
+ if (argc != 3) {
+ fail_usage();
+ }
+ watcher = argv[1];
+ path = argv[2];
+
+ if (strcmp(watcher, "portfs") == 0) {
+ watch_port(path);
+ } else if (strcmp(watcher, "inotify") == 0) {
+ watch_inotify(path);
+ } else {
+ fail_usage();
+ }
+
+ return (0);
+}
diff --git a/usr/src/test/zfs-tests/include/commands.cfg b/usr/src/test/zfs-tests/include/commands.cfg
index f9b0bdf7ac..0ad892e2b2 100644
--- a/usr/src/test/zfs-tests/include/commands.cfg
+++ b/usr/src/test/zfs-tests/include/commands.cfg
@@ -196,4 +196,5 @@ export ZFSTEST_FILES='chg_usr_exec
randwritecomp
readmmap
rename_dir
- rm_lnkcnt_zero_file'
+ rm_lnkcnt_zero_file
+ watch_dir'
diff --git a/usr/src/test/zfs-tests/runfiles/smartos.run b/usr/src/test/zfs-tests/runfiles/smartos.run
index 47059b0b52..18e819a301 100644
--- a/usr/src/test/zfs-tests/runfiles/smartos.run
+++ b/usr/src/test/zfs-tests/runfiles/smartos.run
@@ -130,7 +130,8 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_007_pos', 'zfs_mount_008_pos', 'zfs_mount_009_neg',
'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_012_neg',
'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
- 'zfs_mount_encrypted']
+ 'zfs_mount_encrypted', 'zfs_mount_watched_inotify',
+ 'zfs_mount_watched_portfs' ]
[/opt/zfs-tests/tests/functional/cli_root/zfs_program]
tests = ['zfs_program_json']
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh
new file mode 100644
index 0000000000..0f181621e8
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_inotify.ksh
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs mount' should not get EBUSY due to inotify(5) watching a directory
+#
+# STRATEGY:
+# 1. Create a directory
+# 2. Start watching the directory with inotify(5).
+# 3. Create a filesystem
+# 4. Mount the filesystem at the directory created in step 1
+# 5. Destroy the filesystem
+# 6. Remove the directory
+# 7. Verify the watcher saw the directory removal
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS1
+ log_must rm -rf "$TESTDIR/mntpt" "$TESTDIR/watch_dir.log"
+}
+
+log_onexit cleanup
+
+log_assert "'zfs mount' should not get EBUSY due to inotify(5) watching a directory"
+
+# 1. Create a directory.
+log_must mkdir -p "$TESTDIR/mntpt"
+
+# 2. Start watching the directory with inotify(5).
+watch_dir inotify $TESTDIR/mntpt > $TESTDIR/watch_dir.log &
+
+# 3. Create a filesystem
+log_must zfs create $TESTPOOL/$TESTFS1
+
+# 4. Mount the file system at the directory created in step 1
+log_must zfs set mountpoint=$TESTDIR/mntpt $TESTPOOL/$TESTFS1
+
+# 5. Destroy the filesystem
+log_must zfs destroy $TESTPOOL/$TESTFS1
+
+# 6. Remove the directory. The corresponding inotify event will cause the
+# watcher to exit.
+log_must rmdir $TESTDIR/mntpt
+
+# 7. Verify the watcher saw the directory removal. This ensures that the watcher
+# was watching the directory we are interested in.
+wait
+log_must grep -q DELETE_SELF $TESTDIR/watch_dir.log
+
+log_pass "'zfs mount' should not get EBUSY due to inotify(5) watching a directory"
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh
new file mode 100644
index 0000000000..3135203973
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zfs_mount/zfs_mount_watched_portfs.ksh
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2020 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# 'zfs mount' should not get EBUSY due to portfs watching a directory
+#
+# STRATEGY:
+# 1. Create a directory
+# 2. Start watching the directory with port_associate
+# 3. Create a filesystem
+# 4. Mount the filesystem at the directory created in step 1
+# 5. Destroy the filesystem
+# 6. Remove the directory
+# 7. Verify the watcher saw the directory removal
+#
+
+verify_runnable "both"
+
+function cleanup
+{
+ datasetexists $TESTPOOL/$TESTFS1 && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS1
+ log_must rm -rf "$TESTDIR/mntpt" "$TESTDIR/watch_dir.log"
+}
+
+log_onexit cleanup
+
+log_assert "'zfs mount' should not get EBUSY due to portfs watching a directory"
+
+# 1. Create a directory.
+log_must mkdir -p "$TESTDIR/mntpt"
+
+# 2. Start watching the directory with port_associate
+watch_dir portfs $TESTDIR/mntpt > $TESTDIR/watch_dir.log &
+
+# 3. Create a filesystem
+log_must zfs create $TESTPOOL/$TESTFS1
+
+# 4. Mount the file system at the directory created in step 1
+log_must zfs set mountpoint=$TESTDIR/mntpt $TESTPOOL/$TESTFS1
+
+# 5. Destroy the filesystem
+log_must zfs destroy $TESTPOOL/$TESTFS1
+
+# 6. Remove the directory. The corresponding portfs event will cause the
+# watcher to exit.
+log_must rmdir $TESTDIR/mntpt
+
+# 7. Verify the watcher saw the directory removal. This ensures that the watcher
+# was watching the directory we are interested in.
+wait
+log_must grep -q DELETE.$TESTDIR/mntpt $TESTDIR/watch_dir.log
+
+log_pass "'zfs mount' should not get EBUSY due to portfs watching a directory"