1#[cfg(unix)]
2use nix::unistd::{AccessFlags, access};
3#[cfg(any(windows, unix))]
4use std::path::Path;
5
6#[derive(Debug)]
8pub enum PermissionResult {
9 PermissionOk,
10 PermissionDenied,
11}
12
13#[cfg(windows)]
16pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
17 match dir.as_ref().read_dir() {
18 Err(e) => {
19 if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
20 PermissionResult::PermissionDenied
21 } else {
22 PermissionResult::PermissionOk
23 }
24 }
25 Ok(_) => PermissionResult::PermissionOk,
26 }
27}
28
29#[cfg(unix)]
30pub fn have_permission(dir: impl AsRef<Path>) -> PermissionResult {
33 access(dir.as_ref(), AccessFlags::X_OK).into()
39}
40
41#[cfg(unix)]
42pub mod users {
43 use nix::unistd::{Gid, Group, Uid, User};
44
45 pub fn get_user_by_uid(uid: Uid) -> Option<User> {
46 User::from_uid(uid).ok().flatten()
47 }
48
49 pub fn get_group_by_gid(gid: Gid) -> Option<Group> {
50 Group::from_gid(gid).ok().flatten()
51 }
52
53 pub fn get_current_uid() -> Uid {
54 Uid::current()
55 }
56
57 pub fn get_current_gid() -> Gid {
58 Gid::current()
59 }
60
61 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "android")))]
62 pub fn get_current_username() -> Option<String> {
63 get_user_by_uid(get_current_uid()).map(|user| user.name)
64 }
65
66 #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "android"))]
67 pub fn current_user_groups() -> Option<Vec<Gid>> {
68 if let Ok(mut groups) = nix::unistd::getgroups() {
69 groups.sort_unstable_by_key(|id| id.as_raw());
70 groups.dedup();
71 Some(groups)
72 } else {
73 None
74 }
75 }
76
77 #[cfg(not(any(target_os = "linux", target_os = "freebsd", target_os = "android")))]
93 pub fn get_user_groups(username: &str, gid: Gid) -> Option<Vec<Gid>> {
94 use nix::libc::{c_int, gid_t};
95 use std::ffi::CString;
96
97 #[cfg(target_os = "macos")]
99 let mut buff: Vec<i32> = vec![0; 1024];
100 #[cfg(not(target_os = "macos"))]
101 let mut buff: Vec<gid_t> = vec![0; 1024];
102
103 let name = CString::new(username).ok()?;
104
105 let mut count = buff.len() as c_int;
106
107 #[cfg(target_os = "macos")]
116 let res = unsafe {
117 nix::libc::getgrouplist(
118 name.as_ptr(),
119 gid.as_raw() as i32,
120 buff.as_mut_ptr(),
121 &mut count,
122 )
123 };
124
125 #[cfg(not(target_os = "macos"))]
126 let res = unsafe {
127 nix::libc::getgrouplist(name.as_ptr(), gid.as_raw(), buff.as_mut_ptr(), &mut count)
128 };
129
130 if res < 0 {
131 None
132 } else {
133 buff.truncate(count as usize);
134 buff.sort_unstable();
135 buff.dedup();
136 #[allow(trivial_numeric_casts)]
138 Some(
139 buff.into_iter()
140 .map(|id| Gid::from_raw(id as gid_t))
141 .filter_map(get_group_by_gid)
142 .map(|group| group.gid)
143 .collect(),
144 )
145 }
146 }
147}
148
149impl<T, E> From<Result<T, E>> for PermissionResult {
150 fn from(value: Result<T, E>) -> Self {
151 match value {
152 Ok(_) => Self::PermissionOk,
153 Err(_) => Self::PermissionDenied,
154 }
155 }
156}