rust-volume: fix Windows build of find_needle_from_ecx (#11298)

* rust-volume: fix Windows build of find_needle_from_ecx

The .ecx binary-search fallback path used on non-Unix targets
(Seek + Read, both &mut self receivers) requires the ecx_file
binding to be mutable. On Unix the read_exact_at path takes &self,
so the mut would be unused there — gate that warning with
#[cfg_attr(unix, allow(unused_mut))].

Without this the build-rust-volume-windows CI job fails with
E0596 at ec_volume.rs:1033, breaking the weed-volume_windows_amd64
release asset.

* rust-volume: use positional seek_read for .ecx lookups on Windows

The previous fix (making ecx_file mut) compiled but left the Windows
fallback using Seek + Read on the shared .ecx file cursor. Concurrent
find_needle_from_ecx calls could interleave seek/read and read the
wrong index entry, corrupting the binary search (raised by Devin and
Greptile review on the PR).

Switch the Windows path to std::os::windows::fs::FileExt::seek_read,
which is positional (offset passed via OVERLAPPED, cursor untouched)
and takes &self — so the binding no longer needs mut, and concurrent
callers on the cached handle can't interfere. Mirrors the existing
read_exact_at helper in storage::volume.

Add a compile_error fallback for non-unix/non-windows targets to
match the convention in storage::volume.
This commit is contained in:
Chris Lu
2026-09-13 20:08:01 -07:00
committed by GitHub
parent ea179963c0
commit 15d9f6c6fe
@@ -1057,18 +1057,21 @@ impl EcVolume {
return Err(e);
}
}
#[cfg(not(unix))]
#[cfg(windows)]
{
use std::io::{Read, Seek, SeekFrom};
if let Err(e) = ecx_file.seek(SeekFrom::Start(file_offset)) {
self.check_read_write_error(Some(&e));
return Err(e);
}
if let Err(e) = ecx_file.read_exact(&mut entry_buf) {
// Positional read so concurrent find_needle_from_ecx calls on
// the shared .ecx handle don't interleave seek/read and corrupt
// each other's binary search. Mirrors the read_exact_at helper
// in storage::volume.
if let Err(e) = read_exact_at(ecx_file, &mut entry_buf, file_offset) {
self.check_read_write_error(Some(&e));
return Err(e);
}
}
#[cfg(not(any(unix, windows)))]
{
compile_error!("Platform not supported: only unix and windows are supported");
}
let (key, offset, size) = idx_entry_from_bytes(&entry_buf);
if key == needle_id {
@@ -4407,3 +4410,27 @@ impl EcLocalScrubPlan {
(count, broken, errs)
}
}
/// Windows helper: loop `seek_read` until the buffer is fully filled.
///
/// `seek_read` is positional (it passes the offset through `OVERLAPPED` and
/// never touches the shared file cursor), so concurrent callers reading the
/// same `&File` — as `find_needle_from_ecx` does on the cached `.ecx` handle —
/// can't interleave their reads. Mirrors the helper in `storage::volume`.
#[cfg(windows)]
fn read_exact_at(file: &File, buf: &mut [u8], mut offset: u64) -> io::Result<()> {
use std::os::windows::fs::FileExt;
let mut filled = 0;
while filled < buf.len() {
let n = file.seek_read(&mut buf[filled..], offset)?;
if n == 0 {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected EOF in seek_read",
));
}
filled += n;
offset += n as u64;
}
Ok(())
}