python3-packaging
python3-setuptools
rng-tools
+rsync
samba
sysstat
uuid-dev
readlink
rm
rmdir
+ rsync
scp
script
sed
done
return $rv
}
+
+#
+# Compare two directory trees recursively in a manner similar to diff(1), but
+# using rsync. If there are any discrepancies, a summary of the differences are
+# output and a non-zero error is returned.
+#
+# If you're comparing a directory after a ZIL replay, you should set
+# LIBTEST_DIFF_ZIL_REPLAY=1 or use replay_directory_diff which will cause
+# directory_diff to ignore mtime changes (the ZIL replay won't fix up mtime
+# information).
+#
+function directory_diff # dir_a dir_b
+{
+ dir_a="$1"
+ dir_b="$2"
+ zil_replay="${LIBTEST_DIFF_ZIL_REPLAY:-0}"
+
+ # If one of the directories doesn't exist, return 2. This is to match the
+ # semantics of diff.
+ if ! [ -d "$dir_a" -a -d "$dir_b" ]; then
+ return 2
+ fi
+
+ # Run rsync with --dry-run --itemize-changes to get something akin to diff
+ # output, but rsync is far more thorough in detecting differences (diff
+ # doesn't compare file metadata, and cannot handle special files).
+ #
+ # Also make sure to filter out non-user.* xattrs when comparing. On
+ # SELinux-enabled systems the copied tree will probably have different
+ # SELinux labels.
+ args=("-nicaAHX" '--filter=-x! user.*' "--delete")
+
+ # NOTE: Quite a few rsync builds do not support --crtimes which would be
+ # necessary to verify that creation times are being maintained properly.
+ # Unfortunately because of this we cannot use it unconditionally but we can
+ # check if this rsync build supports it and use it then. This check is
+ # based on the same check in the rsync test suite (testsuite/crtimes.test).
+ #
+ # We check ctimes even with zil_replay=1 because the ZIL does store
+ # creation times and we should make sure they match (if the creation times
+ # do not match there is a "c" entry in one of the columns).
+ if ( rsync --version | grep -q "[, ] crtimes" >/dev/null ); then
+ args+=("--crtimes")
+ else
+ echo "NOTE: This rsync package does not support --crtimes (-N)."
+ fi
+
+ # If we are testing a ZIL replay, we need to ignore timestamp changes.
+ # Unfortunately --no-times doesn't do what we want -- it will still tell
+ # you if the timestamps don't match but rsync will set the timestamps to
+ # the current time (leading to an itemised change entry). It's simpler to
+ # just filter out those lines.
+ if [ "$zil_replay" -eq 0 ]; then
+ filter=("cat")
+ else
+ # Different rsync versions have different numbers of columns. So just
+ # require that aside from the first two, all other columns must be
+ # blank (literal ".") or a timestamp field ("[tT]").
+ filter=("grep" "-v" '^\..[.Tt]\+ ')
+ fi
+
+ diff="$(rsync "${args[@]}" "$dir_a/" "$dir_b/" | "${filter[@]}")"
+ rv=0
+ if [ -n "$diff" ]; then
+ echo "$diff"
+ rv=1
+ fi
+ return $rv
+}
+
+#
+# Compare two directory trees recursively, without checking whether the mtimes
+# match (creation times will be checked if the available rsync binary supports
+# it). This is necessary for ZIL replay checks (because the ZIL does not
+# contain mtimes and thus after a ZIL replay, mtimes won't match).
+#
+# This is shorthand for LIBTEST_DIFF_ZIL_REPLAY=1 directory_diff <...>.
+#
+function replay_directory_diff # dir_a dir_b
+{
+ LIBTEST_DIFF_ZIL_REPLAY=1 directory_diff "$@"
+ return $?
+}
cat $TESTDIR/zr010p | log_must zfs receive -o origin=$fs2@s1 $rfs
mntpnt_old=$(get_prop mountpoint $fs)
mntpnt_new=$(get_prop mountpoint $rfs)
-log_must diff -r $mntpnt_old $mntpnt_new
+log_must directory_diff $mntpnt_old $mntpnt_new
log_must zfs destroy -r $rfs
cat $TESTDIR/zr010p2 | log_must zfs receive -o origin=$fs@s1 $rfs
mntpnt_old=$(get_prop mountpoint $fs2)
mntpnt_new=$(get_prop mountpoint $rfs)
-log_must diff -r $mntpnt_old $mntpnt_new
+log_must directory_diff $mntpnt_old $mntpnt_new
log_pass "zfs receive of full send as clone works"
cat $streamfile | log_must zfs receive $POOL/recvfs
recv_mntpnt=$(get_prop mountpoint "$POOL/recvfs")
- log_must diff -r $mntpnt $recv_mntpnt
+ log_must directory_diff $mntpnt $recv_mntpnt
log_must zfs destroy -rf $POOL/fs
log_must zfs destroy -rf $POOL/recvfs
}
done
log_note "verify snapshot contents"
for ds in $datasets; do
- diff -q -r /$ds /$ds/.zfs/snapshot/snap > /dev/null 2>&1
- if [[ $? -eq 1 ]]; then
- log_fail "snapshot contents are different from" \
- "the filesystem"
- fi
+ [ -d "/$ds/.zfs/snapshot/snap" ] && \
+ log_must directory_diff /$ds /$ds/.zfs/snapshot/snap
done
# We subtract 3 + 7 + 7 + 1 = 18 for three slashes (/), strlen("TESTFSA") == 7,
old_mntpnt=$(get_prop mountpoint $POOL_NAME/testfs)
new_mntpnt=$(get_prop mountpoint $POOL_NAME/fixed/testfs)
-log_must diff -r "$old_mntpnt" "$new_mntpnt"
+log_must directory_diff "$old_mntpnt" "$new_mntpnt"
log_must diff /dev/zvol/$POOL_NAME/testvol /dev/zvol/$POOL_NAME/fixed/testvol
log_must has_ivset_guid $POOL_NAME/fixed/testfs@snap1
fi
log_must eval "zfs recv -F $TEST_RECV_FS < $TEST_STREAMINCR"
-log_must diff -r /$TEST_SEND_FS /$TEST_RECV_FS
+log_must directory_diff /$TEST_SEND_FS /$TEST_RECV_FS
log_must zfs umount $TEST_SEND_FS
log_must zfs umount $TEST_RECV_FS
# Verify receipt of normal incrementals to redaction list members.
log_must eval "zfs send -i $sendfs@snap0 $POOL/stride3@snap >$stream"
log_must eval "zfs recv $POOL2/rstride3 <$stream"
-log_must diff -r /$POOL/stride3 /$POOL2/rstride3
+log_must directory_diff /$POOL/stride3 /$POOL2/rstride3
log_must eval "zfs send -i $sendfs@snap0 $POOL/stride5@snap >$stream"
log_must eval "zfs recv $POOL2/rstride5 <$stream"
-log_must diff -r /$POOL/stride5 /$POOL2/rstride5
+log_must directory_diff /$POOL/stride5 /$POOL2/rstride5
# But not a normal child that we weren't redacted with respect to.
log_must eval "zfs send -i $sendfs@snap0 $POOL/hole@snap >$stream"
# Verify we can receive grandchildren on the child.
log_must eval "zfs send -i $POOL/int@snap $POOL/rm@snap >$stream"
log_must eval "zfs receive $POOL2/rrm <$stream"
-log_must diff -r /$POOL/rm /$POOL2/rrm
+log_must directory_diff /$POOL/rm /$POOL2/rrm
# But not a grandchild that the received child wasn't redacted with respect to.
log_must eval "zfs send -i $POOL/int@snap $POOL/write@snap >$stream"
# Verify we can receive a full clone of the grandchild on the child.
log_must eval "zfs send $POOL/write@snap >$stream"
log_must eval "zfs recv -o origin=$POOL2/rint@snap $POOL2/rwrite <$stream"
-log_must diff -r /$POOL/write /$POOL2/rwrite
+log_must directory_diff /$POOL/write /$POOL2/rwrite
# Along with other origins.
log_must eval "zfs recv -o origin=$POOL2/rfs@snap0 $POOL2/rwrite1 <$stream"
-log_must diff -r /$POOL/write /$POOL2/rwrite1
+log_must directory_diff /$POOL/write /$POOL2/rwrite1
log_must eval "zfs recv -o origin=$POOL2@init $POOL2/rwrite2 <$stream"
-log_must diff -r /$POOL/write /$POOL2/rwrite2
+log_must directory_diff /$POOL/write /$POOL2/rwrite2
log_must zfs destroy -R $POOL2/rwrite2
log_must zfs destroy -R $POOL2/rfs
# sending from the bookmark.
log_must eval "zfs send -i $sendfs#book7 $POOL/hole1@snap >$stream"
log_must eval "zfs recv $POOL2/rhole1 <$stream"
-log_must diff -r /$POOL/hole1 /$POOL2/rhole1
+log_must directory_diff /$POOL/hole1 /$POOL2/rhole1
# Verify we can receive an intermediate clone redacted with respect to a
# non-subset if we send from the bookmark.
log_must eval "zfs send -L -i $sendfs@snap $clone@snap1 >$stream"
log_must stream_has_features $stream large_blocks
log_must eval "zfs recv $recvfs/new <$stream"
-log_must diff -r $clone_mnt $recv_mnt/new
+log_must directory_diff $clone_mnt $recv_mnt/new
log_pass "Large blocks and redacted send work correctly together."
log_must mkdir /$TESTPOOL/tar
log_must tar --directory /$TESTPOOL/tar -xzf $tarfile
-log_must diff -r /$TESTPOOL/tar /$TESTPOOL/recv
+# The recv'd filesystem is called "/fs", so only compare that subdirectory.
+log_must directory_diff /$TESTPOOL/tar/fs /$TESTPOOL/recv/fs
log_pass "zfs can receive dedup send streams with 'zstream redup'"
srcdir=$(get_prop mountpoint $src_fs)
dstdir=$(get_prop mountpoint $dst_fs)
- diff -r $srcdir $dstdir > /dev/null 2>&1
+ replay_directory_diff $srcdir $dstdir
return $?
}
if [[ -d /$recvfs/.zfs/snapshot/a && -d \
/$sendfs/.zfs/snapshot/a ]]; then
- diff -r /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a
+ directory_diff /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a
[[ $? -eq 0 ]] || log_fail "Differences found in snap a"
fi
if [[ -d /$recvfs/.zfs/snapshot/b && -d \
/$sendfs/.zfs/snapshot/b ]]; then
- diff -r /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b
+ directory_diff /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b
[[ $? -eq 0 ]] || log_fail "Differences found in snap b"
fi
}
#
# 4. Copy TESTFS to temporary location (TESTDIR/copy)
#
-log_must mkdir -p $TESTDIR/copy
-log_must cp -a /$TESTPOOL/$TESTFS/* $TESTDIR/copy/
+log_must mkdir -p $TESTDIR
+log_must rsync -aHAX /$TESTPOOL/$TESTFS/ $TESTDIR/copy
#
# 5. Unmount filesystem and export the pool
log_must ls_xattr /$TESTPOOL/$TESTFS/xattr.file
log_note "Verify working set diff:"
-log_must diff -r /$TESTPOOL/$TESTFS $TESTDIR/copy
+log_must replay_directory_diff $TESTDIR/copy /$TESTPOOL/$TESTFS
log_note "Verify file checksum:"
typeset checksum1=$(sha256digest /$TESTPOOL/$TESTFS/payload)
#
# 4. Copy TESTFS to temporary location (TESTDIR/copy)
#
-log_must mkdir -p $TESTDIR/copy
-log_must cp -a /$TESTPOOL/$TESTFS/* $TESTDIR/copy/
+log_must mkdir -p $TESTDIR
+log_must rsync -aHAX /$TESTPOOL/$TESTFS/ $TESTDIR/copy
#
# 5. Unmount filesystem and export the pool
log_must test "$(ls /$TESTPOOL/$TESTFS/dir0 | wc -l)" -eq $NFILES
log_note "Verify working set diff:"
-log_must diff -r /$TESTPOOL/$TESTFS $TESTDIR/copy
+log_must replay_directory_diff $TESTDIR/copy /$TESTPOOL/$TESTFS
log_pass "Replay of intent log succeeds."
cd $CWD || log_fail "Could not cd $CWD"
fi
- snapexists $SNAPFS
- if [[ $? -eq 0 ]]; then
- log_must zfs destroy $SNAPFS
- fi
+ snapexists $SNAPFS
+ if [[ $? -eq 0 ]]; then
+ log_must zfs destroy $SNAPFS
+ fi
- if [[ -e $SNAPDIR ]]; then
- log_must rm -rf $SNAPDIR > /dev/null 2>&1
- fi
+ if [[ -e $SNAPDIR ]]; then
+ log_must rm -rf $SNAPDIR > /dev/null 2>&1
+ fi
- if [[ -e $TESTDIR ]]; then
- log_must rm -rf $TESTDIR/* > /dev/null 2>&1
- fi
+ if [[ -e $TESTDIR ]]; then
+ log_must rm -rf $TESTDIR/* > /dev/null 2>&1
+ fi
+ if [[ -d "$SNAPSHOT_TARDIR" ]]; then
+ log_must rm -rf $SNAPSHOT_TARDIR > /dev/null 2>&1
+ fi
}
log_assert "Verify an archive of a file system is identical to " \
"an archive of its snapshot."
+SNAPSHOT_TARDIR="$(mktemp -d /tmp/zfstests_snapshot_002.XXXXXX)"
log_onexit cleanup
typeset -i COUNT=21
while [ $i -lt $COUNT ]; do
log_must file_write -o $OP -f $TESTDIR/file$i \
-b $BLOCKSZ -c $NUM_WRITES -d $DATA
-
(( i = i + 1 ))
done
log_note "Create a tarball from $TESTDIR contents..."
CWD=$PWD
cd $TESTDIR || log_fail "Could not cd $TESTDIR"
-log_must tar cf $TESTDIR/tarball.original.tar file*
+log_must tar cf $SNAPSHOT_TARDIR/original.tar .
cd $CWD || log_fail "Could not cd $CWD"
log_note "Create a snapshot and mount it..."
log_note "Create tarball of snapshot..."
CWD=$PWD
cd $SNAPDIR || log_fail "Could not cd $SNAPDIR"
-log_must tar cf $TESTDIR/tarball.snapshot.tar file*
+log_must tar cf $SNAPSHOT_TARDIR/snapshot.tar .
cd $CWD || log_fail "Could not cd $CWD"
log_must mkdir $TESTDIR/original
CWD=$PWD
cd $TESTDIR/original || log_fail "Could not cd $TESTDIR/original"
-log_must tar xf $TESTDIR/tarball.original.tar
+log_must tar xf $SNAPSHOT_TARDIR/original.tar
cd $TESTDIR/snapshot || log_fail "Could not cd $TESTDIR/snapshot"
-log_must tar xf $TESTDIR/tarball.snapshot.tar
+log_must tar xf $SNAPSHOT_TARDIR/snapshot.tar
cd $CWD || log_fail "Could not cd $CWD"
-diff -q -r $TESTDIR/original $TESTDIR/snapshot > /dev/null 2>&1
-if [[ $? -eq 1 ]]; then
- log_fail "Directory structures differ."
-fi
-
+log_must directory_diff $TESTDIR/original $TESTDIR/snapshot
log_pass "Directory structures match."
cd $CWD || log_fail "Could not cd $CWD"
fi
- snapexists $SNAPCTR
- if [[ $? -eq 0 ]]; then
- log_must zfs destroy $SNAPCTR
- fi
+ snapexists $SNAPCTR
+ if [[ $? -eq 0 ]]; then
+ log_must zfs destroy $SNAPCTR
+ fi
- if [[ -e $SNAPDIR1 ]]; then
- log_must rm -rf $SNAPDIR1 > /dev/null 2>&1
- fi
+ if [[ -e $SNAPDIR1 ]]; then
+ log_must rm -rf $SNAPDIR1 > /dev/null 2>&1
+ fi
- if [[ -e $TESTDIR1 ]]; then
- log_must rm -rf $TESTDIR1/* > /dev/null 2>&1
- fi
+ if [[ -e $TESTDIR1 ]]; then
+ log_must rm -rf $TESTDIR1/* > /dev/null 2>&1
+ fi
+ if [[ -d "$SNAPSHOT_TARDIR" ]]; then
+ log_must rm -rf $SNAPSHOT_TARDIR > /dev/null 2>&1
+ fi
}
log_assert "Verify that an archive of a dataset is identical to " \
"an archive of the dataset's snapshot."
+SNAPSHOT_TARDIR="$(mktemp -d /tmp/zfstests_snapshot_006.XXXXXX)"
log_onexit cleanup
typeset -i COUNT=21
while [ $i -lt $COUNT ]; do
log_must file_write -o $OP -f $TESTDIR1/file$i \
-b $BLOCKSZ -c $NUM_WRITES -d $DATA
-
(( i = i + 1 ))
done
log_note "Create a tarball from $TESTDIR1 contents..."
CWD=$PWD
cd $TESTDIR1 || log_fail "Could not cd $TESTDIR1"
-log_must tar cf $TESTDIR1/tarball.original.tar file*
+log_must tar cf $SNAPSHOT_TARDIR/original.tar .
cd $CWD || log_fail "Could not cd $CWD"
log_note "Create a snapshot and mount it..."
log_note "Create tarball of snapshot..."
CWD=$PWD
cd $SNAPDIR1 || log_fail "Could not cd $SNAPDIR1"
-log_must tar cf $TESTDIR1/tarball.snapshot.tar file*
+log_must tar cf $SNAPSHOT_TARDIR/snapshot.tar .
cd $CWD || log_fail "Could not cd $CWD"
log_must mkdir $TESTDIR1/original
CWD=$PWD
cd $TESTDIR1/original || log_fail "Could not cd $TESTDIR1/original"
-log_must tar xf $TESTDIR1/tarball.original.tar
+log_must tar xf $SNAPSHOT_TARDIR/original.tar
cd $TESTDIR1/snapshot || log_fail "Could not cd $TESTDIR1/snapshot"
-log_must tar xf $TESTDIR1/tarball.snapshot.tar
+log_must tar xf $SNAPSHOT_TARDIR/snapshot.tar
cd $CWD || log_fail "Could not cd $CWD"
-diff -q -r $TESTDIR1/original $TESTDIR1/snapshot > /dev/null 2>&1
-if [[ $? -eq 1 ]]; then
- log_fail "Directory structures differ."
-fi
-
+log_must directory_diff $TESTDIR1/original $TESTDIR1/snapshot
log_pass "Directory structures match."