cp: Add an option to visit sources in order.

This adds a --sort option which makes cp pass a comparison function to
FTS, ensuring that sources are visited and traversed in a predictable
order.  This will help make certain test cases more reliable.

Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D51214
This commit is contained in:
Dag-Erling Smørgrav
2025-07-09 19:06:07 +02:00
parent c3efa16dc9
commit 2d6b33f801
3 changed files with 26 additions and 4 deletions
+12
View File
@@ -184,6 +184,18 @@ If the source file has both its set-user-ID and set-group-ID bits on,
and either the user ID or group ID cannot be preserved, neither
the set-user-ID nor set-group-ID bits are preserved in the copy's
permissions.
.It Fl -sort
Visit and traverse sources in (non-localized) lexicographical order.
Normally,
.Nm
visits the sources in the order they were listed on the command line,
and if recursing, traverses their contents in whichever order they
were returned in by the kernel, which may be the order in which they
were created, lexicographical order, or something else entirely.
With
.Fl -sort ,
the sources are both visited and traversed in lexicographical order.
This is mostly useful for testing.
.It Fl s , Fl -symbolic-link
Create symbolic links to regular files in a hierarchy instead of copying.
.It Fl v , Fl -verbose
+12 -2
View File
@@ -71,7 +71,7 @@ static char dot[] = ".";
#define END(buf) (buf + sizeof(buf))
PATH_T to = { .dir = -1, .end = to.path };
bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
static bool Hflag, Lflag, Pflag, Rflag, rflag;
static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag;
volatile sig_atomic_t info;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@@ -96,6 +96,7 @@ static const struct option long_opts[] =
{ "symbolic-link", no_argument, NULL, 's' },
{ "verbose", no_argument, NULL, 'v' },
{ "one-file-system", no_argument, NULL, 'x' },
{ "sort", no_argument, NULL, SORT_OPT },
{ 0 }
};
@@ -167,6 +168,9 @@ main(int argc, char *argv[])
case 'x':
fts_options |= FTS_XDEV;
break;
case SORT_OPT:
Sflag = true;
break;
default:
usage();
}
@@ -284,6 +288,12 @@ main(int argc, char *argv[])
&to_stat)));
}
static int
ftscmp(const FTSENT * const *a, const FTSENT * const *b)
{
return (strcmp((*a)->fts_name, (*b)->fts_name));
}
static int
copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
{
@@ -327,7 +337,7 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat)
}
level = FTS_ROOTLEVEL;
if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL)
if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL)
err(1, "fts_open");
for (badcp = rval = 0;
(curr = fts_read(ftsp)) != NULL;
+2 -2
View File
@@ -657,7 +657,7 @@ unrdir_body()
atf_check \
-s exit:1 \
-e match:"^cp: src/b: Permission denied" \
cp -R src dst
cp -R --sort src dst
atf_check test -d dst/a
atf_check cmp src/a/f dst/a/f
atf_check test -d dst/b
@@ -681,7 +681,7 @@ unrfile_body()
atf_check \
-s exit:1 \
-e match:"^cp: src/b: Permission denied" \
cp -R src dst
cp -R --sort src dst
atf_check test -d dst
atf_check cmp src/a dst/a
atf_check test ! -e dst/b