Fixed some potential bugs and made it all around better. I'm working on upstreaming support in xtarfile for LZ4 compression so hopefully the special handling for that can be removed soon. I also fixed a bug in xtarfile that prevents .tar files from being opened. So once the new version hits the repos 'import tarfile' can be removed. diff --git a/namcap b/namcap index ea0bc94..019b077 100755 --- a/namcap +++ b/namcap @@ -1,39 +1,2 @@ #!/bin/bash - -args='' -tmp=$(mktemp -d --tmpdir namcap.XXXXXXXXXX) -cleanup() { - rm -rf "${tmp}" -} -trap 'cleanup' 0 - -for arg in "${@}"; do - if echo "${arg}" | grep -q -E "^.+\.pkg\.tar\..+$" && [ -f "${arg}" ]; then - - extra_opts='' - case "${arg##*.}" in - gz|z|Z) cmd='gzip' ;; - bz2|bz) cmd='bzip2' ;; - xz) cmd='xz' ;; - lzo) cmd='lzop' ;; - lrz) cmd='lrzip' - extra_opts="-q -o -" ;; - lz4) cmd='lz4' - extra_opts="-q" ;; - lz) cmd='lzip' - extra_opts="-q" ;; - zst) cmd='zstd' - extra_opts="-q" ;; - *) echo 'Unsupported compression'; exit 1;; - esac - - tar="${tmp}/$(basename "${arg%.*}")" - $cmd -dcf $extra_opts "${arg}" > "${tar}" - - args="${args} ${tar}" - else - args="${args} ${arg}" - fi -done - -/usr/bin/env python3 -m namcap ${args} +/usr/bin/env python3 -m namcap ${@} diff --git a/namcap.py b/namcap.py index a7f532a..78662db 100755 --- a/namcap.py +++ b/namcap.py @@ -22,7 +22,11 @@ import getopt import os import sys +import subprocess +import tempfile +import pathlib import tarfile +import xtarfile import Namcap.depends import Namcap.tags @@ -49,18 +53,6 @@ def usage(): sys.exit(2) -def open_package(filename): - try: - tar = tarfile.open(filename, "r") - if '.PKGINFO' not in tar.getnames(): - tar.close() - return None - except IOError: - if tar: - tar.close() - return None - return tar - def check_rules_exclude(optlist): '''Check if the -r (--rules) and the -r (--exclude) options are being used at same time''' @@ -76,13 +68,10 @@ def show_messages(name, key, messages): for msg in messages: print("%s %s: %s" % (name, key, Namcap.tags.format_message(msg))) -def process_realpackage(package, modules): +def process_realpackage(pkgtar, package, modules): """Runs namcap checks over a package tarball""" - extracted = 0 - pkgtar = open_package(package) - - if not pkgtar: - print("Error: %s is empty or is not a valid package" % package) + if '.PKGINFO' not in pkgtar.getnames(): + raise TypeError('Error: %s is not a valid package' % package) return 1 pkginfo = Namcap.package.load_from_tarball(package) @@ -98,7 +87,7 @@ def process_realpackage(package, modules): rule.analyze(pkginfo, pkgtar) else: show_messages(pkginfo["name"], 'E', - [('error-running-rule %s', i)]) + [('error-running-rule %s', i)]) # Output the three types of messages show_messages(pkginfo["name"], 'E', rule.errors) @@ -237,15 +226,71 @@ if len(active_modules) == 0: # Go through each package, get the info, and apply the rules for package in packages: - if not os.access(package, os.R_OK): + pkgpath = pathlib.Path(package) + + if not pkgpath.is_file(): print("Error: Problem reading %s" % package) usage() - - if os.path.isfile(package) and tarfile.is_tarfile(package): - process_realpackage(package, active_modules) - elif 'PKGBUILD' in package: + sys.exit(1) + + if pkgpath.with_suffix('').suffix == '.tar' or pkgpath.suffix == '.tar': + try: # Try using xtarfile first + with xtarfile.open(package, "r") as pkgtar: + process_realpackage(pkgtar, package, active_modules) + + # If xtarfile can't handle the compression do it with external software + except NotImplementedError: + if pkgpath.suffix == '.lzo': + cmd = ['lzop', '-cdqf', pkgpath.as_posix()] + elif pkgpath.suffix == '.lrz': + cmd = ['lrzip', '-dqfo', '-', pkgpath.as_posix()] + elif pkgpath.suffix == '.lz4': + cmd = ['lz4', '-cdqf', pkgpath.as_posix()] + elif pkgpath.suffix == '.lz': + cmd = ['lzip', '-cdqf', pkgpath.as_posix()] + else: + print("Unsupported compression format:", package) + sys.exit(1) + + # Run decompression and put the .tar file in a temporary directory + try: + tmpdir = tempfile.TemporaryDirectory(prefix='namcap.') + except Exception as err: + print("Unable to create temporary storage:", err) + sys.exit(1) + + tmpfilepath = pathlib.Path(tmpdir.name).joinpath(pkgpath.with_suffix('').name) + + try: + with open(tmpfilepath, 'wb') as outfile: + p = subprocess.run(cmd, stdout=outfile) + p.check_returncode() # Make sure it actually ran without errors + except FileNotFoundError as err: + print(err, "\nThe required compression software %s wasn't found." % cmd[0], "\nInstall with pacman -S", cmd[0]) + sys.exit(1) + except Exception as err: + print("Unable to decompress:", err) + sys.exit(1) + + try: + with tarfile.open(tmpfilepath) as pkgtar: + process_realpackage(pkgtar, package, active_modules) + except Exception as err: + print("File doesn't exist or isn't a tar file:", package, err) + sys.exit(1) + + except TypeError as err: # The tar file doesn't contain a .PKGINFO file + print(err) + sys.exit(1) + except Exception as err: + print("Something went horrible wrong:", err) + sys.exit(1) + + elif pkgpath.name == 'PKGBUILD': process_pkgbuild(package, active_modules) + else: print("Error: %s not package or PKGBUILD" % package) + sys.exit(1) # vim: set ts=4 sw=4 noet: