mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 07:12:44 +00:00
stat: improve handling of stdin/fifo (fix #3485)
* fix https://github.com/uutils/coreutils/issues/3485 * improve the workaround from #3280 * add tests
This commit is contained in:
parent
f5ffa94129
commit
79e5d80e3e
3 changed files with 78 additions and 42 deletions
|
@ -19,7 +19,9 @@ use uucore::{entries, format_usage};
|
||||||
use clap::{crate_version, Arg, ArgMatches, Command};
|
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
|
use std::ffi::{OsStr, OsString};
|
||||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||||
|
use std::os::unix::prelude::OsStrExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{cmp, fs, iter};
|
use std::{cmp, fs, iter};
|
||||||
|
|
||||||
|
@ -221,7 +223,7 @@ pub struct Stater {
|
||||||
follow: bool,
|
follow: bool,
|
||||||
show_fs: bool,
|
show_fs: bool,
|
||||||
from_user: bool,
|
from_user: bool,
|
||||||
files: Vec<String>,
|
files: Vec<OsString>,
|
||||||
mount_list: Option<Vec<String>>,
|
mount_list: Option<Vec<String>>,
|
||||||
default_tokens: Vec<Token>,
|
default_tokens: Vec<Token>,
|
||||||
default_dev_tokens: Vec<Token>,
|
default_dev_tokens: Vec<Token>,
|
||||||
|
@ -471,24 +473,10 @@ impl Stater {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(matches: &ArgMatches) -> UResult<Self> {
|
fn new(matches: &ArgMatches) -> UResult<Self> {
|
||||||
let mut files: Vec<String> = matches
|
let files = matches
|
||||||
.values_of(ARG_FILES)
|
.values_of_os(ARG_FILES)
|
||||||
.map(|v| v.map(ToString::to_string).collect())
|
.map(|v| v.map(OsString::from).collect())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
#[cfg(unix)]
|
|
||||||
if files.contains(&String::from("-")) {
|
|
||||||
let redirected_path = Path::new("/dev/stdin")
|
|
||||||
.canonicalize()
|
|
||||||
.expect("unable to canonicalize /dev/stdin")
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap();
|
|
||||||
for file in &mut files {
|
|
||||||
if file == "-" {
|
|
||||||
*file = redirected_path.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let format_str = if matches.is_present(options::PRINTF) {
|
let format_str = if matches.is_present(options::PRINTF) {
|
||||||
matches
|
matches
|
||||||
.value_of(options::PRINTF)
|
.value_of(options::PRINTF)
|
||||||
|
@ -550,19 +538,37 @@ impl Stater {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(&self) -> i32 {
|
fn exec(&self) -> i32 {
|
||||||
|
let mut stdin_is_fifo = false;
|
||||||
|
if cfg!(unix) {
|
||||||
|
if let Ok(md) = fs::metadata("/dev/stdin") {
|
||||||
|
stdin_is_fifo = md.file_type().is_fifo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut ret = 0;
|
let mut ret = 0;
|
||||||
for f in &self.files {
|
for f in &self.files {
|
||||||
ret |= self.do_stat(f.as_str());
|
ret |= self.do_stat(f, stdin_is_fifo);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_stat(&self, file: &str) -> i32 {
|
fn do_stat(&self, file: &OsStr, stdin_is_fifo: bool) -> i32 {
|
||||||
if !self.show_fs {
|
let display_name = file.to_string_lossy();
|
||||||
let result = if self.follow {
|
let file: OsString = if cfg!(unix) && display_name.eq("-") {
|
||||||
fs::metadata(file)
|
if let Ok(p) = Path::new("/dev/stdin").canonicalize() {
|
||||||
|
p.into_os_string()
|
||||||
} else {
|
} else {
|
||||||
fs::symlink_metadata(file)
|
OsString::from("/dev/stdin")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
OsString::from(file)
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.show_fs {
|
||||||
|
let result = if self.follow || stdin_is_fifo && display_name.eq("-") {
|
||||||
|
fs::metadata(&file)
|
||||||
|
} else {
|
||||||
|
fs::symlink_metadata(&file)
|
||||||
};
|
};
|
||||||
match result {
|
match result {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
|
@ -658,28 +664,32 @@ impl Stater {
|
||||||
|
|
||||||
// mount point
|
// mount point
|
||||||
'm' => {
|
'm' => {
|
||||||
arg = self.find_mount_point(file).unwrap();
|
arg = self.find_mount_point(&file).unwrap();
|
||||||
output_type = OutputType::Str;
|
output_type = OutputType::Str;
|
||||||
}
|
}
|
||||||
|
|
||||||
// file name
|
// file name
|
||||||
'n' => {
|
'n' => {
|
||||||
arg = file.to_owned();
|
arg = display_name.to_string();
|
||||||
output_type = OutputType::Str;
|
output_type = OutputType::Str;
|
||||||
}
|
}
|
||||||
// quoted file name with dereference if symbolic link
|
// quoted file name with dereference if symbolic link
|
||||||
'N' => {
|
'N' => {
|
||||||
if file_type.is_symlink() {
|
if file_type.is_symlink() {
|
||||||
let dst = match fs::read_link(file) {
|
let dst = match fs::read_link(&file) {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
arg = format!("{} -> {}", file.quote(), dst.quote());
|
arg = format!(
|
||||||
|
"{} -> {}",
|
||||||
|
display_name.quote(),
|
||||||
|
dst.quote()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
arg = file.to_string();
|
arg = display_name.to_string();
|
||||||
}
|
}
|
||||||
output_type = OutputType::Str;
|
output_type = OutputType::Str;
|
||||||
}
|
}
|
||||||
|
@ -771,12 +781,16 @@ impl Stater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_error!("cannot stat {}: {}", file.quote(), e);
|
show_error!("cannot stat {}: {}", display_name.quote(), e);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match statfs(file) {
|
#[cfg(unix)]
|
||||||
|
let p = file.as_bytes();
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let p = file.into_string().unwrap();
|
||||||
|
match statfs(p) {
|
||||||
Ok(meta) => {
|
Ok(meta) => {
|
||||||
let tokens = &self.default_tokens;
|
let tokens = &self.default_tokens;
|
||||||
|
|
||||||
|
@ -829,7 +843,7 @@ impl Stater {
|
||||||
}
|
}
|
||||||
// file name
|
// file name
|
||||||
'n' => {
|
'n' => {
|
||||||
arg = file.to_owned();
|
arg = display_name.to_string();
|
||||||
output_type = OutputType::Str;
|
output_type = OutputType::Str;
|
||||||
}
|
}
|
||||||
// block size (for faster transfers)
|
// block size (for faster transfers)
|
||||||
|
@ -866,7 +880,7 @@ impl Stater {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
show_error!(
|
show_error!(
|
||||||
"cannot read file system information for {}: {}",
|
"cannot read file system information for {}: {}",
|
||||||
file.quote(),
|
display_name.quote(),
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -1028,6 +1042,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
Arg::new(ARG_FILES)
|
Arg::new(ARG_FILES)
|
||||||
.multiple_occurrences(true)
|
.multiple_occurrences(true)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.allow_invalid_utf8(true)
|
||||||
.min_values(1),
|
.min_values(1),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ use std::ffi::CString;
|
||||||
use std::io::Error as IOError;
|
use std::io::Error as IOError;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
#[cfg(not(unix))]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
@ -708,9 +709,9 @@ impl FsMeta for StatFs {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn statfs<P: AsRef<Path>>(path: P) -> Result<StatFs, String>
|
pub fn statfs<P>(path: P) -> Result<StatFs, String>
|
||||||
where
|
where
|
||||||
Vec<u8>: From<P>,
|
P: Into<Vec<u8>>,
|
||||||
{
|
{
|
||||||
match CString::new(path) {
|
match CString::new(path) {
|
||||||
Ok(p) => {
|
Ok(p) => {
|
||||||
|
|
|
@ -312,9 +312,21 @@ fn test_printf() {
|
||||||
ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout);
|
ts.ucmd().args(&args).succeeds().stdout_is(expected_stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(disable_until_fixed)]
|
#[cfg(unix)]
|
||||||
|
fn test_pipe_fifo() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
at.mkfifo("FIFO");
|
||||||
|
ucmd.arg("FIFO")
|
||||||
|
.run()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_contains("fifo")
|
||||||
|
.stdout_contains("File: FIFO")
|
||||||
|
.succeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
fn test_stdin_pipe_fifo1() {
|
fn test_stdin_pipe_fifo1() {
|
||||||
// $ echo | stat -
|
// $ echo | stat -
|
||||||
// File: -
|
// File: -
|
||||||
|
@ -328,17 +340,26 @@ fn test_stdin_pipe_fifo1() {
|
||||||
.stdout_contains("fifo")
|
.stdout_contains("fifo")
|
||||||
.stdout_contains("File: -")
|
.stdout_contains("File: -")
|
||||||
.succeeded();
|
.succeeded();
|
||||||
|
|
||||||
|
new_ucmd!()
|
||||||
|
.args(&["-L", "-"])
|
||||||
|
.set_stdin(std::process::Stdio::piped())
|
||||||
|
.run()
|
||||||
|
.no_stderr()
|
||||||
|
.stdout_contains("fifo")
|
||||||
|
.stdout_contains("File: -")
|
||||||
|
.succeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(disable_until_fixed)]
|
#[cfg(target_os = "linux")]
|
||||||
fn test_stdin_pipe_fifo2() {
|
fn test_stdin_pipe_fifo2() {
|
||||||
// $ stat -
|
// $ stat -
|
||||||
// File: -
|
// File: -
|
||||||
// Size: 0 Blocks: 0 IO Block: 1024 character special file
|
// Size: 0 Blocks: 0 IO Block: 1024 character special file
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg("-")
|
.arg("-")
|
||||||
|
.set_stdin(std::process::Stdio::null())
|
||||||
.run()
|
.run()
|
||||||
.no_stderr()
|
.no_stderr()
|
||||||
.stdout_contains("character special file")
|
.stdout_contains("character special file")
|
||||||
|
@ -346,9 +367,8 @@ fn test_stdin_pipe_fifo2() {
|
||||||
.succeeded();
|
.succeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(disable_until_fixed)]
|
#[cfg(target_os = "linux")]
|
||||||
fn test_stdin_redirect() {
|
fn test_stdin_redirect() {
|
||||||
// $ touch f && stat - < f
|
// $ touch f && stat - < f
|
||||||
// File: -
|
// File: -
|
||||||
|
@ -359,7 +379,7 @@ fn test_stdin_redirect() {
|
||||||
at.touch("f");
|
at.touch("f");
|
||||||
new_ucmd!()
|
new_ucmd!()
|
||||||
.arg("-")
|
.arg("-")
|
||||||
.set_stdin(std::fs::File::open("f").unwrap())
|
.set_stdin(std::fs::File::open(at.plus("f")).unwrap())
|
||||||
.run()
|
.run()
|
||||||
.no_stderr()
|
.no_stderr()
|
||||||
.stdout_contains("regular empty file")
|
.stdout_contains("regular empty file")
|
||||||
|
|
Loading…
Reference in a new issue