- Consolidate
FileExtandAsyncFileExtinto single crate-root traits (fs4::FileExt,fs4::AsyncFileExt) instead of generating a distinct trait per backend module. The per-backend modules (fs4::tokio,fs4::async_std,fs4::smol,fs4::fs_err2,fs4::fs_err3,fs4::fs_err2_tokio,fs4::fs_err3_tokio) now re-export the unified crate-root trait. Method-call sites that import the trait viausecontinue to compile unchanged; code that named two backend traits as distinct types will see them unify. - Add blanket impls
impl<F: FileExt + ?Sized> FileExt for &Fandimpl<F: AsyncFileExt + ?Sized> AsyncFileExt for &F, so the extension methods are now callable through shared references. - Seal
FileExtandAsyncFileExtvia a privatesealed::Sealedsupertrait, so the set of implementing types is closed to the concrete file types fs4 already supports (and references to them). This locks in the freedom to add methods to either trait in future minor releases without breaking downstream impls. - Add
DynAsyncFileExt, an object-safe mirror ofAsyncFileExtwhose async methods returnBoxFuture<'_, T>(alias forPin<Box<dyn Future<Output = T> + Send + '_>>). Use it whenever type erasure is needed (Box<dyn DynAsyncFileExt>,&dyn DynAsyncFileExt); prefer the staticAsyncFileExtfor generic code since it has no allocation or dynamic-dispatch overhead. Every type implementingAsyncFileExtalso implementsDynAsyncFileExt, and the trait is sealed. - Mark the delegating methods
#[inline(always)](skipped undertarpaulincoverage builds).
- Unix
allocate: short-circuit on allocated blocks (metadata().blocks() * 512 >= len) instead of logical EOF. The previousmetadata().len() >= lencheck silently turnedallocateinto a no-op on sparse files (logical length large, zero blocks reserved), violating the documented preallocation guarantee. The new check still skips the macOSF_PREALLOCATEre-allocate-ENOSPC path from #15, since it asks the right question: "are the blocks already reserved?" Applies to both the sync and async implementations. - Windows
statvfs: route the threeGetDiskFreeSpaceExWoutputs correctly.free_spacenow comes fromlpTotalNumberOfFreeBytes(volume-wide, quota-independent),available_spacefromlpFreeBytesAvailable(caller-scoped, honours per-user quotas), andtotal_spaceis computed from cluster math (sectors_per_cluster * bytes_per_sector * total_number_of_clusters) so it reports volume capacity rather than the caller's quota. On quota-enabled volumes the three fields now carry distinct, documented meanings; previouslyfree_spaceandavailable_spacewere identical andtotal_spaceunder-reported capacity. - Added
target_os = "fuchsia"to the Unixallocatefallocatebranch (sync and async). Fuchsia iscfg(unix)under rustc andrustixexposesfallocatethere, so the previous omission left the Fuchsia Unix build without anallocatesymbol onceFileExtwas enabled. With this fix, every fs4 feature builds on Fuchsia exceptfs-err3andfs-err3-tokio; those remain blocked onfs-err v3.3.0referencingstd::os::unix::fs::chroot, which rustc gates out ontarget_os = "fuchsia". The fs4 code no longer has a Fuchsia gap — the remaining one is upstream (andrewhickman/fs-err#90).
- New regression test
allocate_reserves_blocks_on_sparse_file(sync and async, Linux-gated) creates a sparse file viaset_len, assertsallocated_size == 0, callsallocate, and asserts blocks are reserved.
- README gained a Minimum Supported Rust Version section noting
that the crate's declared MSRV (
rust-version = "1.75.0") covers the defaultsyncfeature, and thatasync-std/smolinherit a higher effective MSRV (1.85) from their transitive dependencies (async-lock,async-signal).
- Renamed
FileExt::lock_exclusive/AsyncFileExt::lock_exclusivetolock, matching the stabilizedstd::fs::File::lockAPI. - Renamed
FileExt::try_lock_exclusive/AsyncFileExt::try_lock_exclusivetotry_lock, matchingstd::fs::File::try_lock. - Changed the return type of
try_lockandtry_lock_sharedfromstd::io::Result<bool>toResult<(), TryLockError>.Ok(())still indicates the lock was acquired;Err(TryLockError::WouldBlock)now indicates the lock is held by another handle. This matches the stablestd::fs::File::try_locksignature (Ok(false)was the nightly shape prior to 1.89). - Removed the top-level
lock_contended_error()helper. UseTryLockError::WouldBlockinstead. - Flattened the
fs_stdmodule: theFileExttrait forstd::fs::Filenow lives at the crate root. Update imports fromuse fs4::fs_std::FileExt;touse fs4::FileExt;. All other backends (fs_err2,fs_err3,tokio,smol,async_std,fs_err2_tokio,fs_err3_tokio) remain nested, since each defines its ownFileExt/AsyncFileExtover a different concreteFiletype.
- New
fs4::TryLockErrorenum, mirroringstd::fs::TryLockErrorwithError(io::Error)andWouldBlockvariants. ImplementsDebug,Display,std::error::Error(withsource()exposing the innerio::Error),From<io::Error>(kindWouldBlockcollapses into theWouldBlockvariant; everything else wraps intoError), andFrom<TryLockError> for io::Error(WouldBlockbecomesio::Error::from(io::ErrorKind::WouldBlock);Error(inner)passes through verbatim).
- Fixed feature typos that made the crate fail to compile with only
fs-err2,fs-err3,fs-err2-tokio, orfs-err3-tokioenabled (withoutsync/tokio).#[cfg(feature = "fs-err")]andfeature = "fs-err-tokio"both referenced feature names that do not exist inCargo.toml. - On Windows, skip the internal
set_lencall when the file's existing cluster-aligned allocation already coverslen. Avoids the Windows buffered-I/O quirks observed when the end-of-file pointer is moved inside an already-allocated cluster (#13). The trait doc now carves this behavior out from the general "file size is at leastlenbytes" contract. - Short-circuited
allocateon Unix when the file is already at leastlenbytes long. Fixes macOSfallocatespuriously returningENOSPCwhen re-callingallocate(len)on an existing file (#15). - Added
cygwinto theallocatetarget_osset so builds stop failing with a missingsys::allocatesymbol on that target (#44). - Added
redoxandcygwinto the asyncallocatefallback branch so it matches the sync variant (#43 follow-up). - Moved AIX from the async
fallocatebranch to theset_lenfallback branch, matching the sync implementation. - Trait docs now explicitly state that locks are released automatically when the owning file handle is closed (#23).
- Removed the stale trait-doc reference to non-existent cross-platform
tests in
lib.rs(#16). - Mitigated #31 (compiler warning about name collisions with upcoming
std methods): because 1.0 renames the trait methods to match std
exactly,
std::fs::File::lock/try_lock/unlock(stable in Rust 1.89+) now win via inherent dispatch andunstable_name_collisionsno longer fires for std users on recent rustc. - Gated the
lock_impl!macro with#[allow(unused_macros)]socargo clippy --no-default-features(filesystem-stats-only build) does not fail under-D warnings. - Removed dead duplicate macros
cfg_fs2_err/cfg_fs3_errfromlib.rs(unified withcfg_fs_err2/cfg_fs_err3). - Updated
html_root_urlto the 1.0.0 docs.rs path.
- Bumped
windows-sysfrom 0.59 to 0.61.
- Removed all
#[bench]functions from the test harness. They measured OS syscalls (flock,LockFileEx,fallocate,statvfs) rather than fs4 code, and produced numbers dominated by the underlying filesystem. Dropping them lets the crate build and test on stable Rust (the bench harness was the only thing pinning nightly via#![cfg_attr(test, feature(test))]).rust-toolchain.tomlis nowstable. - Added regression tests:
allocate_preserves_eof_within_cluster— exercises the #13 precondition (cluster-alignedAllocationSizewith EOF inside the cluster) and asserts EOF is not moved whenallocate(len)is re-called withlen <= allocated_sizeon Windows.allocate_idempotent— exercises the #15 precondition (back-to-backallocate(len)calls on macOS) to prevent a regression in the short-circuit.- Seven
TryLockErrorunit tests covering variants,Display,std::error::Error::source,From<io::Error>,From<TryLockError> for io::Error, and round-trip preservation ofErrorKind. FsStatsgetter + derive unit tests (previously every test destructured the struct, so the four getter method bodies were never executed and showed as uncovered).
- Fixed a long-standing macOS test failure: the cross-platform
filesystem_spacetest assertedavailable_space <= free_space, which does not hold on macOS APFS. APFS counts purgeable space (snapshots, cached data reclaimable on demand) inf_bavailbut not inf_bfree, so the POSIX invariant is violated on every run. The assertion is removed and the async variant now makes a singlestatvfscall plus destructure instead of three separate calls racing with concurrent filesystem activity from other tests.
- New
.github/workflows/coverage.ymlworkflow: per-OS matrix (ubuntu-latest, macos-latest, windows-latest) runningcargo tarpaulin --engine llvm --all-features --run-types tests --ignore-testson each runner and uploading per-OS reports to Codecov with per-OS flags. Each runner--exclude-filesthe other OS's sources so they do not register as 0/N uncovered lines. - The
crossjob now installs the MinGW-w64 toolchain when targeting*-pc-windows-gnu.windows-sys0.61 invokesdlltoolduring build, andubuntu-latestships only the x86_64 MinGW toolchain by default. - README install snippets updated from
fs4 = { version = "0.13", ... }tofs4 = { version = "1", ... }. - Housekeeping: added
.codecov.yml,rustfmt.toml(2-space indent, explains the session-wide reformat), and.github/workflows/loc.yml; removed the obsoletetea.yaml.
- Make
try_lock_*returnstd::io::Result<bool>, which is compatible with the upcomingstd::fs::File::try_lock*instd.