diff mbox series

btrfs: add test for shifting devt

Message ID 27bad2e06121a6cd5cb34146e37b8e2dc46dec0c.1709231457.git.boris@bur.io (mailing list archive)
State New, archived
Headers show
Series btrfs: add test for shifting devt | expand

Commit Message

Boris Burkov Feb. 29, 2024, 6:36 p.m. UTC
---
 common/config       |   1 +
 common/rc           |   4 ++
 tests/btrfs/303     | 127 ++++++++++++++++++++++++++++++++++++++++++++
 tests/btrfs/303.out |   2 +
 4 files changed, 134 insertions(+)
 create mode 100755 tests/btrfs/303
 create mode 100644 tests/btrfs/303.out

Comments

Boris Burkov Feb. 29, 2024, 11:32 p.m. UTC | #1
This is a pretty poor submission, I forgot to write a commit message and
there are some random obvious problems with the style of the test
itself. Please disregard until I resend.

Sorry for the spam!
Boris

On Thu, Feb 29, 2024 at 10:36:41AM -0800, Boris Burkov wrote:
> ---
>  common/config       |   1 +
>  common/rc           |   4 ++
>  tests/btrfs/303     | 127 ++++++++++++++++++++++++++++++++++++++++++++
>  tests/btrfs/303.out |   2 +
>  4 files changed, 134 insertions(+)
>  create mode 100755 tests/btrfs/303
>  create mode 100644 tests/btrfs/303.out
> 
> diff --git a/common/config b/common/config
> index a3b15b96f..43b517fda 100644
> --- a/common/config
> +++ b/common/config
> @@ -235,6 +235,7 @@ export BLKZONE_PROG="$(type -P blkzone)"
>  export GZIP_PROG="$(type -P gzip)"
>  export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
>  export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
> +export PARTED_PROG="$(type -P parted)"
>  
>  # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
>  # newer systems have udevadm command but older systems like RHEL5 don't.
> diff --git a/common/rc b/common/rc
> index 30c44dddd..8e009aca9 100644
> --- a/common/rc
> +++ b/common/rc
> @@ -5375,6 +5375,10 @@ _require_unshare() {
>  		_notrun "unshare $*: command not found, should be in util-linux"
>  }
>  
> +_require_parted() {
> +	$PARTED_PROG --list &>/dev/null || _notrun "parted: command not found"
> +}
> +
>  # Return a random file in a directory. A directory is *not* followed
>  # recursively.
>  _random_file() {
> diff --git a/tests/btrfs/303 b/tests/btrfs/303
> new file mode 100755
> index 000000000..dece3eacc
> --- /dev/null
> +++ b/tests/btrfs/303
> @@ -0,0 +1,127 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2024 Meta, Inc. All Rights Reserved.
> +#
> +# FS QA Test 303
> +#
> +# Test an edge case of multi device volume management in btrfs.
> +# If a device changes devt between mounts of a multi device fs, we can trick
> +# btrfs into mounting the same device twice fully (not as a bind mount). From
> +# there, it is trivial to induce corruption.
> +#
> +. ./common/preamble
> +_begin_fstest auto quick volume
> +
> +# Override the default cleanup function.
> +# _cleanup()
> +# {
> +# 	cd /
> +# 	rm -r -f $tmp.*
> +# }
> +
> +# Import common functions.
> +# . ./common/filter
> +
> +# real QA test starts here
> +
> +# Modify as appropriate.
> +_supported_fs btrfs
> +_require_test
> +_require_parted
> +
> +#BARE_MOUNT_PROG=$here/src/bare-mount
> +
> +_cleanup() {
> +	cd /
> +	umount $MNT
> +	umount $BIND
> +	losetup -d $DEV0
> +	losetup -d $DEV1
> +	losetup -d $DEV2
> +	rm $IMG0
> +	rm $IMG1
> +	rm $IMG2
> +}
> +
> +do_mkpart() {
> +	local dev=$1
> +	$PARTED_PROG $dev 'mkpart mypart 1M 100%' --script
> +}
> +
> +do_rmpart() {
> +	local dev=$1
> +	$PARTED_PROG $dev 'rm 1' --script
> +}
> +
> +# Prepare 3 loop devices on the test device
> +IMG0=$TEST_DIR/$$.img0
> +IMG1=$TEST_DIR/$$.img1
> +IMG2=$TEST_DIR/$$.img2
> +truncate -s 1G $IMG0
> +truncate -s 1G $IMG1
> +truncate -s 1G $IMG2
> +DEV0=$(losetup -f $IMG0 --show)
> +DEV1=$(losetup -f $IMG1 --show)
> +DEV2=$(losetup -f $IMG2 --show)
> +D0P1=$DEV0"p1"
> +D1P1=$DEV1"p1"
> +MNT=$TEST_DIR/mnt
> +BIND=$TEST_DIR/bind
> +
> +# Setup partition table with one partition on each device
> +$PARTED_PROG $DEV0 'mktable gpt' --script
> +$PARTED_PROG $DEV1 'mktable gpt' --script
> +do_mkpart $DEV0
> +do_mkpart $DEV1
> +
> +# mkfs with two devices to avoid clearing devices on close
> +# single raid to allow removing DEV2
> +$MKFS_BTRFS_PROG -f -msingle -dsingle $D0P1 $DEV2 &>/dev/null
> +
> +# Cycle mount the two device fs to populate both devices into the
> +# stale device cache
> +mkdir -p $MNT
> +mount $D0P1 $MNT
> +umount $MNT
> +
> +# Swap the partition dev_ts. This leaves the dev_t in the cache out of date.
> +do_rmpart $DEV0
> +do_rmpart $DEV1
> +do_mkpart $DEV1
> +do_mkpart $DEV0
> +
> +# Mount with mismatched dev_t!
> +mount $D0P1 $MNT || _fail "failed to remount; don't proceed and do dangerous stuff on raw mount point"
> +
> +# Remove extra device to bring temp-fsid back in the fray
> +$BTRFS_UTIL_PROG device remove $DEV2 $MNT
> +
> +# Create the should be bind mount
> +mkdir -p $BIND
> +mount $D0P1 $BIND
> +mount_show=$($BTRFS_UTIL_PROG filesystem show $MNT)
> +bind_show=$($BTRFS_UTIL_PROG filesystem show $BIND)
> +# If they're different, we are in trouble.
> +[ "$mount_show" = "$bind_show" ] || echo "$mount_show != $bind_show"
> +
> +# now prove it by corrupting it
> +for i in $(seq 20); do
> +	# TODO diff prog
> +	dd if=/dev/urandom of=$MNT/foo.$i bs=50M count=1 &>/dev/null
> +done
> +for i in $(seq 20); do
> +	# TODO diff prog
> +	dd if=/dev/urandom of=$BIND/foo.$i bs=50M count=1 &>/dev/null
> +done
> +sync
> +find $BIND -type f -delete
> +sync
> +$FSTRIM_PROG $BIND
> +sleep 5
> +echo 3 > /proc/sys/vm/drop_caches
> +$BTRFS_UTIL_PROG scrub start -B $MNT >>$seqres.full 2>&1
> +
> +# success, all done
> +echo "Silence is golden"
> +status=0
> +exit
> diff --git a/tests/btrfs/303.out b/tests/btrfs/303.out
> new file mode 100644
> index 000000000..d48808e60
> --- /dev/null
> +++ b/tests/btrfs/303.out
> @@ -0,0 +1,2 @@
> +QA output created by 303
> +Silence is golden
> -- 
> 2.43.0
>
diff mbox series

Patch

diff --git a/common/config b/common/config
index a3b15b96f..43b517fda 100644
--- a/common/config
+++ b/common/config
@@ -235,6 +235,7 @@  export BLKZONE_PROG="$(type -P blkzone)"
 export GZIP_PROG="$(type -P gzip)"
 export BTRFS_IMAGE_PROG="$(type -P btrfs-image)"
 export BTRFS_MAP_LOGICAL_PROG=$(type -P btrfs-map-logical)
+export PARTED_PROG="$(type -P parted)"
 
 # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled.
 # newer systems have udevadm command but older systems like RHEL5 don't.
diff --git a/common/rc b/common/rc
index 30c44dddd..8e009aca9 100644
--- a/common/rc
+++ b/common/rc
@@ -5375,6 +5375,10 @@  _require_unshare() {
 		_notrun "unshare $*: command not found, should be in util-linux"
 }
 
+_require_parted() {
+	$PARTED_PROG --list &>/dev/null || _notrun "parted: command not found"
+}
+
 # Return a random file in a directory. A directory is *not* followed
 # recursively.
 _random_file() {
diff --git a/tests/btrfs/303 b/tests/btrfs/303
new file mode 100755
index 000000000..dece3eacc
--- /dev/null
+++ b/tests/btrfs/303
@@ -0,0 +1,127 @@ 
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2024 Meta, Inc. All Rights Reserved.
+#
+# FS QA Test 303
+#
+# Test an edge case of multi device volume management in btrfs.
+# If a device changes devt between mounts of a multi device fs, we can trick
+# btrfs into mounting the same device twice fully (not as a bind mount). From
+# there, it is trivial to induce corruption.
+#
+. ./common/preamble
+_begin_fstest auto quick volume
+
+# Override the default cleanup function.
+# _cleanup()
+# {
+# 	cd /
+# 	rm -r -f $tmp.*
+# }
+
+# Import common functions.
+# . ./common/filter
+
+# real QA test starts here
+
+# Modify as appropriate.
+_supported_fs btrfs
+_require_test
+_require_parted
+
+#BARE_MOUNT_PROG=$here/src/bare-mount
+
+_cleanup() {
+	cd /
+	umount $MNT
+	umount $BIND
+	losetup -d $DEV0
+	losetup -d $DEV1
+	losetup -d $DEV2
+	rm $IMG0
+	rm $IMG1
+	rm $IMG2
+}
+
+do_mkpart() {
+	local dev=$1
+	$PARTED_PROG $dev 'mkpart mypart 1M 100%' --script
+}
+
+do_rmpart() {
+	local dev=$1
+	$PARTED_PROG $dev 'rm 1' --script
+}
+
+# Prepare 3 loop devices on the test device
+IMG0=$TEST_DIR/$$.img0
+IMG1=$TEST_DIR/$$.img1
+IMG2=$TEST_DIR/$$.img2
+truncate -s 1G $IMG0
+truncate -s 1G $IMG1
+truncate -s 1G $IMG2
+DEV0=$(losetup -f $IMG0 --show)
+DEV1=$(losetup -f $IMG1 --show)
+DEV2=$(losetup -f $IMG2 --show)
+D0P1=$DEV0"p1"
+D1P1=$DEV1"p1"
+MNT=$TEST_DIR/mnt
+BIND=$TEST_DIR/bind
+
+# Setup partition table with one partition on each device
+$PARTED_PROG $DEV0 'mktable gpt' --script
+$PARTED_PROG $DEV1 'mktable gpt' --script
+do_mkpart $DEV0
+do_mkpart $DEV1
+
+# mkfs with two devices to avoid clearing devices on close
+# single raid to allow removing DEV2
+$MKFS_BTRFS_PROG -f -msingle -dsingle $D0P1 $DEV2 &>/dev/null
+
+# Cycle mount the two device fs to populate both devices into the
+# stale device cache
+mkdir -p $MNT
+mount $D0P1 $MNT
+umount $MNT
+
+# Swap the partition dev_ts. This leaves the dev_t in the cache out of date.
+do_rmpart $DEV0
+do_rmpart $DEV1
+do_mkpart $DEV1
+do_mkpart $DEV0
+
+# Mount with mismatched dev_t!
+mount $D0P1 $MNT || _fail "failed to remount; don't proceed and do dangerous stuff on raw mount point"
+
+# Remove extra device to bring temp-fsid back in the fray
+$BTRFS_UTIL_PROG device remove $DEV2 $MNT
+
+# Create the should be bind mount
+mkdir -p $BIND
+mount $D0P1 $BIND
+mount_show=$($BTRFS_UTIL_PROG filesystem show $MNT)
+bind_show=$($BTRFS_UTIL_PROG filesystem show $BIND)
+# If they're different, we are in trouble.
+[ "$mount_show" = "$bind_show" ] || echo "$mount_show != $bind_show"
+
+# now prove it by corrupting it
+for i in $(seq 20); do
+	# TODO diff prog
+	dd if=/dev/urandom of=$MNT/foo.$i bs=50M count=1 &>/dev/null
+done
+for i in $(seq 20); do
+	# TODO diff prog
+	dd if=/dev/urandom of=$BIND/foo.$i bs=50M count=1 &>/dev/null
+done
+sync
+find $BIND -type f -delete
+sync
+$FSTRIM_PROG $BIND
+sleep 5
+echo 3 > /proc/sys/vm/drop_caches
+$BTRFS_UTIL_PROG scrub start -B $MNT >>$seqres.full 2>&1
+
+# success, all done
+echo "Silence is golden"
+status=0
+exit
diff --git a/tests/btrfs/303.out b/tests/btrfs/303.out
new file mode 100644
index 000000000..d48808e60
--- /dev/null
+++ b/tests/btrfs/303.out
@@ -0,0 +1,2 @@ 
+QA output created by 303
+Silence is golden