starship/src/modules/utils/directory_nix.rs

64 lines
2.1 KiB
Rust

use nix::sys::stat::Mode;
use nix::unistd::{Gid, Uid};
use std::fs;
use std::os::unix::fs::MetadataExt;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
/// Checks if the current user can write to the `folder_path`.
///
/// It extracts Unix access rights from the directory and checks whether
/// 1) the current user is the owner of the directory and whether it has the write access
/// 2) the current user's primary group is the directory group owner whether if it has write access
/// 2a) (not implemented on macOS) one of the supplementary groups of the current user is the
/// directory group owner and whether it has write access
/// 3) 'others' part of the access mask has the write access
pub fn is_write_allowed(folder_path: &Path) -> Result<bool, String> {
let meta =
fs::metadata(folder_path).map_err(|e| format!("Unable to stat() directory: {e:?}"))?;
let perms = meta.permissions().mode();
let euid = Uid::effective();
if euid.is_root() {
return Ok(true);
}
if meta.uid() == euid.as_raw() {
Ok(perms & Mode::S_IWUSR.bits() as u32 != 0)
} else if (meta.gid() == Gid::effective().as_raw())
|| (get_supplementary_groups().contains(&meta.gid()))
{
Ok(perms & Mode::S_IWGRP.bits() as u32 != 0)
} else {
Ok(perms & Mode::S_IWOTH.bits() as u32 != 0)
}
}
#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]
fn get_supplementary_groups() -> Vec<u32> {
match nix::unistd::getgroups() {
Err(_) => Vec::new(),
Ok(v) => v.into_iter().map(nix::unistd::Gid::as_raw).collect(),
}
}
#[cfg(all(unix, any(target_os = "macos", target_os = "ios")))]
fn get_supplementary_groups() -> Vec<u32> {
// at the moment nix crate does not provide it for macOS
Vec::new()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn read_only_test() {
assert_eq!(is_write_allowed(Path::new("/etc")), Ok(false));
assert!(match is_write_allowed(Path::new("/i_dont_exist")) {
Ok(_) => false,
Err(e) => e.starts_with("Unable to stat() directory"),
});
}
}