cp: add support for -x/--one-file-system (#1840)

This commit is contained in:
Neculai Balaban 2021-03-19 22:15:35 +02:00 committed by GitHub
parent 9132d32315
commit c6927d97c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 5 deletions

View file

@ -347,6 +347,7 @@ tempfile = "= 3.1.0"
time = "0.1"
unindent = "0.1"
uucore = { version=">=0.0.7", package="uucore", path="src/uucore", features=["entries"] }
walkdir = "2.2"
[target.'cfg(unix)'.dev-dependencies]
rust-users = { version="0.10", package="users" }

View file

@ -431,6 +431,10 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.arg(Arg::with_name(OPT_NO_DEREFERENCE_PRESERVE_LINKS)
.short("d")
.help("same as --no-dereference --preserve=links"))
.arg(Arg::with_name(OPT_ONE_FILE_SYSTEM)
.short("x")
.long(OPT_ONE_FILE_SYSTEM)
.help("stay on this file system"))
// TODO: implement the following args
.arg(Arg::with_name(OPT_COPY_CONTENTS)
@ -442,10 +446,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.takes_value(true)
.value_name("WHEN")
.help("NotImplemented: control creation of sparse files. See below"))
.arg(Arg::with_name(OPT_ONE_FILE_SYSTEM)
.short("x")
.long(OPT_ONE_FILE_SYSTEM)
.help("NotImplemented: stay on this file system"))
.arg(Arg::with_name(OPT_CONTEXT)
.long(OPT_CONTEXT)
.takes_value(true)
@ -563,6 +563,7 @@ impl Options {
let not_implemented_opts = vec![
OPT_COPY_CONTENTS,
OPT_SPARSE,
#[cfg(not(any(windows, unix)))]
OPT_ONE_FILE_SYSTEM,
OPT_CONTEXT,
#[cfg(windows)]
@ -937,7 +938,7 @@ fn copy_directory(root: &Path, target: &Target, options: &Options) -> CopyResult
#[cfg(any(windows, target_os = "redox"))]
let mut hard_links: Vec<(String, u64)> = vec![];
for path in WalkDir::new(root) {
for path in WalkDir::new(root).same_file_system(options.one_file_system) {
let p = or_continue!(path);
let is_symlink = fs::symlink_metadata(p.path())?.file_type().is_symlink();
let path = if (options.no_dereference || options.dereference) && is_symlink {

View file

@ -31,6 +31,12 @@ static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/";
static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt";
static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new";
static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_COPY_FROM_FOLDER: &str = "dir_with_mount";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_MOUNTPOINT: &str = "mount";
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
#[test]
fn test_cp_cp() {
@ -1001,3 +1007,70 @@ fn test_cp_target_file_dev_null() {
assert!(at.file_exists(file2));
}
#[test]
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
fn test_cp_one_file_system() {
use crate::common::util::AtPath;
use walkdir::WalkDir;
let scene = TestScenario::new(util_name!());
// Test must be run as root (or with `sudo -E`)
if scene.cmd("whoami").run().stdout != "root\n" {
return;
}
let at = scene.fixtures.clone();
let at_src = AtPath::new(&at.plus(TEST_MOUNT_COPY_FROM_FOLDER));
let at_dst = AtPath::new(&at.plus(TEST_COPY_TO_FOLDER_NEW));
// Prepare the mount
at_src.mkdir(TEST_MOUNT_MOUNTPOINT);
let mountpoint_path = &at_src.plus_as_string(TEST_MOUNT_MOUNTPOINT);
let _r = scene
.cmd("mount")
.arg("-t")
.arg("tmpfs")
.arg("-o")
.arg("size=640k") // ought to be enough
.arg("tmpfs")
.arg(mountpoint_path)
.run();
assert!(_r.code == Some(0), _r.stderr);
at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE);
// Begin testing -x flag
let result = scene
.ucmd()
.arg("-rx")
.arg(TEST_MOUNT_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW)
.run();
// Ditch the mount before the asserts
let _r = scene.cmd("umount").arg(mountpoint_path).run();
assert!(_r.code == Some(0), _r.stderr);
assert!(result.success);
assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE));
// Check if the other files were copied from the source folder hirerarchy
for entry in WalkDir::new(at_src.as_string()) {
let entry = entry.unwrap();
let relative_src = entry
.path()
.strip_prefix(at_src.as_string())
.unwrap()
.to_str()
.unwrap();
let ft = entry.file_type();
match (ft.is_dir(), ft.is_file(), ft.is_symlink()) {
(true, _, _) => assert!(at_dst.dir_exists(relative_src)),
(_, true, _) => assert!(at_dst.file_exists(relative_src)),
(_, _, _) => panic!(),
}
}
}

View file

View file