1use std::path::Path;
2
3#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Account {
7 pub username: String,
9 pub password: String,
11}
12
13pub fn is_path_owned_by_current_user(path: &Path) -> std::io::Result<bool> {
17 impl_::is_path_owned_by_current_user(path)
18}
19
20#[cfg(target_os = "wasi")]
22mod impl_ {
23 pub fn is_path_owned_by_current_user(_path: &std::path::Path) -> std::io::Result<bool> {
24 Ok(true)
25 }
26}
27
28#[cfg(all(not(windows), not(target_os = "wasi")))]
29mod impl_ {
30 use std::path::Path;
31
32 pub fn is_path_owned_by_current_user(path: &Path) -> std::io::Result<bool> {
33 fn owner_from_path(path: &Path) -> std::io::Result<u32> {
34 use std::os::unix::fs::MetadataExt;
35 let meta = std::fs::symlink_metadata(path)?;
36 Ok(meta.uid())
37 }
38
39 fn owner_of_current_process() -> std::io::Result<u32> {
40 #[allow(unsafe_code)]
42 let uid = unsafe { libc::geteuid() };
43 Ok(uid)
44 }
45 use std::str::FromStr;
46
47 let owner_of_path = owner_from_path(path)?;
48 let owner_of_process = owner_of_current_process()?;
49 if owner_of_path == owner_of_process {
50 Ok(true)
51 } else if let Some(sudo_uid) =
52 std::env::var_os("SUDO_UID").and_then(|val| val.to_str().and_then(|val_str| u32::from_str(val_str).ok()))
53 {
54 Ok(owner_of_path == sudo_uid)
55 } else {
56 Ok(false)
57 }
58 }
59}
60
61#[cfg(windows)]
62mod impl_ {
63 use std::{
64 io,
65 mem::MaybeUninit,
66 os::windows::io::{FromRawHandle as _, OwnedHandle},
67 path::Path,
68 ptr,
69 };
70
71 macro_rules! error {
72 ($msg:expr) => {{
73 let inner = io::Error::last_os_error();
74 error!(inner, $msg);
75 }};
76 ($inner:expr, $msg:expr) => {{
77 return Err(io::Error::new($inner.kind(), $msg));
78 }};
79 }
80
81 pub fn is_path_owned_by_current_user(path: &Path) -> io::Result<bool> {
82 use windows_sys::Win32::{
83 Foundation::{GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS},
84 Security::{
85 Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT},
86 CheckTokenMembership, EqualSid, GetTokenInformation, IsWellKnownSid, TokenOwner,
87 WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, TOKEN_OWNER,
88 TOKEN_QUERY,
89 },
90 System::Threading::{GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken},
91 };
92
93 if !path.exists() {
94 return Err(io::Error::new(
95 io::ErrorKind::NotFound,
96 format!("{path:?} does not exist."),
97 ));
98 }
99
100 if gix_path::realpath(path).ok() == gix_path::env::home_dir() {
104 return Ok(true);
105 }
106
107 #[allow(unsafe_code)]
108 unsafe {
109 let (folder_owner, descriptor) = {
110 let mut folder_owner = MaybeUninit::uninit();
111 let mut pdescriptor = MaybeUninit::uninit();
112 let result = GetNamedSecurityInfoW(
113 to_wide_path(path).as_ptr(),
114 SE_FILE_OBJECT,
115 OWNER_SECURITY_INFORMATION,
116 folder_owner.as_mut_ptr(),
117 ptr::null_mut(),
118 ptr::null_mut(),
119 ptr::null_mut(),
120 pdescriptor.as_mut_ptr(),
121 );
122
123 if result != ERROR_SUCCESS {
124 let inner = io::Error::from_raw_os_error(result as _);
125 error!(
126 inner,
127 format!(
128 "Couldn't get security information for path '{}' with err {inner}",
129 path.display()
130 )
131 );
132 }
133
134 (folder_owner.assume_init(), pdescriptor.assume_init())
135 };
136
137 struct Descriptor(PSECURITY_DESCRIPTOR);
138
139 impl Drop for Descriptor {
140 fn drop(&mut self) {
141 #[allow(unsafe_code)]
142 unsafe {
144 LocalFree(self.0 as _);
145 }
146 }
147 }
148
149 let _descriptor = Descriptor(descriptor);
150
151 let token = {
152 let mut token = MaybeUninit::uninit();
153
154 if OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, token.as_mut_ptr()) == 0
156 && OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token.as_mut_ptr()) == 0
157 {
158 error!("Couldn't acquire thread or process token");
159 }
160 token.assume_init()
161 };
162
163 let _owned_token = OwnedHandle::from_raw_handle(token as _);
164
165 let buf = 'token_buf: {
166 let mut buffer_size = 36;
167 let mut heap_buf = vec![0; 36];
168
169 loop {
170 if GetTokenInformation(
171 token,
172 TokenOwner,
173 heap_buf.as_mut_ptr().cast(),
174 heap_buf.len() as _,
175 &mut buffer_size,
176 ) != 0
177 {
178 break 'token_buf heap_buf;
179 }
180
181 if GetLastError() != ERROR_INSUFFICIENT_BUFFER {
182 error!("Couldn't acquire token ownership");
183 }
184
185 heap_buf.resize(buffer_size as _, 0);
186 }
187 };
188
189 let token_owner = (*buf.as_ptr().cast::<TOKEN_OWNER>()).Owner;
190
191 if EqualSid(folder_owner, token_owner) != 0 {
194 return Ok(true);
195 }
196
197 if IsWellKnownSid(token_owner, WinBuiltinAdministratorsSid) == 0 {
199 return Ok(false);
200 }
201
202 let mut is_member = 0;
203 if CheckTokenMembership(0, token_owner, &mut is_member) == 0 {
204 error!("Couldn't check if user is an administrator");
205 }
206
207 Ok(is_member != 0)
208 }
209 }
210
211 fn to_wide_path(path: impl AsRef<Path>) -> Vec<u16> {
212 use std::os::windows::ffi::OsStrExt;
213 let mut wide_path: Vec<_> = path.as_ref().as_os_str().encode_wide().collect();
214 wide_path.push(0);
215 wide_path
216 }
217}