diff --git a/src/copy.c b/src/copy.c index df8b1db..d6a0d1a 100644 --- a/src/copy.c +++ b/src/copy.c @@ -1633,11 +1633,11 @@ copy_internal (char const *src_name, char const *dst_name, this src/dest pair, in case this source file is hard-linked to another one. In that case, we'll use the mapping information to link the corresponding - destination names. */ - earlier_file = remember_copied (dst_name, src_sb.st_ino, - src_sb.st_dev); - if (earlier_file) - goto create_hard_link; + destination names. Note we don't hard link DST_NAME + here, because it may be a separate file with newer + or same timestamp. If it's older than SRC_NAME, + then this path is not taken. */ + remember_copied (dst_name, src_sb.st_ino, src_sb.st_dev); return true; } @@ -1959,7 +1959,6 @@ copy_internal (char const *src_name, char const *dst_name, } else { - create_hard_link:; /* We want to guarantee that symlinks are not followed. */ bool link_failed = (linkat (AT_FDCWD, earlier_file, AT_FDCWD, dst_name, 0) != 0); diff --git a/tests/cp/preserve-link b/tests/cp/preserve-link index d0da873..6beae02 100755 --- a/tests/cp/preserve-link +++ b/tests/cp/preserve-link @@ -28,13 +28,31 @@ same_inode() mkdir -p s t/s || framework_failure_ touch s/f t/s/f || framework_failure_ + +# a non existing link must be linked in the dest tree ln s/f s/link || framework_failure_ +# an existing link must be updated +ln s/f s/linke || framework_failure_ +ln t/s/f t/s/linke || framework_failure_ + +# an updated older file must be overwritten +ln s/f s/fileo || framework_failure_ +touch -d "-1 hour" t/s/fileo || framework_failure_ + +# an updated non linked file must not be overwritten +ln s/f s/fileu || framework_failure_ +touch -d "+1 hour" t/s/fileu || framework_failure_ + # This must create a hard link, t/s/link, to the existing file, t/s/f. # With cp from coreutils-8.12 and prior, it would mistakenly copy # the file rather than creating the link. +touch -d "+1 hour" t/s/f || framework_failure_ cp -au s t || fail=1 same_inode t/s/f t/s/link || fail=1 +same_inode t/s/f t/s/linke || fail=1 +same_inode t/s/f t/s/fileo || fail=1 +same_inode t/s/f t/s/fileu && fail=1 Exit $fail