difftreelog
feat(jrb) symlinks
in: master
2 files changed
crates/jrsonnet-pkg/src/install/accessor.rsdiffbeforeafterboth9use tracing::warn;9use tracing::warn;10use zip::{ZipArchive, result::ZipError};10use zip::{ZipArchive, result::ZipError};111112use crate::jsonnet_bundler::{SubDir, SubDirEscapeError};12use crate::jsonnet_bundler::{LocalSource, SubDir, SubDirEscapeError};131314#[derive(thiserror::Error, Debug)]14#[derive(thiserror::Error, Debug)]15pub enum Error {15pub enum Error {105 name.clone(),105 name.clone(),106 if entry.is_dir() {106 if entry.is_dir() {107 AccessorEntry::Dir107 AccessorEntry::Dir108 } else if entry.is_file() {108 } else if entry.is_symlink() {109 let mut target = Vec::new();110 entry.read_to_end(&mut target).map_err(Error::ZipIo)?;111 let Ok(target_str) = std::str::from_utf8(&target) else {112 warn!("non-utf8 symlink target in zip entry: {name:?}");113 continue;114 };115 let Ok(target) = LocalSource::from_str(target_str) else {116 warn!("symlink target {target_str:?} at {name:?} escapes sandbox; skipping");117 continue;118 };119 AccessorEntry::Symlink(target)120 } else if entry.is_file() {109 let mut data = Vec::new();121 let mut data = Vec::new();110 entry.read_to_end(&mut data).map_err(Error::ZipIo)?;122 entry.read_to_end(&mut data).map_err(Error::ZipIo)?;111 AccessorEntry::File(data)123 AccessorEntry::File(data)112 } else {124 } else {113 // TODO: Symlinks?114 panic!("unknown accessor entry type: {name:?}")125 warn!("unknown accessor entry type: {name:?}");126 continue;115 },127 },116 )?;128 )?;117 }129 }133pub enum AccessorEntry {145pub enum AccessorEntry {134 Dir,146 Dir,135 File(Vec<u8>),147 File(Vec<u8>),148 Symlink(LocalSource),136}149}137150crates/jrsonnet-pkg/src/install/github.rsdiffbeforeafterboth7 path::{Path, PathBuf},7 path::{Path, PathBuf},8};8};9910use camino::Utf8PathBuf;10use reqwest::{blocking::Response, header};11use reqwest::{blocking::Response, header};11use tracing::{debug, info};12use tracing::{debug, info, warn};121313use super::{14use super::{14 Error, LocalExtraction, ResolveResult, Result, VendorSource,15 Error, LocalExtraction, ResolveResult, Result, VendorSource,85 )?)86 )?)86}87}8889#[cfg(unix)]90fn make_symlink(target: &str, link: &Path) -> std::io::Result<()> {91 std::os::unix::fs::symlink(target, link)92}9394#[cfg(windows)]95fn make_symlink(target: &str, link: &Path) -> std::io::Result<()> {96 std::os::windows::fs::symlink_file(target, link)97}9899#[cfg(not(any(unix, windows)))]100fn make_symlink(_target: &str, _link: &Path) -> std::io::Result<()> {101 Err(std::io::Error::new(102 std::io::ErrorKind::Unsupported,103 "symlinks are not supported on this platform",104 ))105}8710688fn extract_subdir(archive: &ZipFileAccessor, subdir: &SubDir, dest: &Path) -> Result<()> {107fn extract_subdir(archive: &ZipFileAccessor, subdir: &SubDir, dest: &Path) -> Result<()> {89 archive.iter(subdir, &mut |name, entry| {108 archive.iter(subdir, &mut |name, entry| {90 let target = dest.join(name);109 let target = dest.join(&name);91 match entry {110 match entry {92 AccessorEntry::Dir => {111 AccessorEntry::Dir => {93 fs::create_dir_all(&target).map_err(|e| Error::Io(target, e))?;112 fs::create_dir_all(&target).map_err(|e| Error::Io(target, e))?;98 }117 }99 fs::write(&target, &data).map_err(|e| Error::Io(target, e))?;118 fs::write(&target, &data).map_err(|e| Error::Io(target, e))?;100 }119 }120 AccessorEntry::Symlink(link_target) => {121 let symlink_parent = name122 .as_path()123 .parent()124 .map(|p| SubDir::try_from(Utf8PathBuf::from(p)))125 .transpose()126 .expect("parent of a SubDir is a SubDir")127 .unwrap_or_else(SubDir::empty);128 if link_target.resolve_under(&symlink_parent).is_err() {129 warn!("symlink {name} -> {link_target} escapes extraction; skipping");130 return Ok(());131 }132 if let Some(parent) = target.parent() {133 fs::create_dir_all(parent).map_err(|e| Error::Io(parent.to_owned(), e))?;134 }135 make_symlink(&link_target.to_string(), &target)136 .map_err(|e| Error::Io(target, e))?;137 }101 }138 }102 Ok(())139 Ok(())103 })140 })