mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
Fix the debug results in cp --debug
(#6220)
This commit is contained in:
parent
366af1c056
commit
421b820ec2
2 changed files with 1081 additions and 24 deletions
|
@ -2,10 +2,14 @@
|
|||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap
|
||||
// spell-checker:ignore ficlone reflink ftruncate pwrite fiemap lseek
|
||||
|
||||
use libc::{SEEK_DATA, SEEK_HOLE};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::Read;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::os::unix::fs::FileExt;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::fs::{FileTypeExt, OpenOptionsExt};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::Path;
|
||||
|
||||
|
@ -32,6 +36,25 @@ enum CloneFallback {
|
|||
|
||||
/// Use [`std::fs::copy`].
|
||||
FSCopy,
|
||||
|
||||
/// Use sparse_copy
|
||||
SparseCopy,
|
||||
|
||||
/// Use sparse_copy_without_hole
|
||||
SparseCopyWithoutHole,
|
||||
}
|
||||
|
||||
/// Type of method used for copying files
|
||||
#[derive(Clone, Copy)]
|
||||
enum CopyMethod {
|
||||
/// Do a sparse copy
|
||||
SparseCopy,
|
||||
/// Use [`std::fs::copy`].
|
||||
FSCopy,
|
||||
/// Default (can either be sparse_copy or FSCopy)
|
||||
Default,
|
||||
/// Use sparse_copy_without_hole
|
||||
SparseCopyWithoutHole,
|
||||
}
|
||||
|
||||
/// Use the Linux `ioctl_ficlone` API to do a copy-on-write clone.
|
||||
|
@ -53,17 +76,109 @@ where
|
|||
match fallback {
|
||||
CloneFallback::Error => Err(std::io::Error::last_os_error()),
|
||||
CloneFallback::FSCopy => std::fs::copy(source, dest).map(|_| ()),
|
||||
CloneFallback::SparseCopy => sparse_copy(source, dest),
|
||||
CloneFallback::SparseCopyWithoutHole => sparse_copy_without_hole(source, dest),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether a file contains any non null bytes i.e. any byte != 0x0
|
||||
/// This function returns a tuple of (bool, u64, u64) signifying a tuple of (whether a file has
|
||||
/// data, its size, no of blocks it has allocated in disk)
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn check_for_data(source: &Path) -> Result<(bool, u64, u64), std::io::Error> {
|
||||
let mut src_file = File::open(source)?;
|
||||
let metadata = src_file.metadata()?;
|
||||
|
||||
let size = metadata.size();
|
||||
let blocks = metadata.blocks();
|
||||
// checks edge case of virtual files in /proc which have a size of zero but contains data
|
||||
if size == 0 {
|
||||
let mut buf: Vec<u8> = vec![0; metadata.blksize() as usize]; // Directly use metadata.blksize()
|
||||
let _ = src_file.read(&mut buf)?;
|
||||
return Ok((buf.iter().any(|&x| x != 0x0), size, 0));
|
||||
}
|
||||
|
||||
let src_fd = src_file.as_raw_fd();
|
||||
|
||||
let result = unsafe { libc::lseek(src_fd, 0, SEEK_DATA) };
|
||||
|
||||
match result {
|
||||
-1 => Ok((false, size, blocks)), // No data found or end of file
|
||||
_ if result >= 0 => Ok((true, size, blocks)), // Data found
|
||||
_ => Err(std::io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
/// Checks whether a file is sparse i.e. it contains holes, uses the crude heuristic blocks < size / 512
|
||||
/// Reference:`<https://doc.rust-lang.org/std/os/unix/fs/trait.MetadataExt.html#tymethod.blocks>`
|
||||
fn check_sparse_detection(source: &Path) -> Result<bool, std::io::Error> {
|
||||
let src_file = File::open(source)?;
|
||||
let metadata = src_file.metadata()?;
|
||||
let size = metadata.size();
|
||||
let blocks = metadata.blocks();
|
||||
|
||||
if blocks < size / 512 {
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Optimized sparse_copy, doesn't create holes for large sequences of zeros in non sparse_files
|
||||
/// Used when --sparse=auto
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn sparse_copy_without_hole<P>(source: P, dest: P) -> std::io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let src_file = File::open(source)?;
|
||||
let dst_file = File::create(dest)?;
|
||||
let dst_fd = dst_file.as_raw_fd();
|
||||
|
||||
let size = src_file.metadata()?.size();
|
||||
if unsafe { libc::ftruncate(dst_fd, size.try_into().unwrap()) } < 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
let src_fd = src_file.as_raw_fd();
|
||||
let mut current_offset: isize = 0;
|
||||
loop {
|
||||
let result = unsafe { libc::lseek(src_fd, current_offset.try_into().unwrap(), SEEK_DATA) }
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
current_offset = result;
|
||||
let hole: isize =
|
||||
unsafe { libc::lseek(src_fd, current_offset.try_into().unwrap(), SEEK_HOLE) }
|
||||
.try_into()
|
||||
.unwrap();
|
||||
if result == -1 || hole == -1 {
|
||||
break;
|
||||
}
|
||||
if result <= -2 || hole <= -2 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
let len: isize = hole - current_offset;
|
||||
let mut buf: Vec<u8> = vec![0x0; len as usize];
|
||||
src_file.read_exact_at(&mut buf, current_offset as u64)?;
|
||||
unsafe {
|
||||
libc::pwrite(
|
||||
dst_fd,
|
||||
buf.as_ptr() as *const libc::c_void,
|
||||
len as usize,
|
||||
current_offset.try_into().unwrap(),
|
||||
)
|
||||
};
|
||||
current_offset = hole;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// Perform a sparse copy from one file to another.
|
||||
/// Creates a holes for large sequences of zeros in non_sparse_files, used for --sparse=always
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn sparse_copy<P>(source: P, dest: P) -> std::io::Result<()>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
use std::os::unix::prelude::MetadataExt;
|
||||
|
||||
let mut src_file = File::open(source)?;
|
||||
let dst_file = File::create(dest)?;
|
||||
let dst_fd = dst_file.as_raw_fd();
|
||||
|
@ -97,6 +212,18 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
/// Checks whether an existing destination is a fifo
|
||||
fn check_dest_is_fifo(dest: &Path) -> bool {
|
||||
// If our destination file exists and its a fifo , we do a standard copy .
|
||||
let file_type = std::fs::metadata(dest);
|
||||
match file_type {
|
||||
Ok(f) => f.file_type().is_fifo(),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy the contents of the given source FIFO to the given file.
|
||||
fn copy_fifo_contents<P>(source: P, dest: P) -> std::io::Result<u64>
|
||||
where
|
||||
|
@ -151,35 +278,121 @@ pub(crate) fn copy_on_write(
|
|||
reflink: OffloadReflinkDebug::Unsupported,
|
||||
sparse_detection: SparseDebug::No,
|
||||
};
|
||||
|
||||
let result = match (reflink_mode, sparse_mode) {
|
||||
(ReflinkMode::Never, SparseMode::Always) => {
|
||||
copy_debug.sparse_detection = SparseDebug::Zeros;
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
// Default SparseDebug val for SparseMode::Always
|
||||
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||
sparse_copy(source, dest)
|
||||
}
|
||||
(ReflinkMode::Never, _) => {
|
||||
copy_debug.sparse_detection = SparseDebug::No;
|
||||
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||
std::fs::copy(source, dest).map(|_| ())
|
||||
}
|
||||
(ReflinkMode::Auto, SparseMode::Always) => {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
copy_debug.sparse_detection = SparseDebug::Zeros;
|
||||
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
|
||||
sparse_copy(source, dest)
|
||||
}
|
||||
|
||||
(ReflinkMode::Auto, _) => {
|
||||
copy_debug.sparse_detection = SparseDebug::No;
|
||||
copy_debug.reflink = OffloadReflinkDebug::Unsupported;
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let result = handle_reflink_never_sparse_always(source, dest);
|
||||
if let Ok((debug, method)) = result {
|
||||
copy_debug = debug;
|
||||
copy_method = method;
|
||||
}
|
||||
|
||||
match copy_method {
|
||||
CopyMethod::FSCopy => std::fs::copy(source, dest).map(|_| ()),
|
||||
_ => sparse_copy(source, dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
(ReflinkMode::Never, SparseMode::Never) => {
|
||||
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let result = handle_reflink_never_sparse_never(source);
|
||||
if let Ok(debug) = result {
|
||||
copy_debug = debug;
|
||||
}
|
||||
std::fs::copy(source, dest).map(|_| ())
|
||||
}
|
||||
}
|
||||
(ReflinkMode::Never, SparseMode::Auto) => {
|
||||
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let result = handle_reflink_never_sparse_auto(source, dest);
|
||||
if let Ok((debug, method)) = result {
|
||||
copy_debug = debug;
|
||||
copy_method = method;
|
||||
}
|
||||
|
||||
match copy_method {
|
||||
CopyMethod::SparseCopyWithoutHole => sparse_copy_without_hole(source, dest),
|
||||
_ => std::fs::copy(source, dest).map(|_| ()),
|
||||
}
|
||||
}
|
||||
}
|
||||
(ReflinkMode::Auto, SparseMode::Always) => {
|
||||
copy_debug.sparse_detection = SparseDebug::Zeros; // Default SparseDebug val for
|
||||
// SparseMode::Always
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let result = handle_reflink_auto_sparse_always(source, dest);
|
||||
if let Ok((debug, method)) = result {
|
||||
copy_debug = debug;
|
||||
copy_method = method;
|
||||
}
|
||||
|
||||
match copy_method {
|
||||
CopyMethod::FSCopy => clone(source, dest, CloneFallback::FSCopy),
|
||||
_ => clone(source, dest, CloneFallback::SparseCopy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ReflinkMode::Auto, SparseMode::Never) => {
|
||||
copy_debug.reflink = OffloadReflinkDebug::No;
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let result = handle_reflink_auto_sparse_never(source);
|
||||
if let Ok(debug) = result {
|
||||
copy_debug = debug;
|
||||
}
|
||||
|
||||
clone(source, dest, CloneFallback::FSCopy)
|
||||
}
|
||||
}
|
||||
(ReflinkMode::Auto, SparseMode::Auto) => {
|
||||
if source_is_fifo {
|
||||
copy_debug.offload = OffloadReflinkDebug::Unsupported;
|
||||
copy_fifo_contents(source, dest).map(|_| ())
|
||||
} else {
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let result = handle_reflink_auto_sparse_auto(source, dest);
|
||||
if let Ok((debug, method)) = result {
|
||||
copy_debug = debug;
|
||||
copy_method = method;
|
||||
}
|
||||
|
||||
match copy_method {
|
||||
CopyMethod::SparseCopyWithoutHole => {
|
||||
clone(source, dest, CloneFallback::SparseCopyWithoutHole)
|
||||
}
|
||||
_ => clone(source, dest, CloneFallback::FSCopy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(ReflinkMode::Always, SparseMode::Auto) => {
|
||||
copy_debug.sparse_detection = SparseDebug::No;
|
||||
copy_debug.reflink = OffloadReflinkDebug::Yes;
|
||||
|
@ -193,3 +406,211 @@ pub(crate) fn copy_on_write(
|
|||
result.context(context)?;
|
||||
Ok(copy_debug)
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=auto" and "--sparse=always" and specifies what
|
||||
/// type of copy should be used
|
||||
fn handle_reflink_auto_sparse_always(
|
||||
source: &Path,
|
||||
dest: &Path,
|
||||
) -> Result<(CopyDebug, CopyMethod), std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::Unsupported,
|
||||
sparse_detection: SparseDebug::Zeros,
|
||||
};
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let (data_flag, size, blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
if data_flag || size < 512 {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
}
|
||||
match (sparse_flag, data_flag, blocks) {
|
||||
(true, true, 0) => {
|
||||
// Handling funny files with 0 block allocation but has data
|
||||
// in it
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHoleZeros;
|
||||
}
|
||||
(false, true, 0) => copy_method = CopyMethod::FSCopy,
|
||||
|
||||
(true, false, 0) => copy_debug.sparse_detection = SparseDebug::SeekHole,
|
||||
(true, true, _) => copy_debug.sparse_detection = SparseDebug::SeekHoleZeros,
|
||||
|
||||
(true, false, _) => copy_debug.sparse_detection = SparseDebug::SeekHole,
|
||||
|
||||
(_, _, _) => (),
|
||||
}
|
||||
if check_dest_is_fifo(dest) {
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
}
|
||||
Ok((copy_debug, copy_method))
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=auto" and "--sparse=auto" and specifies what
|
||||
/// type of copy should be used
|
||||
fn handle_reflink_never_sparse_never(source: &Path) -> Result<CopyDebug, std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::No,
|
||||
sparse_detection: SparseDebug::No,
|
||||
};
|
||||
let (data_flag, size, _blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
if sparse_flag {
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHole;
|
||||
}
|
||||
|
||||
if data_flag || size < 512 {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
}
|
||||
Ok(copy_debug)
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=auto" and "--sparse=never", files will be copied
|
||||
/// through cloning them with fallback switching to std::fs::copy
|
||||
fn handle_reflink_auto_sparse_never(source: &Path) -> Result<CopyDebug, std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::No,
|
||||
sparse_detection: SparseDebug::No,
|
||||
};
|
||||
|
||||
let (data_flag, size, _blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
if sparse_flag {
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHole;
|
||||
}
|
||||
|
||||
if data_flag || size < 512 {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
}
|
||||
Ok(copy_debug)
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=auto" and "--sparse=auto" and specifies what
|
||||
/// type of copy should be used
|
||||
fn handle_reflink_auto_sparse_auto(
|
||||
source: &Path,
|
||||
dest: &Path,
|
||||
) -> Result<(CopyDebug, CopyMethod), std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::Unsupported,
|
||||
sparse_detection: SparseDebug::No,
|
||||
};
|
||||
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
let (data_flag, size, blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
if (data_flag && size != 0) || (size > 0 && size < 512) {
|
||||
copy_debug.offload = OffloadReflinkDebug::Yes;
|
||||
}
|
||||
|
||||
if data_flag && size == 0 {
|
||||
// Handling /proc/ files
|
||||
copy_debug.offload = OffloadReflinkDebug::Unsupported;
|
||||
}
|
||||
if sparse_flag {
|
||||
if blocks == 0 && data_flag {
|
||||
// Handling other "virtual" files
|
||||
copy_debug.offload = OffloadReflinkDebug::Unsupported;
|
||||
|
||||
copy_method = CopyMethod::FSCopy; // Doing a standard copy for the virtual files
|
||||
} else {
|
||||
copy_method = CopyMethod::SparseCopyWithoutHole;
|
||||
} // Since sparse_flag is true, sparse_detection shall be SeekHole for any non virtual
|
||||
// regular sparse file and the file will be sparsely copied
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHole;
|
||||
}
|
||||
|
||||
if check_dest_is_fifo(dest) {
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
}
|
||||
Ok((copy_debug, copy_method))
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=never" and "--sparse=auto" and specifies what
|
||||
/// type of copy should be used
|
||||
fn handle_reflink_never_sparse_auto(
|
||||
source: &Path,
|
||||
dest: &Path,
|
||||
) -> Result<(CopyDebug, CopyMethod), std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::No,
|
||||
sparse_detection: SparseDebug::No,
|
||||
};
|
||||
|
||||
let (data_flag, size, blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
let mut copy_method = CopyMethod::Default;
|
||||
if data_flag || size < 512 {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
}
|
||||
|
||||
if sparse_flag {
|
||||
if blocks == 0 && data_flag {
|
||||
copy_method = CopyMethod::FSCopy; // Handles virtual files which have size > 0 but no
|
||||
// disk allocation
|
||||
} else {
|
||||
copy_method = CopyMethod::SparseCopyWithoutHole; // Handles regular sparse-files
|
||||
}
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHole;
|
||||
}
|
||||
|
||||
if check_dest_is_fifo(dest) {
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
}
|
||||
Ok((copy_debug, copy_method))
|
||||
}
|
||||
|
||||
/// Handles debug results when flags are "--reflink=never" and "--sparse=always" and specifies what
|
||||
/// type of copy should be used
|
||||
fn handle_reflink_never_sparse_always(
|
||||
source: &Path,
|
||||
dest: &Path,
|
||||
) -> Result<(CopyDebug, CopyMethod), std::io::Error> {
|
||||
let mut copy_debug = CopyDebug {
|
||||
offload: OffloadReflinkDebug::Unknown,
|
||||
reflink: OffloadReflinkDebug::No,
|
||||
sparse_detection: SparseDebug::Zeros,
|
||||
};
|
||||
let mut copy_method = CopyMethod::SparseCopy;
|
||||
|
||||
let (data_flag, size, blocks) = check_for_data(source)?;
|
||||
let sparse_flag = check_sparse_detection(source)?;
|
||||
|
||||
if data_flag || size < 512 {
|
||||
copy_debug.offload = OffloadReflinkDebug::Avoided;
|
||||
}
|
||||
match (sparse_flag, data_flag, blocks) {
|
||||
(true, true, 0) => {
|
||||
// Handling funny files with 0 block allocation but has data
|
||||
// in it, e.g. files in /sys and other virtual files
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHoleZeros;
|
||||
}
|
||||
(false, true, 0) => copy_method = CopyMethod::FSCopy, // Handling data containing zero sized
|
||||
// files in /proc
|
||||
(true, false, 0) => copy_debug.sparse_detection = SparseDebug::SeekHole, // Handles files
|
||||
// with 0 blocks allocated in disk and
|
||||
(true, true, _) => copy_debug.sparse_detection = SparseDebug::SeekHoleZeros, // Any
|
||||
// sparse_files with data in it will display SeekHoleZeros
|
||||
(true, false, _) => {
|
||||
copy_debug.offload = OffloadReflinkDebug::Unknown;
|
||||
copy_debug.sparse_detection = SparseDebug::SeekHole;
|
||||
}
|
||||
|
||||
(_, _, _) => (),
|
||||
}
|
||||
if check_dest_is_fifo(dest) {
|
||||
copy_method = CopyMethod::FSCopy;
|
||||
}
|
||||
|
||||
Ok((copy_debug, copy_method))
|
||||
}
|
||||
|
|
|
@ -3581,7 +3581,7 @@ fn test_cp_debug_sparse_never() {
|
|||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unknown, reflink: unsupported, sparse detection: no") {
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
@ -3831,6 +3831,642 @@ fn test_acl_preserve() {
|
|||
|
||||
assert!(compare_xattrs(&file, &file_target));
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.write("a", "hello");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
let src_file_metadata = std::fs::metadata(at.plus("a")).unwrap();
|
||||
if dst_file_metadata.blocks() != src_file_metadata.blocks() {
|
||||
panic!("File not sparsely copied");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_empty_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
let src_file_metadata = std::fs::metadata(at.plus("a")).unwrap();
|
||||
if dst_file_metadata.blocks() != src_file_metadata.blocks() {
|
||||
panic!("File not sparsely copied");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unknown, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_default_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let result = ts.ucmd().arg("--debug").arg("a").arg("b").succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
let src_file_metadata = std::fs::metadata(at.plus("a")).unwrap();
|
||||
if dst_file_metadata.blocks() != src_file_metadata.blocks() {
|
||||
panic!("File not sparsely copied");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: yes, reflink: unsupported, sparse detection: SEEK_HOLE")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_default_less_than_512_bytes() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(400).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=auto")
|
||||
.arg("--sparse=auto")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: yes, reflink: unsupported, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_default_without_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
|
||||
let filler_bytes = [0_u8; 10000];
|
||||
|
||||
at.append_bytes("a", &filler_bytes);
|
||||
|
||||
let result = ts.ucmd().arg("--debug").arg("a").arg("b").succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: yes, reflink: unsupported, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_default_empty_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts.ucmd().arg("--debug").arg("a").arg("b").succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
let src_file_metadata = std::fs::metadata(at.plus("a")).unwrap();
|
||||
if dst_file_metadata.blocks() != src_file_metadata.blocks() {
|
||||
panic!("File not sparsely copied");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str
|
||||
.contains("copy offload: unknown, reflink: unsupported, sparse detection: SEEK_HOLE")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_sparse_always_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.write("a", "hello");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=always")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
let src_file_metadata = std::fs::metadata(at.plus("a")).unwrap();
|
||||
if dst_file_metadata.blocks() != src_file_metadata.blocks() {
|
||||
panic!("File not sparsely copied");
|
||||
}
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str
|
||||
.contains("copy offload: avoided, reflink: no, sparse detection: SEEK_HOLE + zeros")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_sparse_always_without_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let empty_bytes = [0_u8; 10000];
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.write("a", "hello");
|
||||
at.append_bytes("a", &empty_bytes);
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=always")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
|
||||
if dst_file_metadata.blocks() != dst_file_metadata.blksize() / 512 {
|
||||
panic!("Zero sequenced blocks not removed");
|
||||
}
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: zeros") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_sparse_always_empty_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=always")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unknown, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_default_virtual_file() {
|
||||
use std::os::unix::prelude::MetadataExt;
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
ts.ucmd()
|
||||
.arg("/sys/kernel/address_bits")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let dest_size = std::fs::metadata(at.plus("b"))
|
||||
.expect("Metadata of copied file cannot be read")
|
||||
.size();
|
||||
if dest_size == 0 {
|
||||
panic!("Copy unsuccessful");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_auto_sparse_always_non_sparse_file_with_long_zero_sequence() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let buf: Vec<u8> = vec![0; 4096 * 4];
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", &buf);
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--sparse=always")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
|
||||
if dst_file_metadata.blocks() != dst_file_metadata.blksize() / 512 {
|
||||
panic!("Zero sequenced blocks not removed");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: unsupported, sparse detection: zeros")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_debug_sparse_never_empty_sparse_file() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_sparse_always_non_sparse_file_with_long_zero_sequence() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let buf: Vec<u8> = vec![0; 4096 * 4];
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", &buf);
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=always")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let dst_file_metadata = std::fs::metadata(at.plus("b")).unwrap();
|
||||
|
||||
if dst_file_metadata.blocks() != dst_file_metadata.blksize() / 512 {
|
||||
panic!("Zero sequenced blocks not removed");
|
||||
}
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: zeros") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_debug_sparse_always_sparse_virtual_file() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--sparse=always")
|
||||
.arg("/sys/kernel/address_bits")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains(
|
||||
"copy offload: avoided, reflink: unsupported, sparse detection: SEEK_HOLE + zeros",
|
||||
) {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_less_than_512_bytes() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(400).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_sparse_never_empty_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unknown, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_sparse_never_less_than_512_bytes() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(400).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=auto")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_sparse_never_without_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
|
||||
let filler_bytes = [0_u8; 10000];
|
||||
|
||||
at.append_bytes("a", &filler_bytes);
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--reflink=auto")
|
||||
.arg("--sparse=never")
|
||||
.arg("--debug")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_sparse_never_empty_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=auto")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unknown, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_sparse_never_file_with_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
let f = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(at.plus("a"))
|
||||
.unwrap();
|
||||
f.set_len(10000).unwrap();
|
||||
at.append_bytes("a", "hello".as_bytes());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=auto")
|
||||
.arg("--sparse=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: SEEK_HOLE") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_debug_default_sparse_virtual_file() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("/sys/kernel/address_bits")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str
|
||||
.contains("copy offload: unsupported, reflink: unsupported, sparse detection: SEEK_HOLE")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_debug_sparse_never_zero_sized_virtual_file() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--sparse=never")
|
||||
.arg("/proc/version")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn test_cp_debug_default_zero_sized_virtual_file() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("/proc/version")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: unsupported, reflink: unsupported, sparse detection: no")
|
||||
{
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn test_cp_debug_reflink_never_without_hole() {
|
||||
let ts = TestScenario::new(util_name!());
|
||||
let filler_bytes = [0_u8; 1000];
|
||||
let at = &ts.fixtures;
|
||||
at.touch("a");
|
||||
at.write("a", "hello");
|
||||
at.append_bytes("a", &filler_bytes);
|
||||
let result = ts
|
||||
.ucmd()
|
||||
.arg("--debug")
|
||||
.arg("--reflink=never")
|
||||
.arg("a")
|
||||
.arg("b")
|
||||
.succeeds();
|
||||
|
||||
let stdout_str = result.stdout_str();
|
||||
if !stdout_str.contains("copy offload: avoided, reflink: no, sparse detection: no") {
|
||||
panic!("Failure: stdout was \n{stdout_str}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cp_force_remove_destination_attributes_only_with_symlink() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue