Skip to content

Commit e6747d9

Browse files
committed
Detect infinite r/w loop on unix systems
1 parent e4df564 commit e6747d9

File tree

4 files changed

+67
-18
lines changed

4 files changed

+67
-18
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ serde = { version = "1.0", features = ["derive"] }
5151
serde_yaml = "0.8"
5252
semver = "0.10"
5353
path_abs = { version = "0.5", default-features = false }
54+
nix = "0.18.0"
5455

5556
[dependencies.git2]
5657
version = "0.13"

src/controller.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,28 @@ impl<'b> Controller<'b> {
6363
}
6464

6565
let attached_to_pager = output_type.is_pager();
66+
67+
#[cfg(not(target_os = "windows"))]
68+
if !attached_to_pager {
69+
use nix::sys::stat::fstat;
70+
use std::os::unix::io::AsRawFd;
71+
// Check whether any of the inputs is the same as the output (i.e. bat a b c > b)
72+
// to avoid an infinite loop.
73+
// We cannot detect this, if the output is piped into less (i.e. bat a b c | less > b).
74+
let out_stats = fstat(io::stdout().as_raw_fd()).unwrap();
75+
let out_dev = out_stats.st_dev;
76+
let out_ino = out_stats.st_ino;
77+
78+
if inputs
79+
.iter()
80+
.flat_map(|input| input.kind.stat())
81+
.any(|stats| stats.st_dev == out_dev && stats.st_ino == out_ino)
82+
{
83+
eprintln!("Duplicate files detected!");
84+
return Ok(false);
85+
}
86+
}
87+
6688
let writer = output_type.handle()?;
6789
let mut no_errors: bool = true;
6890

src/input.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use std::ffi::{OsStr, OsString};
22
use std::fs::File;
33
use std::io::{self, BufRead, BufReader, Read};
4+
#[cfg(not(target_os = "windows"))]
5+
use std::os::unix::io::AsRawFd;
46

57
use content_inspector::{self, ContentType};
8+
#[cfg(not(target_os = "windows"))]
9+
use nix::sys::stat::{fstat, stat, FileStat};
610

711
use crate::error::*;
812

@@ -80,6 +84,15 @@ impl<'a> InputKind<'a> {
8084
InputKind::CustomReader(_) => InputDescription::new("READER"),
8185
}
8286
}
87+
88+
#[cfg(not(target_os = "windows"))]
89+
pub fn stat(&self) -> Option<FileStat> {
90+
match self {
91+
InputKind::OrdinaryFile(ref path) => stat(path.as_os_str()).ok(),
92+
InputKind::StdIn => fstat(io::stdin().as_raw_fd()).ok(),
93+
InputKind::CustomReader(_) => None,
94+
}
95+
}
8396
}
8497

8598
#[derive(Clone, Default)]

0 commit comments

Comments
 (0)