From 376967889ed7ed561e46ff6d88a66779db62737a Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 11 Feb 2017 23:12:31 -0800 Subject: ln: replace destination links more atomically If the file B already exists, commands like 'ln -f A B' and 'cp -fl A B' no longer remove B before creating the new link. Instead, they arrange for the new link to replace B atomically. This should fix a race condition reported by Mike Crowe (Bug#25680). * NEWS, doc/coreutils.texi (cp invocation, ln invocation): Document this. * bootstrap.conf (gnulib_modules): Add symlinkat. * src/copy.c, src/ln.c: Include force-link.h. * src/copy.c (same_file_ok): It's also OK to remove a destination symlink when creating symbolic links, or when the source and destination are on the same file system and when creating hard links. * src/copy.c (create_hard_link, copy_internal): * src/ln.c (do_link): Rewrite using force_linkat and force_symlinkat, to close a window where the destination temporarily does not exist. * src/cp.c (main): Do not set x.unlink_dest_before_opening merely because we are in link-creation mode. * src/force-link.c, src/force-link.h: New files. * src/local.mk (copy_sources, src_ln_SOURCES): Add them. * tests/cp/same-file.sh: Adjust test case to match fixed behavior. --- src/ln.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) (limited to 'src/ln.c') diff --git a/src/ln.c b/src/ln.c index bacf5f8d8..539d238c8 100644 --- a/src/ln.c +++ b/src/ln.c @@ -27,6 +27,7 @@ #include "error.h" #include "filenamecat.h" #include "file-set.h" +#include "force-link.h" #include "hash.h" #include "hash-triple.h" #include "relpath.h" @@ -183,7 +184,6 @@ do_link (const char *source, const char *dest) char *rel_source = NULL; bool dest_lstat_ok = false; bool source_is_dir = false; - bool ok; if (!symbolic_link) { @@ -301,12 +301,7 @@ do_link (const char *source, const char *dest) if (relative) source = rel_source = convert_abs_rel (source, dest); - ok = ((symbolic_link ? symlink (source, dest) - : linkat (AT_FDCWD, source, AT_FDCWD, dest, - logical ? AT_SYMLINK_FOLLOW : 0)) - == 0); - - /* If the attempt to create a link failed and we are removing or + /* If the attempt to create a link fails and we are removing or backing up destinations, unlink the destination and try again. On the surface, POSIX describes an algorithm that states that @@ -324,22 +319,12 @@ do_link (const char *source, const char *dest) that refer to the same file), rename succeeds and DEST remains. If we didn't remove DEST in that case, the subsequent symlink or link call would fail. */ - - if (!ok && errno == EEXIST && (remove_existing_files || dest_backup)) - { - if (unlink (dest) != 0) - { - error (0, errno, _("cannot remove %s"), quoteaf (dest)); - free (dest_backup); - free (rel_source); - return false; - } - - ok = ((symbolic_link ? symlink (source, dest) - : linkat (AT_FDCWD, source, AT_FDCWD, dest, - logical ? AT_SYMLINK_FOLLOW : 0)) - == 0); - } + bool ok_to_remove = remove_existing_files || dest_backup; + bool ok = 0 <= (symbolic_link + ? force_symlinkat (source, AT_FDCWD, dest, ok_to_remove) + : force_linkat (AT_FDCWD, source, AT_FDCWD, dest, + logical ? AT_SYMLINK_FOLLOW : 0, + ok_to_remove)); if (ok) { -- cgit v1.2.3-54-g00ecf