mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
stat: implement support for macos
This commit is contained in:
parent
6908413b00
commit
56761ba584
4 changed files with 205 additions and 10 deletions
|
@ -17,6 +17,7 @@ path = "src/stat.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33"
|
clap = "2.33"
|
||||||
time = "0.1.40"
|
time = "0.1.40"
|
||||||
|
libc = "0.2"
|
||||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "libc"] }
|
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "libc"] }
|
||||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,12 @@
|
||||||
|
|
||||||
extern crate time;
|
extern crate time;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
static LINUX_MTAB: &str = "/etc/mtab";
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
static LINUX_MOUNTINFO: &str = "/proc/self/mountinfo";
|
||||||
|
static MOUNT_OPT_BIND: &str = "bind";
|
||||||
|
|
||||||
use self::time::Timespec;
|
use self::time::Timespec;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
pub use uucore::libc::{
|
pub use uucore::libc::{
|
||||||
|
@ -413,3 +419,184 @@ pub fn pretty_fstype<'a>(fstype: i64) -> Cow<'a, str> {
|
||||||
other => format!("UNKNOWN ({:#x})", other).into(),
|
other => format!("UNKNOWN ({:#x})", other).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "freebsd", target_vendor = "apple"))]
|
||||||
|
extern "C" {
|
||||||
|
#[cfg(all(target_vendor = "apple", target_arch = "x86_64"))]
|
||||||
|
#[link_name = "getmntinfo$INODE64"]
|
||||||
|
fn getmntinfo(mntbufp: *mut *mut Sstatfs, flags: c_int) -> c_int;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
all(target_os = "freebsd"),
|
||||||
|
all(target_vendor = "apple", target_arch = "aarch64")
|
||||||
|
))]
|
||||||
|
fn getmntinfo(mntbufp: *mut *mut Sstatfs, flags: c_int) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MountInfo {
|
||||||
|
// it stores `volume_name` in windows platform and `dev_id` in unix platform
|
||||||
|
dev_id: String,
|
||||||
|
dev_name: String,
|
||||||
|
fs_type: String,
|
||||||
|
pub mount_dir: String,
|
||||||
|
mount_option: String, // we only care "bind" option
|
||||||
|
mount_root: String,
|
||||||
|
remote: bool,
|
||||||
|
dummy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MountInfo {
|
||||||
|
fn set_missing_fields(&mut self) {
|
||||||
|
#[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();
|
||||||
|
if libc::stat(path.as_ptr(), &mut stat) == 0 {
|
||||||
|
self.dev_id = (stat.st_dev as i32).to_string();
|
||||||
|
} else {
|
||||||
|
self.dev_id = "".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set MountInfo::dummy
|
||||||
|
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.find(MOUNT_OPT_BIND).is_none(),
|
||||||
|
}
|
||||||
|
// set MountInfo::remote
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
if self.dev_name.find(':').is_some()
|
||||||
|
|| (self.dev_name.starts_with("//") && self.fs_type == "smbfs"
|
||||||
|
|| self.fs_type == "cifs")
|
||||||
|
|| self.dev_name == "-hosts"
|
||||||
|
{
|
||||||
|
self.remote = true;
|
||||||
|
} else {
|
||||||
|
self.remote = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn new(file_name: &str, raw: Vec<&str>) -> Option<MountInfo> {
|
||||||
|
match file_name {
|
||||||
|
// Format: 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
|
||||||
|
// "man proc" for more details
|
||||||
|
"/proc/self/mountinfo" => {
|
||||||
|
let mut m = MountInfo {
|
||||||
|
dev_id: "".to_string(),
|
||||||
|
dev_name: raw[9].to_string(),
|
||||||
|
fs_type: raw[8].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)
|
||||||
|
}
|
||||||
|
"/etc/mtab" => {
|
||||||
|
let mut m = MountInfo {
|
||||||
|
dev_id: "".to_string(),
|
||||||
|
dev_name: raw[0].to_string(),
|
||||||
|
fs_type: raw[2].to_string(),
|
||||||
|
mount_root: "".to_string(),
|
||||||
|
mount_dir: raw[1].to_string(),
|
||||||
|
mount_option: raw[3].to_string(),
|
||||||
|
remote: false,
|
||||||
|
dummy: false,
|
||||||
|
};
|
||||||
|
m.set_missing_fields();
|
||||||
|
Some(m)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
|
||||||
|
use std::ffi::CStr;
|
||||||
|
#[cfg(any(target_os = "freebsd", target_vendor = "apple"))]
|
||||||
|
impl From<Sstatfs> for MountInfo {
|
||||||
|
fn from(statfs: Sstatfs) -> Self {
|
||||||
|
let mut info = MountInfo {
|
||||||
|
dev_id: "".to_string(),
|
||||||
|
dev_name: unsafe {
|
||||||
|
CStr::from_ptr(&statfs.f_mntfromname[0])
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
},
|
||||||
|
fs_type: unsafe {
|
||||||
|
CStr::from_ptr(&statfs.f_fstypename[0])
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
},
|
||||||
|
mount_dir: unsafe {
|
||||||
|
CStr::from_ptr(&statfs.f_mntonname[0])
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
},
|
||||||
|
mount_root: "".to_string(),
|
||||||
|
mount_option: "".to_string(),
|
||||||
|
remote: false,
|
||||||
|
dummy: false,
|
||||||
|
};
|
||||||
|
info.set_missing_fields();
|
||||||
|
info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::fs::File;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use std::io::{BufRead, BufReader};
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
|
||||||
|
use std::ptr;
|
||||||
|
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
|
||||||
|
use std::slice;
|
||||||
|
pub fn read_fs_list() -> Vec<MountInfo> {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
let (file_name, fobj) = File::open(LINUX_MOUNTINFO)
|
||||||
|
.map(|f| (LINUX_MOUNTINFO, f))
|
||||||
|
.or_else(|_| File::open(LINUX_MTAB).map(|f| (LINUX_MTAB, f)))
|
||||||
|
.expect("failed to find mount list files");
|
||||||
|
let reader = BufReader::new(fobj);
|
||||||
|
reader
|
||||||
|
.lines()
|
||||||
|
.filter_map(|line| line.ok())
|
||||||
|
.filter_map(|line| {
|
||||||
|
let raw_data = line.split_whitespace().collect::<Vec<&str>>();
|
||||||
|
MountInfo::new(file_name, raw_data)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
#[cfg(any(target_os = "freebsd", target_vendor = "apple"))]
|
||||||
|
{
|
||||||
|
let mut mptr: *mut Sstatfs = ptr::null_mut();
|
||||||
|
let len = unsafe { getmntinfo(&mut mptr, 1 as c_int) };
|
||||||
|
if len < 0 {
|
||||||
|
crash!(1, "getmntinfo failed");
|
||||||
|
}
|
||||||
|
let mounts = unsafe { slice::from_raw_parts(mptr, len as usize) };
|
||||||
|
mounts
|
||||||
|
.iter()
|
||||||
|
.map(|m| MountInfo::from(*m))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@ use uucore::entries;
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{cmp, fs, iter};
|
use std::{cmp, fs, iter};
|
||||||
|
@ -97,7 +95,6 @@ pub mod options {
|
||||||
|
|
||||||
static ARG_FILES: &str = "files";
|
static ARG_FILES: &str = "files";
|
||||||
|
|
||||||
const MOUNT_INFO: &str = "/etc/mtab";
|
|
||||||
pub const F_ALTER: u8 = 1;
|
pub const F_ALTER: u8 = 1;
|
||||||
pub const F_ZERO: u8 = 1 << 1;
|
pub const F_ZERO: u8 = 1 << 1;
|
||||||
pub const F_LEFT: u8 = 1 << 2;
|
pub const F_LEFT: u8 = 1 << 2;
|
||||||
|
@ -490,13 +487,9 @@ impl Stater {
|
||||||
// mount points aren't displayed when showing filesystem information
|
// mount points aren't displayed when showing filesystem information
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let reader = BufReader::new(
|
let mut mount_list = read_fs_list()
|
||||||
File::open(MOUNT_INFO).unwrap_or_else(|_| panic!("Failed to read {}", MOUNT_INFO)),
|
.iter()
|
||||||
);
|
.map(|mi| mi.mount_dir.clone())
|
||||||
let mut mount_list = reader
|
|
||||||
.lines()
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.filter_map(|line| line.split_whitespace().nth(1).map(ToOwned::to_owned))
|
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
// Reverse sort. The longer comes first.
|
// Reverse sort. The longer comes first.
|
||||||
mount_list.sort();
|
mount_list.sort();
|
||||||
|
|
|
@ -317,6 +317,20 @@ fn test_multi_files() {
|
||||||
.stdout_is(expected_result(&args));
|
.stdout_is(expected_result(&args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "linux", target_os = "freebsd", target_vendor = "apple"))]
|
||||||
|
#[test]
|
||||||
|
fn test_one_file() {
|
||||||
|
let (at, mut ucmd) = at_and_ucmd!();
|
||||||
|
let file = "TEST_FILE.mp4";
|
||||||
|
at.touch(file);
|
||||||
|
|
||||||
|
ucmd.arg(file)
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains(format!("File: `{}'", file))
|
||||||
|
.stdout_contains(format!("Size: 0"))
|
||||||
|
.stdout_contains(format!("Access: (0644/-rw-r--r--)"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn test_printf() {
|
fn test_printf() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue