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