Files
LeoVasanko d4be755d46 Enforce 128 MiB minimum free space on uploads
- Add cista/util/diskspace.py with MIN_FREE_BYTES limit and cached
  check_free_space() helper.
- Check available space in File.write() before ftruncate/write.
- Catch ENOSPC in File.write and re-raise as InsufficientStorageError.
- upload_file_chunk catches both proactive and ENOSPC errors and
  returns HTTP 507 Insufficient Storage.
- Add tests for low-disk rejection and ENOSPC handling.
2026-05-05 01:46:30 +00:00

50 lines
1.4 KiB
Python

import shutil
import threading
import time
from pathlib import Path
MIN_FREE_BYTES = 128 * 1024 * 1024
_CHECK_CACHE_TTL = 1.0
class InsufficientStorageError(Exception):
"""Raised when there is not enough disk space for an operation."""
_cache: dict[Path, tuple[float, int]] = {}
_lock = threading.Lock()
def check_free_space(path: Path) -> None:
"""Raise InsufficientStorageError if free space on the filesystem containing *path*
is below MIN_FREE_BYTES. Results are cached per directory for 1 second.
"""
check_path = path.parent if path.parent.exists() else path
check_path = check_path.resolve()
now = time.monotonic()
with _lock:
ts, free = _cache.get(check_path, (0, 0))
if now - ts < _CHECK_CACHE_TTL:
if free < MIN_FREE_BYTES:
raise InsufficientStorageError(
f"Insufficient storage: {free} bytes free, "
f"need at least {MIN_FREE_BYTES} bytes"
)
return
try:
free = shutil.disk_usage(check_path).free
except OSError as e:
raise InsufficientStorageError(f"Cannot check disk usage: {e}") from e
with _lock:
_cache[check_path] = (now, free)
if free < MIN_FREE_BYTES:
raise InsufficientStorageError(
f"Insufficient storage: {free} bytes free, "
f"need at least {MIN_FREE_BYTES} bytes"
)