Skip to content

Commit 4d8cade

Browse files
authored
fix: Export try reference bugfix (#149)
## Description We don't want to move, but to copy in that case. We also update the db, but limit the max number of external paths to MAX_EXTERNAL_PATHS This fixes a regression from blobs 0.35 regarding ExportMode::TryReference Fixes n0-computer/sendme#103 ## Breaking Changes None ## Notes & open questions ## Change checklist - [ ] Self-review. - [ ] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [ ] Tests if relevant. - [ ] All breaking changes documented.
1 parent 49ab2b7 commit 4d8cade

File tree

1 file changed

+49
-24
lines changed

1 file changed

+49
-24
lines changed

src/store/fs.rs

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ use crate::{
152152
HashAndFormat,
153153
};
154154

155+
/// Maximum number of external paths we track per blob.
156+
const MAX_EXTERNAL_PATHS: usize = 8;
157+
155158
/// Create a 16 byte unique ID.
156159
fn new_uuid() -> [u8; 16] {
157160
use rand::RngCore;
@@ -1239,18 +1242,21 @@ async fn export_path_impl(
12391242
}
12401243
};
12411244
trace!("exporting {} to {}", cmd.hash.to_hex(), target.display());
1242-
let data = match data_location {
1243-
DataLocation::Inline(data) => MemOrFile::Mem(data),
1244-
DataLocation::Owned(size) => {
1245-
MemOrFile::File((ctx.options().path.data_path(&cmd.hash), size))
1246-
}
1247-
DataLocation::External(paths, size) => MemOrFile::File((
1248-
paths
1249-
.into_iter()
1250-
.next()
1251-
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "no external data path"))?,
1252-
size,
1253-
)),
1245+
let (data, mut external) = match data_location {
1246+
DataLocation::Inline(data) => (MemOrFile::Mem(data), vec![]),
1247+
DataLocation::Owned(size) => (
1248+
MemOrFile::File((ctx.options().path.data_path(&cmd.hash), size)),
1249+
vec![],
1250+
),
1251+
DataLocation::External(paths, size) => (
1252+
MemOrFile::File((
1253+
paths.first().cloned().ok_or_else(|| {
1254+
io::Error::new(io::ErrorKind::NotFound, "no external data path")
1255+
})?,
1256+
size,
1257+
)),
1258+
paths,
1259+
),
12541260
};
12551261
let size = match &data {
12561262
MemOrFile::Mem(data) => data.len() as u64,
@@ -1274,21 +1280,40 @@ async fn export_path_impl(
12741280
);
12751281
}
12761282
ExportMode::TryReference => {
1277-
match std::fs::rename(&source_path, &target) {
1278-
Ok(()) => {}
1279-
Err(cause) => {
1280-
const ERR_CROSS: i32 = 18;
1281-
if cause.raw_os_error() == Some(ERR_CROSS) {
1282-
let source = fs::File::open(&source_path)?;
1283-
let mut target = fs::File::create(&target)?;
1284-
copy_with_progress(&source, size, &mut target, tx).await?;
1285-
} else {
1286-
return Err(cause.into());
1283+
if !external.is_empty() {
1284+
// the file already exists externally, so we need to copy it.
1285+
// if the OS supports reflink, we might as well use that.
1286+
let res =
1287+
reflink_or_copy_with_progress(&source_path, &target, size, tx).await?;
1288+
trace!(
1289+
"exported {} also to {}, {res:?}",
1290+
source_path.display(),
1291+
target.display()
1292+
);
1293+
external.push(target);
1294+
external.sort();
1295+
external.dedup();
1296+
external.truncate(MAX_EXTERNAL_PATHS);
1297+
} else {
1298+
// the file was previously owned, so we can just move it.
1299+
// if that fails with ERR_CROSS, we fall back to copy.
1300+
match std::fs::rename(&source_path, &target) {
1301+
Ok(()) => {}
1302+
Err(cause) => {
1303+
const ERR_CROSS: i32 = 18;
1304+
if cause.raw_os_error() == Some(ERR_CROSS) {
1305+
reflink_or_copy_with_progress(&source_path, &target, size, tx)
1306+
.await?;
1307+
} else {
1308+
return Err(cause.into());
1309+
}
12871310
}
12881311
}
1289-
}
1312+
external.push(target);
1313+
};
1314+
// setting the new entry state will also take care of deleting the owned data file!
12901315
ctx.set(EntryState::Complete {
1291-
data_location: DataLocation::External(vec![target], size),
1316+
data_location: DataLocation::External(external, size),
12921317
outboard_location,
12931318
})
12941319
.await?;

0 commit comments

Comments
 (0)