Skip to content

Commit e85b104

Browse files
committed
fix: bug where hard links could be double counted
When running: dust dir_a dir_b if a file was hard linked in both dir_a and dir_b it would be double counted. This fix resolves this by keeping the shared hashmap around between runs for the second and subsequent arguments.
1 parent a91aa62 commit e85b104

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

src/dir_walker.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ pub struct WalkData<'a> {
3131
pub fn walk_it(dirs: HashSet<PathBuf>, walk_data: WalkData) -> (Vec<Node>, bool) {
3232
let permissions_flag = AtomicBool::new(false);
3333

34+
let mut inodes = HashSet::new();
3435
let top_level_nodes: Vec<_> = dirs
3536
.into_iter()
3637
.filter_map(|d| {
3738
clean_inodes(
3839
walk(d, &permissions_flag, &walk_data, 0)?,
39-
&mut HashSet::new(),
40+
&mut inodes,
4041
walk_data.use_apparent_size,
4142
)
4243
})

tests/tests_symlinks.rs

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ fn build_temp_file(dir: &TempDir) -> PathBuf {
1717
file_path
1818
}
1919

20+
fn link_it(link_path: PathBuf, file_path_s: &str, is_soft: bool) -> String {
21+
let link_name_s = link_path.to_str().unwrap();
22+
let mut c = Command::new("ln");
23+
if is_soft {
24+
c.arg("-s");
25+
}
26+
c.arg(file_path_s);
27+
c.arg(link_name_s);
28+
assert!(c.output().is_ok());
29+
return link_name_s.into();
30+
}
31+
2032
#[cfg_attr(target_os = "windows", ignore)]
2133
#[test]
2234
pub fn test_soft_sym_link() {
@@ -26,13 +38,7 @@ pub fn test_soft_sym_link() {
2638
let file_path_s = file.to_str().unwrap();
2739

2840
let link_name = dir.path().join("the_link");
29-
let link_name_s = link_name.to_str().unwrap();
30-
let c = Command::new("ln")
31-
.arg("-s")
32-
.arg(file_path_s)
33-
.arg(link_name_s)
34-
.output();
35-
assert!(c.is_ok());
41+
let link_name_s = link_it(link_name, file_path_s, true);
3642

3743
let c = format!(" ├── {}", link_name_s);
3844
let b = format!(" ┌── {}", file_path_s);
@@ -61,12 +67,7 @@ pub fn test_hard_sym_link() {
6167
let file_path_s = file.to_str().unwrap();
6268

6369
let link_name = dir.path().join("the_link");
64-
let link_name_s = link_name.to_str().unwrap();
65-
let c = Command::new("ln")
66-
.arg(file_path_s)
67-
.arg(link_name_s)
68-
.output();
69-
assert!(c.is_ok());
70+
link_it(link_name, file_path_s, false);
7071

7172
let file_output = format!(" ┌── {}", file_path_s);
7273
let dirs_output = format!("─┴ {}", dir_s);
@@ -82,21 +83,44 @@ pub fn test_hard_sym_link() {
8283
assert!(output.contains(file_output.as_str()));
8384
}
8485

86+
#[cfg_attr(target_os = "windows", ignore)]
87+
#[test]
88+
pub fn test_hard_sym_link_no_dup_multi_arg() {
89+
let dir = Builder::new().tempdir().unwrap();
90+
let dir_link = Builder::new().tempdir().unwrap();
91+
let file = build_temp_file(&dir);
92+
let dir_s = dir.path().to_str().unwrap();
93+
let dir_link_s = dir_link.path().to_str().unwrap();
94+
let file_path_s = file.to_str().unwrap();
95+
96+
let link_name = dir_link.path().join("the_link");
97+
let link_name_s = link_it(link_name, file_path_s, false);
98+
99+
let mut cmd = Command::cargo_bin("dust").unwrap();
100+
101+
// Mac test runners create long filenames in tmp directories
102+
let output = cmd
103+
.args(["-p", "-c", "-w 999", "-b", dir_link_s, dir_s])
104+
.unwrap()
105+
.stdout;
106+
107+
// The link or the file should appeart but not both
108+
let output = str::from_utf8(&output).unwrap();
109+
println!("cmd:\n{:?}", cmd);
110+
println!("output:\n{:?}", output);
111+
let has_file_only = output.contains(file_path_s) && !output.contains(&link_name_s);
112+
let has_link_only = !output.contains(file_path_s) && output.contains(&link_name_s);
113+
assert!(has_file_only || has_link_only)
114+
}
115+
85116
#[cfg_attr(target_os = "windows", ignore)]
86117
#[test]
87118
pub fn test_recursive_sym_link() {
88119
let dir = Builder::new().tempdir().unwrap();
89120
let dir_s = dir.path().to_str().unwrap();
90121

91122
let link_name = dir.path().join("the_link");
92-
let link_name_s = link_name.to_str().unwrap();
93-
94-
let c = Command::new("ln")
95-
.arg("-s")
96-
.arg(dir_s)
97-
.arg(link_name_s)
98-
.output();
99-
assert!(c.is_ok());
123+
let link_name_s = link_it(link_name, dir_s, true);
100124

101125
let a = format!("─┬ {}", dir_s);
102126
let b = format!(" └── {}", link_name_s);

0 commit comments

Comments
 (0)