running_process_platform_internal/platform_linux/resources.rs
1//! What Linux says about running out of things.
2
3use std::io;
4use std::path::Path;
5
6use crate::platform::resources::InodeCapacity;
7
8/// Whether this error means the process or the system is out of descriptors.
9///
10/// `EMFILE` is this process's limit and `ENFILE` the system's. A caller sheds
11/// load for either, so both answer the same question.
12pub fn signals_fd_exhaustion(error: &io::Error) -> bool {
13 matches!(error.raw_os_error(), Some(libc::EMFILE | libc::ENFILE))
14}
15
16/// Whether this error means the filesystem is out of space.
17///
18/// `EDQUOT` is a quota rather than a full disk, but the caller can do nothing
19/// different about it: the write will not succeed until something is freed.
20pub fn signals_storage_exhaustion(error: &io::Error) -> bool {
21 if matches!(error.kind(), io::ErrorKind::StorageFull) {
22 return true;
23 }
24 matches!(error.raw_os_error(), Some(libc::ENOSPC | libc::EDQUOT))
25}
26
27/// One error this host would report for descriptor exhaustion.
28pub fn fd_exhaustion_error() -> io::Error {
29 io::Error::from_raw_os_error(libc::EMFILE)
30}
31
32/// One error this host would report for storage exhaustion.
33pub fn storage_exhaustion_error() -> io::Error {
34 io::Error::from_raw_os_error(libc::ENOSPC)
35}
36
37/// Probe inode capacity for the filesystem containing `path`.
38///
39/// Inode exhaustion matters here in a way it does not on Windows: a filesystem
40/// with a fixed inode table (ext4 most prominently) can fail writes with
41/// `ENOSPC` while plenty of bytes remain free. A filesystem that reports an
42/// empty table -- btrfs, and others that allocate inodes dynamically -- has
43/// nothing to run out of, and is reported as not applicable rather than as
44/// zero of zero.
45pub fn inode_capacity(path: &Path) -> io::Result<Option<InodeCapacity>> {
46 use std::os::unix::ffi::OsStrExt;
47
48 let c_path = std::ffi::CString::new(path.as_os_str().as_bytes())
49 .map_err(|error| io::Error::new(io::ErrorKind::InvalidInput, error))?;
50 // SAFETY: an all-zero `statvfs` is a valid one; the call fills it.
51 let mut stats: libc::statvfs = unsafe { std::mem::zeroed() };
52 // SAFETY: `c_path` is a NUL-terminated path alive for the call, and
53 // `stats` is valid writable storage of exactly the expected type.
54 let rc = unsafe { libc::statvfs(c_path.as_ptr(), &mut stats) };
55 if rc != 0 {
56 return Err(io::Error::last_os_error());
57 }
58 if stats.f_files == 0 {
59 return Ok(None);
60 }
61 // fsfilcnt_t is u64 on Linux but u32 on macOS; keep explicit casts.
62 #[allow(clippy::unnecessary_cast)]
63 Ok(Some(InodeCapacity {
64 total: stats.f_files as u64,
65 free: stats.f_favail as u64,
66 }))
67}