fix(blob): fsync blob files via a write handle so Windows works
The sync sweep (and the EXDEV copy fallback) opened blob files with File::open — a read-only handle — before calling sync_all. POSIX fsync accepts read-only fds, so Linux never noticed, but Windows FlushFileBuffers requires a GENERIC_WRITE handle and fails with ACCESS_DENIED (os error 5) on every call. On Windows deployments the strict sweep therefore failed every deferred sync, and the post-copy fsync silently never happened. Files now open via OpenOptions::write(true); the best-effort directory fsyncs keep the read-only POSIX dirent idiom unchanged. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -102,7 +102,23 @@ async fn fsync_paths_parallel(paths: Vec<PathBuf>, strict: bool) -> Result<(), D
|
||||
tasks.push(tokio::task::spawn_blocking(
|
||||
move || -> Result<(), (PathBuf, std::io::Error)> {
|
||||
for path in &group {
|
||||
let result = std::fs::File::open(path).and_then(|f| f.sync_all());
|
||||
// `strict` marks blob *file* fsyncs; best-effort marks
|
||||
// prefix *directory* fsyncs. That distinction also picks
|
||||
// the open mode: Windows `FlushFileBuffers` needs a
|
||||
// GENERIC_WRITE handle and fails with ACCESS_DENIED on
|
||||
// the read-only handle `File::open` returns (POSIX fsync
|
||||
// accepts read-only fds, which is why this only surfaced
|
||||
// on Windows). Directories keep the read-only POSIX
|
||||
// dirent-sync idiom — they can't be fsync'd on Windows
|
||||
// at all, and their failures stay best-effort warnings.
|
||||
let result = if strict {
|
||||
std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.open(path)
|
||||
.and_then(|f| f.sync_all())
|
||||
} else {
|
||||
std::fs::File::open(path).and_then(|f| f.sync_all())
|
||||
};
|
||||
if let Err(e) = result {
|
||||
if strict {
|
||||
return Err((path.clone(), e));
|
||||
@@ -394,7 +410,11 @@ impl BlobStorageBackend for LocalBlobBackend {
|
||||
format!("Failed to copy file to blob store: {}", ce),
|
||||
)
|
||||
})?;
|
||||
if let Ok(f) = fs::File::open(&blob_path).await {
|
||||
// Open for write: Windows `FlushFileBuffers` requires a
|
||||
// GENERIC_WRITE handle — the read-only handle from
|
||||
// `File::open` fails with ACCESS_DENIED, silently
|
||||
// skipping this fsync on every Windows deployment.
|
||||
if let Ok(f) = fs::OpenOptions::new().write(true).open(&blob_path).await {
|
||||
let _ = f.sync_all().await;
|
||||
}
|
||||
let _ = fs::remove_file(&source_path).await;
|
||||
|
||||
Reference in New Issue
Block a user