mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
fix(df): Fix Windows support
- add required handleapi feature for winapi crate - remove unneeded 'unsafe' section - remove unused import (GetDiskFreeSpaceExW) - fix Windows df execution Co-Authored-By: Roy Ivy III <rivy.dev@gmail.com>
This commit is contained in:
parent
f9456e80c3
commit
2aed7cb035
2 changed files with 146 additions and 61 deletions
|
@ -13,8 +13,8 @@ libc = "0.2.42"
|
|||
uucore = "0.0.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
kernel32-sys = "*"
|
||||
winapi = "*"
|
||||
kernel32-sys = "0.2.2"
|
||||
winapi = { version = "0.3", features = ["handleapi"] }
|
||||
|
||||
[[bin]]
|
||||
name = "df"
|
||||
|
|
145
src/uu/df/df.rs
Executable file → Normal file
145
src/uu/df/df.rs
Executable file → Normal file
|
@ -4,6 +4,7 @@
|
|||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Fangxu Hu <framlog@gmail.com>
|
||||
* (c) Sylvestre Ledru <sylvestre@debian.org>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
|
@ -22,15 +23,19 @@ extern crate winapi;
|
|||
use clap::{App, Arg};
|
||||
#[cfg(windows)]
|
||||
use kernel32::{
|
||||
FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceW, GetDriveTypeW,
|
||||
GetLastError, GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, QueryDosDeviceW,
|
||||
FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDriveTypeW, GetLastError,
|
||||
GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, QueryDosDeviceW,
|
||||
};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::ffi::CString;
|
||||
use std::{env, mem};
|
||||
#[cfg(unix)]
|
||||
use std::mem;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
use libc::c_int;
|
||||
|
@ -52,7 +57,21 @@ use std::fs::File;
|
|||
use std::io::{BufRead, BufReader};
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::prelude::*;
|
||||
use std::ffi::OsString;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
#[cfg(windows)]
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
#[cfg(windows)]
|
||||
use winapi::um::fileapi::GetDiskFreeSpaceW;
|
||||
#[cfg(windows)]
|
||||
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
|
||||
#[cfg(windows)]
|
||||
use winapi::um::winbase::DRIVE_REMOTE;
|
||||
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
|
||||
|
@ -286,7 +305,10 @@ impl Options {
|
|||
|
||||
impl MountInfo {
|
||||
fn set_missing_fields(&mut self) {
|
||||
// set dev_id
|
||||
#[cfg(unix)]
|
||||
{
|
||||
// We want to keep the dev_id on Windows
|
||||
// but set dev_id
|
||||
let path = CString::new(self.mount_dir.clone()).unwrap();
|
||||
unsafe {
|
||||
let mut stat = mem::zeroed();
|
||||
|
@ -296,6 +318,7 @@ impl MountInfo {
|
|||
self.dev_id = "".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
// set MountInfo::dummy
|
||||
match self.fs_type.as_ref() {
|
||||
"autofs" | "proc" | "subfs"
|
||||
|
@ -313,8 +336,7 @@ impl MountInfo {
|
|||
// set MountInfo::remote
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.remote = winapi::winbase::DRIVE_REMOTE
|
||||
== unsafe { GetDriveTypeW(String2LPWSTR!(self.mount_root)) };
|
||||
self.remote = DRIVE_REMOTE == unsafe { GetDriveTypeW(String2LPWSTR!(self.mount_root)) };
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
|
@ -368,7 +390,7 @@ impl MountInfo {
|
|||
fn new(mut volume_name: String) -> Option<MountInfo> {
|
||||
let mut dev_name_buf = [0u16; MAX_PATH];
|
||||
volume_name.pop();
|
||||
let dev_name_len = unsafe {
|
||||
unsafe {
|
||||
QueryDosDeviceW(
|
||||
OsString::from(volume_name.clone())
|
||||
.as_os_str()
|
||||
|
@ -378,7 +400,7 @@ impl MountInfo {
|
|||
.collect::<Vec<u16>>()
|
||||
.as_ptr(),
|
||||
dev_name_buf.as_mut_ptr(),
|
||||
dev_name_buf.len() as winapi::DWORD,
|
||||
dev_name_buf.len() as DWORD,
|
||||
)
|
||||
};
|
||||
volume_name.push('\\');
|
||||
|
@ -389,7 +411,7 @@ impl MountInfo {
|
|||
GetVolumePathNamesForVolumeNameW(
|
||||
String2LPWSTR!(volume_name),
|
||||
mount_root_buf.as_mut_ptr(),
|
||||
mount_root_buf.len() as winapi::DWORD,
|
||||
mount_root_buf.len() as DWORD,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
|
@ -404,12 +426,12 @@ impl MountInfo {
|
|||
GetVolumeInformationW(
|
||||
String2LPWSTR!(mount_root),
|
||||
ptr::null_mut(),
|
||||
0 as winapi::DWORD,
|
||||
0 as DWORD,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
fs_type_buf.as_mut_ptr(),
|
||||
fs_type_buf.len() as winapi::DWORD,
|
||||
fs_type_buf.len() as DWORD,
|
||||
)
|
||||
};
|
||||
let fs_type = if 0 != success {
|
||||
|
@ -417,11 +439,10 @@ impl MountInfo {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut mn_info = MountInfo {
|
||||
dev_id: volume_name,
|
||||
dev_name,
|
||||
fs_type: fs_type.unwrap_or("".to_string()),
|
||||
fs_type: fs_type.unwrap_or_else(|| "".to_string()),
|
||||
mount_root,
|
||||
mount_dir: "".to_string(),
|
||||
mount_option: "".to_string(),
|
||||
|
@ -436,6 +457,7 @@ impl MountInfo {
|
|||
impl FsUsage {
|
||||
#[cfg(unix)]
|
||||
fn new(statvfs: libc::statvfs) -> FsUsage {
|
||||
{
|
||||
FsUsage {
|
||||
blocksize: if statvfs.f_frsize != 0 {
|
||||
statvfs.f_frsize as u64
|
||||
|
@ -450,23 +472,87 @@ impl FsUsage {
|
|||
ffree: statvfs.f_ffree as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
fn new(statvfs: libc::statvfs) -> FsUsage {
|
||||
unimplemented!();
|
||||
fn new(path: &Path) -> FsUsage {
|
||||
let mut root_path = [0u16; MAX_PATH];
|
||||
let success = unsafe {
|
||||
GetVolumePathNamesForVolumeNameW(
|
||||
//path_utf8.as_ptr(),
|
||||
String2LPWSTR!(path.as_os_str()),
|
||||
root_path.as_mut_ptr(),
|
||||
root_path.len() as DWORD,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if 0 == success {
|
||||
crash!(
|
||||
EXIT_ERR,
|
||||
"GetVolumePathNamesForVolumeNameW failed: {}",
|
||||
unsafe { GetLastError() }
|
||||
);
|
||||
}
|
||||
|
||||
let mut sectors_per_cluster = 0;
|
||||
let mut bytes_per_sector = 0;
|
||||
let mut number_of_free_clusters = 0;
|
||||
let mut total_number_of_clusters = 0;
|
||||
|
||||
let success = 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,
|
||||
)
|
||||
};
|
||||
if 0 == success {
|
||||
// Fails in case of CD for example
|
||||
//crash!(EXIT_ERR, "GetDiskFreeSpaceW failed: {}", unsafe {
|
||||
//GetLastError()
|
||||
//});
|
||||
}
|
||||
|
||||
let bytes_per_cluster = sectors_per_cluster as u64 * bytes_per_sector as u64;
|
||||
FsUsage {
|
||||
// f_bsize File system block size.
|
||||
blocksize: bytes_per_cluster as u64,
|
||||
// f_blocks - Total number of blocks on the file system, in units of f_frsize.
|
||||
// frsize = Fundamental file system block size (fragment size).
|
||||
blocks: total_number_of_clusters as u64,
|
||||
// Total number of free blocks.
|
||||
bfree: number_of_free_clusters as u64,
|
||||
// Total number of free blocks available to non-privileged processes.
|
||||
bavail: 0 as u64,
|
||||
bavail_top_bit_set: ((bytes_per_sector as u64) & (1u64.rotate_right(1))) != 0,
|
||||
// Total number of file nodes (inodes) on the file system.
|
||||
files: 0 as u64, // Not available on windows
|
||||
// Total number of free file nodes (inodes).
|
||||
ffree: 4096 as u64, // Meaningless on Windows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filesystem {
|
||||
// TODO: resolve uuid in `mountinfo.dev_name` if exists
|
||||
fn new(mountinfo: MountInfo) -> Option<Filesystem> {
|
||||
let stat_path = if !mountinfo.mount_dir.is_empty() {
|
||||
let _stat_path = if !mountinfo.mount_dir.is_empty() {
|
||||
mountinfo.mount_dir.clone()
|
||||
} else {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
mountinfo.dev_name.clone()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// On windows, we expect the volume id
|
||||
mountinfo.dev_id.clone()
|
||||
}
|
||||
};
|
||||
#[cfg(unix)]
|
||||
unsafe {
|
||||
let path = CString::new(stat_path).unwrap();
|
||||
let path = CString::new(_stat_path).unwrap();
|
||||
let mut statvfs = mem::zeroed();
|
||||
if libc::statvfs(path.as_ptr(), &mut statvfs) < 0 {
|
||||
None
|
||||
|
@ -478,9 +564,10 @@ impl Filesystem {
|
|||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
unimplemented!();
|
||||
}
|
||||
Some(Filesystem {
|
||||
mountinfo,
|
||||
usage: FsUsage::new(Path::new(&_stat_path)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,13 +605,11 @@ fn read_fs_list() -> Vec<MountInfo> {
|
|||
#[cfg(windows)]
|
||||
{
|
||||
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 winapi::DWORD,
|
||||
)
|
||||
FindFirstVolumeW(volume_name_buf.as_mut_ptr(), volume_name_buf.len() as DWORD)
|
||||
};
|
||||
if winapi::shlobj::INVALID_HANDLE_VALUE == find_handle {
|
||||
if INVALID_HANDLE_VALUE == find_handle {
|
||||
crash!(EXIT_ERR, "FindFirstVolumeW failed: {}", unsafe {
|
||||
GetLastError()
|
||||
});
|
||||
|
@ -532,7 +617,7 @@ fn read_fs_list() -> Vec<MountInfo> {
|
|||
let mut mounts = Vec::<MountInfo>::new();
|
||||
loop {
|
||||
let volume_name = LPWSTR2String(&volume_name_buf);
|
||||
if !volume_name.starts_with("\\\\?\\") || !volume_name.ends_with("\\") {
|
||||
if !volume_name.starts_with("\\\\?\\") || !volume_name.ends_with('\\') {
|
||||
show_warning!("A bad path was skipped: {}", volume_name);
|
||||
continue;
|
||||
}
|
||||
|
@ -543,11 +628,11 @@ fn read_fs_list() -> Vec<MountInfo> {
|
|||
FindNextVolumeW(
|
||||
find_handle,
|
||||
volume_name_buf.as_mut_ptr(),
|
||||
volume_name_buf.len() as winapi::DWORD,
|
||||
volume_name_buf.len() as DWORD,
|
||||
)
|
||||
} {
|
||||
let err = unsafe { GetLastError() };
|
||||
if err != winapi::ERROR_NO_MORE_FILES {
|
||||
if err != winapi::shared::winerror::ERROR_NO_MORE_FILES {
|
||||
crash!(EXIT_ERR, "FindNextVolumeW failed: {}", err);
|
||||
}
|
||||
break;
|
||||
|
@ -556,7 +641,7 @@ fn read_fs_list() -> Vec<MountInfo> {
|
|||
unsafe {
|
||||
FindVolumeClose(find_handle);
|
||||
}
|
||||
return mounts;
|
||||
mounts
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue