git: fe836c50120d - main - cp: Fix copying the root directory
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 22 Nov 2025 12:12:13 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=fe836c50120daed3e4084f43c27d8d650d36dee8
commit fe836c50120daed3e4084f43c27d8d650d36dee8
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-11-22 12:11:59 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-11-22 12:11:59 +0000
cp: Fix copying the root directory
When the source of the copy operation is the root directory, we should
neither append it to the destination path on FTS_D nor trim it back off
on FTS_DP.
PR: 291132
MFC after: 3 days
Fixes: 82fc0d09e862 ("cp: Partly restore symlink folllowing.")
Reviewed by: markj
Differential Revision: https://reviews.freebsd.org/D53863
---
bin/cp/cp.c | 7 ++++++-
bin/cp/tests/cp_test.sh | 15 +++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/bin/cp/cp.c b/bin/cp/cp.c
index 38fe65399d06..7ac1e5f6a4c4 100644
--- a/bin/cp/cp.c
+++ b/bin/cp/cp.c
@@ -433,6 +433,8 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
sep = strchr(to.base, '\0');
sep[0] = '/';
sep[1] = '\0';
+ } else if (strcmp(curr->fts_name, "/") == 0) {
+ /* special case when source is the root directory */
} else {
/* entering a directory; append its name to to.path */
len = snprintf(to.end, END(to.path) - to.end, "%s%s",
@@ -520,6 +522,8 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
if (type == DIR_TO_DNE &&
curr->fts_level == FTS_ROOTLEVEL) {
/* this is actually our created root */
+ } else if (strcmp(curr->fts_name, "/") == 0) {
+ /* special case when source is the root directory */
} else {
while (to.end > to.path && *to.end != '/')
to.end--;
@@ -551,7 +555,8 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
/* Not an error but need to remember it happened. */
if (to.path[0] == '\0') {
/*
- * This can happen in two cases:
+ * This can happen in three cases:
+ * - The source path is the root directory.
* - DIR_TO_DNE; we created the directory and
* populated root_stat earlier.
* - FILE_TO_DIR if a source has a trailing slash;
diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh
index b637f862b7d3..af309ca7ea80 100755
--- a/bin/cp/tests/cp_test.sh
+++ b/bin/cp/tests/cp_test.sh
@@ -747,9 +747,23 @@ dstmode_body()
atf_check cmp dir/file dst/file
}
+atf_test_case root
+root_head()
+{
+ atf_set "descr" "Test copying the root directory"
+}
+root_body()
+{
+ atf_check mkdir dst
+ atf_check -s exit:1 \
+ -e inline:"cp: / is a directory (not copied).\n" \
+ cp / dst
+}
+
atf_test_case to_root cleanup
to_root_head()
{
+ atf_set "descr" "Test copying to the root directory"
atf_set "require.user" "unprivileged"
}
to_root_body()
@@ -893,6 +907,7 @@ atf_init_test_cases()
atf_add_test_case to_deaddirlink
atf_add_test_case to_link_outside
atf_add_test_case dstmode
+ atf_add_test_case root
atf_add_test_case to_root
atf_add_test_case dirloop
atf_add_test_case unrdir