From 4bbf708c8188a6018c1712e4a966900372e92a22 Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Thu, 26 May 2022 12:53:50 +0200 Subject: [PATCH] tail: fix handling of PermissionDenied Error * add tests for opening unreadable files --- src/uu/tail/src/tail.rs | 78 ++++++++++++++++++++++---------------- tests/by-util/test_tail.rs | 40 +++++++++++++++++++ 2 files changed, 86 insertions(+), 32 deletions(-) diff --git a/src/uu/tail/src/tail.rs b/src/uu/tail/src/tail.rs index 18b53c662..c4e9d5aef 100644 --- a/src/uu/tail/src/tail.rs +++ b/src/uu/tail/src/tail.rs @@ -383,30 +383,46 @@ fn uu_tail(mut settings: Settings) -> UResult<()> { } } } else if path.is_tailable() { - if settings.verbose { - files.print_header(&path, !first_header); - first_header = false; - } - let mut file = File::open(&path).unwrap(); - let mut reader; + match File::open(&path) { + Ok(mut file) => { + if settings.verbose { + files.print_header(&path, !first_header); + first_header = false; + } + let mut reader; - if is_seekable(&mut file) && get_block_size(md.as_ref().unwrap()) > 0 { - bounded_tail(&mut file, &settings); - reader = BufReader::new(file); - } else { - reader = BufReader::new(file); - unbounded_tail(&mut reader, &settings)?; - } - if settings.follow.is_some() { - // Insert existing/file `path` into `files.map`. - files.insert( - path.canonicalize().unwrap(), - PathData { - reader: Some(Box::new(reader)), - metadata: md, - display_name, - }, - ); + if is_seekable(&mut file) && get_block_size(md.as_ref().unwrap()) > 0 { + bounded_tail(&mut file, &settings); + reader = BufReader::new(file); + } else { + reader = BufReader::new(file); + unbounded_tail(&mut reader, &settings)?; + } + if settings.follow.is_some() { + // Insert existing/file `path` into `files.map`. + files.insert( + path.canonicalize().unwrap(), + PathData { + reader: Some(Box::new(reader)), + metadata: md, + display_name, + }, + ); + } + } + Err(e) if e.kind() == std::io::ErrorKind::PermissionDenied => { + settings.return_code = 1; + show_error!( + "cannot open {} for reading: Permission denied", + display_name.quote() + ); + } + Err(e) => { + return Err(USimpleError::new( + 1, + format!("{}: {}", display_name.quote(), e), + )); + } } } else if settings.retry && settings.follow.is_some() { if path.is_relative() { @@ -931,15 +947,13 @@ fn handle_event( ); } } - if event_path.is_orphan() { - if !orphans.contains(event_path) { - show_error!("directory containing watched file was removed"); - show_error!( - "{} cannot be used, reverting to polling", - text::BACKEND - ); - orphans.push(event_path.to_path_buf()); - } + if event_path.is_orphan() && !orphans.contains(event_path) { + show_error!("directory containing watched file was removed"); + show_error!( + "{} cannot be used, reverting to polling", + text::BACKEND + ); + orphans.push(event_path.to_path_buf()); } let _ = watcher.unwatch(event_path); } else { diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index 39874bcd2..bc79bea99 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -97,6 +97,46 @@ fn test_stdin_redirect_file() { assert!(buf_stderr.is_empty()); } +#[test] +#[cfg(not(target_os = "windows"))] +#[cfg(feature = "chmod")] +fn test_permission_denied() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.touch("unreadable"); + ts.ccmd("chmod").arg("0").arg("unreadable").succeeds(); + + ts.ucmd() + .set_stdin(Stdio::null()) + .arg("unreadable") + .fails() + .stderr_is("tail: cannot open 'unreadable' for reading: Permission denied\n") + .no_stdout() + .code_is(1); +} + +#[test] +#[cfg(not(target_os = "windows"))] +#[cfg(feature = "chmod")] +fn test_permission_denied_multiple() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.touch("file1"); + at.touch("file2"); + at.touch("unreadable"); + ts.ccmd("chmod").arg("0").arg("unreadable").succeeds(); + + ts.ucmd() + .set_stdin(Stdio::null()) + .args(&["file1", "unreadable", "file2"]) + .fails() + .stderr_is("tail: cannot open 'unreadable' for reading: Permission denied\n") + .stdout_is("==> file1 <==\n\n==> file2 <==\n") + .code_is(1); +} + #[test] #[cfg(target_os = "linux")] fn test_follow_redirect_stdin_name_retry() {