1#!/bin/ksh -p
2# SPDX-License-Identifier: CDDL-1.0
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or https://opensource.org/licenses/CDDL-1.0.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright (c) 2020 by Delphix. All rights reserved.
26#
27
28. $STF_SUITE/include/libtest.shlib
29
30#
31# DESCRIPTION:
32# Verify that 'zfs set sharenfs=on', 'zfs share', and 'zfs unshare' can
33# run concurrently. The test creates 50 filesystem and 50 threads.
34# Each thread will run through the test strategy in parallel.
35#
36# STRATEGY:
37# 1. Verify that the file system is not shared.
38# 2. Enable the 'sharenfs' property
39# 3. Invoke 'zfs unshare' and verify filesystem is no longer shared
40# 4. Invoke 'zfs share'.
41# 4. Verify that the file system is shared.
42# 5. Verify that a shared filesystem cannot be shared again.
43# 6. Verify that share -a succeeds.
44#
45
46verify_runnable "global"
47
48function cleanup
49{
50	wait
51	for fs in {0..50}
52	do
53		for pfs in $TESTFS1 $TESTFS2 $TESTFS3
54		do
55			log_must zfs set sharenfs=off $TESTPOOL/$pfs/$fs
56			unshare_fs $TESTPOOL/$pfs/$fs
57
58			if mounted $TESTPOOL/$pfs/$fs; then
59				log_must zfs unmount $TESTPOOL/$pfs/$fs
60			fi
61
62			datasetexists $TESTPOOL/$pfs/$fs && \
63				destroy_dataset $TESTPOOL/$pfs/$fs -f
64		done
65	done
66
67	log_must zfs share -a
68}
69
70function create_filesystems
71{
72	for fs in {0..50}
73	do
74		log_must zfs create -p $TESTPOOL/$TESTFS1/$fs
75		log_must zfs create -p $TESTPOOL/$TESTFS2/$fs
76		log_must zfs create -p $TESTPOOL/$TESTFS3/$fs
77	done
78}
79
80function sub_fail
81{
82	log_note $$: "$@"
83	exit 1
84}
85
86#
87# Main test routine.
88#
89# Given a file system this routine will attempt
90# share the mountpoint and then verify it has been shared.
91#
92function test_share # filesystem
93{
94	typeset filesystem=$1
95	typeset mntp=$(get_prop mountpoint $filesystem)
96
97	not_shared $mntp || \
98	    sub_fail "File system $filesystem is already shared."
99
100	zfs set sharenfs=on $filesystem || \
101	    sub_fail "zfs set sharenfs=on $filesystem failed."
102
103	#
104	# Verify 'zfs share' results in a shared mount.  We check this
105	# multiple times because of Fedora 37+ it's been observed in
106	# the CI that the share may not be immediately reported.
107	#
108	for retry in $(seq 1 10); do
109		is_shared $mntp && break
110
111		log_note "Wait $retry / 10 for is_shared $mntp (set sharenfs)"
112
113		if [[ $retry -eq 10 ]]; then
114			sub_fail "File system $filesystem is not shared (set sharenfs)."
115		fi
116
117		sleep 1
118	done
119
120	#
121	# Verify 'zfs unshare' works as well.
122	#
123	zfs unshare $filesystem || \
124	    sub_fail "zfs unshare $filesystem failed."
125	is_shared $mntp && \
126	    sub_fail "File system $filesystem is still shared."
127
128
129	zfs share $filesystem || \
130	    sub_fail "zfs share $filesystem failed."
131
132	#
133	# Verify 'zfs share' results in a shared mount.  We check this
134	# multiple times because of Fedora 37+ it's been observed in
135	# the CI that the share may not be immediately reported.
136	#
137	for retry in $(seq 1 10); do
138		is_shared $mntp && break
139
140		log_note "Wait $retry / 10 for is_shared $mntp (zfs share)"
141
142		if [[ $retry -eq 10 ]]; then
143			sub_fail "File system $filesystem is not shared (zfs share)."
144		fi
145
146		sleep 1
147	done
148
149	#log_note "Sharing a shared file system fails."
150	zfs share $filesystem && \
151	    sub_fail "zfs share $filesystem did not fail"
152
153	return 0
154}
155
156function unshare_fs_nolog
157{
158	typeset fs=$1
159
160	if is_shared $fs || is_shared_smb $fs; then
161		zfs unshare $fs ||
162		    sub_fail "zfs unshare $fs: $?"
163	fi
164}
165
166#
167# Set the main process id so that we know to capture
168# failures from child processes and allow the parent process
169# to report the failure.
170#
171set_main_pid $$
172log_assert "Verify that 'zfs share' succeeds as root."
173log_onexit cleanup
174
175create_filesystems
176
177child_pids=()
178for fs in {0..50}
179do
180	for pfs in $TESTFS1 $TESTFS2 $TESTFS3
181	do
182		test_share $TESTPOOL/$pfs/$fs &
183		child_pids+=($!)
184		log_note "$TESTPOOL/$pfs/$fs ==> $!"
185	done
186done
187log_must wait_for_children "${child_pids[@]}"
188
189log_note "Verify 'zfs share -a' succeeds."
190
191#
192# Unshare each of the file systems.
193#
194child_pids=()
195for fs in {0..50}
196do
197	for pfs in $TESTFS1 $TESTFS2 $TESTFS3
198	do
199		unshare_fs_nolog $TESTPOOL/$pfs/$fs &
200		child_pids+=($!)
201		log_note "$TESTPOOL/$pfs/$fs (unshare) ==> $!"
202	done
203done
204log_must wait_for_children "${child_pids[@]}"
205
206#
207# Try a zfs share -a and verify all file systems are shared.
208#
209log_must zfs share -a
210
211#
212# We need to unset __ZFS_POOL_EXCLUDE so that we include all file systems
213# in the os-specific zfs exports file. This will be reset by the next test.
214#
215unset __ZFS_POOL_EXCLUDE
216
217for fs in {0..50}
218do
219	for pfs in $TESTFS1 $TESTFS2 $TESTFS3
220	do
221		log_must is_shared $TESTPOOL/$pfs/$fs
222		log_must is_exported $TESTPOOL/$pfs/$fs
223	done
224done
225
226log_pass "'zfs share [-a] <filesystem>' succeeds as root."
227