Skip to content

Commit

Permalink
cp: add support of cp --debug
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvestre committed May 29, 2023
1 parent e295cb7 commit f2006a9
Show file tree
Hide file tree
Showing 5 changed files with 391 additions and 16 deletions.
85 changes: 83 additions & 2 deletions src/uu/cp/src/cp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,79 @@ pub struct Options {
backup_suffix: String,
target_dir: Option<PathBuf>,
update: UpdateMode,
debug: bool,
verbose: bool,
progress_bar: bool,
}

/// Enum representing various debug states of the offload and reflink actions.
#[derive(Debug)]
#[allow(dead_code)] // All of them are used on Linux
enum OffloadReflinkDebug {
Unknown,
No,
Yes,
Avoided,
Unsupported,
}

/// Enum representing various debug states of the sparse detection.
#[derive(Debug)]
#[allow(dead_code)] // silent for now until we use them
enum SparseDebug {
Unknown,
No,
Zeros,
SeekHole,
SeekHoleZeros,
Unsupported,
}

/// Struct that contains the debug state for each action in a file copy operation.
#[derive(Debug)]
struct CopyDebug {
offload: OffloadReflinkDebug,
reflink: OffloadReflinkDebug,
sparse_detection: SparseDebug,
}

impl OffloadReflinkDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Yes => "yes",
Self::Avoided => "avoided",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
}
}

impl SparseDebug {
fn to_string(&self) -> &'static str {
match self {
Self::No => "no",
Self::Zeros => "zeros",
Self::SeekHole => "SEEK_HOLE",
Self::SeekHoleZeros => "SEEK_HOLE + zeros",
Self::Unsupported => "unsupported",
Self::Unknown => "unknown",
}
}
}

/// This function prints the debug information of a file copy operation if
/// no hard link or symbolic link is required, and data copy is required.
/// It prints the debug information of the offload, reflink, and sparse detection actions.
fn show_debug(copy_debug: &CopyDebug) {
println!(
"copy offload: {}, reflink: {}, sparse detection: {}",
copy_debug.offload.to_string(),
copy_debug.reflink.to_string(),
copy_debug.sparse_detection.to_string(),
);
}

const ABOUT: &str = help_about!("cp.md");
const USAGE: &str = help_usage!("cp.md");
const AFTER_HELP: &str = help_section!("after help", "cp.md");
Expand Down Expand Up @@ -269,6 +338,7 @@ mod options {
pub const STRIP_TRAILING_SLASHES: &str = "strip-trailing-slashes";
pub const SYMBOLIC_LINK: &str = "symbolic-link";
pub const TARGET_DIRECTORY: &str = "target-directory";
pub const DEBUG: &str = "debug";
pub const VERBOSE: &str = "verbose";
}

Expand Down Expand Up @@ -361,6 +431,12 @@ pub fn uu_app() -> Command {
.help("remove any trailing slashes from each SOURCE argument")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::DEBUG)
.long(options::DEBUG)
.help("explain how a file is copied. Implies -v")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::VERBOSE)
.short('v')
Expand Down Expand Up @@ -831,7 +907,8 @@ impl Options {
one_file_system: matches.get_flag(options::ONE_FILE_SYSTEM),
parents: matches.get_flag(options::PARENTS),
update: update_mode,
verbose: matches.get_flag(options::VERBOSE),
debug: matches.get_flag(options::DEBUG),
verbose: matches.get_flag(options::VERBOSE) || matches.get_flag(options::DEBUG),
strip_trailing_slashes: matches.get_flag(options::STRIP_TRAILING_SLASHES),
reflink_mode: {
if let Some(reflink) = matches.get_one::<String>(options::REFLINK) {
Expand Down Expand Up @@ -1745,7 +1822,7 @@ fn copy_helper(
} else if source_is_symlink {
copy_link(source, dest, symlinked_files)?;
} else {
copy_on_write(
let copy_debug = copy_on_write(
source,
dest,
options.reflink_mode,
Expand All @@ -1754,6 +1831,10 @@ fn copy_helper(
#[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))]
source_is_fifo,
)?;

if !options.attributes_only && options.debug {
show_debug(&copy_debug);
}
}

Ok(())
Expand Down
41 changes: 34 additions & 7 deletions src/uu/cp/src/platform/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use quick_error::ResultExt;

use uucore::mode::get_umask;

use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};

// From /usr/include/linux/fs.h:
// #define FICLONE _IOW(0x94, 9, int)
Expand Down Expand Up @@ -145,24 +145,51 @@ pub(crate) fn copy_on_write(
sparse_mode: SparseMode,
context: &str,
source_is_fifo: bool,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
let mut copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unknown,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::No,
};

let result = match (reflink_mode, sparse_mode) {
(ReflinkMode::Never, SparseMode::Always) => sparse_copy(source, dest),
(ReflinkMode::Never, _) => std::fs::copy(source, dest).map(|_| ()),
(ReflinkMode::Auto, SparseMode::Always) => sparse_copy(source, dest),
(ReflinkMode::Never, SparseMode::Always) => {
copy_debug.sparse_detection = SparseDebug::Zeros;
copy_debug.offload = OffloadReflinkDebug::Avoided;
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_fifo_contents(source, dest).map(|_| ())
} else {
clone(source, dest, CloneFallback::FSCopy)
}
}
(ReflinkMode::Always, SparseMode::Auto) => clone(source, dest, CloneFallback::Error),
(ReflinkMode::Always, SparseMode::Auto) => {
copy_debug.sparse_detection = SparseDebug::No;
copy_debug.reflink = OffloadReflinkDebug::Yes;

clone(source, dest, CloneFallback::Error)
}
(ReflinkMode::Always, _) => {
return Err("`--reflink=always` can be used only with --sparse=auto".into())
}
};
result.context(context)?;
Ok(())
Ok(copy_debug)
}
12 changes: 9 additions & 3 deletions src/uu/cp/src/platform/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::path::Path;

use quick_error::ResultExt;

use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};

/// Copies `source` to `dest` using copy-on-write if possible.
///
Expand All @@ -24,10 +24,15 @@ pub(crate) fn copy_on_write(
sparse_mode: SparseMode,
context: &str,
source_is_fifo: bool,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
}
let mut copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unknown,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::Unsupported,
};

// Extract paths in a form suitable to be passed to a syscall.
// The unwrap() is safe because they come from the command-line and so contain non nul
Expand Down Expand Up @@ -72,6 +77,7 @@ pub(crate) fn copy_on_write(
return Err(format!("failed to clone {source:?} from {dest:?}: {error}").into())
}
_ => {
copy_debug.reflink = OffloadReflinkDebug::Yes;
if source_is_fifo {
let mut src_file = File::open(source)?;
let mut dst_file = File::create(dest)?;
Expand All @@ -83,5 +89,5 @@ pub(crate) fn copy_on_write(
};
}

Ok(())
Ok(copy_debug)
}
12 changes: 8 additions & 4 deletions src/uu/cp/src/platform/other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::path::Path;

use quick_error::ResultExt;

use crate::{CopyResult, ReflinkMode, SparseMode};
use crate::{CopyDebug, CopyResult, OffloadReflinkDebug, ReflinkMode, SparseDebug, SparseMode};

/// Copies `source` to `dest` for systems without copy-on-write
pub(crate) fn copy_on_write(
Expand All @@ -17,7 +17,7 @@ pub(crate) fn copy_on_write(
reflink_mode: ReflinkMode,
sparse_mode: SparseMode,
context: &str,
) -> CopyResult<()> {
) -> CopyResult<CopyDebug> {
if reflink_mode != ReflinkMode::Never {
return Err("--reflink is only supported on linux and macOS"
.to_string()
Expand All @@ -26,8 +26,12 @@ pub(crate) fn copy_on_write(
if sparse_mode != SparseMode::Auto {
return Err("--sparse is only supported on linux".to_string().into());
}

let copy_debug = CopyDebug {
offload: OffloadReflinkDebug::Unsupported,
reflink: OffloadReflinkDebug::Unsupported,
sparse_detection: SparseDebug::Unsupported,
};
fs::copy(source, dest).context(context)?;

Ok(())
Ok(copy_debug)
}
Loading

0 comments on commit f2006a9

Please sign in to comment.