diff options
author | Jim Meyering <jim@meyering.net> | 1999-12-05 15:13:21 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 1999-12-05 15:13:21 +0000 |
commit | f880990daf70fa2427c5aeece3443c40e1375cbb (patch) | |
tree | 54c73d3ea451838a34ec22a761f1a11348795cc3 /src | |
parent | 7306126ef0380b9ce1754121241b59632ab2c70b (diff) | |
download | coreutils-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.c | 59 |
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)) |