1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-10-18 02:19:39 +00:00

cp: Add tests for hard link case.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D43052

(cherry picked from commit 1fead66b64)

cp: Add tests for symbolic link case.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans, allanjude
Differential Revision:	https://reviews.freebsd.org/D43054

(cherry picked from commit d3a8e9b43b)

cp: Refactor the core logic.

Rewrite `copy_file()` so the lflag and sflag are handled as early as
possible instead of constantly checking that they're not set and then
handling them at the end.  This also opens the door to changing the
failure logic at some future point (for instance, we might decide to
fall back to copying if `errno` indicates that the file system does not
support links).

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans, allanjude
Differential Revision:	https://reviews.freebsd.org/D43055

(cherry picked from commit d002316fd7)

cp: Split the basic_symlink test case in two.

This test case tests two different things: first, that copying a symlink
results in a file with the same contents as the target of the symlink,
rather than a second symlink, and second, that cp will refuse to copy a
file to itself, or to a link to itself, or a link to its target.  Leave
the first part in basic_symlink, move the second part to a new test case
named samefile, and slightly expand both cases.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D43062

(cherry picked from commit ac56b9d83c)

cp: Move the flags around a bit.

- The HLPR flags are grouped together at the beginning because they are
  the standard flags for programs using FTS.  Move the N flag out from
  among them to its correct place in the sequence.
- The Pflag variable isn't used outside main(), but moving it out lets
  us skip initialization and keeps it with its friends H, L and R.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D43063

(cherry picked from commit 0f4467ce44)

cp: Further simplify the core logic.

If the destination file exists but we decide unlink it, set the dne
flag.  This means we don't need to re-check the conditions that would
have caused us to delete the file when we later need to decide whether
to create or replace it.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans
Differential Revision:	https://reviews.freebsd.org/D43064

(cherry picked from commit 3850927066)

cp: Move the -N flag in the manual page.

This accidentally got left out of 0f4467ce44.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Reviewed by:	kevans, allanjude
Differential Revision:	https://reviews.freebsd.org/D43067

(cherry picked from commit 53fc8e1902)
This commit is contained in:
Dag-Erling Smørgrav 2023-12-13 22:31:05 +01:00
parent 0fcf3a6235
commit d514cadacc
4 changed files with 154 additions and 78 deletions

View File

@ -31,7 +31,7 @@
.\"
.\" @(#)cp.1 8.3 (Berkeley) 4/18/94
.\"
.Dd December 7, 2023
.Dd December 14, 2023
.Dt CP 1
.Os
.Sh NAME
@ -90,10 +90,6 @@ option is specified, symbolic links on the command line are followed.
If the
.Fl R
option is specified, all symbolic links are followed.
.It Fl N
When used with
.Fl p ,
suppress copying file flags.
.It Fl P
No symbolic links are followed.
This is the default if the
@ -161,6 +157,10 @@ or
options.)
.It Fl l
Create hard links to regular files in a hierarchy instead of copying.
.It Fl N
When used with
.Fl p ,
suppress copying file flags.
.It Fl n
Do not overwrite an existing file.
(The

View File

@ -85,7 +85,7 @@ static char emptystring[] = "";
PATH_T to = { to.p_path, emptystring, "" };
int Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag;
static int Hflag, Lflag, Rflag, rflag;
static int Hflag, Lflag, Pflag, Rflag, rflag;
volatile sig_atomic_t info;
enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE };
@ -98,12 +98,11 @@ main(int argc, char *argv[])
{
struct stat to_stat, tmp_stat;
enum op type;
int Pflag, ch, fts_options, r, have_trailing_slash;
int ch, fts_options, r, have_trailing_slash;
char *target;
fts_options = FTS_NOCHDIR | FTS_PHYSICAL;
Pflag = 0;
while ((ch = getopt(argc, argv, "HLNPRafilnprsvx")) != -1)
while ((ch = getopt(argc, argv, "HLPRafilNnprsvx")) != -1)
switch (ch) {
case 'H':
Hflag = 1;
@ -113,9 +112,6 @@ main(int argc, char *argv[])
Lflag = 1;
Hflag = Pflag = 0;
break;
case 'N':
Nflag = 1;
break;
case 'P':
Pflag = 1;
Hflag = Lflag = 0;
@ -140,6 +136,9 @@ main(int argc, char *argv[])
case 'l':
lflag = 1;
break;
case 'N':
Nflag = 1;
break;
case 'n':
nflag = 1;
fflag = iflag = 0;

View File

@ -51,10 +51,7 @@ basic_symlink_body()
atf_check cp baz foo
atf_check test '!' -L foo
atf_check -e inline:"cp: baz and baz are identical (not copied).\n" \
-s exit:1 cp baz baz
atf_check -e inline:"cp: bar and baz are identical (not copied).\n" \
-s exit:1 cp baz bar
atf_check cmp foo bar
}
atf_test_case chrdev
@ -71,6 +68,35 @@ chrdev_body()
check_size trunc 0
}
atf_test_case hardlink
hardlink_body()
{
echo "foo" >foo
atf_check cp -l foo bar
atf_check -o inline:"foo\n" cat bar
atf_check_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)"
}
atf_test_case hardlink_exists
hardlink_exists_body()
{
echo "foo" >foo
echo "bar" >bar
atf_check -s not-exit:0 -e match:exists cp -l foo bar
atf_check -o inline:"bar\n" cat bar
atf_check_not_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)"
}
atf_test_case hardlink_exists_force
hardlink_exists_force_body()
{
echo "foo" >foo
echo "bar" >bar
atf_check cp -fl foo bar
atf_check -o inline:"foo\n" cat bar
atf_check_equal "$(stat -f%d,%i foo)" "$(stat -f%d,%i bar)"
}
atf_test_case matching_srctgt
matching_srctgt_body()
{
@ -198,6 +224,22 @@ recursive_link_Lflag_body()
'(' ! -L foo-mirror/foo/baz ')'
}
atf_test_case samefile
samefile_body()
{
echo "foo" >foo
ln foo bar
ln -s bar baz
atf_check -e match:"baz and baz are identical" \
-s exit:1 cp baz baz
atf_check -e match:"bar and baz are identical" \
-s exit:1 cp baz bar
atf_check -e match:"foo and baz are identical" \
-s exit:1 cp baz foo
atf_check -e match:"bar and foo are identical" \
-s exit:1 cp foo bar
}
file_is_sparse()
{
atf_check ${0%/*}/sparse "$1"
@ -205,7 +247,7 @@ file_is_sparse()
files_are_equal()
{
atf_check test "$(stat -f "%d %i" "$1")" != "$(stat -f "%d %i" "$2")"
atf_check_not_equal "$(stat -f%d,%i "$1")" "$(stat -f%d,%i "$2")"
atf_check cmp "$1" "$2"
}
@ -293,11 +335,42 @@ standalone_Pflag_body()
atf_check -o inline:'Symbolic Link\n' stat -f %SHT baz
}
atf_test_case symlink
symlink_body()
{
echo "foo" >foo
atf_check cp -s foo bar
atf_check -o inline:"foo\n" cat bar
atf_check -o inline:"foo\n" readlink bar
}
atf_test_case symlink_exists
symlink_exists_body()
{
echo "foo" >foo
echo "bar" >bar
atf_check -s not-exit:0 -e match:exists cp -s foo bar
atf_check -o inline:"bar\n" cat bar
}
atf_test_case symlink_exists_force
symlink_exists_force_body()
{
echo "foo" >foo
echo "bar" >bar
atf_check cp -fs foo bar
atf_check -o inline:"foo\n" cat bar
atf_check -o inline:"foo\n" readlink bar
}
atf_init_test_cases()
{
atf_add_test_case basic
atf_add_test_case basic_symlink
atf_add_test_case chrdev
atf_add_test_case hardlink
atf_add_test_case hardlink_exists
atf_add_test_case hardlink_exists_force
atf_add_test_case matching_srctgt
atf_add_test_case matching_srctgt_contained
atf_add_test_case matching_srctgt_link
@ -305,10 +378,14 @@ atf_init_test_cases()
atf_add_test_case recursive_link_dflt
atf_add_test_case recursive_link_Hflag
atf_add_test_case recursive_link_Lflag
atf_add_test_case samefile
atf_add_test_case sparse_leading_hole
atf_add_test_case sparse_multiple_holes
atf_add_test_case sparse_only_hole
atf_add_test_case sparse_to_dev
atf_add_test_case sparse_trailing_hole
atf_add_test_case standalone_Pflag
atf_add_test_case symlink
atf_add_test_case symlink_exists
atf_add_test_case symlink_exists_force
}

View File

@ -68,6 +68,11 @@ static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
*/
#define BUFSIZE_SMALL (MAXPHYS)
/*
* Prompt used in -i case.
*/
#define YESNO "(y/n [n]) "
static ssize_t
copy_fallback(int from_fd, int to_fd)
{
@ -125,7 +130,6 @@ copy_file(const FTSENT *entp, int dne)
* modified by the umask.)
*/
if (!dne) {
#define YESNO "(y/n [n]) "
if (nflag) {
if (vflag)
printf("%s not overwritten\n", to.p_path);
@ -145,70 +149,69 @@ copy_file(const FTSENT *entp, int dne)
}
if (fflag) {
/*
* Remove existing destination file name create a new
* file.
*/
/* remove existing destination file */
(void)unlink(to.p_path);
if (!lflag && !sflag) {
to_fd = open(to.p_path,
O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISUID | S_ISGID));
}
} else if (!lflag && !sflag) {
/* Overwrite existing destination file name. */
to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
dne = 1;
}
} else if (!lflag && !sflag) {
}
rval = 0;
if (lflag) {
if (link(entp->fts_path, to.p_path) != 0) {
warn("%s", to.p_path);
rval = 1;
}
goto done;
}
if (sflag) {
if (symlink(entp->fts_path, to.p_path) != 0) {
warn("%s", to.p_path);
rval = 1;
}
goto done;
}
if (!dne) {
/* overwrite existing destination file */
to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
} else {
/* create new destination file */
to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
fs->st_mode & ~(S_ISUID | S_ISGID));
}
if (!lflag && !sflag && to_fd == -1) {
if (to_fd == -1) {
warn("%s", to.p_path);
rval = 1;
goto done;
}
rval = 0;
if (!lflag && !sflag) {
wtotal = 0;
do {
if (use_copy_file_range) {
wcount = copy_file_range(from_fd, NULL,
to_fd, NULL, SSIZE_MAX, 0);
if (wcount < 0 && errno == EINVAL) {
/* Prob a non-seekable FD */
use_copy_file_range = 0;
}
wtotal = 0;
do {
if (use_copy_file_range) {
wcount = copy_file_range(from_fd, NULL,
to_fd, NULL, SSIZE_MAX, 0);
if (wcount < 0 && errno == EINVAL) {
/* probably a non-seekable descriptor */
use_copy_file_range = 0;
}
if (!use_copy_file_range) {
wcount = copy_fallback(from_fd, to_fd);
}
wtotal += wcount;
if (info) {
info = 0;
(void)fprintf(stderr,
"%s -> %s %3d%%\n",
entp->fts_path, to.p_path,
cp_pct(wtotal, fs->st_size));
}
} while (wcount > 0);
if (wcount < 0) {
warn("%s", entp->fts_path);
rval = 1;
}
} else if (lflag) {
if (link(entp->fts_path, to.p_path)) {
warn("%s", to.p_path);
rval = 1;
if (!use_copy_file_range) {
wcount = copy_fallback(from_fd, to_fd);
}
} else if (sflag) {
if (symlink(entp->fts_path, to.p_path)) {
warn("%s", to.p_path);
rval = 1;
wtotal += wcount;
if (info) {
info = 0;
(void)fprintf(stderr,
"%s -> %s %3d%%\n",
entp->fts_path, to.p_path,
cp_pct(wtotal, fs->st_size));
}
} while (wcount > 0);
if (wcount < 0) {
warn("%s", entp->fts_path);
rval = 1;
}
/*
@ -217,16 +220,13 @@ copy_file(const FTSENT *entp, int dne)
* or its contents might be irreplaceable. It would only be safe
* to remove it if we created it and its length is 0.
*/
if (!lflag && !sflag) {
if (pflag && setfile(fs, to_fd))
rval = 1;
if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
rval = 1;
if (close(to_fd)) {
warn("%s", to.p_path);
rval = 1;
}
if (pflag && setfile(fs, to_fd))
rval = 1;
if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
rval = 1;
if (close(to_fd)) {
warn("%s", to.p_path);
rval = 1;
}
done: