chgrp now accepts the --from=OWNER:GROUP option to restrict changes to files
with matching current OWNER and/or GROUP, as already supported by chown(1).
+ cp now accepts the --keep-directory-symlink option (like tar), to preserve
+ and follow exisiting symlinks to directories in the destination.
+
od now supports printing IEEE half precision floating point with -t fH,
or brain 16 bit floating point with -t fB, where supported by the compiler.
@opindex --verbose
Print the name of each file before moving it.
+@item --keep-directory-symlink
+@opindex --keep-directory-symlink
+Follow existing symlinks to directories when copying. Note that this option
+should only be used when the contents of the destination directory are trusted
+as when this option is enabled, an attacker can place symlinks in the
+destination directory to make @command{cp} write to arbitrary directories in the
+system.
+
@optStripTrailingSlashes
@optBackupSuffix
bool use_lstat
= ((! S_ISREG (src_mode)
&& (! x->copy_as_regular
- || S_ISDIR (src_mode) || S_ISLNK (src_mode)))
+ || (S_ISDIR (src_mode) && !x->keep_directory_symlink)
+ || S_ISLNK (src_mode)))
|| x->move_mode || x->symbolic_link || x->hard_link
|| x->backup_type != no_backups
|| x->unlink_dest_before_opening);
/* If true, display the names of the files before copying them. */
bool verbose;
+ /* If true, follow existing symlinks to directories when copying. */
+ bool keep_directory_symlink;
+
/* If true, display details of how files were copied. */
bool debug;
REFLINK_OPTION,
SPARSE_OPTION,
STRIP_TRAILING_SLASHES_OPTION,
- UNLINK_DEST_BEFORE_OPENING
+ UNLINK_DEST_BEFORE_OPENING,
+ KEEP_DIRECTORY_SYMLINK_OPTION
};
/* True if the kernel is SELinux enabled. */
{"target-directory", required_argument, nullptr, 't'},
{"update", optional_argument, nullptr, 'u'},
{"verbose", no_argument, nullptr, 'v'},
+ {"keep-directory-symlink", no_argument, nullptr,
+ KEEP_DIRECTORY_SYMLINK_OPTION},
{GETOPT_SELINUX_CONTEXT_OPTION_DECL},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
"), stdout);
fputs (_("\
-v, --verbose explain what is being done\n\
+"), stdout);
+ fputs (_("\
+ --keep-directory-symlink follow existing symlinks to directories\n\
"), stdout);
fputs (_("\
-x, --one-file-system stay on this file system\n\
x->update = false;
x->verbose = false;
+ x->keep_directory_symlink = false;
/* By default, refuse to open a dangling destination symlink, because
in general one cannot do that safely, give the current semantics of
x.verbose = true;
break;
+ case KEEP_DIRECTORY_SYMLINK_OPTION:
+ x.keep_directory_symlink = true;
+ break;
+
case 'x':
x.one_file_system = true;
break;
--- /dev/null
+#!/bin/sh
+# Test that cp --keep-directory-symlink follows symlinks.
+
+# Copyright (C) 2024 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cp
+
+mkdir -p a/b b/d/e || framework_failure_
+ln -s b a/d || framework_failure_
+
+cp -RT --copy-contents b a || fail=1
+cp -RT --copy-contents --keep-directory-symlink b a || fail=1
+ls a/b/e || fail=1
tests/cp/existing-perm-dir.sh \
tests/cp/existing-perm-race.sh \
tests/cp/fail-perm.sh \
+ tests/cp/keep-directory-symlink.sh \
tests/cp/sparse-extents.sh \
tests/cp/copy-FMR.sh \
tests/cp/sparse-perf.sh \