1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
//! Some file systems implement COW (copy on write) functionality in order to speed up file copies.
//! On a high level, the new file does not actually get copied, but shares the same on-disk data
//! with the source file. As soon as one of the files is modified, the actual copying is done by
//! the underlying OS.
//!
//! This library exposes a single function, `reflink`, which attempts to copy a file using the
//! underlying OSs' block cloning capabilities. The function signature is identical to `std::fs::copy`.
//!
//! At the moment Linux, Android, OSX, ios and Windows are supported.
//! As soon as other OS support the functionality, support will be added.
mod sys;
use std::fs;
use std::io;
use std::path::Path;
/// Copies a file using COW semantics.
///
/// For compatibility reasons with macos, the target file will be created using `OpenOptions::create_new`.
/// If you want to overwrite existing files, make sure you manually delete the target file first
/// if it exists.
///
/// ```rust
/// match reflink_copy::reflink("src.txt", "dest.txt") {
/// Ok(()) => println!("file has been reflinked"),
/// Err(e) => println!("error while reflinking: {:?}", e)
/// }
/// ```
///
/// # Implementation details per platform
///
/// ## Linux / Android
///
/// Uses `ioctl_ficlone`. Supported file systems include btrfs and XFS (and maybe more in the future).
/// NOTE that it generates a temporary file and is not atomic.
///
/// ## OS X / ios
///
/// Uses `clonefile` library function. This is supported on OS X Version >=10.12 and iOS version >= 10.0
/// This will work on APFS partitions (which means most desktop systems are capable).
///
/// ## Windows
///
/// Uses ioctl `FSCTL_DUPLICATE_EXTENTS_TO_FILE`.
///
/// Only supports ReFS on Windows Server. *Important note*: The windows implementation is currently
/// untested and probably buggy. Contributions/testers with access to a Windows Server welcome.
///
/// NOTE that it generates a temporary file and is not atomic.
#[inline(always)]
pub fn reflink(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
fn inner(from: &Path, to: &Path) -> io::Result<()> {
if !from.is_file() {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the source path is not an existing regular file",
))
} else {
sys::reflink(from, to)
}
}
inner(from.as_ref(), to.as_ref())
}
/// Attempts to reflink a file. If the operation fails, a conventional copy operation is
/// attempted as a fallback.
///
/// If the function reflinked a file, the return value will be `Ok(None)`.
///
/// If the function copied a file, the return value will be `Ok(Some(written))`.
///
/// ```rust
/// match reflink_copy::reflink_or_copy("src.txt", "dest.txt") {
/// Ok(None) => println!("file has been reflinked"),
/// Ok(Some(written)) => println!("file has been copied ({} bytes)", written),
/// Err(e) => println!("an error occured: {:?}", e)
/// }
/// ```
#[inline(always)]
pub fn reflink_or_copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<Option<u64>> {
fn inner(from: &Path, to: &Path) -> io::Result<Option<u64>> {
if let Ok(()) = reflink(from, to) {
Ok(None)
} else {
fs::copy(from, to).map(Some)
}
}
inner(from.as_ref(), to.as_ref())
}