2022-10-10 03:22:36 +00:00
|
|
|
// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile
|
2021-05-30 05:10:54 +00:00
|
|
|
|
2020-05-25 17:05:26 +00:00
|
|
|
use crate::common::util::*;
|
2018-09-23 00:11:13 +00:00
|
|
|
#[cfg(not(windows))]
|
2016-12-28 01:08:43 +00:00
|
|
|
use std::fs::set_permissions;
|
2015-11-16 05:25:01 +00:00
|
|
|
|
2020-05-26 19:35:30 +00:00
|
|
|
#[cfg(not(windows))]
|
|
|
|
use std::os::unix::fs;
|
|
|
|
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2022-07-03 17:01:23 +00:00
|
|
|
use std::os::unix::fs::MetadataExt;
|
2021-09-12 21:14:34 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-06-01 21:06:38 +00:00
|
|
|
use std::os::unix::fs::PermissionsExt;
|
2020-05-26 19:35:30 +00:00
|
|
|
#[cfg(windows)]
|
|
|
|
use std::os::windows::fs::symlink_file;
|
|
|
|
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use filetime::FileTime;
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-02 16:08:09 +00:00
|
|
|
use rlimit::Resource;
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::fs as std_fs;
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::thread::sleep;
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
use std::time::Duration;
|
2022-07-17 20:03:29 +00:00
|
|
|
use uucore::display::Quotable;
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_EXISTING_FILE: &str = "existing_file.txt";
|
|
|
|
static TEST_HELLO_WORLD_SOURCE: &str = "hello_world.txt";
|
2020-05-26 19:35:30 +00:00
|
|
|
static TEST_HELLO_WORLD_SOURCE_SYMLINK: &str = "hello_world.txt.link";
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_HELLO_WORLD_DEST: &str = "copy_of_hello_world.txt";
|
2022-02-09 01:37:55 +00:00
|
|
|
static TEST_HELLO_WORLD_DEST_SYMLINK: &str = "copy_of_hello_world.txt.link";
|
2020-04-13 18:36:03 +00:00
|
|
|
static TEST_HOW_ARE_YOU_SOURCE: &str = "how_are_you.txt";
|
|
|
|
static TEST_HOW_ARE_YOU_DEST: &str = "hello_dir/how_are_you.txt";
|
|
|
|
static TEST_COPY_TO_FOLDER: &str = "hello_dir/";
|
|
|
|
static TEST_COPY_TO_FOLDER_FILE: &str = "hello_dir/hello_world.txt";
|
|
|
|
static TEST_COPY_FROM_FOLDER: &str = "hello_dir_with_file/";
|
|
|
|
static TEST_COPY_FROM_FOLDER_FILE: &str = "hello_dir_with_file/hello_world.txt";
|
2020-05-28 19:54:03 +00:00
|
|
|
static TEST_COPY_TO_FOLDER_NEW: &str = "hello_dir_new";
|
2017-08-10 23:04:25 +00:00
|
|
|
static TEST_COPY_TO_FOLDER_NEW_FILE: &str = "hello_dir_new/hello_world.txt";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_COPY_FROM_FOLDER: &str = "dir_with_mount";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_MOUNTPOINT: &str = "mount";
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
|
2022-01-30 08:33:06 +00:00
|
|
|
#[cfg(unix)]
|
|
|
|
static TEST_NONEXISTENT_FILE: &str = "nonexistent_file.txt";
|
2015-11-16 05:25:01 +00:00
|
|
|
|
2022-09-23 21:42:21 +00:00
|
|
|
/// Assert that mode, ownership, and permissions of two metadata objects match.
|
2022-11-01 20:17:22 +00:00
|
|
|
#[cfg(all(not(windows), not(target_os = "freebsd")))]
|
2022-09-23 21:42:21 +00:00
|
|
|
macro_rules! assert_metadata_eq {
|
|
|
|
($m1:expr, $m2:expr) => {{
|
|
|
|
assert_eq!($m1.mode(), $m2.mode(), "mode is different");
|
|
|
|
assert_eq!($m1.uid(), $m2.uid(), "uid is different");
|
|
|
|
assert_eq!($m1.atime(), $m2.atime(), "atime is different");
|
|
|
|
assert_eq!(
|
|
|
|
$m1.atime_nsec(),
|
|
|
|
$m2.atime_nsec(),
|
|
|
|
"atime_nsec is different"
|
|
|
|
);
|
|
|
|
assert_eq!($m1.mtime(), $m2.mtime(), "mtime is different");
|
|
|
|
assert_eq!(
|
|
|
|
$m1.mtime_nsec(),
|
|
|
|
$m2.mtime_nsec(),
|
|
|
|
"mtime_nsec is different"
|
|
|
|
);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2015-11-16 05:25:01 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_cp() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2015-11-16 05:25:01 +00:00
|
|
|
// Invoke our binary to make the copy.
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2015-11-16 05:25:01 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
2016-07-15 17:17:30 +00:00
|
|
|
|
2018-09-23 00:10:14 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_existing_target() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2018-09-23 00:10:14 +00:00
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2018-09-23 00:10:14 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
|
|
|
|
// No backup should have been created
|
2022-08-22 02:48:09 +00:00
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_EXISTING_FILE)));
|
2018-09-23 00:10:14 +00:00
|
|
|
}
|
|
|
|
|
2016-12-28 01:08:43 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_duplicate_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
|
|
|
.stderr_contains("specified more than once");
|
2016-12-28 01:08:43 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_multiple_files_target_is_file() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("not a directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_directory_not_recursive() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("omitting directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_multiple_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_DEST), "How are you?\n");
|
|
|
|
}
|
|
|
|
|
2017-06-02 11:37:31 +00:00
|
|
|
#[test]
|
2020-10-14 20:34:28 +00:00
|
|
|
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
|
2021-05-29 12:32:35 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2017-06-02 11:37:31 +00:00
|
|
|
fn test_cp_recurse() {
|
2017-08-10 23:04:25 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-r")
|
2017-08-10 23:04:25 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2017-06-02 11:37:31 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
2017-08-10 23:04:25 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
|
2017-06-02 11:37:31 +00:00
|
|
|
}
|
|
|
|
|
2016-07-15 17:17:30 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_with_dirs_t() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-t")
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-10-14 20:34:28 +00:00
|
|
|
// FixME: for MacOS, this has intermittent failures; track repair progress at GH:uutils/coreutils/issues/1590
|
2021-05-29 12:32:35 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2016-07-15 17:17:30 +00:00
|
|
|
fn test_cp_with_dirs() {
|
2016-08-23 11:52:43 +00:00
|
|
|
let scene = TestScenario::new(util_name!());
|
2016-07-29 21:26:32 +00:00
|
|
|
let at = &scene.fixtures;
|
2016-07-15 17:17:30 +00:00
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-04-13 18:36:03 +00:00
|
|
|
.ucmd()
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-04-13 18:36:03 +00:00
|
|
|
.ucmd()
|
2016-07-15 17:17:30 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-07-15 17:17:30 +00:00
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
2016-07-29 21:26:32 +00:00
|
|
|
}
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_target_directory() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("-t")
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_target_directory() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2020-04-13 18:36:03 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("-v")
|
|
|
|
.arg("-T")
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("cannot overwrite directory");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 03:26:33 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_target_directory_is_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-t")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.fails()
|
2022-03-01 06:30:29 +00:00
|
|
|
.stderr_contains(format!("'{}' is not a directory", TEST_HOW_ARE_YOU_SOURCE));
|
2022-02-02 03:26:33 +00:00
|
|
|
}
|
|
|
|
|
2022-10-24 18:01:15 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_update_interactive() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-i")
|
|
|
|
.arg("--update")
|
|
|
|
.succeeds()
|
|
|
|
.no_stdout()
|
|
|
|
.no_stderr();
|
|
|
|
}
|
|
|
|
|
2016-12-28 01:08:43 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_interactive() {
|
2022-10-10 14:41:43 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("a");
|
|
|
|
at.touch("b");
|
|
|
|
ucmd.args(&["-i", "a", "b"])
|
2016-12-28 01:08:43 +00:00
|
|
|
.pipe_in("N\n")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
2022-09-17 16:15:52 +00:00
|
|
|
.no_stdout()
|
2022-11-10 11:45:18 +00:00
|
|
|
.stderr_is("cp: overwrite 'b'? [y/N]:");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-08-04 21:15:33 +00:00
|
|
|
#[cfg(target_os = "linux")]
|
2016-12-28 01:08:43 +00:00
|
|
|
fn test_cp_arg_link() {
|
|
|
|
use std::os::linux::fs::MetadataExt;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--link")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--symbolic-link")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert!(at.is_symlink(TEST_HELLO_WORLD_DEST));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg("--no-clobber")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
2020-12-16 22:52:42 +00:00
|
|
|
}
|
|
|
|
|
2022-01-29 00:03:28 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber_inferred_arg() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("--no-clob")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
|
|
|
}
|
|
|
|
|
2020-12-16 22:52:42 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_no_clobber_twice() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
2021-04-22 20:37:44 +00:00
|
|
|
|
2020-12-16 22:52:42 +00:00
|
|
|
at.touch("source.txt");
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-12-16 22:52:42 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg("source.txt")
|
|
|
|
.arg("dest.txt")
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
2020-12-16 22:52:42 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read("source.txt"), "");
|
|
|
|
|
|
|
|
at.append("source.txt", "some-content");
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-12-16 22:52:42 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg("source.txt")
|
|
|
|
.arg("dest.txt")
|
2022-11-10 11:45:18 +00:00
|
|
|
.succeeds();
|
2020-12-16 22:52:42 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read("source.txt"), "some-content");
|
|
|
|
// Should be empty as the "no-clobber" should keep
|
|
|
|
// the previous version
|
|
|
|
assert_eq!(at.read("dest.txt"), "");
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_arg_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// create dest without write permissions
|
2020-04-13 18:36:03 +00:00
|
|
|
let mut permissions = at
|
|
|
|
.make_file(TEST_HELLO_WORLD_DEST)
|
|
|
|
.metadata()
|
|
|
|
.unwrap()
|
|
|
|
.permissions();
|
2016-12-28 01:08:43 +00:00
|
|
|
permissions.set_readonly(true);
|
|
|
|
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--force")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// TODO: write a better test that differentiates --remove-destination
|
|
|
|
/// from --force. Also this test currently doesn't work on
|
|
|
|
/// Windows. This test originally checked file timestamps, which
|
|
|
|
/// proved to be unreliable per target / CI platform
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_arg_remove_destination() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// create dest without write permissions
|
2020-04-13 18:36:03 +00:00
|
|
|
let mut permissions = at
|
|
|
|
.make_file(TEST_HELLO_WORLD_DEST)
|
|
|
|
.metadata()
|
|
|
|
.unwrap()
|
|
|
|
.permissions();
|
2016-12-28 01:08:43 +00:00
|
|
|
permissions.set_readonly(true);
|
|
|
|
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--remove-destination")
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-b")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
2021-05-26 22:28:17 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup_with_other_args() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg("-vbL")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-26 22:28:17 +00:00
|
|
|
"How are you?\n"
|
2021-05-25 20:41:07 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_backup_arg_first() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
2020-04-13 18:36:03 +00:00
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2020-04-13 18:36:03 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_suffix() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2021-05-25 20:41:07 +00:00
|
|
|
.arg("-b")
|
2016-12-28 01:08:43 +00:00
|
|
|
.arg("--suffix")
|
|
|
|
.arg(".bak")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2016-12-28 01:08:43 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
2020-04-13 18:36:03 +00:00
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.bak", TEST_HOW_ARE_YOU_SOURCE)),
|
2020-04-13 18:36:03 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
2016-12-28 01:08:43 +00:00
|
|
|
}
|
2020-05-26 19:35:30 +00:00
|
|
|
|
2022-01-31 11:10:57 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_arg_suffix_hyphen_value() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg("-b")
|
|
|
|
.arg("--suffix")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}-v", TEST_HOW_ARE_YOU_SOURCE)),
|
2022-01-31 11:10:57 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-25 20:41:07 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_custom_backup_suffix_via_env() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let suffix = "super-suffix-of-the-century";
|
|
|
|
|
|
|
|
ucmd.arg("-b")
|
|
|
|
.env("SIMPLE_BACKUP_SUFFIX", suffix)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}{}", TEST_HOW_ARE_YOU_SOURCE, suffix)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_numbered_with_t() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=t")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_numbered() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=numbered")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=existing")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_nil() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=nil")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_numbered_if_existing_backup_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-08-22 02:48:09 +00:00
|
|
|
let existing_backup = &format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
|
2021-05-25 20:41:07 +00:00
|
|
|
at.touch(existing_backup);
|
|
|
|
|
|
|
|
ucmd.arg("--backup=existing")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
|
|
|
|
assert!(at.file_exists(existing_backup));
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_numbered_if_existing_backup_nil() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-08-22 02:48:09 +00:00
|
|
|
let existing_backup = &format!("{}.~1~", TEST_HOW_ARE_YOU_SOURCE);
|
2021-05-25 20:41:07 +00:00
|
|
|
|
|
|
|
at.touch(existing_backup);
|
|
|
|
ucmd.arg("--backup=nil")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(TEST_HOW_ARE_YOU_SOURCE));
|
|
|
|
assert!(at.file_exists(existing_backup));
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}.~2~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_simple() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=simple")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-06-18 10:53:18 +00:00
|
|
|
fn test_cp_backup_simple_protect_source() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-06-18 16:51:39 +00:00
|
|
|
let source = format!("{}~", TEST_HELLO_WORLD_SOURCE);
|
|
|
|
at.touch(&source);
|
2022-06-18 10:53:18 +00:00
|
|
|
ucmd.arg("--backup=simple")
|
2022-06-18 16:51:39 +00:00
|
|
|
.arg(&source)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
2022-06-18 10:53:18 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_only(format!(
|
|
|
|
"cp: backing up '{}' might destroy source; '{}' not copied",
|
2022-06-18 16:51:39 +00:00
|
|
|
TEST_HELLO_WORLD_SOURCE, source,
|
2022-06-18 10:53:18 +00:00
|
|
|
));
|
|
|
|
|
2022-06-18 16:51:39 +00:00
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(at.read(&source), "");
|
2022-06-18 10:53:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-05-25 20:41:07 +00:00
|
|
|
fn test_cp_backup_never() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=never")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert_eq!(
|
2022-08-22 02:48:09 +00:00
|
|
|
at.read(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
|
2021-05-25 20:41:07 +00:00
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_none() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=none")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_off() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
ucmd.arg("--backup=off")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr();
|
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
assert!(!at.file_exists(&format!("{}~", TEST_HOW_ARE_YOU_SOURCE)));
|
|
|
|
}
|
|
|
|
|
2021-05-26 14:50:41 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_backup_no_clobber_conflicting_options() {
|
2021-11-09 20:23:41 +00:00
|
|
|
new_ucmd!()
|
2021-07-27 05:21:12 +00:00
|
|
|
.arg("--backup")
|
2021-05-26 14:50:41 +00:00
|
|
|
.arg("--no-clobber")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-11-09 20:23:41 +00:00
|
|
|
.fails()
|
|
|
|
.usage_error("options --backup and --no-clobber are mutually exclusive");
|
2021-05-26 14:50:41 +00:00
|
|
|
}
|
|
|
|
|
2020-11-11 20:41:02 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_deref_conflicting_options() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
|
|
|
.arg("-LP")
|
2020-11-11 20:41:02 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_deref() {
|
2021-04-22 20:37:44 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
//using -L option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-L")
|
2020-11-11 20:41:02 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
// unlike -P/--no-deref, we expect a file, not a link
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
2020-05-26 19:35:30 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_no_deref() {
|
2021-04-22 20:37:44 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2020-05-26 19:35:30 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
2020-05-28 19:54:03 +00:00
|
|
|
//using -P option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("-P")
|
2020-05-26 19:35:30 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-05-26 19:35:30 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-05-26 19:35:30 +00:00
|
|
|
}
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2022-02-09 01:37:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_no_deref_link_onto_link() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.copy(TEST_HELLO_WORLD_SOURCE, TEST_HELLO_WORLD_DEST);
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_SOURCE,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let _r = fs::symlink(
|
|
|
|
TEST_HELLO_WORLD_DEST,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK),
|
|
|
|
);
|
|
|
|
#[cfg(windows)]
|
|
|
|
let _r = symlink_file(
|
|
|
|
TEST_HELLO_WORLD_DEST,
|
|
|
|
at.subdir.join(TEST_HELLO_WORLD_DEST_SYMLINK),
|
|
|
|
);
|
|
|
|
|
|
|
|
ucmd.arg("-P")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST_SYMLINK)
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
// Ensure that the target of the destination was not modified.
|
|
|
|
assert!(!at
|
|
|
|
.symlink_metadata(TEST_HELLO_WORLD_DEST)
|
|
|
|
.file_type()
|
|
|
|
.is_symlink());
|
|
|
|
assert!(at
|
|
|
|
.symlink_metadata(TEST_HELLO_WORLD_DEST_SYMLINK)
|
|
|
|
.file_type()
|
|
|
|
.is_symlink());
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST_SYMLINK), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2021-03-09 10:59:26 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_strip_trailing_slashes() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
//using --strip-trailing-slashes option
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--strip-trailing-slashes")
|
2021-03-09 10:59:26 +00:00
|
|
|
.arg(format!("{}/", TEST_HELLO_WORLD_SOURCE))
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-09 10:59:26 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:25:15 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_parents() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--parents")
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-12 12:25:15 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_COPY_FROM_FOLDER_FILE
|
|
|
|
)),
|
|
|
|
"Hello, World!\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_parents_multiple_files() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--parents")
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-12 12:25:15 +00:00
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_COPY_FROM_FOLDER_FILE
|
|
|
|
)),
|
|
|
|
"Hello, World!\n"
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
at.read(&format!(
|
|
|
|
"{}/{}",
|
|
|
|
TEST_COPY_TO_FOLDER, TEST_HOW_ARE_YOU_SOURCE
|
|
|
|
)),
|
|
|
|
"How are you?\n"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_parents_dest_not_directory() {
|
2021-04-22 20:37:44 +00:00
|
|
|
new_ucmd!()
|
2021-03-12 12:25:15 +00:00
|
|
|
.arg("--parents")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER_FILE)
|
|
|
|
.arg(TEST_HELLO_WORLD_DEST)
|
2021-04-22 20:37:44 +00:00
|
|
|
.fails()
|
|
|
|
.stderr_contains("with --parents, the destination must be a directory");
|
2021-03-12 12:25:15 +00:00
|
|
|
}
|
|
|
|
|
2021-12-15 21:18:02 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_writable_special_file_permissions() {
|
|
|
|
new_ucmd!().arg("/dev/null").arg("/dev/zero").succeeds();
|
|
|
|
}
|
|
|
|
|
2021-12-19 17:19:48 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_issue_1665() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
ucmd.arg("/dev/null").arg("foo").succeeds();
|
|
|
|
assert!(at.file_exists("foo"));
|
|
|
|
assert_eq!(at.read("foo"), "");
|
|
|
|
}
|
|
|
|
|
2021-06-16 03:32:15 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_preserve_no_args() {
|
2022-11-16 00:39:25 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let src_file = "a";
|
|
|
|
let dst_file = "b";
|
|
|
|
|
|
|
|
// Prepare the source file
|
|
|
|
at.touch(src_file);
|
|
|
|
#[cfg(unix)]
|
|
|
|
at.set_mode(src_file, 0o0500);
|
|
|
|
|
|
|
|
// Copy
|
|
|
|
ucmd.arg(src_file)
|
|
|
|
.arg(dst_file)
|
2021-06-16 03:32:15 +00:00
|
|
|
.arg("--preserve")
|
|
|
|
.succeeds();
|
2022-11-16 00:39:25 +00:00
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
// Assert that the mode, ownership, and timestamps are preserved
|
|
|
|
// NOTICE: the ownership is not modified on the src file, because that requires root permissions
|
|
|
|
let metadata_src = at.metadata(src_file);
|
|
|
|
let metadata_dst = at.metadata(dst_file);
|
|
|
|
assert_metadata_eq!(metadata_src, metadata_dst);
|
|
|
|
}
|
2021-06-16 03:32:15 +00:00
|
|
|
}
|
|
|
|
|
2020-11-11 20:41:02 +00:00
|
|
|
#[test]
|
|
|
|
// For now, disable the test on Windows. Symlinks aren't well support on Windows.
|
|
|
|
// It works on Unix for now and it works locally when run from a powershell
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_deref_folder_to_folder() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER);
|
2020-11-11 20:41:02 +00:00
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.to_string_lossy(),
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.to_string_lossy(),
|
|
|
|
);
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
//using -P -R option
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-11-11 20:41:02 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-L")
|
|
|
|
.arg("-R")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls source {}", result.stdout_str());
|
2020-11-11 20:41:02 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
// No action as this test is disabled but kept in case we want to
|
|
|
|
// try to make it work in the future.
|
|
|
|
let a = Command::new("cmd").args(&["/C", "dir"]).output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", &at.as_string()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_FROM_FOLDER);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
|
|
|
|
let path_to_new = at.subdir.join(TEST_COPY_TO_FOLDER_NEW_FILE);
|
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
let path_to_check = path_to_new.to_str().unwrap();
|
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
|
|
|
|
|
|
|
// Check the content of the symlink
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-11-11 20:41:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 19:54:03 +00:00
|
|
|
#[test]
|
|
|
|
// For now, disable the test on Windows. Symlinks aren't well support on Windows.
|
|
|
|
// It works on Unix for now and it works locally when run from a powershell
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_cp_no_deref_folder_to_folder() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
let path_to_new_symlink = at.plus(TEST_COPY_FROM_FOLDER);
|
2020-05-28 19:54:03 +00:00
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.to_string_lossy(),
|
|
|
|
&path_to_new_symlink
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK)
|
|
|
|
.to_string_lossy(),
|
|
|
|
);
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
//using -P -R option
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2020-05-28 19:54:03 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-P")
|
|
|
|
.arg("-R")
|
|
|
|
.arg("-v")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls source {}", result.stdout_str());
|
2020-05-28 19:54:03 +00:00
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(path_to_new_symlink).run();
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-05-28 19:54:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
// No action as this test is disabled but kept in case we want to
|
|
|
|
// try to make it work in the future.
|
|
|
|
let a = Command::new("cmd").args(&["/C", "dir"]).output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", &at.as_string()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_FROM_FOLDER);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
|
|
|
|
let path_to_new_symlink = at.subdir.join(TEST_COPY_TO_FOLDER_NEW);
|
|
|
|
|
|
|
|
let a = Command::new("cmd")
|
|
|
|
.args(&["/C", "dir", path_to_new_symlink.to_str().unwrap()])
|
|
|
|
.output();
|
|
|
|
println!("output {:#?}", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
let path_to_new_symlink = at
|
|
|
|
.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join(TEST_HELLO_WORLD_SOURCE_SYMLINK);
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&path_to_new_symlink
|
|
|
|
.clone()
|
|
|
|
.into_os_string()
|
|
|
|
.into_string()
|
|
|
|
.unwrap()
|
|
|
|
));
|
|
|
|
|
|
|
|
let path_to_new = at.subdir.join(TEST_COPY_TO_FOLDER_NEW_FILE);
|
|
|
|
|
|
|
|
// Check the content of the destination file that was copied.
|
|
|
|
let path_to_check = path_to_new.to_str().unwrap();
|
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
|
|
|
|
|
|
|
// Check the content of the symlink
|
|
|
|
let path_to_check = path_to_new_symlink.to_str().unwrap();
|
2021-06-06 19:13:54 +00:00
|
|
|
assert_eq!(at.read(path_to_check), "Hello, World!\n");
|
2020-05-28 19:54:03 +00:00
|
|
|
}
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
fn test_cp_archive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond() as u32);
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--archive")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
2021-04-22 20:37:44 +00:00
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert_eq!(creation, creation2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "android")))]
|
2020-08-04 21:15:33 +00:00
|
|
|
fn test_cp_archive_recursive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// creates
|
|
|
|
// dir/1
|
|
|
|
// dir/1.link => dir/1
|
|
|
|
// dir/2
|
|
|
|
// dir/2.link => dir/2
|
|
|
|
|
|
|
|
let file_1 = at.subdir.join(TEST_COPY_TO_FOLDER).join("1");
|
|
|
|
let file_1_link = at.subdir.join(TEST_COPY_TO_FOLDER).join("1.link");
|
|
|
|
let file_2 = at.subdir.join(TEST_COPY_TO_FOLDER).join("2");
|
|
|
|
let file_2_link = at.subdir.join(TEST_COPY_TO_FOLDER).join("2.link");
|
|
|
|
|
|
|
|
at.touch(&file_1.to_string_lossy());
|
|
|
|
at.touch(&file_2.to_string_lossy());
|
|
|
|
|
2021-08-12 20:42:40 +00:00
|
|
|
at.symlink_file("1", &file_1_link.to_string_lossy());
|
|
|
|
at.symlink_file("2", &file_2_link.to_string_lossy());
|
2020-08-04 21:15:33 +00:00
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg("--archive")
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg(TEST_COPY_TO_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-08-12 20:42:40 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2
|
|
|
|
.cmd("ls")
|
|
|
|
.arg("-al")
|
|
|
|
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER))
|
|
|
|
.run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
let result = scene2
|
|
|
|
.cmd("ls")
|
|
|
|
.arg("-al")
|
|
|
|
.arg(&at.subdir.join(TEST_COPY_TO_FOLDER_NEW))
|
|
|
|
.run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert!(at.file_exists(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("1")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
assert!(at.file_exists(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("2")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("1.link")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
assert!(at.is_symlink(
|
|
|
|
&at.subdir
|
|
|
|
.join(TEST_COPY_TO_FOLDER_NEW)
|
|
|
|
.join("2.link")
|
|
|
|
.to_string_lossy()
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2020-08-04 21:15:33 +00:00
|
|
|
fn test_cp_preserve_timestamps() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond());
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--preserve=timestamps")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
assert_eq!(creation, creation2);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-05-30 05:10:54 +00:00
|
|
|
fn test_cp_no_preserve_timestamps() {
|
2020-08-04 21:15:33 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2022-04-23 19:32:35 +00:00
|
|
|
let ts = time::OffsetDateTime::now_local().unwrap();
|
|
|
|
let previous = FileTime::from_unix_time(ts.unix_timestamp() - 3600, ts.nanosecond());
|
2021-05-30 05:10:54 +00:00
|
|
|
// set the file creation/modification an hour ago
|
2020-08-04 21:15:33 +00:00
|
|
|
filetime::set_file_times(
|
|
|
|
at.plus_as_string(TEST_HELLO_WORLD_SOURCE),
|
|
|
|
previous,
|
|
|
|
previous,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
sleep(Duration::from_secs(3));
|
|
|
|
|
2021-04-22 20:37:44 +00:00
|
|
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
2020-08-04 21:15:33 +00:00
|
|
|
.arg("--no-preserve=timestamps")
|
|
|
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2020-08-04 21:15:33 +00:00
|
|
|
|
|
|
|
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
|
|
|
|
|
|
|
|
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
|
|
|
|
let creation = metadata.modified().unwrap();
|
|
|
|
|
|
|
|
let metadata2 = std_fs::metadata(at.subdir.join(TEST_HOW_ARE_YOU_SOURCE)).unwrap();
|
|
|
|
let creation2 = metadata2.modified().unwrap();
|
|
|
|
|
|
|
|
let scene2 = TestScenario::new("ls");
|
|
|
|
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run();
|
|
|
|
|
2021-04-05 20:03:43 +00:00
|
|
|
println!("ls dest {}", result.stdout_str());
|
2020-08-04 21:15:33 +00:00
|
|
|
println!("creation {:?} / {:?}", creation, creation2);
|
|
|
|
|
|
|
|
assert_ne!(creation, creation2);
|
|
|
|
let res = creation.elapsed().unwrap() - creation2.elapsed().unwrap();
|
|
|
|
// Some margins with time check
|
|
|
|
assert!(res.as_secs() > 3595);
|
|
|
|
assert!(res.as_secs() < 3605);
|
|
|
|
}
|
2021-01-01 17:36:30 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-01-01 17:36:30 +00:00
|
|
|
fn test_cp_target_file_dev_null() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let file1 = "/dev/null";
|
|
|
|
let file2 = "test_cp_target_file_file_i2";
|
|
|
|
|
|
|
|
at.touch(file2);
|
|
|
|
ucmd.arg(file1).arg(file2).succeeds().no_stderr();
|
|
|
|
|
|
|
|
assert!(at.file_exists(file2));
|
|
|
|
}
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
|
2021-03-19 20:15:35 +00:00
|
|
|
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`)
|
2021-04-22 20:37:44 +00:00
|
|
|
if scene.cmd("whoami").run().stdout_str() != "root\n" {
|
2021-03-19 20:15:35 +00:00
|
|
|
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);
|
|
|
|
|
2021-04-17 13:48:23 +00:00
|
|
|
scene
|
2021-03-19 20:15:35 +00:00
|
|
|
.cmd("mount")
|
|
|
|
.arg("-t")
|
|
|
|
.arg("tmpfs")
|
|
|
|
.arg("-o")
|
|
|
|
.arg("size=640k") // ought to be enough
|
|
|
|
.arg("tmpfs")
|
|
|
|
.arg(mountpoint_path)
|
2021-04-17 13:48:23 +00:00
|
|
|
.succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE);
|
|
|
|
|
|
|
|
// Begin testing -x flag
|
2021-04-22 20:37:44 +00:00
|
|
|
scene
|
2021-03-19 20:15:35 +00:00
|
|
|
.ucmd()
|
|
|
|
.arg("-rx")
|
|
|
|
.arg(TEST_MOUNT_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_COPY_TO_FOLDER_NEW)
|
2021-04-22 20:37:44 +00:00
|
|
|
.succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
// Ditch the mount before the asserts
|
2021-04-17 13:48:23 +00:00
|
|
|
scene.cmd("umount").arg(mountpoint_path).succeeds();
|
2021-03-19 20:15:35 +00:00
|
|
|
|
|
|
|
assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE));
|
2021-05-30 05:10:54 +00:00
|
|
|
// Check if the other files were copied from the source folder hierarchy
|
2021-03-19 20:15:35 +00:00
|
|
|
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!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let result = ucmd
|
|
|
|
.arg("--reflink=always")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.run();
|
|
|
|
|
2021-04-24 10:45:55 +00:00
|
|
|
if result.succeeded() {
|
2021-04-18 12:17:55 +00:00
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
} else {
|
|
|
|
// Older Linux versions do not support cloning.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_auto() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-23 05:24:47 +00:00
|
|
|
ucmd.arg("--reflink=auto")
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.succeeds();
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
2022-10-03 18:20:55 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
|
|
|
fn test_cp_reflink_none() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
let result = ucmd
|
|
|
|
.arg("--reflink")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.run();
|
|
|
|
|
|
|
|
if result.succeeded() {
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
} else {
|
|
|
|
// Older Linux versions do not support cloning.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-18 12:17:55 +00:00
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_never() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
2021-04-23 05:24:47 +00:00
|
|
|
ucmd.arg("--reflink=never")
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.succeeds();
|
2021-04-18 12:17:55 +00:00
|
|
|
|
|
|
|
// Check the content of the destination file
|
|
|
|
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
|
2021-04-18 12:17:55 +00:00
|
|
|
fn test_cp_reflink_bad() {
|
|
|
|
let (_, mut ucmd) = at_and_ucmd!();
|
2021-04-24 10:45:55 +00:00
|
|
|
let _result = ucmd
|
2021-04-18 12:17:55 +00:00
|
|
|
.arg("--reflink=bad")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
2021-04-23 05:24:47 +00:00
|
|
|
.fails()
|
2022-10-16 16:28:04 +00:00
|
|
|
.stderr_contains("error: 'bad' isn't a valid value for '--reflink[=<WHEN>]'");
|
2021-04-18 12:17:55 +00:00
|
|
|
}
|
2021-06-01 21:06:38 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-01 21:06:38 +00:00
|
|
|
fn test_cp_reflink_insufficient_permission() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("unreadable")
|
|
|
|
.set_permissions(PermissionsExt::from_mode(0o000))
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
ucmd.arg("-r")
|
|
|
|
.arg("--reflink=auto")
|
|
|
|
.arg("unreadable")
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: 'unreadable' -> 'existing_file.txt': Permission denied (os error 13)");
|
|
|
|
}
|
2021-06-02 16:08:09 +00:00
|
|
|
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2021-06-02 16:08:09 +00:00
|
|
|
#[test]
|
|
|
|
fn test_closes_file_descriptors() {
|
2022-09-07 16:55:32 +00:00
|
|
|
use procfs::process::Process;
|
|
|
|
let me = Process::myself().unwrap();
|
|
|
|
|
|
|
|
// The test suite runs in parallel, we have pipe, sockets
|
|
|
|
// opened by other tests.
|
|
|
|
// So, we take in account the various fd to increase the limit
|
|
|
|
let number_file_already_opened: u64 = me.fd_count().unwrap().try_into().unwrap();
|
|
|
|
let limit_fd: u64 = number_file_already_opened + 9;
|
|
|
|
|
|
|
|
// For debugging purposes:
|
2022-09-09 13:10:42 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-09-07 16:55:32 +00:00
|
|
|
for f in me.fd().unwrap() {
|
|
|
|
let fd = f.unwrap();
|
|
|
|
println!("{:?} {:?}", fd, fd.mode());
|
|
|
|
}
|
|
|
|
|
2021-06-02 16:08:09 +00:00
|
|
|
new_ucmd!()
|
|
|
|
.arg("-r")
|
|
|
|
.arg("--reflink=auto")
|
|
|
|
.arg("dir_with_10_files/")
|
|
|
|
.arg("dir_with_10_files_new/")
|
2022-09-07 16:55:32 +00:00
|
|
|
.with_limit(Resource::NOFILE, limit_fd, limit_fd)
|
2021-06-02 16:08:09 +00:00
|
|
|
.succeeds();
|
|
|
|
}
|
2021-06-17 20:26:13 +00:00
|
|
|
|
cp: Implement `--sparse` flag (#3766)
* cp: Refactor `reflink`/`sparse` handling to enable `--sparse` flag
`--sparse` and `--reflink` options have a lot of similarities:
- They have similar options (`always`, `never`, `auto`)
- Both need OS specific handling
- They can be mutually exclusive
Prior to this change, `sparse` was defined as `CopyMode`, but `reflink`
wasn't. Given the similarities, it makes sense to handle them similarly.
The idea behind this change is to move all OS specific file copy
handling in the `copy_on_write_*` functions. Those function then
dispatch to the correct logic depending on the arguments (at the moment,
the tuple `(reflink, sparse)`).
Also, move the handling of `--reflink=never` from `copy_file` to the
`copy_on_write_*` functions, at the cost of a bit of code duplication,
to allow `copy_on_write_*` to handle all cases (and later handle
`--reflink=never` with `--sparse`).
* cp: Implement `--sparse` flag
This begins to address #3362
At the moment, only the `--sparse=always` logic matches the requirement
form GNU cp info page, i.e. always make holes in destination when
possible.
Sparse copy is done by copying the source to the destination block by
block (blocks being of the destination's fs block size). If the block
only holds NUL bytes, we don't write to the destination.
About `--sparse=auto`: according to GNU cp info page, the destination
file will be made sparse if the source file is sparse as well. The next
step are likely to use `lseek` with `SEEK_HOLE` detect if the source
file has holes. Currently, this has the same behaviour as
`--sparse=never`. This `SEEK_HOLE` logic can also be applied to
`--sparse=always` to improve performance when copying sparse files.
About `--sparse=never`: from my understanding, it is not guaranteed that
Rust's `fs::copy` will always produce a file with no holes, as
["platform-specific behavior may change in the
future"](https://doc.rust-lang.org/std/fs/fn.copy.html#platform-specific-behavior)
About other platforms:
- `macos`: The solution may be to use `fcntl` command `F_PUNCHHOLE`.
- `windows`: I only see `FSCTL_SET_SPARSE`.
This should pass the following GNU tests:
- `tests/cp/sparse.sh`
- `tests/cp/sparse-2.sh`
- `tests/cp/sparse-extents.sh`
- `tests/cp/sparse-extents-2.sh`
`sparse-perf.sh` needs `--sparse=auto`, and in particular a way to skip
holes in the source file.
Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
2022-08-04 11:22:59 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_never_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 4;
|
|
|
|
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=never", "src_file1", "dst_file_non_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
assert_eq!(at.read_bytes("dst_file_non_sparse"), buf);
|
|
|
|
assert_eq!(
|
|
|
|
at.metadata("dst_file_non_sparse").blocks() * 512,
|
|
|
|
buf.len() as u64
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 4;
|
|
|
|
let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert_eq!(at.read_bytes("dst_file_sparse"), buf);
|
|
|
|
assert_eq!(at.metadata("dst_file_sparse").blocks(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_non_empty() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
const BUFFER_SIZE: usize = 4096 * 16 + 3;
|
|
|
|
let mut buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
|
|
|
let blocks_to_touch = [buf.len() / 3, 2 * (buf.len() / 3)];
|
|
|
|
|
|
|
|
for i in blocks_to_touch {
|
|
|
|
buf[i] = b'x';
|
|
|
|
}
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
at.write_bytes("src_file1", &buf);
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
let touched_block_count =
|
|
|
|
blocks_to_touch.len() as u64 * at.metadata("dst_file_sparse").blksize() / 512;
|
|
|
|
|
|
|
|
assert_eq!(at.read_bytes("dst_file_sparse"), buf);
|
|
|
|
assert_eq!(at.metadata("dst_file_sparse").blocks(), touched_block_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_invalid_option() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&["--sparse=invalid", "src_file1", "dst_file"])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_always_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&[
|
|
|
|
"--sparse=always",
|
|
|
|
"--reflink=always",
|
|
|
|
"src_file1",
|
|
|
|
"dst_file",
|
|
|
|
])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_sparse_never_reflink_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
at.make_file("src_file1");
|
|
|
|
|
|
|
|
ucmd.args(&[
|
|
|
|
"--sparse=never",
|
|
|
|
"--reflink=always",
|
|
|
|
"src_file1",
|
|
|
|
"dst_file",
|
|
|
|
])
|
|
|
|
.fails();
|
|
|
|
}
|
|
|
|
|
2022-08-04 06:50:19 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
2022-09-29 14:22:03 +00:00
|
|
|
#[cfg(feature = "truncate")]
|
2022-08-04 06:50:19 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_reflink_always_override() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
|
|
|
|
const DISK: &str = "disk.img";
|
|
|
|
const ROOTDIR: &str = "disk_root/";
|
|
|
|
const USERDIR: &str = "dir/";
|
|
|
|
const MOUNTPOINT: &str = "mountpoint/";
|
|
|
|
|
|
|
|
let src1_path: &str = &vec![MOUNTPOINT, USERDIR, "src1"].concat();
|
|
|
|
let src2_path: &str = &vec![MOUNTPOINT, USERDIR, "src2"].concat();
|
|
|
|
let dst_path: &str = &vec![MOUNTPOINT, USERDIR, "dst"].concat();
|
|
|
|
|
|
|
|
scene.fixtures.mkdir(ROOTDIR);
|
|
|
|
scene.fixtures.mkdir(&vec![ROOTDIR, USERDIR].concat());
|
|
|
|
|
|
|
|
// Setup:
|
|
|
|
// Because neither `mkfs.btrfs` not btrfs `mount` options allow us to have a mountpoint owned
|
|
|
|
// by a non-root user, we want the following directory structure:
|
|
|
|
//
|
|
|
|
// uid | path
|
|
|
|
// ---------------------------
|
|
|
|
// user | .
|
|
|
|
// root | └── mountpoint
|
|
|
|
// user | └── dir
|
|
|
|
// user | ├── src1
|
|
|
|
// user | └── src2
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ccmd("truncate")
|
|
|
|
.args(&["-s", "128M", DISK])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
if !scene
|
|
|
|
.cmd_keepenv("env")
|
|
|
|
.args(&["mkfs.btrfs", "--rootdir", ROOTDIR, DISK])
|
|
|
|
.run()
|
|
|
|
.succeeded()
|
|
|
|
{
|
|
|
|
print!("Test skipped; couldn't make btrfs disk image");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scene.fixtures.mkdir(MOUNTPOINT);
|
|
|
|
|
|
|
|
let mount = scene
|
|
|
|
.cmd_keepenv("sudo")
|
|
|
|
.args(&["-E", "--non-interactive", "mount", DISK, MOUNTPOINT])
|
|
|
|
.run();
|
|
|
|
|
|
|
|
if !mount.succeeded() {
|
|
|
|
print!("Test skipped; requires root user");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scene.fixtures.make_file(src1_path);
|
|
|
|
scene.fixtures.write_bytes(src1_path, &[0x64; 8192]);
|
|
|
|
|
|
|
|
scene.fixtures.make_file(src2_path);
|
|
|
|
scene.fixtures.write(src2_path, "other data");
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.args(&["--reflink=always", src1_path, dst_path])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.args(&["--reflink=always", src2_path, dst_path])
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
scene
|
|
|
|
.cmd_keepenv("sudo")
|
|
|
|
.args(&["-E", "--non-interactive", "umount", MOUNTPOINT])
|
|
|
|
.succeeds();
|
|
|
|
}
|
|
|
|
|
2021-06-17 20:26:13 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_dir_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.symlink_dir("dir", "dir-link");
|
|
|
|
ucmd.args(&["-r", "dir-link", "copy"]).succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy"), "dir");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
2022-09-29 14:22:03 +00:00
|
|
|
#[cfg(feature = "ln")]
|
2021-06-17 20:26:13 +00:00
|
|
|
fn test_copy_dir_with_symlinks() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.make_file("dir/file");
|
|
|
|
|
|
|
|
TestScenario::new("ln")
|
|
|
|
.ucmd()
|
|
|
|
.arg("-sr")
|
|
|
|
.arg(at.subdir.join("dir/file"))
|
|
|
|
.arg(at.subdir.join("dir/file-link"))
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
ucmd.args(&["-r", "dir", "copy"]).succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy/file-link"), "file");
|
|
|
|
}
|
2021-06-19 15:49:04 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_copy_symlink_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("file", "file-link");
|
|
|
|
at.touch("copy");
|
|
|
|
|
|
|
|
ucmd.args(&["file-link", "copy", "-f", "--no-dereference"])
|
|
|
|
.succeeds();
|
|
|
|
assert_eq!(at.resolve_link("copy"), "file");
|
|
|
|
}
|
2021-08-29 23:27:47 +00:00
|
|
|
|
|
|
|
#[test]
|
2021-09-05 16:51:47 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-08-29 23:27:47 +00:00
|
|
|
fn test_no_preserve_mode() {
|
|
|
|
use std::os::unix::prelude::MetadataExt;
|
|
|
|
|
|
|
|
use uucore::mode::get_umask;
|
|
|
|
|
|
|
|
const PERMS_ALL: u32 = 0o7777;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
set_permissions(at.plus("file"), PermissionsExt::from_mode(PERMS_ALL)).unwrap();
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("dest")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
let umask = get_umask();
|
|
|
|
// remove sticky bit, setuid and setgid bit; apply umask
|
|
|
|
let expected_perms = PERMS_ALL & !0o7000 & !umask;
|
|
|
|
assert_eq!(
|
|
|
|
at.plus("dest").metadata().unwrap().mode() & 0o7777,
|
|
|
|
expected_perms
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-09-05 16:51:47 +00:00
|
|
|
#[cfg(all(unix, not(target_os = "freebsd")))]
|
2021-08-29 23:27:47 +00:00
|
|
|
fn test_preserve_mode() {
|
|
|
|
use std::os::unix::prelude::MetadataExt;
|
|
|
|
|
|
|
|
const PERMS_ALL: u32 = 0o7777;
|
|
|
|
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
set_permissions(at.plus("file"), PermissionsExt::from_mode(PERMS_ALL)).unwrap();
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("dest")
|
|
|
|
.arg("-p")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
assert_eq!(
|
|
|
|
at.plus("dest").metadata().unwrap().mode() & 0o7777,
|
|
|
|
PERMS_ALL
|
|
|
|
);
|
|
|
|
}
|
2021-09-03 12:56:35 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_canonicalize_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("dir");
|
|
|
|
at.touch("dir/file");
|
2022-09-23 17:31:24 +00:00
|
|
|
at.relative_symlink_file("../dir/file", "dir/file-ln");
|
2021-09-03 12:56:35 +00:00
|
|
|
ucmd.arg("dir/file-ln")
|
|
|
|
.arg(".")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
}
|
2021-08-27 16:40:42 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_through_just_created_symlink() {
|
2022-04-02 08:47:37 +00:00
|
|
|
for create_t in [true, false] {
|
2021-08-27 16:40:42 +00:00
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.mkdir("c");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.relative_symlink_file("../t", "a/1");
|
2021-08-27 16:40:42 +00:00
|
|
|
at.touch("b/1");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.write("b/1", "hello");
|
2021-08-27 16:40:42 +00:00
|
|
|
if create_t {
|
|
|
|
at.touch("t");
|
2022-07-10 14:49:25 +00:00
|
|
|
at.write("t", "world");
|
2021-08-27 16:40:42 +00:00
|
|
|
}
|
|
|
|
ucmd.arg("--no-dereference")
|
|
|
|
.arg("a/1")
|
|
|
|
.arg("b/1")
|
|
|
|
.arg("c")
|
|
|
|
.fails()
|
|
|
|
.stderr_only(if cfg!(not(target_os = "windows")) {
|
|
|
|
"cp: will not copy 'b/1' through just-created symlink 'c/1'"
|
|
|
|
} else {
|
|
|
|
"cp: will not copy 'b/1' through just-created symlink 'c\\1'"
|
|
|
|
});
|
2022-07-10 14:49:25 +00:00
|
|
|
if create_t {
|
|
|
|
assert_eq!(at.read("a/1"), "world");
|
|
|
|
}
|
2021-08-27 16:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("nonexistent", "target");
|
|
|
|
ucmd.arg("file")
|
|
|
|
.arg("target")
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: not writing through dangling symlink 'target'");
|
|
|
|
}
|
2022-01-30 06:59:53 +00:00
|
|
|
|
2022-05-02 06:20:46 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.symlink_file("no-such-file", "dangle");
|
|
|
|
ucmd.arg("-P")
|
|
|
|
.arg("dangle")
|
|
|
|
.arg("d2")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
}
|
|
|
|
|
2022-07-03 17:01:23 +00:00
|
|
|
/// Test for copying a dangling symbolic link and its permissions.
|
2022-08-12 21:51:28 +00:00
|
|
|
#[cfg(not(target_os = "freebsd"))] // FIXME: fix this test for FreeBSD
|
2022-07-03 17:01:23 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference_permissions() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
// target name link name
|
|
|
|
at.symlink_file("no-such-file", "dangle");
|
2022-08-08 09:11:17 +00:00
|
|
|
// to check if access time and modification time didn't change
|
|
|
|
sleep(Duration::from_millis(5000));
|
2022-07-03 17:01:23 +00:00
|
|
|
// don't dereference the link
|
|
|
|
// | copy permissions, too
|
|
|
|
// | | from the link
|
|
|
|
// | | | to new file d2
|
|
|
|
// | | | |
|
|
|
|
// V V V V
|
|
|
|
ucmd.args(&["-P", "-p", "dangle", "d2"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
2022-08-08 09:11:17 +00:00
|
|
|
assert!(at.symlink_exists("d2"), "symlink wasn't created");
|
2022-07-03 17:01:23 +00:00
|
|
|
|
|
|
|
// `-p` means `--preserve=mode,ownership,timestamps`
|
|
|
|
#[cfg(unix)]
|
|
|
|
{
|
|
|
|
let metadata1 = at.symlink_metadata("dangle");
|
|
|
|
let metadata2 = at.symlink_metadata("d2");
|
2022-09-23 21:42:21 +00:00
|
|
|
assert_metadata_eq!(metadata1, metadata2);
|
2022-07-03 17:01:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 14:49:25 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_through_dangling_symlink_no_dereference_2() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.symlink_file("nonexistent", "target");
|
|
|
|
ucmd.args(&["-P", "file", "target"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: not writing through dangling symlink 'target'");
|
|
|
|
}
|
|
|
|
|
2022-10-23 21:40:30 +00:00
|
|
|
/// Test that copy through a dangling symbolic link fails, even with --force.
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
fn test_copy_through_dangling_symlink_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("src");
|
|
|
|
at.symlink_file("no-such-file", "dest");
|
|
|
|
ucmd.args(&["--force", "src", "dest"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: not writing through dangling symlink 'dest'");
|
|
|
|
assert!(!at.file_exists("dest"));
|
|
|
|
}
|
|
|
|
|
2022-01-30 06:59:53 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_archive_on_nonexistent_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-a")
|
|
|
|
.arg(TEST_NONEXISTENT_FILE)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
2022-01-30 08:33:06 +00:00
|
|
|
.stderr_only(
|
|
|
|
"cp: cannot stat 'nonexistent_file.txt': No such file or directory (os error 2)",
|
|
|
|
);
|
2022-01-30 06:59:53 +00:00
|
|
|
}
|
2022-02-02 06:47:30 +00:00
|
|
|
|
|
|
|
#[test]
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-02-02 06:47:30 +00:00
|
|
|
fn test_cp_link_backup() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file2");
|
|
|
|
ucmd.arg("-l")
|
|
|
|
.arg("-b")
|
|
|
|
.arg(TEST_HELLO_WORLD_SOURCE)
|
|
|
|
.arg("file2")
|
|
|
|
.succeeds();
|
|
|
|
|
|
|
|
assert!(at.file_exists("file2~"));
|
|
|
|
assert_eq!(at.read("file2"), "Hello, World!\n");
|
|
|
|
}
|
2022-02-20 09:31:13 +00:00
|
|
|
|
2022-03-03 21:58:27 +00:00
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn test_cp_fifo() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkfifo("fifo");
|
|
|
|
ucmd.arg("-r")
|
|
|
|
.arg("fifo")
|
|
|
|
.arg("fifo2")
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
assert!(at.is_fifo("fifo2"));
|
|
|
|
}
|
|
|
|
|
2022-02-01 12:07:04 +00:00
|
|
|
#[test]
|
|
|
|
fn test_dir_recursive_copy() {
|
|
|
|
let scene = TestScenario::new(util_name!());
|
|
|
|
let at = &scene.fixtures;
|
|
|
|
|
|
|
|
at.mkdir("parent1");
|
|
|
|
at.mkdir("parent2");
|
|
|
|
at.mkdir("parent1/child");
|
|
|
|
at.mkdir("parent2/child1");
|
|
|
|
at.mkdir("parent2/child1/child2");
|
|
|
|
at.mkdir("parent2/child1/child2/child3");
|
|
|
|
|
|
|
|
// case-1: copy parent1 -> parent1: should fail
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.arg("-R")
|
|
|
|
.arg("parent1")
|
|
|
|
.arg("parent1")
|
|
|
|
.fails()
|
|
|
|
.stderr_contains("cannot copy a directory");
|
|
|
|
// case-2: copy parent1 -> parent1/child should fail
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.arg("-R")
|
|
|
|
.arg("parent1")
|
|
|
|
.arg("parent1/child")
|
|
|
|
.fails()
|
|
|
|
.stderr_contains("cannot copy a directory");
|
|
|
|
// case-3: copy parent1/child -> parent2 should pass
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.arg("-R")
|
|
|
|
.arg("parent1/child")
|
|
|
|
.arg("parent2")
|
|
|
|
.succeeds();
|
|
|
|
// case-4: copy parent2/child1/ -> parent2/child1/child2/child3
|
|
|
|
scene
|
|
|
|
.ucmd()
|
|
|
|
.arg("-R")
|
|
|
|
.arg("parent2/child1/")
|
|
|
|
.arg("parent2/child1/child2/child3")
|
|
|
|
.fails()
|
|
|
|
.stderr_contains("cannot copy a directory");
|
|
|
|
}
|
2022-02-07 17:56:06 +00:00
|
|
|
|
2022-02-03 07:40:26 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_dir_vs_file() {
|
|
|
|
new_ucmd!()
|
|
|
|
.arg("-R")
|
|
|
|
.arg(TEST_COPY_FROM_FOLDER)
|
|
|
|
.arg(TEST_EXISTING_FILE)
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: cannot overwrite non-directory with directory");
|
|
|
|
}
|
2022-01-31 11:54:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_overriding_arguments() {
|
|
|
|
let s = TestScenario::new(util_name!());
|
|
|
|
s.fixtures.touch("file1");
|
2022-04-02 08:47:37 +00:00
|
|
|
for (arg1, arg2) in [
|
2022-01-31 11:54:26 +00:00
|
|
|
#[cfg(not(windows))]
|
|
|
|
("--remove-destination", "--force"),
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
("--force", "--remove-destination"),
|
|
|
|
("--interactive", "--no-clobber"),
|
|
|
|
("--link", "--symbolic-link"),
|
2022-02-09 18:08:28 +00:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2022-01-31 11:54:26 +00:00
|
|
|
("--symbolic-link", "--link"),
|
|
|
|
("--dereference", "--no-dereference"),
|
|
|
|
("--no-dereference", "--dereference"),
|
|
|
|
] {
|
|
|
|
s.ucmd()
|
|
|
|
.arg(arg1)
|
|
|
|
.arg(arg2)
|
|
|
|
.arg("file1")
|
|
|
|
.arg("file2")
|
|
|
|
.succeeds();
|
|
|
|
s.fixtures.remove("file2");
|
|
|
|
}
|
|
|
|
}
|
2022-07-10 14:49:25 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_no_dereference_1() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.touch("a/foo");
|
|
|
|
at.write("a/foo", "bar");
|
|
|
|
at.relative_symlink_file("../a/foo", "b/foo");
|
|
|
|
ucmd.args(&["-P", "a/foo", "b"]).fails();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_abuse_existing() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("b");
|
|
|
|
at.mkdir("c");
|
|
|
|
at.relative_symlink_file("../t", "a/1");
|
|
|
|
at.touch("b/1");
|
|
|
|
at.write("b/1", "hello");
|
|
|
|
at.relative_symlink_file("../t", "c/1");
|
|
|
|
at.touch("t");
|
|
|
|
at.write("t", "i");
|
|
|
|
ucmd.args(&["-dR", "a/1", "b/1", "c"])
|
|
|
|
.fails()
|
|
|
|
.stderr_contains(format!(
|
|
|
|
"will not copy 'b/1' through just-created symlink 'c{}1'",
|
|
|
|
if cfg!(windows) { "\\" } else { "/" }
|
|
|
|
));
|
|
|
|
assert_eq!(at.read("t"), "i");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_same_symlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.relative_symlink_file("t", "a");
|
|
|
|
at.relative_symlink_file("t", "b");
|
|
|
|
at.touch("t");
|
|
|
|
ucmd.args(&["-d", "a", "b"]).succeeds();
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_copy_same_symlink_no_dereference_dangling() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.relative_symlink_file("t", "a");
|
|
|
|
at.relative_symlink_file("t", "b");
|
|
|
|
ucmd.args(&["-d", "a", "b"]).succeeds();
|
|
|
|
}
|
2022-07-17 20:03:29 +00:00
|
|
|
|
2022-11-15 08:00:59 +00:00
|
|
|
// TODO: enable for Android, when #3477 solved
|
|
|
|
#[cfg(not(any(windows, target_os = "android")))]
|
2022-09-02 02:12:16 +00:00
|
|
|
#[test]
|
|
|
|
fn test_cp_parents_2_dirs() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir_all("a/b/c");
|
|
|
|
at.mkdir("d");
|
|
|
|
ucmd.args(&["-a", "--parents", "a/b/c", "d"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
assert!(at.dir_exists("d/a/b/c"));
|
|
|
|
}
|
|
|
|
|
2022-07-17 20:03:29 +00:00
|
|
|
#[test]
|
|
|
|
#[ignore = "issue #3332"]
|
|
|
|
fn test_cp_parents_2() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir_all("a/b");
|
|
|
|
at.touch("a/b/c");
|
|
|
|
at.mkdir("d");
|
|
|
|
ucmd.args(&["--verbose", "-a", "--parents", "a/b/c", "d"])
|
|
|
|
.succeeds()
|
|
|
|
.stdout_is(format!(
|
|
|
|
"{} -> {}\n{} -> {}\n{} -> {}\n",
|
|
|
|
"a",
|
|
|
|
path_concat!("d", "a"),
|
|
|
|
path_concat!("a", "b"),
|
|
|
|
path_concat!("d", "a", "b"),
|
|
|
|
path_concat!("a", "b", "c").quote(),
|
|
|
|
path_concat!("d", "a", "b", "c").quote()
|
|
|
|
));
|
|
|
|
assert!(at.file_exists("d/a/b/c"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[ignore = "issue #3332"]
|
|
|
|
fn test_cp_parents_2_link() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir_all("a/b");
|
|
|
|
at.touch("a/b/c");
|
|
|
|
at.mkdir("d");
|
|
|
|
at.relative_symlink_file("b", "a/link");
|
|
|
|
ucmd.args(&["--verbose", "-a", "--parents", "a/link/c", "d"])
|
|
|
|
.succeeds()
|
|
|
|
.stdout_is(format!(
|
|
|
|
"{} -> {}\n{} -> {}\n{} -> {}\n",
|
|
|
|
"a",
|
|
|
|
path_concat!("d", "a"),
|
|
|
|
path_concat!("a", "link"),
|
|
|
|
path_concat!("d", "a", "link"),
|
|
|
|
path_concat!("a", "link", "c").quote(),
|
|
|
|
path_concat!("d", "a", "link", "c").quote()
|
|
|
|
));
|
|
|
|
assert!(at.dir_exists("d/a/link") && !at.symlink_exists("d/a/link"));
|
|
|
|
assert!(at.file_exists("d/a/link/c"));
|
|
|
|
}
|
2022-09-23 17:31:24 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_copy_symlink_contents_recursive() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("src-dir");
|
|
|
|
at.mkdir("dest-dir");
|
|
|
|
at.touch("f");
|
|
|
|
at.write("f", "f");
|
|
|
|
at.relative_symlink_file("f", "slink");
|
|
|
|
at.relative_symlink_file("no-file", &path_concat!("src-dir", "slink"));
|
|
|
|
ucmd.args(&["-H", "-R", "slink", "src-dir", "dest-dir"])
|
|
|
|
.succeeds();
|
|
|
|
assert!(at.dir_exists("src-dir"));
|
|
|
|
assert!(at.dir_exists("dest-dir"));
|
|
|
|
assert!(at.dir_exists(&path_concat!("dest-dir", "src-dir")));
|
|
|
|
let regular_file = path_concat!("dest-dir", "slink");
|
|
|
|
assert!(!at.symlink_exists(®ular_file) && at.file_exists(®ular_file));
|
|
|
|
assert_eq!(at.read(®ular_file), "f");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_symlink() {
|
|
|
|
for from in ["file", "slink", "slink2"] {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["-s", "-L", from, "z"]).succeeds();
|
|
|
|
assert!(at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read_symlink("z"), from);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Android doesn't allow creating hard links
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_hardlink() {
|
|
|
|
for from in ["file", "slink", "slink2"] {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["--link", "-L", from, "z"]).succeeds();
|
|
|
|
assert!(at.file_exists("z") && !at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read("z"), "f");
|
|
|
|
// checking that it's the same hard link
|
|
|
|
at.append("z", "g");
|
|
|
|
assert_eq!(at.read("file"), "fg");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Android doesn't allow creating hard links
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
#[test]
|
|
|
|
fn test_cp_mode_hardlink_no_dereference() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("file");
|
|
|
|
at.write("file", "f");
|
|
|
|
at.relative_symlink_file("file", "slink");
|
|
|
|
at.relative_symlink_file("slink", "slink2");
|
|
|
|
ucmd.args(&["--link", "-P", "slink2", "z"]).succeeds();
|
|
|
|
assert!(at.symlink_exists("z"));
|
|
|
|
assert_eq!(at.read_symlink("z"), "slink");
|
|
|
|
}
|
2022-09-23 19:56:44 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_remove_destination_symbolic_link_loop() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.symlink_file("loop", "loop");
|
|
|
|
at.plus("loop");
|
|
|
|
at.touch("f");
|
|
|
|
ucmd.args(&["--remove-destination", "f", "loop"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stdout()
|
|
|
|
.no_stderr();
|
|
|
|
assert!(at.file_exists("loop"));
|
|
|
|
}
|
2022-10-06 07:53:02 +00:00
|
|
|
|
2022-09-25 21:09:01 +00:00
|
|
|
/// Test that copying a directory to itself is disallowed.
|
|
|
|
#[test]
|
|
|
|
fn test_copy_directory_to_itself_disallowed() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("d");
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let expected = "cp: cannot copy a directory, 'd', into itself, 'd/d'";
|
|
|
|
#[cfg(windows)]
|
|
|
|
let expected = "cp: cannot copy a directory, 'd', into itself, 'd\\d'";
|
|
|
|
ucmd.args(&["-R", "d", "d"]).fails().stderr_only(expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test that copying a nested directory to itself is disallowed.
|
|
|
|
#[test]
|
|
|
|
fn test_copy_nested_directory_to_itself_disallowed() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("a");
|
|
|
|
at.mkdir("a/b");
|
|
|
|
at.mkdir("a/b/c");
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
let expected = "cp: cannot copy a directory, 'a/b', into itself, 'a/b/c/b'";
|
|
|
|
#[cfg(windows)]
|
|
|
|
let expected = "cp: cannot copy a directory, 'a/b', into itself, 'a/b/c\\b'";
|
|
|
|
ucmd.args(&["-R", "a/b", "a/b/c"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only(expected);
|
|
|
|
}
|
2022-09-23 21:43:28 +00:00
|
|
|
|
|
|
|
/// Test for preserving permissions when copying a directory.
|
2022-11-01 20:17:22 +00:00
|
|
|
#[cfg(all(not(windows), not(target_os = "freebsd")))]
|
2022-09-23 21:43:28 +00:00
|
|
|
#[test]
|
|
|
|
fn test_copy_dir_preserve_permissions() {
|
|
|
|
// Create a directory that has some non-default permissions.
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.mkdir("d1");
|
|
|
|
at.set_mode("d1", 0o0500);
|
|
|
|
|
|
|
|
// Copy the directory, preserving those permissions.
|
|
|
|
//
|
|
|
|
// 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"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stderr()
|
|
|
|
.no_stdout();
|
|
|
|
assert!(at.dir_exists("d2"));
|
|
|
|
|
|
|
|
// Assert that the permissions are preserved.
|
|
|
|
let metadata1 = at.metadata("d1");
|
|
|
|
let metadata2 = at.metadata("d2");
|
|
|
|
assert_metadata_eq!(metadata1, metadata2);
|
|
|
|
}
|
2022-09-24 02:41:13 +00:00
|
|
|
|
|
|
|
/// Test for preserving permissions when copying a directory, even in
|
|
|
|
/// the face of an inaccessible file in that directory.
|
2022-11-01 20:17:22 +00:00
|
|
|
#[cfg(all(not(windows), not(target_os = "freebsd")))]
|
2022-09-24 02:41:13 +00:00
|
|
|
#[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);
|
|
|
|
}
|
2022-10-10 03:22:36 +00:00
|
|
|
|
2022-09-22 03:58:41 +00:00
|
|
|
/// Test that copying file to itself with backup fails.
|
|
|
|
#[test]
|
|
|
|
fn test_same_file_backup() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("f");
|
|
|
|
ucmd.args(&["--backup", "f", "f"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: 'f' and 'f' are the same file");
|
|
|
|
assert!(!at.file_exists("f~"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test that copying file to itself with forced backup succeeds.
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
#[test]
|
|
|
|
fn test_same_file_force() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("f");
|
|
|
|
ucmd.args(&["--force", "f", "f"])
|
|
|
|
.fails()
|
|
|
|
.stderr_only("cp: 'f' and 'f' are the same file");
|
|
|
|
assert!(!at.file_exists("f~"));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Test that copying file to itself with forced backup succeeds.
|
|
|
|
#[cfg(all(not(windows), not(target_os = "macos")))]
|
|
|
|
#[test]
|
|
|
|
fn test_same_file_force_backup() {
|
|
|
|
// TODO This test should work on macos, but the command was
|
|
|
|
// causing an error:
|
|
|
|
//
|
|
|
|
// cp: 'f' -> 'f': No such file or directory (os error 2)
|
|
|
|
//
|
|
|
|
// I couldn't figure out how to fix it, so I just skipped this
|
|
|
|
// test on macos.
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
at.touch("f");
|
|
|
|
ucmd.args(&["--force", "--backup", "f", "f"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stdout()
|
|
|
|
.no_stderr();
|
|
|
|
assert!(at.file_exists("f~"));
|
|
|
|
}
|
2022-11-06 06:24:48 +00:00
|
|
|
|
2022-10-10 03:22:36 +00:00
|
|
|
/// Test for copying the contents of a FIFO as opposed to the FIFO object itself.
|
|
|
|
#[cfg(unix)]
|
|
|
|
#[test]
|
|
|
|
fn test_copy_contents_fifo() {
|
|
|
|
let scenario = TestScenario::new(util_name!());
|
|
|
|
let at = &scenario.fixtures;
|
|
|
|
|
|
|
|
// Start the `cp` process, reading the contents of `fifo` and
|
|
|
|
// writing to regular file `outfile`.
|
|
|
|
at.mkfifo("fifo");
|
|
|
|
let mut ucmd = scenario.ucmd();
|
|
|
|
let child = ucmd
|
|
|
|
.args(&["--copy-contents", "fifo", "outfile"])
|
|
|
|
.run_no_wait();
|
|
|
|
|
|
|
|
// Write some bytes to the `fifo`. We expect these bytes to get
|
|
|
|
// copied through to `outfile`.
|
|
|
|
std::fs::write(at.plus("fifo"), "foo").unwrap();
|
|
|
|
|
|
|
|
// At this point the child process should have terminated
|
|
|
|
// successfully with no output. The `outfile` should have the
|
|
|
|
// contents of `fifo` copied into it.
|
|
|
|
let output = child.wait_with_output().unwrap();
|
|
|
|
assert!(output.status.success());
|
|
|
|
assert!(output.stdout.is_empty());
|
|
|
|
assert!(output.stderr.is_empty());
|
|
|
|
assert_eq!(at.read("outfile"), "foo");
|
|
|
|
}
|
2022-10-12 00:38:04 +00:00
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
#[test]
|
|
|
|
fn test_reflink_never_sparse_always() {
|
|
|
|
let (at, mut ucmd) = at_and_ucmd!();
|
|
|
|
|
|
|
|
// Create a file and make it a large sparse file.
|
|
|
|
//
|
|
|
|
// On common Linux filesystems, setting the length to one megabyte
|
|
|
|
// should cause the file to become a sparse file, but it depends
|
|
|
|
// on the system.
|
|
|
|
std::fs::File::create(at.plus("src"))
|
|
|
|
.unwrap()
|
|
|
|
.set_len(1024 * 1024)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
ucmd.args(&["--reflink=never", "--sparse=always", "src", "dest"])
|
|
|
|
.succeeds()
|
|
|
|
.no_stdout()
|
|
|
|
.no_stderr();
|
|
|
|
at.file_exists("dest");
|
|
|
|
|
|
|
|
let src_metadata = std::fs::metadata(at.plus("src")).unwrap();
|
|
|
|
let dest_metadata = std::fs::metadata(at.plus("dest")).unwrap();
|
|
|
|
assert_eq!(src_metadata.blocks(), dest_metadata.blocks());
|
|
|
|
assert_eq!(dest_metadata.len(), 1024 * 1024);
|
|
|
|
}
|