History

extraction of tar archives with identity check (#384)

Sebastian Thiel committed 4 years ago

Commit 07c1f071 parent c30bebf1 file · +42 −5Browse files

Unified · Split

tests/tools/src/lib.rs+42−5
@@ -1,3 +1,4 @@
1+use std::io::Read;
12 use std::{
23 collections::BTreeMap,
34 path::{Path, PathBuf},
@@ -186,12 +187,12 @@ fn create_archive_if_not_on_ci(source_dir: &Path, archive: &Path, script_identit · 182 unmodified lines
186187 res
187188 }
188189
189−const META_NAME: &str = "__gitoxide_meta__";
190+const META_DIR_NAME: &str = "__gitoxide_meta__";
190191 const META_IDENTITY: &str = "identity";
191192 const META_GIT_VERSION: &str = "git-version";
192193
193194 fn populate_meta_dir(destination_dir: &Path, script_identity: u32) -> std::io::Result<PathBuf> {
194− let meta_dir = destination_dir.join(META_NAME);
195+ let meta_dir = destination_dir.join(META_DIR_NAME);
195196 std::fs::create_dir_all(&meta_dir)?;
196197 std::fs::write(meta_dir.join(META_IDENTITY), format!("{}", script_identity).as_bytes())?;
197198 std::fs::write(
@@ -203,10 +204,46 @@ fn populate_meta_dir(destination_dir: &Path, script_identity: u32) -> std::io::R · 5 unmodified lines
203204
204205 /// `required_script_identity` is the identity of the script that generated the state that is contained in `archive`.
205206 /// If this is not the case, the arvhive will be ignored.
206−fn extract_archive(_archive: &Path, destination_dir: &Path, _required_script_identity: u32) -> std::io::Result<()> {
207+fn extract_archive(archive: &Path, destination_dir: &Path, required_script_identity: u32) -> std::io::Result<()> {
207208 std::fs::create_dir_all(destination_dir)?;
208− // TODO
209− Err(std::io::ErrorKind::NotFound.into())
209+
210+ let mut archive_buf = Vec::<u8>::new();
211+ let mut decoder = xz2::bufread::XzDecoder::new(std::io::BufReader::new(std::fs::File::open(archive)?));
212+ std::io::copy(&mut decoder, &mut archive_buf)?;
213+
214+ let mut entry_buf = Vec::<u8>::new();
215+ let archive_identity: u32 = tar::Archive::new(std::io::Cursor::new(&mut &*archive_buf))
216+ .entries_with_seek()?
217+ .filter_map(|e| e.ok())
218+ .find_map(|mut e: tar::Entry<'_, _>| {
219+ let path = e.path().ok()?;
220+ if path.parent()?.file_name()? == META_DIR_NAME && path.file_name()? == META_IDENTITY {
221+ entry_buf.clear();
222+ e.read_to_end(&mut entry_buf).ok()?;
223+ entry_buf.to_str().ok()?.trim().parse().ok()
224+ } else {
225+ None
226+ }
227+ })
228+ .ok_or_else(|| {
229+ std::io::Error::new(
230+ std::io::ErrorKind::Other,
231+ "BUG: Could not find meta directory in our own archive",
232+ )
233+ })?;
234+ if archive_identity != required_script_identity {
235+ return Err(std::io::ErrorKind::NotFound.into());
236+ }
237+
238+ for entry in tar::Archive::new(&mut &*archive_buf).entries()? {
239+ let mut entry = entry?;
240+ let path = entry.path()?;
241+ if path.to_str() == Some(META_DIR_NAME) || path.parent().and_then(|p| p.to_str()) == Some(META_DIR_NAME) {
242+ continue;
243+ }
244+ entry.unpack_in(&destination_dir)?;
245+ }
246+ Ok(())
210247 }
211248
212249 pub fn to_bstr_err(err: nom::Err<VerboseError<&[u8]>>) -> VerboseError<&BStr> {