|  | ##/bin/bash | 
|  | # SPDX-License-Identifier: GPL-2.0+ | 
|  | # Copyright (c) 2015 Oracle.  All Rights Reserved. | 
|  | # | 
|  | # Routines for reflinking, deduping, and comparing parts of files. | 
|  |  | 
|  | # Check that cp has a reflink argument | 
|  | _require_cp_reflink() | 
|  | { | 
|  | cp --help | grep -q reflink || \ | 
|  | _notrun "This test requires a cp with --reflink support." | 
|  | } | 
|  |  | 
|  | # Can we reflink between arbitrary file sets? | 
|  | # i.e. if we reflink a->b and c->d, can we later share | 
|  | # blocks between b & c? | 
|  | _supports_arbitrary_fileset_reflink() | 
|  | { | 
|  | test "$FSTYP" != "ocfs2" | 
|  | } | 
|  |  | 
|  | _require_arbitrary_fileset_reflink() | 
|  | { | 
|  | _supports_arbitrary_fileset_reflink || | 
|  | _notrun "reflink between arbitrary file groups not supported in $FSTYP" | 
|  | } | 
|  |  | 
|  | # Given 2 files, verify that they have the same mapping but different | 
|  | # inodes - i.e. an undisturbed reflink | 
|  | # Silent if so, make noise if not | 
|  | _verify_reflink() | 
|  | { | 
|  | # not a hard link or symlink? | 
|  | cmp -s  <(stat -c '%i' $1) <(stat -c '%i' $2) \ | 
|  | && echo "$1 and $2 are not reflinks: same inode number" | 
|  |  | 
|  | # same mapping? | 
|  | diff -u <($XFS_IO_PROG -c "fiemap" $1 | grep -v $1) \ | 
|  | <($XFS_IO_PROG -c "fiemap" $2 | grep -v $2) \ | 
|  | || echo "$1 and $2 are not reflinks: different extents" | 
|  | } | 
|  |  | 
|  | # New reflink/dedupe helpers | 
|  |  | 
|  | # this test requires the test fs support reflink... | 
|  | _require_test_reflink() | 
|  | { | 
|  | _require_test | 
|  | _require_xfs_io_command "reflink" | 
|  |  | 
|  | rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2" | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null | 
|  | $XFS_IO_PROG -f -c "reflink $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" > /dev/null | 
|  | if [ ! -s "$TEST_DIR/file2" ]; then | 
|  | rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2" | 
|  | _notrun "Reflink not supported by test filesystem type: $FSTYP" | 
|  | fi | 
|  | rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2" | 
|  | } | 
|  |  | 
|  | # this test requires the scratch fs support reflink... | 
|  | _require_scratch_reflink() | 
|  | { | 
|  | _require_scratch | 
|  | _require_xfs_io_command "reflink" | 
|  |  | 
|  | _scratch_mkfs > /dev/null | 
|  | _scratch_mount | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null | 
|  | $XFS_IO_PROG -f -c "reflink $SCRATCH_MNT/file1 0 0 65536" "$SCRATCH_MNT/file2" > /dev/null | 
|  | if [ ! -s "$SCRATCH_MNT/file2" ]; then | 
|  | _scratch_unmount | 
|  | _notrun "Reflink not supported by scratch filesystem type: $FSTYP" | 
|  | fi | 
|  | _scratch_unmount | 
|  | } | 
|  |  | 
|  | # this test requires duperemove working for the file system | 
|  | _require_scratch_duperemove() | 
|  | { | 
|  | _require_scratch | 
|  | _require_command "$DUPEREMOVE_PROG" duperemove | 
|  |  | 
|  | _scratch_mkfs > /dev/null | 
|  | _scratch_mount | 
|  | dd if=/dev/zero of="$SCRATCH_MNT/file1" bs=128k count=1 >& /dev/null | 
|  | dd if=/dev/zero of="$SCRATCH_MNT/file2" bs=128k count=1 >& /dev/null | 
|  | if ! "$DUPEREMOVE_PROG" -d "$SCRATCH_MNT/file1" \ | 
|  | "$SCRATCH_MNT/file2" >& /dev/null ; then | 
|  | _scratch_unmount | 
|  | _notrun "duperemove does not support file system type: $FSTYP" | 
|  | fi | 
|  | _scratch_unmount | 
|  | } | 
|  |  | 
|  | # this test requires scratch fs to report explicit SHARED flag | 
|  | # e.g. | 
|  | #   0         4K         8K | 
|  | #    / File1: Extent 0  \ | 
|  | #   /                    \ | 
|  | #   |<- On disk Extent-->| | 
|  | #   |        / | 
|  | #   | File2 / | 
|  | #     Extent: 0 | 
|  | # Fs supports explicit SHARED extent reporting should report fiemap like: | 
|  | # File1: 2 extents | 
|  | # Extent 0-4K: SHARED | 
|  | # Extent 4-8K: | 
|  | # File2: 1 extents | 
|  | # Extent 0-4K: SHARED | 
|  | # | 
|  | # Fs doesn't support explicit reporting will report fiemap like: | 
|  | # File1: 1 extent | 
|  | # Extent 0-8K: SHARED | 
|  | # File2: 1 extent | 
|  | # Extent 0-4K: SHARED | 
|  | _require_scratch_explicit_shared_extents() | 
|  | { | 
|  | _require_scratch | 
|  | _require_xfs_io_command "fiemap" | 
|  | _require_scratch_reflink | 
|  | _require_xfs_io_command "reflink" | 
|  | local nr_extents | 
|  |  | 
|  | _scratch_mkfs > /dev/null | 
|  | _scratch_mount | 
|  |  | 
|  | _pwrite_byte 0x61 0 128k $SCRATCH_MNT/file1 >/dev/null | 
|  | _reflink_range $SCRATCH_MNT/file1 0 $SCRATCH_MNT/file2 0 64k >/dev/null | 
|  |  | 
|  | _scratch_cycle_mount | 
|  |  | 
|  | nr_extents=$(_count_extents $SCRATCH_MNT/file1) | 
|  | if [ $nr_extents -eq 1 ]; then | 
|  | _notrun "Explicit SHARED flag reporting not support by filesystem type: $FSTYP" | 
|  | fi | 
|  | _scratch_unmount | 
|  | } | 
|  |  | 
|  | # this test requires the test fs support dedupe... | 
|  | _require_test_dedupe() | 
|  | { | 
|  | _require_test | 
|  | _require_xfs_io_command "dedupe" | 
|  |  | 
|  | rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2" | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file1" > /dev/null | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$TEST_DIR/file2" > /dev/null | 
|  | testio="$($XFS_IO_PROG -f -c "dedupe $TEST_DIR/file1 0 0 65536" "$TEST_DIR/file2" 2>&1)" | 
|  | echo $testio | grep -q "Operation not supported" && \ | 
|  | _notrun "Dedupe not supported by test filesystem type: $FSTYP" | 
|  | echo $testio | grep -q "Inappropriate ioctl for device" && \ | 
|  | _notrun "Dedupe not supported by test filesystem type: $FSTYP" | 
|  | echo $testio | grep -q "Invalid argument" && \ | 
|  | _notrun "Dedupe not supported by test filesystem type: $FSTYP" | 
|  | rm -rf "$TEST_DIR/file1" "$TEST_DIR/file2" | 
|  | } | 
|  |  | 
|  | # this test requires the scratch fs support dedupe... | 
|  | _require_scratch_dedupe() | 
|  | { | 
|  | _require_scratch | 
|  | _require_xfs_io_command "dedupe" | 
|  |  | 
|  | _scratch_mkfs > /dev/null | 
|  | _scratch_mount | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file1" > /dev/null | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0x61 0 65536" "$SCRATCH_MNT/file2" > /dev/null | 
|  | testio="$($XFS_IO_PROG -f -c "dedupe $SCRATCH_MNT/file1 0 0 65536" "$SCRATCH_MNT/file2" 2>&1)" | 
|  | echo $testio | grep -q "Operation not supported" && \ | 
|  | _notrun "Dedupe not supported by scratch filesystem type: $FSTYP" | 
|  | echo $testio | grep -q "Inappropriate ioctl for device" && \ | 
|  | _notrun "Dedupe not supported by scratch filesystem type: $FSTYP" | 
|  | echo $testio | grep -q "Invalid argument" && \ | 
|  | _notrun "Dedupe not supported by scratch filesystem type: $FSTYP" | 
|  | _scratch_unmount | 
|  | } | 
|  |  | 
|  | # Prints a range of a file as a hex dump | 
|  | _read_range() { | 
|  | file="$1" | 
|  | offset="$2" | 
|  | len="$3" | 
|  | xfs_io_args="$4" | 
|  |  | 
|  | $XFS_IO_PROG $xfs_io_args -f -c "pread -q -v $offset $len" "$file" | cut -d ' ' -f '3-18' | 
|  | } | 
|  |  | 
|  | # Prints a range of a file as a hex dump | 
|  | _mread_range() { | 
|  | local file="$1" | 
|  | local offset="$2" | 
|  | local len="$3" | 
|  | local xfs_io_args="$4" | 
|  |  | 
|  | $XFS_IO_PROG $xfs_io_args -f -c "mmap -rw 0 $((offset + len))" \ | 
|  | -c "mread -v $offset $len" "$file" | cut -d ' ' -f '3-18' | 
|  | } | 
|  |  | 
|  | # Compare ranges of two files | 
|  | _compare_range() { | 
|  | file1="$1" | 
|  | offset1="$2" | 
|  | file2="$3" | 
|  | offset2="$4" | 
|  | len="$5" | 
|  |  | 
|  | cmp -s <(_read_range "$file1" "$offset1" "$len") \ | 
|  | <(_read_range "$file2" "$offset2" "$len") | 
|  | } | 
|  |  | 
|  | # Prints the md5 checksum of a hexdump of a part of a given file | 
|  | _md5_range_checksum() { | 
|  | file="$1" | 
|  | offset="$2" | 
|  | len="$3" | 
|  |  | 
|  | md5sum <(_read_range "$file" "$offset" "$len") | cut -d ' ' -f 1 | 
|  | } | 
|  |  | 
|  | # Reflink some file1 into file2 via cp | 
|  | _cp_reflink() { | 
|  | file1="$1" | 
|  | file2="$2" | 
|  |  | 
|  | cp --reflink=always -p -f "$file1" "$file2" | 
|  | } | 
|  |  | 
|  | # Reflink some file1 into file2 | 
|  | _reflink() { | 
|  | file1="$1" | 
|  | file2="$2" | 
|  |  | 
|  | $XFS_IO_PROG -f -c "reflink $file1" "$file2" | 
|  | } | 
|  |  | 
|  | # Reflink some part of file1 into another part of file2 | 
|  | _reflink_range() { | 
|  | file1="$1" | 
|  | offset1="$2" | 
|  | file2="$3" | 
|  | offset2="$4" | 
|  | len="$5" | 
|  | xfs_io_args="$6" | 
|  |  | 
|  | $XFS_IO_PROG $xfs_io_args -f -c "reflink $file1 $offset1 $offset2 $len" "$file2" | 
|  | } | 
|  |  | 
|  | # Dedupe some part of file1 into another part of file2 | 
|  | _dedupe_range() { | 
|  | file1="$1" | 
|  | offset1="$2" | 
|  | file2="$3" | 
|  | offset2="$4" | 
|  | len="$5" | 
|  | xfs_io_args="$6" | 
|  |  | 
|  | $XFS_IO_PROG $xfs_io_args -f -c "dedupe $file1 $offset1 $offset2 $len" "$file2" | 
|  | } | 
|  |  | 
|  | # Unify xfs_io dedupe ioctl error message prefix | 
|  | _filter_dedupe_error() | 
|  | { | 
|  | sed -e 's/^dedupe:/XFS_IOC_FILE_EXTENT_SAME:/g' | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved unwritten and reflinked blocks | 
|  | _weave_reflink_unwritten() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile | 
|  | $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $dfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk | 
|  | seq 0 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved holes and reflinked blocks | 
|  | _weave_reflink_holes() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile | 
|  | $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk | 
|  | seq 0 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # For a file created with _weave_reflink_holes, fill the holes with delalloc | 
|  | # extents | 
|  | _weave_reflink_holes_delalloc() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | dfile=$3 | 
|  |  | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved regular blocks and reflinked blocks | 
|  | _weave_reflink_regular() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile | 
|  | _pwrite_byte 0x62 0 $((blksz * nr)) $dfile | 
|  | _pwrite_byte 0x62 0 $((blksz * nr)) $dfile.chk | 
|  | seq 0 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved holes, unwritten blocks, and regular blocks. | 
|  | _weave_file_rainbow() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | dfile=$3 | 
|  |  | 
|  | $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk | 
|  | # 0 blocks are unwritten | 
|  | seq 1 5 $((nr - 1)) | while read i; do | 
|  | $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 1 blocks are holes | 
|  | seq 2 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 2 blocks are regular | 
|  | seq 3 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 3 blocks are holes | 
|  | seq 2 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 4 blocks are delalloc | 
|  | seq 4 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved holes, unwritten blocks, regular blocks, and | 
|  | # reflinked blocks | 
|  | _weave_reflink_rainbow() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile | 
|  | $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $dfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $dfile.chk | 
|  | # 0 blocks are reflinked | 
|  | seq 0 5 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 1 blocks are unwritten | 
|  | seq 1 5 $((nr - 1)) | while read i; do | 
|  | $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $dfile | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 2 blocks are holes | 
|  | seq 2 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 3 blocks are regular | 
|  | seq 3 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | # 4 blocks will be delalloc later | 
|  | } | 
|  |  | 
|  | # For a file created with _weave_reflink_rainbow, fill the holes with delalloc | 
|  | # extents | 
|  | _weave_reflink_rainbow_delalloc() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | dfile=$3 | 
|  |  | 
|  | # 4 blocks are delalloc (do later) | 
|  | seq 4 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Make the source file have interleaved regular blocks and reflinked blocks | 
|  | _sweave_reflink_regular() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile | 
|  | _pwrite_byte 0x62 0 $((blksz * nr)) $dfile | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $sfile.chk | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | done | 
|  | } | 
|  |  | 
|  | # Make the source file have interleaved unwritten blocks and reflinked blocks | 
|  | _sweave_reflink_unwritten() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | $XFS_IO_PROG -f -c "falloc 0 $((blksz * nr))" $sfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk | 
|  | _pwrite_byte 0x62 0 $((blksz * nr)) $dfile | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | done | 
|  | } | 
|  |  | 
|  | # Make the source file have interleaved holes and reflinked blocks | 
|  | _sweave_reflink_holes() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  | dfile=$4 | 
|  |  | 
|  | $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $sfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk | 
|  | _pwrite_byte 0x62 0 $((blksz * nr)) $dfile | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | seq 1 2 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | done | 
|  | } | 
|  |  | 
|  | # For a file created with _sweave_reflink_holes, fill the holes with delalloc | 
|  | # extents | 
|  | _sweave_reflink_holes_delalloc() { | 
|  | blksz=$1 | 
|  | nr=$2 | 
|  | sfile=$3 | 
|  |  | 
|  | seq 0 2 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile | 
|  | _pwrite_byte 0x64 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | } | 
|  |  | 
|  | # Create a file of interleaved holes, unwritten blocks, regular blocks, and | 
|  | # reflinked blocks | 
|  | _sweave_reflink_rainbow() { | 
|  | local blksz=$1 | 
|  | local nr=$2 | 
|  | local sfile=$3 | 
|  | local dfile=$4 | 
|  |  | 
|  | $XFS_IO_PROG -f -c "truncate $((blksz * nr))" $sfile | 
|  | _pwrite_byte 0x00 0 $((blksz * nr)) $sfile.chk | 
|  | _pwrite_byte 0x61 0 $((blksz * nr)) $dfile | 
|  | seq 0 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | # 0 blocks are reflinked | 
|  | seq 0 5 $((nr - 1)) | while read i; do | 
|  | _reflink_range $sfile $((blksz * i)) $dfile $((blksz * i)) $blksz | 
|  | _pwrite_byte 0x61 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | # 1 blocks are unwritten | 
|  | seq 1 5 $((nr - 1)) | while read i; do | 
|  | $XFS_IO_PROG -f -c "falloc $((blksz * i)) $blksz" $sfile | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | # 2 blocks are holes | 
|  | seq 2 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x00 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | # 3 blocks are regular | 
|  | seq 3 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $sfile | 
|  | _pwrite_byte 0x71 $((blksz * i)) $blksz $sfile.chk | 
|  | done | 
|  | # 4 blocks will be delalloc later | 
|  | } | 
|  |  | 
|  | # For a file created with _sweave_reflink_rainbow, fill the holes with delalloc | 
|  | # extents | 
|  | _sweave_reflink_rainbow_delalloc() { | 
|  | local blksz=$1 | 
|  | local nr=$2 | 
|  | local dfile=$3 | 
|  |  | 
|  | # 4 blocks are delalloc (do later) | 
|  | seq 4 5 $((nr - 1)) | while read i; do | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile | 
|  | _pwrite_byte 0x62 $((blksz * i)) $blksz $dfile.chk | 
|  | done | 
|  | } |