From 47f0045f8c2de9128834bdf1a05dca8cec3fd882 Mon Sep 17 00:00:00 2001 From: Gaspard WITRAND Date: Tue, 22 Oct 2024 09:36:01 +0200 Subject: [PATCH] Implementation of a PAX header writer This crate currently supports reading the PAX headers. This adds a way to write some PAX headers in a Builder. --- src/pax.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ tests/all.rs | 38 ++++++++++++++++++++++++++++++++++++-- tests/entry.rs | 3 ++- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/pax.rs b/src/pax.rs index 6e83edce..f2e0e1ae 100644 --- a/src/pax.rs +++ b/src/pax.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] use std::io; +use std::io::Write; use std::slice; use std::str; @@ -145,3 +146,45 @@ impl<'entry> PaxExtension<'entry> { self.value } } + +/// Extension trait for `Builder` to append PAX extended headers. +impl crate::Builder { + /// Append PAX extended headers to the archive. + /// + /// Takes in an iterator over the list of headers to add to convert it into a header set formatted. + /// + /// Returns io::Error if an error occurs, else it returns () + pub fn append_pax_extensions<'a>(&mut self, headers: impl IntoIterator) -> Result<(), io::Error> { + // Store the headers formatted before write + let mut data: Vec = Vec::new(); + + // For each key in headers, convert into a sized space and add it to data. + // This will then be written in the file + for (key, value) in headers { + let mut len_len = 1; + let mut max_len = 10; + let rest_len = 3 + key.len() + value.len(); + while rest_len + len_len >= max_len { + len_len += 1; + max_len *= 10; + } + let len = rest_len + len_len; + write!(&mut data, "{} {}={}\n", len, key, value)?; + } + + // Ignore the header append if it's empty. + if data.is_empty() { + return Ok(()); + } + + // Create a header of type XHeader, set the size to the length of the + // data, set the entry type to XHeader, and set the checksum + // then append the header and the data to the archive. + let mut header = crate::Header::new_ustar(); + let data_as_bytes: &[u8] = &data; + header.set_size(data_as_bytes.len() as u64); + header.set_entry_type(crate::EntryType::XHeader); + header.set_cksum(); + self.append(&header, data_as_bytes) + } +} diff --git a/tests/all.rs b/tests/all.rs index 27a6fcf1..51b86c4a 100644 --- a/tests/all.rs +++ b/tests/all.rs @@ -6,12 +6,14 @@ extern crate xattr; use std::fs::{self, File}; use std::io::prelude::*; -use std::io::{self, Cursor}; +use std::io::{self, BufWriter, Cursor}; use std::iter::repeat; use std::path::{Path, PathBuf}; use filetime::FileTime; -use tar::{Archive, Builder, Entries, EntryType, Header, HeaderMode}; +use tar::{ + Archive, Builder, BuilderExt, Entries, Entry, EntryType, Header, HeaderMode, PaxBuilder, +}; use tempfile::{Builder as TempBuilder, TempDir}; macro_rules! t { @@ -925,6 +927,38 @@ fn pax_simple() { assert_eq!(third.value(), Ok("1453146164.953123768")); } +#[test] +fn pax_simple_write() { + let td = t!(TempBuilder::new().prefix("tar-rs").tempdir()); + let pax_path = td.path().join("pax.tar"); + let file: File = t!(File::create(&pax_path)); + let mut ar: Builder> = Builder::new(BufWriter::new(file)); + + let mut pax_builder: PaxBuilder = PaxBuilder::new(); + let key: String = "arbitrary_pax_key".to_string(); + let value: String = "arbitrary_pax_value".to_string(); + pax_builder.add(&key, &value); + + t!(ar.append_pax_extensions(&pax_builder)); + t!(ar.append_file("test2", &mut t!(File::open(&pax_path)))); + t!(ar.finish()); + drop(ar); + + let mut archive_opened = Archive::new(t!(File::open(pax_path))); + let mut entries = t!(archive_opened.entries()); + let mut f: Entry = t!(entries.next().unwrap()); + let pax_headers = t!(f.pax_extensions()); + + assert!(pax_headers.is_some(), "pax_headers is None"); + let mut pax_headers = pax_headers.unwrap(); + let pax_arbitrary = t!(pax_headers.next().unwrap()); + + assert_eq!(pax_arbitrary.key(), Ok("arbitrary_pax_key")); + assert_eq!(pax_arbitrary.value(), Ok("arbitrary_pax_value")); + + assert!(entries.next().is_none()); +} + #[test] fn pax_path() { let mut ar = Archive::new(tar!("pax2.tar")); diff --git a/tests/entry.rs b/tests/entry.rs index 62df663e..4d612e2c 100644 --- a/tests/entry.rs +++ b/tests/entry.rs @@ -1,7 +1,8 @@ extern crate tar; extern crate tempfile; -use std::fs::{create_dir, File}; +use std::fs::create_dir; +use std::fs::File; use std::io::Read; use tempfile::Builder;