summaryrefslogtreecommitdiff
path: root/src/copy.c
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2011-03-31 11:28:58 +0100
committerPádraig Brady <P@draigBrady.com>2011-04-01 14:17:27 +0100
commit594292a1d89332dd006cd7fe3116cf46ebe9acf6 (patch)
tree75db5403a519f0e3c501129f1302af352e0827e5 /src/copy.c
parent0ec711b5c4c5094fa206115a666e5878f463a07f (diff)
downloadcoreutils-594292a1d89332dd006cd7fe3116cf46ebe9acf6.tar.xz
copy: link rather than copy symlinks, when --link used
This bug was introduced in commit ca9e212c, 2009-09-24, "cp, mv: use linkat to guarantee semantics", which inadvertently disabled the creation of hardlinks to symlinks. However rather than implementing the intention of that commit and relying on gnulib linkat emulation, we'll revert to the previous emulation as that maintains ownership and timestamps. * src/copy.c (copy_internal): Use our existing hardlink to symlink emulation when link() might dereference the symlink. Also ensure that we copy the timestamps of the original symlink when we use the emulation. * tests/cp/link-symlink: Add a test to ensure timestamps copied. * tests/Makefile.am: Reference the new test. * NEWS: Mention the fix. Reported by Ruediger Meier
Diffstat (limited to 'src/copy.c')
-rw-r--r--src/copy.c27
1 files changed, 20 insertions, 7 deletions
diff --git a/src/copy.c b/src/copy.c
index 104652d06..e839f973f 100644
--- a/src/copy.c
+++ b/src/copy.c
@@ -2169,13 +2169,24 @@ copy_internal (char const *src_name, char const *dst_name,
}
}
- /* cp, invoked with `--link --no-dereference', should not follow the
- link; we guarantee this with gnulib's linkat module (on systems
- where link(2) follows the link, gnulib creates a symlink with
- identical contents, which is good enough for our purposes). */
+ /* POSIX 2008 states that it is implementation-defined whether
+ link() on a symlink creates a hard-link to the symlink, or only
+ to the referent (effectively dereferencing the symlink) (POSIX
+ 2001 required the latter behavior, although many systems provided
+ the former). Yet cp, invoked with `--link --no-dereference',
+ 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
- && (!S_ISLNK (src_mode)
- || x->dereference != DEREF_NEVER))
+ && !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
+ && x->dereference == DEREF_NEVER))
{
if (linkat (AT_FDCWD, src_name, AT_FDCWD, dst_name, 0))
{
@@ -2298,7 +2309,9 @@ 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))
+ if (x->hard_link && ! S_ISDIR (src_mode)
+ && !(LINK_FOLLOWS_SYMLINKS && S_ISLNK (src_mode)
+ && x->dereference == DEREF_NEVER))
return delayed_ok;
if (copied_as_regular)