cap_primitives/rustix/linux/fs/
set_permissions_impl.rs

1use super::procfs::set_permissions_through_proc_self_fd;
2use crate::fs::{open, OpenOptions, Permissions};
3use rustix::fs::{fchmod, Mode, RawMode};
4use std::os::unix::fs::PermissionsExt;
5use std::path::Path;
6use std::{fs, io};
7
8pub(crate) fn set_permissions_impl(
9    start: &fs::File,
10    path: &Path,
11    perm: Permissions,
12) -> io::Result<()> {
13    let std_perm = perm.into_std(start)?;
14
15    // First try using `O_PATH` and doing a chmod on `/proc/self/fd/{}`
16    // (`fchmod` doesn't work on `O_PATH` file descriptors).
17    //
18    // This may fail, due to older Linux versions not supporting `O_PATH`, or
19    // due to procfs being unavailable, but if it does work, go with it, as
20    // `O_PATH` tells Linux that we don't actually need to read or write the
21    // file, which may avoid side effects associated with opening device files.
22    let result = set_permissions_through_proc_self_fd(start, path, std_perm.clone());
23    if let Ok(()) = result {
24        return Ok(());
25    }
26
27    // Then try `fchmod` with a normal handle. Normal handles need some kind of
28    // access, so first try read.
29    match open(start, path, OpenOptions::new().read(true)) {
30        Ok(file) => return set_file_permissions(&file, std_perm),
31        Err(err) => match rustix::io::Errno::from_io_error(&err) {
32            Some(rustix::io::Errno::ACCESS) => (),
33            _ => return Err(err),
34        },
35    }
36
37    // Next try write.
38    match open(start, path, OpenOptions::new().write(true)) {
39        Ok(file) => return set_file_permissions(&file, std_perm),
40        Err(err) => match rustix::io::Errno::from_io_error(&err) {
41            Some(rustix::io::Errno::ACCESS) | Some(rustix::io::Errno::ISDIR) => (),
42            _ => return Err(err),
43        },
44    }
45
46    // Nothing worked, so just return the original error.
47    result
48}
49
50fn set_file_permissions(file: &fs::File, perm: fs::Permissions) -> io::Result<()> {
51    // Use `from_bits_truncate` for compatibility with std, which allows
52    // non-permission bits to propagate through.
53    let mode = Mode::from_bits_truncate(perm.mode() as RawMode);
54    Ok(fchmod(file, mode)?)
55}