Skip to main content

max / makenotwork

server: wire fs2 statvfs into scan spool free-space check
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-27 15:46 UTC
Commit: 4512c3d4fb3607b33e33707630baa34e7a008a28
Parent: 34e566a
3 files changed, +26 insertions, -7 deletions
@@ -2836,6 +2836,16 @@ dependencies = [
2836 2836 ]
2837 2837
2838 2838 [[package]]
2839 + name = "fs2"
2840 + version = "0.4.3"
2841 + source = "registry+https://github.com/rust-lang/crates.io-index"
2842 + checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
2843 + dependencies = [
2844 + "libc",
2845 + "winapi",
2846 + ]
2847 +
2848 + [[package]]
2839 2849 name = "fs_extra"
2840 2850 version = "1.3.0"
2841 2851 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4158,6 +4168,7 @@ dependencies = [
4158 4168 "docengine",
4159 4169 "dotenvy",
4160 4170 "email_address",
4171 + "fs2",
4161 4172 "git2",
4162 4173 "goblin 0.10.5",
4163 4174 "governor",
@@ -80,6 +80,7 @@ infer = "0.19"
80 80 goblin = "0.10"
81 81 zip = "8.2"
82 82 yara-x = "1.16"
83 + fs2 = "0.4"
83 84
84 85 # CSV parsing (import system)
85 86 csv = "1.3"
@@ -44,19 +44,26 @@ impl Drop for SpoolHandle {
44 44 /// Refuse the job if writing `expected_size` would leave the spool volume
45 45 /// with less than `SCAN_SPOOL_FREE_RESERVE_BYTES` free, or if it exceeds
46 46 /// the per-object cap.
47 - ///
48 - /// TODO: the free-space half of this check requires a `statvfs` call.
49 - /// Pending the user's call on adding a `fs2` (or `nix`) dependency, this
50 - /// only enforces the per-object cap; full free-space enforcement lands
51 - /// with the dep.
52 - pub fn check_free_space(_spool_dir: &Path, expected_size: u64) -> Result<(), String> {
47 + pub fn check_free_space(spool_dir: &Path, expected_size: u64) -> Result<(), String> {
53 48 if expected_size > SCAN_SPOOL_MAX_BYTES {
54 49 return Err(format!(
55 50 "object exceeds scan spool cap ({} > {} bytes)",
56 51 expected_size, SCAN_SPOOL_MAX_BYTES
57 52 ));
58 53 }
59 - let _ = SCAN_SPOOL_FREE_RESERVE_BYTES;
54 + let probe = if spool_dir.exists() {
55 + spool_dir
56 + } else {
57 + spool_dir.parent().unwrap_or(Path::new("/"))
58 + };
59 + let free = fs2::available_space(probe)
60 + .map_err(|e| format!("statvfs {}: {e}", probe.display()))?;
61 + if free.saturating_sub(expected_size) < SCAN_SPOOL_FREE_RESERVE_BYTES {
62 + return Err(format!(
63 + "scan spool volume short on space: free={} expected={} reserve={}",
64 + free, expected_size, SCAN_SPOOL_FREE_RESERVE_BYTES
65 + ));
66 + }
60 67 Ok(())
61 68 }
62 69