use {
crate::*,
lazy_regex::*,
snafu::prelude::*,
std::{
path::PathBuf,
str::FromStr,
},
};
static REMOTE_ONLY_FS_TYPES: &[&str] = &["afs", "coda", "auristorfs", "fhgfs", "gpfs", "ibrix", "ocfs2", "vxfs"];
pub type MountId = u32;
#[derive(Debug, Clone)]
pub struct MountInfo {
pub id: MountId,
pub parent: MountId,
pub dev: DeviceId,
pub root: PathBuf,
pub mount_point: PathBuf,
pub fs: String,
pub fs_type: String,
pub bound: bool,
}
impl MountInfo {
pub fn dm_name(&self) -> Option<&str> {
regex_captures!(r#"^/dev/mapper/([^/]+)$"#, &self.fs)
.map(|(_, dm_name)| dm_name)
}
pub fn fs_name(&self) -> Option<&str> {
regex_find!(r#"[^\\/]+$"#, &self.fs)
}
pub fn is_remote(&self) -> bool {
self.fs.contains(':')
|| (
self.fs.starts_with("//")
&& ["cifs", "smb3", "smbfs"].contains(&self.fs_type.as_ref())
)
|| REMOTE_ONLY_FS_TYPES.contains(&self.fs_type.as_ref())
|| self.fs == "-hosts"
}
}
#[derive(Debug, Snafu)]
#[snafu(display("Could not parse {line} as mount info"))]
pub struct ParseMountInfoError {
line: String,
}
impl FromStr for MountInfo {
type Err = ParseMountInfoError;
fn from_str(line: &str) -> Result<Self, Self::Err> {
(|| {
let mut tokens = line.split_whitespace();
let id = tokens.next()?.parse().ok()?;
let parent = tokens.next()?.parse().ok()?;
let dev = tokens.next()?.parse().ok()?;
let root = str_to_pathbuf(tokens.next()?);
let mount_point = str_to_pathbuf(tokens.next()?);
loop {
let token = tokens.next()?;
if token == "-" {
break;
}
};
let fs_type = tokens.next()?.to_string();
let fs = tokens.next()?.to_string();
Some(Self {
id,
parent,
dev,
root,
mount_point,
fs,
fs_type,
bound: false, })
})().with_context(|| ParseMountInfoSnafu { line })
}
}
fn str_to_pathbuf(s: &str) -> PathBuf {
PathBuf::from(sys::decode_string(s))
}
pub fn read_mountinfo() -> Result<Vec<MountInfo>, Error> {
let mut mounts: Vec<MountInfo> = Vec::new();
let path = "/proc/self/mountinfo";
let file_content = sys::read_file(path)
.context(CantReadDirSnafu { path })?;
for line in file_content.trim().split('\n') {
let mut mount: MountInfo = line.parse()
.map_err(|source| Error::ParseMountInfo { source })?;
mount.bound = mounts.iter().any(|m| m.dev == mount.dev);
mounts.push(mount);
}
Ok(mounts)
}
#[test]
fn test_from_str() {
let mi = MountInfo::from_str(
"47 21 0:41 / /dev/hugepages rw,relatime shared:27 - hugetlbfs hugetlbfs rw,pagesize=2M"
).unwrap();
assert_eq!(mi.id, 47);
assert_eq!(mi.dev, DeviceId::new(0, 41));
assert_eq!(mi.root, PathBuf::from("/"));
assert_eq!(mi.mount_point, PathBuf::from("/dev/hugepages"));
let mi = MountInfo::from_str(
"106 26 8:17 / /home/dys/dev rw,relatime shared:57 - xfs /dev/sdb1 rw,attr2,inode64,noquota"
).unwrap();
assert_eq!(mi.id, 106);
assert_eq!(mi.dev, DeviceId::new(8, 17));
assert_eq!(&mi.fs, "/dev/sdb1");
assert_eq!(&mi.fs_type, "xfs");
}