diff options
author | Jim Meyering <meyering@redhat.com> | 2012-02-01 21:42:45 +0100 |
---|---|---|
committer | Jim Meyering <meyering@redhat.com> | 2012-02-12 12:21:53 +0100 |
commit | b01355a1dc4939a3967ee82ac0a6dd19702778c3 (patch) | |
tree | a8f6534979bebdda6d60a5985d9a7094322ae95b /src/copy.c | |
parent | 31eeead93842c6581e0a9b21f6e8eb964d0c9309 (diff) | |
download | coreutils-b01355a1dc4939a3967ee82ac0a6dd19702778c3.tar.xz |
mv: "mv A B" would sometimes succeed, yet A would remain, ...
But only when both A and B were hard links to the same symlink.
* src/copy.c (same_file_ok): Handle another special case: the one
in which we are moving a symlink onto a hard link to itself.
In this case, we must explicitly tell the caller to unlink the
source file. Otherwise, at least the linux-3.x kernel rename
function would do nothing, as mandated by POSIX 2008.
* tests/mv/symlink-onto-hardlink-to-self: New test.
* tests/Makefile.am (TESTS): Add it.
* NEWS (Bug fixes): Mention it.
Reported by Bernhard Voelker in http://bugs.gnu.org/10686
Diffstat (limited to 'src/copy.c')
-rw-r--r-- | src/copy.c | 29 |
1 files changed, 26 insertions, 3 deletions
diff --git a/src/copy.c b/src/copy.c index 4810de872..541ca20cb 100644 --- a/src/copy.c +++ b/src/copy.c @@ -1226,10 +1226,33 @@ same_file_ok (char const *src_name, struct stat const *src_sb, same_link = same; /* If both the source and destination files are symlinks (and we'll - know this here IFF preserving symlinks), then it's ok -- as long - as they are distinct. */ + know this here IFF preserving symlinks), then it's usually ok + when they are distinct. */ if (S_ISLNK (src_sb->st_mode) && S_ISLNK (dst_sb->st_mode)) - return ! same_name (src_name, dst_name); + { + bool sn = same_name (src_name, dst_name); + if ( ! sn) + { + /* It's fine when we're making any type of backup. */ + if (x->backup_type != no_backups) + return true; + + /* Here we have two symlinks that are hard-linked together, + and we're not making backups. In this unusual case, simply + returning true would lead to mv calling "rename(A,B)", + which would do nothing and return 0. I.e., A would + not be removed. Hence, the solution is to tell the + caller that all it must do is unlink A and return. */ + if (same_link) + { + *unlink_src = true; + *return_now = true; + return true; + } + } + + return ! sn; + } src_sb_link = src_sb; dst_sb_link = dst_sb; |