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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 Facebook, Inc. All Rights Reserved.
#
# FS QA Test 290
#
# Test btrfs support for fsverity.
# This test extends the generic fsverity testing by corrupting inline extents,
# preallocated extents, holes, and the Merkle descriptor in a btrfs-aware way.
#
. ./common/preamble
_begin_fstest auto quick verity prealloc
. ./common/filter
. ./common/verity
# Override the default cleanup function.
_cleanup()
{
cd /
_restore_fsverity_signatures
rm -f $tmp.*
}
_supported_fs btrfs
_require_scratch_verity
_require_scratch_nocheck
_require_odirect
_require_xfs_io_command "falloc"
_require_xfs_io_command "pread"
_require_xfs_io_command "pwrite"
_require_btrfs_corrupt_block "value"
_require_btrfs_corrupt_block "offset"
_disable_fsverity_signatures
get_ino() {
local file=$1
stat -c "%i" $file
}
validate() {
local f=$1
local sz=$(_get_filesize $f)
# buffered io
echo $(basename $f)
$XFS_IO_PROG -rc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
# direct io
$XFS_IO_PROG -rdc "pread -q 0 $sz" $f 2>&1 | _filter_scratch
}
# corrupt the data portion of an inline extent
corrupt_inline() {
local f=$SCRATCH_MNT/inl
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 42" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
# inline data starts at disk_bytenr
# overwrite the first u64 with random bogus junk
$BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f disk_bytenr $SCRATCH_DEV
_scratch_mount
validate $f
}
# preallocate a file, then corrupt it by changing it to a regular file
corrupt_prealloc_to_reg() {
local f=$SCRATCH_MNT/prealloc
$XFS_IO_PROG -fc "falloc 0 12k" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
# ensure non-zero at the pre-allocated region on disk
# set extent type from prealloc (2) to reg (1)
$BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type --value 1 $SCRATCH_DEV
_scratch_mount
# now that it's a regular file, reading actually looks at the previously
# preallocated region, so ensure that has non-zero contents.
head -c 5 /dev/zero | tr '\0' X | _fsv_scratch_corrupt_bytes $f 0
validate $f
}
# corrupt a regular file by changing the type to preallocated
corrupt_reg_to_prealloc() {
local f=$SCRATCH_MNT/reg
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
# set type from reg (1) to prealloc (2)
$BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type --value 2 $SCRATCH_DEV
_scratch_mount
validate $f
}
# corrupt a file by punching a hole
corrupt_punch_hole() {
local f=$SCRATCH_MNT/punch
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
# make a new extent in the middle, sync so the writes don't coalesce
$XFS_IO_PROG -c sync $SCRATCH_MNT
$XFS_IO_PROG -fc "pwrite -q -S 0x59 4096 4096" $f
_fsv_enable $f
_scratch_unmount
# change disk_bytenr to 0, representing a hole
$BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr --value 0 \
$SCRATCH_DEV
_scratch_mount
validate $f
}
# plug hole
corrupt_plug_hole() {
local f=$SCRATCH_MNT/plug
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
$XFS_IO_PROG -fc "falloc 4k 4k" $f
_fsv_enable $f
_scratch_unmount
# change disk_bytenr to some value, plugging the hole
$BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr \
--value 13639680 $SCRATCH_DEV
_scratch_mount
validate $f
}
# corrupt the fsverity descriptor item indiscriminately (causes EINVAL)
corrupt_verity_descriptor() {
local f=$SCRATCH_MNT/desc
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
# key for the descriptor item is <inode, BTRFS_VERITY_DESC_ITEM_KEY, 1>,
# 88 is X. So we write 5 Xs to the start of the descriptor
$BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 --value 88 --offset 0 -b 5 \
$SCRATCH_DEV
_scratch_mount
validate $f
}
# specifically target the root hash in the descriptor (causes EIO)
corrupt_root_hash() {
local f=$SCRATCH_MNT/roothash
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
$BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 --value 88 --offset 16 -b 1 \
$SCRATCH_DEV
_scratch_mount
validate $f
}
# corrupt the Merkle tree data itself
corrupt_merkle_tree() {
local f=$SCRATCH_MNT/merkle
$XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f
local ino=$(get_ino $f)
_fsv_enable $f
_scratch_unmount
# key for the descriptor item is <inode, BTRFS_VERITY_MERKLE_ITEM_KEY, 0>,
# 88 is X. So we write 5 Xs to somewhere in the middle of the first
# merkle item
$BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 --value 88 --offset 100 \
-b 5 $SCRATCH_DEV
_scratch_mount
validate $f
}
_scratch_mkfs >/dev/null
_scratch_mount
corrupt_inline
corrupt_prealloc_to_reg
corrupt_reg_to_prealloc
corrupt_punch_hole
corrupt_plug_hole
corrupt_verity_descriptor
corrupt_root_hash
corrupt_merkle_tree
status=0
exit
|