#! /bin/bash
# FS QA Test No. btrfs/127
#
# Test that an incremental send operation works after doing radical changes
# in the directory hierarchy that involve switching the inode that directory
# entries point to.
#
#-----------------------------------------------------------------------
# Copyright (C) 2016 SUSE Linux Products GmbH. All Rights Reserved.
# Author: Filipe Manana <fdmanana@suse.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#-----------------------------------------------------------------------
#

seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"

tmp=/tmp/$$
status=1	# failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15

_cleanup()
{
	cd /
	rm -fr $send_files_dir
	rm -f $tmp.*
}

# get standard environment, filters and checks
. ./common/rc
. ./common/filter

# real QA test starts here
_supported_fs btrfs
_supported_os Linux
_require_test
_require_scratch
_require_fssum

send_files_dir=$TEST_DIR/btrfs-test-$seq

rm -f $seqres.full
rm -fr $send_files_dir
mkdir $send_files_dir

_scratch_mkfs >>$seqres.full 2>&1
_scratch_mount

# case 1
mkdir -p $SCRATCH_MNT/case_1/d/p1
mkdir $SCRATCH_MNT/case_1/p1

# case 2
mkdir -p $SCRATCH_MNT/case_2/a
mkdir $SCRATCH_MNT/case_2/d
mkdir $SCRATCH_MNT/case_2/e
mkdir $SCRATCH_MNT/case_2/f
mkdir $SCRATCH_MNT/case_2/ance
mkdir $SCRATCH_MNT/case_2/d/ance
mkdir $SCRATCH_MNT/case_2/a/c
mv $SCRATCH_MNT/case_2/e $SCRATCH_MNT/case_2/d/ance
mv $SCRATCH_MNT/case_2/f $SCRATCH_MNT/case_2/d/ance
mv $SCRATCH_MNT/case_2/ance $SCRATCH_MNT/case_2/d/ance

# case 3
mkdir -p $SCRATCH_MNT/case_3/d
mkdir $SCRATCH_MNT/case_3/a
mkdir $SCRATCH_MNT/case_3/waiting_dir
mkdir -p $SCRATCH_MNT/case_3/pre/ance
mkdir $SCRATCH_MNT/case_3/d/ance
mkdir $SCRATCH_MNT/case_3/a/c
mv $SCRATCH_MNT/case_3/waiting_dir $SCRATCH_MNT/case_3/d/ance

# case 4
mkdir -p $SCRATCH_MNT/case_4/tmp
mkdir $SCRATCH_MNT/case_4/below_ance
mkdir -p $SCRATCH_MNT/case_4/pre/wait_dir
mkdir $SCRATCH_MNT/case_4/desc
mkdir $SCRATCH_MNT/case_4/ance
mv $SCRATCH_MNT/case_4/below_ance $SCRATCH_MNT/case_4/ance
mkdir $SCRATCH_MNT/case_4/other_dir

# Filesystem looks like:
#
# .                                                                  (ino 256)
# |--- case_1/                                                       (ino 257)
# |       |---- d/                                                   (ino 258)
# |       |     |--- p1/                                             (ino 259)
# |       |
# |       |---- p1/                                                  (ino 260)
# |
# |--- case_2/                                                       (ino 261)
# |       |---- a/                                                   (ino 262)
# |       |     |---- c/                                             (ino 268)
# |       |
# |       |---- d/                                                   (ino 263)
# |             |---- ance/                                          (ino 267)
# |                     |---- e/                                     (ino 264)
# |                     |---- f/                                     (ino 265)
# |                     |---- ance/                                  (ino 266)
# |
# |--- case_3/                                                       (ino 269)
# |       |---- a/                                                   (ino 271)
# |       |     |---- c/                                             (ino 276)
# |       |
# |       |---- d/                                                   (ino 270)
# |       |     |---- ance/                                          (ino 275)
# |       |             |---- waiting_dir/                           (ino 272)
# |       |
# |       |---- pre/                                                 (ino 273)
# |              |---- ance/                                         (ino 274)
# |
# |--- case_4/                                                       (ino 277)
#         |---- tmp/                                                 (ino 278)
#         |---- pre/                                                 (ino 280)
#         |      |---- wait_dir/                                     (ino 281)
#         |
#         |---- desc/                                                (ino 282)
#         |---- ance/                                                (ino 283)
#         |       |---- below_ance/                                  (ino 279)
#         |
#         |---- other_dir/                                           (ino 284)
#
_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/mysnap1

# case 1
#
# The directory named "d" (inode 257) has in both snapshots an entry with the
# name "p1" but it refers to different inodes in both snapshots (inode 258 in
# the parent snapshot and inode 259 in the send snapshot). When attempting to
# move inode 258, the operation is delayed because its new parent, inode 259,
# was not yet moved/renamed (as the stream is currently processing inode 258).
# Later on when processing inode 259, btrfs' send also ended up delaying the
# move/rename operation for this inode, so that it would happen after inode 258
# was processed, creating a circular dependency that resulted in the send stream
# terminating without issuing a rename operations for the inodes 258 and 259.
#
mv $SCRATCH_MNT/case_1/d/p1 $SCRATCH_MNT/case_1/p1
mv $SCRATCH_MNT/case_1/p1 $SCRATCH_MNT/case_1/d

# case 2
#
# When the inode 265 is processed, the path for inode 267 is computed, which at
# that time corresponds to "case_2/d/ance", and it was stored in the name cache
# (to avoid recomputing it again later when needed).
# Later on when processing inode 266, btrfs' send end up orphanizing (renaming
# to a name matching the pattern o<ino>-<gen>-<seq>) inode 267 because it has
# the same name as inode 266 and at that time it's a child of the new parent
# directory (inode 263) for inode 266. After the orphanization and while still
# processing inode 266, a rename operation for inode 266 was generated. However
# the source path for that rename operation was incorrect because it ended up
# using the old, pre-orphanization, name of inode 267.
#
mv $SCRATCH_MNT/case_2/a/c $SCRATCH_MNT/case_2
mv $SCRATCH_MNT/case_2/d/ance $SCRATCH_MNT/case_2/c
mv $SCRATCH_MNT/case_2/c/ance/ance $SCRATCH_MNT/case_2/d
mv $SCRATCH_MNT/case_2/c/ance/f $SCRATCH_MNT/case_2
mv $SCRATCH_MNT/case_2/c/ance/e $SCRATCH_MNT/case_2/f

# case 3
#
# This is similar to cases 1 and 2, but adding more complexity just to exercise
# btrfs' incremental send correctness.
#
mv $SCRATCH_MNT/case_3/d/ance $SCRATCH_MNT/case_3/a
mv $SCRATCH_MNT/case_3/a/c $SCRATCH_MNT/case_3
mv $SCRATCH_MNT/case_3/a/ance/waiting_dir $SCRATCH_MNT/case_3/c
mv $SCRATCH_MNT/case_3/pre/ance $SCRATCH_MNT/case_3/d
mv $SCRATCH_MNT/case_3/pre $SCRATCH_MNT/case_3/c/waiting_dir

# case 4
#
# When attempting to rename inode 283, the incremental send stream included an
# invalid destination path for the rename command. This was due to a missing
# path loop detection in the send code that made the rename of inode 283 happen
# without waiting for the rename of inode 284 to happen first (since it's an
# ancestor in the send snapshot that is beyond the current progress and it was
# also renamed/moved).
#
mv $SCRATCH_MNT/case_4/other_dir $SCRATCH_MNT/case_4/tmp
mv $SCRATCH_MNT/case_4/ance/below_ance $SCRATCH_MNT/case_4/tmp/other_dir
mv $SCRATCH_MNT/case_4/pre/wait_dir $SCRATCH_MNT/case_4/tmp/other_dir
mv $SCRATCH_MNT/case_4/pre $SCRATCH_MNT/case_4/tmp/other_dir/below_ance
mv $SCRATCH_MNT/case_4/desc $SCRATCH_MNT/case_4/tmp/other_dir/wait_dir
mv $SCRATCH_MNT/case_4/ance $SCRATCH_MNT/case_4/tmp/other_dir/wait_dir/desc

# Filesystem now looks like:
#
# .                                                                  (ino 256)
# |--- case_1/                                                       (ino 257)
# |        |--- d/                                                   (ino 258)
# |             |--- p1/                                             (ino 260)
# |                   |--- p1/                                       (ino 259)
# |
# |--- case_2/                                                       (ino 261)
# |       |---- a/                                                   (ino 262)
# |       |---- c/                                                   (ino 268)
# |       |     |---- ance/                                          (ino 267)
# |       |
# |       |---- d/                                                   (ino 263)
# |       |     |---- ance/                                          (ino 266)
# |       |
# |       |---- f/                                                   (ino 265)
# |             |---- e/                                             (ino 264)
# |
# |--- case_3/                                                       (ino 269)
# |       |---- a/                                                   (ino 271)
# |       |     |---- ance/                                          (ino 275)
# |       |
# |       |---- c/                                                   (ino 276)
# |       |     |---- waiting_dir/                                   (ino 272)
# |       |                   |---- pre/                             (ino 273)
# |       |
# |       |---- d/                                                   (ino 270)
# |             |---- ance/                                          (ino 274)
# |
# |--- case_4/                                                       (ino 277)
#         |---- tmp/                                                 (ino 278)
#                |---- other_dir/                                    (ino 284)
#                            |---- below_ance/                       (ino 279)
#                            |            |---- pre/                 (ino 280)
#                            |
#                            |---- wait_dir/                         (ino 281)
#                                       |---- desc/                  (ino 282)
#                                               |---- ance/          (ino 283)
#
_run_btrfs_util_prog subvolume snapshot -r $SCRATCH_MNT $SCRATCH_MNT/mysnap2

run_check $FSSUM_PROG -A -f -w $send_files_dir/1.fssum $SCRATCH_MNT/mysnap1
run_check $FSSUM_PROG -A -f -w $send_files_dir/2.fssum \
	-x $SCRATCH_MNT/mysnap2/mysnap1 $SCRATCH_MNT/mysnap2

_run_btrfs_util_prog send $SCRATCH_MNT/mysnap1 -f $send_files_dir/1.snap
_run_btrfs_util_prog send -p $SCRATCH_MNT/mysnap1 $SCRATCH_MNT/mysnap2 \
	-f $send_files_dir/2.snap

# Now recreate the filesystem by receiving both send streams and verify we get
# the same content that the original filesystem had.
_scratch_unmount
_scratch_mkfs >>$seqres.full 2>&1
_scratch_mount

_run_btrfs_util_prog receive $SCRATCH_MNT -f $send_files_dir/1.snap
run_check $FSSUM_PROG -r $send_files_dir/1.fssum $SCRATCH_MNT/mysnap1
_run_btrfs_util_prog receive $SCRATCH_MNT -f $send_files_dir/2.snap
run_check $FSSUM_PROG -r $send_files_dir/2.fssum $SCRATCH_MNT/mysnap2

echo "Silence is golden"
status=0
exit
