Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion cpio-archive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
132 changes: 104 additions & 28 deletions cpio-archive/src/odc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -297,12 +324,13 @@ pub struct OdcBuilder<W: Write + Sized> {
default_uid: u32,
default_gid: u32,
default_mtime: DateTime<Utc>,
default_mode_file: u32,
default_mode_dir: u32,
default_mode_file: Option<u32>,
default_mode_dir: Option<u32>,
auto_write_dirs: bool,
seen_dirs: HashSet<String>,
entry_count: u32,
finished: bool,
bytes_written: u64,
}

impl<W: Write + Sized> OdcBuilder<W> {
Expand All @@ -313,29 +341,23 @@ impl<W: Write + Sized> OdcBuilder<W> {
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,
finished: false,
bytes_written: 0,
}
}

/// 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<u32>) {
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<u32>) {
self.default_mode_dir = mode;
}

Expand Down Expand Up @@ -372,7 +394,7 @@ impl<W: Write + Sized> OdcBuilder<W> {
OdcHeader {
dev: 0,
inode,
mode: self.default_mode_file,
mode: 0u32,
uid: self.default_uid,
gid: self.default_gid,
nlink: 0,
Expand Down Expand Up @@ -407,7 +429,7 @@ impl<W: Write + Sized> OdcBuilder<W> {

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)?;
Expand All @@ -418,6 +440,21 @@ impl<W: Write + Sized> OdcBuilder<W> {
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<u64> {
let bytes_written = header.write(&mut self.writer)?;
self.bytes_written += bytes_written;

Ok(bytes_written)
}

/// Append a raw header and corresponding file data to the writer.
///
/// The writer and data are written as-is.
Expand All @@ -437,10 +474,13 @@ impl<W: Write + Sized> OdcBuilder<W> {
return Err(Error::SizeMismatch);
}

let written = header.write(&mut self.writer)?;
let bytes_written = header.write(&mut self.writer)?;
self.bytes_written += bytes_written;
self.writer.write_all(data)?;
let data_len = data.len() as u64;
self.bytes_written += data_len;

Ok(written + data.len() as u64)
Ok(bytes_written + data_len)
}

/// Append a raw header and corresponding data from a reader to the writer.
Expand All @@ -455,13 +495,16 @@ impl<W: Write + Sized> OdcBuilder<W> {
header: OdcHeader,
reader: &mut impl Read,
) -> CpioResult<u64> {
let written = header.write(&mut self.writer)?;
let copied = std::io::copy(reader, &mut self.writer)?;
let bytes_written = header.write(&mut self.writer)?;
self.bytes_written += bytes_written;
let bytes_copied = std::io::copy(reader, &mut self.writer)?;

if copied != header.file_size {
if bytes_copied != header.file_size {
Err(Error::SizeMismatch)
} else {
Ok(written + copied)
self.bytes_written += bytes_copied;

Ok(bytes_written + bytes_copied)
}
}

Expand All @@ -486,6 +529,7 @@ impl<W: Write + Sized> OdcBuilder<W> {
self.writer.write_all(data)?;
bytes_written += data.len() as u64;

self.bytes_written += bytes_written;
Ok(bytes_written)
}

Expand Down Expand Up @@ -516,13 +560,11 @@ impl<W: Write + Sized> OdcBuilder<W> {
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)?;
self.bytes_written += bytes_written;

Ok(bytes_written)
}
Expand All @@ -532,7 +574,19 @@ impl<W: Write + Sized> OdcBuilder<W> {
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)?;
Expand All @@ -550,11 +604,33 @@ impl<W: Write + Sized> OdcBuilder<W> {
pub fn finish(&mut self) -> CpioResult<u64> {
if !self.finished {
let mut header = self.next_header();
header.dev = 0u32;
header.mode = 0u32;
header.uid = 0u32;
header.gid = 0u32;
header.nlink = 1u32; // GNU compatibility
header.rdev = 0u32;
header.mtime = 0u32;
header.file_size = 0u64;
header.name = TRAILER.to_string();
let count = header.write(&mut self.writer)?;
self.bytes_written += count;

let block_size = 512usize;
let mut padding_u64 = 0u64;
let rem = (self.bytes_written as usize) % block_size;

if rem != 0 {
let padding = block_size - rem;
padding_u64 = padding as u64;
let data = vec![0u8; padding];
self.writer.write_all(&data)?;
self.bytes_written += padding_u64;
}

self.finished = true;

Ok(count)
Ok(count + padding_u64)
} else {
Ok(0)
}
Expand Down