From nobody Mon Apr 08 22:41:51 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4VD3xC40Y2z5Fyjx; Mon, 8 Apr 2024 22:41:51 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4VD3xC3YzMz4Rsq; Mon, 8 Apr 2024 22:41:51 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1712616111; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=AkkaD3xUXc+ihIRdVbPXHEUZM4ISj1p5f50t+7CmxVk=; b=cho6QedR24VPlV7XhAqZ65o6GQQz3/rCkvw64Z2gOMiNvrgfZzPP2+rD4vwQ6D3IJvkk6D wOZzy3fuI4CLOztov3KwqdzUJNfOyedLZUt5CEHSDFJR3oVCKNyo2i4p7QGxB+HN511+2n RijS0nhqTRsFMyHfsSQUFxBXoiIRjLfGrevAa9ozF/peh28C665Zy5Rh2H4iPnJ2Tj5rmx 5mefMOno63RvcWnopENDFbMu7tLOB+mtM4wOxBkP2aoKi6/ZRvdRhdj1tKiU+oHYaLFHTj 3pQ7KQ9JB/EzRy79vC2dGyhkdRXL0SiMgiFnF/bprbYcABkUm2O5cjhEYYPXOA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1712616111; a=rsa-sha256; cv=none; b=QxHnxhptD0yZwZFEGcqLF2lIWrWRQ88TSP/0+EkXRBeA2fPkzkoQrLelUbV7sAExxWKDld 8wDFciMpB/BnAOZ6rIPEmscKduT3q5mJNNKUlRF/qyqRyvYc86CkAGdaXy6eTGe7zvy1/P yysJxb0/ZUMvJcriD11M3+WKr6UhzXcuTMtUdKhXXTQqupmuaDLgGxxeZ5XlABwmi4AzlM G8kuST3QfzZPOv78c32tkwKF7bcP1Yve+jFdLgNyKSOWgphODff5vcaPU2TeOFjsjqNqpc JqFKius1Btj2XT57HRJ6BGmZyuNbP2DXNAcsWWKK/4epxrsql/Qwo4F2EtFnLA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1712616111; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=AkkaD3xUXc+ihIRdVbPXHEUZM4ISj1p5f50t+7CmxVk=; b=ZXSQ85jH/fJedp2BVBVJw9trJfolqym7HGHOKHtLmNSwVaCCIdSjOIKQ/aESp5LWeNqVz2 6uAyhf2pLlEshC0bbLrtjFye2pl9PYIAjzvrh3HiUaVRJd70G3aPgtTAAKJtbXrFhoViFG cPvTuK16OTt8brWMUe7HOQST0THNEDXcEj9ePc6J8QBGmfVvxb49g8sP7FMoVhdMqNA59s 7P9s2nbwwnEIV683QbSZ6WsqR585gmSCtB0N5QHO8vOythvWCHRkeKNsRfqdPUhmEfslOL yQ5KZVZ8BfDLc5rzIhITYvbWwmyAlmLEM4DUVPM24I0saWKGDZtLoQob+iVLAA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4VD3xC2s8QzV7j; Mon, 8 Apr 2024 22:41:51 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 438MfpBX076124; Mon, 8 Apr 2024 22:41:51 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 438Mfpgd076121; Mon, 8 Apr 2024 22:41:51 GMT (envelope-from git) Date: Mon, 8 Apr 2024 22:41:51 GMT Message-Id: <202404082241.438Mfpgd076121@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= Subject: git: 0729d1e8fd90 - main - cp: Never follow symbolic links in destination. List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 0729d1e8fd90afc2f19e9b9834533181caf6ddee Auto-Submitted: auto-generated The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=0729d1e8fd90afc2f19e9b9834533181caf6ddee commit 0729d1e8fd90afc2f19e9b9834533181caf6ddee Author: Dag-Erling Smørgrav AuthorDate: 2024-04-08 22:41:33 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2024-04-08 22:41:33 +0000 cp: Never follow symbolic links in destination. Historically, BSD cp has followed symbolic links in the destination when copying recursively, while GNU cp has not. POSIX is somewhat vague on the topic, but both interpretations are within bounds. In 33ad990ce974, cp was changed to apply the same logic for symbolic links in the destination as for symbolic links in the source: follow if not recursing (which is moot, as this situation can only arise while recursing) or if the `-L` option was given. There is no support for this in POSIX. We can either switch back, or go all the way. Having carefully weighed the kind of trouble you can run into by following unexpected symlinks up against the kind of trouble you can run into by not following symlinks you expected to follow, we choose to go all the way. Note that this means we need to stat the destination twice: once, following links, to check if it is or references the same file as the source, and a second time, not following links, to set the dne flag and determine the destination's type. While here, remove a needless complication in the dne logic. We don't need to explicitly reject overwriting a directory with a non-directory, because it will fail anyway. Finally, add test cases for copying a directory to a symlink and overwriting a directory with a non-directory. MFC after: never Relnotes: yes Sponsored by: Klara, Inc. Reviewed by: kevans Differential Revision: https://reviews.freebsd.org/D44578 --- bin/cp/cp.c | 52 +++++++++++++------------------------------------ bin/cp/tests/cp_test.sh | 31 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 39 deletions(-) diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 02b879006a4f..cee412e57264 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -260,22 +260,6 @@ main(int argc, char *argv[]) &to_stat))); } -/* Does the right thing based on -R + -H/-L/-P */ -static int -copy_stat(const char *path, struct stat *sb) -{ - - /* - * For -R -H/-P, we need to lstat() instead; copy() cares about the link - * itself rather than the target if we're not following links during the - * traversal. - */ - if (!Rflag || Lflag) - return (stat(path, sb)); - return (lstat(path, sb)); -} - - static int copy(char *argv[], enum op type, int fts_options, struct stat *root_stat) { @@ -403,7 +387,6 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat) continue; } - if (asprintf(&recurse_path, "%s/%s", to.p_path, rootname) == -1) err(1, "asprintf"); @@ -452,30 +435,21 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat) continue; } - /* Not an error but need to remember it happened. */ - if (copy_stat(to.p_path, &to_stat) == -1) - dne = 1; - else { - if (to_stat.st_dev == curr->fts_statp->st_dev && - to_stat.st_ino == curr->fts_statp->st_ino) { - warnx("%s and %s are identical (not copied).", - to.p_path, curr->fts_path); - badcp = rval = 1; - if (S_ISDIR(curr->fts_statp->st_mode)) - (void)fts_set(ftsp, curr, FTS_SKIP); - continue; - } - if (!S_ISDIR(curr->fts_statp->st_mode) && - S_ISDIR(to_stat.st_mode)) { - warnx("cannot overwrite directory %s with " - "non-directory %s", - to.p_path, curr->fts_path); - badcp = rval = 1; - continue; - } - dne = 0; + /* Check if source and destination are identical. */ + if (stat(to.p_path, &to_stat) == 0 && + to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + badcp = rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; } + /* Not an error but need to remember it happened. */ + dne = lstat(to.p_path, &to_stat) != 0; + switch (curr->fts_statp->st_mode & S_IFMT) { case S_IFLNK: /* Catch special case of a non-dangling symlink. */ diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh index 397c06d75bbb..5c581e06ab8e 100755 --- a/bin/cp/tests/cp_test.sh +++ b/bin/cp/tests/cp_test.sh @@ -363,6 +363,35 @@ symlink_exists_force_body() atf_check -o inline:"foo\n" readlink bar } +atf_test_case directory_to_symlink +directory_to_symlink_body() +{ + mkdir -p foo + ln -s .. foo/bar + mkdir bar + touch bar/baz + atf_check -s not-exit:0 -e match:"Not a directory" \ + cp -R bar foo + atf_check -s not-exit:0 -e match:"Not a directory" \ + cp -r bar foo +} + +atf_test_case overwrite_directory +overwrite_directory_body() +{ + mkdir -p foo/bar/baz + touch bar + atf_check -s not-exit:0 -e match:"Is a directory" \ + cp bar foo + rm bar + mkdir bar + touch bar/baz + atf_check -s not-exit:0 -e match:"Is a directory" \ + cp -R bar foo + atf_check -s not-exit:0 -e match:"Is a directory" \ + cp -r bar foo +} + atf_init_test_cases() { atf_add_test_case basic @@ -388,4 +417,6 @@ atf_init_test_cases() atf_add_test_case symlink atf_add_test_case symlink_exists atf_add_test_case symlink_exists_force + atf_add_test_case directory_to_symlink + atf_add_test_case overwrite_directory }