(copy_internal): Be consistent with intra-device behavior
authorJim Meyering <jim@meyering.net>
Sat, 4 Dec 1999 17:21:47 +0000 (17:21 +0000)
committerJim Meyering <jim@meyering.net>
Sat, 4 Dec 1999 17:21:47 +0000 (17:21 +0000)
and *do* allow `mv' to move a file onto a symlink to itself when that
symlink is on a separate partition.  With fileutils-4.0j, it would
fail with a diagnostic saying they were the same file.
Reported by Bruno Haible.

src/copy.c

index 9d8c0c0..fda7eaf 100644 (file)
@@ -371,6 +371,7 @@ copy_internal (const char *src_path, const char *dst_path,
   char *earlier_file;
   char *dst_backup = NULL;
   int fix_mode = 0;
+  int force = x->force;
 
   if (move_mode && rename_succeeded)
     *rename_succeeded = 0;
@@ -426,14 +427,7 @@ copy_internal (const char *src_path, const char *dst_path,
          if (!same
 
              /* If we'll remove DST_PATH first, then this doesn't matter.  */
-             && ! x->force
-
-             /* Allow them to be the same (and don't set `same') if
-                we're in move mode and the target is a symlink
-                on the same partition.  */
-             && !(move_mode
-                  && S_ISLNK (dst_sb.st_mode)
-                  && src_sb.st_dev == dst_sb.st_dev)
+             && ! force
 
              /* If we're making a backup, we'll detect the problem case in
                 copy_reg because SRC_PATH will no longer exist.  Allowing
@@ -453,7 +447,32 @@ copy_internal (const char *src_path, const char *dst_path,
                  && stat (src_path, &src2_sb) == 0
                  && SAME_INODE (src2_sb, dst2_sb))
                {
-                 same = 1;
+                 /* Be careful in move mode when the target is a symlink
+                    to the source.    */
+                 if (move_mode
+                     && S_ISLNK (dst_sb.st_mode))
+                   {
+                     if (src_sb.st_dev != dst_sb.st_dev)
+                       {
+                         /* This happens when the target is a symlink that
+                            resides on a file system different from the one
+                            on which the source resides.  Tell the copying
+                            code (below) that it must unlink the destination
+                            before opening it.  Otherwise, we'd end up
+                            destroying SRC when opening it via the symlink.  */
+                         force = 1;
+                       }
+                     else
+                       {
+                         /* Don't set `same'.
+                            Since they're on the same partition, rename
+                            will end up removing the destination symlink.  */
+                       }
+                   }
+                 else
+                   {
+                     same = 1;
+                   }
                }
            }
 #endif
@@ -464,7 +483,7 @@ copy_internal (const char *src_path, const char *dst_path,
                return 0;
 
              if (x->backup_type == none
-                 && (!x->force || same_name (src_path, dst_path)))
+                 && (!force || same_name (src_path, dst_path)))
                {
                  error (0, 0, _("`%s' and `%s' are the same file"),
                         src_path, dst_path);
@@ -486,7 +505,7 @@ copy_internal (const char *src_path, const char *dst_path,
                return 0;
            }
 
-         if (!S_ISDIR (src_type) && !x->force && x->interactive)
+         if (!S_ISDIR (src_type) && !force && x->interactive)
            {
              if (euidaccess (dst_path, W_OK) != 0)
                {
@@ -554,7 +573,7 @@ copy_internal (const char *src_path, const char *dst_path,
                }
              new_dst = 1;
            }
-         else if (x->force)
+         else if (force)
            {
              if (S_ISDIR (dst_sb.st_mode))
                {