Merge pull request #8314 from bvinc/tail_zero
Some checks are pending
CICD / Style/cargo-deny (push) Waiting to run
CICD / MinRustV (push) Waiting to run
CICD / Dependencies (push) Waiting to run
CICD / Tests/BusyBox test suite (push) Blocked by required conditions
CICD / Tests/Toybox test suite (push) Blocked by required conditions
CICD / Code Coverage (push) Waiting to run
CICD / Separate Builds (push) Waiting to run
CICD / Test all features separately (push) Blocked by required conditions
CICD / Build/SELinux (push) Blocked by required conditions
CICD / Build (push) Blocked by required conditions
CICD / Style/deps (push) Waiting to run
CICD / Documentation/warnings (push) Waiting to run
CICD / Build/Makefile (push) Blocked by required conditions
CICD / Build/stable (push) Blocked by required conditions
CICD / Build/nightly (push) Blocked by required conditions
CICD / Binary sizes (push) Blocked by required conditions
GnuTests / Run GNU tests (push) Waiting to run
Android / Test builds (push) Waiting to run
Code Quality / Style/format (push) Waiting to run
Code Quality / Style/lint (push) Waiting to run
Code Quality / Style/spelling (push) Waiting to run
Code Quality / Style/toml (push) Waiting to run
Code Quality / Style/Python (push) Waiting to run
Code Quality / Pre-commit hooks (push) Waiting to run
FreeBSD / Style and Lint (push) Waiting to run
FreeBSD / Tests (push) Waiting to run

tail: fix tailing /dev/zero
This commit is contained in:
Daniel Hofstetter 2025-07-07 10:30:34 +02:00 committed by GitHub
commit b9a790b476
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 33 additions and 23 deletions

View file

@ -23,7 +23,7 @@ clap = { workspace = true }
libc = { workspace = true } libc = { workspace = true }
memchr = { workspace = true } memchr = { workspace = true }
notify = { workspace = true } notify = { workspace = true }
uucore = { workspace = true, features = ["parser"] } uucore = { workspace = true, features = ["fs", "parser"] }
same-file = { workspace = true } same-file = { workspace = true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]

View file

@ -158,7 +158,6 @@ impl FileExtTail for File {
pub trait MetadataExtTail { pub trait MetadataExtTail {
fn is_tailable(&self) -> bool; fn is_tailable(&self) -> bool;
fn got_truncated(&self, other: &Metadata) -> UResult<bool>; fn got_truncated(&self, other: &Metadata) -> UResult<bool>;
fn get_block_size(&self) -> u64;
fn file_id_eq(&self, other: &Metadata) -> bool; fn file_id_eq(&self, other: &Metadata) -> bool;
} }
@ -180,17 +179,6 @@ impl MetadataExtTail for Metadata {
Ok(other.len() < self.len() && other.modified()? != self.modified()?) Ok(other.len() < self.len() && other.modified()? != self.modified()?)
} }
fn get_block_size(&self) -> u64 {
#[cfg(unix)]
{
self.blocks()
}
#[cfg(not(unix))]
{
self.len()
}
}
fn file_id_eq(&self, _other: &Metadata) -> bool { fn file_id_eq(&self, _other: &Metadata) -> bool {
#[cfg(unix)] #[cfg(unix)]
{ {

View file

@ -26,7 +26,7 @@ use args::{FilterMode, Settings, Signum, parse_args};
use chunks::ReverseChunks; use chunks::ReverseChunks;
use follow::Observer; use follow::Observer;
use memchr::{memchr_iter, memrchr_iter}; use memchr::{memchr_iter, memrchr_iter};
use paths::{FileExtTail, HeaderPrinter, Input, InputKind, MetadataExtTail}; use paths::{FileExtTail, HeaderPrinter, Input, InputKind};
use same_file::Handle; use same_file::Handle;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
@ -169,14 +169,15 @@ fn tail_file(
} }
observer.add_bad_path(path, input.display_name.as_str(), false)?; observer.add_bad_path(path, input.display_name.as_str(), false)?;
} else if input.is_tailable() { } else if input.is_tailable() {
let metadata = path.metadata().ok();
match File::open(path) { match File::open(path) {
Ok(mut file) => { Ok(mut file) => {
let st = file.metadata()?;
let blksize_limit = uucore::fs::sane_blksize::sane_blksize_from_metadata(&st);
header_printer.print_input(input); header_printer.print_input(input);
let mut reader; let mut reader;
if !settings.presume_input_pipe if !settings.presume_input_pipe
&& file.is_seekable(if input.is_stdin() { offset } else { 0 }) && file.is_seekable(if input.is_stdin() { offset } else { 0 })
&& metadata.as_ref().unwrap().get_block_size() > 0 && (!st.is_file() || st.len() > blksize_limit)
{ {
bounded_tail(&mut file, settings); bounded_tail(&mut file, settings);
reader = BufReader::new(file); reader = BufReader::new(file);
@ -448,6 +449,7 @@ fn backwards_thru_file(file: &mut File, num_delimiters: u64, delimiter: u8) {
/// being a nice performance win for very large files. /// being a nice performance win for very large files.
fn bounded_tail(file: &mut File, settings: &Settings) { fn bounded_tail(file: &mut File, settings: &Settings) {
debug_assert!(!settings.presume_input_pipe); debug_assert!(!settings.presume_input_pipe);
let mut limit = None;
// Find the position in the file to start printing from. // Find the position in the file to start printing from.
match &settings.mode { match &settings.mode {
@ -462,9 +464,8 @@ fn bounded_tail(file: &mut File, settings: &Settings) {
return; return;
} }
FilterMode::Bytes(Signum::Negative(count)) => { FilterMode::Bytes(Signum::Negative(count)) => {
let len = file.seek(SeekFrom::End(0)).unwrap(); file.seek(SeekFrom::End(-(*count as i64))).unwrap();
file.seek(SeekFrom::End(-((*count).min(len) as i64))) limit = Some(*count);
.unwrap();
} }
FilterMode::Bytes(Signum::Positive(count)) if count > &1 => { FilterMode::Bytes(Signum::Positive(count)) if count > &1 => {
// GNU `tail` seems to index bytes and lines starting at 1, not // GNU `tail` seems to index bytes and lines starting at 1, not
@ -477,10 +478,7 @@ fn bounded_tail(file: &mut File, settings: &Settings) {
_ => {} _ => {}
} }
// Print the target section of the file. print_target_section(file, limit);
let stdout = stdout();
let mut stdout = stdout.lock();
io::copy(file, &mut stdout).unwrap();
} }
fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> { fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UResult<()> {
@ -547,6 +545,21 @@ fn unbounded_tail<T: Read>(reader: &mut BufReader<T>, settings: &Settings) -> UR
Ok(()) Ok(())
} }
fn print_target_section<R>(file: &mut R, limit: Option<u64>)
where
R: Read + ?Sized,
{
// Print the target section of the file.
let stdout = stdout();
let mut stdout = stdout.lock();
if let Some(limit) = limit {
let mut reader = file.take(limit);
io::copy(&mut reader, &mut stdout).unwrap();
} else {
io::copy(file, &mut stdout).unwrap();
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -4923,3 +4923,12 @@ fn test_failed_write_is_reported() {
.fails() .fails()
.stderr_is("tail: No space left on device\n"); .stderr_is("tail: No space left on device\n");
} }
#[test]
#[cfg(target_os = "linux")]
fn test_dev_zero() {
new_ucmd!()
.args(&["-c", "1", "/dev/zero"])
.succeeds()
.stdout_only("\0");
}