From dc52a6fec5be7bec59582e8e676d890aae5628a4 Mon Sep 17 00:00:00 2001 From: Piepmatz Date: Tue, 7 Jan 2025 22:44:55 +0100 Subject: [PATCH] Handle permission denied error at `nu_engine::glob_from` (#14679) # Description #14528 mentioned that trying to `open` a file in a directory where you don't have read access results in a "file not found" error. I investigated the error and could find the root issue in the `nu_engine::glob_from` function. It uses `std::path::Path::canonicalize` some layers down and that may return an `std::io::Error`. All these errors were handled as "directory not found" which will be translated to "file not found" in the `open` command. To fix this, I handled the `PermssionDenied` error kind of the io error and passed that down. Now trying to `open` a file from a directory with no permissions returns a "permission denied" error. Before/After: ![image](https://github.com/user-attachments/assets/168cea24-36a6-4c66-98c9-f7ccfa2ea826) # User-Facing Changes That error is fixed, so correct error message. # Tests + Formatting - :green_circle: `toolkit fmt` - :green_circle: `toolkit clippy` - :green_circle: `toolkit test` - :green_circle: `toolkit test stdlib` # After Submitting fixes #14528 --- crates/nu-command/src/filesystem/open.rs | 15 ++++++++++++ crates/nu-engine/src/glob_from.rs | 31 +++++++++++++++++------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/crates/nu-command/src/filesystem/open.rs b/crates/nu-command/src/filesystem/open.rs index 7cbce3902b..db00f35d6d 100644 --- a/crates/nu-command/src/filesystem/open.rs +++ b/crates/nu-command/src/filesystem/open.rs @@ -91,6 +91,21 @@ impl Command for Open { file: path.item.to_string(), span, }, + // that particular error in `nu_engine::glob_from` doesn't have a span attached + // to it, so let's add it + ShellError::GenericError { + error, + msg, + span: _, + help, + inner, + } if error.as_str() == "Permission denied" => ShellError::GenericError { + error, + msg, + span: Some(arg_span), + help, + inner, + }, _ => err, })? .1 diff --git a/crates/nu-engine/src/glob_from.rs b/crates/nu-engine/src/glob_from.rs index 376b3c2baf..3765ab7c7b 100644 --- a/crates/nu-engine/src/glob_from.rs +++ b/crates/nu-engine/src/glob_from.rs @@ -3,6 +3,7 @@ use nu_path::{canonicalize_with, expand_path_with}; use nu_protocol::{NuGlob, ShellError, Span, Spanned}; use std::{ fs, + io::ErrorKind, path::{Component, Path, PathBuf}, }; @@ -70,20 +71,32 @@ pub fn glob_from( if is_symlink { (path.parent().map(|parent| parent.to_path_buf()), path) } else { - let path = if let Ok(p) = canonicalize_with(path.clone(), cwd) { - if nu_glob::is_glob(p.to_string_lossy().as_ref()) { + let path = match canonicalize_with(path.clone(), cwd) { + Ok(p) if nu_glob::is_glob(p.to_string_lossy().as_ref()) => { // our path might contain glob metacharacters too. // in such case, we need to escape our path to make // glob work successfully PathBuf::from(nu_glob::Pattern::escape(&p.to_string_lossy())) - } else { - p } - } else { - return Err(ShellError::DirectoryNotFound { - dir: path.to_string_lossy().to_string(), - span: pattern.span, - }); + Ok(p) => p, + Err(err) => { + return match err.kind() { + ErrorKind::PermissionDenied => Err(ShellError::GenericError { + error: "Permission denied".into(), + msg: err.to_string(), + span: None, + help: None, + inner: vec![], + }), + // Previously, all these errors were treated as "directory not found." + // Now, permission denied errors are handled separately. + // TODO: Refine handling of I/O errors for more precise responses. + _ => Err(ShellError::DirectoryNotFound { + dir: path.to_string_lossy().to_string(), + span: pattern.span, + }), + }; + } }; (path.parent().map(|parent| parent.to_path_buf()), path) }