Skip to content

Commit 3764d59

Browse files
refactor files_by_unix_mode
1 parent 98de40b commit 3764d59

File tree

1 file changed

+52
-36
lines changed

1 file changed

+52
-36
lines changed

src/read.rs

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,33 @@ impl<'a> TryFrom<&'a CentralDirectoryEndInfo> for CentralDirectoryInfo {
536536
}
537537
}
538538

539+
/// Store all entries which specify a numeric "mode" which is familiar to POSIX operating systems.
540+
#[cfg(unix)]
541+
#[derive(Default, Debug)]
542+
struct FilesByUnixMode {
543+
map: std::collections::BTreeMap<PathBuf, u32>,
544+
}
545+
546+
#[cfg(unix)]
547+
impl FilesByUnixMode {
548+
pub fn add_mode(&mut self, path: PathBuf, mode: u32) {
549+
// We don't print a warning or consider it remotely out of the ordinary to receive two
550+
// separate modes for the same path: just take the later one.
551+
let _ = self.map.insert(path, mode);
552+
}
553+
554+
// Child nodes will be sorted later lexicographically, so reversing the order puts them first.
555+
pub fn all_perms_with_children_first(
556+
self,
557+
) -> impl IntoIterator<Item = (PathBuf, std::fs::Permissions)> {
558+
use std::os::unix::fs::PermissionsExt;
559+
self.map
560+
.into_iter()
561+
.rev()
562+
.map(|(p, m)| (p, std::fs::Permissions::from_mode(m)))
563+
}
564+
}
565+
539566
impl<R> ZipArchive<R> {
540567
pub(crate) fn from_finalized_writer(
541568
files: IndexMap<Box<str>, ZipFileData>,
@@ -885,33 +912,30 @@ impl<R: Read + Seek> ZipArchive<R> {
885912
.transpose()?;
886913

887914
#[cfg(unix)]
888-
let mut files_by_unix_mode = Vec::new();
915+
let mut files_by_unix_mode = FilesByUnixMode::default();
889916

890917
for i in 0..self.len() {
891918
let mut file = self.by_index(i)?;
892919

893920
let mut outpath = directory.clone();
921+
/* TODO: the control flow of this method call and subsequent expectations about the
922+
* values in this loop is extremely difficult to follow. It also appears to
923+
* perform a nested loop upon extracting every single file entry? Why does it
924+
* accept two arguments that point to the same directory path, one mutable? */
894925
file.safe_prepare_path(directory.as_ref(), &mut outpath, root_dir.as_ref())?;
895926

896927
let mut symlink_target: Option<Vec<u8>> = None;
897-
cfg_if! {
898-
if #[cfg(any(unix, windows))] {
899-
if file.is_symlink() {
900-
let mut target = Vec::with_capacity(file.size() as usize);
901-
file.read_to_end(&mut target)?;
902-
symlink_target = Some(target);
903-
}
904-
} else {
905-
if file.is_symlink() {
906-
todo!("no support for symlinks outside of unix or windows platforms");
907-
}
908-
}
928+
#[cfg(any(unix, windows))]
929+
if file.is_symlink() {
930+
let mut target = Vec::with_capacity(file.size() as usize);
931+
file.read_to_end(&mut target)?;
932+
symlink_target = Some(target);
909933
}
934+
910935
if symlink_target.is_none() && file.is_dir() {
911936
crate::read::make_writable_dir_all(&outpath)?;
912937
continue;
913938
}
914-
915939
if let Some(target) = symlink_target {
916940
drop(file);
917941
make_symlink(&outpath, &target, &self.shared.files)?;
@@ -920,36 +944,28 @@ impl<R: Read + Seek> ZipArchive<R> {
920944
let mut outfile = fs::File::create(&outpath)?;
921945

922946
io::copy(&mut file, &mut outfile)?;
947+
948+
// Check for real permissions, which we'll set in a second pass.
923949
#[cfg(unix)]
924-
{
925-
// Check for real permissions, which we'll set in a second pass
926-
if let Some(mode) = file.unix_mode() {
927-
files_by_unix_mode.push((outpath.clone(), mode));
928-
}
950+
if let Some(mode) = file.unix_mode() {
951+
files_by_unix_mode.add_mode(outpath, mode);
929952
}
953+
954+
// Set original timestamp.
930955
#[cfg(feature = "chrono")]
931-
{
932-
// Set original timestamp.
933-
if let Some(last_modified) = file.last_modified() {
934-
if let Some(t) = datetime_to_systemtime(&last_modified) {
935-
outfile.set_modified(t)?;
936-
}
956+
if let Some(last_modified) = file.last_modified() {
957+
if let Some(t) = datetime_to_systemtime(&last_modified) {
958+
outfile.set_modified(t)?;
937959
}
938960
}
939961
}
940-
#[cfg(unix)]
941-
{
942-
use std::cmp::Reverse;
943-
use std::os::unix::fs::PermissionsExt;
944962

945-
if files_by_unix_mode.len() > 1 {
946-
// Ensure we update children's permissions before making a parent unwritable
947-
files_by_unix_mode.sort_by_key(|(path, _)| Reverse(path.clone()));
948-
}
949-
for (path, mode) in files_by_unix_mode.into_iter() {
950-
fs::set_permissions(&path, fs::Permissions::from_mode(mode))?;
951-
}
963+
// Ensure we update children's permissions before making a parent unwritable.
964+
#[cfg(unix)]
965+
for (path, perms) in files_by_unix_mode.all_perms_with_children_first() {
966+
std::fs::set_permissions(path, perms)?;
952967
}
968+
953969
Ok(())
954970
}
955971

0 commit comments

Comments
 (0)