summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>1999-12-05 15:13:21 +0000
committerJim Meyering <jim@meyering.net>1999-12-05 15:13:21 +0000
commitf880990daf70fa2427c5aeece3443c40e1375cbb (patch)
tree54c73d3ea451838a34ec22a761f1a11348795cc3 /src
parent7306126ef0380b9ce1754121241b59632ab2c70b (diff)
downloadcoreutils-f880990daf70fa2427c5aeece3443c40e1375cbb.tar.xz
Revert the previous change.
(copy_internal): In move mode, if the rename attempt fails, then unlink any existing destination file. This makes a cross-device `mv' more consistent with the intra-device behavior. This change is required by POSIX to make a cross-device move act with semantics similar to those of the rename syscall. For example now `mv' can 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.
Diffstat (limited to 'src')
-rw-r--r--src/copy.c59
1 files changed, 28 insertions, 31 deletions
diff --git a/src/copy.c b/src/copy.c
index fda7eaf50..a4f2976de 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -371,7 +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;
+ int rename_errno;
if (move_mode && rename_succeeded)
*rename_succeeded = 0;
@@ -427,7 +427,15 @@ copy_internal (const char *src_path, const char *dst_path,
if (!same
/* If we'll remove DST_PATH first, then this doesn't matter. */
- && ! force
+ && ! 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. That is ok, since
+ we remove any existing destination file before opening it --
+ via `rename' if they're on the same file system,
+ via `unlink(DST_PATH)' otherwise. */
+ && !(move_mode
+ && S_ISLNK (dst_sb.st_mode))
/* If we're making a backup, we'll detect the problem case in
copy_reg because SRC_PATH will no longer exist. Allowing
@@ -447,32 +455,7 @@ copy_internal (const char *src_path, const char *dst_path,
&& stat (src_path, &src2_sb) == 0
&& SAME_INODE (src2_sb, dst2_sb))
{
- /* 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;
- }
+ same = 1;
}
}
#endif
@@ -483,7 +466,7 @@ copy_internal (const char *src_path, const char *dst_path,
return 0;
if (x->backup_type == none
- && (!force || same_name (src_path, dst_path)))
+ && (!x->force || same_name (src_path, dst_path)))
{
error (0, 0, _("`%s' and `%s' are the same file"),
src_path, dst_path);
@@ -505,7 +488,7 @@ copy_internal (const char *src_path, const char *dst_path,
return 0;
}
- if (!S_ISDIR (src_type) && !force && x->interactive)
+ if (!S_ISDIR (src_type) && !x->force && x->interactive)
{
if (euidaccess (dst_path, W_OK) != 0)
{
@@ -573,7 +556,7 @@ copy_internal (const char *src_path, const char *dst_path,
}
new_dst = 1;
}
- else if (force)
+ else if (x->force)
{
if (S_ISDIR (dst_sb.st_mode))
{
@@ -675,6 +658,20 @@ copy_internal (const char *src_path, const char *dst_path,
/* Ignore other types of failure (e.g. EXDEV), since the following
code will try to perform a copy, then remove. */
+
+ /* Save this value of errno to use in case the unlink fails. */
+ rename_errno = errno;
+
+ /* 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_path) && errno != ENOENT)
+ {
+ /* Use the value of errno from the failed rename. */
+ error (0, rename_errno, _("cannot move `%s' to `%s'"),
+ src_path, dst_path);
+ return 1;
+ }
}
if (S_ISDIR (src_type))