Skip to content

Commit

Permalink
Implement check_reflink_support for windows (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vaiz authored Nov 10, 2024
1 parent 9e0b2cf commit dbd5c43
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 9 deletions.
34 changes: 32 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,43 @@ jobs:

- uses: Swatinem/rust-cache@v2

- name: Setup Dev-Drive ReFS1
if: ${{ matrix.os == 'windows-latest' }}
uses: samypr100/setup-dev-drive@v3
with:
drive-size: 1GB
drive-format: ReFS
drive-type: Dynamic
drive-path: "${{ runner.temp }}/dev-drives/refs1.vhdx"
mount-path: "${{ runner.temp }}/dev-drives/refs1"

- name: Setup Dev-Drive ReFS2
if: ${{ matrix.os == 'windows-latest' }}
uses: samypr100/setup-dev-drive@v3
with:
drive-size: 1GB
drive-format: ReFS
drive-type: Dynamic
drive-path: "${{ runner.temp }}/dev-drives/refs2.vhdx"
mount-path: "${{ runner.temp }}/dev-drives/refs2"

- name: Setup Dev-Drive NTFS
if: ${{ matrix.os == 'windows-latest' }}
uses: samypr100/setup-dev-drive@v3
with:
drive-size: 1GB
drive-format: NTFS
drive-type: Dynamic
drive-path: "${{ runner.temp }}/dev-drives/ntfs.vhdx"
mount-path: "${{ runner.temp }}/dev-drives/ntfs"

- name: Test
if: "! matrix.use-cross"
run: cargo test --target ${{ matrix.target }}
run: cargo test --target ${{ matrix.target }} -- --ignored

- name: Test using cross
if: "matrix.use-cross"
run: cross test --target ${{ matrix.target }}
run: cross test --target ${{ matrix.target }} -- --ignored

cross-check:
strategy:
Expand Down
74 changes: 74 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ tracing = ["dep:tracing", "dep:tracing-attributes"]

[dev-dependencies]
tempfile = "3.12.0"
regex = "1.11.1"
walkdir = "2.5.0"
15 changes: 15 additions & 0 deletions examples/check_reflink_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// cargo run --example check_reflink_support V:\folder1 X:\folder2

fn main() -> std::io::Result<()> {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <source_path> <target_path>", args[0]);
return Ok(());
}
let src_path = &args[1];
let tgt_path = &args[2];

let result = reflink_copy::check_reflink_support(src_path, tgt_path)?;
println!("{result:?}");
Ok(())
}
56 changes: 56 additions & 0 deletions examples/copy_folder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use reflink_copy::ReflinkSupport;
use std::fs;
use std::io;
use std::path::Path;
use walkdir::WalkDir;

// cargo run --example copy_folder V:/1 V:/2

fn main() -> io::Result<()> {
let args: Vec<_> = std::env::args().collect();
if args.len() < 3 {
eprintln!("Usage: {} <source_folder> <target_folder>", args[0]);
return Ok(());
}

let from = Path::new(&args[1]);
let to = Path::new(&args[2]);
let reflink_support = reflink_copy::check_reflink_support(from, to)?;
println!("Reflink support: {reflink_support:?}");

let mut reflinked_count = 0u64;
let mut copied_count = 0u64;

for entry in WalkDir::new(from) {
let entry = entry?;
let relative_path = entry.path().strip_prefix(from).unwrap();
let target_path = to.join(relative_path);

if entry.file_type().is_dir() {
fs::create_dir_all(&target_path)?;
} else {
match reflink_support {
ReflinkSupport::Supported => {
reflink_copy::reflink(entry.path(), target_path)?;
reflinked_count = reflinked_count.saturating_add(1);
}
ReflinkSupport::Unknown => {
let result = reflink_copy::reflink_or_copy(entry.path(), target_path)?;
if result.is_some() {
copied_count = copied_count.saturating_add(1);
} else {
reflinked_count = reflinked_count.saturating_add(1);
}
}
ReflinkSupport::NotSupported => {
fs::copy(entry.path(), target_path)?;
copied_count = copied_count.saturating_add(1);
}
}
}
}

println!("reflinked files count: {reflinked_count}");
println!("copied files count: {copied_count}");
Ok(())
}
38 changes: 38 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,41 @@ pub fn reflink_or_copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Resu

inner(from.as_ref(), to.as_ref())
}
/// Checks whether reflink is supported on the filesystem for the specified source and target paths.
///
/// This function verifies that both paths are on the same volume and that the filesystem supports
/// reflink.
///
/// > Note: Currently the function works only for windows. It returns `Ok(ReflinkSupport::Unknown)`
/// > for any other platform.
///
/// # Example
/// ```
/// fn main() -> std::io::Result<()> {
/// let support = reflink_copy::check_reflink_support("C:\\path\\to\\file", "C:\\path\\to\\another_file")?;
/// println!("{support:?}");
/// let support = reflink_copy::check_reflink_support("path\\to\\folder", "path\\to\\another_folder")?;
/// println!("{support:?}");
/// Ok(())
/// }
/// ```
pub fn check_reflink_support(
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> io::Result<ReflinkSupport> {
#[cfg(windows)]
return sys::check_reflink_support(from, to);
#[cfg(not(windows))]
Ok(ReflinkSupport::Unknown)
}

/// Enum indicating the reflink support status.
#[derive(Debug, PartialEq, Eq)]
pub enum ReflinkSupport {
/// Reflink is supported.
Supported,
/// Reflink is not supported.
NotSupported,
/// Reflink support is unconfirmed.
Unknown,
}
1 change: 1 addition & 0 deletions src/sys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cfg_if! {
} else if #[cfg(windows)] {
mod windows_impl;
pub use self::windows_impl::reflink;
pub use self::windows_impl::check_reflink_support;
} else {
pub use self::reflink_not_supported as reflink;
}
Expand Down
Loading

0 comments on commit dbd5c43

Please sign in to comment.