Skip to content

Commit

Permalink
Very basic Pax header writer (#382)
Browse files Browse the repository at this point in the history
* 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.
Also implemented tests to showcase usage

* Pax header handler, switch to not owned data types

Since we can keep the ownership of the data when calling append_pax_extensions, we can use &str and &[u8] according to the lib documentation
  • Loading branch information
gwitrand-ovh authored Oct 24, 2024
1 parent e0e69f6 commit adbbc29
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
48 changes: 48 additions & 0 deletions src/pax.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![allow(dead_code)]
use std::io;
use std::io::Write;
use std::slice;
use std::str;

Expand Down Expand Up @@ -145,3 +146,50 @@ impl<'entry> PaxExtension<'entry> {
self.value
}
}

/// Extension trait for `Builder` to append PAX extended headers.
impl<T: Write> crate::Builder<T> {
/// 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<'key, 'value>(
&mut self,
headers: impl IntoIterator<Item = (&'key str, &'value [u8])>,
) -> Result<(), io::Error> {
// Store the headers formatted before write
let mut data: Vec<u8> = 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, "{} {}=", len, key)?;
data.extend_from_slice(value);
data.push(b'\n');
}

// 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)
}
}
38 changes: 36 additions & 2 deletions tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ 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, Entries, Entry, EntryType, Header, HeaderMode};
use tempfile::{Builder as TempBuilder, TempDir};

macro_rules! t {
Expand Down Expand Up @@ -925,6 +925,40 @@ 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<BufWriter<File>> = Builder::new(BufWriter::new(file));

let pax_extensions = [
("arbitrary_pax_key", b"arbitrary_pax_value".as_slice()),
("SCHILY.xattr.security.selinux", b"foo_t"),
];

t!(ar.append_pax_extensions(pax_extensions));
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<File> = 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"));
let xattr = t!(pax_headers.next().unwrap());
assert_eq!(xattr.key().unwrap(), pax_extensions[1].0);
assert_eq!(xattr.value_bytes(), pax_extensions[1].1);

assert!(entries.next().is_none());
}

#[test]
fn pax_path() {
let mut ar = Archive::new(tar!("pax2.tar"));
Expand Down
3 changes: 2 additions & 1 deletion tests/entry.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down

0 comments on commit adbbc29

Please sign in to comment.