refactor(utils/os): Optimize Windows OS utilities and add safety comments (#1671)

Co-authored-by: weisd <weishidavip@163.com>
This commit is contained in:
houseme
2026-01-30 16:13:42 +08:00
committed by GitHub
parent dce117840c
commit 90ed75a3dc
10 changed files with 347 additions and 240 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{DiskInfo, IOStats};
use crate::os::{DiskInfo, IOStats};
use rustix::fs::statfs;
use std::fs::File;
use std::io::{self, BufRead, Error, ErrorKind};
+1 -1
View File
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{DiskInfo, IOStats};
use crate::os::{DiskInfo, IOStats};
use rustix::fs::{StatVfs, statvfs};
use std::io::Error;
use std::path::Path;
+28 -9
View File
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::{DiskInfo, IOStats};
#![allow(unsafe_code)] // TODO: audit unsafe code
use crate::os::{DiskInfo, IOStats};
use std::io::Error;
use std::path::Path;
use windows::Win32::Foundation::MAX_PATH;
@@ -20,17 +22,15 @@ use windows::Win32::Storage::FileSystem::{GetDiskFreeSpaceExW, GetDiskFreeSpaceW
/// Returns total and free bytes available in a directory, e.g. `C:\`.
pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
let path_wide = p
.as_ref()
.to_string_lossy()
.encode_utf16()
.chain(std::iter::once(0))
.collect::<Vec<u16>>();
let path_wide = to_wide_path(p.as_ref());
let mut free_bytes_available = 0u64;
let mut total_number_of_bytes = 0u64;
let mut total_number_of_free_bytes = 0u64;
// SAFETY:
// 1. `path_wide` is a valid null-terminated UTF-16 string.
// 2. Pointers to `u64` variables are valid and point to initialized stack memory.
unsafe {
GetDiskFreeSpaceExW(
windows::core::PCWSTR::from_raw(path_wide.as_ptr()),
@@ -56,6 +56,9 @@ pub fn get_info(p: impl AsRef<Path>) -> std::io::Result<DiskInfo> {
let mut number_of_free_clusters = 0u32;
let mut total_number_of_clusters = 0u32;
// SAFETY:
// 1. `path_wide` is a valid null-terminated UTF-16 string.
// 2. Pointers to `u32` variables are valid and point to initialized stack memory.
unsafe {
GetDiskFreeSpaceW(
windows::core::PCWSTR::from_raw(path_wide.as_ptr()),
@@ -87,6 +90,10 @@ fn get_windows_fs_type(p: &[u16]) -> std::io::Result<String> {
let mut volume_name_buffer = [0u16; MAX_PATH as usize];
let mut file_system_name_buffer = [0u16; MAX_PATH as usize];
// SAFETY:
// 1. `path` is a valid null-terminated UTF-16 string (volume root path).
// 2. Buffers are allocated with `MAX_PATH` size, which is sufficient for standard Windows paths.
// 3. Pointers to output variables are valid.
unsafe {
GetVolumeInformationW(
windows::core::PCWSTR::from_raw(path.as_ptr()),
@@ -105,6 +112,11 @@ fn get_windows_fs_type(p: &[u16]) -> std::io::Result<String> {
fn get_volume_name(v: &[u16]) -> std::io::Result<Vec<u16>> {
let mut volume_name_buffer = [0u16; MAX_PATH as usize];
// SAFETY:
// 1. `v` is a valid null-terminated UTF-16 string.
// 2. `volume_name_buffer` is allocated with `MAX_PATH` size.
// 3. `GetVolumePathNameW` writes to the buffer and respects the buffer size (implicitly MAX_PATH for this API context usually, though explicit length param isn't present, it expects a buffer large enough).
// Note: GetVolumePathNameW documentation says "The buffer should be large enough to hold the path". MAX_PATH is generally safe for volume roots.
unsafe {
GetVolumePathNameW(windows::core::PCWSTR::from_raw(v.as_ptr()), &mut volume_name_buffer)
.map_err(|e| Error::from_raw_os_error(e.code().0 as i32))?;
@@ -122,9 +134,16 @@ fn utf16_to_string(v: &[u16]) -> String {
String::from_utf16_lossy(&v[..len])
}
fn to_wide_path(path: &Path) -> Vec<u16> {
path.as_os_str().encode_wide().chain(std::iter::once(0)).collect()
}
// Helper trait to access encode_wide which is only available on Windows
use std::os::windows::ffi::OsStrExt;
pub fn same_disk(disk1: &str, disk2: &str) -> std::io::Result<bool> {
let path1_wide: Vec<u16> = disk1.encode_utf16().chain(std::iter::once(0)).collect();
let path2_wide: Vec<u16> = disk2.encode_utf16().chain(std::iter::once(0)).collect();
let path1_wide = to_wide_path(Path::new(disk1));
let path2_wide = to_wide_path(Path::new(disk2));
let volume1 = get_volume_name(&path1_wide)?;
let volume2 = get_volume_name(&path2_wide)?;