diff --git a/lxd/rsync/rsync.go b/lxd/rsync/rsync.go index a4a857abc04c..32f8ffc8c9f2 100644 --- a/lxd/rsync/rsync.go +++ b/lxd/rsync/rsync.go @@ -78,8 +78,9 @@ func LocalCopy(source string, dest string, bwlimit string, xattrs bool, rsyncArg "--sparse", "--devices", "--delete", - "--checksum", "--numeric-ids", + // Checks for file modifications on nanoseconds granularity. + "--modify-window=-1", } if xattrs { @@ -316,6 +317,9 @@ func Recv(path string, conn io.ReadWriteCloser, tracker *ioprogress.ProgressTrac "--devices", "--partial", "--sparse", + // This flag is only required on the receiving end. + // Checks for file modifications on nanoseconds granularity. + "--modify-window=-1", } if len(features) > 0 { diff --git a/test/suites/migration.sh b/test/suites/migration.sh index ed5f4bde20ea..a0a33260e914 100644 --- a/test/suites/migration.sh +++ b/test/suites/migration.sh @@ -292,9 +292,49 @@ migration() { lxc_remote copy l1:c1 l2:c2 --refresh lxc_remote start l2:c2 lxc_remote file pull l2:c2/root/testfile1 . + [ "$(cat testfile1)" = "test" ] rm testfile1 lxc_remote stop -f l2:c2 + # Change the files modification time by adding one nanosecond. + # Perform the change on the test runner since the busybox instances `touch` doesn't support setting nanoseconds. + lxc_remote start l1:c1 + c1_pid="$(lxc_remote query l1:/1.0/instances/c1?recursion=1 | jq -r .state.pid)" + mtime_old="$(stat -c %y "/proc/${c1_pid}/root/root/testfile1")" + mtime_old_ns="$(date -d "$mtime_old" +%N | sed 's/^0*//')" + + # Ensure the final nanoseconds are padded with zeros to create a valid format. + mtime_new_ns="$(printf "%09d\n" "$((mtime_old_ns+1))")" + mtime_new="$(date -d "$mtime_old" "+%Y-%m-%d %H:%M:%S.${mtime_new_ns} %z")" + lxc_remote stop -f l1:c1 + + # Before setting the new mtime create a local copy too. + lxc_remote copy l1:c1 l1:c2 + + # Change the modification time. + lxc_remote start l1:c1 + c1_pid="$(lxc_remote query l1:/1.0/instances/c1?recursion=1 | jq -r .state.pid)" + touch -m -d "$mtime_new" "/proc/${c1_pid}/root/root/testfile1" + lxc_remote stop -f l1:c1 + + # Starting from rsync 3.1.3 it should discover the change of +1 nanosecond. + # Check if the file got refreshed to a different remote. + lxc_remote copy l1:c1 l2:c2 --refresh + lxc_remote start l1:c1 l2:c2 + c1_pid="$(lxc_remote query l1:/1.0/instances/c1?recursion=1 | jq -r .state.pid)" + c2_pid="$(lxc_remote query l2:/1.0/instances/c2?recursion=1 | jq -r .state.pid)" + [ "$(stat "/proc/${c1_pid}/root/root/testfile1" -c %y)" = "$(stat "/proc/${c2_pid}/root/root/testfile1" -c %y)" ] + lxc_remote stop -f l1:c1 l2:c2 + + # Check if the file got refreshed locally. + lxc_remote copy l1:c1 l1:c2 --refresh + lxc_remote start l1:c1 l1:c2 + c1_pid="$(lxc_remote query l1:/1.0/instances/c1?recursion=1 | jq -r .state.pid)" + c2_pid="$(lxc_remote query l1:/1.0/instances/c2?recursion=1 | jq -r .state.pid)" + [ "$(stat "/proc/${c1_pid}/root/root/testfile1" -c %y)" = "$(stat "/proc/${c2_pid}/root/root/testfile1" -c %y)" ] + lxc_remote rm -f l1:c2 + lxc_remote stop -f l1:c1 + # This will create snapshot c1/snap0 with test device and expiry date. lxc_remote config device add l1:c1 testsnapdev none lxc_remote config set l1:c1 snapshots.expiry '1d'