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;
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> {
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"),
});
}
}