#!/usr/bin/env python3 # # This script is in the public domain # # 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()