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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
use std::ffi::OsString;
use std::io::prelude::*;
use std::os::unix::ffi::OsStringExt;
use std::path::Path;
use openssl::hash::{DigestBytes, Hasher, MessageDigest};
use rustc_serialize::hex::ToHex;
use super::Result;
use super::suretree::{SureFile, SureTree};
use super::escape::*;
use super::progress::Progress;
pub trait SureHash {
fn hash_estimate(&self) -> Estimate;
fn hash_update(&mut self, path: &Path, meter: &mut Progress);
}
#[derive(Debug)]
pub struct Estimate {
pub files: u64,
pub bytes: u64,
}
impl SureHash for SureTree {
fn hash_estimate(&self) -> Estimate {
let mut est = Estimate { files: 0, bytes: 0 };
est.update(self);
est
}
fn hash_update(&mut self, path: &Path, meter: &mut Progress) {
for d in &mut self.children {
let s: OsString = OsStringExt::from_vec(d.name.unescape().unwrap());
let cpath = path.join(&s);
d.hash_update(&cpath, meter);
}
for f in &mut self.files {
if !f.needs_hash() {
continue;
}
let s: OsString = OsStringExt::from_vec(f.name.unescape().unwrap());
let fpath = path.join(&s);
match noatime_open(&fpath) {
Ok(mut fd) => {
match hash_file(&mut fd) {
Ok(h) => {
let hex = h.to_hex();
f.atts.insert("sha1".to_string(), hex);
}
Err(e) => {
error!("Unable to has file: '{:?}' ({})", fpath, e);
}
}
}
Err(e) => {
error!("Unable to open '{:?}' for hashing ({})", fpath, e);
}
}
meter.update(1, f.atts["size"].parse().unwrap());
}
}
}
impl SureFile {
fn needs_hash(&self) -> bool {
match (self.atts.get("kind"), self.atts.get("sha1")) {
(Some(k), None) if k == "file" => true,
_ => false,
}
}
}
impl Estimate {
fn update(&mut self, node: &SureTree) {
for f in &node.files {
if f.needs_hash() {
self.files += 1;
self.bytes += f.atts["size"].parse::<u64>().unwrap();
}
}
for d in &node.children {
self.update(d);
}
}
}
fn hash_file<R: Read>(rd: &mut R) -> Result<DigestBytes> {
let mut h = Hasher::new(MessageDigest::sha1())?;
let mut buf = vec![0u8; 8192];
loop {
let count = rd.read(&mut buf)?;
if count == 0 {
break;
}
h.write_all(&buf[0..count])?;
}
Ok(h.finish()?)
}
use self::atime_impl::noatime_open;
#[cfg(target_os = "linux")]
mod atime_impl {
use std::fs::{File, OpenOptions};
use std::os::unix::fs::OpenOptionsExt;
use std::io;
use std::path::Path;
const O_NOATIME: i32 = 0o1000000;
pub fn noatime_open(name: &Path) -> io::Result<File> {
match OpenOptions::new().read(true).custom_flags(O_NOATIME).open(
name,
) {
Ok(f) => Ok(f),
Err(_) => OpenOptions::new().read(true).open(name),
}
}
}
#[cfg(not(target_os = "linux"))]
mod atime_impl {
use std::fs::{File, OpenOptions};
use std::path::Path;
use std::io;
pub fn noatime_open(name: &Path) -> io::Result<File> {
OpenOptions::new().read(true).open(name)
}
}