mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-03 05:03:46 +00:00
Use the windows crate facade consistently (#15737)
The initial motivation for this change was that we were using both the `windows`, the `window_sys` and the `windows_core` crate in various places. These crates have slightly unconventional versioning scheme where there is a large workspace with the same version in general, but only some crates get breaking releases when a new breaking release happens, the others stay on the previous breaking version. The `windows` crate is a shim for all three of them, with a single version. This simplifies handling the versions. Using `windows` over `windows_sys` has the advantage of a higher level error interface, we now get a `Result` for all windows API calls instead of C-style int-returns and get-last-error calls. This makes the uv-keyring crate more resilient. We keep using the `windows_registry` crate, which provides a higher level interface to windows registry access.
This commit is contained in:
parent
12764df8b2
commit
cd49e1d11f
15 changed files with 123 additions and 130 deletions
|
|
@ -37,7 +37,6 @@ rustix = { workspace = true }
|
|||
backon = { workspace = true }
|
||||
junction = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
windows-core = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||
fn get_binary_type(path: &Path) -> windows::core::Result<u32> {
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows::Win32::Storage::FileSystem::GetBinaryTypeW;
|
||||
use windows_core::PCWSTR;
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
// References:
|
||||
// https://github.com/denoland/deno/blob/01a6379505712be34ebf2cdc874fa7f54a6e9408/runtime/permissions/which.rs#L131-L154
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ apple-native = ["dep:security-framework"]
|
|||
## Use the secret-service on *nix.
|
||||
secret-service = ["dep:secret-service"]
|
||||
## Use the built-in credential store on Windows
|
||||
windows-native = ["dep:windows-sys", "dep:byteorder"]
|
||||
windows-native = ["dep:windows", "dep:byteorder"]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
|
|
@ -33,7 +33,7 @@ secret-service = { workspace = true, features = ["rt-tokio-crypto-rust"], option
|
|||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
byteorder = { workspace = true, optional = true }
|
||||
windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_Security_Credentials"], optional = true }
|
||||
windows = { workspace = true, features = ["Win32_Foundation", "Win32_Security_Credentials"], optional = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
|||
|
|
@ -44,15 +44,16 @@ use byteorder::{ByteOrder, LittleEndian};
|
|||
use std::collections::HashMap;
|
||||
use std::iter::once;
|
||||
use std::str;
|
||||
use windows_sys::Win32::Foundation::{
|
||||
use windows::Win32::Foundation::{
|
||||
ERROR_BAD_USERNAME, ERROR_INVALID_FLAGS, ERROR_INVALID_PARAMETER, ERROR_NO_SUCH_LOGON_SESSION,
|
||||
ERROR_NOT_FOUND, FILETIME, GetLastError,
|
||||
ERROR_NOT_FOUND, FILETIME, WIN32_ERROR,
|
||||
};
|
||||
use windows_sys::Win32::Security::Credentials::{
|
||||
use windows::Win32::Security::Credentials::{
|
||||
CRED_FLAGS, CRED_MAX_CREDENTIAL_BLOB_SIZE, CRED_MAX_GENERIC_TARGET_NAME_LENGTH,
|
||||
CRED_MAX_STRING_LENGTH, CRED_MAX_USERNAME_LENGTH, CRED_PERSIST_ENTERPRISE, CRED_TYPE_GENERIC,
|
||||
CREDENTIAL_ATTRIBUTEW, CREDENTIALW, CredDeleteW, CredFree, CredReadW, CredWriteW,
|
||||
};
|
||||
use windows::core::PWSTR;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// The representation of a Windows Generic credential.
|
||||
|
|
@ -160,15 +161,13 @@ impl CredentialApi for WinCredential {
|
|||
/// credential in the store.
|
||||
async fn delete_credential(&self) -> Result<()> {
|
||||
self.validate_attributes(None, None)?;
|
||||
let target_name = to_wstr(&self.target_name);
|
||||
let mut target_name = to_wstr(&self.target_name);
|
||||
let cred_type = CRED_TYPE_GENERIC;
|
||||
crate::blocking::spawn_blocking(move || {
|
||||
// SAFETY: Calling Windows API
|
||||
if unsafe { CredDeleteW(target_name.as_ptr(), cred_type, 0) } != 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
// SAFETY: Calling Windows API
|
||||
Err(Error::from_win32_code(unsafe { GetLastError() }).into())
|
||||
unsafe {
|
||||
CredDeleteW(PWSTR(target_name.as_mut_ptr()), cred_type, None)
|
||||
.map_err(|err| Error(err).into())
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
|
@ -264,22 +263,20 @@ impl WinCredential {
|
|||
let credential = CREDENTIALW {
|
||||
Flags: flags,
|
||||
Type: cred_type,
|
||||
TargetName: target_name.as_mut_ptr(),
|
||||
Comment: comment.as_mut_ptr(),
|
||||
TargetName: PWSTR(target_name.as_mut_ptr()),
|
||||
Comment: PWSTR(comment.as_mut_ptr()),
|
||||
LastWritten: last_written,
|
||||
CredentialBlobSize: blob_len,
|
||||
CredentialBlob: blob.as_mut_ptr(),
|
||||
Persist: persist,
|
||||
AttributeCount: attribute_count,
|
||||
Attributes: attributes,
|
||||
TargetAlias: target_alias.as_mut_ptr(),
|
||||
UserName: username.as_mut_ptr(),
|
||||
TargetAlias: PWSTR(target_alias.as_mut_ptr()),
|
||||
UserName: PWSTR(username.as_mut_ptr()),
|
||||
};
|
||||
// SAFETY: Calling Windows API
|
||||
let result = match unsafe { CredWriteW(&raw const credential, 0) } {
|
||||
0 => Err(Error::from_win32_code(unsafe { GetLastError() }).into()),
|
||||
_ => Ok(()),
|
||||
};
|
||||
let result =
|
||||
unsafe { CredWriteW(&raw const credential, 0) }.map_err(|err| Error(err).into());
|
||||
// erase the copy of the secret
|
||||
blob.zeroize();
|
||||
result
|
||||
|
|
@ -300,31 +297,33 @@ impl WinCredential {
|
|||
T: Send + 'static,
|
||||
{
|
||||
self.validate_attributes(None, None)?;
|
||||
let target_name = to_wstr(&self.target_name);
|
||||
let mut target_name = to_wstr(&self.target_name);
|
||||
crate::blocking::spawn_blocking(move || {
|
||||
let mut p_credential = std::ptr::null_mut();
|
||||
// at this point, p_credential is just a pointer to nowhere.
|
||||
// The allocation happens in the `CredReadW` call below.
|
||||
let cred_type = CRED_TYPE_GENERIC;
|
||||
// SAFETY: Calling windows API
|
||||
let result =
|
||||
unsafe { CredReadW(target_name.as_ptr(), cred_type, 0, &raw mut p_credential) };
|
||||
if result == 0 {
|
||||
// `CredReadW` failed, so no allocation has been done, so no free needs to be done
|
||||
Err(Error::from_win32_code(unsafe { GetLastError() }).into())
|
||||
} else {
|
||||
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential. Apply
|
||||
// the passed extractor function to it.
|
||||
let ref_cred: &mut CREDENTIALW = unsafe { &mut *p_credential };
|
||||
let result = f(ref_cred);
|
||||
// Finally, we erase the secret and free the allocated credential.
|
||||
erase_secret(ref_cred);
|
||||
let p_credential = p_credential;
|
||||
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential.
|
||||
// Free the allocation.
|
||||
unsafe { CredFree(p_credential.cast()) }
|
||||
result
|
||||
unsafe {
|
||||
CredReadW(
|
||||
PWSTR(target_name.as_mut_ptr()),
|
||||
cred_type,
|
||||
None,
|
||||
&raw mut p_credential,
|
||||
)
|
||||
}
|
||||
.map_err(Error)?;
|
||||
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential. Apply
|
||||
// the passed extractor function to it.
|
||||
let ref_cred: &mut CREDENTIALW = unsafe { &mut *p_credential };
|
||||
let result = f(ref_cred);
|
||||
// Finally, we erase the secret and free the allocated credential.
|
||||
erase_secret(ref_cred);
|
||||
let p_credential = p_credential;
|
||||
// SAFETY: `CredReadW` succeeded, so p_credential points at an allocated credential.
|
||||
// Free the allocation.
|
||||
unsafe { CredFree(p_credential.cast()) }
|
||||
result
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
@ -332,10 +331,10 @@ impl WinCredential {
|
|||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn extract_credential(w_credential: &CREDENTIALW) -> Result<Self> {
|
||||
Ok(Self {
|
||||
username: unsafe { from_wstr(w_credential.UserName) },
|
||||
target_name: unsafe { from_wstr(w_credential.TargetName) },
|
||||
target_alias: unsafe { from_wstr(w_credential.TargetAlias) },
|
||||
comment: unsafe { from_wstr(w_credential.Comment) },
|
||||
username: unsafe { from_wstr(w_credential.UserName.as_ptr()) },
|
||||
target_name: unsafe { from_wstr(w_credential.TargetName.as_ptr()) },
|
||||
target_alias: unsafe { from_wstr(w_credential.TargetAlias.as_ptr()) },
|
||||
comment: unsafe { from_wstr(w_credential.Comment.as_ptr()) },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -475,34 +474,40 @@ unsafe fn from_wstr(ws: *const u16) -> String {
|
|||
|
||||
/// Windows error codes are `DWORDS` which are 32-bit unsigned ints.
|
||||
#[derive(Debug)]
|
||||
pub struct Error(pub u32);
|
||||
pub struct Error(windows::core::Error);
|
||||
|
||||
impl Error {
|
||||
/// Create a Windows error from a Win32 error code.
|
||||
pub fn from_win32_code(code: u32) -> Self {
|
||||
Self(code)
|
||||
impl From<WIN32_ERROR> for Error {
|
||||
fn from(error: WIN32_ERROR) -> Self {
|
||||
Self(windows::core::Error::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for ErrorCode {
|
||||
fn from(err: Error) -> Self {
|
||||
match err.0 {
|
||||
ERROR_NOT_FOUND => Self::NoEntry,
|
||||
ERROR_NO_SUCH_LOGON_SESSION => Self::NoStorageAccess(Box::new(err)),
|
||||
_ => Self::PlatformFailure(Box::new(err)),
|
||||
if err.0 == ERROR_NOT_FOUND.into() {
|
||||
Self::NoEntry
|
||||
} else if err.0 == ERROR_NO_SUCH_LOGON_SESSION.into() {
|
||||
Self::NoStorageAccess(Box::new(err))
|
||||
} else {
|
||||
Self::PlatformFailure(Box::new(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self.0 {
|
||||
ERROR_NO_SUCH_LOGON_SESSION => write!(f, "Windows ERROR_NO_SUCH_LOGON_SESSION"),
|
||||
ERROR_NOT_FOUND => write!(f, "Windows ERROR_NOT_FOUND"),
|
||||
ERROR_BAD_USERNAME => write!(f, "Windows ERROR_BAD_USERNAME"),
|
||||
ERROR_INVALID_FLAGS => write!(f, "Windows ERROR_INVALID_FLAGS"),
|
||||
ERROR_INVALID_PARAMETER => write!(f, "Windows ERROR_INVALID_PARAMETER"),
|
||||
err => write!(f, "Windows error code {err}"),
|
||||
if self.0 == ERROR_NO_SUCH_LOGON_SESSION.into() {
|
||||
write!(f, "Windows ERROR_NO_SUCH_LOGON_SESSION")
|
||||
} else if self.0 == ERROR_NOT_FOUND.into() {
|
||||
write!(f, "Windows ERROR_NOT_FOUND")
|
||||
} else if self.0 == ERROR_BAD_USERNAME.into() {
|
||||
write!(f, "Windows ERROR_BAD_USERNAME")
|
||||
} else if self.0 == ERROR_INVALID_FLAGS.into() {
|
||||
write!(f, "Windows ERROR_INVALID_FLAGS")
|
||||
} else if self.0 == ERROR_INVALID_PARAMETER.into() {
|
||||
write!(f, "Windows ERROR_INVALID_PARAMETER")
|
||||
} else {
|
||||
write!(f, "Windows error code {}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -545,18 +550,18 @@ mod tests {
|
|||
let attribute_count = 0;
|
||||
let attributes: *mut CREDENTIAL_ATTRIBUTEW = std::ptr::null_mut();
|
||||
CREDENTIALW {
|
||||
Flags: 0,
|
||||
Flags: CRED_FLAGS(0),
|
||||
Type: CRED_TYPE_GENERIC,
|
||||
TargetName: std::ptr::null_mut(),
|
||||
Comment: std::ptr::null_mut(),
|
||||
TargetName: PWSTR::null(),
|
||||
Comment: PWSTR::null(),
|
||||
LastWritten: last_written,
|
||||
CredentialBlobSize: password.len() as u32,
|
||||
CredentialBlob: password.as_mut_ptr(),
|
||||
Persist: CRED_PERSIST_ENTERPRISE,
|
||||
AttributeCount: attribute_count,
|
||||
Attributes: attributes,
|
||||
TargetAlias: std::ptr::null_mut(),
|
||||
UserName: std::ptr::null_mut(),
|
||||
TargetAlias: PWSTR::null(),
|
||||
UserName: PWSTR::null(),
|
||||
}
|
||||
}
|
||||
// the first malformed sequence can't be UTF-16 because it has an odd number of bytes.
|
||||
|
|
|
|||
|
|
@ -70,8 +70,7 @@ once_cell = { workspace = true }
|
|||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows-registry = { workspace = true }
|
||||
windows-result = { workspace = true }
|
||||
windows-sys = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@ use std::{env, io, iter};
|
|||
use std::{path::Path, path::PathBuf, str::FromStr};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, instrument, trace};
|
||||
use uv_preview::Preview;
|
||||
use which::{which, which_all};
|
||||
|
||||
use uv_cache::Cache;
|
||||
use uv_fs::Simplified;
|
||||
use uv_fs::which::is_executable;
|
||||
|
|
@ -18,8 +15,10 @@ use uv_pep440::{
|
|||
LowerBound, Prerelease, UpperBound, Version, VersionSpecifier, VersionSpecifiers,
|
||||
release_specifiers_to_ranges,
|
||||
};
|
||||
use uv_preview::Preview;
|
||||
use uv_static::EnvVars;
|
||||
use uv_warnings::warn_user_once;
|
||||
use which::{which, which_all};
|
||||
|
||||
use crate::downloads::{PlatformRequest, PythonDownloadRequest};
|
||||
use crate::implementation::ImplementationName;
|
||||
|
|
@ -251,7 +250,7 @@ pub enum Error {
|
|||
|
||||
#[cfg(windows)]
|
||||
#[error("Failed to query installed Python versions from the Windows registry")]
|
||||
RegistryError(#[from] windows_result::Error),
|
||||
RegistryError(#[from] windows::core::Error),
|
||||
|
||||
/// An invalid version request was given
|
||||
#[error("Invalid version request: {0}")]
|
||||
|
|
@ -1502,13 +1501,15 @@ fn warn_on_unsupported_python(interpreter: &Interpreter) {
|
|||
pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
|
||||
use std::os::windows::fs::MetadataExt;
|
||||
use std::os::windows::prelude::OsStrExt;
|
||||
use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE};
|
||||
use windows_sys::Win32::Storage::FileSystem::{
|
||||
use windows::Win32::Foundation::CloseHandle;
|
||||
use windows::Win32::Storage::FileSystem::{
|
||||
CreateFileW, FILE_ATTRIBUTE_REPARSE_POINT, FILE_FLAG_BACKUP_SEMANTICS,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, MAXIMUM_REPARSE_DATA_BUFFER_SIZE, OPEN_EXISTING,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_MODE, MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
|
||||
OPEN_EXISTING,
|
||||
};
|
||||
use windows_sys::Win32::System::IO::DeviceIoControl;
|
||||
use windows_sys::Win32::System::Ioctl::FSCTL_GET_REPARSE_POINT;
|
||||
use windows::Win32::System::IO::DeviceIoControl;
|
||||
use windows::Win32::System::Ioctl::FSCTL_GET_REPARSE_POINT;
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
// The path must be absolute.
|
||||
if !path.is_absolute() {
|
||||
|
|
@ -1553,7 +1554,7 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
|
|||
let Ok(md) = fs_err::symlink_metadata(path) else {
|
||||
return false;
|
||||
};
|
||||
if md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
if md.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT.0 == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1567,19 +1568,19 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
|
|||
#[allow(unsafe_code)]
|
||||
let reparse_handle = unsafe {
|
||||
CreateFileW(
|
||||
path_encoded.as_mut_ptr(),
|
||||
PCWSTR(path_encoded.as_mut_ptr()),
|
||||
0,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
FILE_SHARE_MODE(0),
|
||||
None,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
std::ptr::null_mut(),
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
if reparse_handle == INVALID_HANDLE_VALUE {
|
||||
let Ok(reparse_handle) = reparse_handle else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = [0u16; MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize];
|
||||
let mut bytes_returned = 0;
|
||||
|
|
@ -1590,19 +1591,20 @@ pub(crate) fn is_windows_store_shim(path: &Path) -> bool {
|
|||
DeviceIoControl(
|
||||
reparse_handle,
|
||||
FSCTL_GET_REPARSE_POINT,
|
||||
std::ptr::null_mut(),
|
||||
None,
|
||||
0,
|
||||
buf.as_mut_ptr().cast(),
|
||||
Some(buf.as_mut_ptr().cast()),
|
||||
buf.len() as u32 * 2,
|
||||
&raw mut bytes_returned,
|
||||
std::ptr::null_mut(),
|
||||
) != 0
|
||||
Some(&raw mut bytes_returned),
|
||||
None,
|
||||
)
|
||||
.is_ok()
|
||||
};
|
||||
|
||||
// SAFETY: The handle is valid.
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
CloseHandle(reparse_handle);
|
||||
let _ = CloseHandle(reparse_handle);
|
||||
}
|
||||
|
||||
// If the operation failed, assume it's not a reparse point.
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use windows_sys::Win32::Foundation::{APPMODEL_ERROR_NO_PACKAGE, ERROR_CANT_ACCESS_FILE};
|
||||
use windows::Win32::Foundation::{APPMODEL_ERROR_NO_PACKAGE, ERROR_CANT_ACCESS_FILE, WIN32_ERROR};
|
||||
|
||||
/// A Python executable and its associated platform markers.
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -928,8 +928,10 @@ impl InterpreterInfo {
|
|||
_ => {}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
if let Some(APPMODEL_ERROR_NO_PACKAGE | ERROR_CANT_ACCESS_FILE) =
|
||||
err.raw_os_error().and_then(|code| u32::try_from(code).ok())
|
||||
if let Some(APPMODEL_ERROR_NO_PACKAGE | ERROR_CANT_ACCESS_FILE) = err
|
||||
.raw_os_error()
|
||||
.and_then(|code| u32::try_from(code).ok())
|
||||
.map(WIN32_ERROR)
|
||||
{
|
||||
// These error codes are returned if the Python interpreter is a corrupt MSIX
|
||||
// package, which we want to differentiate from a typical spawn failure.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use thiserror::Error;
|
|||
use tracing::{debug, warn};
|
||||
use uv_preview::{Preview, PreviewFeatures};
|
||||
#[cfg(windows)]
|
||||
use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
use windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
|
||||
use uv_fs::{LockedFile, Simplified, replace_symlink, symlink_or_copy_file};
|
||||
use uv_platform::{Error as PlatformError, Os};
|
||||
|
|
@ -857,7 +857,7 @@ impl PythonMinorVersionLink {
|
|||
.is_ok_and(|metadata| {
|
||||
// Check that this is a reparse point, which indicates this
|
||||
// is a symlink or junction.
|
||||
(metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT) != 0
|
||||
(metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT.0) != 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,13 +12,10 @@ use thiserror::Error;
|
|||
use tracing::debug;
|
||||
use uv_platform::Arch;
|
||||
use uv_warnings::{warn_user, warn_user_once};
|
||||
use windows::Win32::Foundation::ERROR_FILE_NOT_FOUND;
|
||||
use windows::Win32::System::Registry::{KEY_WOW64_32KEY, KEY_WOW64_64KEY};
|
||||
use windows::core::HRESULT;
|
||||
use windows_registry::{CURRENT_USER, HSTRING, Key, LOCAL_MACHINE, Value};
|
||||
use windows_result::HRESULT;
|
||||
use windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND;
|
||||
use windows_sys::Win32::System::Registry::{KEY_WOW64_32KEY, KEY_WOW64_64KEY};
|
||||
|
||||
/// Code returned when the registry key doesn't exist.
|
||||
const ERROR_NOT_FOUND: HRESULT = HRESULT::from_win32(ERROR_FILE_NOT_FOUND);
|
||||
|
||||
/// A Python interpreter found in the Windows registry through PEP 514 or from a known Microsoft
|
||||
/// Store path.
|
||||
|
|
@ -32,7 +29,7 @@ pub(crate) struct WindowsPython {
|
|||
}
|
||||
|
||||
/// Find all Pythons registered in the Windows registry following PEP 514.
|
||||
pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows_result::Error> {
|
||||
pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows::core::Error> {
|
||||
let mut registry_pythons = Vec::new();
|
||||
// Prefer `HKEY_CURRENT_USER` over `HKEY_LOCAL_MACHINE`.
|
||||
// By default, a 64-bit program does not see a 32-bit global (HKLM) installation of Python in
|
||||
|
|
@ -47,7 +44,7 @@ pub(crate) fn registry_pythons() -> Result<Vec<WindowsPython>, windows_result::E
|
|||
let mut open_options = root_key.options();
|
||||
open_options.read();
|
||||
if let Some(access_modifier) = access_modifier {
|
||||
open_options.access(access_modifier);
|
||||
open_options.access(access_modifier.0);
|
||||
}
|
||||
let Ok(key_python) = open_options.open(r"Software\Python") else {
|
||||
continue;
|
||||
|
|
@ -131,7 +128,7 @@ pub enum ManagedPep514Error {
|
|||
#[error("Windows has an unknown pointer width for arch: `{_0}`")]
|
||||
InvalidPointerSize(Arch),
|
||||
#[error("Failed to write registry entry: {0}")]
|
||||
WriteError(#[from] windows_result::Error),
|
||||
WriteError(#[from] windows::core::Error),
|
||||
}
|
||||
|
||||
/// Register a managed Python installation in the Windows registry following PEP 514.
|
||||
|
|
@ -216,7 +213,7 @@ pub fn remove_registry_entry<'a>(
|
|||
if all {
|
||||
debug!("Removing registry key HKCU:\\{}", astral_key);
|
||||
if let Err(err) = CURRENT_USER.remove_tree(&astral_key) {
|
||||
if err.code() == ERROR_NOT_FOUND {
|
||||
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
|
||||
debug!("No registry entries to remove, no registry key {astral_key}");
|
||||
} else {
|
||||
warn_user!("Failed to clear registry entries under {astral_key}: {err}");
|
||||
|
|
@ -230,7 +227,7 @@ pub fn remove_registry_entry<'a>(
|
|||
let python_entry = format!("{astral_key}\\{python_tag}");
|
||||
debug!("Removing registry key HKCU:\\{}", python_entry);
|
||||
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
|
||||
if err.code() == ERROR_NOT_FOUND {
|
||||
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
|
||||
debug!(
|
||||
"No registry entries to remove for {}, no registry key {}",
|
||||
installation.key(),
|
||||
|
|
@ -256,7 +253,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
|
|||
let astral_key = format!("Software\\Python\\{COMPANY_KEY}");
|
||||
let key = match CURRENT_USER.open(&astral_key) {
|
||||
Ok(subkeys) => subkeys,
|
||||
Err(err) if err.code() == ERROR_NOT_FOUND => {
|
||||
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => {
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
@ -268,7 +265,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
|
|||
// Separate assignment since `keys()` creates a borrow.
|
||||
let subkeys = match key.keys() {
|
||||
Ok(subkeys) => subkeys,
|
||||
Err(err) if err.code() == ERROR_NOT_FOUND => {
|
||||
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => {
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
@ -284,7 +281,7 @@ pub fn remove_orphan_registry_entries(installations: &[ManagedPythonInstallation
|
|||
let python_entry = format!("{astral_key}\\{subkey}");
|
||||
debug!("Removing orphan registry key HKCU:\\{}", python_entry);
|
||||
if let Err(err) = CURRENT_USER.remove_tree(&python_entry) {
|
||||
if err.code() == ERROR_NOT_FOUND {
|
||||
if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) {
|
||||
continue;
|
||||
}
|
||||
// TODO(konsti): We don't have an installation key here.
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@ tracing = { workspace = true }
|
|||
nix = { workspace = true }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { workspace = true }
|
||||
windows-registry = { workspace = true }
|
||||
windows-result = { workspace = true }
|
||||
windows-sys = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
fs-err = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ use std::path::Path;
|
|||
|
||||
use anyhow::Context;
|
||||
use tracing::warn;
|
||||
use windows::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA};
|
||||
use windows::core::HRESULT;
|
||||
use windows_registry::{CURRENT_USER, HSTRING};
|
||||
use windows_result::HRESULT;
|
||||
use windows_sys::Win32::Foundation::{ERROR_FILE_NOT_FOUND, ERROR_INVALID_DATA};
|
||||
|
||||
use uv_static::EnvVars;
|
||||
|
||||
|
|
@ -60,13 +60,11 @@ fn get_windows_path_var() -> anyhow::Result<Option<HSTRING>> {
|
|||
let reg_value = environment.get_hstring(EnvVars::PATH);
|
||||
match reg_value {
|
||||
Ok(reg_value) => Ok(Some(reg_value)),
|
||||
Err(err) if err.code() == HRESULT::from_win32(ERROR_INVALID_DATA) => {
|
||||
Err(err) if err.code() == HRESULT::from(ERROR_INVALID_DATA) => {
|
||||
warn!("`HKEY_CURRENT_USER\\Environment\\PATH` is a non-string");
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) if err.code() == HRESULT::from_win32(ERROR_FILE_NOT_FOUND) => {
|
||||
Ok(Some(HSTRING::new()))
|
||||
}
|
||||
Err(err) if err.code() == HRESULT::from(ERROR_FILE_NOT_FOUND) => Ok(Some(HSTRING::new())),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ zip = { workspace = true }
|
|||
arrayvec = { workspace = true }
|
||||
self-replace = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
windows-result = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -54,11 +54,11 @@ enum ExceptionSafeStderr {
|
|||
}
|
||||
|
||||
impl ExceptionSafeStderr {
|
||||
fn new() -> Result<Self, windows_result::Error> {
|
||||
fn new() -> Result<Self, windows::core::Error> {
|
||||
// SAFETY: winapi call, no interesting parameters
|
||||
let handle = unsafe { GetStdHandle(STD_ERROR_HANDLE) }?;
|
||||
if handle.is_invalid() {
|
||||
return Err(windows_result::Error::empty());
|
||||
return Err(windows::core::Error::empty());
|
||||
}
|
||||
let mut mode = CONSOLE_MODE::default();
|
||||
// SAFETY: winapi calls, no interesting parameters
|
||||
|
|
@ -73,7 +73,7 @@ impl ExceptionSafeStderr {
|
|||
}
|
||||
}
|
||||
|
||||
fn write_winerror(&mut self, s: &str) -> Result<(), windows_result::Error> {
|
||||
fn write_winerror(&mut self, s: &str) -> Result<(), windows::core::Error> {
|
||||
match self {
|
||||
Self::WriteConsole(handle) => {
|
||||
// According to comments in the ReactOS source, NT's behavior is that writes of 80
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue