Skip to content

Commit 9e0b2cf

Browse files
authored
Fail reflink_or_copy if target file already exist (#81)
1 parent cfe3de0 commit 9e0b2cf

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
/target
22
**/*.rs.bk
3+
4+
# JetBrains
5+
/.idea

examples/clone_or_copy.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// cargo run --example clone_or_copy V:\file.bin V:\file-clone.bin
2+
3+
fn main() -> std::io::Result<()> {
4+
let args: Vec<_> = std::env::args().collect();
5+
if args.len() < 3 {
6+
eprintln!("Usage: {} <source_file> <target_file>", args[0]);
7+
return Ok(());
8+
}
9+
let src_file = &args[1];
10+
let tgt_file = &args[2];
11+
12+
let result = reflink_copy::reflink_or_copy(src_file, tgt_file)?;
13+
match result {
14+
Some(_) => println!("File has been copied"),
15+
None => println!("File has been cloned"),
16+
}
17+
Ok(())
18+
}

examples/clonefile.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// cargo run --example clonefile V:\file.bin V:\file-clone.bin
2+
3+
fn main() -> std::io::Result<()> {
4+
let args: Vec<_> = std::env::args().collect();
5+
if args.len() < 3 {
6+
eprintln!("Usage: {} <source_file> <target_file>", args[0]);
7+
return Ok(());
8+
}
9+
let src_file = &args[1];
10+
let tgt_file = &args[2];
11+
12+
reflink_copy::reflink(src_file, tgt_file)
13+
}

src/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod sys;
1818

1919
use std::fs;
2020
use std::io;
21+
use std::io::ErrorKind;
2122
use std::path::Path;
2223

2324
/// Copies a file using COW semantics.
@@ -93,6 +94,8 @@ pub fn reflink(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
9394
///
9495
/// If the function copied a file, the return value will be `Ok(Some(written))`.
9596
///
97+
/// If target file already exists, operation fails with [`ErrorKind::AlreadyExists`].
98+
///
9699
/// ```rust
97100
/// match reflink_copy::reflink_or_copy("src.txt", "dest.txt") {
98101
/// Ok(None) => println!("file has been reflinked"),
@@ -116,13 +119,20 @@ pub fn reflink_or_copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Resu
116119
tracing_attributes::instrument(name = "reflink_or_copy")
117120
)]
118121
fn inner(from: &Path, to: &Path) -> io::Result<Option<u64>> {
119-
if let Err(_err) = sys::reflink(from, to) {
122+
if let Err(err) = sys::reflink(from, to) {
123+
match err.kind() {
124+
ErrorKind::NotFound | ErrorKind::PermissionDenied | ErrorKind::AlreadyExists => {
125+
return Err(err);
126+
}
127+
_ => {}
128+
}
129+
120130
#[cfg(feature = "tracing")]
121-
tracing::warn!(?_err, "Failed to reflink, fallback to fs::copy");
131+
tracing::warn!(?err, "Failed to reflink, fallback to fs::copy");
122132

123133
fs::copy(from, to).map(Some).map_err(|err| {
124134
// Both regular files and symlinks to regular files can be copied, so unlike
125-
// `reflink` we don't want to report invalid input on both files and and symlinks
135+
// `reflink` we don't want to report invalid input on both files and symlinks
126136
if from.is_file() {
127137
err
128138
} else {

0 commit comments

Comments
 (0)