Skip to content

Commit

Permalink
fix: clone again if dest is not correct
Browse files Browse the repository at this point in the history
  • Loading branch information
nachoaldamav committed Oct 10, 2023
1 parent e32892a commit 2c62202
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 19 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,6 @@ Cargo.lock
*.node
*.exe

@reflink
@reflink

sandbox
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
napi-derive = "2.12.2"
reflink-copy = "0.1.9"

[target.'cfg(unix)'.dependencies]
xattr = "1.0.1"

[build-dependencies]
napi-build = "2.0.1"

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
"devDependencies": {
"@napi-rs/cli": "^2.16.3",
"@types/node": "^20.8.0",
"chalk": "^5.3.0",
"rimraf": "^5.0.5",
"typescript": "^5.2.2",
"vitest": "^0.34.6"
},
Expand All @@ -42,5 +44,8 @@
"universal": "napi universal",
"version": "napi version"
},
"optionalDependencies": {
"@reflink/reflink-win32-x64-msvc": "file:./npm/win32-x64-msvc"
},
"packageManager": "[email protected]"
}
132 changes: 115 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,83 @@ extern crate napi_derive;
use napi::{bindgen_prelude::AsyncTask, Env, Error, JsNumber, Result, Task};
use std::path::PathBuf;
use reflink_copy;
use std::fs;

// Return a napi::Result, so you can use napi::Error
fn has_null_bytes(path: &PathBuf) -> Result<bool> {
match fs::read(path) {
Ok(content) => Ok(content.iter().any(|&byte| byte == 0)),
Err(io_err) => {
println!("Error reading file: {}", io_err.to_string());
Err(Error::from_reason(io_err.to_string()))
}
}
}


#[cfg(not(target_os = "windows"))]
extern crate xattr;

pub struct AsyncReflink {
src: PathBuf,
dst: PathBuf,
}

#[cfg(not(target_os = "windows"))]
fn set_destination_metadata(src: &PathBuf, dst: &PathBuf) -> std::io::Result<()> {
let metadata_key = "user.reflink_destinations";

let mut destinations = match xattr::get(src, metadata_key) {
Ok(Some(data)) => String::from_utf8_lossy(&data).to_string(),
_ => String::from(""),
};

if !destinations.is_empty() {
destinations.push_str(",");
}
destinations.push_str(dst.to_str().unwrap());

xattr::set(src, metadata_key, destinations.as_bytes())
}

#[napi]
impl Task for AsyncReflink {
type Output = ();
type JsValue = JsNumber;

fn compute(&mut self) -> Result<Self::Output> {
match reflink_copy::reflink(&self.src, &self.dst) {
Ok(_) => Ok(()),
Err(err) => Err(Error::from_reason(format!(
"{}, reflink '{}' -> '{}'",
err.to_string(),
self.src.display(),
self.dst.display()
))),
let mut retry_count = 0;
loop {
match reflink_copy::reflink(&self.src, &self.dst) {
Ok(_) => {
#[cfg(not(target_os = "windows"))]
{
if let Err(err) = set_destination_metadata(&self.src, &self.dst) {
return Err(Error::from_reason(err.to_string()));
}
}
// Check for null bytes in the destination file
let contains_null = has_null_bytes(&self.dst).map_err(|e| Error::from_reason(e.to_string()))?;

if !contains_null {
return Ok(());
} else {
// Delete the destination and retry if it contains null bytes
fs::remove_file(&self.dst).map_err(|e| Error::from_reason(e.to_string()))?;
retry_count += 1;

if retry_count >= 3 { // Limit the number of retries
return Err(Error::from_reason("Maximum retry attempts reached".to_string()));
}
}
},
Err(err) => return Err(Error::from_reason(format!(
"{}, reflink '{}' -> '{}'",
err.to_string(),
self.src.display(),
self.dst.display()
))),
}
}
}

Expand All @@ -47,13 +104,54 @@ pub fn reflink_task(src: String, dst: String) -> AsyncTask<AsyncReflink> {
pub fn reflink_sync(env: Env, src: String, dst: String) -> Result<JsNumber> {
let src_path = PathBuf::from(src.clone());
let dst_path = PathBuf::from(dst.clone());
match reflink_copy::reflink(&src_path, &dst_path) {
Ok(_) => Ok(env.create_int32(0)?),
Err(err) => Err(Error::from_reason(format!(
"{}, reflink '{}' -> '{}'",
err.to_string(),
src_path.display(),
dst_path.display()
))),
let mut retry_count = 0;

loop {
// Attempt to perform reflink
let reflink_result = reflink_copy::reflink(&src_path, &dst_path);

match reflink_result {
Ok(_) => {
// Check if the source and destination files differ
if let Ok(differs) = has_null_bytes(&dst_path) {
if differs {
if retry_count >= 3 { // Max retry count
return Err(Error::from_reason(format!(
"Max retries reached, could not create identical reflink for '{}' -> '{}'",
src_path.display(),
dst_path.display()
)));
}
// Remove the destination and retry
if let Err(err) = fs::remove_file(&dst_path) {
return Err(Error::from_reason(format!(
"Failed to remove destination file '{}': {}",
dst_path.display(),
err.to_string()
)));
}
retry_count += 1;
continue; // Retry the operation
}
}

// Metadata and return handling here (existing code)
#[cfg(not(target_os = "windows"))]
{
if let Err(err) = set_destination_metadata(&src_path, &dst_path) {
return Err(Error::from_reason(err.to_string()));
}
}
return Ok(env.create_int32(0)?);
},
Err(err) => {
return Err(Error::from_reason(format!(
"{}, reflink '{}' -> '{}'",
err.to_string(),
src_path.display(),
dst_path.display()
)));
},
}
}
}
}
33 changes: 32 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,27 @@ __metadata:
languageName: node
linkType: hard

"@reflink/reflink-win32-x64-msvc@file:./npm/win32-x64-msvc::locator=%40reflink%2Freflink%40workspace%3A.":
version: 0.1.8
resolution: "@reflink/reflink-win32-x64-msvc@file:./npm/win32-x64-msvc#./npm/win32-x64-msvc::hash=460e85&locator=%40reflink%2Freflink%40workspace%3A."
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard

"@reflink/reflink@workspace:.":
version: 0.0.0-use.local
resolution: "@reflink/reflink@workspace:."
dependencies:
"@napi-rs/cli": ^2.16.3
"@reflink/reflink-win32-x64-msvc": "file:./npm/win32-x64-msvc"
"@types/node": ^20.8.0
chalk: ^5.3.0
rimraf: ^5.0.5
typescript: ^5.2.2
vitest: ^0.34.6
dependenciesMeta:
"@reflink/reflink-win32-x64-msvc":
optional: true
languageName: unknown
linkType: soft

Expand Down Expand Up @@ -495,6 +508,13 @@ __metadata:
languageName: node
linkType: hard

"chalk@npm:^5.3.0":
version: 5.3.0
resolution: "chalk@npm:5.3.0"
checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80
languageName: node
linkType: hard

"check-error@npm:^1.0.3":
version: 1.0.3
resolution: "check-error@npm:1.0.3"
Expand Down Expand Up @@ -808,7 +828,7 @@ __metadata:
languageName: node
linkType: hard

"glob@npm:^10.2.2":
"glob@npm:^10.2.2, glob@npm:^10.3.7":
version: 10.3.10
resolution: "glob@npm:10.3.10"
dependencies:
Expand Down Expand Up @@ -1395,6 +1415,17 @@ __metadata:
languageName: node
linkType: hard

"rimraf@npm:^5.0.5":
version: 5.0.5
resolution: "rimraf@npm:5.0.5"
dependencies:
glob: ^10.3.7
bin:
rimraf: dist/esm/bin.mjs
checksum: d66eef829b2e23b16445f34e73d75c7b7cf4cbc8834b04720def1c8f298eb0753c3d76df77325fad79d0a2c60470525d95f89c2475283ad985fd7441c32732d1
languageName: node
linkType: hard

"rollup@npm:^3.29.2":
version: 3.29.4
resolution: "rollup@npm:3.29.4"
Expand Down

0 comments on commit 2c62202

Please sign in to comment.