From fc6073d6338693d2d5f6aec51c67c8bb0282832c Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 4 Apr 2003 21:53:27 +0000 Subject: 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. --- src/copy.c | 49 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) (limited to 'src/copy.c') 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; -- cgit v1.2.3-54-g00ecf