fix: fixcd (#6799)

* fix: fixcd

try to fix

Log: try to fix the bug with can enter a permisson error fold

* change wording

* fat

* fmt

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Access 2022-11-05 02:38:39 +08:00 committed by GitHub
parent bb968304da
commit b9195c2668
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 5 deletions

5
Cargo.lock generated
View file

@ -1974,9 +1974,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.134"
version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "libgit2-sys"
@ -2583,6 +2583,7 @@ dependencies = [
"is-root",
"itertools",
"lazy_static",
"libc",
"log",
"lscolors",
"md-5",

View file

@ -98,6 +98,7 @@ winreg = "0.10.1"
[target.'cfg(unix)'.dependencies]
umask = "2.0.0"
users = "0.11.0"
libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "2.1.3"

View file

@ -6,6 +6,29 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
};
use std::path::Path;
// when the file under the fold executeable
#[cfg(unix)]
mod permission_mods {
pub type Mode = u32;
pub mod unix {
use super::Mode;
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
}
}
// use to return the message of the result of change director
// TODO: windows, maybe should use file_attributes function in https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
// TODO: the meaning of the result of the function can be found in https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
// TODO: if have realize the logic on windows, remove the cfg
#[derive(Debug)]
enum PermissionResult<'a> {
PermissionOk,
PermissionDenied(&'a str),
}
#[derive(Clone)]
pub struct Cd;
@ -141,6 +164,7 @@ impl Command for Cd {
}
};
let path_tointo = path.clone();
let path_value = Value::String { val: path, span };
let cwd = Value::String {
val: cwd.to_string_lossy().to_string(),
@ -172,9 +196,16 @@ impl Command for Cd {
//FIXME: this only changes the current scope, but instead this environment variable
//should probably be a block that loads the information from the state in the overlay
stack.add_env_var("PWD".into(), path_value);
Ok(PipelineData::new(call.head))
match have_permission(&path_tointo) {
PermissionResult::PermissionOk => {
stack.add_env_var("PWD".into(), path_value);
Ok(PipelineData::new(call.head))
}
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError(format!(
"Cannot change directory to {}: {}",
path_tointo, reason
))),
}
}
fn examples(&self) -> Vec<Example> {
@ -197,3 +228,65 @@ impl Command for Cd {
]
}
}
#[cfg(windows)]
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
match dir.as_ref().read_dir() {
Err(e) => {
if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
PermissionResult::PermissionDenied("Folder is unable to be read")
} else {
PermissionResult::PermissionOk
}
}
Ok(_) => PermissionResult::PermissionOk,
}
}
#[cfg(unix)]
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
match dir.as_ref().metadata() {
Ok(metadata) => {
use std::os::unix::fs::MetadataExt;
let bits = metadata.mode();
let has_bit = |bit| bits & bit == bit;
let current_user = users::get_current_uid();
if current_user == 0 {
return PermissionResult::PermissionOk;
}
let current_group = users::get_current_gid();
let owner_user = metadata.uid();
let owner_group = metadata.gid();
match (current_user == owner_user, current_group == owner_group) {
(true, _) => {
if has_bit(permission_mods::unix::USER_EXECUTE) {
PermissionResult::PermissionOk
} else {
PermissionResult::PermissionDenied(
"You are the owner but do not have the execute permission",
)
}
}
(false, true) => {
if has_bit(permission_mods::unix::GROUP_EXECUTE) {
PermissionResult::PermissionOk
} else {
PermissionResult::PermissionDenied(
"You are in the group but do not have the execute permission",
)
}
}
// other_user or root
(false, false) => {
if has_bit(permission_mods::unix::OTHER_EXECUTE) {
PermissionResult::PermissionOk
} else {
PermissionResult::PermissionDenied(
"You are neither the owner, in the group, nor the super user and do not have permission",
)
}
}
}
}
Err(_) => PermissionResult::PermissionDenied("Could not retrieve the metadata"),
}
}

View file

@ -289,3 +289,43 @@ fn test_change_windows_drive() {
.exists());
})
}
#[cfg(unix)]
#[test]
fn cd_permission_deined_folder() {
Playground::setup("cd_test_21", |dirs, sandbox| {
sandbox.mkdir("banned");
let actual = nu!(
cwd: dirs.test(),
r#"
chmod -x banned
cd banned
"#
);
assert!(actual.err.contains("Cannot change directory to"));
nu!(
cwd: dirs.test(),
r#"
chmod +x banned
rm banned
"#
);
});
}
// FIXME: cd_permission_deined_folder on windows
#[ignore]
#[cfg(windows)]
#[test]
fn cd_permission_deined_folder() {
Playground::setup("cd_test_21", |dirs, sandbox| {
sandbox.mkdir("banned");
let actual = nu!(
cwd: dirs.test(),
r#"
icacls banned /deny BUILTIN\Administrators:F
cd banned
"#
);
assert!(actual.err.contains("Folder is not able to read"));
});
}