Subject: bug#8419: cp -au : New hard links in source becomes new files at destination when using cp -au

From: Jim Meyering <[email-masked]>
Date: Tue, 26 Jul 2011 11:50:27 +0200

Pádraig Brady wrote:
> On 25/07/11 17:26, Pádraig Brady wrote:
>> Actually I'm wondering now whether the new code
>> should be unconditionally replacing the dest?
>> What if the dest is a separate newer file?
>> I.E. I think the following amended test should pass?
>> Also what about backups if the separate file is older?
>
> Well backups take a different path, as do
> older or non existing destination paths.
> So how about this change to just remove
> the new create_hard_link: call and beef up the test?
>
> cheers,
> Pádraig.
>
> 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);

BTW, I made exactly that mistake for the first iteration of the
patch I committed yesterday.  Obviously (to me, now), I should
have written more in that commit to justify the link-creating code.
But it's only this morning that I realized the non-determinism
and understood well enough to write coherently about it.

For the record, if I apply that change and run the existing test,
it fails like this (using ext4 and Fedora 15):

    $ make check -C tests TESTS=cp/preserve-link VERBOSE=yes
    ...
    + mkdir -p s t/s
    + touch s/f t/s/f
    + ln s/f s/link
    + cp -au s t
    + same_inode t/s/f t/s/link
    + local u v
    ++ stat --format %i t/s/f
    + u=1573873
    ++ stat --format %i t/s/link
    + v=1573874
    + test 1573873 = 1573874
    + fail=1
    + Exit 1
    ...
    ======================================
    1 of 1 test failed