git: 006017419bea - stable/13 - cp: Add tests for hard link case.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 17 Jan 2024 18:28:36 UTC
The branch stable/13 has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=006017419beab30f45131ca3953c7e4273e9aa65 commit 006017419beab30f45131ca3953c7e4273e9aa65 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2023-12-13 21:31:05 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2024-01-17 16:15:59 +0000 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 1fead66b64822f3f8106ad09bef0b9656836fa1a) 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 d3a8e9b43b4cef5b80e3845dfa8fd1fec6e568f9) 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 d002316fd7bf0b359ea2f5518f3c10f6ad89a9ac) 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 ac56b9d83c75f548667912ffe422be6bd4f5c27e) 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 0f4467ce444b201468d2268958130f495951ca3c) 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 38509270663f336103273878cc8ddc88a225b9d8) cp: Move the -N flag in the manual page. This accidentally got left out of 0f4467ce444b. MFC after: 1 week Sponsored by: Klara, Inc. Reviewed by: kevans, allanjude Differential Revision: https://reviews.freebsd.org/D43067 (cherry picked from commit 53fc8e190241233d94e183f8a39ec39f2154dfa8) --- bin/cp/cp.1 | 10 ++-- bin/cp/cp.c | 13 +++--- bin/cp/tests/cp_test.sh | 87 ++++++++++++++++++++++++++++++++-- bin/cp/utils.c | 122 ++++++++++++++++++++++++------------------------ 4 files changed, 154 insertions(+), 78 deletions(-) diff --git a/bin/cp/cp.1 b/bin/cp/cp.1 index 3862babafe7f..d8d62ef076a1 100644 --- a/bin/cp/cp.1 +++ b/bin/cp/cp.1 @@ -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 diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 8217a1e5d3c9..852868e65dcb 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -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; diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh index f995d709cc3c..397c06d75bbb 100755 --- a/bin/cp/tests/cp_test.sh +++ b/bin/cp/tests/cp_test.sh @@ -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 } diff --git a/bin/cp/utils.c b/bin/cp/utils.c index f43902eab3e6..0bb5157e3f57 100644 --- a/bin/cp/utils.c +++ b/bin/cp/utils.c @@ -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; + } + } + + 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; } - } else if (!lflag && !sflag) { + 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; - } - } - if (!use_copy_file_range) { - wcount = copy_fallback(from_fd, to_fd); + 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; } - 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: