Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5b52294

Browse files
committed
Add hardlink tracking, and an option to disable it
1 parent 0c86b89 commit 5b52294

8 files changed

Lines changed: 91 additions & 4 deletions

File tree

src/aggregate.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{WalkOptions, WalkResult};
1+
use crate::{WalkOptions, WalkResult, InodeFilter};
22
use failure::Error;
33
use std::borrow::Cow;
44
use std::{fmt, io, path::Path};
@@ -20,6 +20,7 @@ pub fn aggregate(
2020
let mut total = 0;
2121
let mut num_roots = 0;
2222
let mut aggregates = Vec::new();
23+
let mut inodes = InodeFilter::default();
2324
for path in paths.into_iter() {
2425
num_roots += 1;
2526
let mut num_bytes = 0u64;
@@ -29,7 +30,7 @@ pub fn aggregate(
2930
match entry {
3031
Ok(entry) => {
3132
let file_size = match entry.metadata {
32-
Some(Ok(ref m)) if !m.is_dir() => {
33+
Some(Ok(ref m)) if !m.is_dir() && (options.count_links || inodes.add(m)) => {
3334
if options.apparent_size {
3435
m.len()
3536
} else {

src/common.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ pub struct WalkOptions {
152152
/// for more information.
153153
pub threads: usize,
154154
pub byte_format: ByteFormat,
155+
pub count_links: bool,
155156
pub apparent_size: bool,
156157
pub color: Color,
157158
pub sorting: TraversalSorting,

src/inodefilter.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
2+
#![cfg_attr(windows, feature(windows_by_handle))]
3+
4+
use std::collections::HashMap;
5+
6+
#[derive(Debug, Default, Clone)]
7+
pub struct InodeFilter {
8+
inner: HashMap<u64, u64>
9+
}
10+
11+
impl InodeFilter {
12+
#[cfg(unix)]
13+
pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
14+
use std::os::unix::fs::MetadataExt;
15+
16+
self.add_inode(metadata.ino(), metadata.nlink())
17+
}
18+
19+
#[cfg(windows)]
20+
pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
21+
use std::os::windows::fs::MetadataExt;
22+
23+
if let (Some(inode), Some(nlinks)) = (metadata.file_index(), metadata.number_of_links()) {
24+
self.add_inode(inode, nlinks as u64)
25+
} else {
26+
true
27+
}
28+
}
29+
30+
#[cfg(not(any(unix, windows)))]
31+
pub fn add(&mut self, metadata: &std::fs::Metadata) -> bool {
32+
true
33+
}
34+
35+
pub fn add_inode(&mut self, inode: u64, nlinks: u64) -> bool {
36+
if nlinks <= 1 {
37+
return true;
38+
}
39+
40+
match self.inner.get_mut(&inode) {
41+
Some(count) => {
42+
*count -= 1;
43+
44+
if *count == 0 {
45+
self.inner.remove(&inode);
46+
}
47+
48+
false
49+
},
50+
None => {
51+
self.inner.insert(inode, nlinks - 1);
52+
true
53+
}
54+
}
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use super::*;
61+
62+
#[test]
63+
fn it_filters_inodes() {
64+
let mut inodes = InodeFilter::default();
65+
66+
assert!(inodes.add_inode(1, 2));
67+
assert!(!inodes.add_inode(1, 2));
68+
69+
assert!(inodes.add_inode(1, 3));
70+
assert!(!inodes.add_inode(1, 3));
71+
assert!(!inodes.add_inode(1, 3));
72+
73+
assert!(inodes.add_inode(1, 1));
74+
assert!(inodes.add_inode(1, 1));
75+
}
76+
}

src/interactive/app_test/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ pub fn initialized_app_and_terminal_with_closure<P: AsRef<Path>>(
165165
threads: 1,
166166
byte_format: ByteFormat::Metric,
167167
apparent_size: true,
168+
count_links: false,
168169
color: Color::None,
169170
sorting: TraversalSorting::AlphabeticalByFileName,
170171
},

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ extern crate jwalk;
55

66
mod aggregate;
77
mod common;
8+
mod inodefilter;
89

910
pub mod traverse;
1011

1112
pub use aggregate::aggregate;
1213
pub use common::*;
14+
pub(crate) use inodefilter::InodeFilter;

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ fn run() -> Result<(), Error> {
3030
Color::None
3131
},
3232
apparent_size: opt.apparent_size,
33+
count_links: opt.count_links,
3334
sorting: TraversalSorting::None,
3435
};
3536
let res = match opt.command {

src/options.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ pub struct Args {
5656
#[structopt(short = "A", long = "apparent-size")]
5757
pub apparent_size: bool,
5858

59+
/// Count hard-linked files each time they are seen
60+
#[structopt(short = "l", long = "count-links")]
61+
pub count_links: bool,
62+
5963
/// One or more input files or directories. If unset, we will use all entries in the current working directory.
6064
#[structopt(parse(from_os_str))]
6165
pub input: Vec<PathBuf>,

src/traverse.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{get_size_or_panic, WalkOptions};
1+
use crate::{get_size_or_panic, WalkOptions, InodeFilter};
22
use failure::Error;
33
use petgraph::{graph::NodeIndex, stable_graph::StableGraph, Directed, Direction};
44
use std::{ffi::OsString, path::PathBuf, time::Duration, time::Instant};
@@ -66,6 +66,7 @@ impl Traversal {
6666
let mut sizes_per_depth_level = Vec::new();
6767
let mut current_size_at_depth = 0;
6868
let mut previous_depth = 0;
69+
let mut inodes = InodeFilter::default();
6970

7071
let mut last_checked = Instant::now();
7172

@@ -93,7 +94,7 @@ impl Traversal {
9394
entry.file_name
9495
};
9596
let file_size = match entry.metadata {
96-
Some(Ok(ref m)) if !m.is_dir() => {
97+
Some(Ok(ref m)) if !m.is_dir() && (walk_options.count_links || inodes.add(m)) => {
9798
if walk_options.apparent_size {
9899
m.len()
99100
} else {

0 commit comments

Comments
 (0)