tarfilter --idshift now provides taridshift

This commit is contained in:
Johannes Schauer Marin Rodrigues 2022-08-31 05:35:40 +02:00
parent 226f86fea9
commit 902bc55c4d
Signed by: josch
GPG key ID: F2CBA5C78FBD83E1
5 changed files with 26 additions and 76 deletions

View file

@ -96,7 +96,6 @@ def main():
# copy over files from git or as distributed # copy over files from git or as distributed
for (git, dist, target) in [ for (git, dist, target) in [
("./mmdebstrap", "/usr/bin/mmdebstrap", "mmdebstrap"), ("./mmdebstrap", "/usr/bin/mmdebstrap", "mmdebstrap"),
("./taridshift", "/usr/bin/mmtaridshift", "taridshift"),
("./tarfilter", "/usr/bin/mmtarfilter", "tarfilter"), ("./tarfilter", "/usr/bin/mmtarfilter", "tarfilter"),
( (
"./proxysolver", "./proxysolver",

View file

@ -52,7 +52,7 @@ Skip-If:
fmt == "squashfs" and dist == "oldstable" # squashfs-tools-ng is not available fmt == "squashfs" and dist == "oldstable" # squashfs-tools-ng is not available
fmt == "ext2" and dist == "oldstable" # genext2fs does not support SOURCE_DATE_EPOCH fmt == "ext2" and dist == "oldstable" # genext2fs does not support SOURCE_DATE_EPOCH
Test: taridshift-utility Test: tarfilter-idshift
Needs-QEMU: true Needs-QEMU: true
Skip-If: dist == "oldstable" # python3 tarfile module does not preserve xattrs Skip-If: dist == "oldstable" # python3 tarfile module does not preserve xattrs

View file

@ -68,7 +68,10 @@ Both types of options use Unix shell-style wildcards:
Thirdly, strip leading directory components off of tar members. Just as with Thirdly, strip leading directory components off of tar members. Just as with
GNU tar --strip-components, tar members that have less or equal components in GNU tar --strip-components, tar members that have less or equal components in
their path are not passed through. their path are not passed through.
"""
Lastly, shift user id and group id of each entry by the value given by the
--idshift argument. The resulting uid or gid must not be negative.
""",
) )
parser.add_argument( parser.add_argument(
"--path-exclude", "--path-exclude",
@ -100,6 +103,12 @@ their path are not passed through.
type=int, type=int,
help="Strip NUMBER leading components from file names", help="Strip NUMBER leading components from file names",
) )
parser.add_argument(
"--idshift",
metavar="NUM",
type=int,
help="Integer value by which to shift the uid and gid of each entry",
)
args = parser.parse_args() args = parser.parse_args()
if ( if (
not hasattr(args, "pathfilter") not hasattr(args, "pathfilter")
@ -165,6 +174,15 @@ their path are not passed through.
for k, v in member.pax_headers.items() for k, v in member.pax_headers.items()
if not pax_filter_should_skip(k) if not pax_filter_should_skip(k)
} }
if args.idshift:
if args.idshift < 0 and -args.idshift > member.uid:
print("uid cannot be negative", file=sys.stderr)
exit(1)
if args.idshift < 0 and -args.idshift > member.gid:
print("gid cannot be negative", file=sys.stderr)
exit(1)
member.uid += args.idshift
member.gid += args.idshift
if member.isfile(): if member.isfile():
with in_tar.extractfile(member) as file: with in_tar.extractfile(member) as file:
out_tar.addfile(member, file) out_tar.addfile(member, file)

View file

@ -1,67 +0,0 @@
#!/usr/bin/env python3
#
# This script is in the public domain
#
# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
#
# This script accepts a tarball on standard input and prints a tarball on
# standard output with the same contents but all uid and gid ownership
# information shifted by the value given as first command line argument.
#
# A tool like this should be written in C but libarchive has issues:
# https://github.com/libarchive/libarchive/issues/587
# https://github.com/libarchive/libarchive/pull/1288/ (needs 3.4.1)
# Should these issues get fixed, then a good template is tarfilter.c in the
# examples directory of libarchive.
#
# We are not using Perl either, because Archive::Tar slurps the whole tarball
# into memory.
#
# We could also use Go but meh...
# https://stackoverflow.com/a/59542307/784669
import tarfile
import sys
import argparse
def main():
parser = argparse.ArgumentParser(
description="""\
Accepts a tarball on standard input and prints a tarball on standard output
with the same contents but all uid and gid ownership information shifted by the
value given as first command line argument.
"""
)
parser.add_argument(
"idshift",
metavar="NUM",
type=int,
help="Integer value by which to shift the uid and gid of each entry",
)
args = parser.parse_args()
# starting with Python 3.8, the default format became PAX_FORMAT, so this
# is only for compatibility with older versions of Python 3
with tarfile.open(fileobj=sys.stdin.buffer, mode="r|*") as in_tar, tarfile.open(
fileobj=sys.stdout.buffer, mode="w|", format=tarfile.PAX_FORMAT
) as out_tar:
for member in in_tar:
if args.idshift < 0 and -args.idshift > member.uid:
print("uid cannot be negative", file=sys.stderr)
exit(1)
if args.idshift < 0 and -args.idshift > member.gid:
print("gid cannot be negative", file=sys.stderr)
exit(1)
member.uid += args.idshift
member.gid += args.idshift
if member.isfile():
with in_tar.extractfile(member) as file:
out_tar.addfile(member, file)
else:
out_tar.addfile(member)
if __name__ == "__main__":
main()

View file

@ -10,12 +10,12 @@ adduser --gecos user --disabled-password user
echo user:100000:65536 | cmp /etc/subuid - echo user:100000:65536 | cmp /etc/subuid -
echo user:100000:65536 | cmp /etc/subgid - echo user:100000:65536 | cmp /etc/subgid -
sysctl -w kernel.unprivileged_userns_clone=1 sysctl -w kernel.unprivileged_userns_clone=1
# include iputils-ping so that we can verify that taridshift does not remove # include iputils-ping so that we can verify that tarfilter does not remove
# extended attributes # extended attributes
# run through tarshift no-op to create a tarball that should be bit-by-bit # run through tarshift no-op to create a tarball that should be bit-by-bit
# identical to a round trip through "taridshift X" and "taridshift -X" # identical to a round trip through "tarfilter --idshift X" and "tarfilter --idshift -X"
runuser -u user -- {{ CMD }} --mode=unshare --variant=apt --include=iputils-ping {{ DIST }} - {{ MIRROR }} \ runuser -u user -- {{ CMD }} --mode=unshare --variant=apt --include=iputils-ping {{ DIST }} - {{ MIRROR }} \
| ./taridshift 0 > /tmp/debian-chroot.tar | ./tarfilter --idshift 0 > /tmp/debian-chroot.tar
# make sure that xattrs are set in the original tarball # make sure that xattrs are set in the original tarball
mkdir /tmp/debian-chroot mkdir /tmp/debian-chroot
tar --xattrs --xattrs-include='*' --directory /tmp/debian-chroot -xf /tmp/debian-chroot.tar ./bin/ping tar --xattrs --xattrs-include='*' --directory /tmp/debian-chroot -xf /tmp/debian-chroot.tar ./bin/ping
@ -25,9 +25,9 @@ rm /tmp/debian-chroot/bin/ping
rmdir /tmp/debian-chroot/bin rmdir /tmp/debian-chroot/bin
rmdir /tmp/debian-chroot rmdir /tmp/debian-chroot
# shift the uid/gid forward by 100000 and backward by 100000 # shift the uid/gid forward by 100000 and backward by 100000
./taridshift 100000 < /tmp/debian-chroot.tar > /tmp/debian-chroot-shifted.tar ./tarfilter --idshift 100000 < /tmp/debian-chroot.tar > /tmp/debian-chroot-shifted.tar
./taridshift -100000 < /tmp/debian-chroot-shifted.tar > /tmp/debian-chroot-shiftedback.tar ./tarfilter --idshift -100000 < /tmp/debian-chroot-shifted.tar > /tmp/debian-chroot-shiftedback.tar
# the tarball before and after the roundtrip through taridshift should be bit # the tarball before and after the roundtrip through tarfilter should be bit
# by bit identical # by bit identical
cmp /tmp/debian-chroot.tar /tmp/debian-chroot-shiftedback.tar cmp /tmp/debian-chroot.tar /tmp/debian-chroot-shiftedback.tar
# manually adjust uid/gid and compare "tar -t" output # manually adjust uid/gid and compare "tar -t" output