summaryrefslogtreecommitdiff
path: root/src/copy.c
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2013-12-12 18:31:48 +0000
committerPádraig Brady <P@draigBrady.com>2014-02-10 10:02:34 +0000
commit9654b67a5443f046dd1e5fe655dc463463f6f9e3 (patch)
tree1568967d54c11fdf6620f9ed60801bb774d953b2 /src/copy.c
parent6e824a66194528696ba265d6111a6bddce4a8ff8 (diff)
downloadcoreutils-9654b67a5443f046dd1e5fe655dc463463f6f9e3.tar.xz
cp: with --link always use linkat() if available
* src/copy.c (copy_reg): If linkat() is available it doesn't matter about the gnulib emulation provided, and thus the LINK_FOLLOWS_SYMLINKS should not have significance here. This was noticed on FreeBSD and the consequence is that cp --link will create hardlinks to symlinks there, rather than emulating with symlinks to symlinks. * tests/cp/link-deref.sh: Adjust the checks to cater for all cases where hardlinks to symlinks are supported.
Diffstat (limited to 'src/copy.c')
-rw-r--r--src/copy.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/src/copy.c b/src/copy.c
index 3e4cbff7f..0b7c59ea9 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -98,6 +98,14 @@ rpl_mkfifo (char const *file, mode_t mode)
#define SAME_GROUP(A, B) ((A).st_gid == (B).st_gid)
#define SAME_OWNER_AND_GROUP(A, B) (SAME_OWNER (A, B) && SAME_GROUP (A, B))
+/* LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know
+ how link() behaves, so assume we can't hardlink symlinks in that case. */
+#if defined HAVE_LINKAT || ! LINK_FOLLOWS_SYMLINKS
+# define CAN_HARDLINK_SYMLINKS 1
+#else
+# define CAN_HARDLINK_SYMLINKS 0
+#endif
+
struct dir_list
{
struct dir_list *parent;
@@ -2484,15 +2492,13 @@ copy_internal (char const *src_name, char const *dst_name,
should not follow the link. We can approximate the desired
behavior by skipping this hard-link creating block and instead
copying the symlink, via the 'S_ISLNK'- copying code below.
- LINK_FOLLOWS_SYMLINKS is tri-state; if it is -1, we don't know
- how link() behaves, so we use the fallback case for safety.
Note gnulib's linkat module, guarantees that the symlink is not
dereferenced. However its emulation currently doesn't maintain
timestamps or ownership so we only call it when we know the
emulation will not be needed. */
else if (x->hard_link
- && !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
+ && !(! CAN_HARDLINK_SYMLINKS && S_ISLNK (src_mode)
&& x->dereference == DEREF_NEVER))
{
if (! create_hard_link (src_name, dst_name, false, false, dereference))
@@ -2632,7 +2638,7 @@ copy_internal (char const *src_name, char const *dst_name,
/* If we've just created a hard-link due to cp's --link option,
we're done. */
if (x->hard_link && ! S_ISDIR (src_mode)
- && !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
+ && !(! CAN_HARDLINK_SYMLINKS && S_ISLNK (src_mode)
&& x->dereference == DEREF_NEVER))
return delayed_ok;