summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGian Piero Carrubba <gpiero@rm-rf.it>2013-11-07 23:35:52 +0100
committerBernhard Voelker <mail@bernhard-voelker.de>2013-11-07 23:45:22 +0100
commitbf6bf52dce37832b03ecfbe1b3a3b104f532df42 (patch)
treec62463b576c33400b029667a0991367c9da47b7c /src
parent2c6736f92fc7d2f310714473ea84ceff57e01da2 (diff)
downloadcoreutils-bf6bf52dce37832b03ecfbe1b3a3b104f532df42.tar.xz
cp: fix --link regarding the dereferencing of symbolic links
* src/copy.c (create_hard_link): Add a bool 'dereference' parameter, and pass AT_SYMLINK_FOLLOW as 'flags' to linkat() when dereference is true. (should_dereference): Add new 'bool' function to determine if a file should be dereferenced or not. (copy_internal): Use the above new should_dereference() and remember its return value in a new local bool 'dereference' variable. Use that in all three calls to create_hard_link(). * src/cp.c (main): after parsing the options, if x.dereference is still DEFEF_UNDEFINED and the x.recursive is true, then only set x.dereference to DEREF_NEVER iff --link was not specified. * doc/coreutils.texi (cp invocation): Mention that cp(1) does not follow symbolic links in the source when --link is specified. Likewise in the description of the -R option when used together with that option. * tests/cp/same-file.sh: Adapt the expected results for the -fl, the -bl and the -bfl tests. * tests/cp/link-deref.sh: Add a new test. * tests/local.mk (all_tests): Reference the above new test. * NEWS (Changes in behavior): Mention the change. This fixes http://bugs.gnu.org/15173 Co-authored-by: Bernhard Voelker <mail@bernhard-voelker.de>
Diffstat (limited to 'src')
-rw-r--r--src/copy.c46
-rw-r--r--src/cp.c2
2 files changed, 34 insertions, 14 deletions
diff --git a/src/copy.c b/src/copy.c
index f66ab4646..bcae12368 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -1558,18 +1558,23 @@ restore_default_fscreatecon_or_die (void)
_("failed to restore the default file creation context"));
}
-/* Create a hard link DST_NAME to SRC_NAME, honoring the REPLACE and
- VERBOSE settings. Return true upon success. Otherwise, diagnose
- the failure and return false.
- If SRC_NAME is a symbolic link it will not be followed. If the system
- doesn't support hard links to symbolic links, then DST_NAME will
- be created as a symbolic link to SRC_NAME. */
+/* Create a hard link DST_NAME to SRC_NAME, honoring the REPLACE, VERBOSE and
+ DEREFERENCE settings. Return true upon success. Otherwise, diagnose the
+ failure and return false. If SRC_NAME is a symbolic link, then it will not
+ be followed unless DEREFERENCE is true.
+ If the system doesn't support hard links to symbolic links, then DST_NAME
+ will be created as a symbolic link to SRC_NAME. */
static bool
create_hard_link (char const *src_name, char const *dst_name,
- bool replace, bool verbose)
+ bool replace, bool verbose, bool dereference)
{
- /* We want to guarantee that symlinks are not followed. */
- bool link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, 0) != 0);
+ /* 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. */
@@ -1582,7 +1587,8 @@ create_hard_link (char const *src_name, char const *dst_name,
}
if (verbose)
printf (_("removed %s\n"), quote (dst_name));
- link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, 0) != 0);
+ link_failed = (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, flags)
+ != 0);
}
if (link_failed)
@@ -1595,6 +1601,17 @@ create_hard_link (char const *src_name, char const *dst_name,
return true;
}
+/* Return true if the current file should be (tried to be) dereferenced:
+ either for DEREF_ALWAYS or for DEREF_COMMAND_LINE_ARGUMENTS in the case
+ where the current file is a COMMAND_LINE_ARG; otherwise return false. */
+static inline bool _GL_ATTRIBUTE_PURE
+should_dereference (const struct cp_options *x, bool command_line_arg)
+{
+ return x->dereference == DEREF_ALWAYS
+ || (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS
+ && command_line_arg);
+}
+
/* Copy the file SRC_NAME to the file DST_NAME. The files may be of
any type. NEW_DST should be true if the file DST_NAME cannot
exist because its parent directory was just created; NEW_DST should
@@ -1670,6 +1687,8 @@ copy_internal (char const *src_name, char const *dst_name,
record_file (x->src_info, src_name, &src_sb);
}
+ bool dereference = should_dereference (x, command_line_arg);
+
if (!new_dst)
{
/* Regular files can be created by writing through symbolic
@@ -1748,7 +1767,7 @@ copy_internal (char const *src_name, char const *dst_name,
/* Note we currently replace DST_NAME unconditionally,
even if it was a newer separate file. */
if (! create_hard_link (earlier_file, dst_name, true,
- x->verbose))
+ x->verbose, dereference))
{
goto un_backup;
}
@@ -2078,7 +2097,8 @@ copy_internal (char const *src_name, char const *dst_name,
}
else
{
- if (! create_hard_link (earlier_file, dst_name, true, x->verbose))
+ if (! create_hard_link (earlier_file, dst_name, true, x->verbose,
+ dereference))
goto un_backup;
return true;
@@ -2389,7 +2409,7 @@ copy_internal (char const *src_name, char const *dst_name,
&& !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
&& x->dereference == DEREF_NEVER))
{
- if (! create_hard_link (src_name, dst_name, false, false))
+ if (! create_hard_link (src_name, dst_name, false, false, dereference))
goto un_backup;
}
else if (S_ISREG (src_mode)
diff --git a/src/cp.c b/src/cp.c
index 7bc8630fb..78c0a0402 100644
--- a/src/cp.c
+++ b/src/cp.c
@@ -1135,7 +1135,7 @@ main (int argc, char **argv)
if (x.dereference == DEREF_UNDEFINED)
{
- if (x.recursive)
+ if (x.recursive && ! x.hard_link)
/* This is compatible with FreeBSD. */
x.dereference = DEREF_NEVER;
else