reflink: test CoW operations against the source file
[xfstests-dev.git] / common / reflink
1 ##/bin/bash
2 # Routines for reflinking, deduping, and comparing parts of files.
3 #-----------------------------------------------------------------------
4 #  Copyright (c) 2015 Oracle.  All Rights Reserved.
5 #  This program is free software; you can redistribute it and/or modify
6 #  it under the terms of the GNU General Public License as published by
7 #  the Free Software Foundation; either version 2 of the License, or
8 #  (at your option) any later version.
9 #
10 #  This program is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU General Public License for more details.
14 #
15 #  You should have received a copy of the GNU General Public License
16 #  along with this program; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
18 #  USA
19 #
20 #  Contact information: Oracle Corporation, 500 Oracle Parkway,
21 #  Redwood Shores, CA 94065, USA, or: http://www.oracle.com/
22 #-----------------------------------------------------------------------
23
24 # Check that cp has a reflink argument
25 _require_cp_reflink()
26 {
27        cp --help | grep -q reflink || \
28                _notrun "This test requires a cp with --reflink support."
29 }
30
31 # Given 2 files, verify that they have the same mapping but different
32 # inodes - i.e. an undisturbed reflink
33 # Silent if so, make noise if not
34 _verify_reflink()
35 {
36        # not a hard link or symlink?
37        cmp -s  <(stat -c '%i' $1) <(stat -c '%i' $2) \
38                && echo "$1 and $2 are not reflinks: same inode number"
39
40        # same mapping?
41        diff -u <($XFS_IO_PROG -c "fiemap" $1 | grep -v $1) \
42                <($XFS_IO_PROG -c "fiemap" $2 | grep -v $2) \
43                || echo "$1 and $2 are not reflinks: different extents"
44 }
45
46 # New reflink/dedupe helpers
47
48 # this test requires the test fs support reflink...
49 _require_test_reflink()
50 {
51         _require_test
52         _require_xfs_io_command "reflink"
53
54         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
55         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null
56         "$XFS_IO_PROG" -f -c "reflink $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" > /dev/null
57         if [ ! -s "$TEST_DIR/file2" ]; then
58                 rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
59                 _notrun "Reflink not supported by test filesystem type: $FSTYP"
60         fi
61         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
62 }
63
64 # this test requires the scratch fs support reflink...
65 _require_scratch_reflink()
66 {
67         _require_scratch
68         _require_xfs_io_command "reflink"
69
70         _scratch_mkfs > /dev/null
71         _scratch_mount
72         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null
73         "$XFS_IO_PROG" -f -c "reflink $SCRATCH_MNT/file1 0 0 65536" "$SCRATCH_MNT/file2" > /dev/null
74         if [ ! -s "$SCRATCH_MNT/file2" ]; then
75                 _scratch_unmount
76                 _notrun "Reflink not supported by scratch filesystem type: $FSTYP"
77         fi
78         _scratch_unmount
79 }
80
81 # this test requires the test fs support dedupe...
82 _require_test_dedupe()
83 {
84         _require_test
85         _require_xfs_io_command "dedupe"
86
87         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
88         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null
89         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file2" > /dev/null
90         testio="$("$XFS_IO_PROG" -f -c "dedupe $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" 2>&1)"
91         echo $testio | grep -q "Operation not supported" && \
92                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
93         echo $testio | grep -q "Inappropriate ioctl for device" && \
94                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
95         echo $testio | grep -q "Invalid argument" && \
96                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
97         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
98 }
99
100 # this test requires the scratch fs support dedupe...
101 _require_scratch_dedupe()
102 {
103         _require_scratch
104         _require_xfs_io_command "dedupe"
105
106         _scratch_mkfs > /dev/null
107         _scratch_mount
108         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null
109         "$XFS_IO_PROG" -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file2" > /dev/null
110         testio="$("$XFS_IO_PROG" -f -c "dedupe $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" 2>&1)"
111         echo $testio | grep -q "Operation not supported" && \
112                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
113         echo $testio | grep -q "Inappropriate ioctl for device" && \
114                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
115         echo $testio | grep -q "Invalid argument" && \
116                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
117         _scratch_unmount
118 }
119
120 # Prints a range of a file as a hex dump
121 _read_range() {
122         file="$1"
123         offset="$2"
124         len="$3"
125         xfs_io_args="$4"
126
127         $XFS_IO_PROG $xfs_io_args -f -c "pread -q -v $offset $len" "$file" | cut -d ' ' -f '3-18'
128 }
129
130 # Compare ranges of two files
131 _compare_range() {
132         file1="$1"
133         offset1="$2"
134         file2="$3"
135         offset2="$4"
136         len="$5"
137
138         cmp -s <(_read_range "$file1" "$offset1" "$len") \
139                <(_read_range "$file2" "$offset2" "$len")
140 }
141
142 # Prints the md5 checksum of a hexdump of a part of a given file
143 _md5_range_checksum() {
144         file="$1"
145         offset="$2"
146         len="$3"
147
148         md5sum <(_read_range "$file" "$offset" "$len") | cut -d ' ' -f 1
149 }
150
151 # Reflink some file1 into file2 via cp
152 _cp_reflink() {
153         file1="$1"
154         file2="$2"
155
156         cp --reflink=always "$file1" "$file2"
157 }
158
159 # Reflink some file1 into file2
160 _reflink() {
161         file1="$1"
162         file2="$2"
163
164         "$XFS_IO_PROG" -f -c "reflink $file1" "$file2"
165 }
166
167 # Reflink some part of file1 into another part of file2
168 _reflink_range() {
169         file1="$1"
170         offset1="$2"
171         file2="$3"
172         offset2="$4"
173         len="$5"
174         xfs_io_args="$6"
175
176         "$XFS_IO_PROG" $xfs_io_args -f -c "reflink $file1 $offset1 $offset2 $len" "$file2"
177 }
178
179 # Dedupe some part of file1 into another part of file2
180 _dedupe_range() {
181         file1="$1"
182         offset1="$2"
183         file2="$3"
184         offset2="$4"
185         len="$5"
186         xfs_io_args="$6"
187
188         "$XFS_IO_PROG" $xfs_io_args -f -c "dedupe $file1 $offset1 $offset2 $len" "$file2"
189 }
190
191 # Create a file of interleaved unwritten and reflinked blocks
192 _weave_reflink_unwritten() {
193         blksz=$1
194         nr=$2
195         sfile=$3
196         dfile=$4
197
198         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
199         $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $dfile
200         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
201         seq 0 2 $((nr - 1)) | while read i; do
202                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
203                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
204         done
205 }
206
207 # Create a file of interleaved holes and reflinked blocks
208 _weave_reflink_holes() {
209         blksz=$1
210         nr=$2
211         sfile=$3
212         dfile=$4
213
214         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
215         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
216         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
217         seq 0 2 $((nr - 1)) | while read i; do
218                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
219                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
220         done
221 }
222
223 # For a file created with _weave_reflink_holes, fill the holes with delalloc
224 # extents
225 _weave_reflink_holes_delalloc() {
226         blksz=$1
227         nr=$2
228         dfile=$3
229
230         seq 1 2 $((nr - 1)) | while read i; do
231                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
232                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
233         done
234 }
235
236 # Create a file of interleaved regular blocks and reflinked blocks
237 _weave_reflink_regular() {
238         blksz=$1
239         nr=$2
240         sfile=$3
241         dfile=$4
242
243         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
244         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
245         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile.chk
246         seq 0 2 $((nr - 1)) | while read i; do
247                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
248                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
249         done
250 }
251
252 # Create a file of interleaved holes, unwritten blocks, regular blocks, and
253 # reflinked blocks
254 _weave_reflink_rainbow() {
255         blksz=$1
256         nr=$2
257         sfile=$3
258         dfile=$4
259
260         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
261         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
262         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
263         # 0 blocks are reflinked
264         seq 0 5 $((nr - 1)) | while read i; do
265                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
266                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
267         done
268         # 1 blocks are unwritten
269         seq 1 5 $((nr - 1)) | while read i; do
270                 $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile
271                 _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
272         done
273         # 2 blocks are holes
274         seq 2 5 $((nr - 1)) | while read i; do
275                 _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
276         done
277         # 3 blocks are regular
278         seq 3 5 $((nr - 1)) | while read i; do
279                 _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile
280                 _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk
281         done
282         # 4 blocks will be delalloc later
283 }
284
285 # For a file created with _weave_reflink_rainbow, fill the holes with delalloc
286 # extents
287 _weave_reflink_rainbow_delalloc() {
288         blksz=$1
289         nr=$2
290         dfile=$3
291
292         # 4 blocks are delalloc (do later)
293         seq 4 5 $((nr - 1)) | while read i; do
294                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
295                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
296         done
297 }
298
299 # Make the source file have interleaved regular blocks and reflinked blocks
300 _sweave_reflink_regular() {
301         blksz=$1
302         nr=$2
303         sfile=$3
304         dfile=$4
305
306         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
307         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
308         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile.chk
309         seq 1 2 $((nr - 1)) | while read i; do
310                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
311         done
312 }
313
314 # Make the source file have interleaved unwritten blocks and reflinked blocks
315 _sweave_reflink_unwritten() {
316         blksz=$1
317         nr=$2
318         sfile=$3
319         dfile=$4
320
321         $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $sfile
322         _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk
323         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
324         seq 1 2 $((nr - 1)) | while read i; do
325                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile
326                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk
327         done
328         seq 1 2 $((nr - 1)) | while read i; do
329                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
330         done
331 }
332
333 # Make the source file have interleaved holes and reflinked blocks
334 _sweave_reflink_holes() {
335         blksz=$1
336         nr=$2
337         sfile=$3
338         dfile=$4
339
340         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $sfile
341         _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk
342         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
343         seq 1 2 $((nr - 1)) | while read i; do
344                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile
345                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk
346         done
347         seq 1 2 $((nr - 1)) | while read i; do
348                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
349         done
350 }
351
352 # For a file created with _sweave_reflink_holes, fill the holes with delalloc
353 # extents
354 _sweave_reflink_holes_delalloc() {
355         blksz=$1
356         nr=$2
357         sfile=$3
358
359         seq 0 2 $((nr - 1)) | while read i; do
360                 _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile
361                 _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile.chk
362         done
363 }