Fix: dst error on cp command (#7895)

Fixes #7693 

On `cp` commands there were two error which pass error message with
invalid detail about source and destination files . there error were for
Not exist file and Permission denied .

Examples:
  Before :
Copy `source_file_valid` to `destination_invalid_dir` throw this error ;
`copy file "/source_file_valid" failed: No such file or directory (os
error 2) `

 After this PR it will throw this if destination will be invalid :
`copying to destination "/destination_invalid_dir" failed: No such file
or directory (os error 2) `

it was for Permission denied too .

---------

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
This commit is contained in:
Amirhossein Akhlaghpour 2023-02-01 15:48:21 -05:00 committed by GitHub
parent 4db960c0a6
commit c130ca1bc6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 10 deletions

View File

@ -351,17 +351,38 @@ fn copy_file(src: PathBuf, dst: PathBuf, span: Span) -> Value {
Value::String { val: msg, span }
}
Err(e) => {
let message = format!("copy file {src:?} failed: {e}");
let message_src = format!(
"copying file '{src_display}' failed: {e}",
src_display = src.display()
);
let message_dst = format!(
"copying to destination '{dst_display}' failed: {e}",
dst_display = dst.display()
);
use std::io::ErrorKind;
let shell_error = match e.kind() {
ErrorKind::NotFound => ShellError::FileNotFoundCustom(message, span),
ErrorKind::PermissionDenied => ShellError::PermissionDeniedError(message, span),
ErrorKind::Interrupted => ShellError::IOInterrupted(message, span),
ErrorKind::OutOfMemory => ShellError::OutOfMemoryError(message, span),
ErrorKind::NotFound => {
if std::path::Path::new(&dst).exists() {
ShellError::FileNotFoundCustom(message_src, span)
} else {
ShellError::FileNotFoundCustom(message_dst, span)
}
}
ErrorKind::PermissionDenied => match std::fs::metadata(&dst) {
Ok(meta) => {
if meta.permissions().readonly() {
ShellError::PermissionDeniedError(message_dst, span)
} else {
ShellError::PermissionDeniedError(message_src, span)
}
}
Err(_) => ShellError::PermissionDeniedError(message_dst, span),
},
ErrorKind::Interrupted => ShellError::IOInterrupted(message_src, span),
ErrorKind::OutOfMemory => ShellError::OutOfMemoryError(message_src, span),
// TODO: handle ExecutableFileBusy etc. when io_error_more is stabilized
// https://github.com/rust-lang/rust/issues/86442
_ => ShellError::IOErrorSpanned(message, span),
_ => ShellError::IOErrorSpanned(message_src, span),
};
Value::Error { error: shell_error }

View File

@ -1,5 +1,8 @@
use nu_test_support::fs::file_contents;
use nu_test_support::fs::{files_exist_at, AbsoluteFile, Stub::EmptyFile};
use nu_test_support::fs::{
files_exist_at, AbsoluteFile,
Stub::{EmptyFile, FileWithPermission},
};
use nu_test_support::nu;
use nu_test_support::playground::Playground;
use std::path::Path;
@ -344,3 +347,37 @@ fn copy_ignores_ansi() {
assert_eq!(actual.out, "success.txt");
});
}
#[test]
fn copy_file_not_exists_dst() {
Playground::setup("cp_test_17", |_dirs, sandbox| {
sandbox.with_files(vec![EmptyFile("valid.txt")]);
let actual = nu!(
cwd: sandbox.cwd(),
"cp valid.txt ~/invalid_dir/invalid_dir1"
);
assert!(
actual.err.contains("invalid_dir1") && actual.err.contains("copying to destination")
);
});
}
#[test]
fn copy_file_with_read_permission() {
Playground::setup("cp_test_18", |_dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("valid.txt"),
FileWithPermission("invalid_prem.txt", false),
]);
let actual = nu!(
cwd: sandbox.cwd(),
"cp valid.txt invalid_prem.txt",
);
assert!(
actual.err.contains("invalid_prem.txt")
&& actual.err.contains("copying to destination")
);
});
}

View File

@ -137,6 +137,7 @@ pub enum Stub<'a> {
FileWithContent(&'a str, &'a str),
FileWithContentToBeTrimmed(&'a str, &'a str),
EmptyFile(&'a str),
FileWithPermission(&'a str, bool),
}
pub fn file_contents(full_path: impl AsRef<Path>) -> String {

View File

@ -202,7 +202,8 @@ impl<'a> Playground<'a> {
.iter()
.map(|f| {
let mut path = PathBuf::from(&self.cwd);
let mut permission_set = false;
let mut write_able = true;
let (file_name, contents) = match *f {
Stub::EmptyFile(name) => (name, "fake data".to_string()),
Stub::FileWithContent(name, content) => (name, content.to_string()),
@ -215,11 +216,24 @@ impl<'a> Playground<'a> {
.collect::<Vec<&str>>()
.join(&endl),
),
Stub::FileWithPermission(name, is_write_able) => {
permission_set = true;
write_able = is_write_able;
(name, "check permission".to_string())
}
};
path.push(file_name);
std::fs::write(path, contents.as_bytes()).expect("can not create file");
std::fs::write(&path, contents.as_bytes()).expect("can not create file");
if permission_set {
let err_perm = "can not set permission";
let mut perm = std::fs::metadata(path.clone())
.expect(err_perm)
.permissions();
perm.set_readonly(!write_able);
std::fs::set_permissions(path, perm).expect(err_perm);
}
})
.for_each(drop);
self.back_to_playground();