Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ include = [
"CHANGELOG.md",
]

[features]
reflink = ["dep:reflink"]

[dependencies]
reflink = { version = "^0.1.0", optional = true }
37 changes: 13 additions & 24 deletions src/dir.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use crate::error::*;
use std::collections::{HashMap, HashSet};
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,
Expand All @@ -21,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 {
Expand All @@ -43,6 +49,8 @@ impl CopyOptions {
copy_inside: false,
content_only: false,
depth: 0,
#[cfg(feature = "reflink")]
reflink: RefLinkUsage::Never,
}
}

Expand Down Expand Up @@ -615,11 +623,7 @@ where
let tp = Path::new(&file).strip_prefix(from)?;
let path = to.join(&tp);

let file_options = super::file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};
let file_options = super::file::CopyOptions::from(options);
let mut result_copy: Result<u64>;
let mut work = true;

Expand Down Expand Up @@ -927,12 +931,7 @@ where
let file_name = file_name.unwrap();
to.push(file_name);

let mut file_options = super::file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};

let mut file_options = super::file::CopyOptions::from(&options);
if let Some(file_name) = file_name.to_str() {
info_process.file_name = file_name.to_string();
} else {
Expand Down Expand Up @@ -1122,12 +1121,7 @@ where
let tp = Path::new(&file).strip_prefix(from)?;
let path = to.join(&tp);

let file_options = super::file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};

let file_options = super::file::CopyOptions::from(options);
let mut result_copy: Result<u64>;
let mut work = true;
while work {
Expand Down Expand Up @@ -1264,12 +1258,7 @@ where
let file_name = file_name.unwrap();
to.push(file_name);

let mut file_options = super::file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};

let mut file_options = super::file::CopyOptions::from(&options);
if let Some(file_name) = file_name.to_str() {
info_process.file_name = file_name.to_string();
} else {
Expand Down
52 changes: 51 additions & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
use crate::error::{Error, ErrorKind, Result};
use std;
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)]
pub struct CopyOptions {
/// Sets the option true for overwrite existing files.
pub overwrite: bool,
/// Sets the option true for skip existing files.
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 {
Expand All @@ -30,6 +37,8 @@ impl CopyOptions {
overwrite: false,
skip_exist: false,
buffer_size: 64000, //64kb
#[cfg(feature = "reflink")]
reflink: RefLinkUsage::Never,
}
}

Expand Down Expand Up @@ -58,6 +67,18 @@ impl Default for CopyOptions {
}
}

impl From<&super::dir::CopyOptions> for CopyOptions {
fn from(dir_options: &super::dir::CopyOptions) -> Self {
CopyOptions {
overwrite: dir_options.overwrite,
skip_exist: dir_options.skip_exist,
buffer_size: dir_options.buffer_size,
#[cfg(feature = "reflink")]
reflink: dir_options.reflink,
}
}
}

/// A structure which stores information about the current status of a file that's copied or moved. .
pub struct TransitProcess {
/// Copied bytes on this time.
Expand Down Expand Up @@ -124,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.
Expand Down Expand Up @@ -193,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();
Expand Down
36 changes: 21 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "reflink")]
extern crate reflink;

macro_rules! err {
($text:expr, $kind:expr) => {
return Err(Error::new($kind, $text))
Expand Down Expand Up @@ -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).
Expand Down Expand Up @@ -350,11 +368,7 @@ where
};
result += dir::copy_with_progress(item, &to, &dir_options, handler)?;
} else {
let mut file_options = file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};
let mut file_options = file::CopyOptions::from(&options);

if let Some(file_name) = item.file_name() {
if let Some(file_name) = file_name.to_str() {
Expand Down Expand Up @@ -537,11 +551,7 @@ where

result += dir::move_dir(item, &to, options)?;
} else {
let file_options = file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};
let file_options = file::CopyOptions::from(options);

if let Some(file_name) = item.file_name() {
if let Some(file_name) = file_name.to_str() {
Expand Down Expand Up @@ -662,11 +672,7 @@ where
};
result += dir::move_dir_with_progress(item, &to, &dir_options, handler)?;
} else {
let mut file_options = file::CopyOptions {
overwrite: options.overwrite,
skip_exist: options.skip_exist,
buffer_size: options.buffer_size,
};
let mut file_options = file::CopyOptions::from(&options);

if let Some(file_name) = item.file_name() {
if let Some(file_name) = file_name.to_str() {
Expand Down