#[cfg(test)]
mod tests;
use crate::core::error::{AppError, Result};
use std::{marker::PhantomData, rc::Rc};
use windows::{
Win32::{
Foundation::{CloseHandle, HANDLE, WAIT_ABANDONED, WAIT_OBJECT_0},
System::Threading::{
CreateMutexExW, MUTEX_MODIFY_STATE, ReleaseMutex, SYNCHRONIZATION_SYNCHRONIZE,
WaitForSingleObject,
},
},
core::PCWSTR,
};
pub(crate) struct Guard {
handle: HANDLE,
owned: bool,
_thread: PhantomData<Rc<()>>,
}
pub(crate) fn acquire() -> Result<Guard> {
acquire_named("Local\\qq-cli-native-vault-v1", 1000)
}
fn open(name: &str) -> Result<Guard> {
if !name.starts_with("Local\\qq-cli-") || name.len() > 200 || name.contains('\0') {
return Err(unavailable());
}
let name = name.encode_utf16().chain([0]).collect::<Vec<_>>();
let handle = unsafe {
CreateMutexExW(
None,
PCWSTR(name.as_ptr()),
0,
(SYNCHRONIZATION_SYNCHRONIZE | MUTEX_MODIFY_STATE).0,
)
}
.map_err(|_| unavailable())?;
Ok(Guard {
handle,
owned: false,
_thread: PhantomData,
})
}
fn acquire_named(name: &str, timeout: u32) -> Result<Guard> {
let mut guard = open(name)?;
match unsafe { WaitForSingleObject(guard.handle, timeout) } {
WAIT_OBJECT_0 => {
guard.owned = true;
Ok(guard)
}
WAIT_ABANDONED => {
guard.owned = true;
Err(unavailable())
}
_ => Err(unavailable()),
}
}
impl Drop for Guard {
fn drop(&mut self) {
if self.owned {
let _ = unsafe { ReleaseMutex(self.handle) };
}
let _ = unsafe { CloseHandle(self.handle) };
}
}
fn unavailable() -> AppError {
AppError::new(
"CREDENTIAL_UNAVAILABLE",
"Protected credential access could not be coordinated.",
4,
)
}