| #![cfg(windows)] |
| |
| use std::env; |
| use std::ffi::OsString; |
| use std::io; |
| use std::os::windows::ffi::OsStringExt; |
| use std::path::PathBuf; |
| use std::ptr; |
| |
| use winapi::shared::minwindef::DWORD; |
| use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; |
| use winapi::um::errhandlingapi::{GetLastError, SetLastError}; |
| use winapi::um::handleapi::CloseHandle; |
| use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken}; |
| use winapi::um::userenv::GetUserProfileDirectoryW; |
| use winapi::um::winnt::TOKEN_READ; |
| |
| pub fn home_dir_inner() -> Option<PathBuf> { |
| env::var_os("USERPROFILE") |
| .map(PathBuf::from) |
| .or_else(home_dir_crt) |
| } |
| |
| #[cfg(not(target_vendor = "uwp"))] |
| fn home_dir_crt() -> Option<PathBuf> { |
| unsafe { |
| let me = GetCurrentProcess(); |
| let mut token = ptr::null_mut(); |
| if OpenProcessToken(me, TOKEN_READ, &mut token) == 0 { |
| return None; |
| } |
| let _g = scopeguard::guard(token, |h| { |
| let _ = CloseHandle(h); |
| }); |
| fill_utf16_buf( |
| |buf, mut sz| { |
| match GetUserProfileDirectoryW(token, buf, &mut sz) { |
| 0 if GetLastError() != ERROR_INSUFFICIENT_BUFFER => 0, |
| 0 => sz, |
| _ => sz - 1, // sz includes the null terminator |
| } |
| }, |
| os2path, |
| ) |
| .ok() |
| } |
| } |
| |
| #[cfg(target_vendor = "uwp")] |
| fn home_dir_crt() -> Option<PathBuf> { |
| None |
| } |
| |
| fn os2path(s: &[u16]) -> PathBuf { |
| PathBuf::from(OsString::from_wide(s)) |
| } |
| |
| // Many Windows APIs follow a pattern of where we hand a buffer and then they |
| // will report back to us how large the buffer should be or how many bytes |
| // currently reside in the buffer. This function is an abstraction over these |
| // functions by making them easier to call. |
| // |
| // The first callback, `f1`, is yielded a (pointer, len) pair which can be |
| // passed to a syscall. The `ptr` is valid for `len` items (u16 in this case). |
| // The closure is expected to return what the syscall returns which will be |
| // interpreted by this function to determine if the syscall needs to be invoked |
| // again (with more buffer space). |
| // |
| // Once the syscall has completed (errors bail out early) the second closure is |
| // yielded the data which has been read from the syscall. The return value |
| // from this closure is then the return value of the function. |
| // |
| // Taken from rust-lang/rust/src/libstd/sys/windows/mod.rs#L106 |
| fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> io::Result<T> |
| where |
| F1: FnMut(*mut u16, DWORD) -> DWORD, |
| F2: FnOnce(&[u16]) -> T, |
| { |
| // Start off with a stack buf but then spill over to the heap if we end up |
| // needing more space. |
| let mut stack_buf = [0u16; 512]; |
| let mut heap_buf = Vec::new(); |
| unsafe { |
| let mut n = stack_buf.len(); |
| loop { |
| let buf = if n <= stack_buf.len() { |
| &mut stack_buf[..] |
| } else { |
| let extra = n - heap_buf.len(); |
| heap_buf.reserve(extra); |
| heap_buf.set_len(n); |
| &mut heap_buf[..] |
| }; |
| |
| // This function is typically called on windows API functions which |
| // will return the correct length of the string, but these functions |
| // also return the `0` on error. In some cases, however, the |
| // returned "correct length" may actually be 0! |
| // |
| // To handle this case we call `SetLastError` to reset it to 0 and |
| // then check it again if we get the "0 error value". If the "last |
| // error" is still 0 then we interpret it as a 0 length buffer and |
| // not an actual error. |
| SetLastError(0); |
| let k = match f1(buf.as_mut_ptr(), n as DWORD) { |
| 0 if GetLastError() == 0 => 0, |
| 0 => return Err(io::Error::last_os_error()), |
| n => n, |
| } as usize; |
| if k == n && GetLastError() == ERROR_INSUFFICIENT_BUFFER { |
| n *= 2; |
| } else if k >= n { |
| n = k; |
| } else { |
| return Ok(f2(&buf[..k])); |
| } |
| } |
| } |
| } |