0.31.0: hardening and correctness pass across mux, codec, AACS/CSS, UDF/MPLS/CLPI, recovery, drive/SCSI, labels, and I/O
Library-wide review-and-fix pass: tightened AACS keydb/handshake/variant handling and trailing-partial-unit policy, corrected MPLS mark offset and added UDF allocation bounds, hardened the mux/codec framing and M2TS paths, guarded SCSI READ CAPACITY short transfers and unified error mapping, added overflow guards on untrusted disc input, and made prefetch shutdown deterministic. Release profile now builds with thin LTO + single codegen unit.
This commit is contained in:
@@ -8,32 +8,22 @@
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// `F_RDADVISE` opcode — not in libc's named constants on all SDKs.
|
||||
const F_RDADVISE: libc::c_int = 44;
|
||||
|
||||
/// Cap on the byte length we pass to `F_RDADVISE`. Asking for a
|
||||
/// multi-GB readahead window is counterproductive — the OS doesn't
|
||||
/// have that much cache to throw at one fd. 64 MiB is generous for
|
||||
/// our use case (sweep, mux) and matches the byte-channel cap so the
|
||||
/// kernel's prefetch ≥ our app-level pipeline depth.
|
||||
/// our use case (sweep, mux) so the kernel's prefetch ≥ our app-level
|
||||
/// pipeline depth.
|
||||
const RDADVISE_MAX_BYTES: i64 = 64 * 1024 * 1024;
|
||||
|
||||
/// `radvisory` per `<sys/fcntl.h>`. repr(C) layout is stable.
|
||||
#[repr(C)]
|
||||
struct RadAdvisory {
|
||||
ra_offset: libc::off_t,
|
||||
ra_count: libc::c_int,
|
||||
}
|
||||
|
||||
pub(super) fn hint_sequential(file: &File, len_bytes: u64) {
|
||||
let bytes = (len_bytes as i64).min(RDADVISE_MAX_BYTES);
|
||||
let mut ra = RadAdvisory {
|
||||
let mut ra = libc::radvisory {
|
||||
ra_offset: 0,
|
||||
ra_count: bytes as libc::c_int,
|
||||
};
|
||||
// Best-effort.
|
||||
unsafe {
|
||||
libc::fcntl(file.as_raw_fd(), F_RDADVISE, &mut ra);
|
||||
libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &mut ra);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,11 +42,12 @@ pub(super) fn drop_window(_file: &File, _start: u64, _len: u64) {}
|
||||
/// returns immediately.
|
||||
pub(super) fn prefetch(file: &File, offset: u64, len: u64) {
|
||||
let bytes = (len as i64).min(RDADVISE_MAX_BYTES);
|
||||
let mut ra = RadAdvisory {
|
||||
let mut ra = libc::radvisory {
|
||||
ra_offset: offset as libc::off_t,
|
||||
ra_count: bytes as libc::c_int,
|
||||
};
|
||||
// Best-effort — kernel hint only.
|
||||
unsafe {
|
||||
libc::fcntl(file.as_raw_fd(), F_RDADVISE, &mut ra);
|
||||
libc::fcntl(file.as_raw_fd(), libc::F_RDADVISE, &mut ra);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,17 @@
|
||||
//! Without page-cache eviction an 85 GB streaming ISO read pins the
|
||||
//! entire file in memory, starves the concurrent writer, and collapses
|
||||
//! mux throughput (observed: 2.7 MB/s mux on 0.21.5 vs. 70 MB/s
|
||||
//! isolated NFS reads). Every [`READ_DROP_CHUNK_BYTES`] of consumed
|
||||
//! bytes we call `posix_fadvise(DONTNEED)` over that window, mirroring
|
||||
//! the write-side [`crate::io::writeback::WritebackPipeline`] policy.
|
||||
//! isolated NFS reads). Every [`READ_DROP_CHUNK_BYTES_DEFAULT`] of
|
||||
//! consumed bytes we call `posix_fadvise(DONTNEED)` over that window,
|
||||
//! mirroring the write-side [`crate::io::writeback::WritebackPipeline`]
|
||||
//! policy.
|
||||
//!
|
||||
//! The drop window is accounted by a monotonic forward byte counter,
|
||||
//! which matches the sequential streaming pattern the mux highway
|
||||
//! drives. Under random or backward access the dropped range no longer
|
||||
//! lines up with the bytes actually read — but `DONTNEED` is purely an
|
||||
//! advisory cache hint with no correctness impact, so this degrades to
|
||||
//! a slightly imprecise hint rather than a bug.
|
||||
//!
|
||||
//! ## Platform open hint
|
||||
//!
|
||||
@@ -102,7 +110,10 @@ pub struct FileSectorSource {
|
||||
bytes_read_since_drop: u64,
|
||||
/// File offset at which the current drop window starts. The next
|
||||
/// DONTNEED drops from `drop_window_start` for
|
||||
/// `bytes_read_since_drop` bytes.
|
||||
/// `bytes_read_since_drop` bytes. This advances monotonically with
|
||||
/// the byte count, so it tracks the actual reads only under the
|
||||
/// forward-sequential access the mux highway uses; under random
|
||||
/// access it degrades to a harmless, imprecise advisory hint.
|
||||
drop_window_start: u64,
|
||||
/// Cached drop chunk size (resolved from env once at open).
|
||||
drop_chunk_bytes: u64,
|
||||
@@ -116,16 +127,18 @@ impl FileSectorSource {
|
||||
///
|
||||
/// Issues the platform's "sequential access expected" hint on the
|
||||
/// fd (Linux `posix_fadvise(SEQUENTIAL)`, macOS `fcntl(F_RDADVISE)`,
|
||||
/// Windows TODO stub) so the kernel's readahead widens.
|
||||
pub fn open(path: &Path) -> std::io::Result<Self> {
|
||||
let file = File::open(path)?;
|
||||
let len = file.metadata()?.len();
|
||||
/// Windows no-op) so the kernel's readahead widens.
|
||||
pub fn open(path: &Path) -> Result<Self> {
|
||||
let file = File::open(path).map_err(|e| Error::IoError { source: e })?;
|
||||
let len = file
|
||||
.metadata()
|
||||
.map_err(|e| Error::IoError { source: e })?
|
||||
.len();
|
||||
let sectors = len / SECTOR_SIZE as u64;
|
||||
if sectors > u32::MAX as u64 {
|
||||
return Err(Error::IsoTooLarge {
|
||||
path: path.to_string_lossy().into_owned(),
|
||||
}
|
||||
.into());
|
||||
});
|
||||
}
|
||||
let capacity = sectors as u32;
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
//! Windows: the canonical sequential-access hint is
|
||||
//! `FILE_FLAG_SEQUENTIAL_SCAN` passed to `CreateFile` at open time —
|
||||
//! it cannot be set after the fact via `SetFileInformationByHandle`.
|
||||
//! Routing the open call through this module would mean a custom
|
||||
//! `File::from_raw_handle` plumb for every `FileSectorSource::open`
|
||||
//! caller, which is more invasive than the Phase 1 scope.
|
||||
//!
|
||||
//! TODO: replumb `FileSectorSource::open` to take an
|
||||
//! `OpenOptions`-style builder so the Windows path can flip the flag
|
||||
//! at open time. For now this is a no-op stub.
|
||||
//! `FILE_FLAG_SEQUENTIAL_SCAN`, which must be passed to `CreateFile`
|
||||
//! at open time and cannot be set afterward via
|
||||
//! `SetFileInformationByHandle`. Since `FileSectorSource::open` uses a
|
||||
//! plain `File::open`, the hints in this module are no-op stubs.
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
/// No-op stub. `FILE_FLAG_SEQUENTIAL_SCAN` can only be set at
|
||||
/// `CreateFile` open time, which the plain `File::open` path does not
|
||||
/// do, so there is no post-open hint to issue here.
|
||||
pub(super) fn hint_sequential(_file: &File, _len_bytes: u64) {
|
||||
tracing::debug!(
|
||||
target: "mux",
|
||||
"FileSectorSource hint_sequential: windows stub (TODO: FILE_FLAG_SEQUENTIAL_SCAN at open)"
|
||||
"FileSectorSource hint_sequential: windows no-op stub"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user