wasmer_cli/utils/
unpack.rs

1#![allow(unused)]
2
3use crate::common::normalize_path;
4use std::path::{Path, PathBuf};
5
6/// Convenience function that will unpack .tar.gz files and .tar.bz
7/// files to a target directory (does NOT remove the original .tar.gz)
8pub fn try_unpack_targz<P: AsRef<Path>>(
9    target_targz_path: P,
10    target_path: P,
11    strip_toplevel: bool,
12) -> Result<PathBuf, anyhow::Error> {
13    let target_targz_path = target_targz_path.as_ref().to_string_lossy().to_string();
14    let target_targz_path = normalize_path(&target_targz_path);
15    let target_targz_path = Path::new(&target_targz_path);
16
17    let target_path = target_path.as_ref().to_string_lossy().to_string();
18    let target_path = normalize_path(&target_path);
19    let target_path = Path::new(&target_path);
20
21    let open_file = || {
22        std::fs::File::open(target_targz_path)
23            .map_err(|e| anyhow::anyhow!("failed to open {}: {e}", target_targz_path.display()))
24    };
25
26    let try_decode_gz = || {
27        let file = open_file()?;
28        let gz_decoded = flate2::read::GzDecoder::new(&file);
29        let ar = tar::Archive::new(gz_decoded);
30        if strip_toplevel {
31            unpack_sans_parent(ar, target_path).map_err(|e| {
32                anyhow::anyhow!(
33                    "failed to unpack (gz_ar_unpack_sans_parent) {}: {e}",
34                    target_targz_path.display()
35                )
36            })
37        } else {
38            unpack_with_parent(ar, target_path).map_err(|e| {
39                anyhow::anyhow!(
40                    "failed to unpack (gz_ar_unpack) {}: {e}",
41                    target_targz_path.display()
42                )
43            })
44        }
45    };
46
47    let try_decode_xz = || {
48        let file = open_file()?;
49        let mut decomp: Vec<u8> = Vec::new();
50        let mut bufread = std::io::BufReader::new(&file);
51        lzma_rs::xz_decompress(&mut bufread, &mut decomp).map_err(|e| {
52            anyhow::anyhow!(
53                "failed to unpack (try_decode_xz) {}: {e}",
54                target_targz_path.display()
55            )
56        })?;
57
58        let cursor = std::io::Cursor::new(decomp);
59        let mut ar = tar::Archive::new(cursor);
60        if strip_toplevel {
61            unpack_sans_parent(ar, target_path).map_err(|e| {
62                anyhow::anyhow!(
63                    "failed to unpack (sans parent) {}: {e}",
64                    target_targz_path.display()
65                )
66            })
67        } else {
68            ar.unpack(target_path).map_err(|e| {
69                anyhow::anyhow!(
70                    "failed to unpack (with parent) {}: {e}",
71                    target_targz_path.display()
72                )
73            })
74        }
75    };
76
77    try_decode_gz().or_else(|e1| {
78        try_decode_xz()
79            .map_err(|e2| anyhow::anyhow!("could not decode gz: {e1}, could not decode xz: {e2}"))
80    })?;
81
82    Ok(Path::new(&target_targz_path).to_path_buf())
83}
84
85pub fn unpack_with_parent<R>(mut archive: tar::Archive<R>, dst: &Path) -> Result<(), anyhow::Error>
86where
87    R: std::io::Read,
88{
89    use std::path::Component::Normal;
90
91    let dst_normalized = normalize_path(dst.to_string_lossy().as_ref());
92
93    for entry in archive.entries()? {
94        let mut entry = entry?;
95        let path: PathBuf = entry
96            .path()?
97            .components()
98            .skip(1) // strip top-level directory
99            .filter(|c| matches!(c, Normal(_))) // prevent traversal attacks
100            .collect();
101        if entry.header().entry_type().is_file() {
102            entry.unpack_in(&dst_normalized)?;
103        } else if entry.header().entry_type() == tar::EntryType::Directory {
104            std::fs::create_dir_all(Path::new(&dst_normalized).join(&path))?;
105        }
106    }
107    Ok(())
108}
109
110pub fn unpack_sans_parent<R>(mut archive: tar::Archive<R>, dst: &Path) -> std::io::Result<()>
111where
112    R: std::io::Read,
113{
114    use std::path::Component::Normal;
115
116    let dst_normalized = normalize_path(dst.to_string_lossy().as_ref());
117
118    for entry in archive.entries()? {
119        let mut entry = entry?;
120        let path: PathBuf = entry
121            .path()?
122            .components()
123            .skip(1) // strip top-level directory
124            .filter(|c| matches!(c, Normal(_))) // prevent traversal attacks
125            .collect();
126        entry.unpack(Path::new(&dst_normalized).join(path))?;
127    }
128    Ok(())
129}