-
-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathaware.rs
More file actions
116 lines (104 loc) · 3.37 KB
/
aware.rs
File metadata and controls
116 lines (104 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use super::{
hardlink_list, DeduplicateSharedSize, HardlinkList, LinkPathList, RecordHardlinks,
RecordHardlinksArgument,
};
use crate::{
data_tree::DataTree,
device::DeviceNumber,
inode::InodeNumber,
os_string_display::OsStringDisplay,
reporter::{event::HardlinkDetection, Event, Reporter},
size,
};
use derive_more::{AsMut, AsRef, Display, Error, From, Into};
use pipe_trait::Pipe;
use smart_default::SmartDefault;
use std::{convert::Infallible, fmt::Debug, os::unix::fs::MetadataExt, path::Path};
/// Be aware of hardlinks. Treat them as links that share space.
/// Detect files with more than 1 links and record them.
/// Deduplicate them (remove duplicated size) from total size to
/// accurately reflect the real size of their containers.
#[derive(Debug, SmartDefault, Clone, AsRef, AsMut, From, Into)]
pub struct Aware<Size> {
/// Map an inode number and device number to their size and detected paths.
record: HardlinkList<Size>,
}
pub use Aware as HardlinkAware;
impl<Size> Aware<Size> {
/// Create new hardlinks handler.
pub fn new() -> Self {
HardlinkList::default().pipe(Aware::from)
}
/// Create a detector/recorder of hardlinks.
pub fn from_record(record: HardlinkList<Size>) -> Self {
Aware::from(record)
}
}
/// Error that occurs when [`Aware::record_hardlinks`] fails.
#[derive(Debug, Display, Error)]
#[non_exhaustive]
pub enum ReportHardlinksError<Size> {
/// Fail to add an entry to the record.
#[display("Fail to add an entry to record: {_0}")]
AddToRecord(hardlink_list::AddError<Size>),
}
impl<Size, Report> RecordHardlinks<Size, Report> for Aware<Size>
where
Size: size::Size + Eq + Debug,
Report: Reporter<Size> + ?Sized,
{
type Error = ReportHardlinksError<Size>;
fn record_hardlinks(
&self,
argument: RecordHardlinksArgument<Size, Report>,
) -> Result<(), Self::Error> {
let RecordHardlinksArgument {
path,
stats,
size,
reporter,
} = argument;
if stats.is_dir() {
return Ok(());
}
let links = stats.nlink();
if links <= 1 {
return Ok(());
}
reporter.report(Event::DetectHardlink(HardlinkDetection {
path,
stats,
size,
links,
}));
let ino = InodeNumber::get(stats);
let dev = DeviceNumber::get(stats);
self.record
.add(ino, dev, size, links, path)
.map_err(ReportHardlinksError::AddToRecord)
}
}
impl<Size> DeduplicateSharedSize<Size> for Aware<Size>
where
DataTree<OsStringDisplay, Size>: Send,
Size: size::Size + Sync,
{
type Report = HardlinkList<Size>;
type Error = Infallible;
fn deduplicate(
self,
data_tree: &mut DataTree<OsStringDisplay, Size>,
) -> Result<Self::Report, Self::Error> {
let record: Self::Report = self.into();
let hardlink_info: Box<[(Size, LinkPathList)]> = record
.iter()
.map(|values| (*values.size(), values.paths().clone()))
.collect();
let hardlink_info: Box<[(Size, Vec<&Path>)]> = hardlink_info
.iter()
.map(|(size, paths)| (*size, paths.iter().map(AsRef::as_ref).collect()))
.collect();
data_tree.par_deduplicate_hardlinks(&hardlink_info);
Ok(record)
}
}