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