diff --git a/rust/Cargo.Bazel.lock b/rust/Cargo.Bazel.lock index b8778dd03b8f1..c4c994fad5606 100644 --- a/rust/Cargo.Bazel.lock +++ b/rust/Cargo.Bazel.lock @@ -1,5 +1,5 @@ { - "checksum": "d2d968dacd2ceab962c18fa28697dc189f31b3991a3554ff21e167836627a3b8", + "checksum": "94dc89c8790518c956fd52cd06689af1557f6c755c7bd1a1414719ef73ac468c", "crates": { "addr2line 0.21.0": { "name": "addr2line", @@ -13478,6 +13478,10 @@ "id": "which 7.0.2", "target": "which" }, + { + "id": "winapi 0.3.9", + "target": "winapi" + }, { "id": "xz2 0.1.7", "target": "xz2" @@ -18838,14 +18842,36 @@ ], "crate_features": { "common": [ - "fileapi", - "handleapi", - "processthreadsapi", - "std", - "winbase", - "winerror" + "sysinfoapi", + "winnt", + "winver" ], - "selects": {} + "selects": { + "aarch64-pc-windows-msvc": [ + "fileapi", + "handleapi", + "processthreadsapi", + "std", + "winbase", + "winerror" + ], + "i686-pc-windows-msvc": [ + "fileapi", + "handleapi", + "processthreadsapi", + "std", + "winbase", + "winerror" + ], + "x86_64-pc-windows-msvc": [ + "fileapi", + "handleapi", + "processthreadsapi", + "std", + "winbase", + "winerror" + ] + } }, "deps": { "common": [ @@ -21972,6 +21998,7 @@ "toml 0.8.20", "walkdir 2.5.0", "which 7.0.2", + "winapi 0.3.9", "xz2 0.1.7", "zip 2.2.3" ], diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c616fc119dbed..96f9a5ef26d2f 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1866,6 +1866,7 @@ dependencies = [ "toml", "walkdir", "which", + "winapi", "xz2", "zip", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index e04305650c819..2ca5cf46081d6 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -38,6 +38,7 @@ apple-flat-package = "0.20.0" which = "7.0.2" fs2 = "0.4.3" fs_extra = "1.3.0" +winapi = { version = "0.3.9", features = ["winver", "winnt", "sysinfoapi"] } [dev-dependencies] assert_cmd = "2.0.16" diff --git a/rust/src/config.rs b/rust/src/config.rs index b92b3fac2fe89..74dbb0abbf2f8 100644 --- a/rust/src/config.rs +++ b/rust/src/config.rs @@ -21,7 +21,7 @@ use crate::{ default_cache_folder, format_one_arg, path_to_string, Command, ENV_PROCESSOR_ARCHITECTURE, REQUEST_TIMEOUT_SEC, UNAME_COMMAND, }; -use crate::{ARCH_AMD64, ARCH_ARM64, ARCH_X86, TTL_SEC, WMIC_COMMAND_OS}; +use crate::{ARCH_AMD64, ARCH_ARM64, ARCH_X86, TTL_SEC}; use anyhow::anyhow; use anyhow::Error; use std::cell::RefCell; @@ -30,6 +30,13 @@ use std::env::consts::OS; use std::fs::read_to_string; use std::path::Path; use toml::Table; +#[cfg(windows)] +use winapi::um::sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}; +#[cfg(windows)] +use winapi::um::winnt::{ + PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_ARM, PROCESSOR_ARCHITECTURE_ARM64, + PROCESSOR_ARCHITECTURE_IA64, PROCESSOR_ARCHITECTURE_INTEL, +}; thread_local!(static CACHE_PATH: RefCell = RefCell::new(path_to_string(&default_cache_folder()))); @@ -69,14 +76,16 @@ impl ManagerConfig { let self_os = OS; let self_arch = if WINDOWS.is(self_os) { - let mut architecture = env::var(ENV_PROCESSOR_ARCHITECTURE).unwrap_or_default(); - if architecture.is_empty() { - let get_os_command = Command::new_single(WMIC_COMMAND_OS.to_string()); - architecture = run_shell_command_by_os(self_os, get_os_command).unwrap_or_default(); + let mut _architecture = env::var(ENV_PROCESSOR_ARCHITECTURE).unwrap_or_default(); + #[cfg(windows)] + { + if _architecture.is_empty() { + _architecture = get_win_os_architecture(); + } } - if architecture.contains("32") { + if _architecture.contains("32") { ARCH_X86.to_string() - } else if architecture.contains("ARM") { + } else if _architecture.contains("ARM") { ARCH_ARM64.to_string() } else { ARCH_AMD64.to_string() @@ -297,3 +306,21 @@ fn read_cache_path() -> String { }); cache_path } + +#[cfg(windows)] +fn get_win_os_architecture() -> String { + unsafe { + let mut system_info: SYSTEM_INFO = std::mem::zeroed(); + GetNativeSystemInfo(&mut system_info); + + match system_info.u.s() { + si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 => "64-bit", + si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL => "32-bit", + si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM => "ARM", + si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64 => "ARM64", + si if si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 => "Itanium-based", + _ => "Unknown", + } + .to_string() + } +} diff --git a/rust/src/files.rs b/rust/src/files.rs index 8dc208a3cfb85..5f3679bc96575 100644 --- a/rust/src/files.rs +++ b/rust/src/files.rs @@ -29,13 +29,23 @@ use directories::BaseDirs; use flate2::read::GzDecoder; use fs_extra::dir::{move_dir, CopyOptions}; use regex::Regex; +#[cfg(windows)] +use std::ffi::OsStr; use std::fs; use std::fs::File; use std::io; use std::io::{BufReader, Cursor, Read}; +#[cfg(windows)] +use std::os::windows::ffi::OsStrExt; use std::path::{Path, PathBuf}; +#[cfg(windows)] +use std::ptr; use tar::Archive; use walkdir::{DirEntry, WalkDir}; +#[cfg(windows)] +use winapi::shared::minwindef::LPVOID; +#[cfg(windows)] +use winapi::um::winver::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW}; use xz2::read::XzDecoder; use zip::ZipArchive; @@ -594,3 +604,86 @@ pub fn capitalize(s: &str) -> String { Some(first) => first.to_uppercase().collect::() + chars.as_str(), } } + +#[cfg(not(windows))] +pub fn get_win_file_version(_file_path: &str) -> Option { + None +} + +#[cfg(windows)] +pub fn get_win_file_version(file_path: &str) -> Option { + unsafe { + let wide_path: Vec = OsStr::new(file_path).encode_wide().chain(Some(0)).collect(); + + let mut dummy = 0; + let size = GetFileVersionInfoSizeW(wide_path.as_ptr(), &mut dummy); + if size == 0 { + return None; + } + + let mut buffer: Vec = Vec::with_capacity(size as usize); + if GetFileVersionInfoW(wide_path.as_ptr(), 0, size, buffer.as_mut_ptr() as LPVOID) == 0 { + return None; + } + buffer.set_len(size as usize); + + let mut lang_and_codepage_ptr: LPVOID = ptr::null_mut(); + let mut lang_and_codepage_len: u32 = 0; + + if VerQueryValueW( + buffer.as_ptr() as LPVOID, + OsStr::new("\\VarFileInfo\\Translation") + .encode_wide() + .chain(Some(0)) + .collect::>() + .as_ptr(), + &mut lang_and_codepage_ptr, + &mut lang_and_codepage_len, + ) == 0 + { + return None; + } + + if lang_and_codepage_len == 0 { + return None; + } + + let lang_and_codepage_slice = std::slice::from_raw_parts( + lang_and_codepage_ptr as *const u16, + lang_and_codepage_len as usize / 2, + ); + let lang = lang_and_codepage_slice[0]; + let codepage = lang_and_codepage_slice[1]; + + let query = format!( + "\\StringFileInfo\\{:04x}{:04x}\\ProductVersion", + lang, codepage + ); + let query_wide: Vec = OsStr::new(&query).encode_wide().chain(Some(0)).collect(); + + let mut product_version_ptr: LPVOID = ptr::null_mut(); + let mut product_version_len: u32 = 0; + + if VerQueryValueW( + buffer.as_ptr() as LPVOID, + query_wide.as_ptr(), + &mut product_version_ptr, + &mut product_version_len, + ) == 0 + { + return None; + } + + if product_version_ptr.is_null() { + return None; + } + + let product_version_slice = std::slice::from_raw_parts( + product_version_ptr as *const u16, + product_version_len as usize, + ); + let product_version = String::from_utf16_lossy(product_version_slice); + + Some(product_version.trim_end_matches('\0').to_string()) + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 851ba31dd1da2..9f367a5528d98 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -20,6 +20,7 @@ use crate::config::OS::{MACOS, WINDOWS}; use crate::config::{str_to_os, ManagerConfig}; use crate::downloads::download_to_tmp_folder; use crate::edge::{EdgeManager, EDGEDRIVER_NAME, EDGE_NAMES, WEBVIEW2_NAME}; +use crate::files::get_win_file_version; use crate::files::{ capitalize, collect_files_from_cache, create_path_if_not_exists, default_cache_folder, find_latest_from_cache, get_binary_extension, path_to_string, @@ -75,8 +76,6 @@ pub const DEV: &str = "dev"; pub const CANARY: &str = "canary"; pub const NIGHTLY: &str = "nightly"; pub const ESR: &str = "esr"; -pub const WMIC_COMMAND: &str = "wmic datafile where name='{}' get Version /value"; -pub const WMIC_COMMAND_OS: &str = "wmic os get osarchitecture"; pub const REG_VERSION_ARG: &str = "version"; pub const REG_CURRENT_VERSION_ARG: &str = "CurrentVersion"; pub const REG_PV_ARG: &str = "pv"; @@ -453,8 +452,9 @@ pub trait SeleniumManager { driver_version_command, ) { Ok(out) => out, - Err(_e) => continue, + Err(_) => continue, }; + let full_browser_version = parse_version(output, self.get_logger()).unwrap_or_default(); if full_browser_version.is_empty() { continue; @@ -1158,9 +1158,7 @@ pub trait SeleniumManager { let mut commands = Vec::new(); if WINDOWS.is(self.get_os()) { if !escaped_browser_path.is_empty() { - let wmic_command = - Command::new_single(format_one_arg(WMIC_COMMAND, &escaped_browser_path)); - commands.push(wmic_command); + return Ok(get_win_file_version(&escaped_browser_path)); } if !self.is_browser_version_unstable() { let reg_command =