Skip to content

Commit ffc61cc

Browse files
committed
tac: use temp file for stdin to respect TMPDIR and handle disk-full errors
1 parent 130f780 commit ffc61cc

6 files changed

Lines changed: 89 additions & 74 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fuzz/Cargo.lock

Lines changed: 49 additions & 66 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/tac/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ clap = { workspace = true }
2727
uucore = { workspace = true }
2828
thiserror = { workspace = true }
2929
fluent = { workspace = true }
30+
tempfile = { workspace = true }
3031

3132
[[bin]]
3233
name = "tac"

src/uu/tac/src/tac.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ use clap::{Arg, ArgAction, Command};
1010
use memchr::memmem;
1111
use memmap2::Mmap;
1212
use std::ffi::OsString;
13-
use std::io::{BufWriter, Read, Write, stdin, stdout};
13+
use std::io::{BufWriter, Read, Seek, Write, stdin, stdout};
1414
use std::{
1515
fs::{File, read},
16+
io::copy,
1617
path::Path,
1718
};
1819
use uucore::error::UError;
@@ -241,14 +242,18 @@ fn tac(filenames: &[OsString], before: bool, regex: bool, separator: &str) -> UR
241242
mmap = mmap1;
242243
&mmap
243244
} else {
244-
let mut buf1 = Vec::new();
245-
if let Err(e) = stdin().read_to_end(&mut buf1) {
246-
let e: Box<dyn UError> = TacError::ReadError(OsString::from("stdin"), e).into();
247-
show!(e);
248-
continue;
245+
// Copy stdin to a temp file (respects TMPDIR), then read it back.
246+
// This allows proper error handling when disk space is exhausted.
247+
match read_stdin_to_buf() {
248+
Ok(buf1) => {
249+
buf = buf1;
250+
&buf
251+
}
252+
Err(e) => {
253+
show!(TacError::ReadError(OsString::from("stdin"), e));
254+
continue;
255+
}
249256
}
250-
buf = buf1;
251-
&buf
252257
}
253258
} else {
254259
let path = Path::new(filename);
@@ -304,6 +309,16 @@ fn try_mmap_stdin() -> Option<Mmap> {
304309
unsafe { Mmap::map(&stdin()).ok() }
305310
}
306311

312+
/// Copy stdin to a temp file, then read it into a buffer.
313+
fn read_stdin_to_buf() -> std::io::Result<Vec<u8>> {
314+
let mut tmp = tempfile::tempfile()?;
315+
copy(&mut stdin(), &mut tmp)?;
316+
tmp.rewind()?;
317+
let mut buf = Vec::new();
318+
tmp.read_to_end(&mut buf)?;
319+
Ok(buf)
320+
}
321+
307322
fn try_mmap_path(path: &Path) -> Option<Mmap> {
308323
let file = File::open(path).ok()?;
309324

0 commit comments

Comments
 (0)