git: 11ed0a95bfa7 - main - zfs tests: stop writing to arbitrary devices

From: Eric van Gyzen <vangyzen_at_FreeBSD.org>
Date: Fri, 11 Nov 2022 20:44:29 UTC
The branch main has been updated by vangyzen:

URL: https://cgit.FreeBSD.org/src/commit/?id=11ed0a95bfa76791dc6428eb2d47a986c0c6f8a3

commit 11ed0a95bfa76791dc6428eb2d47a986c0c6f8a3
Author:     Eric van Gyzen <vangyzen@FreeBSD.org>
AuthorDate: 2022-11-03 02:42:54 +0000
Commit:     Eric van Gyzen <vangyzen@FreeBSD.org>
CommitDate: 2022-11-11 20:43:47 +0000

    zfs tests: stop writing to arbitrary devices
    
    TL;DR:  Three ZFS tests created ZFS pools on all unmounted devices listed
    in /etc/fstab, corrupting their contents.  Stop that.
    
    Imagine my surprise when the ESP on my main dev/test VM would "randomly"
    become corrupted, making it unbootable.  Three tests collect various devices
    from the system and try to add them to a test pool.  The test expects this
    to fail because it _assumes_ these devices are in use and ZFS will correctly
    reject the request.
    
    My /etc/fstab has two entries for devices in /dev:
    
        /dev/gpt/swap0  none        swap    sw,trimonce,late
        /dev/gpt/esp0   /boot/efi   msdosfs rw,noauto
    
    Note the `noauto` on the ESP.  In a remarkable example of irony, I chose
    this because it should keep the ESP more protected from corruption;
    in fact, mounting it would have protected it from this case.
    
    The tests added all of these devices to a test pool in a _single command_,
    expecting the command to fail.  The swap device was in use, so the command
    correctly failed, but the ESP was added and therefore corrupted.  However,
    since the command correctly failed, the test didn't notice the ESP problem.
    If each device had been added with its own command, the test _might_ have
    noticed that one of them incorrectly succeeded.  However, two of these
    tests would not have noticed:
    
    hotspare_create_001_neg was incorrectly specified as needing the Solaris
    dumpadm command, so it was skipped.  _Some_ of the test needs that command,
    but it checks for its presence and runs fine without it.
    
    Due to bug 241070, zpool_add_005_pos was marked as an expected failure.
    Due to the coarse level of integration with ATF, this test would still
    "pass" even if it failed for the wrong reason.  I wrote bug 267554 to
    reconsider the use of atf_expect_fail in these tests.
    
    Let's further consider the use of various devices found around the system.
    In addition to devices in /etc/fstab, the tests also used mounted devices
    listed by the `mount` command.  If ZFS behaves correctly, it will refuse
    to added mounted devices and swap devices to a pool.  However, these are
    unit tests used by developers to ensure that ZFS still works after they
    modify it, so it's reasonable to expect ZFS to do the _wrong_ thing
    sometimes.  Using random host devices is unsafe.
    
    Fix the root problem by using only the disks provided via the "disks"
    variable in kyua.conf.  Use one to create a UFS file system and mount it.
    Use another as a swap device.  Use a third as a dump device, but expect
    it to fail due to bug 241070.
    
    While I'm here:
    
    Due to commit 6b6e2954dd65, we can simply add a second dump device and
    remove it in cleanup.  We no longer need to save, replace, and restore the
    pre-existing dump device.
    
    The cleanup_devices function used `camcontrol inquiry` to distinguish disks
    from other devices, such as partitions.  That works fine for SCSI, but not
    for ATA or VirtIO block.  Use `geom disk list` instead.
    
    PR:             241070
    PR:             267554
    Reviewed by:    asomers
    Sponsored by:   Dell Inc.
    Differential Revision:  https://reviews.freebsd.org/D37257
---
 tests/sys/cddl/zfs/include/libtest.kshlib          |  2 +-
 .../zfs/tests/cli_root/zpool_add/zpool_add.kshlib  | 68 ----------------------
 .../tests/cli_root/zpool_add/zpool_add_005_pos.ksh | 29 ++++++---
 .../zfs/tests/cli_root/zpool_add/zpool_add_test.sh |  3 +-
 .../zfs/tests/hotspare/hotspare_add_003_neg.ksh    | 60 +++++++++----------
 .../zfs/tests/hotspare/hotspare_create_001_neg.ksh | 60 +++++++++----------
 tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh |  4 +-
 7 files changed, 83 insertions(+), 143 deletions(-)

diff --git a/tests/sys/cddl/zfs/include/libtest.kshlib b/tests/sys/cddl/zfs/include/libtest.kshlib
index 389f6ac4cfa5..f9872d69cf2e 100644
--- a/tests/sys/cddl/zfs/include/libtest.kshlib
+++ b/tests/sys/cddl/zfs/include/libtest.kshlib
@@ -1856,7 +1856,7 @@ function cleanup_devices #vdevs
 		$ZPOOL labelclear -f $device
 		# Only wipe partition tables for arguments that are disks,
 		# as opposed to slices (which are valid arguments here).
-		if camcontrol inquiry $device >/dev/null 2>&1; then
+		if geom disk list | grep -qx "Geom name: ${device#/dev/}"; then
 			wipe_partition_table $device
 		fi
 	done
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib
index cdb1f1f0b08f..60b1bf86615c 100644
--- a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add.kshlib
@@ -67,74 +67,6 @@ function iscontained
 
 }
 
-#
-# Find the storage device in /etc/fstab
-#
-function find_vfstab_dev
-{
-	typeset vfstab="/etc/fstab"
-	typeset tmpfile="$TMPDIR/fstab.tmp"
-	typeset vfstabdev
-	typeset vfstabdevs=""
-	typeset line
-
-	$CAT $vfstab | $GREP "^/dev/" >$tmpfile
-	while read -r line
-	do
-		vfstabdev=`$ECHO "$line" | $AWK '{print $1}'`
-		vfstabdev=${vfstabdev%%:}
-		vfstabdevs="$vfstabdev $vfstabdevs"
-	done <$tmpfile
-
-	$RM -f $tmpfile
-	$ECHO $vfstabdevs	
-} 
-
-#
-# Find the storage device in /etc/mnttab
-#
-function find_mnttab_dev
-{
-	typeset mnttab="/etc/mnttab"
-	typeset tmpfile="$TMPDIR/mnttab.tmp"
-	typeset mnttabdev
-	typeset mnttabdevs=""
-	typeset line
-
-	$MOUNT | $GREP "^/dev/" >$tmpfile
-	while read -r line
-	do
-		mnttabdev=`$ECHO "$line" | $AWK '{print $1}'`
-		mnttabdev=${mnttabdev%%:}
-		mnttabdevs="$mnttabdev $mnttabdevs"
-	done <$tmpfile
-
-	$RM -f $tmpfile
-	$ECHO $mnttabdevs	
-} 
-
-#
-# Save the systme current dump device configuration
-#
-function save_dump_dev
-{
-
-	typeset dumpdev
-	typeset swapdev
-	typeset swapdevs=""
-	typeset tmpfile="$TMPDIR/swapinfo.tmp"
-	
-	dumpdev=`readlink /dev/dumpdev`
-	swapinfo | $GREP "^/dev/" >$tmpfile
-	while read -r line
-	do
-		swapdev=`$ECHO "$line" | $AWK '{print $1}'`
-		swapdev=${swapdev%%:}
-		swapdevs="$swapdev $swapdevs"
-	done <$tmpfile
-	$ECHO "$dumpdev $swapdevs"
-}
-
 #
 # Common cleanup routine for partitions used in testing
 #
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh
index 8bd0220a9fe0..ba0c95f59836 100644
--- a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_005_pos.ksh
@@ -70,29 +70,40 @@ function cleanup
 	poolexists "$TESTPOOL1" && \
 		destroy_pool "$TESTPOOL1"
 
-	$DUMPON -r $dump_dev
+	log_onfail $UMOUNT $TMPDIR/mounted_dir
+	log_onfail $SWAPOFF $swap_dev
+	log_onfail $DUMPON -r $dump_dev
 }
 
 log_assert "'zpool add' should fail with inapplicable scenarios."
 
 log_onexit cleanup
 
-mnttab_dev=$(find_mnttab_dev)
-vfstab_dev=$(find_vfstab_dev)
-dump_dev=${DISK2}
-
 create_pool "$TESTPOOL" "${DISK0}"
 log_must poolexists "$TESTPOOL"
 
 create_pool "$TESTPOOL1" "${DISK1}"
 log_must poolexists "$TESTPOOL1"
-log_mustnot $ZPOOL add -f "$TESTPOOL" ${DISK1}
 
-log_mustnot $ZPOOL add -f "$TESTPOOL" $mnttab_dev
+mounted_dev=${DISK2}
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
 
-log_mustnot $ZPOOL add -f "$TESTPOOL" $vfstab_dev
+swap_dev=${DISK3}
+log_must $SWAPON $swap_dev
 
+dump_dev=${DISK4}
 log_must $DUMPON $dump_dev
-log_mustnot $ZPOOL add -f "$TESTPOOL" $dump_dev
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" ${DISK1}
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" $mounted_dev
+
+log_mustnot $ZPOOL add -f "$TESTPOOL" $swap_dev
+
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, change this to log_mustnot.
+log_must $ZPOOL add -f "$TESTPOOL" $dump_dev
 
 log_pass "'zpool add' should fail with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh
index 19d3e17b6a5e..86a18bf68513 100755
--- a/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh
+++ b/tests/sys/cddl/zfs/tests/cli_root/zpool_add/zpool_add_test.sh
@@ -148,8 +148,7 @@ zpool_add_005_pos_body()
 	. $(atf_get_srcdir)/zpool_add.kshlib
 	. $(atf_get_srcdir)/zpool_add.cfg
 
-	verify_disk_count "$DISKS" 3
-	atf_expect_fail "PR 241070 dumpon opens geom devices non-exclusively"
+	verify_disk_count "$DISKS" 5
 	ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
 	ksh93 $(atf_get_srcdir)/zpool_add_005_pos.ksh || atf_fail "Testcase failed"
 }
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh
index 4f4e139b7b4a..d95e0caa9dd5 100644
--- a/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_add_003_neg.ksh
@@ -39,11 +39,11 @@
 # DESCRIPTION: 
 # 'zpool add' with hot spares will fail
 # while the hot spares belong to the following cases:
-#	- nonexist device,
+#	- nonexistent device,
 #	- part of an active pool,
 #	- currently mounted,
-#	- devices in /etc/vfstab,
-#	- specified as the dedicated dump device,
+#	- a swap device,
+#	- a dump device,
 #	- identical with the basic or spares vdev within the pool,
 #	- belong to a exported or potentially active ZFS pool,
 #	- a volume device that belong to the given pool,
@@ -72,15 +72,9 @@ function cleanup
 	poolexists "$TESTPOOL1" && \
 		destroy_pool "$TESTPOOL1"
 
-	if [[ -n $saved_dump_dev ]]; then
-		if [[ -n $DUMPADM ]]; then
-			log_must $DUMPADM -u -d $saved_dump_dev
-		fi
-	fi
-
-	if [[ -n $DUMPADM ]]; then
-		cleanup_devices $dump_dev
-	fi
+	log_onfail $UMOUNT $TMPDIR/mounted_dir
+	log_onfail $SWAPOFF $swap_dev
+	log_onfail $DUMPON -r $dump_dev
 
 	partition_cleanup
 }
@@ -91,11 +85,10 @@ log_onexit cleanup
 
 set_devs
 
-mnttab_dev=$(find_mnttab_dev)
-vfstab_dev=$(find_vfstab_dev)
-saved_dump_dev=$(save_dump_dev)
-dump_dev=${disk}s0
-nonexist_dev=${disk}sbad_slice_num
+mounted_dev=${DISK0}
+swap_dev=${DISK1}
+dump_dev=${DISK2}
+nonexist_dev=${DISK2}bad_slice_num
 
 create_pool "$TESTPOOL" "${pooldevs[0]}"
 log_must poolexists "$TESTPOOL"
@@ -103,19 +96,25 @@ log_must poolexists "$TESTPOOL"
 create_pool "$TESTPOOL1" "${pooldevs[1]}"
 log_must poolexists "$TESTPOOL1"
 
-[[ -n $mnttab_dev ]] || log_note "No mnttab devices found"
-[[ -n $vfstab_dev ]] || log_note "No vfstab devices found"
-#	- nonexist device,
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
+
+log_must $SWAPON $swap_dev
+
+log_must $DUMPON $dump_dev
+
+#	- nonexistent device,
 #	- part of an active pool,
 #	- currently mounted,
-#	- devices in /etc/vfstab,
+#	- a swap device,
 #	- identical with the basic or spares vdev within the pool,
 
 set -A arg "$nonexist_dev" \
 	"${pooldevs[0]}" \
 	"${pooldevs[1]}" \
-	"$mnttab_dev" \
-	"$vfstab_dev"
+	"$mounted_dev" \
+	"$swap_dev"
 
 typeset -i i=0
 while (( i < ${#arg[*]} )); do
@@ -126,14 +125,13 @@ while (( i < ${#arg[*]} )); do
 	(( i = i + 1 ))
 done
 
-#	- specified as the dedicated dump device,
-# This part of the test can only be run on platforms for which DUMPADM is
-# defined; ie Solaris
-if [[ -n $DUMPADM ]]; then
-	log_must $DUMPADM -u -d /dev/$dump_dev
-	log_mustnot $ZPOOL add "$TESTPOOL" spare $dump_dev
-	log_mustnot $ZPOOL add -f "$TESTPOOL" spare $dump_dev
-fi
+#	- a dump device,
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, add $dump_dev to $arg and remove this block.
+log_must $ZPOOL add $TESTPOOL spare ${dump_dev}
+log_must $ZPOOL remove $TESTPOOL ${dump_dev}
+log_must $ZPOOL add -f $TESTPOOL spare ${dump_dev}
+log_must $ZPOOL remove $TESTPOOL ${dump_dev}
 
 #	- belong to a exported or potentially active ZFS pool,
 
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh
index dc930cb82238..2f4dea7fbb17 100644
--- a/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_create_001_neg.ksh
@@ -40,11 +40,11 @@
 # 'zpool create [-f]' with hot spares will fail 
 # while the hot spares belong to the following cases:
 #	- existing pool
-#	- nonexist device,
+#	- nonexistent device,
 #	- part of an active pool,
 #	- currently mounted,
-#	- devices in /etc/vfstab,
-#	- specified as the dedicated dump device,
+#	- a swap device,
+#	- a dump device,
 #	- identical with the basic vdev within the pool,
 #
 # STRATEGY:
@@ -72,11 +72,9 @@ function cleanup
 		destroy_pool $pool
 	done
 
-	if [[ -n $saved_dump_dev ]]; then
-        if [[ -n $DUMPADM ]]; then
-            log_must $DUMPADM -u -d $saved_dump_dev
-        fi
-	fi
+	log_onfail $UMOUNT $TMPDIR/mounted_dir
+	log_onfail $SWAPOFF $swap_dev
+	log_onfail $DUMPON -r $dump_dev
 
 	partition_cleanup
 }
@@ -87,28 +85,35 @@ log_onexit cleanup
 
 set_devs
 
-mnttab_dev=$(find_mnttab_dev)
-vfstab_dev=$(find_vfstab_dev)
-saved_dump_dev=$(save_dump_dev)
-dump_dev=${disk}s0
+mounted_dev=${DISK0}
+swap_dev=${DISK1}
+dump_dev=${DISK2}
 nonexist_dev=${disk}sbad_slice_num
 
 create_pool "$TESTPOOL" ${pooldevs[0]}
 
+log_must $MKDIR $TMPDIR/mounted_dir
+log_must $NEWFS $mounted_dev
+log_must $MOUNT $mounted_dev $TMPDIR/mounted_dir
+
+log_must $SWAPON $swap_dev
+
+log_must $DUMPON $dump_dev
+
 #
 # Set up the testing scenarios parameters
 #	- existing pool
-#	- nonexist device,
+#	- nonexistent device,
 #	- part of an active pool,
 #	- currently mounted,
-#	- devices in /etc/vfstab,
+#	- a swap device,
 #	- identical with the basic vdev within the pool,
 
 set -A arg "$TESTPOOL ${pooldevs[1]} spare ${pooldevs[2]}" \
 	"$TESTPOOL1 ${pooldevs[1]} spare $nonexist_dev" \
 	"$TESTPOOL1 ${pooldevs[1]} spare ${pooldevs[0]}" \
-	"$TESTPOOL1 ${pooldevs[1]} spare $mnttab_dev" \
-	"$TESTPOOL1 ${pooldevs[1]} spare $vfstab_dev" \
+	"$TESTPOOL1 ${pooldevs[1]} spare $mounted_dev" \
+	"$TESTPOOL1 ${pooldevs[1]} spare $swap_dev" \
 	"$TESTPOOL1 ${pooldevs[1]} spare ${pooldevs[1]}"
 
 typeset -i i=0
@@ -118,22 +123,15 @@ while (( i < ${#arg[*]} )); do
 	(( i = i + 1 ))
 done
 
+#	- a dump device,
+# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=241070
+# When that bug is fixed, add $dump_dev to $arg and remove this block.
+log_must $ZPOOL create $TESTPOOL1 ${pooldevs[1]} spare $dump_dev
+log_must $ZPOOL destroy -f $TESTPOOL1
+log_must $ZPOOL create -f $TESTPOOL1 ${pooldevs[1]} spare $dump_dev
+log_must $ZPOOL destroy -f $TESTPOOL1
+
 # now destroy the pool to be polite
 log_must $ZPOOL destroy -f $TESTPOOL
 
-#
-#	- specified as the dedicated dump device,
-# This part of the test can only be run on platforms for which DUMPADM is
-# defined; ie Solaris
-#
-if [[ -n $DUMPADM ]]; then
-    # create/destroy a pool as a simple way to set the partitioning
-    # back to something normal so we can use this $disk as a dump device
-    cleanup_devices $dump_dev
-
-    log_must $DUMPADM -u -d /dev/$dump_dev
-    log_mustnot $ZPOOL create $TESTPOOL1 ${pooldevs[1]} spare "$dump_dev"
-    log_mustnot $ZPOOL create -f $TESTPOOL1 ${pooldevs[1]} spare "$dump_dev"
-fi
-
 log_pass "'zpool create [-f]' with hot spare is failed as expected with inapplicable scenarios."
diff --git a/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh b/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh
index 0299eab8886b..3f7ba56dbe92 100755
--- a/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh
+++ b/tests/sys/cddl/zfs/tests/hotspare/hotspare_test.sh
@@ -90,6 +90,7 @@ hotspare_add_003_neg_body()
 	. $(atf_get_srcdir)/hotspare.kshlib
 	. $(atf_get_srcdir)/hotspare.cfg
 
+	verify_disk_count "$DISKS" 3
 	ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
 	ksh93 $(atf_get_srcdir)/hotspare_add_003_neg.ksh || atf_fail "Testcase failed"
 }
@@ -185,7 +186,7 @@ atf_test_case hotspare_create_001_neg cleanup
 hotspare_create_001_neg_head()
 {
 	atf_set "descr" "'zpool create [-f]' with hot spares should be failedwith inapplicable scenarios."
-	atf_set "require.progs" "ksh93 dumpadm zpool"
+	atf_set "require.progs" "ksh93 zpool"
 	atf_set "timeout" 3600
 }
 hotspare_create_001_neg_body()
@@ -194,6 +195,7 @@ hotspare_create_001_neg_body()
 	. $(atf_get_srcdir)/hotspare.kshlib
 	. $(atf_get_srcdir)/hotspare.cfg
 
+	verify_disk_count "$DISKS" 3
 	ksh93 $(atf_get_srcdir)/setup.ksh || atf_fail "Setup failed"
 	ksh93 $(atf_get_srcdir)/hotspare_create_001_neg.ksh || atf_fail "Testcase failed"
 }