mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-24 10:23:31 +08:00
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
This commit is contained in:
parent
31eeead938
commit
b01355a1dc
6
NEWS
6
NEWS
@ -11,6 +11,12 @@ GNU coreutils NEWS -*- outline -*-
|
||||
referent, there is no risk of data loss, since the symlink will
|
||||
typically still point to one of the hard links.
|
||||
|
||||
"mv A B" could succeed, yet A would remain. This would happen only when
|
||||
both A and B were hard links to the same symlink, and with a kernel for
|
||||
which rename("A","B") does nothing and returns 0 (POSIX mandates this
|
||||
surprising rename no-op behavior). Now, mv handles this case by skipping
|
||||
the usually-useless rename and simply unlinking A.
|
||||
|
||||
|
||||
* Noteworthy changes in release 8.15 (2012-01-06) [stable]
|
||||
|
||||
|
29
src/copy.c
29
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;
|
||||
|
@ -492,6 +492,7 @@ TESTS = \
|
||||
mv/partition-perm \
|
||||
mv/perm-1 \
|
||||
mv/symlink-onto-hardlink \
|
||||
mv/symlink-onto-hardlink-to-self \
|
||||
mv/to-symlink \
|
||||
mv/trailing-slash \
|
||||
mv/update \
|
||||
|
56
tests/mv/symlink-onto-hardlink-to-self
Executable file
56
tests/mv/symlink-onto-hardlink-to-self
Executable file
@ -0,0 +1,56 @@
|
||||
#!/bin/sh
|
||||
# Demonstrate that when moving a symlink onto a hardlink-to-that-symlink, the
|
||||
# source symlink is removed. Depending on your kernel (e.g., Linux, Solaris,
|
||||
# but not NetBSD), prior to coreutils-8.16, the mv would successfully perform
|
||||
# a no-op. I.e., surprisingly, mv s1 s2 would succeed, yet fail to remove s1.
|
||||
|
||||
# Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
. "${srcdir=.}/init.sh"; path_prepend_ ../src
|
||||
print_ver_ mv
|
||||
|
||||
# Create a file f, and a symlink s1 to that file.
|
||||
touch f || framework_failure_
|
||||
ln -s f s2 || framework_failure_
|
||||
|
||||
for opt in '' --backup; do
|
||||
|
||||
# Attempt to create a hard link to that symlink.
|
||||
# On some systems, it's not possible: they create a hard link to the referent.
|
||||
ln s2 s1 || framework_failure_
|
||||
|
||||
# If s1 is not a symlink, skip this test.
|
||||
test -h s1 \
|
||||
|| skip_ your kernel or file system cannot create a hard link to a symlink
|
||||
|
||||
mv $opt s1 s2 > out 2>&1 || fail=1
|
||||
compare /dev/null out || fail=1
|
||||
|
||||
# Ensure that s1 is gone.
|
||||
test -e s1 && fail=1
|
||||
|
||||
if test "$opt" = --backup; then
|
||||
# With --backup, ensure that the backup file was created.
|
||||
ref=$(readlink s2~) || fail=1
|
||||
test "$ref" = f || fail=1
|
||||
else
|
||||
# Without --backup, ensure there is no backup file.
|
||||
test -e s2~ && fail=1
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
Exit $fail
|
Loading…
Reference in New Issue
Block a user