diff --git a/src/uu/cat/src/splice.rs b/src/uu/cat/src/splice.rs index 87cbff81a3e..061cbf736bb 100644 --- a/src/uu/cat/src/splice.rs +++ b/src/uu/cat/src/splice.rs @@ -7,7 +7,7 @@ use super::{CatResult, FdReadable, InputHandle}; use rustix::io::{read, write}; use std::os::{fd::AsFd, unix::io::AsRawFd}; -use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, pipe, splice, splice_exact}; +use uucore::pipes::{MAX_ROOTLESS_PIPE_SIZE, is_end_of_file, pipe, splice, splice_exact}; const BUF_SIZE: usize = 1024 * 16; @@ -30,7 +30,7 @@ pub(super) fn write_fast_using_splice( loop { match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) { Ok(1..) => {} - Ok(0) => return Ok(false), + Ok(0) => return Ok(!is_end_of_file(&handle.reader)), Err(_) => return Ok(true), } } @@ -38,7 +38,7 @@ pub(super) fn write_fast_using_splice( // both of in/output are not pipe. needs broker to use splice() with additional costs loop { match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) { - Ok(0) => return Ok(false), + Ok(0) => return Ok(!is_end_of_file(&handle.reader)), Ok(n) => { if splice_exact(&pipe_rd, write_fd, n).is_err() { // If the first splice manages to copy to the intermediate diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index bd4acb8b2fa..d538ff50e92 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -37,7 +37,7 @@ jiff = { workspace = true, optional = true, features = [ "tzdb-concatenated", ] } rustc-hash = { workspace = true } -rustix = { workspace = true, features = ["fs", "pipe"] } +rustix = { workspace = true, features = ["event", "fs", "time", "pipe"] } time = { workspace = true, optional = true, features = [ "formatting", "local-offset", diff --git a/src/uucore/src/lib/features/pipes.rs b/src/uucore/src/lib/features/pipes.rs index cb96908aad9..94741e6b963 100644 --- a/src/uucore/src/lib/features/pipes.rs +++ b/src/uucore/src/lib/features/pipes.rs @@ -52,6 +52,27 @@ pub fn splice(source: &impl AsFd, target: &impl AsFd, len: usize) -> std::io::Re )?) } +/// check that splice reached to end of file +/// ignore 2nd Ctrl + D +/// +#[inline] +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn is_end_of_file(source: &impl AsFd) -> bool { + use rustix::event::{PollFd, PollFlags, poll}; + use rustix::time::Timespec; + let mut pfd = [PollFd::new(source, PollFlags::IN)]; + matches!( + poll( + &mut pfd, + Some(&Timespec { + tv_sec: 0, + tv_nsec: 0 + }) + ), + Ok(0) + ) +} + /// Splice wrapper which fully finishes the write. /// /// Exactly `len` bytes are moved from `source` into `target`.