[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#10686: mv: moving hardlink of a softlink to the softlink does nothin
From: |
Jim Meyering |
Subject: |
bug#10686: mv: moving hardlink of a softlink to the softlink does nothing |
Date: |
Wed, 01 Feb 2012 13:47:44 +0100 |
Bernhard Voelker wrote:
> Playing around with the latest mv checkin,
> I noticed another corner case:
>
> Create a file 'f', a symlink 'l' to it, and
> then a hardlink 's' to that symlink:
>
> $ touch f && ln -s f l && ln l s && ls -ogi
> total 0
> 6444 -rw-r--r-- 1 0 Feb 1 08:52 f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 l -> f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 s -> f
Hi Bernhard,
Thanks for the report.
Here, you've already gotten into questionable territory, since the
idea of a hard link to a symbolic link is not standardized.
For example, on FreeBSD 9.0, you cannot even create one:
(instead, it hard-links the referent of the symlink)
freebsd$ touch f && ln -s f l && ln l s && ls -ogi f l s
1587047 -rw-r--r-- 2 0 Feb 1 04:58 f
1587100 lrwxr-xr-x 1 1 Feb 1 04:58 l -> f
1587047 -rw-r--r-- 2 0 Feb 1 04:58 s
> Trying to mv the hardlink over the symlink seems to succeed:
>
> $ ~/git/coreutils/src/mv s l
>
> ... but the name 's' was not unlinked:
That is definitely undesirable behavior.
For now, one possibility is to make "mv" reject
that corner case with this diagnostic:
mv: 's' and 'l' are the same file
Proposed patch below.
> $ ls -ogi
> total 0
> 6444 -rw-r--r-- 1 0 Feb 1 08:52 f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 l -> f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 s -> f
>
> Using the -v option looks also normal, but is a nop:
>
> $ ~/git/coreutils/src/mv -v s l
> āsā -> ālā
> $ ls -ogi
> total 0
> 6444 -rw-r--r-- 1 0 Feb 1 08:52 f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 l -> f
> 6462 lrwxrwxrwx 2 1 Feb 1 08:52 s -> f
>
> The strace only shows the rename():
>
> stat("l", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
> lstat("s", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
> lstat("l", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
> rename("s", "l") = 0
>
>
> I'd have expected either
> a) the name 's' to be unlinked, or
> b) an error message that something was wrong (well, whatever).
> I'd prefer a) of course.
So would I.
> That behaviour didn't change at least since version 7.1
> (on openSuSE-11.3), but back in the earlier days in 5.93
> (SLES-10.3), 's' disappeared as expected:
>
> stat("l", {st_mode=S_IFREG|0640, st_size=0, ...}) = 0
> lstat("s", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
> lstat("l", {st_mode=S_IFLNK|0777, st_size=1, ...}) = 0
> unlink("l") = 0
> rename("s", "l") = 0
>
> Does mv now work as specified? Is this a kernel or
> a coreutils (or a user) issue?
It's a standards and kernel issue.
POSIX explicitly says (of rename)
If the old argument and the new argument resolve to the same existing
file, rename( ) shall return successfully and perform no other action.
though that particular wording might be slated to change with POSIX 2008,
to allow different (more desirable) behavior. Search the austin-group
archives if you need to be sure.
Also, I don't know whether the above wording applies to this particular
case of two hard links to the same symlink. Again, I think we're in
unspecified territory.
Here's a proposed patch.
This is such an improbable corner case that I think it's not worth trying
to fix any other way (either by unlinking the destination or by
replacing rename).
(of course, if I go with this, I'll insert a big comment justifying
this tiny change)
diff --git a/src/copy.c b/src/copy.c
index 4810de8..78cd8c8 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1229,7 +1229,7 @@ same_file_ok (char const *src_name, struct stat const
*src_sb,
know this here IFF preserving symlinks), then it's ok -- as long
as they are distinct. */
if (S_ISLNK (src_sb->st_mode) && S_ISLNK (dst_sb->st_mode))
- return ! same_name (src_name, dst_name);
+ return ! same_name (src_name, dst_name) && ! same_link;
src_sb_link = src_sb;
dst_sb_link = dst_sb;