diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2017-02-11 23:12:31 -0800 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2017-02-11 23:14:02 -0800 |
commit | 376967889ed7ed561e46ff6d88a66779db62737a (patch) | |
tree | 7ca32f07b738fc2f9902c1643bb01dedb7d1066b /src/ln.c | |
parent | 2f69dba5df8caaf9eda658c1808b1379e9949f22 (diff) | |
download | coreutils-376967889ed7ed561e46ff6d88a66779db62737a.tar.xz |
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.
Diffstat (limited to 'src/ln.c')
-rw-r--r-- | src/ln.c | 31 |
1 files changed, 8 insertions, 23 deletions
@@ -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) { |