1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

df: Adds support for mount path prefix matching and input path (#3161)

* Adds support for mount path prefix matching and input path
canonicalization

- Sorts mount paths in reverse lexicographical order
- Canonicalize all paths and clear invalid paths
- Checking of mount path prefix matches input path
This commit is contained in:
Kartik Sharma 2022-03-11 14:06:34 +05:30 committed by GitHub
parent 1795272473
commit 5c5f4ca6ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 18 deletions

View file

@ -5,7 +5,7 @@
// //
// For the full copyright and license information, please view the LICENSE file // For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code. // that was distributed with this source code.
// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs // spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs lofs
mod blocks; mod blocks;
mod columns; mod columns;
mod table; mod table;
@ -22,7 +22,6 @@ use std::collections::HashSet;
use std::fmt; use std::fmt;
use std::iter::FromIterator; use std::iter::FromIterator;
#[cfg(windows)]
use std::path::Path; use std::path::Path;
use crate::blocks::{block_size_from_matches, BlockSize}; use crate::blocks::{block_size_from_matches, BlockSize};
@ -188,7 +187,7 @@ impl Filesystem {
} }
/// Whether to display the mount info given the inclusion settings. /// Whether to display the mount info given the inclusion settings.
fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool { fn is_included(mi: &MountInfo, opt: &Options) -> bool {
// Don't show remote filesystems if `--local` has been given. // Don't show remote filesystems if `--local` has been given.
if mi.remote && opt.show_local_fs { if mi.remote && opt.show_local_fs {
return false; return false;
@ -204,12 +203,6 @@ fn is_included(mi: &MountInfo, paths: &[String], opt: &Options) -> bool {
return false; return false;
} }
// Don't show filesystems other than the ones specified on the
// command line, if any.
if !paths.is_empty() && !paths.contains(&mi.mount_dir) {
return false;
}
true true
} }
@ -259,15 +252,13 @@ fn is_best(previous: &[MountInfo], mi: &MountInfo) -> bool {
/// Keep only the specified subset of [`MountInfo`] instances. /// Keep only the specified subset of [`MountInfo`] instances.
/// ///
/// If `paths` is non-empty, this function excludes any [`MountInfo`]
/// that is not mounted at the specified path.
///
/// The `opt` argument specifies a variety of ways of excluding /// The `opt` argument specifies a variety of ways of excluding
/// [`MountInfo`] instances; see [`Options`] for more information. /// [`MountInfo`] instances; see [`Options`] for more information.
/// ///
/// Finally, if there are duplicate entries, the one with the shorter /// Finally, if there are duplicate entries, the one with the shorter
/// path is kept. /// path is kept.
fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Vec<MountInfo> {
fn filter_mount_list(vmi: Vec<MountInfo>, opt: &Options) -> Vec<MountInfo> {
let mut result = vec![]; let mut result = vec![];
for mi in vmi { for mi in vmi {
// TODO The running time of the `is_best()` function is linear // TODO The running time of the `is_best()` function is linear
@ -275,21 +266,45 @@ fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Ve
// this loop quadratic in the length of `vmi`. This could be // this loop quadratic in the length of `vmi`. This could be
// improved by a more efficient implementation of `is_best()`, // improved by a more efficient implementation of `is_best()`,
// but `vmi` is probably not very long in practice. // but `vmi` is probably not very long in practice.
if is_included(&mi, paths, opt) && is_best(&result, &mi) { if is_included(&mi, opt) && is_best(&result, &mi) {
result.push(mi); result.push(mi);
} }
} }
result result
} }
/// Assign 1 `MountInfo` entry to each path
/// `lofs` entries are skipped and dummy mount points are skipped
/// Only the longest matching prefix for that path is considered
/// `lofs` is for Solaris style loopback filesystem and is present in Solaris and FreeBSD.
/// It works similar to symlinks
fn get_point_list(vmi: &[MountInfo], paths: &[String]) -> Vec<MountInfo> {
paths
.iter()
.map(|p| {
vmi.iter()
.filter(|mi| mi.fs_type.ne("lofs"))
.filter(|mi| !mi.dummy)
.filter(|mi| p.starts_with(&mi.mount_dir))
.max_by_key(|mi| mi.mount_dir.len())
.unwrap()
.clone()
})
.collect::<Vec<MountInfo>>()
}
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().get_matches_from(args); let matches = uu_app().get_matches_from(args);
let paths: Vec<String> = matches // Canonicalize the input_paths and then convert to string
let paths = matches
.values_of(OPT_PATHS) .values_of(OPT_PATHS)
.map(|v| v.map(ToString::to_string).collect()) .unwrap_or_default()
.unwrap_or_default(); .map(Path::new)
.filter_map(|v| v.canonicalize().ok())
.filter_map(|v| v.into_os_string().into_string().ok())
.collect::<Vec<_>>();
#[cfg(windows)] #[cfg(windows)]
{ {
@ -302,7 +317,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let opt = Options::from(&matches).map_err(|e| USimpleError::new(1, format!("{}", e)))?; let opt = Options::from(&matches).map_err(|e| USimpleError::new(1, format!("{}", e)))?;
let mounts = read_fs_list(); let mounts = read_fs_list();
let data: Vec<Row> = filter_mount_list(mounts, &paths, &opt)
let op_mount_points: Vec<MountInfo> = if paths.is_empty() {
// Get all entries
filter_mount_list(mounts, &opt)
} else {
// Get Point for each input_path
get_point_list(&mounts, &paths)
};
let data: Vec<Row> = op_mount_points
.into_iter() .into_iter()
.filter_map(Filesystem::new) .filter_map(Filesystem::new)
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs) .filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)

View file

@ -54,6 +54,18 @@ fn test_order_same() {
assert_eq!(output1, output2); assert_eq!(output1, output2);
} }
/// Test of mount point begin repeated
#[cfg(unix)]
#[test]
fn test_output_mp_repeat() {
let output1 = new_ucmd!().arg("/").arg("/").succeeds().stdout_move_str();
let output1: Vec<String> = output1
.lines()
.map(|l| String::from(l.split_once(' ').unwrap().0))
.collect();
assert_eq!(3, output1.len());
assert_eq!(output1[1], output1[2]);
}
#[test] #[test]
fn test_output_conflict_options() { fn test_output_conflict_options() {
for option in ["-i", "-T", "-P"] { for option in ["-i", "-T", "-P"] {