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:          82fc0d09e8 ("cp: Partly restore symlink folllowing.")
Reviewed by:	markj
Differential Revision:	https://reviews.freebsd.org/D53863
This commit is contained in:
Dag-Erling Smørgrav 2025-11-22 13:11:59 +01:00
parent 833e5d42ab
commit fe836c5012
2 changed files with 21 additions and 1 deletions

View file

@ -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;

View file

@ -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