diff --git a/Cargo.lock b/Cargo.lock index 3de0ea0ccb..a95181381d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3447,6 +3447,7 @@ dependencies = [ "gcd", "libc", "nix", + "rustix", "tempfile", "thiserror 2.0.18", "uucore", diff --git a/src/uu/cat/src/cat.rs b/src/uu/cat/src/cat.rs index 66bd69dfc8..17b22fde5d 100644 --- a/src/uu/cat/src/cat.rs +++ b/src/uu/cat/src/cat.rs @@ -480,31 +480,25 @@ fn get_input_type(path: &OsString) -> CatResult { /// Writes handle to stdout with no configuration. This allows a /// simple memory copy. fn print_fast(handle: &mut InputHandle) -> CatResult<()> { - let stdout = io::stdout(); - #[cfg(any(target_os = "linux", target_os = "android"))] - let mut stdout = stdout; #[cfg(any(target_os = "linux", target_os = "android"))] { // If we're on Linux or Android, try to use the splice() system call // for faster writing. If it works, we're done. - if !splice::write_fast_using_splice(handle, &mut stdout)? { + if !splice::write_fast_using_splice(handle, &mut *uucore::stdio::stdout_raw())? { return Ok(()); } } // If we're not on Linux or Android, or the splice() call failed, // fall back on slower writing. - print_unbuffered(handle, stdout) + print_unbuffered(handle) } #[cfg_attr(any(target_os = "linux", target_os = "android"), inline(never))] // splice fast-path does not require this allocation -fn print_unbuffered( - handle: &mut InputHandle, - stdout: io::Stdout, -) -> CatResult<()> { +fn print_unbuffered(handle: &mut InputHandle) -> CatResult<()> { #[cfg(any(unix, target_os = "wasi"))] - let mut stdout = uucore::io::RawWriter(stdout); // use raw syscall to remove buffering + let mut stdout = uucore::stdio::stdout_raw(); #[cfg(not(any(unix, target_os = "wasi")))] - let mut stdout = stdout.lock(); + let mut stdout = io::stdout().lock(); let mut buf = [0; 1024 * 64]; loop { match handle.reader.read(&mut buf) { diff --git a/src/uu/dd/Cargo.toml b/src/uu/dd/Cargo.toml index 438b9ccf55..24b8d8b911 100644 --- a/src/uu/dd/Cargo.toml +++ b/src/uu/dd/Cargo.toml @@ -22,6 +22,7 @@ doctest = false clap = { workspace = true } gcd = { workspace = true } libc = { workspace = true } +rustix = { workspace = true } uucore = { workspace = true, features = [ "format", "parser-size", diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index f164e86554..0775a7ffdf 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -36,7 +36,13 @@ jiff = { workspace = true, optional = true, features = [ "tzdb-concatenated", ] } rustc-hash = { workspace = true } -rustix = { workspace = true, features = ["fs", "net", "pipe", "process"] } +rustix = { workspace = true, features = [ + "fs", + "net", + "pipe", + "process", + "stdio", +] } time = { workspace = true, optional = true, features = [ "formatting", "local-offset", diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index e136c4d8e3..4827cd107b 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -32,6 +32,8 @@ pub use crate::mods::locale; pub use crate::mods::os; pub use crate::mods::panic; pub use crate::mods::posix; +#[cfg(any(unix, target_os = "wasi"))] +pub use crate::mods::stdio; // * feature-gated modules #[cfg(feature = "backup-control")] diff --git a/src/uucore/src/lib/mods.rs b/src/uucore/src/lib/mods.rs index e33bf03195..c0faa5a0db 100644 --- a/src/uucore/src/lib/mods.rs +++ b/src/uucore/src/lib/mods.rs @@ -14,3 +14,5 @@ pub mod locale; pub mod os; pub mod panic; pub mod posix; +#[cfg(any(unix, target_os = "wasi"))] +pub mod stdio; diff --git a/src/uucore/src/lib/mods/stdio.rs b/src/uucore/src/lib/mods/stdio.rs new file mode 100644 index 0000000000..59688690a7 --- /dev/null +++ b/src/uucore/src/lib/mods/stdio.rs @@ -0,0 +1,55 @@ +// 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. + +//! Abstractions for raw access to stdin and stdout, without buffering. + +use core::ops::{Deref, DerefMut}; +use std::{fs::File, mem::ManuallyDrop}; + +pub struct StdinRaw(ManuallyDrop); + +pub struct StdoutRaw(ManuallyDrop); + +pub fn stdin_raw() -> StdinRaw { + // SAFETY: We ensure that the file descriptor is never closed by + // wrapping the `File` in `ManuallyDrop`. + let fd = unsafe { rustix::stdio::take_stdin() }; + StdinRaw(ManuallyDrop::new(File::from(fd))) +} + +pub fn stdout_raw() -> StdoutRaw { + // SAFETY: We ensure that the file descriptor is never closed by + // wrapping the `File` in `ManuallyDrop`. + let fd = unsafe { rustix::stdio::take_stdout() }; + StdoutRaw(ManuallyDrop::new(File::from(fd))) +} + +impl Deref for StdinRaw { + type Target = File; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StdinRaw { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Deref for StdoutRaw { + type Target = File; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for StdoutRaw { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +}