#!/bin/sh set -eu export LC_ALL=C.UTF-8 trap "rm -f /tmp/mkpaxtar.pl /tmp/orig.tar /tmp/file /tmp/expected /tmp/filtered.tar" EXIT INT TERM cat <<'END' >/tmp/mkpaxtar.pl #!/usr/bin/env perl use strict; use warnings; my @entries = ( # filename mode type content ['./PaxHeaders/file', oct(644), 'x', "57 SCHILY.xattr.security.capability=\x01\0\0\x02\0\x20\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0a"], ['./file', oct(644), 0, 'test'], ); my $num_entries = 0; foreach my $file (@entries) { my ($fname, $mode, $type, $content) = @{$file}; my $entry = pack( 'a100 a8 a8 a8 a12 a12 A8 a1 a100 a6 a2 a32 a32 a8 a8 a155 x12', $fname, sprintf('%07o', $mode), sprintf('%07o', 0), # uid sprintf('%07o', 0), # gid sprintf('%011o', length $content), # size sprintf('%011o', 0), # mtime '', # checksum $type, '', # linkname "ustar", # magic "00", # version '', # username '', # groupname '', # dev major '', # dev minor '', # prefix ); # compute and insert checksum substr($entry, 148, 7) = sprintf("%06o\0", unpack("%16C*", $entry)); print $entry; $num_entries += 1; if (length $content) { print(pack 'a512', $content); $num_entries += 1; } } # https://www.gnu.org/software/tar/manual/html_node/Standard.html # # Physically, an archive consists of a series of file entries terminated by an # end-of-archive entry, which consists of two 512 blocks of zero bytes. At the # end of the archive file there are two 512-byte blocks filled with binary # zeros as an end-of-file marker. print(pack 'a512', ''); print(pack 'a512', ''); $num_entries += 2; # https://www.gnu.org/software/tar/manual/html_section/tar_76.html # # Some devices requires that all write operations be a multiple of a certain # size, and so, tar pads the archive out to the next record boundary. # # The default blocking factor is 20. With a block size of 512 bytes, we get a # record size of 10240. for (my $i = $num_entries ; $i < 20 ; $i++) { print(pack 'a512', ''); } END MMTARFILTER= [ -x /usr/bin/mmtarfilter ] && MMTARFILTER=/usr/bin/mmtarfilter [ -x ./tarfilter ] && MMTARFILTER=./tarfilter perl /tmp/mkpaxtar.pl | "$MMTARFILTER" >/tmp/orig.tar # make sure that xattrs are set in the original tarball tar --xattrs --xattrs-include='*' --directory /tmp/ -xf /tmp/orig.tar ./file echo "/tmp/file cap_net_raw=ep" >/tmp/expected getcap /tmp/file | diff -u /tmp/expected - >&2 # make sure that the file content is as expected printf test | diff -u /tmp/file - >&2 # make sure that uid/gid are as expected in the original tarball echo "0 0 644" >/tmp/expected stat --format="%u %g %a" /tmp/file | diff -u /tmp/expected - >&2 rm /tmp/file # tarball must be bit by-bit-identical after round-trip "$MMTARFILTER" --idshift 0 /tmp/filtered.tar cmp /tmp/orig.tar /tmp/filtered.tar # now shift uid/gid "$MMTARFILTER" --idshift 100000 /tmp/filtered.tar # make sure that uid/gid are as expected in the filtered tarball tar --xattrs --xattrs-include='*' --directory /tmp/ -xf /tmp/filtered.tar ./file echo "100000 100000 644" >/tmp/expected stat --format="%u %g %a" /tmp/file | diff -u /tmp/expected - >&2 rm /tmp/file # now shift uid/gid back to create a round-trip "$MMTARFILTER" --idshift -100000 /tmp/filtered2.tar # the result must be identical to the original and will thus also include the # correct xattr information cmp /tmp/orig.tar /tmp/filtered2.tar