Skip to content

Commit

Permalink
Add the option to use reflinks (i.e. copy on write semantics).
Browse files Browse the repository at this point in the history
Using the reflink crate, with the option to disallow completely
(never), auto (use when available), or always (fail if not possible).
Fully conditional on a new feature "reflink".
  • Loading branch information
mwanner committed Mar 10, 2023
1 parent 4ad3d29 commit fee492e
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
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 }
9 changes: 8 additions & 1 deletion src/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -44,6 +49,8 @@ impl CopyOptions {
copy_inside: false,
content_only: false,
depth: 0,
#[cfg(feature = "reflink")]
reflink: RefLinkUsage::Never,
}
}

Expand Down
40 changes: 39 additions & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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 {
Expand All @@ -32,6 +37,8 @@ impl CopyOptions {
overwrite: false,
skip_exist: false,
buffer_size: 64000, //64kb
#[cfg(feature = "reflink")]
reflink: RefLinkUsage::Never,
}
}

Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down
18 changes: 18 additions & 0 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

0 comments on commit fee492e

Please sign in to comment.