docker_registry/
render.rs1use std::{fs, io, path};
7
8use libflate::gzip;
9
10#[derive(Debug, thiserror::Error)]
11pub enum RenderError {
12 #[error("wrong target path {}: must be absolute path to existing directory", _0.display())]
13 WrongTargetPath(path::PathBuf),
14 #[error("io error")]
15 Io(#[from] std::io::Error),
16}
17
18pub fn unpack(layers: &[Vec<u8>], target_dir: &path::Path) -> Result<(), RenderError> {
23 _unpack(layers, target_dir, |mut archive, target_dir| {
24 Ok(archive.unpack(target_dir)?)
25 })
26}
27
28pub fn filter_unpack<P>(layers: &[Vec<u8>], target_dir: &path::Path, predicate: P) -> Result<(), RenderError>
34where
35 P: Fn(&path::Path) -> bool,
36{
37 _unpack(layers, target_dir, |mut archive, target_dir| {
38 for entry in archive.entries()? {
39 let mut entry = entry?;
40 let path = entry.path()?;
41
42 if predicate(&path) {
43 entry.unpack_in(target_dir)?;
44 }
45 }
46
47 Ok(())
48 })
49}
50
51fn _unpack<U>(layers: &[Vec<u8>], target_dir: &path::Path, unpacker: U) -> Result<(), RenderError>
52where
53 U: Fn(tar::Archive<gzip::Decoder<&[u8]>>, &path::Path) -> Result<(), RenderError>,
54{
55 if !target_dir.is_absolute() || !target_dir.exists() || !target_dir.is_dir() {
56 return Err(RenderError::WrongTargetPath(target_dir.to_path_buf()));
57 }
58 for l in layers {
59 let gz_dec = gzip::Decoder::new(l.as_slice())?;
61 let mut archive = tar::Archive::new(gz_dec);
62 archive.set_preserve_permissions(true);
63 archive.set_unpack_xattrs(true);
64 unpacker(archive, target_dir)?;
65
66 let gz_dec = gzip::Decoder::new(l.as_slice())?;
68 let mut archive = tar::Archive::new(gz_dec);
69 for entry in archive.entries()? {
70 let file = entry?;
71 let path = file.path()?;
72 let parent = path.parent().unwrap_or_else(|| path::Path::new("/"));
73 if let Some(fname) = path.file_name() {
74 let wh_name = fname.to_string_lossy();
75 if wh_name == ".wh..wh..opq" {
76 } else if wh_name.starts_with(".wh.") {
78 let rel_parent = path::PathBuf::from("./".to_string() + &parent.to_string_lossy());
79
80 let real_name = wh_name.trim_start_matches(".wh.");
82 let abs_real_path = target_dir.join(&rel_parent).join(real_name);
83 remove_whiteout(abs_real_path)?;
84
85 let abs_wh_path = target_dir.join(&rel_parent).join(fname);
87 remove_whiteout(abs_wh_path)?;
88 };
89 }
90 }
91 }
92 Ok(())
93}
94
95fn remove_whiteout(path: path::PathBuf) -> io::Result<()> {
100 let res = fs::remove_dir_all(path);
101
102 match res {
103 Ok(_) => res,
104 Err(ref e) => match e.kind() {
105 io::ErrorKind::NotFound => Ok(()),
106 _ => res,
107 },
108 }
109}