mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
head: Fixed TOCTOU bug in checking of metadata (#12439)
* head: Fixed TOCTOU bug in checking of metadata * Fixed missing import and qualification for windows
This commit is contained in:
+52
-27
@@ -13,7 +13,9 @@ use std::io::{self, BufWriter, Read, Seek, SeekFrom, Write};
|
||||
use std::num::TryFromIntError;
|
||||
#[cfg(unix)]
|
||||
use std::os::fd::AsFd;
|
||||
use std::path::{Path, PathBuf};
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use thiserror::Error;
|
||||
use uucore::display::{Quotable, print_verbatim};
|
||||
use uucore::error::{FromIo, UError, UResult, USimpleError};
|
||||
@@ -444,11 +446,56 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
// Stat the path first so we know whether to print the header.
|
||||
// When 0 bytes or 0 lines are requested, there is nothing to
|
||||
// read, so we should succeed on directories just like GNU head
|
||||
// does. Skip opening the file entirely in that case.
|
||||
let zero_output = matches!(options.mode, Mode::FirstBytes(0) | Mode::FirstLines(0));
|
||||
|
||||
// GNU head prints "==> name <==" for existing files and
|
||||
// directories, but NOT for nonexistent ones — those produce
|
||||
// only an error message.
|
||||
let metadata = match Path::new(file).metadata() {
|
||||
let mut print_header = || -> UResult<()> {
|
||||
if (options.files.len() > 1 && !options.quiet) || options.verbose {
|
||||
if !first {
|
||||
writeln!(stdout)?;
|
||||
}
|
||||
write!(stdout, "==> ")?;
|
||||
print_verbatim(file).unwrap();
|
||||
writeln!(stdout, " <==")?;
|
||||
first = false;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut file_handle = match File::open(file) {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
#[cfg(windows)]
|
||||
// On Windows, `File::open` on a directory fails with "Permission denied").
|
||||
if err.kind() == io::ErrorKind::PermissionDenied {
|
||||
if let Ok(m) = Path::new(file).metadata() {
|
||||
if m.is_dir() {
|
||||
// We need to print the header, as we have an existing directory
|
||||
print_header()?;
|
||||
if !zero_output {
|
||||
show!(USimpleError::new(
|
||||
1,
|
||||
translate!("head-error-reading-file", "name" => file.quote(), "err" => "Is a directory")
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
show!(err.map_err_context(
|
||||
|| translate!("head-error-cannot-open", "name" => file.quote())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let metadata = match file_handle.metadata() {
|
||||
Ok(m) => m,
|
||||
Err(err) => {
|
||||
show!(err.map_err_context(
|
||||
@@ -457,21 +504,8 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if (options.files.len() > 1 && !options.quiet) || options.verbose {
|
||||
if !first {
|
||||
writeln!(stdout)?;
|
||||
}
|
||||
write!(stdout, "==> ")?;
|
||||
print_verbatim(file).unwrap();
|
||||
writeln!(stdout, " <==")?;
|
||||
first = false;
|
||||
}
|
||||
// When 0 bytes or 0 lines are requested, there is nothing to
|
||||
// read, so we should succeed on directories just like GNU head
|
||||
// does. Skip opening the file entirely in that case (also
|
||||
// avoids platform differences: on Windows, `File::open` on a
|
||||
// directory fails with "Permission denied").
|
||||
let zero_output = matches!(options.mode, Mode::FirstBytes(0) | Mode::FirstLines(0));
|
||||
|
||||
print_header()?;
|
||||
if metadata.is_dir() {
|
||||
if !zero_output {
|
||||
show!(USimpleError::new(
|
||||
@@ -481,15 +515,6 @@ fn uu_head(options: &HeadOptions) -> UResult<()> {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let mut file_handle = match File::open(file) {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
show!(err.map_err_context(
|
||||
|| translate!("head-error-cannot-open", "name" => file.quote())
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
head_file(&mut file_handle, options)?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user