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