diff options
author | Sebastian Thiel <sthiel@thoughtworks.com> | 2020-05-04 13:07:58 +0800 |
---|---|---|
committer | Sebastian Thiel <sthiel@thoughtworks.com> | 2020-05-04 13:07:58 +0800 |
commit | 9156cf7cac8f91a496f7383940f3ce6140ffe54c (patch) | |
tree | 8965c3a980646caacc87af2ceb7ac44de96ee724 | |
parent | a5988d091b437315a91accd21f6f1b61d21e2e9a (diff) |
Add '-x' flag to not cross filesystems
Fixes #3
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | src/aggregate.rs | 20 | ||||
-rw-r--r-- | src/common.rs | 1 | ||||
-rw-r--r-- | src/crossdev.rs | 25 | ||||
-rw-r--r-- | src/interactive/app_test/utils.rs | 1 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 1 | ||||
-rw-r--r-- | src/options.rs | 4 | ||||
-rw-r--r-- | src/traverse.rs | 7 |
9 files changed, 51 insertions, 10 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0033c3d..8043cb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ #### v2.6.0 * Use `x` to only mark entries for deletion, instead of toggling them. +* Add `-x` | `--stay-on-filesystem` flag to force staying on the file system the root is on, similar to `-x` in the venerable `du` tool. #### v2.5.0 Much more nuanced percentage bars for a more precise visualization of space consumption diff --git a/src/aggregate.rs b/src/aggregate.rs index b04fa83..b7ef995 100644 --- a/src/aggregate.rs +++ b/src/aggregate.rs @@ -1,4 +1,4 @@ -use crate::{InodeFilter, WalkOptions, WalkResult}; +use crate::{crossdev, InodeFilter, WalkOptions, WalkResult}; use failure::Error; use filesize::PathExt; use std::borrow::Cow; @@ -10,7 +10,7 @@ use termion::color; /// If `sort_by_size_in_bytes` is set, we will sort all sizes (ascending) before outputting them. pub fn aggregate( mut out: impl io::Write, - options: WalkOptions, + walk_options: WalkOptions, compute_total: bool, sort_by_size_in_bytes: bool, paths: impl IntoIterator<Item = impl AsRef<Path>>, @@ -27,15 +27,19 @@ pub fn aggregate( num_roots += 1; let mut num_bytes = 0u64; let mut num_errors = 0u64; - for entry in options.iter_from_path(path.as_ref()) { + let device_id = crossdev::init(path.as_ref())?; + for entry in walk_options.iter_from_path(path.as_ref()) { stats.entries_traversed += 1; match entry { Ok(entry) => { let file_size = match entry.client_state { Some(Ok(ref m)) - if !m.is_dir() && (options.count_hard_links || inodes.add(m)) => + if !m.is_dir() + && (walk_options.count_hard_links || inodes.add(m)) + && (walk_options.cross_filesystems + || crossdev::is_same_device(device_id, m)) => { - if options.apparent_size { + if walk_options.apparent_size { m.len() } else { entry.path().size_on_disk_fast(m).unwrap_or_else(|_| { @@ -64,7 +68,7 @@ pub fn aggregate( } else { write_path( &mut out, - &options, + &walk_options, &path, num_bytes, num_errors, @@ -84,7 +88,7 @@ pub fn aggregate( for (path, num_bytes, num_errors) in aggregates.into_iter() { write_path( &mut out, - &options, + &walk_options, &path, num_bytes, num_errors, @@ -96,7 +100,7 @@ pub fn aggregate( if num_roots > 1 && compute_total { write_path( &mut out, - &options, + &walk_options, Path::new("total"), total, res.num_errors, diff --git a/src/common.rs b/src/common.rs index f88c491..e5cf697 100644 --- a/src/common.rs +++ b/src/common.rs @@ -155,6 +155,7 @@ pub struct WalkOptions { pub apparent_size: bool, pub color: Color, pub sorting: TraversalSorting, + pub cross_filesystems: bool, } type WalkDir = jwalk::WalkDirGeneric<((), Option<Result<std::fs::Metadata, jwalk::Error>>)>; diff --git a/src/crossdev.rs b/src/crossdev.rs new file mode 100644 index 0000000..94c5c67 --- /dev/null +++ b/src/crossdev.rs @@ -0,0 +1,25 @@ +use std::{io, path::Path}; + +#[cfg(unix)] +pub fn init(path: &Path) -> io::Result<u64> { + use std::os::unix::fs::MetadataExt; + + path.metadata().map(|m| m.dev()) +} + +#[cfg(unix)] +pub fn is_same_device(device_id: u64, meta: &std::fs::Metadata) -> bool { + use std::os::unix::fs::MetadataExt; + + meta.dev() == device_id +} + +#[cfg(not(any(unix, windows)))] +pub fn is_same_device(device_id: u64, meta: &std::fs::Metadata) -> bool { + true +} + +#[cfg(not(any(unix, windows)))] +pub fn init(path: &Path) -> io::Result<u64> { + Ok(0) +} diff --git a/src/interactive/app_test/utils.rs b/src/interactive/app_test/utils.rs index 8fde29f..57854d9 100644 --- a/src/interactive/app_test/utils.rs +++ b/src/interactive/app_test/utils.rs @@ -175,6 +175,7 @@ pub fn initialized_app_and_terminal_with_closure<P: AsRef<Path>>( count_hard_links: false, color: Color::None, sorting: TraversalSorting::AlphabeticalByFileName, + cross_filesystems: false, }, input, Interaction::None, @@ -5,6 +5,7 @@ extern crate jwalk; mod aggregate; mod common; +mod crossdev; mod inodefilter; pub mod traverse; diff --git a/src/main.rs b/src/main.rs index e9ea37e..7f505d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,6 +32,7 @@ fn run() -> Result<(), Error> { apparent_size: opt.apparent_size, count_hard_links: opt.count_hard_links, sorting: TraversalSorting::None, + cross_filesystems: !opt.stay_on_filesystem, }; let res = match opt.command { Some(Interactive { input }) => { diff --git a/src/options.rs b/src/options.rs index 1a06e45..5b9d604 100644 --- a/src/options.rs +++ b/src/options.rs @@ -60,6 +60,10 @@ pub struct Args { #[structopt(short = "l", long)] pub count_hard_links: bool, + /// If set, we will not cross filesystems or traverse mount points + #[structopt(short = "x", long)] + pub stay_on_filesystem: bool, + /// One or more input files or directories. If unset, we will use all entries in the current working directory. #[structopt(parse(from_os_str))] pub input: Vec<PathBuf>, diff --git a/src/traverse.rs b/src/traverse.rs index d78041f..5027b69 100644 --- a/src/traverse.rs +++ b/src/traverse.rs @@ -1,4 +1,4 @@ -use crate::{get_size_or_panic, InodeFilter, WalkOptions}; +use crate::{crossdev, get_size_or_panic, InodeFilter, WalkOptions}; use failure::Error; use filesize::PathExt; use petgraph::{graph::NodeIndex, stable_graph::StableGraph, Directed, Direction}; @@ -80,6 +80,7 @@ impl Traversal { } for path in input.into_iter() { let mut last_seen_eid = 0; + let device_id = crossdev::init(path.as_ref())?; for (eid, entry) in walk_options .iter_from_path(path.as_ref()) .into_iter() @@ -97,7 +98,9 @@ impl Traversal { let file_size = match entry.client_state { Some(Ok(ref m)) if !m.is_dir() - && (walk_options.count_hard_links || inodes.add(m)) => + && (walk_options.count_hard_links || inodes.add(m)) + && (walk_options.cross_filesystems + || crossdev::is_same_device(device_id, m)) => { if walk_options.apparent_size { m.len() |