From 74797313f378d9890642f72d6e1d7aea01a3ff7d Mon Sep 17 00:00:00 2001 From: Harshit Verma Date: Sun, 2 Feb 2025 13:50:56 +0530 Subject: [PATCH 1/2] First version of `findmnt` --- Cargo.lock | 10 ++ Cargo.toml | 2 + src/uu/findmnt/Cargo.toml | 16 +++ src/uu/findmnt/findmnt.md | 7 ++ src/uu/findmnt/src/findmnt.rs | 189 ++++++++++++++++++++++++++++++++++ src/uu/findmnt/src/main.rs | 1 + 6 files changed, 225 insertions(+) create mode 100644 src/uu/findmnt/Cargo.toml create mode 100644 src/uu/findmnt/findmnt.md create mode 100644 src/uu/findmnt/src/findmnt.rs create mode 100644 src/uu/findmnt/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index cd067d3d..53ec5213 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1028,6 +1028,7 @@ dependencies = [ "textwrap", "uu_ctrlaltdel", "uu_dmesg", + "uu_findmnt", "uu_fsfreeze", "uu_last", "uu_lscpu", @@ -1060,6 +1061,15 @@ dependencies = [ "uucore", ] +[[package]] +name = "uu_findmnt" +version = "0.0.1" +dependencies = [ + "clap", + "tabled", + "uucore", +] + [[package]] name = "uu_fsfreeze" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 859018b3..bdb227cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ feat_common_core = [ "mountpoint", "rev", "setsid", + "findmnt", ] [workspace.dependencies] @@ -76,6 +77,7 @@ lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/ mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" } rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" } setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" } +findmnt = { optional = true, version = "0.0.1", package = "uu_findmnt", path = "src/uu/findmnt" } [dev-dependencies] # dmesg test require fixed-boot-time feature turned on. diff --git a/src/uu/findmnt/Cargo.toml b/src/uu/findmnt/Cargo.toml new file mode 100644 index 00000000..676627c9 --- /dev/null +++ b/src/uu/findmnt/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "uu_findmnt" +version = "0.0.1" +edition = "2021" + +[lib] +path = "src/findmnt.rs" + +[[bin]] +name = "findmnt" +path = "src/main.rs" + +[dependencies] +tabled = { workspace = true } +uucore = { workspace = true } +clap = { workspace = true } \ No newline at end of file diff --git a/src/uu/findmnt/findmnt.md b/src/uu/findmnt/findmnt.md new file mode 100644 index 00000000..27a1f1bc --- /dev/null +++ b/src/uu/findmnt/findmnt.md @@ -0,0 +1,7 @@ +# findmnt + +``` +findmnt [options] +``` + +findmnt will list all mounted filesytems or search for a filesystem. The findmnt command is able to search in /etc/fstab, /etc/fstab.d, /etc/mtab or /proc/self/mountinfo. If device or mountpoint is not given, all filesystems are shown. \ No newline at end of file diff --git a/src/uu/findmnt/src/findmnt.rs b/src/uu/findmnt/src/findmnt.rs new file mode 100644 index 00000000..bbadffa4 --- /dev/null +++ b/src/uu/findmnt/src/findmnt.rs @@ -0,0 +1,189 @@ +use core::fmt; +use std::fs; + +use clap::{crate_version, Command}; +use tabled::{settings::Style, Table, Tabled}; +use uucore::{error::UResult, help_about}; + +#[uucore::main] +pub fn uumain(args: impl uucore::Args) -> UResult<()> { + let _matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?; + + // By default findmnt reads /proc/self/mountinfo + let mut res = Findmnt::new(MOUNTINFO_DIR); + res.form_nodes(); + res.print_table(); + Ok(()) +} + +pub static MOUNTINFO_DIR: &str = "/proc/self/mountinfo"; +pub static ABOUT: &str = help_about!("findmnt.md"); + +pub fn uu_app() -> Command { + Command::new(uucore::util_name()) + .version(crate_version!()) + .about(ABOUT) +} + +#[derive(Debug, Clone)] +pub struct Findmnt<'a> { + pub nodes_vec: Vec, + file_name: &'a str, +} + +impl<'a> Findmnt<'a> { + pub fn new(file_name: &str) -> Findmnt { + Findmnt { + file_name, + nodes_vec: Vec::::new(), + } + } + + pub fn form_nodes(&mut self) { + let res = fs::read_to_string(self.file_name).unwrap(); + let lines = res.lines(); + let mut unsorted_vec = Vec::::new(); + + for line in lines { + let res = Node::parse(line); + unsorted_vec.push(res); + } + + self.nodes_vec = unsorted_vec; + // /, /proc, /sys, /dev, /run, /tmp, /boot + // Sort the vec according to this + self.sort_nodes(); + } + + pub fn print_table(&self) { + let mut table = Table::new(self.nodes_vec.clone()); + table.with(Style::empty()); + print!("{}", table) + } + + fn sort_nodes(&mut self) { + let unsorted_vec = self.nodes_vec.clone(); + let mut sorted_vec = Vec::new(); + + // "/" + // This should always give one element + let res = unsorted_vec + .iter() + .find(|node| node.target == Types::ROOT.to_string()); + sorted_vec.push(res.unwrap().clone()); + + // "proc" + sorted_vec.extend(self.filter(Types::PROC)); + + // "/sys" + sorted_vec.extend(self.filter(Types::SYS)); + + // "/dev" + sorted_vec.extend(self.filter(Types::DEV)); + + // "/run" + sorted_vec.extend(self.filter(Types::RUN)); + + // "/tmp" + sorted_vec.extend(self.filter(Types::TMP)); + + // "/boot" + sorted_vec.extend(self.filter(Types::BOOT)); + + self.nodes_vec = sorted_vec; + } + + fn filter(&self, pattern: Types) -> Vec { + let mut temp_vec = Vec::::new(); + let _ = self.filter_with_pattern(pattern).iter().for_each(|node| { + temp_vec.push(node.clone()); + }); + temp_vec + } + + fn filter_with_pattern(&self, pattern: Types) -> Vec { + self.nodes_vec + .iter() + .filter(|node| node.target.starts_with(&pattern.to_string())) + .cloned() + .collect() + } +} + +// Different types for a particular node +#[derive(Debug, Clone)] +pub enum Types { + ROOT, + PROC, + SYS, + DEV, + RUN, + TMP, + BOOT, +} + +impl fmt::Display for Types { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Types::ROOT => write!(f, "{}", "/"), + Types::PROC => write!(f, "{}", "/proc"), + Types::SYS => write!(f, "{}", "/sys"), + Types::DEV => write!(f, "{}", "/dev"), + Types::RUN => write!(f, "{}", "/run"), + Types::TMP => write!(f, "{}", "/tmp"), + Types::BOOT => write!(f, "{}", "/boot"), + } + } +} + +// Represents each row for the table +#[derive(Debug, Clone, Tabled)] +pub struct Node { + target: String, + source: String, + fstype: String, + options: String, +} + +impl Node { + fn new(target: String, source: String, fstype: String, options: String) -> Node { + Node { + target, + source, + fstype, + options, + } + } + + pub fn filter_with_pattern(node_vec: &Vec, pattern: Types) -> Vec { + node_vec + .iter() + .filter(|node| node.target.starts_with(&pattern.to_string())) + .cloned() + .collect() + } + + // This is the main function that parses the default /proc/self/mountinfo + pub fn parse(line: &str) -> Self { + let (_, rest) = line.split_once("/").unwrap(); + let (target, rest) = rest.trim().split_once(" ").unwrap(); + let (options, rest) = rest.trim().split_once(" ").unwrap(); + let (_, rest) = rest.trim().split_once("-").unwrap(); + let (fstype, rest) = rest.trim().split_once(" ").unwrap(); + let (source, rest) = rest.trim().split_once(" ").unwrap(); + let options_added = if let Some(_) = rest.split_once("rw") { + rest.split_once("rw").unwrap().1 + } else { + rest + }; + + let final_options = options.to_owned() + options_added; + + Self::new( + target.to_string(), + source.to_string(), + fstype.to_string(), + final_options, + ) + } +} diff --git a/src/uu/findmnt/src/main.rs b/src/uu/findmnt/src/main.rs new file mode 100644 index 00000000..c30a45ec --- /dev/null +++ b/src/uu/findmnt/src/main.rs @@ -0,0 +1 @@ +uucore::bin!(uu_findmnt); From 7bd0166fd273fb31f688a2e46fa8050e21571051 Mon Sep 17 00:00:00 2001 From: Harshit Verma Date: Sun, 2 Feb 2025 13:51:36 +0530 Subject: [PATCH 2/2] Add tests for `findmnt` --- src/uu/findmnt/Cargo.toml | 2 +- src/uu/findmnt/src/findmnt.rs | 22 +++++++++++----------- src/uu/findmnt/src/main.rs | 1 + tests/by-util/test_findmnt.rs | 7 +++++++ tests/tests.rs | 4 ++++ 5 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 tests/by-util/test_findmnt.rs diff --git a/src/uu/findmnt/Cargo.toml b/src/uu/findmnt/Cargo.toml index 676627c9..c8eac8aa 100644 --- a/src/uu/findmnt/Cargo.toml +++ b/src/uu/findmnt/Cargo.toml @@ -13,4 +13,4 @@ path = "src/main.rs" [dependencies] tabled = { workspace = true } uucore = { workspace = true } -clap = { workspace = true } \ No newline at end of file +clap = { workspace = true } diff --git a/src/uu/findmnt/src/findmnt.rs b/src/uu/findmnt/src/findmnt.rs index bbadffa4..bfa80381 100644 --- a/src/uu/findmnt/src/findmnt.rs +++ b/src/uu/findmnt/src/findmnt.rs @@ -31,7 +31,7 @@ pub struct Findmnt<'a> { file_name: &'a str, } -impl<'a> Findmnt<'a> { +impl Findmnt<'_> { pub fn new(file_name: &str) -> Findmnt { Findmnt { file_name, @@ -95,7 +95,7 @@ impl<'a> Findmnt<'a> { fn filter(&self, pattern: Types) -> Vec { let mut temp_vec = Vec::::new(); - let _ = self.filter_with_pattern(pattern).iter().for_each(|node| { + self.filter_with_pattern(pattern).iter().for_each(|node| { temp_vec.push(node.clone()); }); temp_vec @@ -125,13 +125,13 @@ pub enum Types { impl fmt::Display for Types { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Types::ROOT => write!(f, "{}", "/"), - Types::PROC => write!(f, "{}", "/proc"), - Types::SYS => write!(f, "{}", "/sys"), - Types::DEV => write!(f, "{}", "/dev"), - Types::RUN => write!(f, "{}", "/run"), - Types::TMP => write!(f, "{}", "/tmp"), - Types::BOOT => write!(f, "{}", "/boot"), + Types::ROOT => write!(f, "/"), + Types::PROC => write!(f, "/proc"), + Types::SYS => write!(f, "/sys"), + Types::DEV => write!(f, "/dev"), + Types::RUN => write!(f, "/run"), + Types::TMP => write!(f, "/tmp"), + Types::BOOT => write!(f, "/boot"), } } } @@ -155,7 +155,7 @@ impl Node { } } - pub fn filter_with_pattern(node_vec: &Vec, pattern: Types) -> Vec { + pub fn filter_with_pattern(node_vec: &[Node], pattern: Types) -> Vec { node_vec .iter() .filter(|node| node.target.starts_with(&pattern.to_string())) @@ -171,7 +171,7 @@ impl Node { let (_, rest) = rest.trim().split_once("-").unwrap(); let (fstype, rest) = rest.trim().split_once(" ").unwrap(); let (source, rest) = rest.trim().split_once(" ").unwrap(); - let options_added = if let Some(_) = rest.split_once("rw") { + let options_added = if rest.split_once("rw").is_some() { rest.split_once("rw").unwrap().1 } else { rest diff --git a/src/uu/findmnt/src/main.rs b/src/uu/findmnt/src/main.rs index c30a45ec..ec0d24d6 100644 --- a/src/uu/findmnt/src/main.rs +++ b/src/uu/findmnt/src/main.rs @@ -1 +1,2 @@ +#[cfg(target_os = "linux")] uucore::bin!(uu_findmnt); diff --git a/tests/by-util/test_findmnt.rs b/tests/by-util/test_findmnt.rs new file mode 100644 index 00000000..53027fba --- /dev/null +++ b/tests/by-util/test_findmnt.rs @@ -0,0 +1,7 @@ +use crate::common::util::TestScenario; + +#[cfg(target_os = "linux")] +#[test] +fn test_findmnt() { + new_ucmd!().succeeds().stdout_contains("/proc"); +} diff --git a/tests/tests.rs b/tests/tests.rs index 824a096e..6df9070b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -40,3 +40,7 @@ mod test_dmesg; #[cfg(feature = "fsfreeze")] #[path = "by-util/test_fsfreeze.rs"] mod test_fsfreeze; + +#[cfg(feature = "findmnt")] +#[path = "by-util/test_findmnt.rs"] +mod test_findmnt;