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