The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=9075d4cfad5b339aabdf8033623a2164898c2786

commit 9075d4cfad5b339aabdf8033623a2164898c2786
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2024-04-17 01:36:26 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2024-04-17 02:03:28 +0000

    cp: Additional sanity check.
    
    Once we've successfully opened the file we've been asked to copy, check
    that it's of the same type as FTS told us it was.
    
    MFC after:      1 week
    Sponsored by:   Klara, Inc.
    Reviewed by:    allanjude, markj
    Differential Revision:  https://reviews.freebsd.org/D44806
---
 bin/cp/utils.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/bin/cp/utils.c b/bin/cp/utils.c
index 6c99d4c63f4a..d102fb076139 100644
--- a/bin/cp/utils.c
+++ b/bin/cp/utils.c
@@ -100,21 +100,34 @@ copy_fallback(int from_fd, int to_fd)
 int
 copy_file(const FTSENT *entp, int dne)
 {
-       struct stat *fs;
+       struct stat sb, *fs;
        ssize_t wcount;
        off_t wtotal;
        int ch, checkch, from_fd, rval, to_fd;
        int use_copy_file_range = 1;
 
+       fs = entp->fts_statp;
        from_fd = to_fd = -1;
-       if (!lflag && !sflag &&
-           (from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
-               warn("%s", entp->fts_path);
-               return (1);
+       if (!lflag && !sflag) {
+               if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) < 0 ||
+                   fstat(from_fd, &sb) != 0) {
+                       warn("%s", entp->fts_path);
+                       return (1);
+               }
+               /*
+                * Check that the file hasn't been replaced with one of a
+                * different type.  This can happen if we've been asked to
+                * copy something which is actively being modified and
+                * lost the race, or if we've been asked to copy something
+                * like /proc/X/fd/Y which stat(2) reports as S_IFREG but
+                * is actually something else once you open it.
+                */
+               if ((sb.st_mode & S_IFMT) != (fs->st_mode & S_IFMT)) {
+                       warnx("%s: File changed", entp->fts_path);
+                       return (1);
+               }
        }
 
-       fs = entp->fts_statp;
-
        /*
         * If the file exists and we're interactive, verify with the user.
         * If the file DNE, set the mode to be the from file, minus setuid

Reply via email to