Skip to content

Commit dbd5c43

Browse files
authored
Implement check_reflink_support for windows (#83)
1 parent 9e0b2cf commit dbd5c43

File tree

9 files changed

+483
-9
lines changed

9 files changed

+483
-9
lines changed

.github/workflows/build.yml

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,43 @@ jobs:
6262

6363
- uses: Swatinem/rust-cache@v2
6464

65+
- name: Setup Dev-Drive ReFS1
66+
if: ${{ matrix.os == 'windows-latest' }}
67+
uses: samypr100/setup-dev-drive@v3
68+
with:
69+
drive-size: 1GB
70+
drive-format: ReFS
71+
drive-type: Dynamic
72+
drive-path: "${{ runner.temp }}/dev-drives/refs1.vhdx"
73+
mount-path: "${{ runner.temp }}/dev-drives/refs1"
74+
75+
- name: Setup Dev-Drive ReFS2
76+
if: ${{ matrix.os == 'windows-latest' }}
77+
uses: samypr100/setup-dev-drive@v3
78+
with:
79+
drive-size: 1GB
80+
drive-format: ReFS
81+
drive-type: Dynamic
82+
drive-path: "${{ runner.temp }}/dev-drives/refs2.vhdx"
83+
mount-path: "${{ runner.temp }}/dev-drives/refs2"
84+
85+
- name: Setup Dev-Drive NTFS
86+
if: ${{ matrix.os == 'windows-latest' }}
87+
uses: samypr100/setup-dev-drive@v3
88+
with:
89+
drive-size: 1GB
90+
drive-format: NTFS
91+
drive-type: Dynamic
92+
drive-path: "${{ runner.temp }}/dev-drives/ntfs.vhdx"
93+
mount-path: "${{ runner.temp }}/dev-drives/ntfs"
94+
6595
- name: Test
6696
if: "! matrix.use-cross"
67-
run: cargo test --target ${{ matrix.target }}
97+
run: cargo test --target ${{ matrix.target }} -- --ignored
6898

6999
- name: Test using cross
70100
if: "matrix.use-cross"
71-
run: cross test --target ${{ matrix.target }}
101+
run: cross test --target ${{ matrix.target }} -- --ignored
72102

73103
cross-check:
74104
strategy:

Cargo.lock

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ tracing = ["dep:tracing", "dep:tracing-attributes"]
3030

3131
[dev-dependencies]
3232
tempfile = "3.12.0"
33+
regex = "1.11.1"
34+
walkdir = "2.5.0"

examples/check_reflink_support.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// cargo run --example check_reflink_support V:\folder1 X:\folder2
2+
3+
fn main() -> std::io::Result<()> {
4+
let args: Vec<_> = std::env::args().collect();
5+
if args.len() < 3 {
6+
eprintln!("Usage: {} <source_path> <target_path>", args[0]);
7+
return Ok(());
8+
}
9+
let src_path = &args[1];
10+
let tgt_path = &args[2];
11+
12+
let result = reflink_copy::check_reflink_support(src_path, tgt_path)?;
13+
println!("{result:?}");
14+
Ok(())
15+
}

examples/copy_folder.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use reflink_copy::ReflinkSupport;
2+
use std::fs;
3+
use std::io;
4+
use std::path::Path;
5+
use walkdir::WalkDir;
6+
7+
// cargo run --example copy_folder V:/1 V:/2
8+
9+
fn main() -> io::Result<()> {
10+
let args: Vec<_> = std::env::args().collect();
11+
if args.len() < 3 {
12+
eprintln!("Usage: {} <source_folder> <target_folder>", args[0]);
13+
return Ok(());
14+
}
15+
16+
let from = Path::new(&args[1]);
17+
let to = Path::new(&args[2]);
18+
let reflink_support = reflink_copy::check_reflink_support(from, to)?;
19+
println!("Reflink support: {reflink_support:?}");
20+
21+
let mut reflinked_count = 0u64;
22+
let mut copied_count = 0u64;
23+
24+
for entry in WalkDir::new(from) {
25+
let entry = entry?;
26+
let relative_path = entry.path().strip_prefix(from).unwrap();
27+
let target_path = to.join(relative_path);
28+
29+
if entry.file_type().is_dir() {
30+
fs::create_dir_all(&target_path)?;
31+
} else {
32+
match reflink_support {
33+
ReflinkSupport::Supported => {
34+
reflink_copy::reflink(entry.path(), target_path)?;
35+
reflinked_count = reflinked_count.saturating_add(1);
36+
}
37+
ReflinkSupport::Unknown => {
38+
let result = reflink_copy::reflink_or_copy(entry.path(), target_path)?;
39+
if result.is_some() {
40+
copied_count = copied_count.saturating_add(1);
41+
} else {
42+
reflinked_count = reflinked_count.saturating_add(1);
43+
}
44+
}
45+
ReflinkSupport::NotSupported => {
46+
fs::copy(entry.path(), target_path)?;
47+
copied_count = copied_count.saturating_add(1);
48+
}
49+
}
50+
}
51+
}
52+
53+
println!("reflinked files count: {reflinked_count}");
54+
println!("copied files count: {copied_count}");
55+
Ok(())
56+
}

src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,41 @@ pub fn reflink_or_copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Resu
149149

150150
inner(from.as_ref(), to.as_ref())
151151
}
152+
/// Checks whether reflink is supported on the filesystem for the specified source and target paths.
153+
///
154+
/// This function verifies that both paths are on the same volume and that the filesystem supports
155+
/// reflink.
156+
///
157+
/// > Note: Currently the function works only for windows. It returns `Ok(ReflinkSupport::Unknown)`
158+
/// > for any other platform.
159+
///
160+
/// # Example
161+
/// ```
162+
/// fn main() -> std::io::Result<()> {
163+
/// let support = reflink_copy::check_reflink_support("C:\\path\\to\\file", "C:\\path\\to\\another_file")?;
164+
/// println!("{support:?}");
165+
/// let support = reflink_copy::check_reflink_support("path\\to\\folder", "path\\to\\another_folder")?;
166+
/// println!("{support:?}");
167+
/// Ok(())
168+
/// }
169+
/// ```
170+
pub fn check_reflink_support(
171+
from: impl AsRef<Path>,
172+
to: impl AsRef<Path>,
173+
) -> io::Result<ReflinkSupport> {
174+
#[cfg(windows)]
175+
return sys::check_reflink_support(from, to);
176+
#[cfg(not(windows))]
177+
Ok(ReflinkSupport::Unknown)
178+
}
179+
180+
/// Enum indicating the reflink support status.
181+
#[derive(Debug, PartialEq, Eq)]
182+
pub enum ReflinkSupport {
183+
/// Reflink is supported.
184+
Supported,
185+
/// Reflink is not supported.
186+
NotSupported,
187+
/// Reflink support is unconfirmed.
188+
Unknown,
189+
}

src/sys/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cfg_if! {
1111
} else if #[cfg(windows)] {
1212
mod windows_impl;
1313
pub use self::windows_impl::reflink;
14+
pub use self::windows_impl::check_reflink_support;
1415
} else {
1516
pub use self::reflink_not_supported as reflink;
1617
}

0 commit comments

Comments
 (0)