Skip to content

Commit eada7da

Browse files
committed
Add the option to use reflinks (i.e. copy on write semantics).
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".
1 parent 8651b23 commit eada7da

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

Cargo.toml

+4
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,8 @@ include = [
1717
"CHANGELOG.md",
1818
]
1919

20+
[features]
21+
reflink = ["dep:reflink"]
22+
2023
[dependencies]
24+
reflink = { version = "^0.1.0", optional = true }

src/dir.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use std::convert::From;
44
use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, Metadata};
55
use std::path::{Path, PathBuf};
66
use std::time::SystemTime;
7+
#[cfg(feature = "reflink")]
8+
use super::RefLinkUsage;
79

810
/// Options and flags which can be used to configure how a file will be copied or moved.
9-
#[derive(Clone)]
11+
#[derive(Clone, Copy)]
1012
pub struct CopyOptions {
1113
/// Overwrite existing files if true (default: false).
1214
pub overwrite: bool,
@@ -22,6 +24,9 @@ pub struct CopyOptions {
2224
///
2325
/// Warning: Work only for copy operations!
2426
pub depth: u64,
27+
/// Controls the usage of reflinks for files on filesystems supporting it.
28+
#[cfg(feature = "reflink")]
29+
pub reflink: RefLinkUsage,
2530
}
2631

2732
impl CopyOptions {
@@ -44,6 +49,8 @@ impl CopyOptions {
4449
copy_inside: false,
4550
content_only: false,
4651
depth: 0,
52+
#[cfg(feature = "reflink")]
53+
reflink: RefLinkUsage::Never,
4754
}
4855
}
4956

src/file.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::convert::From;
44
use std::fs::{remove_file, File};
55
use std::io::{Read, Write};
66
use std::path::Path;
7+
#[cfg(feature = "reflink")]
8+
use super::RefLinkUsage;
79

810
// Options and flags which can be used to configure how a file will be copied or moved.
911
#[derive(Debug, Copy, Clone)]
@@ -14,6 +16,9 @@ pub struct CopyOptions {
1416
pub skip_exist: bool,
1517
/// Sets buffer size for copy/move work only with receipt information about process work.
1618
pub buffer_size: usize,
19+
/// Controls the usage of reflinks on filesystems supporting it.
20+
#[cfg(feature = "reflink")]
21+
pub reflink: RefLinkUsage,
1722
}
1823

1924
impl CopyOptions {
@@ -32,6 +37,8 @@ impl CopyOptions {
3237
overwrite: false,
3338
skip_exist: false,
3439
buffer_size: 64000, //64kb
40+
#[cfg(feature = "reflink")]
41+
reflink: RefLinkUsage::Never,
3542
}
3643
}
3744

@@ -66,6 +73,8 @@ impl From<&super::dir::CopyOptions> for CopyOptions {
6673
overwrite: dir_options.overwrite,
6774
skip_exist: dir_options.skip_exist,
6875
buffer_size: dir_options.buffer_size,
76+
#[cfg(feature = "reflink")]
77+
reflink: dir_options.reflink,
6978
}
7079
}
7180
}
@@ -136,7 +145,24 @@ where
136145
}
137146
}
138147

139-
Ok(std::fs::copy(from, to)?)
148+
Ok(
149+
#[cfg(not(feature = "reflink"))]
150+
{ std::fs::copy(from, to)? },
151+
152+
#[cfg(feature = "reflink")]
153+
match options.reflink {
154+
RefLinkUsage::Never => std::fs::copy(from, to)?,
155+
156+
#[cfg(feature = "reflink")]
157+
RefLinkUsage::Auto => reflink::reflink_or_copy(from, to)?.unwrap_or(0),
158+
159+
#[cfg(feature = "reflink")]
160+
RefLinkUsage::Always => {
161+
reflink::reflink(from, to)?;
162+
0
163+
},
164+
}
165+
)
140166
}
141167

142168
/// Copies the contents of one file to another file with information about progress.
@@ -205,6 +231,18 @@ where
205231
err!(&msg, ErrorKind::AlreadyExists);
206232
}
207233
}
234+
235+
#[cfg(feature = "reflink")]
236+
if options.reflink != RefLinkUsage::Never {
237+
match reflink::reflink(&from, &to) {
238+
Ok(()) => return Ok(0),
239+
Err(e) if options.reflink == RefLinkUsage::Always => {
240+
return Err(::std::convert::From::from(e));
241+
},
242+
Err(_) => { /* continue with plain copy */ }
243+
}
244+
}
245+
208246
let mut file_from = File::open(from)?;
209247
let mut buf = vec![0; options.buffer_size];
210248
let file_size = file_from.metadata()?.len();

src/lib.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
#[cfg(feature = "reflink")]
2+
extern crate reflink;
3+
14
macro_rules! err {
25
($text:expr, $kind:expr) => {
36
return Err(Error::new($kind, $text))
@@ -156,6 +159,21 @@ pub mod dir;
156159
use crate::error::*;
157160
use std::path::Path;
158161

162+
/// Possible values for the reflink field in CopyOptions. These
163+
/// correspond to the `--reflink` option of the Unix `cp` command.
164+
#[cfg(feature = "reflink")]
165+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
166+
pub enum RefLinkUsage {
167+
/// Do not use reflinks.
168+
Never,
169+
/// Use reflinks if possible.
170+
#[cfg(feature = "reflink")]
171+
Auto,
172+
/// Force use of reflinks, error out if not possible.
173+
#[cfg(feature = "reflink")]
174+
Always,
175+
}
176+
159177
/// Copies a list of directories and files to another place recursively. This function will
160178
/// also copy the permission bits of the original files to destination files (not for
161179
/// directories).

0 commit comments

Comments
 (0)