diff options
author | Jim Meyering <meyering@redhat.com> | 2011-07-25 11:31:01 +0200 |
---|---|---|
committer | Jim Meyering <meyering@redhat.com> | 2011-07-25 14:15:44 +0200 |
commit | f28a09810b7dc11261cb43e2a09726caf9a993b7 (patch) | |
tree | 5fd2afa981e816f28eaa8aba4a189620a032abfe /src | |
parent | aeb5222a6d7f7e9be5b737e2dbaf71185a9946db (diff) | |
download | coreutils-f28a09810b7dc11261cb43e2a09726caf9a993b7.tar.xz |
cp -up: preserve all hard links
* src/copy.c (copy_internal): With --update (-u), this function would
return early once it found that the destination is not older than the
source, *without* recording the source-dev/ino--to--dest_name mapping.
That mapping is required in order to preserve src hard links in the
destination tree, so when using cp with --update and --preserve=links
(perhaps via -p or -a), cp could fail to preserve one hard link
per inode when at least one of the hard-linked names already exists
in the destination tree.
Reported by Odd Harry Mannsverk in http://debbugs.gnu.org/8419.
* tests/cp/preserve-link: New file. Exercise the flaw/fix.
* tests/Makefile.am (TESTS): Add it.
* NEWS (Bug fixes): Mention it.
Diffstat (limited to 'src')
-rw-r--r-- | src/copy.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/src/copy.c b/src/copy.c index c17b94211..df8b1db74 100644 --- a/src/copy.c +++ b/src/copy.c @@ -1628,6 +1628,17 @@ copy_internal (char const *src_name, char const *dst_name, end up removing the source file. */ if (rename_succeeded) *rename_succeeded = true; + + /* However, we still must record that we've processed + 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; + return true; } } @@ -1948,6 +1959,7 @@ 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); |