bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-12 Thread Jim Meyering
Eric Blake wrote:
 On 02/11/2012 04:23 AM, Jim Meyering wrote:
 +++ b/NEWS
...
 +  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) would do nothing and return 0.  Now, in this
 +  unusual case, mv does not call rename, and instead simply unlinks A.

 You make it sound like a kernel where rename(A,B) returns 0 is
 unusual;

Thank you for the review and suggestions.

Such kernels *should* be unusual.  This rename-is-sometimes-a-no-op
exception makes it hard to use rename in an application that must
reliably produce results that make sense even to people who don't
care what inodes and invariants are.

 on the contrary, that is normal, since it is the POSIX-mandated
 behavior for kernels.  What is unusual is having two hardlinks to the
 same symlink.  Maybe we should reword this slightly, to attach the
 unusual modifier to the correct phrase, or even take kernel out of
 the description:

 mv A B could succeed, yet A would remain.  This would only happen
 in the unusual case when both A and B were hard links to the same
 symlink, due to the standard behavior of rename.  Now, mv recognizes
 the case and simply unlinks A.

This is the NEWS file, where we prefer to stick to the facts, but I
feel I have to make a small statement, so have adjusted it like this:

  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.


 +++ b/tests/mv/symlink-onto-hardlink-to-self
 @@ -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., with the linux
 +# kernel), prior to coreutils-8.16, the mv would successfully perform a 
 no-op.

 Again, this is the POSIX-required behavior of ALL kernels, and not
 something special to Linux.

NetBSD 5.1 has the sensible kernel rename behavior, i.e.,
what one would expect in the absence of standardized legacy:

netbsd$ :  f; ln f g; perl -e 'rename qw(f g) or die $!'; ls f g
ls: cannot access f: No such file or directory
g
[Exit 2]

Programs like mv should not have to jump through hoops like copy.c's
same_file_ok function just to avoid the surprising (nonsensical, to most)
behavior of the standardized rename syscall.

I adjusted that comment, too, and pushed the result:

# 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.





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-11 Thread Jim Meyering
Jim Meyering wrote:
 The above wasn't quite right in that it failed to honor mv's --backup
 option.  mv --backup s f would not have created the required backup file.
 I've adjusted it to fix that, and added tests to cover both cases.
 This is still not quite ready (i.e., the FIXME comment, where I'll add
 some explanation), but otherwise should be fine.

 Date: Wed, 1 Feb 2012 21:42:45 +0100
 Subject: [PATCH] mv: mv A B would sometimes succeed, yet A would remain,
  ...

I address the FIXME and tweaked the comment: (A,B) seemed a little
clearer than (s1,s2).  I've also tightened up the test, regarding
--backup and existence of backup files.


From de817cfa6e780323b3eac1f92ac81e8c7871d088 Mon Sep 17 00:00:00 2001
From: Jim Meyering meyer...@redhat.com
Date: Wed, 1 Feb 2012 21:42:45 +0100
Subject: [PATCH] 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
---
 NEWS   |5 +++
 src/copy.c |   29 +++--
 tests/Makefile.am  |1 +
 tests/mv/symlink-onto-hardlink-to-self |   56 
 4 files changed, 88 insertions(+), 3 deletions(-)
 create mode 100755 tests/mv/symlink-onto-hardlink-to-self

diff --git a/NEWS b/NEWS
index 9eebbf6..ca8c0b5 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,11 @@ 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) would do nothing and return 0.  Now, in this
+  unusual case, mv does not call rename, and instead simply unlinks A.
+

 * Noteworthy changes in release 8.15 (2012-01-06) [stable]

diff --git a/src/copy.c b/src/copy.c
index 4810de8..541ca20 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;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a94aaa2..c951f69 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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\
diff --git a/tests/mv/symlink-onto-hardlink-to-self 
b/tests/mv/symlink-onto-hardlink-to-self
new file mode 100755
index 000..efaff66
--- /dev/null
+++ b/tests/mv/symlink-onto-hardlink-to-self
@@ -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., with the linux
+# kernel), prior to 

bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-11 Thread Eric Blake
On 02/11/2012 04:23 AM, Jim Meyering wrote:
 +++ b/NEWS
 @@ -11,6 +11,11 @@ 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) would do nothing and return 0.  Now, in this
 +  unusual case, mv does not call rename, and instead simply unlinks A.

You make it sound like a kernel where rename(A,B) returns 0 is
unusual; on the contrary, that is normal, since it is the POSIX-mandated
behavior for kernels.  What is unusual is having two hardlinks to the
same symlink.  Maybe we should reword this slightly, to attach the
unusual modifier to the correct phrase, or even take kernel out of
the description:

mv A B could succeed, yet A would remain.  This would only happen
in the unusual case when both A and B were hard links to the same
symlink, due to the standard behavior of rename.  Now, mv recognizes
the case and simply unlinks A.

 +++ b/tests/mv/symlink-onto-hardlink-to-self
 @@ -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., with the linux
 +# kernel), prior to coreutils-8.16, the mv would successfully perform a 
 no-op.

Again, this is the POSIX-required behavior of ALL kernels, and not
something special to Linux.

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org



signature.asc
Description: OpenPGP digital signature


bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-07 Thread Bernhard Voelker

On 02/07/2012 11:45 AM, Michael Felt wrote:
 Just reading - for reference only - AIX 5.3, and I expect new versions behave 
 as follows
 (second link becomes a hardlink to original file, mv removes one hard link, 
 i.e. original file (as inode) remains.
 
 root@x105:[/tmp/test]touch f
 root@x105:[/tmp/test]ln -s f l
 root@x105:[/tmp/test]ls -l
 total 0
 -rw-r--r--  1 root system 0 2012-02-07 11:41 f
 lrwxrwxrwx  1 root system 1 2012-02-07 11:41 l - f
 root@x105:[/tmp/test]ln l s
 root@x105:[/tmp/test]ls -ogi
 total 0
 239 -rw-r--r--  2 0 2012-02-07 11:41 f
 240 lrwxrwxrwx  1 1 2012-02-07 11:41 l - f
 239 -rw-r--r--  2 0 2012-02-07 11:41 s
 root@x105:[/tmp/test]mv s f
 root@x105:[/tmp/test]ls -ogi
 total 0
 239 -rw-r--r--  1 0 2012-02-07 11:41 f
 240 lrwxrwxrwx  1 1 2012-02-07 11:41 l - f

Hi Michael,

the result is the same, but there's a little difference here:
your ln does not create a hardlink to the symlink, but
rather a hardlink to the symlink's *target*.

On Linux, the situation before mv looks like this:

  $ touch f  ln -s f l  ln l s  ls -ogi
  total 0
  11279 -rw-r--r-- 1 0 Feb  7 13:17 f
  11280 lrwxrwxrwx 2 1 Feb  7 13:17 l - f
  11280 lrwxrwxrwx 2 1 Feb  7 13:17 s - f

Before Jim's patch, mv didn't remove the source, i.e. 's':

  $ /bin/mv s l ; ls -ogi
  total 0
  11279 -rw-r--r-- 1 0 Feb  7 13:17 f
  11280 lrwxrwxrwx 2 1 Feb  7 13:17 l - f
  11280 lrwxrwxrwx 2 1 Feb  7 13:17 s - f

Now, mv correctly unlinks 's':

  $ ~/git/coreutils/src/mv s l ; ls -ogi
  total 0
  11279 -rw-r--r-- 1 0 Feb  7 13:17 f
  11280 lrwxrwxrwx 1 1 Feb  7 13:17 l - f

Regarding your case, there's no change.

Have a nice day,
Berny





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-06 Thread Bernhard Voelker
On 02/04/2012 04:49 PM, Jim Meyering wrote:
 The above wasn't quite right in that it failed to honor mv's --backup
 option.  mv --backup s f would not have created the required backup file.
 I've adjusted it to fix that, and added tests to cover both cases.
 This is still not quite ready (i.e., the FIXME comment, where I'll add
 some explanation), but otherwise should be fine.

Thanks, looks fine.

Have a nice day,
Berny





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-04 Thread Jim Meyering
Jim Meyering wrote:

 Eric Blake wrote:

 On 02/01/2012 05:47 AM, Jim Meyering wrote:
 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.

 Actually, POSIX 2008 _did_ standardize the notion of a hard link to a
 symlink, thanks to linkat().

 For example, on FreeBSD 9.0, you cannot even create one:

 That's a POSIX-compliance bug in FreeBSD.  In that case, the best we can
 do is emulate it by creating a new symlink with identical contents and
 owner and as much of the timestamp as lutimens will allow.


 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.

 The wording for rename(2) did not change, but the wording for mv(1)
 _did_ change to allow slightly more desirable behavior.  Here's the
 latest wording, as modified by the latest bug report:

 http://austingroupbugs.net/view.php?id=534

 If the source_file operand and destination path resolve to either the
 same existing directory entry or different directory entries for the
 same existing file, then the destination path shall not be removed, and
 one of the following shall occur:
  a. No change is made to source_file, no error occurs, and no diagnostic
 is issued.
  b. No change is made to source_file, a diagnostic is issued to standard
 error identifying the two names, and the exit status is affected.
  c. 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 mv utility shall do nothing more with the current source_file, and
 go on to any remaining source_files.

 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.

 POSIX actually specifies this quite well - two hardlinks to the same
 symlink qualify as an instance of two file names that resolve to the
 same inode after path resolution as checked with lstat().

 Thanks for the clarification and quotes, Eric.

 Bernhard, here's a better patch.
 With it, mv simply unlinks the s in your example:

 diff --git a/src/copy.c b/src/copy.c
 index 4810de8..73f5cc5 100644
 --- a/src/copy.c
 +++ b/src/copy.c
 @@ -1229,7 +1229,17 @@ 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);
 +{
 +  bool sn = same_name (src_name, dst_name);
 +  if ( ! sn  same_link)
 +{
 +  *unlink_src = true;
 +  *return_now = true;
 +  return true;
 +}
 +
 +  return ! sn;
 +}

The above wasn't quite right in that it failed to honor mv's --backup
option.  mv --backup s f would not have created the required backup file.
I've adjusted it to fix that, and added tests to cover both cases.
This is still not quite ready (i.e., the FIXME comment, where I'll add
some explanation), but otherwise should be fine.


From 646456b8ce053b85d30174462620492df648e0e2 Mon Sep 17 00:00:00 2001
From: Jim Meyering meyer...@redhat.com
Date: Wed, 1 Feb 2012 21:42:45 +0100
Subject: [PATCH] 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
---
 NEWS   |5 +++
 src/copy.c |   26 ++--
 tests/Makefile.am  |1 +
 tests/mv/symlink-onto-hardlink-to-self |   53 
 4 files changed, 82 insertions(+), 3 deletions(-)

bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Bernhard Voelker
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

Trying to mv the hardlink over the symlink seems to succeed:

  $ ~/git/coreutils/src/mv s l

... but the name 's' was not unlinked:

  $ 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.

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?

Have a nice day,
Berny






bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Bernhard Voelker
On 02/01/2012 09:21 AM, Bernhard Voelker wrote:

   $ 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
 
 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 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:

Hmm, looks like commit
   f1d1ee91217d08527c6bf7682987339e38116268
introduced this in 2006.

Have a nice day,
Berny





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Jim Meyering
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;





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Bernhard Voelker
On 02/01/2012 01:47 PM, Jim Meyering wrote:
 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;

Hi Jim,

well, if it's not standardized (yet), then I agree with you that we
should choose the simpler b) alternative.

Do you think it's work thinking about the -f case?

  $ ~/git/coreutils/src/mv -f s l
  /home/berny/git/coreutils/src/mv: ‘s’ and ‘l’ are the same file

The info pages are not quite clear in which cases mv tries to
overwrite the destination. Usually, I am tempted to think that
a --force will just do it - regardless of data loss.
But in this case, mv just does not and the only thing left
is to rm that hardlink.

That makes me think that the implementation of the a) alternative
just reduces to calling unlink() ... ;-)

Have a nice day,
Berny





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Jim Meyering
Bernhard Voelker wrote:
...
 well, if it's not standardized (yet), then I agree with you that we
 should choose the simpler b) alternative.

 Do you think it's work thinking about the -f case?

   $ ~/git/coreutils/src/mv -f s l
   /home/berny/git/coreutils/src/mv: ‘s’ and ‘l’ are the same file

mv's -f option is unrelated to this case.
It relates solely to controlling whether one is prompted
due to permissions or -i.

From 'info mv's description of the common case (no options):

   If a destination file exists but is normally unwritable, standard
input is a terminal, and the `-f' or `--force' option is not given,
`mv' prompts the user for whether to replace the file.  (You might own
the file, or have write permission on its directory.)  If the response
is not affirmative, the file is skipped.


Yes, it is unfortunate that the standards-imposed semantics
of -f is different from what we'd expect.

 The info pages are not quite clear in which cases mv tries to
 overwrite the destination. Usually, I am tempted to think that
 a --force will just do it - regardless of data loss.
 But in this case, mv just does not and the only thing left
 is to rm that hardlink.

If the above is still not clear, please suggest how to improve it.

 That makes me think that the implementation of the a) alternative
 just reduces to calling unlink() ... ;-)





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Bernhard Voelker


On 02/01/2012 03:02 PM, Jim Meyering wrote:
 Bernhard Voelker wrote:
 ...
 well, if it's not standardized (yet), then I agree with you that we
 should choose the simpler b) alternative.

 Do you think it's work thinking about the -f case?

   $ ~/git/coreutils/src/mv -f s l
   /home/berny/git/coreutils/src/mv: ‘s’ and ‘l’ are the same file
 
 mv's -f option is unrelated to this case.
 It relates solely to controlling whether one is prompted
 due to permissions or -i.
 
 From 'info mv's description of the common case (no options):
 
If a destination file exists but is normally unwritable, standard
 input is a terminal, and the `-f' or `--force' option is not given,
 `mv' prompts the user for whether to replace the file.  (You might own
 the file, or have write permission on its directory.)  If the response
 is not affirmative, the file is skipped.
 
 
 Yes, it is unfortunate that the standards-imposed semantics
 of -f is different from what we'd expect.
 

 If the above is still not clear, please suggest how to improve it.

Well, if permissions are the only case when mv would require
confirmation or -f, then it should be added to mv's description.

Additionally, the info page might also have note in which cases
mv will refuse to work, e.g. when ‘s’ and ‘l’ are the same file.

As I'm not sure (anymore) about both, I cannot propose a wording.


 That makes me think that the implementation of the a) alternative
 just reduces to calling unlink() ... ;-)

This hardlink-to-softlink case is IMHO identical to the normal
hardlinks case (in which mv just unlinks the source):

  $ touch f1  ln f1 f2  ls -ogi f1 f2
  6590 -rw-r--r-- 2 0 Feb  1 15:17 f1
  6590 -rw-r--r-- 2 0 Feb  1 15:17 f2
  $ ~/git/coreutils/src/mv f1 f2
  $ ls -ogi f1 f2
  ls: cannot access f1: No such file or directory
  6590 -rw-r--r-- 1 0 Feb  1 15:17 f2

Therefore, I'd tend to a) again. It doesn't matter if src is
a regular file or a symlink. What do you think?

Have a nice day,
Berny





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Eric Blake
On 02/01/2012 05:47 AM, Jim Meyering wrote:
 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.

Actually, POSIX 2008 _did_ standardize the notion of a hard link to a
symlink, thanks to linkat().

 For example, on FreeBSD 9.0, you cannot even create one:

That's a POSIX-compliance bug in FreeBSD.  In that case, the best we can
do is emulate it by creating a new symlink with identical contents and
owner and as much of the timestamp as lutimens will allow.

 
 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.

The wording for rename(2) did not change, but the wording for mv(1)
_did_ change to allow slightly more desirable behavior.  Here's the
latest wording, as modified by the latest bug report:

http://austingroupbugs.net/view.php?id=534

If the source_file operand and destination path resolve to either the
same existing directory entry or different directory entries for the
same existing file, then the destination path shall not be removed, and
one of the following shall occur:
 a. No change is made to source_file, no error occurs, and no diagnostic
is issued.
 b. No change is made to source_file, a diagnostic is issued to standard
error identifying the two names, and the exit status is affected.
 c. 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 mv utility shall do nothing more with the current source_file, and
go on to any remaining source_files.

 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.

POSIX actually specifies this quite well - two hardlinks to the same
symlink qualify as an instance of two file names that resolve to the
same inode after path resolution as checked with lstat().

-- 
Eric Blake   ebl...@redhat.com+1-919-301-3266
Libvirt virtualization library http://libvirt.org



signature.asc
Description: OpenPGP digital signature


bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Jim Meyering
Eric Blake wrote:

 On 02/01/2012 05:47 AM, Jim Meyering wrote:
 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.

 Actually, POSIX 2008 _did_ standardize the notion of a hard link to a
 symlink, thanks to linkat().

 For example, on FreeBSD 9.0, you cannot even create one:

 That's a POSIX-compliance bug in FreeBSD.  In that case, the best we can
 do is emulate it by creating a new symlink with identical contents and
 owner and as much of the timestamp as lutimens will allow.


 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.

 The wording for rename(2) did not change, but the wording for mv(1)
 _did_ change to allow slightly more desirable behavior.  Here's the
 latest wording, as modified by the latest bug report:

 http://austingroupbugs.net/view.php?id=534

 If the source_file operand and destination path resolve to either the
 same existing directory entry or different directory entries for the
 same existing file, then the destination path shall not be removed, and
 one of the following shall occur:
  a. No change is made to source_file, no error occurs, and no diagnostic
 is issued.
  b. No change is made to source_file, a diagnostic is issued to standard
 error identifying the two names, and the exit status is affected.
  c. 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 mv utility shall do nothing more with the current source_file, and
 go on to any remaining source_files.

 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.

 POSIX actually specifies this quite well - two hardlinks to the same
 symlink qualify as an instance of two file names that resolve to the
 same inode after path resolution as checked with lstat().

Thanks for the clarification and quotes, Eric.

Bernhard, here's a better patch.
With it, mv simply unlinks the s in your example:

diff --git a/src/copy.c b/src/copy.c
index 4810de8..73f5cc5 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1229,7 +1229,17 @@ 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);
+{
+  bool sn = same_name (src_name, dst_name);
+  if ( ! sn  same_link)
+{
+  *unlink_src = true;
+  *return_now = true;
+  return true;
+}
+
+  return ! sn;
+}

   src_sb_link = src_sb;
   dst_sb_link = dst_sb;





bug#10686: mv: moving hardlink of a softlink to the softlink does nothing

2012-02-01 Thread Jim Meyering
Bernhard Voelker wrote:

 On 02/01/2012 04:45 PM, Jim Meyering wrote:
 Thanks for the clarification and quotes, Eric.

 Bernhard, here's a better patch.
 With it, mv simply unlinks the s in your example:

 diff --git a/src/copy.c b/src/copy.c
 index 4810de8..73f5cc5 100644
 --- a/src/copy.c
 +++ b/src/copy.c
 @@ -1229,7 +1229,17 @@ 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);
 +{
 +  bool sn = same_name (src_name, dst_name);
 +  if ( ! sn  same_link)
 +{
 +  *unlink_src = true;
 +  *return_now = true;
 +  return true;
 +}
 +
 +  return ! sn;
 +}

src_sb_link = src_sb;
dst_sb_link = dst_sb;

 Thank you both.
 The patch works.

 I wonder if just removing the x-hard_link constraint at the beginning
 of same_file_ok() would alsoo do (although the comment sounds like this
 is no good idea):

 @@ -1213,11 +1213,11 @@ same_file_ok (char const *src_name, struct stat const 
 *src_sb,
/* FIXME: this should (at the very least) be moved into the following
   if-block.  More likely, it should be removed, because it inhibits
   making backups.  But removing it will result in a change in behavior
   that will probably have to be documented -- and tests will have to
   be updated.  */
 -  if (same  x-hard_link)
 +  if (same)

I doubt that would work (it doesn't set *unlink_src) -- though you're
welcome to try it.  Even if it did, I'd prefer not to put a change like
this in code that might be considered for removal.

*return_now = true;
return true;
  }

 Please don't think I just want to give you more work,
 but I think this deserves a new test, doesn't it?

Of course ;-)  That was not a complete patch by a long shot.
It didn't even have a commit log.  You obviously know by now that
any change like this absolutely requires an addition to the tests suite.
And since (thanks to Eric's clarification) it does officially count
as a bug fix, I'll write a NEWS entry, too.

Thanks again for spotting the problem.