1use std::path::Path;
2
3#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
4#[cfg_attr(feature = "serde1", 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: impl AsRef<Path>) -> std::io::Result<bool> {
17 impl_::is_path_owned_by_current_user(path)
18}
19
20#[cfg(not(windows))]
21mod impl_ {
22 use std::path::Path;
23
24 pub fn is_path_owned_by_current_user(path: impl AsRef<Path>) -> std::io::Result<bool> {
25 fn owner_from_path(path: impl AsRef<Path>) -> std::io::Result<u32> {
26 use std::os::unix::fs::MetadataExt;
27 let meta = std::fs::symlink_metadata(path)?;
28 Ok(meta.uid())
29 }
30
31 fn owner_of_current_process() -> std::io::Result<u32> {
32 #[allow(unsafe_code)]
34 let uid = unsafe { libc::geteuid() };
35 Ok(uid)
36 }
37 use std::str::FromStr;
38
39 let owner_of_path = owner_from_path(path)?;
40 let owner_of_process = owner_of_current_process()?;
41 if owner_of_path == owner_of_process {
42 Ok(true)
43 } else if let Some(sudo_uid) =
44 std::env::var_os("SUDO_UID").and_then(|val| val.to_str().and_then(|val_str| u32::from_str(val_str).ok()))
45 {
46 Ok(owner_of_path == sudo_uid)
47 } else {
48 Ok(false)
49 }
50 }
51}
52
53#[cfg(windows)]
54mod impl_ {
55 use std::path::Path;
56
57 fn err(msg: impl Into<String>) -> std::io::Error {
58 std::io::Error::new(std::io::ErrorKind::Other, msg.into())
59 }
60
61 pub fn is_path_owned_by_current_user(path: impl AsRef<Path>) -> std::io::Result<bool> {
62 use windows::{
63 core::{Error, PCWSTR},
64 Win32::{
65 Foundation::{CloseHandle, BOOL, HANDLE, PSID},
66 Security::{
67 Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT},
68 CheckTokenMembership, EqualSid, GetTokenInformation, IsWellKnownSid, TokenOwner,
69 WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, TOKEN_OWNER,
70 TOKEN_QUERY,
71 },
72 System::{
73 Memory::LocalFree,
74 Threading::{GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken},
75 },
76 },
77 };
78
79 let mut err_msg = None;
80 let mut is_owned = false;
81 let path = path.as_ref();
82
83 if !path.exists() {
84 return Err(std::io::Error::new(
85 std::io::ErrorKind::NotFound,
86 format!("{:?} does not exist.", path),
87 ));
88 }
89
90 if git_path::realpath(path).ok() == dirs::home_dir() {
94 return Ok(true);
95 }
96
97 #[allow(unsafe_code)]
98 unsafe {
99 let mut folder_owner = PSID::default();
100 let mut pdescriptor = PSECURITY_DESCRIPTOR::default();
101 let result = GetNamedSecurityInfoW(
102 PCWSTR(to_wide_path(path).as_ptr()),
103 SE_FILE_OBJECT,
104 OWNER_SECURITY_INFORMATION,
105 Some(&mut folder_owner),
106 None,
107 None,
108 None,
109 &mut pdescriptor,
110 );
111
112 if result.is_ok() {
114 let mut token = HANDLE::default();
115 OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, &mut token)
117 .ok()
118 .or_else(|_| OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).ok())?;
119
120 let mut buffer_size = 0;
121 let mut buffer = Vec::<u8>::new();
122 GetTokenInformation(token, TokenOwner, None, 0, &mut buffer_size);
123 if buffer_size != 0 {
124 buffer.resize(buffer_size as usize, 0);
125 if GetTokenInformation(
126 token,
127 TokenOwner,
128 Some(buffer.as_mut_ptr() as *mut std::ffi::c_void),
129 buffer_size,
130 &mut buffer_size,
131 )
132 .as_bool()
133 {
134 let token_owner = buffer.as_ptr() as *const TOKEN_OWNER;
135 let token_owner = (*token_owner).Owner;
136
137 is_owned = EqualSid(folder_owner, token_owner).as_bool();
138
139 if !is_owned && IsWellKnownSid(token_owner, WinBuiltinAdministratorsSid).as_bool() {
141 let mut is_member = BOOL::default();
142 match CheckTokenMembership(HANDLE::default(), token_owner, &mut is_member).ok() {
144 Err(e) => err_msg = Some(format!("Couldn't check if user is an administrator: {}", e)),
145 Ok(()) => is_owned = is_member.as_bool(),
146 }
147 }
148 } else {
149 err_msg = format!(
150 "Couldn't get actual token information for current process with err: {}",
151 Error::from_win32()
152 )
153 .into();
154 }
155 } else {
156 err_msg = format!(
157 "Couldn't get token information size info for current process with err: {}",
158 Error::from_win32()
159 )
160 .into();
161 }
162 CloseHandle(token);
163 } else {
164 err_msg = format!(
165 "Couldn't get security information for path '{}' with err {}",
166 path.display(),
167 Error::from_win32()
168 )
169 .into();
170 }
171 LocalFree(pdescriptor.0 as isize);
172 }
173
174 err_msg.map(|msg| Err(err(msg))).unwrap_or(Ok(is_owned))
175 }
176
177 fn to_wide_path(path: impl AsRef<Path>) -> Vec<u16> {
178 use std::os::windows::ffi::OsStrExt;
179 let mut wide_path: Vec<_> = path.as_ref().as_os_str().encode_wide().collect();
180 wide_path.push(0);
181 wide_path
182 }
183}