bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#14763: mv directory cross-filesystem where destination exists fails


From: Ken Booth
Subject: bug#14763: mv directory cross-filesystem where destination exists fails to remove dest with EISDIR
Date: Tue, 02 Jul 2013 01:29:44 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130514 Thunderbird/17.0.6

Hi Eric,

Thanks for the reference to the POSIX standards.

The reason I wrote the patch the way I did was to emulate Solaris behaviour for a non-empty directory, however I read at
http://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html
the sentence "If /new/ names an existing directory, it shall be required to be an empty directory. "

Therefore I conclude that Solaris is not POSIX compliant.

After reading the instructions you suggested I hope the following patch is in the correct format, conforms to the requirements, and is also POSIX compliant ...

From 8b6549f321c06ee81262f58c6d7bd7e9c9092b30 Mon Sep 17 00:00:00 2001
From: Ken Booth <address@hidden>
Date: Tue, 2 Jul 2013 01:06:32 +0100
Subject: [PATCH] mv: if inter-device move target is directory use rmdir, not
 unlink

---
 src/copy.c | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/src/copy.c b/src/copy.c
index c1c8273..5137f27 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -2173,13 +2173,27 @@ copy_internal (char const *src_name, char const *dst_name,
       /* The rename attempt has failed.  Remove any existing destination
          file so that a cross-device 'mv' acts as if it were really using
          the rename syscall.  */
-      if (unlink (dst_name) != 0 && errno != ENOENT)
+      if (S_ISDIR (src_mode))
         {
-          error (0, errno,
- _("inter-device move failed: %s to %s; unable to remove target"),
-                 quote_n (0, src_name), quote_n (1, dst_name));
-          forget_created (src_sb.st_ino, src_sb.st_dev);
-          return false;
+          if (rmdir (dst_name) != 0 && errno != ENOENT)
+            {
+              error (0, errno,
+ _("inter-device move failed: %s to %s; unable to remove target directory"),
+                     quote_n (0, src_name), quote_n (1, dst_name));
+              forget_created (src_sb.st_ino, src_sb.st_dev);
+              return false;
+            }
+        }
+      else
+        {
+          if (unlink (dst_name) != 0 && errno != ENOENT)
+            {
+              error (0, errno,
+ _("inter-device move failed: %s to %s; unable to remove target"),
+                     quote_n (0, src_name), quote_n (1, dst_name));
+              forget_created (src_sb.st_ino, src_sb.st_dev);
+              return false;
+            }
         }

       new_dst = true;
--
1.8.1.4

I've tested it works with an empty directory, and produces the following error with a non-empty directory

/home/ken/git/fedora/coreutils/src/mv: inter-device move failed: ‘/home/test’ to ‘/test’; unable to remove target directory: Directory not empty

I'm not sure of the etiquette here, but should I push the change, or leave that to a maintainer?

Regards,
Ken.

On 01/07/13 23:41, Eric Blake wrote:
On 07/01/2013 04:34 PM, Eric Blake wrote:
If the destination path exists, mv shall attempt to remove it. If this fails 
for any reason, mv shall write a diagnostic message to standard error, do 
nothing more with the current source_file, and go on to any remaining 
source_files.
Aha - we are attempting to remove it with unlink().  But for empty
directories, we should be using rmdir(), or even better, we should be
using remove() (which subsumes both unlink() and rmdir() at once).  I
wonder if a simpler patch would be to just s/unlink/rmdir/ in the line
of code where you were adding special-casing on errno values.
Then again, we DON'T want to replace a non-directory with a directory
(or vice-versa, we don't want to replace an empty directory with a
non-directory); so maybe it pays to be more careful about explicitly
using unlink() vs. rmdir() on the destination (and not the shortcut of
remove() which does not care about type), all based on what file type we
already know that the source is, so that we can give the same sorts of
failures as rename() would give on a local file system when attempting a
cross-file-type rename.







reply via email to

[Prev in Thread] Current Thread [Next in Thread]