1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

cp: continue directory walk if file inaccessible

Stop `cp` from terminating prematurely if a file in a directory is
inaccesible due to insufficient permissions.
This commit is contained in:
Jeffrey Finkelstein 2022-09-23 22:41:13 -04:00
parent 2450493302
commit b89e8e54c4
2 changed files with 56 additions and 3 deletions

View file

@ -38,7 +38,7 @@ use libc::mkfifo;
use quick_error::ResultExt;
use uucore::backup_control::{self, BackupMode};
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UClapError, UError, UResult, UUsageError};
use uucore::error::{set_exit_code, UClapError, UError, UIoError, UResult, UUsageError};
use uucore::format_usage;
use uucore::fs::{
canonicalize, paths_refer_to_same_file, FileInformation, MissingHandling, ResolveMode,
@ -1173,13 +1173,31 @@ fn copy_directory(
}?;
}
} else {
copy_file(
// At this point, `path` is just a plain old file.
// Terminate this function immediately if there is any
// kind of error *except* a "permission denied" error.
//
// TODO What other kinds of errors, if any, should
// cause us to continue walking the directory?
match copy_file(
path.as_path(),
local_to_target.as_path(),
options,
symlinked_files,
false,
)?;
) {
Ok(_) => {}
Err(Error::IoErrContext(e, _))
if e.kind() == std::io::ErrorKind::PermissionDenied =>
{
show!(uio_error!(
e,
"cannot open {} for reading",
p.path().quote()
));
}
Err(e) => return Err(e),
}
}
}
}

View file

@ -2160,3 +2160,38 @@ fn test_copy_dir_preserve_permissions() {
let metadata2 = at.metadata("d2");
assert_metadata_eq!(metadata1, metadata2);
}
/// Test for preserving permissions when copying a directory, even in
/// the face of an inaccessible file in that directory.
#[cfg(not(windows))]
#[test]
fn test_copy_dir_preserve_permissions_inaccessible_file() {
// Create a directory that has some non-default permissions and
// contains an inaccessible file.
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("d1");
at.touch("d1/f");
at.set_mode("d1/f", 0);
at.set_mode("d1", 0o0500);
// Copy the directory, preserving those permissions. There should
// be an error message that the file `d1/f` is inaccessible.
//
// preserve permissions (mode, ownership, timestamps)
// | copy directories recursively
// | | from this source directory
// | | | to this destination
// | | | |
// V V V V
ucmd.args(&["-p", "-R", "d1", "d2"])
.fails()
.status_code(1)
.stderr_only("cp: cannot open 'd1/f' for reading: Permission denied");
assert!(at.dir_exists("d2"));
assert!(!at.file_exists("d2/f"));
// Assert that the permissions are preserved.
let metadata1 = at.metadata("d1");
let metadata2 = at.metadata("d2");
assert_metadata_eq!(metadata1, metadata2);
}