diff --git a/CHANGELOG.md b/CHANGELOG.md index 19ef66c..61b7d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,6 +142,7 @@ Categories Used: - CI: Rewrite [\#135](https://github.com/ouch-org/ouch/pull/135) ([figsoda](https://github.com/figsoda)) - Improving error messages and removing dead error treatment code [\#140](https://github.com/ouch-org/ouch/pull/140) ([marcospb19](https://github.com/marcospb19)) - Apply clippy lints and simplify smart_unpack [\#267](https://github.com/ouch-org/ouch/pull/267) ([figsoda](https://github.com/figsoda)) +- Respect file permissions when compressing zip files [\#271](https://github.com/ouch-org/ouch/pull/271) ([figsoda](https://github.com/figsoda)) ### Tweaks diff --git a/Cargo.lock b/Cargo.lock index eba92b6..702f634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,6 +384,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "is_executable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +dependencies = [ + "winapi", +] + [[package]] name = "itertools" version = "0.10.3" @@ -525,6 +534,7 @@ dependencies = [ "ignore", "indicatif", "infer", + "is_executable", "libc", "linked-hash-map", "lzzzz", diff --git a/Cargo.toml b/Cargo.toml index d4298b8..2520880 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,9 @@ indicatif = "0.16.2" [target.'cfg(unix)'.dependencies] time = { version = "0.3.9", default-features = false } +[target.'cfg(not(unix))'.dependencies] +is_executable = "1.0.1" + [build-dependencies] clap = { version = "3.1.18", features = ["derive", "env"] } clap_complete = "3.1.4" diff --git a/src/archive/zip.rs b/src/archive/zip.rs index a16468e..9e0e3a5 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -1,5 +1,7 @@ //! Contains Zip-specific building and unpacking functions +#[cfg(unix)] +use std::os::unix::fs::PermissionsExt; use std::{ env, io::{self, prelude::*}, @@ -138,6 +140,9 @@ where let mut writer = zip::ZipWriter::new(writer); let options = zip::write::FileOptions::default(); + #[cfg(not(unix))] + let executable = options.unix_permissions(0o755); + // Vec of any filename that failed the UTF-8 check let invalid_unicode_filenames = get_invalid_utf8_paths(input_filenames); @@ -168,21 +173,33 @@ where // and so on info!(@display_handle, inaccessible, "Compressing '{}'.", to_utf(path)); - if path.is_dir() { + let metadata = match path.metadata() { + Ok(metadata) => metadata, + Err(e) => { + if e.kind() == std::io::ErrorKind::NotFound && utils::is_symlink(path) { + // This path is for a broken symlink + // We just ignore it + continue; + } + return Err(e.into()); + } + }; + + #[cfg(unix)] + let options = options.unix_permissions(metadata.permissions().mode()); + + if metadata.is_dir() { writer.add_directory(path.to_str().unwrap().to_owned(), options)?; } else { - writer.start_file(path.to_str().unwrap().to_owned(), options)?; - let file_bytes = match fs::read(entry.path()) { - Ok(b) => b, - Err(e) => { - if e.kind() == std::io::ErrorKind::NotFound && utils::is_symlink(path) { - // This path is for a broken symlink - // We just ignore it - continue; - } - return Err(e.into()); - } + #[cfg(not(unix))] + let options = if is_executable::is_executable(path) { + executable + } else { + options }; + + writer.start_file(path.to_str().unwrap().to_owned(), options)?; + let file_bytes = fs::read(entry.path())?; writer.write_all(&file_bytes)?; } } @@ -260,7 +277,7 @@ fn set_last_modified_time(file: &fs::File, zip_file: &ZipFile) -> crate::Result< #[cfg(unix)] fn __unix_set_permissions(file_path: &Path, file: &ZipFile) -> crate::Result<()> { - use std::{fs::Permissions, os::unix::fs::PermissionsExt}; + use std::fs::Permissions; if let Some(mode) = file.unix_mode() { fs::set_permissions(file_path, Permissions::from_mode(mode))?;