diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..80edc67 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,4 @@ +hard_tabs = true + +# Requires nightly +# imports_granularity = "Module" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 0601c70..7d256cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,7 +116,7 @@ dependencies = [ "slog", "slog-async", "slog-term", - "winapi", + "windows-sys", ] [[package]] @@ -329,3 +329,60 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" diff --git a/Cargo.toml b/Cargo.toml index decae65..d6ef543 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,19 +1,31 @@ -[package] -name = "inno_updater" -version = "0.9.0" -authors = ["Microsoft "] -build = "build.rs" - -[dependencies] -byteorder = "1" -crc = "^1.0.0" -slog = "2.1.1" -slog-async = "2.2.0" -slog-term = "2.3.0" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "^0.3.9", features = ["winuser", "libloaderapi", "commctrl", "processthreadsapi", "tlhelp32", "handleapi", "psapi", "errhandlingapi", "winbase", "shellapi"] } - -[profile.release] -lto = true -panic = 'abort' +[package] +name = "inno_updater" +version = "0.9.0" +authors = ["Microsoft "] +build = "build.rs" + +[dependencies] +byteorder = "1" +crc = "^1.0.0" +slog = "2.1.1" +slog-async = "2.2.0" +slog-term = "2.3.0" + +[target.'cfg(windows)'.dependencies.windows-sys] +version = "0.42" +features = [ + "Win32_Foundation", + "Win32_System_Shutdown", + "Win32_UI_WindowsAndMessaging", + "Win32_System_Threading", + "Win32_System_LibraryLoader", + "Win32_System_Diagnostics_Debug", + "Win32_Storage_FileSystem", + "Win32_Security", + "Win32_System_ProcessStatus", + "Win32_System_Diagnostics_ToolHelp" +] + +[profile.release] +lto = true +panic = 'abort' diff --git a/build.rs b/build.rs index d932aa8..baeb842 100644 --- a/build.rs +++ b/build.rs @@ -18,6 +18,12 @@ fn main() { assert!(ecode.success(), "Resource compiler failed"); - println!("cargo:rustc-link-search=native={}", resources.parent().unwrap().to_str().unwrap()); - println!("cargo:rustc-link-lib={}", resources.file_stem().unwrap().to_str().unwrap()); + println!( + "cargo:rustc-link-search=native={}", + resources.parent().unwrap().to_str().unwrap() + ); + println!( + "cargo:rustc-link-lib={}", + resources.file_stem().unwrap().to_str().unwrap() + ); } diff --git a/src/blockio.rs b/src/blockio.rs index 741ab7e..61ac868 100644 --- a/src/blockio.rs +++ b/src/blockio.rs @@ -5,9 +5,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use crc::{crc32, Hasher32}; -use std::cmp; -use std::io; use std::io::prelude::*; +use std::{cmp, io}; const BLOCK_MAX_SIZE: usize = 4096; @@ -48,8 +47,8 @@ impl<'a> BlockRead<'a> { } let size = size as usize; - let mut buffer = &mut self.buffer[..size]; - self.reader.read_exact(&mut buffer)?; + let buffer = &mut self.buffer[..size]; + self.reader.read_exact(buffer)?; let mut digest = crc32::Digest::new(crc32::IEEE); digest.write(buffer); diff --git a/src/gui.rs b/src/gui.rs index 364418d..f7a79a9 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -6,13 +6,11 @@ use std::sync::mpsc::Sender; use std::{mem, ptr}; use strings::to_utf16; -use winapi::shared::basetsd::INT_PTR; -use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, UINT, WPARAM}; -use winapi::shared::ntdef::LPCWSTR; -use winapi::shared::windef::HWND; +use windows_sys::core::PCWSTR; +use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM, WPARAM}; extern "system" { - pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: LPCWSTR) -> BOOL; + pub fn ShutdownBlockReasonCreate(hWnd: HWND, pwszReason: PCWSTR) -> BOOL; pub fn ShutdownBlockReasonDestroy(hWnd: HWND) -> BOOL; } @@ -21,23 +19,27 @@ struct DialogData { tx: Sender, } -unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) -> INT_PTR { +unsafe extern "system" fn dlgproc(hwnd: HWND, msg: u32, _: WPARAM, l: LPARAM) -> isize { use resources; - use winapi::shared::windef::RECT; - use winapi::um::commctrl::PBM_SETMARQUEE; - use winapi::um::processthreadsapi::GetCurrentThreadId; - use winapi::um::winuser::{ - GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow, HWND_TOPMOST, - SW_HIDE, WM_DESTROY, WM_INITDIALOG, + use windows_sys::Win32::Foundation::RECT; + use windows_sys::Win32::System::Threading::GetCurrentThreadId; + use windows_sys::Win32::UI::WindowsAndMessaging::{ + GetDesktopWindow, GetWindowRect, SendDlgItemMessageW, SetWindowPos, ShowWindow, + HWND_TOPMOST, SW_HIDE, WM_DESTROY, WM_INITDIALOG, WM_USER, }; match msg { WM_INITDIALOG => { let data = &*(l as *const DialogData); if !data.silent { - SendDlgItemMessageW(hwnd, resources::PROGRESS_SLIDER, PBM_SETMARQUEE, 1, 0); + SendDlgItemMessageW(hwnd, resources::PROGRESS_SLIDER, WM_USER + 10, 1, 0); - let mut rect = mem::MaybeUninit::::uninit().assume_init(); + let mut rect = RECT { + top: 0, + left: 0, + bottom: 0, + right: 0, + }; GetWindowRect(hwnd, &mut rect); let width = rect.right - rect.left; @@ -58,8 +60,7 @@ unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) - ShowWindow(hwnd, SW_HIDE); } - data - .tx + data.tx .send(ProgressWindow { ui_thread_id: GetCurrentThreadId(), }) @@ -77,14 +78,14 @@ unsafe extern "system" fn dlgproc(hwnd: HWND, msg: UINT, _: WPARAM, l: LPARAM) - } pub struct ProgressWindow { - ui_thread_id: DWORD, + ui_thread_id: u32, } unsafe impl Send for ProgressWindow {} impl ProgressWindow { pub fn exit(&self) { - use winapi::um::winuser::{PostThreadMessageW, WM_QUIT}; + use windows_sys::Win32::UI::WindowsAndMessaging::{PostThreadMessageW, WM_QUIT}; unsafe { PostThreadMessageW(self.ui_thread_id, WM_QUIT, 0, 0); @@ -94,16 +95,16 @@ impl ProgressWindow { pub fn run_progress_window(silent: bool, tx: Sender) { use resources; - use winapi::um::libloaderapi::GetModuleHandleW; - use winapi::um::winuser::{DialogBoxParamW, MAKEINTRESOURCEW}; + use windows_sys::Win32::System::LibraryLoader::GetModuleHandleW; + use windows_sys::Win32::UI::WindowsAndMessaging::DialogBoxParamW; let data = DialogData { silent, tx }; unsafe { DialogBoxParamW( GetModuleHandleW(ptr::null_mut()), - MAKEINTRESOURCEW(resources::PROGRESS_DIALOG), - ptr::null_mut(), + resources::PROGRESS_DIALOG as PCWSTR, + mem::zeroed(), Some(dlgproc), (&data as *const DialogData) as LPARAM, ); @@ -130,16 +131,16 @@ pub enum MessageBoxResult { } pub fn message_box(text: &str, caption: &str, mbtype: MessageBoxType) -> MessageBoxResult { - use winapi::um::winuser::{ - MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, IDYES, - MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL, + use windows_sys::Win32::UI::WindowsAndMessaging::{ + MessageBoxW, IDABORT, IDCANCEL, IDCONTINUE, IDIGNORE, IDNO, IDOK, IDRETRY, IDTRYAGAIN, + IDYES, MB_ICONERROR, MB_RETRYCANCEL, MB_SYSTEMMODAL, }; let result: i32; unsafe { result = MessageBoxW( - ptr::null_mut(), + mem::zeroed(), to_utf16(text).as_ptr(), to_utf16(caption).as_ptr(), match mbtype { diff --git a/src/handle.rs b/src/handle.rs index 9835d96..88eac1d 100644 --- a/src/handle.rs +++ b/src/handle.rs @@ -3,96 +3,97 @@ * Licensed under the MIT License. See LICENSE in the project root for license information. *----------------------------------------------------------------------------------------*/ +use std::ffi::c_void; use std::path::Path; use std::{error, io, ptr}; use strings::to_u16s; use util; -use winapi::um::winnt::HANDLE; +use windows_sys::Win32::Foundation::HANDLE; pub struct FileHandle(HANDLE); impl FileHandle { - pub fn new(path: &Path) -> Result> { - use winapi::um::fileapi::CreateFileW; - use winapi::um::fileapi::OPEN_EXISTING; - use winapi::um::handleapi::INVALID_HANDLE_VALUE; - use winapi::um::winnt::DELETE; - use winapi::um::winnt::FILE_ATTRIBUTE_NORMAL; + pub fn new(path: &Path) -> Result> { + use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; + use windows_sys::Win32::Storage::FileSystem::{ + CreateFileW, DELETE, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING, + }; - unsafe { - let handle = CreateFileW( - to_u16s(path.as_os_str()).as_ptr(), - DELETE, - 0, - ptr::null_mut(), - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - ptr::null_mut(), - ); + unsafe { + let handle = CreateFileW( + to_u16s(path.as_os_str()).as_ptr(), + DELETE, + 0, + ptr::null_mut(), + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + std::mem::zeroed(), + ); - if handle == INVALID_HANDLE_VALUE { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Failed to create file handle: {}", - util::get_last_error_message()? - ), - ) - .into()); - } + if handle == INVALID_HANDLE_VALUE { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to create file handle: {}", + util::get_last_error_message()? + ), + ) + .into()); + } - Ok(FileHandle(handle)) - } - } + Ok(FileHandle(handle)) + } + } - pub fn mark_for_deletion(&self) -> Result<(), Box> { - use std::mem; - use winapi::shared::minwindef::{DWORD, FALSE, LPVOID, TRUE}; - use winapi::um::fileapi::SetFileInformationByHandle; - use winapi::um::fileapi::FILE_DISPOSITION_INFO; - use winapi::um::minwinbase::FileDispositionInfo; + pub fn mark_for_deletion(&self) -> Result<(), Box> { + use std::mem; + use windows_sys::Win32::Foundation::BOOLEAN; + use windows_sys::Win32::Storage::FileSystem::{ + FileDispositionInfo, SetFileInformationByHandle, FILE_DISPOSITION_INFO, + }; - unsafe { - let mut info = FILE_DISPOSITION_INFO { DeleteFile: TRUE as u8 }; - let result = SetFileInformationByHandle( - self.0, - FileDispositionInfo, - &mut info as *mut _ as LPVOID, - mem::size_of::() as DWORD, - ); + unsafe { + let mut info = FILE_DISPOSITION_INFO { + DeleteFile: 1 as BOOLEAN, + }; + let result = SetFileInformationByHandle( + self.0, + FileDispositionInfo, + &mut info as *mut _ as *mut c_void, + mem::size_of::() as u32, + ); - if result == FALSE { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Failed to mark file for deletion: {}", - util::get_last_error_message()? - ), - ) - .into()); - } - } + if result.is_negative() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to mark file for deletion: {}", + util::get_last_error_message()? + ), + ) + .into()); + } + } - Ok(()) - } + Ok(()) + } - pub fn close(&self) -> Result<(), Box> { - use winapi::shared::minwindef::FALSE; - use winapi::um::handleapi::CloseHandle; + pub fn close(&self) -> Result<(), Box> { + use windows_sys::Win32::Foundation::CloseHandle; - unsafe { - if CloseHandle(self.0) == FALSE { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Failed to close file handle: {}", - util::get_last_error_message()? - ), - ) - .into()); - } - } + unsafe { + if CloseHandle(self.0).is_negative() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to close file handle: {}", + util::get_last_error_message()? + ), + ) + .into()); + } + } - Ok(()) - } + Ok(()) + } } diff --git a/src/main.rs b/src/main.rs index 490100b..9170c23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ extern crate crc; extern crate slog; extern crate slog_async; extern crate slog_term; -extern crate winapi; +extern crate windows_sys; mod blockio; mod gui; @@ -33,7 +33,7 @@ use std::time::SystemTime; use std::vec::Vec; use std::{env, error, fmt, fs, io, thread}; -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); +const VERSION: &str = env!("CARGO_PKG_VERSION"); fn read_file(path: &Path) -> Result<(Header, Vec), Box> { let input_file = fs::File::open(path)?; @@ -100,17 +100,15 @@ fn delete_existing_version( let root = PathBuf::from(root_path); directories.push_back(root); - while directories.len() > 0 { - let dir = directories.pop_front().unwrap(); + while let Some(dir) = directories.pop_front() { info!(log, "Reading directory: {:?}", dir); for entry in fs::read_dir(&dir)? { let entry = entry?; let entry_name = entry.file_name(); - let entry_name = entry_name.to_str().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get entry name", - ))?; + let entry_name = entry_name + .to_str() + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?; if dir == root_path { // don't delete the update folder @@ -122,7 +120,7 @@ fn delete_existing_version( if entry_name == "tools" { continue; } - + // don't delete any of the unins* files if entry_name.starts_with("unins") { continue; @@ -220,10 +218,12 @@ fn move_update( "move_update: {:?}, {}", uninstdat_path, update_folder_name ); - let root_path = uninstdat_path.parent().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get parent path of uninstdat", - ))?; + let root_path = uninstdat_path.parent().ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + "Could not get parent path of uninstdat", + ) + })?; let mut update_path = PathBuf::from(root_path); update_path.push(update_folder_name); @@ -231,7 +231,9 @@ fn move_update( let stat = fs::metadata(&update_path)?; if !stat.is_dir() { - return Err(io::Error::new(io::ErrorKind::Other, "Update folder is not a directory").into()); + return Err( + io::Error::new(io::ErrorKind::Other, "Update folder is not a directory").into(), + ); } // safely delete all current files @@ -241,10 +243,9 @@ fn move_update( for entry in fs::read_dir(&update_path)? { let entry = entry?; let entry_name = entry.file_name(); - let entry_name = entry_name.to_str().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get entry name", - ))?; + let entry_name = entry_name + .to_str() + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get entry name"))?; let mut target = PathBuf::from(root_path); target.push(entry_name); @@ -272,15 +273,17 @@ fn patch_uninstdat( uninstdat_path: &PathBuf, update_folder_name: &str, ) -> Result<(), Box> { - let (header, recs) = read_file(&uninstdat_path)?; + let (header, recs) = read_file(uninstdat_path)?; info!(log, "header: {:?}", header); info!(log, "num_recs: {:?}", recs.len()); - let root_path = uninstdat_path.parent().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get parent path of uninstdat", - ))?; + let root_path = uninstdat_path.parent().ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + "Could not get parent path of uninstdat", + ) + })?; let mut update_path = PathBuf::from(root_path); update_path.push(&update_folder_name); @@ -296,7 +299,7 @@ fn patch_uninstdat( .collect(); info!(log, "Updating uninstall file {:?}", uninstdat_path); - write_file(&uninstdat_path, &header, recs?)?; + write_file(uninstdat_path, &header, recs?)?; Ok(()) } @@ -308,10 +311,12 @@ fn do_update( ) -> Result<(), Box> { info!(log, "do_update: {:?}, {}", code_path, update_folder_name); - let root_path = code_path.parent().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get parent path of uninstdat", - ))?; + let root_path = code_path.parent().ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + "Could not get parent path of uninstdat", + ) + })?; let mut uninstdat_path = PathBuf::from(root_path); uninstdat_path.push("unins000.dat"); @@ -349,7 +354,7 @@ fn update( .recv() .map_err(|_| io::Error::new(io::ErrorKind::Other, "Could not receive GUI window handle"))?; - do_update(&log, code_path, update_folder_name)?; + do_update(log, code_path, update_folder_name)?; window.exit(); Ok(()) @@ -374,19 +379,17 @@ impl error::Error for ArgumentError { } } -fn _main(log: &slog::Logger, args: &Vec) -> Result<(), Box> { +fn _main(log: &slog::Logger, args: &[String]) -> Result<(), Box> { info!(log, "Starting: {}, {}", args[1], args[2]); let code_path = PathBuf::from(&args[1]); if !code_path.is_absolute() { - return Err( - ArgumentError(format!( - "Code path needs to be absolute. Instead got: {}", - args[1] - )) - .into(), - ); + return Err(ArgumentError(format!( + "Code path needs to be absolute. Instead got: {}", + args[1] + )) + .into()); } if !code_path.exists() { @@ -396,32 +399,28 @@ fn _main(log: &slog::Logger, args: &Vec) -> Result<(), Box) -> i32 { +fn __main(args: &[String]) -> i32 { let mut log_path = env::temp_dir(); log_path.push(format!( "vscode-inno-updater-{:?}.log", @@ -522,7 +521,7 @@ fn main() { Some(5), ); - if let Err(_) = result { + if result.is_err() { handle_error(&log_path); } diff --git a/src/model/filerec.rs b/src/model/filerec.rs index bdfcf3e..df54941 100644 --- a/src/model/filerec.rs +++ b/src/model/filerec.rs @@ -62,7 +62,7 @@ pub struct FileRec { data: Vec, } -impl<'a> fmt::Debug for FileRec { +impl fmt::Debug for FileRec { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!( formatter, @@ -95,10 +95,10 @@ impl<'a> error::Error for StringDecodeError<'a> { fn decode_strings<'a>(data: &[u8]) -> Result, StringDecodeError<'a>> { let mut result: Vec = Vec::with_capacity(10); - let mut slice = data.clone(); + let mut slice = data; loop { - let reader: &mut dyn Read = &mut slice.clone(); + let reader: &mut dyn Read = &mut slice; let byte_result = reader .read_u8() .map_err(|_| StringDecodeError("Failed to parse file rec string header"))?; @@ -241,7 +241,7 @@ impl error::Error for RebaseError { } } -impl<'a> FileRec { +impl FileRec { pub fn from_reader<'b>(reader: &mut dyn Read) -> Result> { let typ = reader .read_u16::() @@ -251,7 +251,8 @@ impl<'a> FileRec { .map_err(|_| FileRecParseError("Failed to parse file rec extra data"))?; let data_size = reader .read_u32::() - .map_err(|_| FileRecParseError("Failed to parse file rec data size"))? as usize; + .map_err(|_| FileRecParseError("Failed to parse file rec data size"))? + as usize; if data_size > 0x8000000 { return Err(FileRecParseError("File rec data size too large")); @@ -303,8 +304,8 @@ impl<'a> FileRec { let rebased_paths: Vec = paths .iter() .map(|p| { - if p.starts_with(from) { - [to, &p[from.len()..]].join("") + if let Some(p) = p.strip_prefix(from) { + format!("{to}{p}") } else { p.clone() } diff --git a/src/model/header.rs b/src/model/header.rs index b5cfdae..58e7a97 100644 --- a/src/model/header.rs +++ b/src/model/header.rs @@ -112,8 +112,7 @@ impl Header { .map_err(|_| HeaderParseError("Failed to parse header flags"))?; let mut reserved = [0; 108]; - read - .read_exact(&mut reserved) + read.read_exact(&mut reserved) .map_err(|_| HeaderParseError("Failed to parse header reserved"))?; let crc = read diff --git a/src/process.rs b/src/process.rs index e86f19b..6aa279a 100644 --- a/src/process.rs +++ b/src/process.rs @@ -1,235 +1,229 @@ -/*----------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for license information. - *----------------------------------------------------------------------------------------*/ - -use slog; -use std::path::{Path, PathBuf}; -use std::{error, io, mem, ptr, thread, time}; -use strings::from_utf16; -use util; -use winapi::shared::minwindef::{DWORD, TRUE}; - -pub struct RunningProcess { - pub name: String, - pub id: DWORD, -} - -pub fn get_running_processes() -> Result, io::Error> { - use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE}; - use winapi::um::tlhelp32::{ - CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS, - }; - - unsafe { - let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - if handle == INVALID_HANDLE_VALUE { - return Err(io::Error::new( - io::ErrorKind::Other, - "Could not create process snapshot", - )); - } - - let mut pe32 = PROCESSENTRY32W { - dwSize: 0, - cntUsage: 0, - th32ProcessID: 0, - th32DefaultHeapID: 0, - th32ModuleID: 0, - cntThreads: 0, - th32ParentProcessID: 0, - pcPriClassBase: 0, - dwFlags: 0, - szExeFile: [0u16; 260], - }; - - pe32.dwSize = mem::size_of::() as u32; - - if Process32FirstW(handle, &mut pe32) != TRUE { - CloseHandle(handle); - - return Err(io::Error::new( - io::ErrorKind::Other, - "Could not get first process data", - )); - } - - let mut result: Vec = vec![]; - - loop { - result.push(RunningProcess { - name: from_utf16(&pe32.szExeFile).map_err(|e| { - CloseHandle(handle); - e - })?, - id: pe32.th32ProcessID, - }); - - if Process32NextW(handle, &mut pe32) != TRUE { - CloseHandle(handle); - break; - } - } - - return Ok(result); - } -} - -/** - * Kills a running process, if its path is the same as the provided one. - */ -fn kill_process_if( - log: &slog::Logger, - process: &RunningProcess, - path: &Path, -) -> Result<(), Box> { - use winapi::shared::minwindef::MAX_PATH; - use winapi::um::handleapi::CloseHandle; - use winapi::um::processthreadsapi::{OpenProcess, TerminateProcess}; - use winapi::um::psapi::GetModuleFileNameExW; - use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, PROCESS_VM_READ}; - - info!( - log, - "Kill process if found: {}, {}", process.id, process.name - ); - - unsafe { - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx - let handle = OpenProcess( - PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, - 0, - process.id, - ); - - if handle.is_null() { - return Err( - io::Error::new( - io::ErrorKind::Other, - format!( - "Failed to open process: {}", - util::get_last_error_message()? - ), - ) - .into(), - ); - } - - let mut raw_path = [0u16; MAX_PATH]; - let len = GetModuleFileNameExW( - handle, - ptr::null_mut(), - raw_path.as_mut_ptr(), - MAX_PATH as DWORD, - ) as usize; - - if len == 0 { - CloseHandle(handle); - - return Err( - io::Error::new( - io::ErrorKind::Other, - format!( - "Failed to get process file name: {}", - util::get_last_error_message()? - ), - ) - .into(), - ); - } - - let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?); - - if process_path != path { - CloseHandle(handle); - return Ok(()); - } - - info!( - log, - "Found {} running, pid {}, attempting to kill...", process.name, process.id - ); - - if TerminateProcess(handle, 0) != TRUE { - return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into()); - } - - info!( - log, - "Successfully killed {}, pid {}", process.name, process.id - ); - - CloseHandle(handle); - Ok(()) - } -} - -pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box> { - let file_name = path.file_name().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get process file name", - ))?; - - let file_name = file_name.to_str().ok_or(io::Error::new( - io::ErrorKind::Other, - "Could not get convert file name to str", - ))?; - - let mut attempt: u32 = 0; - - // wait for 10 seconds until all processes are dead - loop { - attempt += 1; - - info!( - log, - "Checking for running {} processes... (attempt {})", file_name, attempt - ); - - let process_found = get_running_processes()? - .into_iter() - .any(|p| p.name == file_name); - - if !process_found { - info!(log, "{} is not running", file_name); - break; - } - - // give up after 60 * 500ms = 30 seconds - if attempt == 60 { - info!(log, "Gave up waiting for {} to exit", file_name); - break; - } - - info!(log, "{} is running, wait a bit", file_name); - thread::sleep(time::Duration::from_millis(500)); - } - - // try to kill any running processes - util::retry( - "attempting to kill any running Code.exe processes", - |attempt| { - info!( - log, - "Checking for possible conflicting running processes... (attempt {})", attempt - ); - - let kill_errors: Vec<_> = get_running_processes()? - .into_iter() - .filter(|p| p.name == file_name) - .filter_map(|p| kill_process_if(log, &p, path).err()) - .collect(); - - for err in &kill_errors { - warn!(log, "Kill error {}", err); - } - - match kill_errors.len() { - 0 => Ok(()), - _ => Err(kill_errors.into_iter().nth(1).unwrap()), - } - }, - None, - ) -} +/*----------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *----------------------------------------------------------------------------------------*/ + +use std::ffi::c_void; +use std::path::{Path, PathBuf}; +use std::{error, io, mem, ptr, thread, time}; +use strings::from_utf16; +use {slog, util}; + +pub struct RunningProcess { + pub name: String, + pub id: u32, +} + +pub fn get_running_processes() -> Result, io::Error> { + use windows_sys::Win32::Foundation::{CloseHandle, INVALID_HANDLE_VALUE}; + use windows_sys::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, + TH32CS_SNAPPROCESS, + }; + + unsafe { + let handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if handle == INVALID_HANDLE_VALUE { + return Err(io::Error::new( + io::ErrorKind::Other, + "Could not create process snapshot", + )); + } + + let mut pe32 = PROCESSENTRY32W { + dwSize: 0, + cntUsage: 0, + th32ProcessID: 0, + th32DefaultHeapID: 0, + th32ModuleID: 0, + cntThreads: 0, + th32ParentProcessID: 0, + pcPriClassBase: 0, + dwFlags: 0, + szExeFile: [0u16; 260], + }; + + pe32.dwSize = mem::size_of::() as u32; + + if Process32FirstW(handle, &mut pe32) == 0 { + CloseHandle(handle); + + return Err(io::Error::new( + io::ErrorKind::Other, + "Could not get first process data", + )); + } + + let mut result: Vec = vec![]; + + loop { + result.push(RunningProcess { + name: from_utf16(&pe32.szExeFile).map_err(|e| { + CloseHandle(handle); + e + })?, + id: pe32.th32ProcessID, + }); + + if Process32NextW(handle, &mut pe32) == 0 { + CloseHandle(handle); + break; + } + } + + Ok(result) + } +} + +/** + * Kills a running process, if its path is the same as the provided one. + */ +fn kill_process_if( + log: &slog::Logger, + process: &RunningProcess, + path: &Path, +) -> Result<(), Box> { + use windows_sys::Win32::Foundation::{CloseHandle, MAX_PATH}; + use windows_sys::Win32::System::ProcessStatus::K32GetModuleFileNameExW; + use windows_sys::Win32::System::Threading::{ + OpenProcess, TerminateProcess, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, + PROCESS_VM_READ, + }; + + info!( + log, + "Kill process if found: {}, {}", process.id, process.name + ); + + unsafe { + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx + let handle = OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, + 0, + process.id, + ); + + if ptr::eq(handle as *mut c_void, ptr::null()) { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to open process: {}", + util::get_last_error_message()? + ), + ) + .into()); + } + + let mut raw_path = [0u16; MAX_PATH as usize]; + let len = K32GetModuleFileNameExW(handle, mem::zeroed(), raw_path.as_mut_ptr(), MAX_PATH) + as usize; + + if len == 0 { + CloseHandle(handle); + + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Failed to get process file name: {}", + util::get_last_error_message()? + ), + ) + .into()); + } + + let process_path = PathBuf::from(from_utf16(&raw_path[0..len])?); + + if process_path != path { + CloseHandle(handle); + return Ok(()); + } + + info!( + log, + "Found {} running, pid {}, attempting to kill...", process.name, process.id + ); + + if TerminateProcess(handle, 0).is_negative() { + return Err(io::Error::new(io::ErrorKind::Other, "Failed to kill process").into()); + } + + info!( + log, + "Successfully killed {}, pid {}", process.name, process.id + ); + + CloseHandle(handle); + Ok(()) + } +} + +pub fn wait_or_kill(log: &slog::Logger, path: &Path) -> Result<(), Box> { + let file_name = path + .file_name() + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Could not get process file name"))?; + + let file_name = file_name.to_str().ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + "Could not get convert file name to str", + ) + })?; + + let mut attempt: u32 = 0; + + // wait for 10 seconds until all processes are dead + loop { + attempt += 1; + + info!( + log, + "Checking for running {} processes... (attempt {})", file_name, attempt + ); + + let process_found = get_running_processes()? + .into_iter() + .any(|p| p.name == file_name); + + if !process_found { + info!(log, "{} is not running", file_name); + break; + } + + // give up after 60 * 500ms = 30 seconds + if attempt == 60 { + info!(log, "Gave up waiting for {} to exit", file_name); + break; + } + + info!(log, "{} is running, wait a bit", file_name); + thread::sleep(time::Duration::from_millis(500)); + } + + // try to kill any running processes + util::retry( + "attempting to kill any running Code.exe processes", + |attempt| { + info!( + log, + "Checking for possible conflicting running processes... (attempt {})", attempt + ); + + let kill_errors: Vec<_> = get_running_processes()? + .into_iter() + .filter(|p| p.name == file_name) + .filter_map(|p| kill_process_if(log, &p, path).err()) + .collect(); + + for err in &kill_errors { + warn!(log, "Kill error {}", err); + } + + match kill_errors.len() { + 0 => Ok(()), + _ => Err(kill_errors.into_iter().nth(1).unwrap()), + } + }, + None, + ) +} diff --git a/src/strings.rs b/src/strings.rs index c7df1af..7c36c5c 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -4,10 +4,9 @@ *----------------------------------------------------------------------------------------*/ use std::ffi::OsStr; -use std::io; use std::io::prelude::*; use std::os::windows::ffi::OsStrExt; -use std::string; +use std::{io, string}; #[derive(Debug)] pub enum ReadUtf8StringError { @@ -23,11 +22,11 @@ pub fn read_utf8_string( reader .read_exact(&mut vec) - .map_err(|err| ReadUtf8StringError::IOError(err)) + .map_err(ReadUtf8StringError::IOError) .and_then(|_| { let pos = vec.iter().position(|&x| x == 0).unwrap_or(64); let bar = &vec[0..pos]; - String::from_utf8(Vec::from(bar)).map_err(|err| ReadUtf8StringError::UTF8Error(err)) + String::from_utf8(Vec::from(bar)).map_err(ReadUtf8StringError::UTF8Error) }) } @@ -37,7 +36,7 @@ pub fn write_utf8_string( capacity: usize, ) -> Result<(), io::Error> { let bytes = string.as_bytes(); - writer.write_all(&bytes)?; + writer.write_all(bytes)?; let rest = vec![0; capacity - bytes.len()]; writer.write_all(&rest)?; diff --git a/src/util.rs b/src/util.rs index 548c47c..807e10e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,82 +1,85 @@ -/*----------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for license information. - *----------------------------------------------------------------------------------------*/ - -use gui; -use std::{error, ptr, thread, time}; -use strings::from_utf16; - -/** - * Quadratic backoff retry mechanism. - * - * Use `max_attempts` to control how long it should retry for: - * - 11 (default): 19s - * - 16: ~1 minute - * - 20: ~2 minutes - * - 23: ~3 minutes - * - 25: ~4 minutes - * - 27: ~5 minutes - */ -pub fn retry(task: &str, closure: F, max_attempts: T) -> Result> -where - F: Fn(u32) -> Result>, - T: Into>, -{ - let mut attempt: u32 = 0; - let max_attempts = max_attempts.into().unwrap_or(11); - - loop { - attempt += 1; - - let result = closure(attempt); - match result { - Ok(_) => return result, - Err(err) => { - if attempt >= max_attempts { - let msg = format!("There was an error while {}:\n\n{}\n\nPlease verify there are no Visual Studio Code processes still executing.", task, err); - let mb_result = - gui::message_box(&msg, "Visual Studio Code", gui::MessageBoxType::RetryCancel); - - match mb_result { - gui::MessageBoxResult::Retry => { - attempt = 0; - } - _ => { - return Err(err); - } - } - } - - thread::sleep(time::Duration::from_millis((attempt.pow(2) * 50) as u64)); - } - } - } -} - -pub fn get_last_error_message() -> Result> { - use winapi::um::errhandlingapi::GetLastError; - use winapi::um::winbase::{ - FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, - }; - - let mut error_message = [0u16; 32000]; - let error_message_len: usize; - - unsafe { - error_message_len = FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - ptr::null_mut(), - GetLastError(), - 0, - error_message.as_mut_ptr(), - 32000, - ptr::null_mut(), - ) as usize; - } - - Ok(match error_message_len { - 0 => String::from("unknown error"), - _ => from_utf16(&error_message[0..error_message_len])?, - }) -} +/*----------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *----------------------------------------------------------------------------------------*/ + +use gui; +use std::{error, ptr, thread, time}; +use strings::from_utf16; + +/** + * Quadratic backoff retry mechanism. + * + * Use `max_attempts` to control how long it should retry for: + * - 11 (default): 19s + * - 16: ~1 minute + * - 20: ~2 minutes + * - 23: ~3 minutes + * - 25: ~4 minutes + * - 27: ~5 minutes + */ +pub fn retry(task: &str, closure: F, max_attempts: T) -> Result> +where + F: Fn(u32) -> Result>, + T: Into>, +{ + let mut attempt: u32 = 0; + let max_attempts = max_attempts.into().unwrap_or(11); + + loop { + attempt += 1; + + let result = closure(attempt); + match result { + Ok(_) => return result, + Err(err) => { + if attempt >= max_attempts { + let msg = format!("There was an error while {}:\n\n{}\n\nPlease verify there are no Visual Studio Code processes still executing.", task, err); + let mb_result = gui::message_box( + &msg, + "Visual Studio Code", + gui::MessageBoxType::RetryCancel, + ); + + match mb_result { + gui::MessageBoxResult::Retry => { + attempt = 0; + } + _ => { + return Err(err); + } + } + } + + thread::sleep(time::Duration::from_millis((attempt.pow(2) * 50) as u64)); + } + } + } +} + +pub fn get_last_error_message() -> Result> { + use windows_sys::Win32::Foundation::GetLastError; + use windows_sys::Win32::System::Diagnostics::Debug::{ + FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS, + }; + + let mut error_message = [0u16; 32000]; + let error_message_len: usize; + + unsafe { + error_message_len = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + ptr::null_mut(), + GetLastError(), + 0, + error_message.as_mut_ptr(), + 32000, + ptr::null_mut(), + ) as usize; + } + + Ok(match error_message_len { + 0 => String::from("unknown error"), + _ => from_utf16(&error_message[0..error_message_len])?, + }) +}