let zip can extract symlink

Signed-off-by: tommady <tommady@users.noreply.github.com>
This commit is contained in:
tommady 2025-04-13 02:49:17 +00:00
parent cdee3dbe88
commit fcf38ae6c9
No known key found for this signature in database
GPG Key ID: 175B664929DF2F2F
2 changed files with 31 additions and 6 deletions

View File

@ -30,12 +30,11 @@ pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool)
let mut files_unpacked = 0; let mut files_unpacked = 0;
for file in archive.entries()? { for file in archive.entries()? {
let mut file = file?; let mut file = file?;
let entry_type = file.header().entry_type();
match file.header().entry_type() {
tar::EntryType::Symlink => {
let relative_path = file.path()?.to_path_buf(); let relative_path = file.path()?.to_path_buf();
let full_path = output_folder.join(&relative_path); let full_path = output_folder.join(&relative_path);
match entry_type {
tar::EntryType::Symlink => {
let target = file let target = file
.link_name()? .link_name()?
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Missing symlink target"))?; .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::InvalidData, "Missing symlink target"))?;

View File

@ -85,8 +85,23 @@ where
)); ));
} }
let mode = file.unix_mode().ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::InvalidData, "Cannot extract file's mode")
})?;
let is_symlink = (mode & 0o170000) == 0o120000;
if is_symlink {
let mut target = String::new();
file.read_to_string(&mut target)?;
#[cfg(unix)]
std::os::unix::fs::symlink(&target, &file_path)?;
#[cfg(windows)]
std::os::windows::fs::symlink_file(&target, &file_path)?;
} else {
let mut output_file = fs::File::create(file_path)?; let mut output_file = fs::File::create(file_path)?;
io::copy(&mut file, &mut output_file)?; io::copy(&mut file, &mut output_file)?;
}
set_last_modified_time(&file, file_path)?; set_last_modified_time(&file, file_path)?;
} }
@ -232,6 +247,17 @@ where
if metadata.is_dir() { if metadata.is_dir() {
writer.add_directory(entry_name, options)?; writer.add_directory(entry_name, options)?;
} else if path.is_symlink() {
let target_path = path.read_link()?;
let target_name = target_path.to_str().ok_or_else(|| {
FinalError::with_title("Zip requires that all directories names are valid UTF-8")
.detail(format!("File at '{target_path:?}' has a non-UTF-8 name"))
})?;
// This approach writes the symlink target path as the content of the symlink entry.
// We detect symlinks during extraction by checking for the Unix symlink mode (0o120000) in the entry's permissions.
let symlink_options = options.unix_permissions(0o120777);
writer.add_symlink(entry_name, target_name, symlink_options)?;
} else { } else {
#[cfg(not(unix))] #[cfg(not(unix))]
let options = if is_executable::is_executable(path) { let options = if is_executable::is_executable(path) {