diff --git a/examples/list-mount.rs b/examples/list-mount.rs
index bc83bce..613fa33 100644
--- a/examples/list-mount.rs
+++ b/examples/list-mount.rs
@@ -14,7 +14,7 @@
extern crate mnt;
-use mnt::get_mount;
+use mnt::mount::get_mount;
use std::env::args;
use std::path::{Path, PathBuf};
diff --git a/examples/list-submounts.rs b/examples/list-submounts.rs
index 5391991..c77b696 100644
--- a/examples/list-submounts.rs
+++ b/examples/list-submounts.rs
@@ -14,7 +14,7 @@
extern crate mnt;
-use mnt::{get_submounts, VecMountEntry};
+use mnt::mount::{get_submounts, VecMountEntry};
use std::env::args;
use std::path::{Path, PathBuf};
diff --git a/src/error.rs b/src/error.rs
index aa00df1..1ec97e6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -61,21 +61,45 @@ pub enum LineError {
InvalidFreq(String),
MissingPassno,
InvalidPassno(String),
+ MissingId,
+ InvalidId(String),
+ MissingParentId,
+ InvalidParentId(String),
+ MissingMajMin,
+ InvalidMajMin(String),
+ MissingRoot,
+ InvalidRoot(String),
+ MissingOptional,
+ InvalidOptional(String),
+ MissingSuperOptions,
+ InvalidSuperOptions(String),
}
impl fmt::Display for LineError {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
let desc: Cow<_> = match *self {
- LineError::MissingSpec => "Missing field #1 (spec)".into(),
- LineError::MissingFile => "Missing field #2 (file)".into(),
- LineError::InvalidFilePath(ref f) => format!("Bad field #2 (file) value (not absolute path): {}", f).into(),
- LineError::InvalidFile(ref f) => format!("Bad field #2 (file) value: {}", f).into(),
- LineError::MissingVfstype => "Missing field #3 (vfstype)".into(),
- LineError::MissingMntops => "Missing field #4 (mntops)".into(),
- LineError::MissingFreq => "Missing field #5 (freq)".into(),
- LineError::InvalidFreq(ref f) => format!("Bad field #5 (dump) value: {}", f).into(),
- LineError::MissingPassno => "Missing field #6 (passno)".into(),
- LineError::InvalidPassno(ref f) => format!("Bad field #6 (passno) value: {}", f).into(),
+ LineError::MissingSpec => "Missing field: spec".into(),
+ LineError::MissingFile => "Missing field: file".into(),
+ LineError::InvalidFilePath(ref f) => format!("Bad 'file' field value (not absolute path): {}", f).into(),
+ LineError::InvalidFile(ref f) => format!("Bad 'file' field value: {}", f).into(),
+ LineError::MissingVfstype => "Missing field: vfstype".into(),
+ LineError::MissingMntops => "Missing field: mntops".into(),
+ LineError::MissingFreq => "Missing field: freq".into(),
+ LineError::InvalidFreq(ref f) => format!("Bad 'dump' field value: {}", f).into(),
+ LineError::MissingPassno => "Missing field: passno".into(),
+ LineError::InvalidPassno(ref f) => format!("Bad 'passno' field value: {}", f).into(),
+ LineError::MissingId => "Missing field: id".into(),
+ LineError::InvalidId(ref f) => format!("Bad 'id' field value: {}", f).into(),
+ LineError::MissingParentId => "Missing field: parent id".into(),
+ LineError::InvalidParentId(ref f) => format!("Bad 'parent id' field value: {}", f).into(),
+ LineError::MissingMajMin => "Missing field: maj:min".into(),
+ LineError::InvalidMajMin(ref f) => format!("Bad 'maj:min' field value: {}", f).into(),
+ LineError::MissingRoot => "Missing field: root".into(),
+ LineError::InvalidRoot(ref f) => format!("Bad 'root' field value: {}", f).into(),
+ LineError::MissingOptional => "Missing field: optional".into(),
+ LineError::InvalidOptional(ref f) => format!("Bad 'optional' field value: {}", f).into(),
+ LineError::MissingSuperOptions => "Missing field: superoptions".into(),
+ LineError::InvalidSuperOptions(ref f) => format!("Bad 'superoptions' field value: {}", f).into(),
};
write!(out, "Line parsing: {}", desc)
}
diff --git a/src/lib.rs b/src/lib.rs
index 696ee07..4728ab5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,7 +13,46 @@
// along with this program. If not, see .
pub use error::*;
-pub use parse::*;
mod error;
-mod parse;
+pub mod mount;
+pub mod mountinfo;
+
+use std::str::FromStr;
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum MntOps {
+ Atime(bool),
+ DirAtime(bool),
+ RelAtime(bool),
+ Dev(bool),
+ Exec(bool),
+ Suid(bool),
+ Write(bool),
+ Extra(String),
+}
+
+impl FromStr for MntOps {
+ type Err = LineError;
+
+ fn from_str(token: &str) -> Result {
+ Ok(match token {
+ "atime" => MntOps::Atime(true),
+ "noatime" => MntOps::Atime(false),
+ "diratime" => MntOps::DirAtime(true),
+ "nodiratime" => MntOps::DirAtime(false),
+ "relatime" => MntOps::RelAtime(true),
+ "norelatime" => MntOps::RelAtime(false),
+ "dev" => MntOps::Dev(true),
+ "nodev" => MntOps::Dev(false),
+ "exec" => MntOps::Exec(true),
+ "noexec" => MntOps::Exec(false),
+ "suid" => MntOps::Suid(true),
+ "nosuid" => MntOps::Suid(false),
+ "rw" => MntOps::Write(true),
+ "ro" => MntOps::Write(false),
+ // TODO: Replace with &str
+ extra => MntOps::Extra(extra.to_string()),
+ })
+ }
+}
diff --git a/src/parse.rs b/src/mount.rs
similarity index 93%
rename from src/parse.rs
rename to src/mount.rs
index 8c6ed67..a7c7cc3 100644
--- a/src/parse.rs
+++ b/src/mount.rs
@@ -15,6 +15,7 @@
extern crate libc;
use error::*;
+use MntOps;
use self::libc::c_int;
use std::cmp::Ordering;
use std::convert::{AsRef, From};
@@ -35,43 +36,6 @@ pub enum DumpField {
pub type PassField = Option;
-#[derive(Clone, PartialEq, Eq, Debug)]
-pub enum MntOps {
- Atime(bool),
- DirAtime(bool),
- RelAtime(bool),
- Dev(bool),
- Exec(bool),
- Suid(bool),
- Write(bool),
- Extra(String),
-}
-
-impl FromStr for MntOps {
- type Err = LineError;
-
- fn from_str(token: &str) -> Result {
- Ok(match token {
- "atime" => MntOps::Atime(true),
- "noatime" => MntOps::Atime(false),
- "diratime" => MntOps::DirAtime(true),
- "nodiratime" => MntOps::DirAtime(false),
- "relatime" => MntOps::RelAtime(true),
- "norelatime" => MntOps::RelAtime(false),
- "dev" => MntOps::Dev(true),
- "nodev" => MntOps::Dev(false),
- "exec" => MntOps::Exec(true),
- "noexec" => MntOps::Exec(false),
- "suid" => MntOps::Suid(true),
- "nosuid" => MntOps::Suid(false),
- "rw" => MntOps::Write(true),
- "ro" => MntOps::Write(false),
- // TODO: Replace with &str
- extra => MntOps::Extra(extra.to_string()),
- })
- }
-}
-
#[derive(Clone, Debug)]
pub enum MountParam<'a> {
Spec(&'a str),
diff --git a/src/mountinfo.rs b/src/mountinfo.rs
new file mode 100644
index 0000000..1b170be
--- /dev/null
+++ b/src/mountinfo.rs
@@ -0,0 +1,430 @@
+// Copyright (C) 2014-2015 Mickaël Salaün
+// Copyright (C) 2018 Andy Grover
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see .
+
+// Support for parsing /proc//mountinfo. Fields are based on description
+// in the kernel's Documentation/filesystems/proc.txt section 3.5.
+
+use error::*;
+use std::collections::{HashMap, HashSet};
+use std::convert::{AsRef, From};
+use std::fs::File;
+use std::io::{BufReader, BufRead, Lines};
+use std::iter::Enumerate;
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+
+use super::MntOps;
+
+const PROC_MOUNTINFO: &str = "/proc/self/mountinfo";
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MountInfoEntry {
+ pub id: i32,
+ pub parent_id: i32,
+ pub major: u32,
+ pub minor: u32,
+ pub root: PathBuf,
+ pub file: PathBuf,
+ pub mntops: Vec,
+ pub optionals: HashMap>,
+ pub vfstype: String,
+ pub spec: Option,
+ pub super_options: HashSet,
+}
+
+#[derive(Clone, Debug)]
+pub enum MountInfoParam<'a> {
+ MountId(i32),
+ ParentId(i32),
+ Major(u32),
+ Minor(u32),
+ Root(&'a Path),
+ MountPoint(&'a Path),
+ MntOps(&'a MntOps),
+ Optionals(&'a str),
+ VfsType(&'a str),
+ Spec(Option<&'a str>),
+ SuperOptions(&'a str),
+}
+
+impl MountInfoEntry {
+ pub fn contains(&self, search: &MountInfoParam) -> bool {
+ match search {
+ &MountInfoParam::MountId(id) => id == self.id,
+ &MountInfoParam::ParentId(id) => id == self.parent_id,
+ &MountInfoParam::Major(maj) => maj == self.major,
+ &MountInfoParam::Minor(min) => min == self.minor,
+ &MountInfoParam::Root(root) => root == self.root,
+ &MountInfoParam::MountPoint(file) => file == &self.file,
+ &MountInfoParam::MntOps(mntops) => self.mntops.contains(mntops),
+ &MountInfoParam::Optionals(optional) => self.optionals.contains_key(optional),
+ &MountInfoParam::VfsType(vfstype) => vfstype == &self.vfstype,
+ &MountInfoParam::Spec(spec) => spec == self.spec.as_ref().map(|x| &**x),
+ &MountInfoParam::SuperOptions(superops) => self.super_options.contains(superops),
+ }
+ }
+}
+
+impl FromStr for MountInfoEntry {
+ type Err = LineError;
+
+ fn from_str(line: &str) -> Result {
+ let line = line.trim();
+ let mut tokens = line.split_terminator(|s: char| s == ' ' || s == '\t')
+ .filter(|s| s != &"");
+
+ let id = try!(tokens.next().ok_or(LineError::MissingId)).parse().unwrap();
+ let parent_id = try!(tokens.next().ok_or(LineError::MissingParentId))
+ .parse()
+ .unwrap();
+ let (major, minor): (u32, u32) = {
+ let majmin = try!(tokens.next().ok_or(LineError::MissingMajMin)).to_string();
+ let mut spl = majmin.splitn(2, ":");
+ let maj = spl.next().unwrap();
+ let min = spl.next().unwrap();
+ (maj.parse().unwrap(), min.parse().unwrap())
+ };
+ let root = PathBuf::from(try!(tokens.next().ok_or(LineError::MissingRoot)));
+ let file = PathBuf::from(try!(tokens.next().ok_or(LineError::MissingFile)));
+ let mntops =
+ try!(tokens.next().ok_or(LineError::MissingMntops))
+ // FIXME: Handle MntOps errors
+ .split_terminator(',').map(|x| { FromStr::from_str(x).unwrap() }).collect();
+
+ let mut optionals = HashMap::new();
+ loop {
+ let optional = try!(tokens.next().ok_or(LineError::MissingOptional)).to_string();
+ if optional == "-" {
+ break;
+ }
+
+ if optional.contains(":") {
+ let mut spl = optional.splitn(2, ":");
+ let tag = spl.next().unwrap();
+ let value = spl.next().unwrap();
+ optionals.insert(tag.to_owned(), Some(value.to_owned()));
+ } else {
+ optionals.insert(optional, None);
+ }
+ }
+
+ let vfstype = try!(tokens.next().ok_or(LineError::MissingVfstype)).to_string();
+ let spec = match try!(tokens.next().ok_or(LineError::MissingSpec)) {
+ "none" => None,
+ x => Some(x.to_owned()),
+ };
+ let super_options = try!(tokens.next().ok_or(LineError::MissingSuperOptions))
+ .split_terminator(',')
+ .map(|x| x.to_owned())
+ .collect();
+
+ Ok(MountInfoEntry {
+ id,
+ parent_id,
+ major,
+ minor,
+ root,
+ file,
+ mntops,
+ optionals,
+ vfstype,
+ spec,
+ super_options,
+ })
+ }
+}
+
+/// Get a list of all mount points from `root` and beneath using a custom `BufRead`
+pub fn get_submounts_from(root: T, iter: MountInfoIter) -> Result, ParseError>
+ where T: AsRef,
+ U: BufRead
+{
+ let mut ret = vec![];
+ for mount in iter {
+ match mount {
+ Ok(m) => {
+ if m.file.starts_with(&root) {
+ ret.push(m);
+ }
+ }
+ Err(e) => return Err(e),
+ }
+ }
+ Ok(ret)
+}
+
+/// Get a list of all mount points from `root` and beneath using */proc/mounts*
+pub fn get_submounts(root: T) -> Result, ParseError>
+ where T: AsRef
+{
+ get_submounts_from(root, try!(MountInfoIter::new_from_self()))
+}
+
+/// Get the mount point for the `target` using a custom `BufRead`
+pub fn get_mount_from(target: T, iter: MountInfoIter) -> Result