aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2025-11-22 13:11:59 +0100
committerColin Percival <cperciva@FreeBSD.org>2025-11-23 08:38:48 -0800
commit54808d50412aee31692f0041b4f78ddaf43bac8f (patch)
tree0c2af07080f649ed01d23d8ade69aeaf7d2f94b9
parentd84de55c15016640a03d0ff43898addd5b9323d8 (diff)
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. Approved by: re (cperciva) PR: 291132 MFC after: 3 days Fixes: 82fc0d09e862 ("cp: Partly restore symlink folllowing.") Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D53863 (cherry picked from commit fe836c50120daed3e4084f43c27d8d650d36dee8) (cherry picked from commit c4843e5805dec10ee552d21f19c8da7707c1fcbc)
-rw-r--r--bin/cp/cp.c7
-rwxr-xr-xbin/cp/tests/cp_test.sh15
2 files changed, 21 insertions, 1 deletions
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