From 225cb8d7473eadb481a4884e929bf23589d4bd82 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 6 Apr 2024 15:13:23 -0700 Subject: [PATCH 1/4] =?UTF-8?q?cat:=20don=E2=80=99t=20trust=20st=5Fsize=20?= =?UTF-8?q?on=20/proc=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * src/cat.c (main): Improve test for when copying will exhaust the output device. Do not rely on st_size, which is unreliable in /proc. Use lseek instead; this is good enough here. * tests/cat/cat-self.sh: Test the relaxation of the heuristic for self-copying. --- src/cat.c | 31 +++++++++++++++++++++---------- tests/cat/cat-self.sh | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/cat.c b/src/cat.c index 4ed404363..b33faeb35 100644 --- a/src/cat.c +++ b/src/cat.c @@ -645,9 +645,10 @@ main (int argc, char **argv) /* Optimal size of i/o operations of output. */ idx_t outsize = io_blksize (&stat_buf); - /* Device and I-node number of the output. */ + /* Device, I-node number and lazily-acquired flags of the output. */ dev_t out_dev = stat_buf.st_dev; ino_t out_ino = stat_buf.st_ino; + int out_flags = -2; /* True if the output is a regular file. */ bool out_isreg = S_ISREG (stat_buf.st_mode) != 0; @@ -701,17 +702,27 @@ main (int argc, char **argv) fdadvise (input_desc, 0, 0, FADVISE_SEQUENTIAL); - /* Don't copy a nonempty regular file to itself, as that would - merely exhaust the output device. It's better to catch this - error earlier rather than later. */ + /* Don't copy a file to itself if that would merely exhaust the + output device. It's better to catch this error earlier + rather than later. */ - if (out_isreg - && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino - && lseek (input_desc, 0, SEEK_CUR) < stat_buf.st_size) + if (stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino) { - error (0, 0, _("%s: input file is output file"), quotef (infile)); - ok = false; - goto contin; + if (out_flags < -1) + out_flags = fcntl (STDOUT_FILENO, F_GETFL); + bool exhausting = 0 <= out_flags && out_flags & O_APPEND; + if (!exhausting) + { + off_t in_pos = lseek (input_desc, 0, SEEK_CUR); + if (0 <= in_pos) + exhausting = in_pos < lseek (STDOUT_FILENO, 0, SEEK_CUR); + } + if (exhausting) + { + error (0, 0, _("%s: input file is output file"), quotef (infile)); + ok = false; + goto contin; + } } /* Pointer to the input buffer. */ diff --git a/tests/cat/cat-self.sh b/tests/cat/cat-self.sh index e0f6455c0..854825def 100755 --- a/tests/cat/cat-self.sh +++ b/tests/cat/cat-self.sh @@ -30,4 +30,24 @@ echo y >doc.end || framework_failure_ cat doc doc.end >doc || fail=1 compare doc doc.end || fail=1 +# This terminates even though it copies a file to itself. +# Coreutils 9.5 and earlier rejected this. +echo x >fx || framework_failure_ +echo y >fy || framework_failure_ +cat fx fy >fxy || fail=1 +for i in 1 2; do + cat fx >fxy$i || fail=1 +done +for i in 3 4 5 6; do + cat fx >fx$i || fail=1 +done +cat - fy fxy1 || fail=1 +compare fxy fxy1 || fail=1 +cat fxy2 fy 1<>fxy2 || fail=1 +compare fxy fxy2 || fail=1 +returns_ 1 cat fx fx3 1<>fx3 || fail=1 +returns_ 1 cat - fx4 fx4 || fail=1 +returns_ 1 cat fx5 >>fx5 || fail=1 +returns_ 1 cat >fx6 || fail=1 + Exit $fail -- 2.40.1