diff --git a/Cargo.lock b/Cargo.lock index 1372d84d53ef..ca1240e694ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5271,8 +5271,10 @@ name = "windows-installer" version = "0.0.0" dependencies = [ "anyhow", + "mullvad-version", "tempfile", "windows-sys 0.52.0", + "winres", ] [[package]] diff --git a/windows-installer/Cargo.toml b/windows-installer/Cargo.toml index 3e827e489bfd..85c4193ff103 100644 --- a/windows-installer/Cargo.toml +++ b/windows-installer/Cargo.toml @@ -15,7 +15,7 @@ windows-sys = { version = "0.52.0", features = ["Win32_System", "Win32_System_Li tempfile = "3.10" anyhow = "1.0" -[target.build-dependencies.'cfg(target_os = "windows")'.dependencies] +[target.'cfg(target_os = "windows")'.build-dependencies] winres = "0.1" anyhow = "1.0" windows-sys = { version = "0.52.0", features = ["Win32_System", "Win32_System_LibraryLoader", "Win32_System_SystemServices"] } diff --git a/windows-installer/src/main.rs b/windows-installer/src/main.rs index 90c646c96c05..a2df75caed38 100644 --- a/windows-installer/src/main.rs +++ b/windows-installer/src/main.rs @@ -11,6 +11,7 @@ use std::{ ffi::{c_ushort, OsStr}, io::{self, Write}, process::{Command, ExitStatus}, + ptr::NonNull, }; use tempfile::TempPath; use windows_sys::{ @@ -23,19 +24,14 @@ use windows_sys::{ }; mod resource { + // Import resource constants from `resource.rs`. This is automatically generated by the build + // script. include!(concat!(env!("OUT_DIR"), "/resource.rs")); } fn main() -> anyhow::Result<()> { - let resource_id = match get_native_arch()? { - Architecture::X64 => ResourceId::X86Bin, - Architecture::Arm64 => ResourceId::Arm64Bin, - Architecture::Unsupported(arch) => { - bail!("unsupported processor architecture {arch}"); - } - }; - - let exe_data = find_binary_data(resource_id)?; + let architecture = get_native_arch()?; + let exe_data = find_binary_data(architecture)?; let path = write_file_to_temp(&exe_data)?; let status = run_with_forwarded_args(&path).context("Failed to run unpacked installer")?; @@ -64,19 +60,18 @@ fn write_file_to_temp(data: &[u8]) -> anyhow::Result { Ok(file.into_temp_path()) } -#[repr(usize)] -enum ResourceId { - X86Bin = resource::IDB_X64EXE, - Arm64Bin = resource::IDB_ARM64EXE, -} - /// Return a slice of data for the given resource -fn find_binary_data(resource_id: ResourceId) -> anyhow::Result<&'static [u8]> { +fn find_binary_data(architecture: Architecture) -> anyhow::Result<&'static [u8]> { + let resource_id = match architecture { + Architecture::X64 => resource::IDB_X64EXE, + Architecture::Arm64 => resource::IDB_ARM64EXE, + }; + // SAFETY: Looks unsafe but is actually safe. The cast is equivalent to `MAKEINTRESOURCE`, // which is not available in windows-sys, as it is a macro. // `resource_id` is guaranteed by the build script to refer to an actual resource. // See https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-findresourcew - let resource_info = unsafe { FindResourceW(0, resource_id as usize as _, w!("BINARY")) }; + let resource_info = unsafe { FindResourceW(0, resource_id as _, w!("BINARY")) }; if resource_info == 0 { bail!("Failed to find resource: {}", io::Error::last_os_error()); } @@ -84,10 +79,9 @@ fn find_binary_data(resource_id: ResourceId) -> anyhow::Result<&'static [u8]> { // SAFETY: We have a valid resource info handle // NOTE: Resources loaded with LoadResource should not be freed. // See https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadresource - let resource = unsafe { LoadResource(0, resource_info) }; - if resource.is_null() { + let Some(resource) = NonNull::new(unsafe { LoadResource(0, resource_info) }) else { bail!("Failed to load resource: {}", io::Error::last_os_error()); - } + }; // SAFETY: We have a valid resource info handle let resource_size = unsafe { SizeofResource(0, resource_info) }; @@ -101,20 +95,19 @@ fn find_binary_data(resource_id: ResourceId) -> anyhow::Result<&'static [u8]> { // SAFETY: We have a valid resource info handle // NOTE: We do not need to unload this handle, because it doesn't actually lock anything. // See https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-lockresource - let resource_data = unsafe { LockResource(resource as _) }; - if resource_data.is_null() { + let Some(resource_data) = NonNull::new(unsafe { LockResource(resource.as_ptr()) }) else { bail!( "Failed to get resource data: {}", io::Error::last_os_error() ); - } + }; debug_assert!(resource_data.is_aligned()); // SAFETY: The pointer is non-null, valid and constant for the remainder of the process lifetime let resource_slice = unsafe { std::slice::from_raw_parts( - resource_data as *const u8, + resource_data.as_ptr() as *const u8, usize::try_from(resource_size).unwrap(), ) }; @@ -126,11 +119,10 @@ fn find_binary_data(resource_id: ResourceId) -> anyhow::Result<&'static [u8]> { enum Architecture { X64, Arm64, - Unsupported(u16), } /// Return native architecture (ignoring WOW64) -fn get_native_arch() -> io::Result { +fn get_native_arch() -> anyhow::Result { let mut running_arch: c_ushort = 0; let mut native_arch: c_ushort = 0; @@ -138,12 +130,15 @@ fn get_native_arch() -> io::Result { // undocumented but refers to the current process. let result = unsafe { IsWow64Process2(0, &mut running_arch, &mut native_arch) }; if result == 0 { - return Err(io::Error::last_os_error()); + bail!( + "Failed to get native architecture: {}", + io::Error::last_os_error() + ); } match native_arch { IMAGE_FILE_MACHINE_AMD64 => Ok(Architecture::X64), IMAGE_FILE_MACHINE_ARM64 => Ok(Architecture::Arm64), - other => Ok(Architecture::Unsupported(other)), + other => bail!("unsupported architecture: {other}"), } }