Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions src/backend/linux_raw/fs/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
const _ = !0;
Expand Down
69 changes: 69 additions & 0 deletions tests/fs/statfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>())
.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();
Expand Down
Loading