mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-14 21:20:48 +08:00
fix(singleton): repair Windows lock file permissions in place
This commit is contained in:
@@ -5,6 +5,10 @@
|
||||
|
||||
- 修复从开始菜单再次打开应用后,窗口反复弹出并提示启动失败的问题
|
||||
|
||||
**🖥️ Windows**
|
||||
|
||||
- 修复 Windows 升级后因文件权限异常无法启动、需要手动清理配置的问题
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
|
||||
@@ -63,8 +63,8 @@ pub(crate) fn open_private_current_user_file(path: &Path) -> Result<std::fs::Fil
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn open_or_create_private_current_user_file(path: &Path) -> Result<std::fs::File> {
|
||||
windows_owner::open_or_create_private_file(path)
|
||||
pub(crate) fn open_or_create_current_user_lock(path: &Path) -> Result<std::fs::File> {
|
||||
windows_owner::open_or_create_lock_file(path)
|
||||
}
|
||||
|
||||
/// Repairs an application data root Windows no longer reports as owned by the current user.
|
||||
@@ -110,7 +110,7 @@ mod windows_owner {
|
||||
BY_HANDLE_FILE_INFORMATION, CREATE_NEW, CreateFileW, FILE_ALL_ACCESS, FILE_ATTRIBUTE_DIRECTORY,
|
||||
FILE_ATTRIBUTE_HIDDEN, FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_TYPE_DISK,
|
||||
GetFileInformationByHandle, GetFileType, OPEN_EXISTING, READ_CONTROL, WRITE_DAC, WRITE_OWNER,
|
||||
GetFileInformationByHandle, GetFileType, OPEN_ALWAYS, OPEN_EXISTING, READ_CONTROL, WRITE_DAC, WRITE_OWNER,
|
||||
};
|
||||
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
|
||||
|
||||
@@ -215,22 +215,49 @@ mod windows_owner {
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
pub(super) fn open_or_create_private_file(path: &Path) -> Result<std::fs::File> {
|
||||
pub(super) fn open_or_create_lock_file(path: &Path) -> Result<std::fs::File> {
|
||||
let sid = current_sid()?;
|
||||
let descriptor = LocalSecurityDescriptor::from_sid(&sid)?;
|
||||
match create_file(path, &descriptor) {
|
||||
Ok(file) => Ok(file),
|
||||
Err(error)
|
||||
if error
|
||||
.downcast_ref::<std::io::Error>()
|
||||
.is_some_and(|error| error.raw_os_error() == Some(ERROR_FILE_EXISTS as i32)) =>
|
||||
{
|
||||
let file = open_file(path)?;
|
||||
validate_private_file(&file, descriptor.owner()?)?;
|
||||
Ok(file)
|
||||
}
|
||||
Err(error) => Err(error),
|
||||
let attributes = SECURITY_ATTRIBUTES {
|
||||
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
|
||||
lpSecurityDescriptor: descriptor.0,
|
||||
bInheritHandle: 0,
|
||||
};
|
||||
let wide = wide_path(path)?;
|
||||
let handle = unsafe {
|
||||
CreateFileW(
|
||||
wide.as_ptr(),
|
||||
GENERIC_READ | READ_CONTROL | WRITE_DAC,
|
||||
// Replacing a live lock would let a second instance lock a different file.
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
&attributes,
|
||||
OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if handle == INVALID_HANDLE_VALUE {
|
||||
return Err(std::io::Error::last_os_error()).context("failed to open current-user lock file");
|
||||
}
|
||||
let file = unsafe { std::fs::File::from_raw_handle(handle) };
|
||||
let mut information = BY_HANDLE_FILE_INFORMATION::default();
|
||||
if unsafe { GetFileInformationByHandle(file.as_raw_handle(), &mut information) } == 0 {
|
||||
return Err(std::io::Error::last_os_error()).context("failed to inspect current-user lock file");
|
||||
}
|
||||
if information.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT) != 0
|
||||
|| unsafe { GetFileType(file.as_raw_handle()) } != FILE_TYPE_DISK
|
||||
|| information.nNumberOfLinks != 1
|
||||
{
|
||||
bail!("current-user lock is not an ordinary, single-link file");
|
||||
}
|
||||
let expected_owner = descriptor.owner()?;
|
||||
if !owner_matches(&file, expected_owner)? {
|
||||
bail!("current-user lock belongs to a different Windows user");
|
||||
}
|
||||
// Lock contents are never trusted; records and tokens retain their separate validation.
|
||||
descriptor.apply_dacl(file.as_raw_handle())?;
|
||||
validate_private_file(&file, expected_owner)?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn create_file(path: &Path, descriptor: &LocalSecurityDescriptor) -> Result<std::fs::File> {
|
||||
@@ -442,7 +469,8 @@ mod windows_owner {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// A DACL that refuses even the owner read settles it just as well.
|
||||
// Failed inspection does not prove that a live lock belongs to another user.
|
||||
Err(error) if name == crate::utils::server::INSTANCE_LOCK_FILE => return Err(error),
|
||||
Err(_) => false,
|
||||
};
|
||||
if !ours {
|
||||
@@ -712,7 +740,7 @@ mod windows_owner {
|
||||
)
|
||||
};
|
||||
if status != 0 {
|
||||
bail!("failed to restrict owner token DACL: Windows error {status}");
|
||||
bail!("failed to restrict current-user file DACL: Windows error {status}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ fn open_instance_lock(path: &Path) -> Result<std::fs::File> {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
crate::core::owner_identity::open_or_create_private_current_user_file(path)
|
||||
crate::core::owner_identity::open_or_create_current_user_lock(path)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
|
||||
Reference in New Issue
Block a user