Skip to content

Instantly share code, notes, and snippets.

@cgwalters
Created July 25, 2025 20:07
Show Gist options
  • Save cgwalters/f9e9afcc8d94572554184c3b2d5b054e to your computer and use it in GitHub Desktop.
Save cgwalters/f9e9afcc8d94572554184c3b2d5b054e to your computer and use it in GitHub Desktop.
tar metadump
//! Given an input tarball on stdin, output a copy of it where all regular
//! file data is empty. This is a bit like how e.g. XFS supports "metadump".
use std::io::{stdin, stdout};
use clap::Parser;
use color_eyre::eyre::eyre;
use color_eyre::{eyre::Report, Result};
#[derive(Parser, Debug)]
#[clap(author, version, about)]
struct Args {}
/// Copy a tar entry to a new tar archive, optionally using a different filesystem path.
pub(crate) fn copy_entry(
mut entry: tar::Entry<impl std::io::Read>,
dest: &mut tar::Builder<impl std::io::Write>,
) -> Result<()> {
// Make copies of both the header and path, since that's required for the append APIs
let path = (*entry.path()?).to_owned();
let mut header = entry.header().clone();
if let Some(headers) = entry.pax_extensions()? {
let extensions = headers
.map(|ext| {
let ext = ext?;
Ok((ext.key()?, ext.value_bytes()))
})
.collect::<Result<Vec<_>>>()?;
dest.append_pax_extensions(extensions.as_slice().iter().copied())?;
}
// Need to use the entry.link_name() not the header.link_name()
// api as the header api does not handle long paths:
// https://github.com/alexcrichton/tar-rs/issues/192
match entry.header().entry_type() {
tar::EntryType::Symlink => {
let target = entry.link_name()?.ok_or_else(|| eyre!("Invalid link"))?;
dest.append_link(&mut header, path, target)
}
tar::EntryType::Link => {
let target = entry.link_name()?.ok_or_else(|| eyre!("Invalid link"))?;
dest.append_link(&mut header, path, target)
}
_ => {
header.set_size(0);
dest.append_data(&mut header, path, std::io::empty())
}
}
.map_err(Into::into)
}
#[tokio::main]
async fn main() -> Result<(), Report> {
color_eyre::install()?;
let _args = Args::parse();
let mut i = tar::Archive::new(stdin().lock());
let mut o = tar::Builder::new(stdout().lock());
for entry in i.entries()? {
copy_entry(entry?, &mut o)?;
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment