mirror of
https://github.com/uutils/coreutils
synced 2025-01-07 10:49:09 +00:00
Merge pull request #5680 from tertsdiepraam/fsext-refactor
Fsext refactor
This commit is contained in:
commit
6510115d9e
1 changed files with 156 additions and 144 deletions
|
@ -10,44 +10,41 @@
|
||||||
use time::macros::format_description;
|
use time::macros::format_description;
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
|
|
||||||
pub use crate::*; // import macros from `../../macros.rs`
|
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
const LINUX_MTAB: &str = "/etc/mtab";
|
const LINUX_MTAB: &str = "/etc/mtab";
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
const LINUX_MOUNTINFO: &str = "/proc/self/mountinfo";
|
const LINUX_MOUNTINFO: &str = "/proc/self/mountinfo";
|
||||||
|
#[cfg(unix)]
|
||||||
static MOUNT_OPT_BIND: &str = "bind";
|
static MOUNT_OPT_BIND: &str = "bind";
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const MAX_PATH: usize = 266;
|
const MAX_PATH: usize = 266;
|
||||||
#[cfg(not(unix))]
|
#[cfg(windows)]
|
||||||
static EXIT_ERR: i32 = 1;
|
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 crate::show_warning;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows_sys::Win32::Foundation::{ERROR_NO_MORE_FILES, INVALID_HANDLE_VALUE};
|
use windows_sys::Win32::{
|
||||||
#[cfg(windows)]
|
Foundation::{ERROR_NO_MORE_FILES, INVALID_HANDLE_VALUE},
|
||||||
use windows_sys::Win32::Storage::FileSystem::{
|
Storage::FileSystem::{
|
||||||
FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceW, GetDriveTypeW,
|
FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceW, GetDriveTypeW,
|
||||||
GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, QueryDosDeviceW,
|
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::<Vec<u16>>()
|
|
||||||
.as_ptr()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -56,30 +53,28 @@ fn LPWSTR2String(buf: &[u16]) -> String {
|
||||||
String::from_utf16(&buf[..len]).unwrap()
|
String::from_utf16(&buf[..len]).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn to_nul_terminated_wide_string(s: impl AsRef<OsStr>) -> Vec<u16> {
|
||||||
|
s.as_ref()
|
||||||
|
.encode_wide()
|
||||||
|
.chain(Some(0))
|
||||||
|
.collect::<Vec<u16>>()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use libc::{
|
use libc::{
|
||||||
mode_t, strerror, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK,
|
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::borrow::Cow;
|
||||||
use std::convert::{AsRef, From};
|
use std::convert::From;
|
||||||
#[cfg(any(
|
#[cfg(unix)]
|
||||||
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::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
#[cfg(not(windows))]
|
#[cfg(unix)]
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::io::Error as IOError;
|
use std::io::Error as IOError;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::mem;
|
use std::mem;
|
||||||
#[cfg(not(unix))]
|
#[cfg(windows)]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
@ -145,63 +140,27 @@ impl BirthTime for Metadata {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MountInfo {
|
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_id: String,
|
||||||
pub dev_name: String,
|
pub dev_name: String,
|
||||||
pub fs_type: String,
|
pub fs_type: String,
|
||||||
pub mount_dir: String,
|
|
||||||
pub mount_option: String, // we only care "bind" option
|
|
||||||
pub mount_root: String,
|
pub mount_root: String,
|
||||||
|
pub mount_dir: String,
|
||||||
|
/// We only care whether this field contains "bind"
|
||||||
|
pub mount_option: String,
|
||||||
pub remote: bool,
|
pub remote: bool,
|
||||||
pub dummy: bool,
|
pub dummy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MountInfo {
|
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"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
fn new(file_name: &str, raw: &[&str]) -> Option<Self> {
|
fn new(file_name: &str, raw: &[&str]) -> Option<Self> {
|
||||||
|
let dev_name;
|
||||||
|
let fs_type;
|
||||||
|
let mount_root;
|
||||||
|
let mount_dir;
|
||||||
|
let mount_option;
|
||||||
|
|
||||||
match file_name {
|
match file_name {
|
||||||
// spell-checker:ignore (word) noatime
|
// spell-checker:ignore (word) noatime
|
||||||
// Format: 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
// Format: 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||||
|
@ -211,36 +170,38 @@ impl MountInfo {
|
||||||
let after_fields = raw[FIELDS_OFFSET..].iter().position(|c| *c == "-").unwrap()
|
let after_fields = raw[FIELDS_OFFSET..].iter().position(|c| *c == "-").unwrap()
|
||||||
+ FIELDS_OFFSET
|
+ FIELDS_OFFSET
|
||||||
+ 1;
|
+ 1;
|
||||||
let mut m = Self {
|
dev_name = raw[after_fields + 1].to_string();
|
||||||
dev_id: String::new(),
|
fs_type = raw[after_fields].to_string();
|
||||||
dev_name: raw[after_fields + 1].to_string(),
|
mount_root = raw[3].to_string();
|
||||||
fs_type: raw[after_fields].to_string(),
|
mount_dir = raw[4].to_string();
|
||||||
mount_root: raw[3].to_string(),
|
mount_option = raw[5].to_string();
|
||||||
mount_dir: raw[4].to_string(),
|
|
||||||
mount_option: raw[5].to_string(),
|
|
||||||
remote: false,
|
|
||||||
dummy: false,
|
|
||||||
};
|
|
||||||
m.set_missing_fields();
|
|
||||||
Some(m)
|
|
||||||
}
|
}
|
||||||
LINUX_MTAB => {
|
LINUX_MTAB => {
|
||||||
let mut m = Self {
|
dev_name = raw[0].to_string();
|
||||||
dev_id: String::new(),
|
fs_type = raw[2].to_string();
|
||||||
dev_name: raw[0].to_string(),
|
mount_root = String::new();
|
||||||
fs_type: raw[2].to_string(),
|
mount_dir = raw[1].to_string();
|
||||||
mount_root: String::new(),
|
mount_option = raw[3].to_string();
|
||||||
mount_dir: raw[1].to_string(),
|
}
|
||||||
mount_option: raw[3].to_string(),
|
_ => return None,
|
||||||
remote: false,
|
|
||||||
dummy: false,
|
|
||||||
};
|
};
|
||||||
m.set_missing_fields();
|
|
||||||
Some(m)
|
let dev_id = mount_dev_id(&mount_dir);
|
||||||
}
|
let dummy = is_dummy_filesystem(&fs_type, &mount_option);
|
||||||
_ => None,
|
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)]
|
#[cfg(windows)]
|
||||||
fn new(mut volume_name: String) -> Option<Self> {
|
fn new(mut volume_name: String) -> Option<Self> {
|
||||||
let mut dev_name_buf = [0u16; MAX_PATH];
|
let mut dev_name_buf = [0u16; MAX_PATH];
|
||||||
|
@ -262,8 +223,9 @@ impl MountInfo {
|
||||||
|
|
||||||
let mut mount_root_buf = [0u16; MAX_PATH];
|
let mut mount_root_buf = [0u16; MAX_PATH];
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
|
let volume_name = to_nul_terminated_wide_string(&volume_name);
|
||||||
GetVolumePathNamesForVolumeNameW(
|
GetVolumePathNamesForVolumeNameW(
|
||||||
String2LPWSTR!(volume_name),
|
volume_name.as_ptr(),
|
||||||
mount_root_buf.as_mut_ptr(),
|
mount_root_buf.as_mut_ptr(),
|
||||||
mount_root_buf.len() as u32,
|
mount_root_buf.len() as u32,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
@ -277,8 +239,9 @@ impl MountInfo {
|
||||||
|
|
||||||
let mut fs_type_buf = [0u16; MAX_PATH];
|
let mut fs_type_buf = [0u16; MAX_PATH];
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
|
let mount_root = to_nul_terminated_wide_string(&mount_root);
|
||||||
GetVolumeInformationW(
|
GetVolumeInformationW(
|
||||||
String2LPWSTR!(mount_root),
|
mount_root.as_ptr(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
@ -293,18 +256,21 @@ impl MountInfo {
|
||||||
} else {
|
} else {
|
||||||
Some(LPWSTR2String(&fs_type_buf))
|
Some(LPWSTR2String(&fs_type_buf))
|
||||||
};
|
};
|
||||||
let mut mn_info = Self {
|
let remote = DRIVE_REMOTE
|
||||||
|
== unsafe {
|
||||||
|
let mount_root = to_nul_terminated_wide_string(&mount_root);
|
||||||
|
GetDriveTypeW(mount_root.as_ptr())
|
||||||
|
};
|
||||||
|
Some(Self {
|
||||||
dev_id: volume_name,
|
dev_id: volume_name,
|
||||||
dev_name,
|
dev_name,
|
||||||
fs_type: fs_type.unwrap_or_default(),
|
fs_type: fs_type.unwrap_or_default(),
|
||||||
mount_root,
|
mount_root,
|
||||||
mount_dir: String::new(),
|
mount_dir: String::new(),
|
||||||
mount_option: String::new(),
|
mount_option: String::new(),
|
||||||
remote: false,
|
remote,
|
||||||
dummy: false,
|
dummy: false,
|
||||||
};
|
})
|
||||||
mn_info.set_missing_fields();
|
|
||||||
Some(mn_info)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,33 +282,77 @@ impl MountInfo {
|
||||||
))]
|
))]
|
||||||
impl From<StatFs> for MountInfo {
|
impl From<StatFs> for MountInfo {
|
||||||
fn from(statfs: StatFs) -> Self {
|
fn from(statfs: StatFs) -> Self {
|
||||||
let mut info = Self {
|
let dev_name = unsafe {
|
||||||
dev_id: String::new(),
|
|
||||||
dev_name: unsafe {
|
|
||||||
// spell-checker:disable-next-line
|
// spell-checker:disable-next-line
|
||||||
CStr::from_ptr(&statfs.f_mntfromname[0])
|
CStr::from_ptr(&statfs.f_mntfromname[0])
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned()
|
.into_owned()
|
||||||
},
|
};
|
||||||
fs_type: unsafe {
|
let fs_type = unsafe {
|
||||||
// spell-checker:disable-next-line
|
// spell-checker:disable-next-line
|
||||||
CStr::from_ptr(&statfs.f_fstypename[0])
|
CStr::from_ptr(&statfs.f_fstypename[0])
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned()
|
.into_owned()
|
||||||
},
|
};
|
||||||
mount_dir: unsafe {
|
let mount_dir = unsafe {
|
||||||
// spell-checker:disable-next-line
|
// spell-checker:disable-next-line
|
||||||
CStr::from_ptr(&statfs.f_mntonname[0])
|
CStr::from_ptr(&statfs.f_mntonname[0])
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned()
|
.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_root: String::new(),
|
||||||
mount_option: String::new(),
|
mount_option: String::new(),
|
||||||
remote: false,
|
remote,
|
||||||
dummy: false,
|
dummy,
|
||||||
};
|
}
|
||||||
info.set_missing_fields();
|
}
|
||||||
info
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,13 +562,14 @@ impl FsUsage {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(unix))]
|
#[cfg(windows)]
|
||||||
pub fn new(path: &Path) -> Self {
|
pub fn new(path: &Path) -> Self {
|
||||||
let mut root_path = [0u16; MAX_PATH];
|
let mut root_path = [0u16; MAX_PATH];
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
|
let path = to_nul_terminated_wide_string(path);
|
||||||
GetVolumePathNamesForVolumeNameW(
|
GetVolumePathNamesForVolumeNameW(
|
||||||
//path_utf8.as_ptr(),
|
//path_utf8.as_ptr(),
|
||||||
String2LPWSTR!(path.as_os_str()),
|
path.as_ptr(),
|
||||||
root_path.as_mut_ptr(),
|
root_path.as_mut_ptr(),
|
||||||
root_path.len() as u32,
|
root_path.len() as u32,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
@ -578,8 +589,9 @@ impl FsUsage {
|
||||||
let mut total_number_of_clusters = 0;
|
let mut total_number_of_clusters = 0;
|
||||||
|
|
||||||
let success = unsafe {
|
let success = unsafe {
|
||||||
|
let path = to_nul_terminated_wide_string(path);
|
||||||
GetDiskFreeSpaceW(
|
GetDiskFreeSpaceW(
|
||||||
String2LPWSTR!(path.as_os_str()),
|
path.as_ptr(),
|
||||||
&mut sectors_per_cluster,
|
&mut sectors_per_cluster,
|
||||||
&mut bytes_per_sector,
|
&mut bytes_per_sector,
|
||||||
&mut number_of_free_clusters,
|
&mut number_of_free_clusters,
|
||||||
|
|
Loading…
Reference in a new issue