Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Very basic Pax header writer #382

Merged
merged 2 commits into from
Oct 24, 2024
Merged
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
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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't an extension trait anymore

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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't care too much but this change seem spurious i.e. unnecessary

use std::fs::File;
use std::io::Read;

use tempfile::Builder;
Expand Down