diff --git a/Cargo.lock b/Cargo.lock index a142d5373d6..6e21d285856 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4381,6 +4381,7 @@ dependencies = [ "fluent", "nom 8.0.0", "rustix", + "tempfile", "uucore", ] diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 2ea6be054b9..93a609d9d5d 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -2101,6 +2101,7 @@ dependencies = [ "clap", "fluent", "nom", + "rustix", "uucore", ] diff --git a/src/uu/tr/Cargo.toml b/src/uu/tr/Cargo.toml index 45e95ebfe5d..9fb84ba4311 100644 --- a/src/uu/tr/Cargo.toml +++ b/src/uu/tr/Cargo.toml @@ -26,9 +26,12 @@ uucore = { workspace = true, features = ["fs", "signals"] } fluent = { workspace = true } bytecount = { workspace = true, features = ["runtime-dispatch-simd"] } +[target.'cfg(unix)'.dependencies] +rustix = { workspace = true, features = ["stdio", "fs"] } + [dev-dependencies] divan = { workspace = true } -rustix = { workspace = true, features = ["stdio", "fs"] } +tempfile = { workspace = true } uucore = { workspace = true, features = ["benchmark"] } [[bin]] diff --git a/src/uu/tr/src/tr.rs b/src/uu/tr/src/tr.rs index 58976d3ac06..291ea7f9a88 100644 --- a/src/uu/tr/src/tr.rs +++ b/src/uu/tr/src/tr.rs @@ -97,6 +97,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } let stdin = stdin(); + #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] + let _ = rustix::fs::fadvise(&stdin, 0, None, rustix::fs::Advice::Sequential); let mut locked_stdin = stdin.lock(); let mut locked_stdout = stdout().lock(); diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index 066474fc985..7117f493af6 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -5,10 +5,15 @@ // spell-checker:ignore aabbaa aabbcc aabc abbb abbbcddd abcc abcdefabcdef abcdefghijk abcdefghijklmn abcdefghijklmnop ABCDEFGHIJKLMNOPQRS abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFZZ abcxyz ABCXYZ abcxyzabcxyz ABCXYZABCXYZ acbdef alnum amzamz AMZXAMZ bbbd cclass cefgm cntrl compl dabcdef dncase fooclass Gzabcdefg PQRST upcase wxyzz xdigit XXXYYY xycde xyyye xyyz xyzzzzxyzzzz ZABCDEF Zamz Cdefghijkl Cdefghijklmn asdfqqwweerr qwerr asdfqwer qwer aassddffqwer asdfqwer use uutests::at_and_ucmd; use uutests::new_ucmd; +#[cfg(target_os = "linux")] +use uutests::util::get_tests_binary; #[cfg(unix)] use std::{ffi::OsStr, os::unix::ffi::OsStrExt}; +#[cfg(target_os = "linux")] +use std::process::Command; + #[test] fn test_invalid_arg() { new_ucmd!().arg("--definitely-invalid").fails_with_code(1); @@ -1628,6 +1633,36 @@ fn test_octal_escape_ambiguous_followed_by_non_utf8() { .stderr_contains("warning: invalid utf8 sequence"); } +#[test] +#[cfg(target_os = "linux")] +#[cfg_attr(wasi_runner, ignore = "WASI: strace cannot trace WebAssembly")] +fn test_sequential_fadvise() { + let (at, _ucmd) = at_and_ucmd!(); + at.write("input", "abc"); + + let trace_file = at.plus_as_string("strace.out"); + let result = Command::new("strace") + .args(["-o", &trace_file, "-e", "fadvise64,fadvise64_64"]) + .arg(get_tests_binary()) + .args(["tr", "a", "b"]) + .stdin(std::fs::File::open(at.plus("input")).unwrap()) + .output(); + + if result.is_err() { + return; // strace not available + } + + let trace = at.read("strace.out"); + let advised_stdin = trace.lines().any(|line| { + (line.starts_with("fadvise64(0,") || line.starts_with("fadvise64_64(0,")) + && line.contains("POSIX_FADV_SEQUENTIAL") // spell-checker:disable-line + }); + assert!( + advised_stdin, + "Expected sequential fadvise on stdin: {trace}" + ); +} + #[cfg(target_os = "linux")] #[test] fn test_failed_write_is_reported() {