diff --git a/Cargo.toml b/Cargo.toml index e5af6fd..5958728 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,8 @@ include = [ "CHANGELOG.md", ] +[features] +reflink = ["dep:reflink"] + [dependencies] +reflink = { version = "^0.1.0", optional = true } diff --git a/src/dir.rs b/src/dir.rs index 5920012..c7906c0 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -4,9 +4,11 @@ use std::convert::From; use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata}; use std::path::{Path, PathBuf}; use std::time::SystemTime; +#[cfg(feature = "reflink")] +use super::RefLinkUsage; /// Options and flags which can be used to configure how a file will be copied or moved. -#[derive(Clone)] +#[derive(Clone, Copy)] pub struct CopyOptions { /// Overwrite existing files if true (default: false). pub overwrite: bool, @@ -22,6 +24,9 @@ pub struct CopyOptions { /// /// Warning: Work only for copy operations! pub depth: u64, + /// Controls the usage of reflinks for files on filesystems supporting it. + #[cfg(feature = "reflink")] + pub reflink: RefLinkUsage, } impl CopyOptions { @@ -44,6 +49,8 @@ impl CopyOptions { copy_inside: false, content_only: false, depth: 0, + #[cfg(feature = "reflink")] + reflink: RefLinkUsage::Never, } } diff --git a/src/file.rs b/src/file.rs index ccc8bf0..1d1f7f2 100644 --- a/src/file.rs +++ b/src/file.rs @@ -4,6 +4,8 @@ use std::convert::From; use std::fs::{remove_file, File}; use std::io::{Read, Write}; use std::path::Path; +#[cfg(feature = "reflink")] +use super::RefLinkUsage; // Options and flags which can be used to configure how a file will be copied or moved. #[derive(Debug, Copy, Clone)] @@ -14,6 +16,9 @@ pub struct CopyOptions { pub skip_exist: bool, /// Sets buffer size for copy/move work only with receipt information about process work. pub buffer_size: usize, + /// Controls the usage of reflinks on filesystems supporting it. + #[cfg(feature = "reflink")] + pub reflink: RefLinkUsage, } impl CopyOptions { @@ -32,6 +37,8 @@ impl CopyOptions { overwrite: false, skip_exist: false, buffer_size: 64000, //64kb + #[cfg(feature = "reflink")] + reflink: RefLinkUsage::Never, } } @@ -66,6 +73,8 @@ impl From<&super::dir::CopyOptions> for CopyOptions { overwrite: dir_options.overwrite, skip_exist: dir_options.skip_exist, buffer_size: dir_options.buffer_size, + #[cfg(feature = "reflink")] + reflink: dir_options.reflink, } } } @@ -136,7 +145,24 @@ where } } - Ok(std::fs::copy(from, to)?) + Ok( + #[cfg(not(feature = "reflink"))] + { std::fs::copy(from, to)? }, + + #[cfg(feature = "reflink")] + match options.reflink { + RefLinkUsage::Never => std::fs::copy(from, to)?, + + #[cfg(feature = "reflink")] + RefLinkUsage::Auto => reflink::reflink_or_copy(from, to)?.unwrap_or(0), + + #[cfg(feature = "reflink")] + RefLinkUsage::Always => { + reflink::reflink(from, to)?; + 0 + }, + } + ) } /// Copies the contents of one file to another file with information about progress. @@ -205,6 +231,18 @@ where err!(&msg, ErrorKind::AlreadyExists); } } + + #[cfg(feature = "reflink")] + if options.reflink != RefLinkUsage::Never { + match reflink::reflink(&from, &to) { + Ok(()) => return Ok(0), + Err(e) if options.reflink == RefLinkUsage::Always => { + return Err(::std::convert::From::from(e)); + }, + Err(_) => { /* continue with plain copy */ } + } + } + let mut file_from = File::open(from)?; let mut buf = vec![0; options.buffer_size]; let file_size = file_from.metadata()?.len(); diff --git a/src/lib.rs b/src/lib.rs index e07a9fe..7d94e0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "reflink")] +extern crate reflink; + macro_rules! err { ($text:expr, $kind:expr) => { return Err(Error::new($kind, $text)) @@ -156,6 +159,21 @@ pub mod dir; use crate::error::*; use std::path::Path; +/// Possible values for the reflink field in CopyOptions. These +/// correspond to the `--reflink` option of the Unix `cp` command. +#[cfg(feature = "reflink")] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum RefLinkUsage { + /// Do not use reflinks. + Never, + /// Use reflinks if possible. + #[cfg(feature = "reflink")] + Auto, + /// Force use of reflinks, error out if not possible. + #[cfg(feature = "reflink")] + Always, +} + /// Copies a list of directories and files to another place recursively. This function will /// also copy the permission bits of the original files to destination files (not for /// directories).