598f0877bedc73681ffe95c259d8a0cd841a79ef
[xfstests-dev.git] / common / reflink
1 ##/bin/bash
2 # SPDX-License-Identifier: GPL-2.0+
3 # Copyright (c) 2015 Oracle.  All Rights Reserved.
4 #
5 # Routines for reflinking, deduping, and comparing parts of files.
6
7 # Check that cp has a reflink argument
8 _require_cp_reflink()
9 {
10        cp --help | grep -q reflink || \
11                _notrun "This test requires a cp with --reflink support."
12 }
13
14 # Can we reflink between arbitrary file sets?
15 # i.e. if we reflink a->b and c->d, can we later share
16 # blocks between b & c?
17 _supports_arbitrary_fileset_reflink()
18 {
19         test "$FSTYP" != "ocfs2"
20 }
21
22 _require_arbitrary_fileset_reflink()
23 {
24         _supports_arbitrary_fileset_reflink ||
25                 _notrun "reflink between arbitrary file groups not supported in $FSTYP"
26 }
27
28 # Given 2 files, verify that they have the same mapping but different
29 # inodes - i.e. an undisturbed reflink
30 # Silent if so, make noise if not
31 _verify_reflink()
32 {
33        # not a hard link or symlink?
34        cmp -s  <(stat -c '%i' $1) <(stat -c '%i' $2) \
35                && echo "$1 and $2 are not reflinks: same inode number"
36
37        # same mapping?
38        diff -u <($XFS_IO_PROG -c "fiemap" $1 | grep -v $1) \
39                <($XFS_IO_PROG -c "fiemap" $2 | grep -v $2) \
40                || echo "$1 and $2 are not reflinks: different extents"
41 }
42
43 # New reflink/dedupe helpers
44
45 # this test requires the test fs support reflink...
46 _require_test_reflink()
47 {
48         _require_test
49         _require_xfs_io_command "reflink"
50
51         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
52         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null
53         $XFS_IO_PROG -f -c "reflink $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" > /dev/null
54         if [ ! -s "$TEST_DIR/file2" ]; then
55                 rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
56                 _notrun "Reflink not supported by test filesystem type: $FSTYP"
57         fi
58         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
59 }
60
61 # this test requires the scratch fs support reflink...
62 _require_scratch_reflink()
63 {
64         _require_scratch
65         _require_xfs_io_command "reflink"
66
67         _scratch_mkfs > /dev/null
68         _scratch_mount
69         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null
70         $XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/file1 0 0 65536" "$SCRATCH_MNT/file2" > /dev/null
71         if [ ! -s "$SCRATCH_MNT/file2" ]; then
72                 _scratch_unmount
73                 _notrun "Reflink not supported by scratch filesystem type: $FSTYP"
74         fi
75         _scratch_unmount
76 }
77
78 # this test requires scratch fs to report explicit SHARED flag
79 # e.g.
80 #   0         4K         8K
81 #    / File1: Extent 0  \
82 #   /                    \
83 #   |<- On disk Extent-->|
84 #   |        /
85 #   | File2 /
86 #     Extent: 0
87 # Fs supports explicit SHARED extent reporting should report fiemap like:
88 # File1: 2 extents
89 # Extent 0-4K: SHARED
90 # Extent 4-8K:
91 # File2: 1 extents
92 # Extent 0-4K: SHARED
93 #
94 # Fs doesn't support explicit reporting will report fiemap like:
95 # File1: 1 extent
96 # Extent 0-8K: SHARED
97 # File2: 1 extent
98 # Extent 0-4K: SHARED
99 _require_scratch_explicit_shared_extents()
100 {
101         _require_scratch
102         _require_xfs_io_command "fiemap"
103         _require_scratch_reflink
104         _require_xfs_io_command "reflink"
105         local nr_extents
106
107         _scratch_mkfs > /dev/null
108         _scratch_mount
109
110         _pwrite_byte 0x61 0 128k $SCRATCH_MNT/file1 >/dev/null
111         _reflink_range $SCRATCH_MNT/file1 0 $SCRATCH_MNT/file2 0 64k >/dev/null
112
113         _scratch_cycle_mount
114
115         nr_extents=$(_count_extents $SCRATCH_MNT/file1)
116         if [ $nr_extents -eq 1 ]; then
117                 _notrun "Explicit SHARED flag reporting not support by filesystem type: $FSTYP"
118         fi
119         _scratch_unmount
120 }
121
122 # this test requires the test fs support dedupe...
123 _require_test_dedupe()
124 {
125         _require_test
126         _require_xfs_io_command "dedupe"
127
128         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
129         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null
130         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file2" > /dev/null
131         testio="$($XFS_IO_PROG -f -c "dedupe $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" 2>&1)"
132         echo $testio | grep -q "Operation not supported" && \
133                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
134         echo $testio | grep -q "Inappropriate ioctl for device" && \
135                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
136         echo $testio | grep -q "Invalid argument" && \
137                 _notrun "Dedupe not supported by test filesystem type: $FSTYP"
138         rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2"
139 }
140
141 # this test requires the scratch fs support dedupe...
142 _require_scratch_dedupe()
143 {
144         _require_scratch
145         _require_xfs_io_command "dedupe"
146
147         _scratch_mkfs > /dev/null
148         _scratch_mount
149         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null
150         $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file2" > /dev/null
151         testio="$($XFS_IO_PROG -f -c "dedupe $SCRATCH_MNT/file1 0 0 65536" "$SCRATCH_MNT/file2" 2>&1)"
152         echo $testio | grep -q "Operation not supported" && \
153                 _notrun "Dedupe not supported by scratch filesystem type: $FSTYP"
154         echo $testio | grep -q "Inappropriate ioctl for device" && \
155                 _notrun "Dedupe not supported by scratch filesystem type: $FSTYP"
156         echo $testio | grep -q "Invalid argument" && \
157                 _notrun "Dedupe not supported by scratch filesystem type: $FSTYP"
158         _scratch_unmount
159 }
160
161 # Prints a range of a file as a hex dump
162 _read_range() {
163         file="$1"
164         offset="$2"
165         len="$3"
166         xfs_io_args="$4"
167
168         $XFS_IO_PROG $xfs_io_args -f -c "pread -q -v $offset $len" "$file" | cut -d ' ' -f '3-18'
169 }
170
171 # Compare ranges of two files
172 _compare_range() {
173         file1="$1"
174         offset1="$2"
175         file2="$3"
176         offset2="$4"
177         len="$5"
178
179         cmp -s <(_read_range "$file1" "$offset1" "$len") \
180                <(_read_range "$file2" "$offset2" "$len")
181 }
182
183 # Prints the md5 checksum of a hexdump of a part of a given file
184 _md5_range_checksum() {
185         file="$1"
186         offset="$2"
187         len="$3"
188
189         md5sum <(_read_range "$file" "$offset" "$len") | cut -d ' ' -f 1
190 }
191
192 # Reflink some file1 into file2 via cp
193 _cp_reflink() {
194         file1="$1"
195         file2="$2"
196
197         cp --reflink=always -p -f "$file1" "$file2"
198 }
199
200 # Reflink some file1 into file2
201 _reflink() {
202         file1="$1"
203         file2="$2"
204
205         $XFS_IO_PROG -f -c "reflink $file1" "$file2"
206 }
207
208 # Reflink some part of file1 into another part of file2
209 _reflink_range() {
210         file1="$1"
211         offset1="$2"
212         file2="$3"
213         offset2="$4"
214         len="$5"
215         xfs_io_args="$6"
216
217         $XFS_IO_PROG $xfs_io_args -f -c "reflink $file1 $offset1 $offset2 $len" "$file2"
218 }
219
220 # Dedupe some part of file1 into another part of file2
221 _dedupe_range() {
222         file1="$1"
223         offset1="$2"
224         file2="$3"
225         offset2="$4"
226         len="$5"
227         xfs_io_args="$6"
228
229         $XFS_IO_PROG $xfs_io_args -f -c "dedupe $file1 $offset1 $offset2 $len" "$file2"
230 }
231
232 # Unify xfs_io dedupe ioctl error message prefix
233 _filter_dedupe_error()
234 {
235         sed -e 's/^dedupe:/XFS_IOC_FILE_EXTENT_SAME:/g'
236 }
237
238 # Create a file of interleaved unwritten and reflinked blocks
239 _weave_reflink_unwritten() {
240         blksz=$1
241         nr=$2
242         sfile=$3
243         dfile=$4
244
245         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
246         $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $dfile
247         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
248         seq 0 2 $((nr - 1)) | while read i; do
249                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
250                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
251         done
252 }
253
254 # Create a file of interleaved holes and reflinked blocks
255 _weave_reflink_holes() {
256         blksz=$1
257         nr=$2
258         sfile=$3
259         dfile=$4
260
261         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
262         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
263         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
264         seq 0 2 $((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 }
269
270 # For a file created with _weave_reflink_holes, fill the holes with delalloc
271 # extents
272 _weave_reflink_holes_delalloc() {
273         blksz=$1
274         nr=$2
275         dfile=$3
276
277         seq 1 2 $((nr - 1)) | while read i; do
278                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
279                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
280         done
281 }
282
283 # Create a file of interleaved regular blocks and reflinked blocks
284 _weave_reflink_regular() {
285         blksz=$1
286         nr=$2
287         sfile=$3
288         dfile=$4
289
290         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
291         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
292         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile.chk
293         seq 0 2 $((nr - 1)) | while read i; do
294                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
295                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
296         done
297 }
298
299 # Create a file of interleaved holes, unwritten blocks, regular blocks, and
300 # reflinked blocks
301 _weave_reflink_rainbow() {
302         blksz=$1
303         nr=$2
304         sfile=$3
305         dfile=$4
306
307         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
308         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile
309         _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk
310         # 0 blocks are reflinked
311         seq 0 5 $((nr - 1)) | while read i; do
312                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
313                 _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk
314         done
315         # 1 blocks are unwritten
316         seq 1 5 $((nr - 1)) | while read i; do
317                 $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile
318                 _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
319         done
320         # 2 blocks are holes
321         seq 2 5 $((nr - 1)) | while read i; do
322                 _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk
323         done
324         # 3 blocks are regular
325         seq 3 5 $((nr - 1)) | while read i; do
326                 _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile
327                 _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk
328         done
329         # 4 blocks will be delalloc later
330 }
331
332 # For a file created with _weave_reflink_rainbow, fill the holes with delalloc
333 # extents
334 _weave_reflink_rainbow_delalloc() {
335         blksz=$1
336         nr=$2
337         dfile=$3
338
339         # 4 blocks are delalloc (do later)
340         seq 4 5 $((nr - 1)) | while read i; do
341                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile
342                 _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk
343         done
344 }
345
346 # Make the source file have interleaved regular blocks and reflinked blocks
347 _sweave_reflink_regular() {
348         blksz=$1
349         nr=$2
350         sfile=$3
351         dfile=$4
352
353         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile
354         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
355         _pwrite_byte 0x61 0 $((blksz * nr)) $sfile.chk
356         seq 1 2 $((nr - 1)) | while read i; do
357                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
358         done
359 }
360
361 # Make the source file have interleaved unwritten blocks and reflinked blocks
362 _sweave_reflink_unwritten() {
363         blksz=$1
364         nr=$2
365         sfile=$3
366         dfile=$4
367
368         $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $sfile
369         _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk
370         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
371         seq 1 2 $((nr - 1)) | while read i; do
372                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile
373                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk
374         done
375         seq 1 2 $((nr - 1)) | while read i; do
376                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
377         done
378 }
379
380 # Make the source file have interleaved holes and reflinked blocks
381 _sweave_reflink_holes() {
382         blksz=$1
383         nr=$2
384         sfile=$3
385         dfile=$4
386
387         $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $sfile
388         _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk
389         _pwrite_byte 0x62 0 $((blksz * nr)) $dfile
390         seq 1 2 $((nr - 1)) | while read i; do
391                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile
392                 _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk
393         done
394         seq 1 2 $((nr - 1)) | while read i; do
395                 _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz
396         done
397 }
398
399 # For a file created with _sweave_reflink_holes, fill the holes with delalloc
400 # extents
401 _sweave_reflink_holes_delalloc() {
402         blksz=$1
403         nr=$2
404         sfile=$3
405
406         seq 0 2 $((nr - 1)) | while read i; do
407                 _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile
408                 _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile.chk
409         done
410 }