* Noteworthy changes in release ?.? (????-??-??) [?]
+** Improvements
+
+ 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.
+ That is, there is no longer a brief moment when B does not exist.
+
** Bug fixes
date again converts from a specified time zone. Previously output was
not converted to the local time zone, and remained in the specified one.
[bug introduced in coreutils-8.26]
+ Commands like 'cp --no-dereference -l A B' are no longer quiet no-ops
+ when A is a regular file and B is a symbolic link that points to A.
+ [bug introduced in fileutils-4.0]
+
factor no longer goes into an infinite loop for certain numbers like
158909489063877810457 and 222087527029934481871.
[bug introduced in coreutils-8.20]
strtod
strtoimax
strtoumax
- symlink
+ symlinkat
sys_ioctl
sys_resource
sys_stat
When copying without this option and an existing destination file cannot
be opened for writing, the copy fails. However, with @option{--force},
when a destination file cannot be opened, @command{cp} then removes it and
-tries to open it again. Contrast this behavior with that enabled by
-@option{--link} and @option{--symbolic-link}, whereby the destination file
-is never opened but rather is removed unconditionally. Also see the
+tries to open it again. When this option is combined with
+@option{--link} (@option{-l}) or @option{--symbolic-link}
+(@option{-s}), the destination link is replaced, and unless
+@option{--backup} (@option{-b}) is also given there is no brief
+moment when the destination does not exist. Also see the
description of @option{--remove-destination}.
This option is independent of the @option{--interactive} or
@end itemize
-Normally @command{ln} does not remove existing files. Use the
-@option{--force} (@option{-f}) option to remove them unconditionally,
-the @option{--interactive} (@option{-i}) option to remove them
+Normally @command{ln} does not replace existing files. Use the
+@option{--force} (@option{-f}) option to replace them unconditionally,
+the @option{--interactive} (@option{-i}) option to replace them
conditionally, and the @option{--backup} (@option{-b}) option to
-rename them.
+rename them. Unless the @option{--backup} (@option{-b}) option is
+used there is no brief moment when the destination does not exist;
+this is an extension to POSIX.
@cindex hard link, defined
@cindex inode, and hard links
#include "file-set.h"
#include "filemode.h"
#include "filenamecat.h"
+#include "force-link.h"
#include "full-write.h"
#include "hash.h"
#include "hash-triple.h"
}
}
- /* It's ok to remove a destination symlink. But that works only when we
- unlink before opening the destination and when the source and destination
- files are on the same partition. */
- if (x->unlink_dest_before_opening
- && S_ISLNK (dst_sb_link->st_mode))
+ /* It's ok to remove a destination symlink. But that works only
+ when creating symbolic links, or when the source and destination
+ are on the same file system and when creating hard links or when
+ unlinking before opening the destination. */
+ if (x->symbolic_link
+ || ((x->hard_link || x->unlink_dest_before_opening)
+ && S_ISLNK (dst_sb_link->st_mode)))
return dst_sb_link->st_dev == src_sb_link->st_dev;
if (x->dereference == DEREF_NEVER)
create_hard_link (char const *src_name, char const *dst_name,
bool replace, bool verbose, bool dereference)
{
- /* We want to guarantee that symlinks are not followed, unless requested. */
- int flags = 0;
- if (dereference)
- flags = AT_SYMLINK_FOLLOW;
-
- bool link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags)
- != 0);
-
- /* If the link failed because of an existing destination,
- remove that file and then call link again. */
- if (link_failed && replace && errno == EEXIST)
- {
- if (unlink (dst_name) != 0)
- {
- error (0, errno, _("cannot remove %s"), quoteaf (dst_name));
- return false;
- }
- if (verbose)
- printf (_("removed %s\n"), quoteaf (dst_name));
- link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags)
- != 0);
- }
-
- if (link_failed)
+ int status = force_linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name,
+ dereference ? AT_SYMLINK_FOLLOW : 0,
+ replace);
+ if (status < 0)
{
error (0, errno, _("cannot create hard link %s to %s"),
quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
return false;
}
-
+ if (0 < status && verbose)
+ printf (_("removed %s\n"), quoteaf (dst_name));
return true;
}
goto un_backup;
}
}
- if (symlink (src_name, dst_name) != 0)
+ if (force_symlinkat (src_name, AT_FDCWD, dst_name,
+ x->unlink_dest_after_failed_open)
+ < 0)
{
error (0, errno, _("cannot create symbolic link %s to %s"),
quoteaf_n (0, dst_name), quoteaf_n (1, src_name));
&& !(! CAN_HARDLINK_SYMLINKS && S_ISLNK (src_mode)
&& x->dereference == DEREF_NEVER))
{
- if (! create_hard_link (src_name, dst_name, false, false, dereference))
+ if (! create_hard_link (src_name, dst_name,
+ x->unlink_dest_after_failed_open,
+ false, dereference))
goto un_backup;
}
else if (S_ISREG (src_mode)
goto un_backup;
}
- if (symlink (src_link_val, dst_name) == 0)
- free (src_link_val);
- else
+ int symlink_r = force_symlinkat (src_link_val, AT_FDCWD, dst_name,
+ x->unlink_dest_after_failed_open);
+ int symlink_err = symlink_r < 0 ? errno : 0;
+ if (symlink_err && x->update && !new_dst && S_ISLNK (dst_sb.st_mode)
+ && dst_sb.st_size == strlen (src_link_val))
{
- int saved_errno = errno;
- bool same_link = false;
- if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode)
- && dst_sb.st_size == strlen (src_link_val))
+ /* See if the destination is already the desired symlink.
+ FIXME: This behavior isn't documented, and seems wrong
+ in some cases, e.g., if the destination symlink has the
+ wrong ownership, permissions, or timestamps. */
+ char *dest_link_val =
+ areadlink_with_size (dst_name, dst_sb.st_size);
+ if (dest_link_val)
{
- /* See if the destination is already the desired symlink.
- FIXME: This behavior isn't documented, and seems wrong
- in some cases, e.g., if the destination symlink has the
- wrong ownership, permissions, or timestamps. */
- char *dest_link_val =
- areadlink_with_size (dst_name, dst_sb.st_size);
- if (dest_link_val && STREQ (dest_link_val, src_link_val))
- same_link = true;
+ if (STREQ (dest_link_val, src_link_val))
+ symlink_err = 0;
free (dest_link_val);
}
- free (src_link_val);
-
- if (! same_link)
- {
- error (0, saved_errno, _("cannot create symbolic link %s"),
- quoteaf (dst_name));
- goto un_backup;
- }
+ }
+ free (src_link_val);
+ if (symlink_err)
+ {
+ error (0, symlink_err, _("cannot create symbolic link %s"),
+ quoteaf (dst_name));
+ goto un_backup;
}
if (x->preserve_security_context)
if (x.recursive)
x.copy_as_regular = copy_contents;
- /* If --force (-f) was specified and we're in link-creation mode,
- first remove any existing destination file. */
- if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link))
- x.unlink_dest_before_opening = true;
-
/* Ensure -Z overrides -a. */
if ((x.set_security_context || scontext)
&& ! x.require_preserve_context)
--- /dev/null
+/* Implement ln -f "atomically"
+
+ Copyright 2017 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Paul Eggert. */
+
+/* A naive "ln -f A B" unlinks B and then links A to B. This module
+ instead links A to a randomly-named temporary T in B's directory,
+ and then renames T to B. This approach has a window with a
+ randomly-named temporary, which is safer for many applications than
+ a window where B does not exist. */
+
+#include <config.h>
+
+#include "force-link.h"
+
+#include <dirname.h>
+#include <tempname.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* A basename pattern suitable for a temporary file. It should work
+ even on file systems like FAT that support only short names.
+ "Cu" is short for "Coreutils" or for "Changeable unstable",
+ take your pick.... */
+
+static char const simple_pattern[] = "CuXXXXXX";
+enum { x_suffix_len = sizeof "XXXXXX" - 1 };
+
+/* A size for smallish buffers containing file names. Longer file
+ names can use malloc. */
+
+enum { smallsize = 256 };
+
+/* Return a template for a file in the same directory as DSTNAME.
+ Use BUF if the template fits, otherwise use malloc and return NULL
+ (setting errno) if unsuccessful. */
+
+static char *
+samedir_template (char const *dstname, char buf[smallsize])
+{
+ ptrdiff_t dstdirlen = last_component (dstname) - dstname;
+ size_t dsttmpsize = dstdirlen + sizeof simple_pattern;
+ char *dsttmp;
+ if (dsttmpsize <= smallsize)
+ dsttmp = buf;
+ else
+ {
+ dsttmp = malloc (dsttmpsize);
+ if (!dsttmp)
+ return dsttmp;
+ }
+ strcpy (mempcpy (dsttmp, dstname, dstdirlen), simple_pattern);
+ return dsttmp;
+}
+
+
+/* Auxiliaries for force_linkat. */
+
+struct link_arg
+{
+ int srcdir;
+ char const *srcname;
+ int dstdir;
+ int flags;
+};
+
+static int
+try_link (char *dest, void *arg)
+{
+ struct link_arg *a = arg;
+ return linkat (a->srcdir, a->srcname, a->dstdir, dest, a->flags);
+}
+
+/* Hard-link directory SRCDIR's file SRCNAME to directory DSTDIR's
+ file DSTNAME, using linkat-style FLAGS to control the linking.
+ If FORCE and DSTNAME already exists, replace it atomically. Return
+ 1 if successful and DSTNAME already existed,
+ 0 if successful and DSTNAME did not already exist, and
+ -1 (setting errno) on failure. */
+int
+force_linkat (int srcdir, char const *srcname,
+ int dstdir, char const *dstname, int flags, bool force)
+{
+ int r = linkat (srcdir, srcname, dstdir, dstname, flags);
+ if (!force || r == 0 || errno != EEXIST)
+ return r;
+
+ char buf[smallsize];
+ char *dsttmp = samedir_template (dstname, buf);
+ if (! dsttmp)
+ return -1;
+ struct link_arg arg = { srcdir, srcname, dstdir, flags };
+ int err;
+
+ if (try_tempname_len (dsttmp, 0, &arg, try_link, x_suffix_len) != 0)
+ err = errno;
+ else
+ {
+ err = renameat (dstdir, dsttmp, dstdir, dstname) == 0 ? 0 : errno;
+ /* Unlink DSTTMP even if renameat succeeded, in case DSTTMP
+ and DSTNAME were already the same hard link and renameat
+ was a no-op. */
+ unlinkat (dstdir, dsttmp, 0);
+ }
+
+ if (dsttmp != buf)
+ free (dsttmp);
+ if (!err)
+ return 1;
+ errno = err;
+ return -1;
+}
+
+
+/* Auxiliaries for force_symlinkat. */
+
+struct symlink_arg
+{
+ char const *srcname;
+ int dstdir;
+};
+
+static int
+try_symlink (char *dest, void *arg)
+{
+ struct symlink_arg *a = arg;
+ return symlinkat (a->srcname, a->dstdir, dest);
+}
+
+/* Create a symlink containing SRCNAME in directory DSTDIR's file DSTNAME.
+ If FORCE and DSTNAME already exists, replace it atomically. Return
+ 1 if successful and DSTNAME already existed,
+ 0 if successful and DSTNAME did not already exist, and
+ -1 (setting errno) on failure. */
+int
+force_symlinkat (char const *srcname, int dstdir, char const *dstname,
+ bool force)
+{
+ int r = symlinkat (srcname, dstdir, dstname);
+ if (!force || r == 0 || errno != EEXIST)
+ return r;
+
+ char buf[smallsize];
+ char *dsttmp = samedir_template (dstname, buf);
+ if (!dsttmp)
+ return -1;
+ struct symlink_arg arg = { srcname, dstdir };
+ int err;
+
+ if (try_tempname_len (dsttmp, 0, &arg, try_symlink, x_suffix_len) != 0)
+ err = errno;
+ else if (renameat (dstdir, dsttmp, dstdir, dstname) != 0)
+ {
+ err = errno;
+ unlinkat (dstdir, dsttmp, 0);
+ }
+ else
+ {
+ /* Don't worry about renameat being a no-op, since DSTTMP is
+ newly created. */
+ err = 0;
+ }
+
+ if (dsttmp != buf)
+ free (dsttmp);
+ if (!err)
+ return 1;
+ errno = err;
+ return -1;
+}
--- /dev/null
+#include <stdbool.h>
+extern int force_linkat (int, char const *, int, char const *, int, bool);
+extern int force_symlinkat (char const *, int, char const *, bool);
#include "error.h"
#include "filenamecat.h"
#include "file-set.h"
+#include "force-link.h"
#include "hash.h"
#include "hash-triple.h"
#include "relpath.h"
char *rel_source = NULL;
bool dest_lstat_ok = false;
bool source_is_dir = false;
- bool ok;
if (!symbolic_link)
{
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
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)
{
src/copy.c \
src/cp-hash.c \
src/extent-scan.c \
- src/extent-scan.h
+ src/extent-scan.h \
+ src/force-link.c \
+ src/force-link.h
# Use 'ginstall' in the definition of PROGRAMS and in dependencies to avoid
# confusion with the 'install' target. The install rule transforms 'ginstall'
src_id_SOURCES = src/id.c src/group-list.c
src_groups_SOURCES = src/groups.c src/group-list.c
src_ls_SOURCES = src/ls.c src/ls-ls.c
-src_ln_SOURCES = src/ln.c src/relpath.c src/relpath.h
+src_ln_SOURCES = src/ln.c \
+ src/force-link.c src/force-link.h \
+ src/relpath.c src/relpath.h
src_chown_SOURCES = src/chown.c src/chown-core.c
src_chgrp_SOURCES = src/chgrp.c src/chown-core.c
src_kill_SOURCES = src/kill.c src/operand2sig.c
0 -bf (foo symlink symlink.~1~ -> foo)
0 -bdf (foo symlink symlink.~1~ -> foo)
1 -l [cp: cannot create hard link 'symlink' to 'foo'] (foo symlink -> foo)
-0 -dl (foo symlink -> foo)
+1 -dl [cp: cannot create hard link 'symlink' to 'foo'] (foo symlink -> foo)
0 -fl (foo symlink)
0 -dfl (foo symlink)
0 -bl (foo symlink symlink.~1~ -> foo)