Skip to content

Commit 352fec7

Browse files
committed
fix untarring tarballs with subdirectories
1 parent 3e08a2c commit 352fec7

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "godot-package-manager"
3-
version = "0.5.0"
3+
version = "0.5.1"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

src/package.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::fs::{create_dir_all, read_dir, read_to_string, remove_dir_all, write};
66
use std::io;
77
use std::path::{Component::Normal, Path, PathBuf};
88
use std::{collections::HashMap, fmt};
9-
use tar::Archive;
9+
use tar::{Archive, EntryType::Directory};
1010

1111
const REGISTRY: &str = "https://registry.npmjs.org";
1212

@@ -95,24 +95,42 @@ impl Package {
9595
.expect("Tarball should be bytes");
9696

9797
/// Emulates `tar xzf archive --strip-components=1 --directory=P`.
98-
pub fn unpack<P, R>(mut archive: Archive<R>, dst: P) -> io::Result<()>
98+
pub fn unpack<R>(mut archive: Archive<R>, dst: &Path) -> io::Result<()>
9999
where
100-
P: AsRef<Path>,
101100
R: io::Read,
102101
{
103-
if dst.as_ref().symlink_metadata().is_err() {
102+
if dst.symlink_metadata().is_err() {
104103
create_dir_all(&dst)?;
105104
}
106105

106+
let dst = &dst.canonicalize().unwrap_or(dst.to_path_buf());
107+
108+
// Delay any directory entries until the end (they will be created if needed by
109+
// descendants), to ensure that directory permissions do not interfer with descendant
110+
// extraction.
111+
let mut directories = Vec::new();
107112
for entry in archive.entries()? {
108-
let mut entry = entry?;
109-
let path: PathBuf = entry
110-
.path()?
111-
.components()
112-
.skip(1) // strip top-level directory
113-
.filter(|c| matches!(c, Normal(_))) // prevent traversal attacks
114-
.collect();
115-
entry.unpack(dst.as_ref().join(path))?;
113+
let entry = entry?;
114+
let mut entry = (
115+
dst.join(
116+
entry
117+
.path()?
118+
.components()
119+
.skip(1)
120+
.filter(|c| matches!(c, Normal(_)))
121+
.collect::<PathBuf>(),
122+
),
123+
entry,
124+
);
125+
if entry.1.header().entry_type() == Directory {
126+
directories.push(entry);
127+
} else {
128+
create_dir_all(entry.0.parent().unwrap())?;
129+
entry.1.unpack(entry.0)?;
130+
}
131+
}
132+
for mut dir in directories {
133+
dir.1.unpack(dir.0)?;
116134
}
117135
Ok(())
118136
}

0 commit comments

Comments
 (0)