mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
830349865e
Add some more clone range tests that missed various "wacky" combinations of file state. Specifically, we test reflinking into and out of rainbow ranges (a mix of real, unwritten, hole, delalloc, and shared extents), and also we test that we can correctly handle double-inode locking no matter what order of inodes or the filesystem's locking rules. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Eryu Guan <guaneryu@gmail.com> Signed-off-by: Eryu Guan <guaneryu@gmail.com>
411 lines
11 KiB
Plaintext
411 lines
11 KiB
Plaintext
##/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 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'
|
|
}
|
|
|
|
# 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, 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
|
|
}
|