cyfs_util/pkg/
zip_package.rs1extern crate walkdir;
2use cyfs_sha2::{Digest, Sha256};
3use std::error::Error;
4
5use std::fs::File;
6use std::path::{Path, PathBuf};
7use walkdir::{DirEntry, WalkDir};
8use zip;
9use std::fmt::Write;
10use std::io::{Read};
11
12
13pub struct ZipPackage {
14 src_dir: PathBuf,
15 all: Vec<PathBuf>,
16 hash: Option<String>,
17 zip_file: Option<zip::ZipWriter<std::fs::File>>,
18}
19
20impl ZipPackage {
21 pub fn new() -> ZipPackage {
22 ZipPackage {
23 src_dir: PathBuf::from(""),
24 all: Vec::new(),
25 hash: None,
26 zip_file: None,
27 }
28 }
29
30 pub fn load(&mut self, dir: &Path) {
31 assert!(self.all.is_empty());
32
33 self.src_dir = dir.to_owned();
34
35 let is_ignore = |entry: &DirEntry| -> bool {
36 entry
37 .file_name()
38 .to_str()
39 .map(|s| s.starts_with("."))
40 .unwrap_or(false)
41 };
42 let walker = WalkDir::new(dir).into_iter();
43 for entry in walker.filter_entry(|e| !is_ignore(e)) {
44 let entry = entry.unwrap();
45 if entry.file_type().is_dir() {
46 continue;
47 }
48
49 let path = entry.path();
50 self.all.push(path.to_path_buf());
52 }
53 self.all.sort();
54 }
55
56 pub fn calc_hash(&mut self) -> Result<String, Box<dyn Error>> {
57 let mut hasher = Sha256::new();
58 for path in &self.all {
59 let ret = File::open(&path);
60 if let Err(e) = ret {
61 let msg = format!("open file error! file={}, err={}", path.display(), e);
62 error!("{}", msg);
63 return Err(Box::<dyn Error>::from(msg));
64 }
65 let mut file = ret.unwrap();
66 let ret = std::io::copy(&mut file, &mut hasher);
67 if let Err(e) = ret {
68 let msg = format!("read file error! file={}, err={}", path.display(), e);
69 error!("{}", msg);
70 return Err(Box::<dyn Error>::from(msg));
71 }
72 }
73 let hex = hasher.result();
74 let mut s = String::new();
75 for &byte in hex.as_slice() {
76 write!(&mut s, "{:X}", byte).expect("Unable to format hex string");
77 }
78
79 self.hash = Some(s.clone());
80
81 Ok(s)
82 }
83
84 pub fn begin_zip(&mut self, dest_file: &str) -> Result<(), Box<dyn Error>> {
85 assert!(self.zip_file.is_none());
86
87 use std::io::Write;
88
89 let target_file = File::create(dest_file).unwrap();
90
91 let mut zip = zip::ZipWriter::new(target_file);
92
93 let options =
94 zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Bzip2);
95 let mut buffer = Vec::new();
96 for path in &self.all {
97 let name = path.strip_prefix(&self.src_dir).unwrap();
98
99 info!(
100 "adding file to zip {} => {} ...",
101 path.display(),
102 name.display()
103 );
104
105 let opt;
119 #[cfg(windows)]
120 {
121 opt = options;
122 }
123
124 #[cfg(not(windows))]
125 {
126 use std::os::unix::fs::PermissionsExt;
127 let metadata = std::fs::metadata(path)?;
128 opt = options.unix_permissions(metadata.permissions().mode());
130 }
131
132 zip.start_file(name.to_string_lossy(), opt)?;
133
134 let ret = File::open(path);
135 if let Err(e) = ret {
136 return Err(Box::new(e));
137 }
138
139 let mut f = ret.unwrap();
140 f.read_to_end(&mut buffer)?;
141 zip.write_all(&*buffer)?;
142
143 buffer.clear();
144 }
145 self.zip_file = Some(zip);
146
147 Ok(())
148 }
149
150 pub fn append_pkg_hash(&mut self) -> Result<(), Box<dyn Error>> {
151 assert!(self.zip_file.is_some());
152
153 if self.hash.is_none() {
154 if let Err(e) = self.calc_hash() {
155 error!("calc hash error! err={}", e);
156 return Err(e);
157 }
158 }
159
160 {
162 let options = zip::write::FileOptions::default()
163 .compression_method(zip::CompressionMethod::Bzip2);
164 let name = Path::new(".hash");
165
166 info!(
167 "adding .hash file to zip {} = {} ...",
168 name.display(),
169 self.hash.as_ref().unwrap()
170 );
171
172 let zip = self.zip_file.as_mut().unwrap();
173 zip.start_file(name.to_string_lossy(), options)?;
174
175 use std::io::Write;
176 zip.write_all(self.hash.as_ref().unwrap().as_bytes())?;
177 }
178
179 Ok(())
180 }
181
182 pub fn append_file(&mut self, path: &Path, bytes: &[u8]) -> Result<(), Box<dyn Error>> {
183 assert!(self.zip_file.is_some());
184
185 let options =
186 zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Bzip2);
187
188 info!("adding file to zip {}...", path.display(),);
189
190 let zip = self.zip_file.as_mut().unwrap();
191 zip.start_file(path.to_string_lossy(), options)?;
192
193 use std::io::Write;
194 zip.write_all(bytes)?;
195
196 Ok(())
197 }
198
199 pub fn finish_zip(&mut self) -> Result<(), Box<dyn Error>> {
200 assert!(self.zip_file.is_some());
201
202 let mut zip = self.zip_file.take().unwrap();
203
204 zip.finish()?;
206
207 Ok(())
208 }
209}