# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/within-what-ephemeral-workspace-and-disk-headroom-bounds-must-an-instance-confine-temporary-file-lifecycles-to-prevent-inode-exhaustion-and-enospc-crashes.md # Remnant Warrant: https://corpusfetch.com/c/within-what-ephemeral-workspace-and-disk-headroom-bounds-must-an-instance-confine-temporary-file-lifecycles-to-prevent-inode-exhaustion-and-enospc-crashes.warrant.json # Markdown SHA-256: f818b5432c9e9e1bb76c18dc55cd7321fdda16d89aee3330ae95d7db3eaf983f # Code AST SHA-256: e20e3b45d95bf42907a21857e14883ef9930704c1be3ea13604ddfe84efbe2f8 # Verified Classes: DiskHeadroomError # Defensive Guards: 2 fail-closed invariants (zero external deps) import os import shutil import tempfile import time from contextlib import contextmanager from pathlib import Path from typing import Iterator class DiskHeadroomError(OSError): '''Raised when a filesystem lacks sufficient byte or inode headroom for safe execution.''' pass def verify_filesystem_headroom( target_dir: str | Path, *, required_bytes: int = 64 * 1024 * 1024, # 64 MiB default task estimate reserve_free_bytes: int = 512 * 1024 * 1024, # Always leave >= 512 MiB untouched reserve_free_inodes: int = 4096, # Always leave >= 4,096 free inodes ) -> None: ''' Inspects kernel filesystem statistics via `os.statvfs` and fails fast before writing if either unprivileged free bytes or free inodes would drop below the mandatory safety reserve. ''' path_obj = Path(target_dir).resolve() check_path = path_obj if path_obj.exists() else path_obj.parent st = os.statvfs(check_path) avail_bytes = st.f_bavail * st.f_frsize needed_total_bytes = required_bytes + reserve_free_bytes if avail_bytes < needed_total_bytes: raise DiskHeadroomError( f"Insufficient disk byte headroom on {check_path}: " f"available={avail_bytes // (1024 * 1024)} MiB, " f"required_with_reserve={needed_total_bytes // (1024 * 1024)} MiB." ) # Note: Some virtual filesystems report f_files == 0 when inodes are dynamic if st.f_files > 0 and st.f_favail < reserve_free_inodes: raise DiskHeadroomError( f"Insufficient filesystem inodes on {check_path}: " f"available_inodes={st.f_favail}, reserve_required={reserve_free_inodes}." ) def sweep_stale_scratch_dirs( scratch_root: str | Path, *, prefix: str = "cf_task_", max_age_sec: float = 3600.0, ) -> int: ''' Sweeps orphaned task scratch directories left behind by previously SIGKILLed or crashed processes whose last-modified timestamp exceeds `max_age_sec`. ''' root = Path(scratch_root) if not root.is_dir(): return 0 now = time.time() reclaimed = 0 for entry in root.iterdir(): if not entry.name.startswith(prefix): continue try: age = now - entry.stat().st_mtime if age > max_age_sec: if entry.is_dir() and not entry.is_symlink(): shutil.rmtree(entry, ignore_errors=True) else: entry.unlink(missing_ok=True) reclaimed += 1 except OSError: continue return reclaimed @contextmanager def ephemeral_task_workspace( *, base_dir: str | Path | None = None, prefix: str = "cf_task_", estimated_write_bytes: int = 64 * 1024 * 1024, ) -> Iterator[Path]: ''' Provides an isolated, mode-0700 temporary workspace directory that: 1. Verifies sufficient byte and inode headroom before creation. 2. Sweeps stale dead-process siblings older than 1 hour. 3. Guarantees recursive cleanup on normal exit or exception. ''' root_dir = Path(base_dir) if base_dir is not None else Path(tempfile.gettempdir()) root_dir.mkdir(parents=True, exist_ok=True) # Gate 1: Reclaim any stale orphans from crashed prior runs sweep_stale_scratch_dirs(root_dir, prefix=prefix, max_age_sec=3600.0) # Gate 2: Verify byte and inode headroom before allocating workspace verify_filesystem_headroom(root_dir, required_bytes=estimated_write_bytes) # Gate 3: Create CSPRNG-named 0700 directory and enforce cleanup in finally raw_tmp = tempfile.mkdtemp(prefix=prefix, dir=str(root_dir)) workspace_path = Path(raw_tmp) os.chmod(workspace_path, 0o700) try: yield workspace_path finally: shutil.rmtree(workspace_path, ignore_errors=True)