summaryrefslogtreecommitdiff
path: root/src/copy.c
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2003-04-04 21:53:27 +0000
committerJim Meyering <jim@meyering.net>2003-04-04 21:53:27 +0000
commitfc6073d6338693d2d5f6aec51c67c8bb0282832c (patch)
treec02bf9a8a60018593ab679a1ca38da83a88feb34 /src/copy.c
parente46be69e868f5bb6382289e802b749cd9804c6db (diff)
downloadcoreutils-fc6073d6338693d2d5f6aec51c67c8bb0282832c.tar.xz
Because of inappropriate (but POSIX-mandated) behavior of rename,
`mv a b' would not remove `a' in some unusual cases. Work around this by unlinking `a' when necessary. (same_file_ok): Add an output parameter. Set it in the offending case. (copy_internal): When necessary, unlink SRC_PATH and inform caller.
Diffstat (limited to 'src/copy.c')
-rw-r--r--src/copy.c49
1 files changed, 42 insertions, 7 deletions
diff --git a/src/copy.c b/src/copy.c
index 91ae106f5..1f1403e1f 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -424,12 +424,22 @@ close_src_desc:
making the `copy' operation remove both copies of the file
in that case, while still allowing the user to e.g., move or
copy a regular file onto a symlink that points to it.
- Try to minimize the cost of this function in the common case. */
+ Try to minimize the cost of this function in the common case.
+ Set *RETURN_NOW if we've determined that the caller has no more
+ work to do and should return successfully, right away.
+
+ Set *UNLINK_SRC if we've determined that the caller wants to do
+ `rename (a, b)' where `a' and `b' are hard links to the same file.
+ In that case, the caller should try to unlink `a' and then return
+ successfully. Ideally, we wouldn't have to do that, and we'd be
+ able to rely on rename to remove the source file. However, POSIX
+ mistakenly requires that such a rename call do *nothing* and return
+ successfully. */
static int
same_file_ok (const char *src_path, const struct stat *src_sb,
const char *dst_path, const struct stat *dst_sb,
- const struct cp_options *x, int *return_now)
+ const struct cp_options *x, int *return_now, int *unlink_src)
{
const struct stat *src_sb_link;
const struct stat *dst_sb_link;
@@ -440,6 +450,7 @@ same_file_ok (const char *src_path, const struct stat *src_sb,
int same = (SAME_INODE (*src_sb, *dst_sb));
*return_now = 0;
+ *unlink_src = 0;
/* FIXME: this should (at the very least) be moved into the following
if-block. More likely, it should be removed, because it inhibits
@@ -548,10 +559,21 @@ same_file_ok (const char *src_path, const struct stat *src_sb,
destination file before opening it -- via `rename' if they're on
the same file system, via `unlink (DST_PATH)' otherwise.
It's also ok if they're distinct hard links to the same file. */
- if ((x->move_mode || x->unlink_dest_before_opening)
- && (S_ISLNK (dst_sb_link->st_mode)
- || (same_link && !same_name (src_path, dst_path))))
- return 1;
+ if (x->move_mode || x->unlink_dest_before_opening)
+ {
+ if (S_ISLNK (dst_sb_link->st_mode))
+ return 1;
+
+ if (same_link && !same_name (src_path, dst_path))
+ {
+ if (x->move_mode)
+ {
+ *unlink_src = 1;
+ *return_now = 1;
+ }
+ return 1;
+ }
+ }
/* If neither is a symlink, then it's ok as long as they aren't
hard links to the same file. */
@@ -856,8 +878,21 @@ copy_internal (const char *src_path, const char *dst_path,
else
{
int return_now;
+ int unlink_src;
int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb,
- x, &return_now);
+ x, &return_now, &unlink_src);
+ if (unlink_src)
+ {
+ if (unlink (src_path))
+ {
+ error (0, errno, _("cannot remove %s"), quote (src_path));
+ return 1;
+ }
+ /* Tell the caller that there's no need to remove src_path. */
+ if (rename_succeeded)
+ *rename_succeeded = 1;
+ }
+
if (return_now)
return 0;