Skip to content

Commit 4ceb880

Browse files
oech3sylvestre
authored andcommitted
uucore: add a function returning /dev/null to use splice() for wc,dd,tail
1 parent 5ca5e01 commit 4ceb880

2 files changed

Lines changed: 23 additions & 14 deletions

File tree

src/uu/wc/src/count_fast.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ use uucore::hardware::SimdPolicy;
99

1010
use super::WordCountable;
1111

12-
#[cfg(any(target_os = "linux", target_os = "android"))]
13-
use std::fs::OpenOptions;
1412
use std::io::{self, ErrorKind, Read};
1513

1614
#[cfg(unix)]
@@ -41,18 +39,7 @@ const BUF_SIZE: usize = 256 * 1024;
4139
#[inline]
4240
#[cfg(any(target_os = "linux", target_os = "android"))]
4341
fn count_bytes_using_splice(fd: &impl AsFd) -> Result<usize, usize> {
44-
let null_file = OpenOptions::new()
45-
.write(true)
46-
.open("/dev/null")
47-
.map_err(|_| 0_usize)?;
48-
let null_rdev = rustix::fs::fstat(null_file.as_fd())
49-
.map_err(|_| 0_usize)?
50-
.st_rdev as libc::dev_t;
51-
if (libc::major(null_rdev), libc::minor(null_rdev)) != (1, 3) {
52-
// This is not a proper /dev/null, writing to it is probably bad
53-
// Bit of an edge case, but it has been known to happen
54-
return Err(0);
55-
}
42+
let null_file = uucore::pipes::dev_null().ok_or(0_usize)?;
5643
// todo: avoid generating broker if input is pipe (fcntl_setpipe_size succeed) and directly splice() to /dev/null to save RAM usage
5744
let (pipe_rd, pipe_wr) = pipe().map_err(|_| 0_usize)?;
5845

src/uucore/src/lib/features/pipes.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
1818
/// Returns two `File` objects: everything written to the second can be read
1919
/// from the first.
2020
/// This is used only for resolving the limitation for splice: one of a input or output should be pipe
21+
#[inline]
2122
#[cfg(any(target_os = "linux", target_os = "android"))]
2223
pub fn pipe() -> std::io::Result<(File, File)> {
2324
let (read, write) = rustix::pipe::pipe()?;
@@ -36,6 +37,7 @@ pub fn pipe() -> std::io::Result<(File, File)> {
3637
/// To get around this requirement, consider splicing from your source into
3738
/// a [`pipe`] and then from the pipe into your target (with `splice_exact`):
3839
/// this is still very efficient.
40+
#[inline]
3941
#[cfg(any(target_os = "linux", target_os = "android"))]
4042
pub fn splice(source: &impl AsFd, target: &impl AsFd, len: usize) -> std::io::Result<usize> {
4143
Ok(rustix::pipe::splice(
@@ -53,6 +55,7 @@ pub fn splice(source: &impl AsFd, target: &impl AsFd, len: usize) -> std::io::Re
5355
/// Exactly `len` bytes are moved from `source` into `target`.
5456
///
5557
/// Panics if `source` runs out of data before `len` bytes have been moved.
58+
#[inline]
5659
#[cfg(any(target_os = "linux", target_os = "android"))]
5760
pub fn splice_exact(source: &impl AsFd, target: &impl AsFd, len: usize) -> std::io::Result<()> {
5861
let mut left = len;
@@ -63,3 +66,22 @@ pub fn splice_exact(source: &impl AsFd, target: &impl AsFd, len: usize) -> std::
6366
}
6467
Ok(())
6568
}
69+
70+
/// Return verified /dev/null
71+
///
72+
/// `splice` to /dev/null is faster than `read` when we skip or count the input which is not able to seek
73+
#[inline]
74+
#[cfg(any(target_os = "linux", target_os = "android"))]
75+
pub fn dev_null() -> Option<File> {
76+
let null = std::fs::OpenOptions::new()
77+
.write(true)
78+
.open("/dev/null")
79+
.ok()?;
80+
let stat = rustix::fs::fstat(&null).ok()?;
81+
let dev = stat.st_rdev;
82+
if (rustix::fs::major(dev), rustix::fs::minor(dev)) == (1, 3) {
83+
Some(null)
84+
} else {
85+
None
86+
}
87+
}

0 commit comments

Comments
 (0)