summaryrefslogtreecommitdiff
path: root/tests/btrfs/318
blob: 1eb726256880e59eda80cd225ffc990197eadcf1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2024 Meta, Inc. All Rights Reserved.
#
# FS QA Test No. 318
#
# 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 scrub tempfsid

_fixed_by_kernel_commit 9f7eb8405dcb \
	"btrfs: validate device maj:min during open"

# real QA test starts here
_supported_fs btrfs
_require_test
_require_command "$PARTED_PROG" parted
_require_batched_discard "$TEST_DIR"

_cleanup() {
	cd /
	$UMOUNT_PROG $MNT
	$UMOUNT_PROG $BIND
	losetup -d $DEV0 $DEV1 $DEV2
	rm -f $IMG0 $IMG1 $IMG2
	rm -rf $MNT $BIND
}

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=$(_create_loop_device $IMG0)
DEV1=$(_create_loop_device $IMG1)
DEV2=$(_create_loop_device $IMG2)
D0P1=$DEV0"p1"
D1P1=$DEV1"p1"
MNT=$TEST_DIR/mnt-${seq}
BIND=$TEST_DIR/bind-${seq}

# Setup partition table with one partition on each device.
$PARTED_PROG $DEV0 'mktable gpt' --script
$PARTED_PROG $DEV1 'mktable gpt' --script
$PARTED_PROG $DEV0 'mkpart mypart 1M 100%' --script
$PARTED_PROG $DEV1 'mkpart mypart 1M 100%' --script

# mkfs with two devices to avoid clearing devices on close
# single raid to allow removing DEV2.
$MKFS_BTRFS_PROG -f -msingle -dsingle $D0P1 $DEV2 >>$seqres.full 2>&1 || _fail "failed to mkfs.btrfs"

# Cycle mount the two device fs to populate both devices into the
# stale device cache.
rm -rf $MNT
mkdir -p $MNT
_mount $D0P1 $MNT
$UMOUNT_PROG $MNT

# Swap the partition dev_ts. This leaves the dev_t in the cache out of date.
$PARTED_PROG $DEV0 'rm 1' --script
$PARTED_PROG $DEV1 'rm 1' --script
$PARTED_PROG $DEV1 'mkpart mypart 1M 100%' --script
$PARTED_PROG $DEV0 'mkpart mypart 1M 100%' --script

# Mount with mismatched dev_t!
_mount $D0P1 $MNT

# Remove the extra device to bring temp-fsid back in the fray.
$BTRFS_UTIL_PROG device remove $DEV2 $MNT

# Create the would be bind mount.
rm -rf $BIND
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 really prove it by corrupting the first mount with the second.
for i in $(seq 20); do
	$XFS_IO_PROG -f -c "pwrite 0 50M" $MNT/foo.$i >>$seqres.full 2>&1
done
for i in $(seq 20); do
	$XFS_IO_PROG -f -c "pwrite 0 50M" $BIND/foo.$i >>$seqres.full 2>&1
done

# sync so that we really write the large file data out to the shared device
sync

# now delete from one view of the shared device
find $BIND -type f -delete
# sync so that fstrim definitely has deleted data to trim
sync
# This should blow up both mounts, if the writes somehow didn't overlap at all.
$FSTRIM_PROG $BIND
# drop caches to improve the odds we read from the corrupted device while scrubbing.
echo 3 > /proc/sys/vm/drop_caches
$BTRFS_UTIL_PROG scrub start -B $MNT | grep "Error summary:"

status=0
exit