diff --git a/Cargo.lock b/Cargo.lock index f8b0d2e48..3366f953f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1145,7 +1145,6 @@ name = "cpio-archive" version = "0.10.0" dependencies = [ "chrono", - "is_executable", "simple-file-manifest", "thiserror 2.0.18", ] @@ -2359,15 +2358,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is_executable" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baabb8b4867b26294d818bf3f651a454b6901431711abb96e296245888d6e8c4" -dependencies = [ - "windows-sys 0.60.2", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" diff --git a/cpio-archive/Cargo.toml b/cpio-archive/Cargo.toml index 272497b36..165fcfee6 100644 --- a/cpio-archive/Cargo.toml +++ b/cpio-archive/Cargo.toml @@ -13,6 +13,5 @@ readme = "README.md" [dependencies] chrono = "0.4.43" -is_executable = "1.0.5" simple-file-manifest = "0.11.0" thiserror = "2.0.18" diff --git a/cpio-archive/src/odc.rs b/cpio-archive/src/odc.rs index 486a44c06..629f90779 100644 --- a/cpio-archive/src/odc.rs +++ b/cpio-archive/src/odc.rs @@ -11,13 +11,13 @@ use { crate::{CpioHeader, CpioReader, CpioResult, Error}, chrono::{DateTime, Utc}, - is_executable::IsExecutable, simple_file_manifest::{ FileManifest, S_IFDIR, S_IRGRP, S_IROTH, S_IRUSR, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR, }, std::{ collections::HashSet, ffi::CStr, + fs, io::{Read, Take, Write}, path::Path, }, @@ -70,6 +70,33 @@ fn write_octal(value: u64, writer: &mut impl Write, size: usize) -> CpioResult<( Ok(()) } +/// permissions_to_u32 converts fs::Permissions objects to chmod integers. +pub fn permissions_to_u32(permissions: fs::Permissions) -> u32 { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + permissions.mode() + } + #[cfg(windows)] + { + if permissions.readonly() { + 0o444u32 + } else { + 0o666u32 + } + } +} + +/// GENERIC_DIRECTORY_MODE applies to auto generated directories. +const GENERIC_DIRECTORY_MODE: u32 = S_IFDIR + | S_IRUSR + | S_IWUSR + | S_IXUSR + | S_IRGRP + | S_IXGRP + | S_IROTH + | S_IXOTH; + /// Parsed portable ASCII format header. #[derive(Clone, Debug)] pub struct OdcHeader { @@ -297,8 +324,8 @@ pub struct OdcBuilder { default_uid: u32, default_gid: u32, default_mtime: DateTime, - default_mode_file: u32, - default_mode_dir: u32, + default_mode_file: Option, + default_mode_dir: Option, auto_write_dirs: bool, seen_dirs: HashSet, entry_count: u32, @@ -313,15 +340,8 @@ impl OdcBuilder { default_uid: 0, default_gid: 0, default_mtime: Utc::now(), - default_mode_file: S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, - default_mode_dir: S_IFDIR - | S_IRUSR - | S_IWUSR - | S_IXUSR - | S_IRGRP - | S_IXGRP - | S_IROTH - | S_IXOTH, + default_mode_file: None, + default_mode_dir: None, auto_write_dirs: true, seen_dirs: HashSet::new(), entry_count: 0, @@ -330,12 +350,12 @@ impl OdcBuilder { } /// Set the default file mode to use for files. - pub fn default_mode_file(&mut self, mode: u32) { + pub fn default_mode_file(&mut self, mode: Option) { self.default_mode_file = mode; } /// Set the default file mode to use for directories. - pub fn default_mode_directory(&mut self, mode: u32) { + pub fn default_mode_directory(&mut self, mode: Option) { self.default_mode_dir = mode; } @@ -372,7 +392,7 @@ impl OdcBuilder { OdcHeader { dev: 0, inode, - mode: self.default_mode_file, + mode: 0u32, uid: self.default_uid, gid: self.default_gid, nlink: 0, @@ -407,7 +427,7 @@ impl OdcBuilder { if !self.seen_dirs.contains(&dir) { let mut header = self.next_header(); - header.mode = self.default_mode_dir; + header.mode = self.default_mode_file.unwrap_or(GENERIC_DIRECTORY_MODE); header.name = dir.clone(); bytes_written += header.write(&mut self.writer)?; @@ -418,6 +438,18 @@ impl OdcBuilder { Ok(bytes_written) } + /// Append a raw header, such as for a directory. + /// + /// The writer is written as-is. + /// + /// Automatic directory emission is not processed in this mode. + pub fn append_header( + &mut self, + header: OdcHeader, + ) -> CpioResult { + header.write(&mut self.writer) + } + /// Append a raw header and corresponding file data to the writer. /// /// The writer and data are written as-is. @@ -516,10 +548,7 @@ impl OdcBuilder { let mut header = self.next_header(); header.name = archive_path; header.file_size = metadata.len(); - - if path.is_executable() { - header.mode |= S_IXUSR | S_IXGRP | S_IXOTH; - } + header.mode = self.default_mode_file.unwrap_or(permissions_to_u32(metadata.permissions())); bytes_written += header.write(&mut self.writer)?; bytes_written += std::io::copy(&mut fh, &mut self.writer)?; @@ -532,7 +561,19 @@ impl OdcBuilder { let mut bytes_written = 0; for (path, entry) in manifest.iter_entries() { - let mode = if entry.is_executable() { 0o755 } else { 0o644 }; + let metadata = path.metadata()?; + let mut mode = permissions_to_u32(metadata.permissions()); + + if metadata.is_file() { + if let Some(m) = self.default_mode_file { + mode = m; + } + } else if metadata.is_dir() { + if let Some(m) = self.default_mode_dir { + mode = m; + } + } + let data = entry.resolve_content()?; bytes_written += self.append_file_from_data(path.display().to_string(), data, mode)?;