From 3cde4d25ef2ce580a1a0d0cf32ea2822a6a54cb8 Mon Sep 17 00:00:00 2001 From: oech3 <79379754+oech3@users.noreply.github.com> Date: Mon, 10 Aug 2026 21:45:34 +0900 Subject: [PATCH] cat: catch 2nd+ splice's error --- src/uucore/src/lib/features/pipes.rs | 32 +++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/uucore/src/lib/features/pipes.rs b/src/uucore/src/lib/features/pipes.rs index 02c13dc8bad..41778c51307 100644 --- a/src/uucore/src/lib/features/pipes.rs +++ b/src/uucore/src/lib/features/pipes.rs @@ -99,17 +99,33 @@ pub fn splice_unbounded_auto(source: &impl AsFd, dest: &mut impl AsFd) -> PipeRe let _ = fcntl_setpipe_size(&mut *dest, MAX_ROOTLESS_PIPE_SIZE); // pre-generate page caches for splice let _ = rustix::fs::fadvise(source, 0, None, rustix::fs::Advice::Sequential); - loop { - match splice(&source, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) { - Ok(0) => return Ok(Ok(())), - Ok(n) => { - if drain_pipe(pipe_rd, dest, n)?.is_err() { - return Ok(Err(())); - } + // 1st error is used to detect missing support for splice + match splice(&source, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) { + Ok(0) => return Ok(Ok(())), + Ok(mut n) => { + if let Ok(s) = splice(pipe_rd, dest, n) { + n -= s; + } else { + // read/write fallback + // use read_to_end to make pipe empty for the case write failed + let mut drain = Vec::with_capacity(n); + pipe_rd.take(n as u64).read_to_end(&mut drain)?; + RawWriter(&dest).write_all(&drain)?; + return Ok(Err(())); + } + while n > 0 { + n -= splice(pipe_rd, dest, n)?; } - Err(_) => return Ok(Err(())), } + Err(_) => return Ok(Err(())), } + // GNU cat catches all strace injections for 2nd+ splice + while let mut n @ 1.. = splice(&source, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE)? { + while n > 0 { + n -= splice(pipe_rd, dest, n)?; + } + } + Ok(Ok(())) } /// splice `n` bytes with read/write fallback