cap_primitives/rustix/linux/fs/
procfs.rs

1//! Utilities for working with `/proc`, where Linux's `procfs` is typically
2//! mounted. `/proc` serves as an adjunct to Linux's main syscall surface area,
3//! providing additional features with an awkward interface.
4//!
5//! This module does a considerable amount of work to determine whether `/proc`
6//! is mounted, with actual `procfs`, and without any additional mount points
7//! on top of the paths we open.
8
9use crate::fs::OpenOptionsExt;
10use crate::fs::{
11    errors, open, read_link_unchecked, set_times_follow_unchecked, OpenOptions, SystemTimeSpec,
12};
13use io_lifetimes::{AsFd, AsFilelike};
14use rustix::fs::{chmodat, AtFlags, Mode, OFlags, RawMode};
15use rustix::path::DecInt;
16use rustix_linux_procfs::proc_self_fd;
17use std::os::unix::fs::PermissionsExt;
18use std::path::{Path, PathBuf};
19use std::{fs, io};
20
21pub(crate) fn get_path_from_proc_self_fd(file: &fs::File) -> io::Result<PathBuf> {
22    read_link_unchecked(
23        &proc_self_fd()?.as_filelike_view::<fs::File>(),
24        DecInt::from_fd(file).as_ref(),
25        PathBuf::new(),
26    )
27}
28
29/// Linux's `fchmodat` doesn't support `AT_NOFOLLOW_SYMLINK`, so we can't trust
30/// that it won't follow a symlink outside the sandbox. As an alternative, the
31/// symlinks in Linux's /proc/self/fd/* aren't ordinary symlinks, they're
32/// "magic links", which are more transparent, to the point of allowing chmod
33/// to work. So we open the file with `O_PATH` and then do `fchmodat` on the
34/// corresponding /proc/self/fd/* link.
35pub(crate) fn set_permissions_through_proc_self_fd(
36    start: &fs::File,
37    path: &Path,
38    perm: fs::Permissions,
39) -> io::Result<()> {
40    let opath = open(
41        start,
42        path,
43        OpenOptions::new()
44            .read(true)
45            .custom_flags(OFlags::PATH.bits() as i32),
46    )?;
47
48    let dirfd = proc_self_fd()?;
49    let mode = Mode::from_bits(perm.mode() as RawMode).ok_or_else(errors::invalid_flags)?;
50    Ok(chmodat(
51        dirfd,
52        DecInt::from_fd(&opath),
53        mode,
54        AtFlags::empty(),
55    )?)
56}
57
58pub(crate) fn set_times_through_proc_self_fd(
59    start: &fs::File,
60    path: &Path,
61    atime: Option<SystemTimeSpec>,
62    mtime: Option<SystemTimeSpec>,
63) -> io::Result<()> {
64    let opath = open(
65        start,
66        path,
67        OpenOptions::new()
68            .read(true)
69            .custom_flags(OFlags::PATH.bits() as i32),
70    )?;
71
72    // Don't pass `AT_SYMLINK_NOFOLLOW`, because we do actually want to follow
73    // the first symlink. We don't want to follow any subsequent symlinks, but
74    // omitting `O_NOFOLLOW` above ensures that the destination of the link
75    // isn't a symlink.
76    set_times_follow_unchecked(
77        proc_self_fd()?.as_fd(),
78        DecInt::from_fd(&opath).as_ref(),
79        atime,
80        mtime,
81    )
82}