On 04/04/2026 03:27, Collin Funk wrote:
Attatched a v4 patch.
Since cat is such a fundamental tool, I think it's worth being extra defensive
by trying the read()/write() loop after the splice.
I've heard of cases where some fuse file systems lie about the size in stat,
while read() provides all the data.
Also it seems a bit wasteful to allocate large pipes in kernel mem,
for catting small files.
I think we could handle both these cases with the attached adjustment,
which avoids splice for small regular files, and tries read()/write()
after splice() always. Note the size threshold was determined with:
$ truncate -s $((32*1024)) mid $ hyperfine -N 'src/cat-splice mid' 'src/cat
mid'
Since we're not doing any more stats() to determine this info,
this should be more memory efficient and safer.
cheers,
Padraig
diff --git a/src/cat.c b/src/cat.c
index ed7e7ab3f..4b5516e06 100644
--- a/src/cat.c
+++ b/src/cat.c
@@ -864,14 +864,23 @@ main (int argc, char **argv)
}
else
{
- int splice_cat_status = splice_cat ();
- if (splice_cat_status != 0)
+ /* Note 32768 was determined as the limit when splice
+ starts to have a performance advantage. It also
+ excludes zero length files which may not be compatible
+ with splice in some edge cases. */
+ int splice_cat_status =
+ usable_st_size (&istat_buf) && istat_buf.st_size < 32768
+ ? 0 : splice_cat ();
+ if (splice_cat_status < 0)
{
inbuf = NULL;
- ok &= 0 < splice_cat_status;
+ ok = false;
}
else
{
+ /* Note we try simple_cat() even if splice_cat() succeeded,
+ to handle edge cases where splice finishes but read()
+ still returns data (seen on some FUSE systems). */
insize = MAX (insize, outsize);
inbuf = xalignalloc (page_size, insize);
ok &= simple_cat (inbuf, insize);