Subject: bug#6960: mv refuses to move a symlink over a hard link to the same file

From: Eric Blake <[email-masked]>
Date: Tue, 31 Aug 2010 17:30:19 -0600

On 08/31/2010 04:48 PM, Davide Brini wrote:
> A simpler example is something like
>
> $ touch New_York
> $ ln -s New_York New_York.sym
> $ mv New_York.sym New_York
> mv: `New_York.sym' and `New_York' are the same file
>
> I think the reason is that when the source is a symbolic link, mv operates
> on the symlink itself, not the file it points to. So, it can't rename the
> symlink, because if successful that would imply having two entries named
> "New_York" in the same directory.

Not quite.  POSIX specifies that:
http://www.opengroup.org/onlinepubs/9699919799/utilities/mv.html

If the source_file operand and destination path name the same existing 
file, then the destination path shall not be removed, and one of the 
following shall occur:

    1. No change is made to source_file, no error occurs, and no 
diagnostic is issued.
    2. No change is made to source_file, a diagnostic is issued to 
standard error identifying the two names, and the exit status is affected.
    3. If the source_file operand and destination path name distinct 
directory entries, then the source_file operand is removed, no error 
occurs, and no diagnostic is issued.

The question boils down to whether "New_York" and "New_York.sym" name 
the same file (stat semantics), or whether, since mv generally operates 
on symlinks rather than dereferencing them, they are distinct files 
(lstat semantics).

Solaris /usr/xpg4/bin/mv (which is supposedly POSIX-compliant, although 
we could debate that) goes ahead with the move:
$ touch a
$ ln -s a b
$ /usr/xpg4/bin/mv b a
$ echo $? ?
0 a
$ readlink a
a

Certainly, the underlying rename() call is allowed to replace a file 
with a symlink, even if the symlink was to the file that it was 
replacing (and thus will create an ELOOP situation on the symlink while 
losing the contents that were pointed to).  So, in that regards, you 
could argue that Solaris' behavior is dangerous (it lost data) while 
coreutils is playing it safe, when the destination has a link count of 1 
in this simplified example.  But when the destination has a link count 
of 2, there is no data loss, so the original example seems like it is 
pointing out a case where coreutils is over-strict.

And I would argue that the strict POSIX wording says that we should only 
be refusing to move if the two files have the same inode; in both the 
original and simplified examples, the symlink has a different inode than 
the file it would be overwriting, so I think POSIX mandates that the 
data destruction happen rather than the coreutils behavior.

Maybe this would argue for a POSIXLY_CORRECT behavior choice?  :(

Maybe we need to raise this as a question to the Austin Group to argue 
that coreutils behavior should be permitted?

> If this is really the reason for the
> error, the error message could probably be improved.

This part is true - the error message could definitely be more precise, 
if we decide that POSIX even lets us issue an error in the first place.

-- 
Eric Blake   [email-masked]    +1-801-349-2682
Libvirt virtualization library http://libvirt.org