From 34c60f953a1b4b679f1801d2f39a7a6981feea96 Mon Sep 17 00:00:00 2001 From: Daringcuteseal Date: Sun, 29 Dec 2024 10:28:22 +0700 Subject: [PATCH 1/2] yes: adapt to use new functions provided from uucore/buf-copy --- src/uu/yes/Cargo.toml | 3 ++ src/uu/yes/src/splice.rs | 77 ---------------------------------------- src/uu/yes/src/yes.rs | 63 +++++++++++++++++++++----------- 3 files changed, 46 insertions(+), 97 deletions(-) delete mode 100644 src/uu/yes/src/splice.rs diff --git a/src/uu/yes/Cargo.toml b/src/uu/yes/Cargo.toml index 21b5ff7ac33..5970c828760 100644 --- a/src/uu/yes/Cargo.toml +++ b/src/uu/yes/Cargo.toml @@ -24,6 +24,9 @@ itertools = { workspace = true } uucore = { workspace = true, features = ["pipes", "signals"] } nix = { workspace = true } +[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] +uucore = { workspace = true, features = ["buf-copy"] } + [target.'cfg(not(unix))'.dependencies] uucore = { workspace = true, features = ["pipes"] } diff --git a/src/uu/yes/src/splice.rs b/src/uu/yes/src/splice.rs deleted file mode 100644 index 5537d55e1be..00000000000 --- a/src/uu/yes/src/splice.rs +++ /dev/null @@ -1,77 +0,0 @@ -// This file is part of the uutils coreutils package. -// -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. -//! On Linux we can use vmsplice() to write data more efficiently. -//! -//! This does not always work. We're not allowed to splice to some targets, -//! and on some systems (notably WSL 1) it isn't supported at all. -//! -//! If we get an error code that suggests splicing isn't supported then we -//! tell that to the caller so it can fall back to a robust naïve method. If -//! we get another kind of error we bubble it up as normal. -//! -//! vmsplice() can only splice into a pipe, so if the output is not a pipe -//! we make our own and use splice() to bridge the gap from the pipe to the -//! output. -//! -//! We assume that an "unsupported" error will only ever happen before any -//! data was successfully written to the output. That way we don't have to -//! make any effort to rescue data from the pipe if splice() fails, we can -//! just fall back and start over from the beginning. - -use std::{ - io, - os::fd::{AsFd, AsRawFd}, -}; - -use nix::{errno::Errno, libc::S_IFIFO, sys::stat::fstat}; - -use uucore::pipes::{pipe, splice_exact, vmsplice}; - -pub(crate) fn splice_data(bytes: &[u8], out: &T) -> Result<()> -where - T: AsRawFd + AsFd, -{ - let is_pipe = fstat(out.as_raw_fd())?.st_mode as nix::libc::mode_t & S_IFIFO != 0; - - if is_pipe { - loop { - let mut bytes = bytes; - while !bytes.is_empty() { - let len = vmsplice(out, bytes).map_err(maybe_unsupported)?; - bytes = &bytes[len..]; - } - } - } else { - let (read, write) = pipe()?; - loop { - let mut bytes = bytes; - while !bytes.is_empty() { - let len = vmsplice(&write, bytes).map_err(maybe_unsupported)?; - splice_exact(&read, out, len).map_err(maybe_unsupported)?; - bytes = &bytes[len..]; - } - } - } -} - -pub(crate) enum Error { - Unsupported, - Io(io::Error), -} - -type Result = std::result::Result; - -impl From for Error { - fn from(error: nix::Error) -> Self { - Self::Io(io::Error::from_raw_os_error(error as i32)) - } -} - -fn maybe_unsupported(error: nix::Error) -> Error { - match error { - Errno::EINVAL | Errno::ENOSYS | Errno::EBADF => Error::Unsupported, - _ => error.into(), - } -} diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index b344feaa8d3..ba7c9759d83 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -8,15 +8,19 @@ use clap::{builder::ValueParser, crate_version, Arg, ArgAction, Command}; use std::error::Error; use std::ffi::OsString; -use std::io::{self, Write}; +use std::io::{stdout, Write}; +use uucore::error::UResult; +use uucore::{format_usage, help_about, help_usage}; + +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::fd::{AsFd, AsRawFd}; #[cfg(any(target_os = "linux", target_os = "android"))] -use std::os::fd::AsFd; -use uucore::error::{UResult, USimpleError}; +use uucore::buf_copy::{is_pipe, splice_data_to_fd, splice_data_to_pipe}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use uucore::pipes::pipe; + #[cfg(unix)] use uucore::signals::enable_pipe_errors; -use uucore::{format_usage, help_about, help_usage}; -#[cfg(any(target_os = "linux", target_os = "android"))] -mod splice; const ABOUT: &str = help_about!("yes.md"); const USAGE: &str = help_usage!("yes.md"); @@ -33,11 +37,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { args_into_buffer(&mut buffer, matches.get_many::("STRING")).unwrap(); prepare_buffer(&mut buffer); - match exec(&buffer) { - Ok(()) => Ok(()), - Err(err) if err.kind() == io::ErrorKind::BrokenPipe => Ok(()), - Err(err) => Err(USimpleError::new(1, format!("standard output: {err}"))), - } + exec(&buffer) } pub fn uu_app() -> Command { @@ -112,20 +112,43 @@ fn prepare_buffer(buf: &mut Vec) { } } -pub fn exec(bytes: &[u8]) -> io::Result<()> { - let stdout = io::stdout(); +/// On Linux and Android, repeatedly call the splice function to write our buffered string into +/// standard output using the `splice` system call. +#[cfg(any(target_os = "linux", target_os = "android"))] +fn loop_splice_data(bytes: &[u8], out: &T) -> UResult<()> +where + T: AsRawFd + AsFd, +{ + let is_pipe = is_pipe(out)?; + + // Under the if statements below, an Ok(()) is returned to indicate that the + // splice call failed but is still recoverable, i.e by using the + // stdout.write_all method. + if is_pipe { + loop { + if splice_data_to_pipe(bytes, out)?.1 { + return Ok(()); + } + } + } else { + let (read, write) = pipe()?; + loop { + if splice_data_to_fd(bytes, &read, &write, out)?.1 { + return Ok(()); + }; + } + } +} + +pub fn exec(bytes: &[u8]) -> UResult<()> { + let stdout = stdout(); let mut stdout = stdout.lock(); + #[cfg(unix)] enable_pipe_errors()?; #[cfg(any(target_os = "linux", target_os = "android"))] - { - match splice::splice_data(bytes, &stdout.as_fd()) { - Ok(_) => return Ok(()), - Err(splice::Error::Io(err)) => return Err(err), - Err(splice::Error::Unsupported) => (), - } - } + loop_splice_data(bytes, &stdout)?; loop { stdout.write_all(bytes)?; From d25cb9c7c7524f2704efb29194fc01011dda9925 Mon Sep 17 00:00:00 2001 From: Daringcuteseal Date: Thu, 2 Jan 2025 13:35:09 +0700 Subject: [PATCH 2/2] - --- src/uu/yes/src/yes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/yes/src/yes.rs b/src/uu/yes/src/yes.rs index ba7c9759d83..ebd6e7a1527 100644 --- a/src/uu/yes/src/yes.rs +++ b/src/uu/yes/src/yes.rs @@ -147,8 +147,8 @@ pub fn exec(bytes: &[u8]) -> UResult<()> { #[cfg(unix)] enable_pipe_errors()?; - #[cfg(any(target_os = "linux", target_os = "android"))] - loop_splice_data(bytes, &stdout)?; + // #[cfg(any(target_os = "linux", target_os = "android"))] + // loop_splice_data(bytes, &stdout)?; loop { stdout.write_all(bytes)?;