diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 7abd66539..dc25356b8 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -534,35 +534,40 @@ bitflags! { bitflags! { /// `ST_*` constants for use with [`StatVfs`]. + /// + /// These are the values the kernel reports in `statfs`'s `f_flags`, + /// which `statvfs` passes through unconverted. linux-raw-sys does not + /// define the `ST_*` names, and they are not all equal to the `MS_*` + /// ones: `ST_RELATIME` is `0x1000`, while `MS_RELATIME` is `1 << 21`. #[repr(transparent)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub struct StatVfsMountFlags: u64 { /// `ST_MANDLOCK` - const MANDLOCK = linux_raw_sys::general::MS_MANDLOCK as u64; + const MANDLOCK = 0x0040; /// `ST_NOATIME` - const NOATIME = linux_raw_sys::general::MS_NOATIME as u64; + const NOATIME = 0x0400; /// `ST_NODEV` - const NODEV = linux_raw_sys::general::MS_NODEV as u64; + const NODEV = 0x0004; /// `ST_NODIRATIME` - const NODIRATIME = linux_raw_sys::general::MS_NODIRATIME as u64; + const NODIRATIME = 0x0800; /// `ST_NOEXEC` - const NOEXEC = linux_raw_sys::general::MS_NOEXEC as u64; + const NOEXEC = 0x0008; /// `ST_NOSUID` - const NOSUID = linux_raw_sys::general::MS_NOSUID as u64; + const NOSUID = 0x0002; /// `ST_RDONLY` - const RDONLY = linux_raw_sys::general::MS_RDONLY as u64; + const RDONLY = 0x0001; /// `ST_RELATIME` - const RELATIME = linux_raw_sys::general::MS_RELATIME as u64; + const RELATIME = 0x1000; /// `ST_SYNCHRONOUS` - const SYNCHRONOUS = linux_raw_sys::general::MS_SYNCHRONOUS as u64; + const SYNCHRONOUS = 0x0010; /// const _ = !0; diff --git a/tests/fs/statfs.rs b/tests/fs/statfs.rs index c3ff160ce..e951eba69 100644 --- a/tests/fs/statfs.rs +++ b/tests/fs/statfs.rs @@ -77,6 +77,75 @@ fn test_statvfs() { assert_ne!(f_frsize, 0); } +/// `StatVfsMountFlags` has the `ST_*` values, not the `MS_*` ones. +#[cfg(all(linux_kernel, target_env = "gnu"))] +#[test] +fn test_statvfs_mount_flags_abi() { + use rustix::fs::StatVfsMountFlags as Flags; + + assert_eq!(Flags::MANDLOCK.bits(), libc::ST_MANDLOCK as u64); + assert_eq!(Flags::NOATIME.bits(), libc::ST_NOATIME as u64); + assert_eq!(Flags::NODEV.bits(), libc::ST_NODEV as u64); + assert_eq!(Flags::NODIRATIME.bits(), libc::ST_NODIRATIME as u64); + assert_eq!(Flags::NOEXEC.bits(), libc::ST_NOEXEC as u64); + assert_eq!(Flags::NOSUID.bits(), libc::ST_NOSUID as u64); + assert_eq!(Flags::RDONLY.bits(), libc::ST_RDONLY as u64); + assert_eq!(Flags::RELATIME.bits(), libc::ST_RELATIME as u64); + assert_eq!(Flags::SYNCHRONOUS.bits(), libc::ST_SYNCHRONOUS as u64); +} + +/// The flags `statvfs` reports for a mount are the per-mount options its +/// `/proc/self/mountinfo` line lists. +#[cfg(linux_kernel)] +#[test] +fn test_statvfs_mount_flags_match_mountinfo() { + use rustix::fs::StatVfsMountFlags as Flags; + + let table = std::fs::read_to_string("/proc/self/mountinfo").unwrap(); + let mut checked = 0; + for point in ["/", "/proc"] { + // The last line at a path is the mount on top, which the path + // reaches. Field 5 is the mount point, field 6 the per-mount options. + let Some(options) = table + .lines() + .map(|line| line.split(' ').collect::>()) + .filter(|fields| fields.len() > 5 && fields[4] == point) + .map(|fields| fields[5].to_owned()) + .next_back() + else { + continue; + }; + let options: Vec<&str> = options.split(',').collect(); + let flags = rustix::fs::statvfs(point).unwrap().f_flag; + #[allow(unused_mut)] + let mut pairs = vec![ + ("ro", Flags::RDONLY), + ("nosuid", Flags::NOSUID), + ("nodev", Flags::NODEV), + ("noexec", Flags::NOEXEC), + ("noatime", Flags::NOATIME), + ("nodiratime", Flags::NODIRATIME), + ]; + // The libc backend has `RELATIME` only where libc defines + // `ST_RELATIME`. + #[cfg(any( + linux_raw, + target_os = "android", + all(target_os = "linux", target_env = "gnu") + ))] + pairs.push(("relatime", Flags::RELATIME)); + for (option, flag) in pairs { + assert_eq!( + flags.contains(flag), + options.contains(&option), + "{point}: {option} in {options:?}, statvfs says {flags:?}" + ); + } + checked += 1; + } + assert_ne!(checked, 0, "neither / nor /proc is in the mount table"); +} + #[test] fn test_fstatvfs() { let file = std::fs::File::open("Cargo.toml").unwrap();