summaryrefslogtreecommitdiff
path: root/src/ln.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2017-02-11 23:12:31 -0800
committerPaul Eggert <eggert@cs.ucla.edu>2017-02-11 23:14:02 -0800
commit376967889ed7ed561e46ff6d88a66779db62737a (patch)
tree7ca32f07b738fc2f9902c1643bb01dedb7d1066b /src/ln.c
parent2f69dba5df8caaf9eda658c1808b1379e9949f22 (diff)
downloadcoreutils-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.c31
1 files changed, 8 insertions, 23 deletions
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)
{