diff --git a/Cargo.lock b/Cargo.lock index 44d458ae319..b60297eb02c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2364,7 +2364,7 @@ dependencies = [ "libc", "parse_datetime", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -2422,7 +2422,7 @@ dependencies = [ "clap", "glob", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -2545,7 +2545,7 @@ dependencies = [ "clap", "hostname", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -2836,7 +2836,7 @@ dependencies = [ "libc", "uucore", "walkdir", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -2980,7 +2980,7 @@ dependencies = [ "libc", "nix", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -3007,7 +3007,7 @@ dependencies = [ "same-file", "uucore", "winapi-util", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -3048,7 +3048,7 @@ dependencies = [ "filetime", "parse_datetime", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -3181,7 +3181,7 @@ dependencies = [ "clap", "libc", "uucore", - "windows-sys 0.48.0", + "windows", ] [[package]] @@ -3226,7 +3226,7 @@ dependencies = [ "walkdir", "wild", "winapi-util", - "windows-sys 0.48.0", + "windows", "z85", ] @@ -3385,6 +3385,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index accfc2a0653..82f71b25ee9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -330,7 +330,7 @@ unicode-width = "0.1.11" utf-8 = "0.7.6" walkdir = "2.4" winapi-util = "0.1.6" -windows-sys = { version = "0.48.0", default-features = false } +windows = { version = "0.52.0", default-features = false } xattr = "1.1.3" zip = { version = "0.6.6", default-features = false, features = ["deflate"] } diff --git a/src/uu/date/Cargo.toml b/src/uu/date/Cargo.toml index c5682f83e5a..58690e1c6e3 100644 --- a/src/uu/date/Cargo.toml +++ b/src/uu/date/Cargo.toml @@ -25,7 +25,7 @@ parse_datetime = { workspace = true } libc = { workspace = true } [target.'cfg(windows)'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_Foundation", "Win32_System_SystemInformation", ] } diff --git a/src/uu/date/src/date.rs b/src/uu/date/src/date.rs index ee3c7bfdfae..df929c97de6 100644 --- a/src/uu/date/src/date.rs +++ b/src/uu/date/src/date.rs @@ -20,7 +20,7 @@ use uucore::error::FromIo; use uucore::error::{UResult, USimpleError}; use uucore::{format_usage, help_about, help_usage, show}; #[cfg(windows)] -use windows_sys::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime}; +use windows::Win32::{Foundation::SYSTEMTIME, System::SystemInformation::SetSystemTime}; use uucore::shortcut_value_parser::ShortcutValueParser; @@ -472,8 +472,9 @@ fn set_system_datetime(date: DateTime) -> UResult<()> { let result = unsafe { SetSystemTime(&system_time) }; - if result == 0 { - Err(std::io::Error::last_os_error().map_err_context(|| "cannot set date".to_string())) + if let Err(e) = result { + Err(std::io::Error::from_raw_os_error(e.code().0) + .map_err_context(|| "cannot set date".to_string())) } else { Ok(()) } diff --git a/src/uu/du/Cargo.toml b/src/uu/du/Cargo.toml index 5e87b2f4381..a78bce5ac90 100644 --- a/src/uu/du/Cargo.toml +++ b/src/uu/du/Cargo.toml @@ -22,7 +22,7 @@ clap = { workspace = true } uucore = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_Storage_FileSystem", "Win32_Foundation", ] } diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index 1213e004f15..46462fdc6c5 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -32,10 +32,10 @@ use uucore::parse_glob; use uucore::parse_size::{parse_size_u64, ParseSizeError}; use uucore::{format_usage, help_about, help_section, help_usage, show, show_warning}; #[cfg(windows)] -use windows_sys::Win32::Foundation::HANDLE; +use windows::Win32::Foundation::HANDLE; #[cfg(windows)] -use windows_sys::Win32::Storage::FileSystem::{ - FileIdInfo, FileStandardInfo, GetFileInformationByHandleEx, FILE_ID_128, FILE_ID_INFO, +use windows::Win32::Storage::FileSystem::{ + FileIdInfo, FileStandardInfo, GetFileInformationByHandleEx, FILE_ID_INFO, FILE_STANDARD_INFO, }; @@ -230,14 +230,14 @@ fn get_size_on_disk(path: &Path) -> u64 { let mut file_info: FILE_STANDARD_INFO = core::mem::zeroed(); let file_info_ptr: *mut FILE_STANDARD_INFO = &mut file_info; - let success = GetFileInformationByHandleEx( - file.as_raw_handle() as HANDLE, + let result = GetFileInformationByHandleEx( + HANDLE(file.as_raw_handle() as isize), FileStandardInfo, file_info_ptr as _, std::mem::size_of::() as u32, ); - if success != 0 { + if result.is_ok() { size_on_disk = file_info.AllocationSize as u64; } } @@ -247,33 +247,26 @@ fn get_size_on_disk(path: &Path) -> u64 { #[cfg(windows)] fn get_file_info(path: &Path) -> Option { - let mut result = None; - - let file = match fs::File::open(path) { - Ok(file) => file, - Err(_) => return result, - }; + let file = fs::File::open(path).ok()?; + let mut file_info = FILE_ID_INFO::default(); + let file_info_ptr: *mut FILE_ID_INFO = &mut file_info; unsafe { - let mut file_info: FILE_ID_INFO = core::mem::zeroed(); - let file_info_ptr: *mut FILE_ID_INFO = &mut file_info; - - let success = GetFileInformationByHandleEx( - file.as_raw_handle() as HANDLE, + GetFileInformationByHandleEx( + HANDLE(file.as_raw_handle() as isize), FileIdInfo, file_info_ptr as _, std::mem::size_of::() as u32, - ); - - if success != 0 { - result = Some(FileInfo { - file_id: std::mem::transmute::(file_info.FileId), - dev_id: file_info.VolumeSerialNumber, - }); - } - } + ).ok()? + }; - result + // `from_le_bytes` won't be correct on all systems. However, we only care + // whether the value is unique for all files since it's only used to put + // the `FileInfo` in a `HashSet`. + Some(FileInfo { + file_id: u128::from_le_bytes(file_info.FileId.Identifier), + dev_id: file_info.VolumeSerialNumber, + }) } fn read_block_size(s: Option<&str>) -> UResult { diff --git a/src/uu/hostname/Cargo.toml b/src/uu/hostname/Cargo.toml index 1fe10170964..47ca102f092 100644 --- a/src/uu/hostname/Cargo.toml +++ b/src/uu/hostname/Cargo.toml @@ -20,7 +20,7 @@ hostname = { workspace = true, features = ["set"] } uucore = { workspace = true, features = ["wide"] } [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_Networking_WinSock", "Win32_Foundation", ] } diff --git a/src/uu/hostname/src/hostname.rs b/src/uu/hostname/src/hostname.rs index 6a318cb8c26..7d0dc61e4a9 100644 --- a/src/uu/hostname/src/hostname.rs +++ b/src/uu/hostname/src/hostname.rs @@ -30,7 +30,7 @@ static OPT_HOST: &str = "host"; mod wsa { use std::io; - use windows_sys::Win32::Networking::WinSock::{WSACleanup, WSAStartup, WSADATA}; + use windows::Win32::Networking::WinSock::{WSACleanup, WSAStartup, WSADATA}; pub(super) struct WsaHandle(()); diff --git a/src/uu/rm/Cargo.toml b/src/uu/rm/Cargo.toml index 34ada60e865..a758a078f53 100644 --- a/src/uu/rm/Cargo.toml +++ b/src/uu/rm/Cargo.toml @@ -23,7 +23,7 @@ uucore = { workspace = true, features = ["fs"] } libc = { workspace = true } [target.'cfg(windows)'.dependencies] -windows-sys = { workspace = true, features = ["Win32_Storage_FileSystem"] } +windows = { workspace = true, features = ["Win32_Storage_FileSystem"] } [[bin]] name = "rm" diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 4fc37a1300f..ac84d54be25 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -564,8 +564,8 @@ fn handle_writable_directory(path: &Path, options: &Options, metadata: &Metadata #[cfg(windows)] fn handle_writable_directory(path: &Path, options: &Options, metadata: &Metadata) -> bool { use std::os::windows::prelude::MetadataExt; - use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_READONLY; - let not_user_writable = (metadata.file_attributes() & FILE_ATTRIBUTE_READONLY) != 0; + use windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_READONLY; + let not_user_writable = (metadata.file_attributes() & FILE_ATTRIBUTE_READONLY.0) != 0; if not_user_writable { prompt_yes!("remove write-protected directory {}?", path.quote()) } else if options.interactive == InteractiveMode::Always { @@ -606,8 +606,8 @@ fn is_symlink_dir(_metadata: &Metadata) -> bool { #[cfg(windows)] fn is_symlink_dir(metadata: &Metadata) -> bool { use std::os::windows::prelude::MetadataExt; - use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_DIRECTORY; + use windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_DIRECTORY; metadata.file_type().is_symlink() - && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY) != 0) + && ((metadata.file_attributes() & FILE_ATTRIBUTE_DIRECTORY.0) != 0) } diff --git a/src/uu/sync/Cargo.toml b/src/uu/sync/Cargo.toml index 4048115243a..0d164d64c65 100644 --- a/src/uu/sync/Cargo.toml +++ b/src/uu/sync/Cargo.toml @@ -23,7 +23,7 @@ uucore = { workspace = true, features = ["wide"] } nix = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_Storage_FileSystem", "Win32_System_WindowsProgramming", "Win32_Foundation", diff --git a/src/uu/sync/src/sync.rs b/src/uu/sync/src/sync.rs index c0b8f3d00b5..4d87a536db2 100644 --- a/src/uu/sync/src/sync.rs +++ b/src/uu/sync/src/sync.rs @@ -73,26 +73,27 @@ mod platform { use std::path::Path; use uucore::crash; use uucore::wide::{FromWide, ToWide}; - use windows_sys::Win32::Foundation::{ - GetLastError, ERROR_NO_MORE_FILES, HANDLE, INVALID_HANDLE_VALUE, MAX_PATH, + use windows::Win32::Foundation::{ + ERROR_NO_MORE_FILES, HANDLE, MAX_PATH, }; - use windows_sys::Win32::Storage::FileSystem::{ + use windows::Win32::Storage::FileSystem::{ FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, FlushFileBuffers, GetDriveTypeW, }; - use windows_sys::Win32::System::WindowsProgramming::DRIVE_FIXED; + use windows::Win32::System::WindowsProgramming::DRIVE_FIXED; + use windows::core::{PCWSTR, Error}; unsafe fn flush_volume(name: &str) { let name_wide = name.to_wide_null(); - if GetDriveTypeW(name_wide.as_ptr()) == DRIVE_FIXED { + if GetDriveTypeW(PCWSTR::from_raw(name_wide.as_ptr())) == DRIVE_FIXED { let sliced_name = &name[..name.len() - 1]; // eliminate trailing backslash match OpenOptions::new().write(true).open(sliced_name) { Ok(file) => { - if FlushFileBuffers(file.as_raw_handle() as HANDLE) == 0 { - crash!(GetLastError() as i32, "failed to flush file buffer"); + if FlushFileBuffers(HANDLE(file.as_raw_handle() as isize)).is_err() { + crash!(1, "failed to flush file buffer"); } } - Err(e) => crash!( - e.raw_os_error().unwrap_or(1), + Err(_) => crash!( + 1, "failed to create volume handle" ), } @@ -101,10 +102,9 @@ mod platform { unsafe fn find_first_volume() -> (String, HANDLE) { let mut name: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize]; - let handle = FindFirstVolumeW(name.as_mut_ptr(), name.len() as u32); - if handle == INVALID_HANDLE_VALUE { - crash!(GetLastError() as i32, "failed to find first volume"); - } + let Ok(handle) = FindFirstVolumeW(&mut name) else { + crash!(1, "failed to find first volume"); + }; (String::from_wide_null(&name), handle) } @@ -113,13 +113,12 @@ mod platform { let mut volumes = vec![first_volume]; loop { let mut name: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize]; - if FindNextVolumeW(next_volume_handle, name.as_mut_ptr(), name.len() as u32) == 0 { - match GetLastError() { - ERROR_NO_MORE_FILES => { - FindVolumeClose(next_volume_handle); - return volumes; - } - err => crash!(err as i32, "failed to find next volume"), + if let Err(e) = FindNextVolumeW(next_volume_handle, &mut name) { + if e == Error::from(ERROR_NO_MORE_FILES) { + let _ = FindVolumeClose(next_volume_handle); + return volumes; + } else { + crash!(1, "failed to find next volume"); } } else { volumes.push(String::from_wide_null(&name)); diff --git a/src/uu/tail/Cargo.toml b/src/uu/tail/Cargo.toml index 636480e08ba..d9a8083b4a6 100644 --- a/src/uu/tail/Cargo.toml +++ b/src/uu/tail/Cargo.toml @@ -25,7 +25,7 @@ same-file = { workspace = true } fundu = { workspace = true } [target.'cfg(windows)'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_System_Threading", "Win32_Foundation", ] } diff --git a/src/uu/tail/src/platform/windows.rs b/src/uu/tail/src/platform/windows.rs index 5925161621b..a4cc051447f 100644 --- a/src/uu/tail/src/platform/windows.rs +++ b/src/uu/tail/src/platform/windows.rs @@ -3,46 +3,50 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -use windows_sys::Win32::Foundation::{CloseHandle, BOOL, HANDLE, WAIT_FAILED, WAIT_OBJECT_0}; -use windows_sys::Win32::System::Threading::{ +use windows::Win32::Foundation::{CloseHandle, HANDLE, WAIT_FAILED, WAIT_OBJECT_0}; +use windows::Win32::System::Threading::{ OpenProcess, WaitForSingleObject, PROCESS_SYNCHRONIZE, }; pub type Pid = u32; -pub struct ProcessChecker { - dead: bool, - handle: HANDLE, +pub enum ProcessChecker { + Alive(HANDLE), + Dead, } impl ProcessChecker { pub fn new(process_id: self::Pid) -> Self { - #[allow(non_snake_case)] - let FALSE: BOOL = 0; - let h = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, FALSE, process_id) }; - Self { - dead: h == 0, - handle: h, + let h = unsafe { OpenProcess(PROCESS_SYNCHRONIZE, false, process_id) }; + match h { + Ok(h) => Self::Alive(h), + Err(_) => Self::Dead, } } #[allow(clippy::wrong_self_convention)] pub fn is_dead(&mut self) -> bool { - if !self.dead { - self.dead = unsafe { - let status = WaitForSingleObject(self.handle, 0); - status == WAIT_OBJECT_0 || status == WAIT_FAILED + match self { + Self::Alive(h) => { + let status = unsafe { WaitForSingleObject(*h, 0) }; + if status == WAIT_OBJECT_0 || status == WAIT_FAILED { + *self = Self::Dead; + true + } else { + false + } } + Self::Dead => true, } - - self.dead } } impl Drop for ProcessChecker { fn drop(&mut self) { - unsafe { - CloseHandle(self.handle); + if let Self::Alive(h) = self { + unsafe { + let _ = CloseHandle(*h); + } } } } diff --git a/src/uu/touch/Cargo.toml b/src/uu/touch/Cargo.toml index d67cd1f8a16..d9e6791f234 100644 --- a/src/uu/touch/Cargo.toml +++ b/src/uu/touch/Cargo.toml @@ -23,7 +23,7 @@ parse_datetime = { workspace = true } uucore = { workspace = true, features = ["libc"] } [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_Storage_FileSystem", "Win32_Foundation", ] } diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 51c09801b0a..af2c1e032f9 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -454,15 +454,13 @@ fn pathbuf_from_stdout() -> UResult { #[cfg(windows)] { use std::os::windows::prelude::AsRawHandle; - use windows_sys::Win32::Foundation::{ + use windows::Win32::Foundation::{ GetLastError, ERROR_INVALID_PARAMETER, ERROR_NOT_ENOUGH_MEMORY, ERROR_PATH_NOT_FOUND, - HANDLE, MAX_PATH, - }; - use windows_sys::Win32::Storage::FileSystem::{ - GetFinalPathNameByHandleW, FILE_NAME_OPENED, + HANDLE, MAX_PATH, WIN32_ERROR, }; + use windows::Win32::Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_NAME_OPENED}; - let handle = std::io::stdout().lock().as_raw_handle() as HANDLE; + let handle = HANDLE(std::io::stdout().lock().as_raw_handle() as isize); let mut file_path_buffer: [u16; MAX_PATH as usize] = [0; MAX_PATH as usize]; // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea#examples @@ -473,34 +471,30 @@ fn pathbuf_from_stdout() -> UResult { // the buffer size is correct. We know the buffer size (MAX_PATH) at // compile time. MAX_PATH is a small number (260) so we can cast it // to a u32. - let ret = unsafe { - GetFinalPathNameByHandleW( - handle, - file_path_buffer.as_mut_ptr(), - file_path_buffer.len() as u32, - FILE_NAME_OPENED, - ) - }; + let ret = + unsafe { GetFinalPathNameByHandleW(handle, &mut file_path_buffer, FILE_NAME_OPENED) }; - let buffer_size = match ret { - ERROR_PATH_NOT_FOUND | ERROR_NOT_ENOUGH_MEMORY | ERROR_INVALID_PARAMETER => { - return Err(USimpleError::new( - 1, - format!("GetFinalPathNameByHandleW failed with code {ret}"), - )) - } - 0 => { - return Err(USimpleError::new( - 1, - format!( - "GetFinalPathNameByHandleW failed with code {}", - // SAFETY: GetLastError is thread-safe and has no documented memory unsafety. - unsafe { GetLastError() } - ), - )); - } - e => e as usize, - }; + if let ERROR_PATH_NOT_FOUND | ERROR_NOT_ENOUGH_MEMORY | ERROR_INVALID_PARAMETER = + WIN32_ERROR(ret) + { + return Err(USimpleError::new( + 1, + format!("GetFinalPathNameByHandleW failed with code {ret}"), + )); + } + + if ret == 0 { + return Err(USimpleError::new( + 1, + format!( + "GetFinalPathNameByHandleW failed with code {}", + // SAFETY: GetLastError is thread-safe and has no documented memory unsafety. + unsafe { GetLastError() }.unwrap_err() + ), + )); + } + + let buffer_size = ret as usize; // Don't include the null terminator Ok(String::from_utf16(&file_path_buffer[0..buffer_size]) diff --git a/src/uu/whoami/Cargo.toml b/src/uu/whoami/Cargo.toml index 5336568334f..4213d5841e8 100644 --- a/src/uu/whoami/Cargo.toml +++ b/src/uu/whoami/Cargo.toml @@ -19,7 +19,7 @@ clap = { workspace = true } uucore = { workspace = true, features = ["entries"] } [target.'cfg(target_os = "windows")'.dependencies] -windows-sys = { workspace = true, features = [ +windows = { workspace = true, features = [ "Win32_NetworkManagement_NetManagement", "Win32_System_WindowsProgramming", "Win32_Foundation", diff --git a/src/uu/whoami/src/platform/windows.rs b/src/uu/whoami/src/platform/windows.rs index aad2eed3d13..2dd556fd9f6 100644 --- a/src/uu/whoami/src/platform/windows.rs +++ b/src/uu/whoami/src/platform/windows.rs @@ -6,16 +6,19 @@ use std::ffi::OsString; use std::io; use std::os::windows::ffi::OsStringExt; -use windows_sys::Win32::NetworkManagement::NetManagement::UNLEN; -use windows_sys::Win32::System::WindowsProgramming::GetUserNameW; +use windows::Win32::NetworkManagement::NetManagement::UNLEN; +use windows::Win32::System::WindowsProgramming::GetUserNameW; +use windows::core::PWSTR; pub fn get_username() -> io::Result { const BUF_LEN: u32 = UNLEN + 1; let mut buffer = [0_u16; BUF_LEN as usize]; let mut len = BUF_LEN; // SAFETY: buffer.len() == len - if unsafe { GetUserNameW(buffer.as_mut_ptr(), &mut len) } == 0 { - return Err(io::Error::last_os_error()); + let result = unsafe { GetUserNameW(PWSTR::from_raw(buffer.as_mut_ptr()), &mut len) }; + + match result { + Ok(_) => Ok(OsString::from_wide(&buffer[..len as usize - 1])), + Err(_) => Err(io::Error::last_os_error()), } - Ok(OsString::from_wide(&buffer[..len as usize - 1])) } diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 44f8bb2d13f..8a98b543819 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -62,7 +62,7 @@ tempfile = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] winapi-util = { workspace = true, optional = true } -windows-sys = { workspace = true, optional = true, default-features = false, features = [ +windows = { workspace = true, optional = true, default-features = false, features = [ "Win32_Storage_FileSystem", "Win32_Foundation", "Win32_System_WindowsProgramming", @@ -75,8 +75,8 @@ backup-control = [] colors = [] encoding = ["data-encoding", "data-encoding-macro", "z85", "thiserror"] entries = ["libc"] -fs = ["dunce", "libc", "winapi-util", "windows-sys"] -fsext = ["libc", "time", "windows-sys"] +fs = ["dunce", "libc", "winapi-util", "windows"] +fsext = ["libc", "time", "windows"] lines = [] format = ["itertools"] mode = ["libc"] diff --git a/src/uucore/src/lib/features/fs.rs b/src/uucore/src/lib/features/fs.rs index 41437c55468..6bcc31ef0cd 100644 --- a/src/uucore/src/lib/features/fs.rs +++ b/src/uucore/src/lib/features/fs.rs @@ -78,14 +78,16 @@ impl FileInformation { { use std::fs::OpenOptions; use std::os::windows::prelude::*; + use windows::Win32::Storage::FileSystem::{ + FILE_FLAGS_AND_ATTRIBUTES, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + }; let mut open_options = OpenOptions::new(); - let mut custom_flags = 0; + let mut custom_flags = FILE_FLAGS_AND_ATTRIBUTES(0); if !dereference { - custom_flags |= - windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OPEN_REPARSE_POINT; + custom_flags |= FILE_FLAG_OPEN_REPARSE_POINT; } - custom_flags |= windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS; - open_options.custom_flags(custom_flags); + custom_flags |= FILE_FLAG_BACKUP_SEMANTICS; + open_options.custom_flags(custom_flags.0); let file = open_options.read(true).open(path.as_ref())?; Self::from_file(&file) } diff --git a/src/uucore/src/lib/features/fsext.rs b/src/uucore/src/lib/features/fsext.rs index 9ee5e2464e5..7b7a8d17657 100644 --- a/src/uucore/src/lib/features/fsext.rs +++ b/src/uucore/src/lib/features/fsext.rs @@ -5,49 +5,45 @@ //! Set of functions to manage file systems -// spell-checker:ignore DATETIME getmntinfo subsecond (arch) bitrig ; (fs) cifs smbfs +// spell-checker:ignore DATETIME getmntinfo subsecond (arch) bitrig ; (fs) cifs smbfs HSTRING use time::macros::format_description; use time::UtcOffset; -pub use crate::*; // import macros from `../../macros.rs` - #[cfg(any(target_os = "linux", target_os = "android"))] const LINUX_MTAB: &str = "/etc/mtab"; #[cfg(any(target_os = "linux", target_os = "android"))] const LINUX_MOUNTINFO: &str = "/proc/self/mountinfo"; +#[cfg(unix)] static MOUNT_OPT_BIND: &str = "bind"; + #[cfg(windows)] const MAX_PATH: usize = 266; -#[cfg(not(unix))] +#[cfg(windows)] static EXIT_ERR: i32 = 1; +#[cfg(any( + windows, + target_os = "freebsd", + target_vendor = "apple", + target_os = "netbsd", + target_os = "openbsd" +))] +use crate::crash; #[cfg(windows)] -use std::ffi::OsStr; -#[cfg(windows)] -use std::os::windows::ffi::OsStrExt; +use crate::show_warning; + #[cfg(windows)] -use windows_sys::Win32::Foundation::{ERROR_NO_MORE_FILES, INVALID_HANDLE_VALUE}; +use windows::core::HSTRING; #[cfg(windows)] -use windows_sys::Win32::Storage::FileSystem::{ - FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceW, GetDriveTypeW, - GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, QueryDosDeviceW, +use windows::Win32::{ + Foundation::ERROR_NO_MORE_FILES, + Storage::FileSystem::{ + FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceW, GetDriveTypeW, + GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, QueryDosDeviceW, + }, + System::WindowsProgramming::DRIVE_REMOTE, }; -#[cfg(windows)] -use windows_sys::Win32::System::WindowsProgramming::DRIVE_REMOTE; - -// Warning: the pointer has to be used *immediately* or the Vec -// it points to will be dropped! -#[cfg(windows)] -macro_rules! String2LPWSTR { - ($str: expr) => { - OsStr::new(&$str) - .encode_wide() - .chain(Some(0)) - .collect::>() - .as_ptr() - }; -} #[cfg(windows)] #[allow(non_snake_case)] @@ -61,25 +57,15 @@ use libc::{ mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK, }; use std::borrow::Cow; -use std::convert::{AsRef, From}; -#[cfg(any( - target_vendor = "apple", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "linux", - target_os = "android", - target_os = "illumos", - target_os = "solaris", - target_os = "redox", -))] +use std::convert::From; +#[cfg(unix)] use std::ffi::CStr; -#[cfg(not(windows))] +#[cfg(unix)] use std::ffi::CString; use std::io::Error as IOError; #[cfg(unix)] use std::mem; -#[cfg(not(unix))] +#[cfg(windows)] use std::path::Path; use std::time::UNIX_EPOCH; @@ -145,63 +131,27 @@ impl BirthTime for Metadata { #[derive(Debug, Clone)] pub struct MountInfo { - // it stores `volume_name` in windows platform and `dev_id` in unix platform + /// Stores `volume_name` in windows platform and `dev_id` in unix platform pub dev_id: String, pub dev_name: String, pub fs_type: String, - pub mount_dir: String, - pub mount_option: String, // we only care "bind" option pub mount_root: String, + pub mount_dir: String, + /// We only care whether this field contains "bind" + pub mount_option: String, pub remote: bool, pub dummy: bool, } impl MountInfo { - fn set_missing_fields(&mut self) { - #[cfg(unix)] - { - use std::os::unix::fs::MetadataExt; - // We want to keep the dev_id on Windows - // but set dev_id - if let Ok(stat) = std::fs::metadata(&self.mount_dir) { - // Why do we cast this to i32? - self.dev_id = (stat.dev() as i32).to_string(); - } else { - self.dev_id = String::new(); - } - } - // set MountInfo::dummy - // spell-checker:disable - match self.fs_type.as_ref() { - "autofs" | "proc" | "subfs" - /* for Linux 2.6/3.x */ - | "debugfs" | "devpts" | "fusectl" | "mqueue" | "rpc_pipefs" | "sysfs" - /* FreeBSD, Linux 2.4 */ - | "devfs" - /* for NetBSD 3.0 */ - | "kernfs" - /* for Irix 6.5 */ - | "ignore" => self.dummy = true, - _ => self.dummy = self.fs_type == "none" - && !self.mount_option.contains(MOUNT_OPT_BIND) - } - // spell-checker:enable - // set MountInfo::remote - #[cfg(windows)] - { - self.remote = DRIVE_REMOTE == unsafe { GetDriveTypeW(String2LPWSTR!(self.mount_root)) }; - } - #[cfg(unix)] - { - self.remote = self.dev_name.find(':').is_some() - || (self.dev_name.starts_with("//") && self.fs_type == "smbfs" - || self.fs_type == "cifs") - || self.dev_name == "-hosts"; - } - } - #[cfg(any(target_os = "linux", target_os = "android"))] fn new(file_name: &str, raw: &[&str]) -> Option { + let dev_name; + let fs_type; + let mount_root; + let mount_dir; + let mount_option; + match file_name { // spell-checker:ignore (word) noatime // Format: 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue @@ -211,100 +161,87 @@ impl MountInfo { let after_fields = raw[FIELDS_OFFSET..].iter().position(|c| *c == "-").unwrap() + FIELDS_OFFSET + 1; - let mut m = Self { - dev_id: String::new(), - dev_name: raw[after_fields + 1].to_string(), - fs_type: raw[after_fields].to_string(), - mount_root: raw[3].to_string(), - mount_dir: raw[4].to_string(), - mount_option: raw[5].to_string(), - remote: false, - dummy: false, - }; - m.set_missing_fields(); - Some(m) + dev_name = raw[after_fields + 1].to_string(); + fs_type = raw[after_fields].to_string(); + mount_root = raw[3].to_string(); + mount_dir = raw[4].to_string(); + mount_option = raw[5].to_string(); } LINUX_MTAB => { - let mut m = Self { - dev_id: String::new(), - dev_name: raw[0].to_string(), - fs_type: raw[2].to_string(), - mount_root: String::new(), - mount_dir: raw[1].to_string(), - mount_option: raw[3].to_string(), - remote: false, - dummy: false, - }; - m.set_missing_fields(); - Some(m) + dev_name = raw[0].to_string(); + fs_type = raw[2].to_string(); + mount_root = String::new(); + mount_dir = raw[1].to_string(); + mount_option = raw[3].to_string(); } - _ => None, - } + _ => return None, + }; + + let dev_id = mount_dev_id(&mount_dir); + let dummy = is_dummy_filesystem(&fs_type, &mount_option); + let remote = is_remote_filesystem(&dev_name, &fs_type); + + Some(Self { + dev_id, + dev_name, + fs_type, + mount_dir, + mount_option, + mount_root, + remote, + dummy, + }) } + #[cfg(windows)] fn new(mut volume_name: String) -> Option { let mut dev_name_buf = [0u16; MAX_PATH]; volume_name.pop(); - unsafe { - QueryDosDeviceW( - OsStr::new(&volume_name) - .encode_wide() - .chain(Some(0)) - .skip(4) - .collect::>() - .as_ptr(), - dev_name_buf.as_mut_ptr(), - dev_name_buf.len() as u32, - ) - }; + unsafe { QueryDosDeviceW(&HSTRING::from(&volume_name), Some(&mut dev_name_buf)) }; volume_name.push('\\'); let dev_name = LPWSTR2String(&dev_name_buf); let mut mount_root_buf = [0u16; MAX_PATH]; - let success = unsafe { + let result = unsafe { GetVolumePathNamesForVolumeNameW( - String2LPWSTR!(volume_name), - mount_root_buf.as_mut_ptr(), - mount_root_buf.len() as u32, + &HSTRING::from(&volume_name), + Some(&mut mount_root_buf), ptr::null_mut(), ) }; - if 0 == success { + if result.is_err() { // TODO: support the case when `GetLastError()` returns `ERROR_MORE_DATA` return None; } let mount_root = LPWSTR2String(&mount_root_buf); let mut fs_type_buf = [0u16; MAX_PATH]; - let success = unsafe { + let result = unsafe { GetVolumeInformationW( - String2LPWSTR!(mount_root), - ptr::null_mut(), - 0, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - fs_type_buf.as_mut_ptr(), - fs_type_buf.len() as u32, + &HSTRING::from(&mount_root), + None, + None, + None, + None, + Some(&mut fs_type_buf), ) }; - let fs_type = if 0 == success { + let fs_type = if result.is_err() { None } else { Some(LPWSTR2String(&fs_type_buf)) }; - let mut mn_info = Self { + let remote = DRIVE_REMOTE == unsafe { GetDriveTypeW(&HSTRING::from(&mount_root)) }; + Some(Self { dev_id: volume_name, dev_name, fs_type: fs_type.unwrap_or_default(), mount_root, mount_dir: String::new(), mount_option: String::new(), - remote: false, + remote, dummy: false, - }; - mn_info.set_missing_fields(); - Some(mn_info) + }) } } @@ -316,33 +253,77 @@ impl MountInfo { ))] impl From for MountInfo { fn from(statfs: StatFs) -> Self { - let mut info = Self { - dev_id: String::new(), - dev_name: unsafe { - // spell-checker:disable-next-line - CStr::from_ptr(&statfs.f_mntfromname[0]) - .to_string_lossy() - .into_owned() - }, - fs_type: unsafe { - // spell-checker:disable-next-line - CStr::from_ptr(&statfs.f_fstypename[0]) - .to_string_lossy() - .into_owned() - }, - mount_dir: unsafe { - // spell-checker:disable-next-line - CStr::from_ptr(&statfs.f_mntonname[0]) - .to_string_lossy() - .into_owned() - }, + let dev_name = unsafe { + // spell-checker:disable-next-line + CStr::from_ptr(&statfs.f_mntfromname[0]) + .to_string_lossy() + .into_owned() + }; + let fs_type = unsafe { + // spell-checker:disable-next-line + CStr::from_ptr(&statfs.f_fstypename[0]) + .to_string_lossy() + .into_owned() + }; + let mount_dir = unsafe { + // spell-checker:disable-next-line + CStr::from_ptr(&statfs.f_mntonname[0]) + .to_string_lossy() + .into_owned() + }; + + let dev_id = mount_dev_id(&mount_dir); + let dummy = is_dummy_filesystem(&fs_type, ""); + let remote = is_remote_filesystem(&dev_name, &fs_type); + + Self { + dev_id, + dev_name, + fs_type, + mount_dir, mount_root: String::new(), mount_option: String::new(), - remote: false, - dummy: false, - }; - info.set_missing_fields(); - info + remote, + dummy, + } + } +} + +#[cfg(unix)] +fn is_dummy_filesystem(fs_type: &str, mount_option: &str) -> bool { + // spell-checker:disable + match fs_type { + "autofs" | "proc" | "subfs" + // for Linux 2.6/3.x + | "debugfs" | "devpts" | "fusectl" | "mqueue" | "rpc_pipefs" | "sysfs" + // FreeBSD, Linux 2.4 + | "devfs" + // for NetBSD 3.0 + | "kernfs" + // for Irix 6.5 + | "ignore" => true, + _ => fs_type == "none" + && !mount_option.contains(MOUNT_OPT_BIND) + } + // spell-checker:enable +} + +#[cfg(unix)] +fn is_remote_filesystem(dev_name: &str, fs_type: &str) -> bool { + dev_name.find(':').is_some() + || (dev_name.starts_with("//") && fs_type == "smbfs" || fs_type == "cifs") + || dev_name == "-hosts" +} + +#[cfg(unix)] +fn mount_dev_id(mount_dir: &str) -> String { + use std::os::unix::fs::MetadataExt; + + if let Ok(stat) = std::fs::metadata(mount_dir) { + // Why do we cast this to i32? + (stat.dev() as i32).to_string() + } else { + String::new() } } @@ -438,15 +419,11 @@ pub fn read_fs_list() -> Result, std::io::Error> { { let mut volume_name_buf = [0u16; MAX_PATH]; // As recommended in the MS documentation, retrieve the first volume before the others - let find_handle = - unsafe { FindFirstVolumeW(volume_name_buf.as_mut_ptr(), volume_name_buf.len() as u32) }; - if INVALID_HANDLE_VALUE == find_handle { - crash!( - EXIT_ERR, - "FindFirstVolumeW failed: {}", - IOError::last_os_error() - ); - } + let result = unsafe { FindFirstVolumeW(&mut volume_name_buf) }; + let find_handle = match result { + Ok(h) => h, + Err(err) => crash!(EXIT_ERR, "FindFirstVolumeW failed: {}", err), + }; let mut mounts = Vec::::new(); loop { let volume_name = LPWSTR2String(&volume_name_buf); @@ -457,22 +434,15 @@ pub fn read_fs_list() -> Result, std::io::Error> { if let Some(m) = MountInfo::new(volume_name) { mounts.push(m); } - if 0 == unsafe { - FindNextVolumeW( - find_handle, - volume_name_buf.as_mut_ptr(), - volume_name_buf.len() as u32, - ) - } { - let err = IOError::last_os_error(); - if err.raw_os_error() != Some(ERROR_NO_MORE_FILES as i32) { + if let Err(err) = unsafe { FindNextVolumeW(find_handle, &mut volume_name_buf) } { + if err != ERROR_NO_MORE_FILES.into() { crash!(EXIT_ERR, "FindNextVolumeW failed: {}", err); } break; } } unsafe { - FindVolumeClose(find_handle); + let _ = FindVolumeClose(find_handle); } Ok(mounts) } @@ -552,19 +522,17 @@ impl FsUsage { }; } } - #[cfg(not(unix))] + #[cfg(windows)] pub fn new(path: &Path) -> Self { let mut root_path = [0u16; MAX_PATH]; - let success = unsafe { + let result = unsafe { GetVolumePathNamesForVolumeNameW( - //path_utf8.as_ptr(), - String2LPWSTR!(path.as_os_str()), - root_path.as_mut_ptr(), - root_path.len() as u32, + &HSTRING::from(path), + Some(&mut root_path), ptr::null_mut(), ) }; - if 0 == success { + if result.is_err() { crash!( EXIT_ERR, "GetVolumePathNamesForVolumeNameW failed: {}", @@ -577,16 +545,16 @@ impl FsUsage { let mut number_of_free_clusters = 0; let mut total_number_of_clusters = 0; - let success = unsafe { + let result = unsafe { GetDiskFreeSpaceW( - String2LPWSTR!(path.as_os_str()), - &mut sectors_per_cluster, - &mut bytes_per_sector, - &mut number_of_free_clusters, - &mut total_number_of_clusters, + &HSTRING::from(path), + Some(&mut sectors_per_cluster), + Some(&mut bytes_per_sector), + Some(&mut number_of_free_clusters), + Some(&mut total_number_of_clusters), ) }; - if 0 == success { + if result.is_err() { // Fails in case of CD for example // crash!( // EXIT_ERR, diff --git a/src/uucore/src/lib/lib.rs b/src/uucore/src/lib/lib.rs index dcef26443ea..96aa69d15f7 100644 --- a/src/uucore/src/lib/lib.rs +++ b/src/uucore/src/lib/lib.rs @@ -7,8 +7,8 @@ // * feature-gated external crates (re-shared as public internal modules) #[cfg(feature = "libc")] pub extern crate libc; -#[cfg(all(feature = "windows-sys", target_os = "windows"))] -pub extern crate windows_sys; +#[cfg(all(feature = "windows", target_os = "windows"))] +pub extern crate windows; //## internal modules