** Changes in behavior
+ cp --link now dereferences a symbolic link as source before creating the
+ hard link in the destination unless the -P,--no-deref option is specified.
+ Previously, it would create a hard link of the symbolic link, even when
+ the dereferencing options -L or -H were specified.
+
dd status=none now suppresses all non fatal diagnostic messages,
not just the transfer counts.
to corresponding destination directories.
When copying from a symbolic link, @command{cp} normally follows the
-link only when not copying
-recursively. This default can be overridden with the
+link only when not copying recursively or when @option{--link}
+(@option{-l}) is used. This default can be overridden with the
@option{--archive} (@option{-a}), @option{-d}, @option{--dereference}
(@option{-L}), @option{--no-dereference} (@option{-P}), and
@option{-H} options. If more than one of these options is specified,
@cindex recursively copying directories
@cindex non-directories, copying as special files
Copy directories recursively. By default, do not follow symbolic
-links in the source; see the @option{--archive} (@option{-a}), @option{-d},
+links in the source unless used together with the @option{--link}
+(@option{-l}) option; see the @option{--archive} (@option{-a}), @option{-d},
@option{--dereference} (@option{-L}), @option{--no-dereference}
(@option{-P}), and @option{-H} options. Special files are copied by
creating a destination file of the same type as the source; see the
_("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. */
}
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)
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
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
/* 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;
}
}
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;
&& !(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)
if (x.dereference == DEREF_UNDEFINED)
{
- if (x.recursive)
+ if (x.recursive && ! x.hard_link)
/* This is compatible with FreeBSD. */
x.dereference = DEREF_NEVER;
else
--- /dev/null
+#!/bin/sh
+# Exercise cp --link's behavior regarding the dereferencing of symbolic links.
+
+# Copyright (C) 2013 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/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir dir || framework_failure_
+: > file || framework_failure_
+ln -s dir dirlink || framework_failure_
+ln -s file filelink || framework_failure_
+ln -s nowhere danglink || framework_failure_
+
+# printf format of the output line.
+outformat='%s|result=%s|inode=%s|type=%s|error=%s\n'
+
+for src in dirlink filelink danglink; do
+ # Get symlink's target.
+ tgt=$(readlink $src) || framework_failure_
+ # Get inodes and file type of the symlink (src) and its target (tgt).
+ # Note: this will fail for 'danglink'; catch it.
+ ino_src="$(stat -c '%i' $src)" || framework_failure_
+ typ_src="$(stat -c '%F' $src)" || framework_failure_
+ ino_tgt="$(stat -c '%i' $tgt 2>/dev/null)" || ino_tgt=
+ typ_tgt="$(stat -c '%F' $tgt 2>/dev/null)" || typ_tgt=
+
+ for o in '' -L -H -P; do
+ for r in '' -R; do
+
+ command="cp --link $o $r $src dst"
+ $command 2> err
+ result=$?
+
+ # Get inode and file type of the destination (which may fail, too).
+ ino_dst="$(stat -c '%i' dst 2>/dev/null)" || ini_dst=
+ typ_dst="$(stat -c '%F' dst 2>/dev/null)" || typ_dst=
+
+ # Print the actual result in a certain format.
+ printf "$outformat" \
+ "$command" \
+ "$result" \
+ "$ino_dst" \
+ "$typ_dst" \
+ "$(< err)" \
+ > out
+
+ # What was expected?
+ if [ "$o" = "-P" ]; then
+ # cp --link should not dereference if -P is given.
+ exp_result=0
+ exp_inode=$ino_src
+ exp_ftype=$typ_src
+ exp_error=
+ elif [ "$src" = 'danglink' ]; then
+ # Dereferencing should fail for the 'danglink'.
+ exp_result=1
+ exp_inode=
+ exp_ftype=
+ exp_error="cp: cannot stat 'danglink': No such file or directory"
+ elif [ "$src" = 'dirlink' ] && [ "$r" != '-R' ]; then
+ # Dereferencing should fail for the 'dirlink' without -R.
+ exp_result=1
+ exp_inode=
+ exp_ftype=
+ exp_error="cp: omitting directory 'dirlink'"
+ elif [ "$src" = 'dirlink' ]; then
+ # cp --link -R 'dirlink' should create a new directory.
+ exp_result=0
+ exp_inode=$ino_dst
+ exp_ftype=$typ_dst
+ exp_error=
+ else
+ # cp --link 'filelink' should create a hard link to the target.
+ exp_result=0
+ exp_inode=$ino_tgt
+ exp_ftype=$typ_tgt
+ exp_error=
+ fi
+
+ # Print the expected result in a certain format.
+ printf "$outformat" \
+ "$command" \
+ "$exp_result" \
+ "$exp_inode" \
+ "$exp_ftype" \
+ "$exp_error" \
+ > exp
+
+ compare exp out || { ls -lid $src $tgt dst; fail=1; }
+
+ rm -rf dst err exp out || framework_failure_
+ done
+ done
+done
+
+Exit $fail
0 -bf (foo sl1 -> foo sl2 sl2.~1~ -> foo)
0 -bdf (foo sl1 -> foo sl2 -> foo sl2.~1~ -> foo)
1 -l [cp: cannot create hard link 'sl2' to 'sl1'] (foo sl1 -> foo sl2 -> foo)
-0 -fl (foo sl1 -> foo sl2 -> foo)
-0 -bl (foo sl1 -> foo sl2 -> foo sl2.~1~ -> foo)
-0 -bfl (foo sl1 -> foo sl2 -> foo sl2.~1~ -> foo)
+0 -fl (foo sl1 -> foo sl2)
+0 -bl (foo sl1 -> foo sl2 sl2.~1~ -> foo)
+0 -bfl (foo sl1 -> foo sl2 sl2.~1~ -> foo)
1 [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
1 -d [cp: 'foo' and 'hardlink' are the same file] (foo hardlink)
tests/cp/file-perm-race.sh \
tests/cp/into-self.sh \
tests/cp/link.sh \
+ tests/cp/link-deref.sh \
tests/cp/link-no-deref.sh \
tests/cp/link-preserve.sh \
tests/cp/link-symlink.sh \