Package: coreutils;
Reported by: Daan De Meyer <daan.j.demeyer <at> gmail.com>
Date: Wed, 13 Dec 2023 01:41:02 UTC
Severity: normal
Tags: patch
To reply to this bug, email your comments to 67805 AT debbugs.gnu.org.
Toggle the display of automated, internal messages from the tracker.
View this report as an mbox folder, status mbox, maintainer mbox
bug-coreutils <at> gnu.org
:bug#67805
; Package coreutils
.
(Wed, 13 Dec 2023 01:41:02 GMT) Full text and rfc822 format available.Daan De Meyer <daan.j.demeyer <at> gmail.com>
:bug-coreutils <at> gnu.org
.
(Wed, 13 Dec 2023 01:41:02 GMT) Full text and rfc822 format available.Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):
From: Daan De Meyer <daan.j.demeyer <at> gmail.com> To: bug-coreutils <at> gnu.org Cc: Daan De Meyer <daan.j.demeyer <at> gmail.com> Subject: [PATCH] cp: Add --keep-directory-symlink option Date: Tue, 12 Dec 2023 22:11:15 +0100
When recursively copying files into OS trees, it often happens that some subdirectory of the source directory is a symlink in the target directory. Currently, cp will fail in that scenario with the error: "cannot overwrite non-directory %s with directory %s" However, we'd like cp in this scenario to follow the destination directory symlink and copy the files into the symlinked directory instead. Let's support this by adding a new option --keep-directory-symlink that makes cp follow destination directory symlinks. We name the option --keep-directory-symlink to keep consistent with tar which has the same option with the same effect. --- doc/coreutils.texi | 4 ++++ src/copy.c | 3 ++- src/copy.h | 3 +++ src/cp.c | 12 +++++++++++- tests/cp/keep-directory-symlink.sh | 27 +++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100755 tests/cp/keep-directory-symlink.sh diff --git a/doc/coreutils.texi b/doc/coreutils.texi index d445ea228..51c460929 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -10281,6 +10281,10 @@ option is also specified. @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. + @optStripTrailingSlashes @optBackupSuffix diff --git a/src/copy.c b/src/copy.c index f54253e5b..bbadca293 100644 --- a/src/copy.c +++ b/src/copy.c @@ -2311,7 +2311,8 @@ copy_internal (char const *src_name, char const *dst_name, 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); diff --git a/src/copy.h b/src/copy.h index 3809f8d23..834685c51 100644 --- a/src/copy.h +++ b/src/copy.h @@ -256,6 +256,9 @@ struct cp_options /* 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; diff --git a/src/cp.c b/src/cp.c index 04a5cbee3..992d28e1e 100644 --- a/src/cp.c +++ b/src/cp.c @@ -68,7 +68,8 @@ enum 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. */ @@ -141,6 +142,7 @@ static struct option const long_opts[] = {"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}, @@ -230,6 +232,9 @@ Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\ "), stdout); fputs (_("\ -v, --verbose explain what is being done\n\ +"), stdout); + fputs (_("\ + --keep-directory-symlink preserve existing symlinks to directories\n\ "), stdout); fputs (_("\ -x, --one-file-system stay on this file system\n\ @@ -859,6 +864,7 @@ cp_option_init (struct cp_options *x) 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 @@ -1161,6 +1167,10 @@ main (int argc, char **argv) x.verbose = true; break; + case KEEP_DIRECTORY_SYMLINK_OPTION: + x.keep_directory_symlink = true; + break; + case 'x': x.one_file_system = true; break; diff --git a/tests/cp/keep-directory-symlink.sh b/tests/cp/keep-directory-symlink.sh new file mode 100755 index 000000000..6ae116cd8 --- /dev/null +++ b/tests/cp/keep-directory-symlink.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# Test that cp --keep-directory-symlink follows symlinks. + +# Copyright (C) 2006-2023 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 && framework_failure_ +cp -RT --copy-contents --keep-directory-symlink b a || framework_failure_ +ls a/b/e -- 2.43.0
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.