Skip to content

Commit 569afcc

Browse files
authored
Merge pull request #7260 from jfinkels/cat-error-append-same-file
cat: error when output is input and appending
2 parents e0a7c31 + 9a88526 commit 569afcc

File tree

2 files changed

+60
-13
lines changed

2 files changed

+60
-13
lines changed

src/uu/cat/src/cat.rs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,31 @@
44
// file that was distributed with this source code.
55

66
// spell-checker:ignore (ToDO) nonprint nonblank nonprinting ELOOP
7-
use clap::{crate_version, Arg, ArgAction, Command};
87
use std::fs::{metadata, File};
98
use std::io::{self, IsTerminal, Read, Write};
10-
use thiserror::Error;
11-
use uucore::display::Quotable;
12-
use uucore::error::UResult;
13-
use uucore::fs::FileInformation;
14-
15-
#[cfg(unix)]
16-
use std::os::fd::{AsFd, AsRawFd};
17-
18-
/// Linux splice support
19-
#[cfg(any(target_os = "linux", target_os = "android"))]
20-
mod splice;
21-
229
/// Unix domain socket support
2310
#[cfg(unix)]
2411
use std::net::Shutdown;
2512
#[cfg(unix)]
13+
use std::os::fd::{AsFd, AsRawFd};
14+
#[cfg(unix)]
2615
use std::os::unix::fs::FileTypeExt;
2716
#[cfg(unix)]
2817
use std::os::unix::net::UnixStream;
18+
19+
use clap::{crate_version, Arg, ArgAction, Command};
20+
#[cfg(unix)]
21+
use nix::fcntl::{fcntl, FcntlArg};
22+
use thiserror::Error;
23+
use uucore::display::Quotable;
24+
use uucore::error::UResult;
25+
use uucore::fs::FileInformation;
2926
use uucore::{format_usage, help_about, help_usage};
3027

28+
/// Linux splice support
29+
#[cfg(any(target_os = "linux", target_os = "android"))]
30+
mod splice;
31+
3132
const USAGE: &str = help_usage!("cat.md");
3233
const ABOUT: &str = help_about!("cat.md");
3334

@@ -322,6 +323,24 @@ fn cat_handle<R: FdReadable>(
322323
}
323324
}
324325

326+
/// Whether this process is appending to stdout.
327+
#[cfg(unix)]
328+
fn is_appending() -> bool {
329+
let stdout = std::io::stdout();
330+
let flags = match fcntl(stdout.as_raw_fd(), FcntlArg::F_GETFL) {
331+
Ok(flags) => flags,
332+
Err(_) => return false,
333+
};
334+
// TODO Replace `1 << 10` with `nix::fcntl::Oflag::O_APPEND`.
335+
let o_append = 1 << 10;
336+
(flags & o_append) > 0
337+
}
338+
339+
#[cfg(not(unix))]
340+
fn is_appending() -> bool {
341+
false
342+
}
343+
325344
fn cat_path(
326345
path: &str,
327346
options: &OutputOptions,
@@ -331,10 +350,16 @@ fn cat_path(
331350
match get_input_type(path)? {
332351
InputType::StdIn => {
333352
let stdin = io::stdin();
353+
let in_info = FileInformation::from_file(&stdin)?;
334354
let mut handle = InputHandle {
335355
reader: stdin,
336356
is_interactive: std::io::stdin().is_terminal(),
337357
};
358+
if let Some(out_info) = out_info {
359+
if in_info == *out_info && is_appending() {
360+
return Err(CatError::OutputIsInput);
361+
}
362+
}
338363
cat_handle(&mut handle, options, state)
339364
}
340365
InputType::Directory => Err(CatError::IsDirectory),

tests/by-util/test_cat.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crate::common::util::vec_of_size;
99
use crate::common::util::TestScenario;
1010
#[cfg(any(target_os = "linux", target_os = "android"))]
1111
use rlimit::Resource;
12+
#[cfg(target_os = "linux")]
13+
use std::fs::File;
1214
use std::fs::OpenOptions;
1315
#[cfg(not(windows))]
1416
use std::process::Stdio;
@@ -646,3 +648,23 @@ fn test_u_ignored() {
646648
.stdout_only("hello");
647649
}
648650
}
651+
652+
#[test]
653+
#[cfg(target_os = "linux")]
654+
fn test_appending_same_input_output() {
655+
let (at, mut ucmd) = at_and_ucmd!();
656+
657+
at.write("foo", "content");
658+
let foo_file = at.plus_as_string("foo");
659+
660+
let file_read = File::open(&foo_file).unwrap();
661+
let file_write = OpenOptions::new().append(true).open(&foo_file).unwrap();
662+
663+
ucmd.set_stdin(file_read);
664+
ucmd.set_stdout(file_write);
665+
666+
ucmd.run()
667+
.failure()
668+
.no_stdout()
669+
.stderr_contains("input file is output file");
670+
}

0 commit comments

Comments
 (0)