1#!/bin/sh 2# Test cp --sparse=always through SEEK_DATA copy 3 4# Copyright (C) 2010-2023 Free Software Foundation, Inc. 5 6# This program is free software: you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation, either version 3 of the License, or 9# (at your option) any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program. If not, see <https://www.gnu.org/licenses/>. 18 19. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src 20print_ver_ cp 21require_perl_ 22 23# The test was seen to fail on ext3 so exclude that type 24# (or any file system where the type can't be determined) 25touch sparse_chk 26if seek_data_capable_ sparse_chk && ! df -t ext3 . >/dev/null; then 27 : # Current partition has working extents. Good! 28else 29 skip_ "insufficient SEEK_DATA support" 30 31 # It's not; we need to create one, hence we need root access. 32 require_root_ 33 34 cwd=$PWD 35 cleanup_() { cd /; umount "$cwd/mnt"; } 36 37 skip=0 38 # Create an ext4 loopback file system 39 dd if=/dev/zero of=blob bs=32k count=1000 || skip=1 40 mkdir mnt 41 mkfs -t ext4 -F blob || 42 skip_ "failed to create ext4 file system" 43 mount -oloop blob mnt || skip=1 44 cd mnt || skip=1 45 echo test > f || skip=1 46 test -s f || skip=1 47 48 test $skip = 1 && 49 skip_ "insufficient mount/ext4 support" 50fi 51 52# ================================================= 53# The data below was set up to ensure that the original FIEMAP-copying code 54# was exercised enough to provoke at least two iterations of the do...while loop 55# in which it calls ioctl (fd, FS_IOC_FIEMAP,... 56# This also verifies that non-trivial extents are preserved. 57 58# Extract logical block number and length pairs from filefrag -v output. 59# The initial sed is to remove the "eof" from the normally-empty "flags" field. 60# Similarly, remove flags values like "unknown,delalloc,eof". 61# That is required when that final extent has no number in the "expected" field. 62f() 63{ 64 sed 's/ [a-z,][a-z,]*$//' $@ \ 65 | $AWK '/^ *[0-9]/ {printf "%d %d ", $2, (NF>=6 ? $6 : (NF<5 ? $NF : $5)) } 66 END {print ""}' 67} 68 69for i in $(seq 1 2 21); do 70 for j in 1 2 31 100; do 71 $PERL -e '$n = '$i' * 1024; *F = *STDOUT;' \ 72 -e 'for (1..'$j') { sysseek (*F, $n, 1)' \ 73 -e '&& syswrite (*F, chr($_)x$n) or die "$!"}' > j1 || fail=1 74 75 # Note there is an implicit sync performed by cp on Linux kernels 76 # before 2.6.39 to work around bugs in EXT4 and BTRFS. 77 # (this was removed in the release after coreutils-8.32). 78 # Note also the -s parameter to the filefrag commands below 79 # for the same reasons. 80 cp --reflink=never --sparse=always j1 j2 || fail=1 81 82 cmp j1 j2 || fail_ "data loss i=$i j=$j" 83 if ! filefrag -vs j1 | grep -F extent >/dev/null; then 84 test $skip != 1 && warn_ 'skipping part; you lack filefrag' 85 skip=1 86 else 87 # Here is sample filefrag output: 88 # $ perl -e 'BEGIN{$n=16*1024; *F=*STDOUT}' \ 89 # -e 'for (1..5) { sysseek(*F,$n,1)' \ 90 # -e '&& syswrite *F,"."x$n or die "$!"}' > j 91 # $ filefrag -v j 92 # File system type is: ef53 93 # File size of j is 163840 (40 blocks, blocksize 4096) 94 # ext logical physical expected length flags 95 # 0 4 6258884 4 96 # 1 12 6258892 6258887 4 97 # 2 20 6258900 6258895 4 98 # 3 28 6258908 6258903 4 99 # 4 36 6258916 6258911 4 eof 100 # j: 6 extents found 101 102 # exclude the physical block numbers; they always differ 103 filefrag -v j1 > ff1 || framework_failure_ 104 filefrag -vs j2 > ff2 || framework_failure_ 105 { f ff1; f ff2; } | $PERL $abs_srcdir/tests/filefrag-extent-compare \ 106 || { 107 warn_ ignoring filefrag-reported extent map differences 108 # Show the differing extent maps. 109 head -n99 ff1 ff2 110 } 111 fi 112 test $fail = 1 && break 2 113 done 114done 115 116Exit $fail 117