mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-06 20:47:00 +02:00
The standard location is /usr/lib/modules but on Flatcar "lib" was a symlink to "lib64". Now this is going to be split up in separate directories but with compatibility symlinks. Add the new location to the ignore list.
343 lines
13 KiB
Python
Executable File
343 lines
13 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
# Copyright (c) 2015 The CoreOS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import fnmatch
|
|
import os
|
|
import stat
|
|
import sys
|
|
|
|
import portage
|
|
from portage import dep
|
|
from portage import output
|
|
from portage.dep.soname.SonameAtom import SonameAtom
|
|
from portage.dep.soname.parse import parse_soname_deps
|
|
|
|
VARDB = portage.db[portage.root]["vartree"].dbapi
|
|
|
|
# TODO(marneam): possibly accept globs for arch and sonames
|
|
IGNORE_MISSING = {
|
|
# /usr/lib/go/src/debug/elf/testdata/gcc-386-freebsd-exec
|
|
# /usr/lib/go/src/debug/elf/testdata/gcc-amd64-linux-exec
|
|
"dev-lang/go": [SonameAtom("x86_32", "libc.so.6"),
|
|
SonameAtom("x86_64", "libc.so.6")],
|
|
"dev-lang/go-bootstrap": [SonameAtom("x86_32", "libc.so.6"),
|
|
SonameAtom("x86_64", "libc.so.6")],
|
|
|
|
# RPATHs and symlinks apparently confuse the perl-5.24 package
|
|
"dev-lang/perl": [SonameAtom("arm_64", "libperl.so.5.26.2"),
|
|
SonameAtom("x86_64", "libperl.so.5.26.2")],
|
|
"dev-perl/XML-Parser": [SonameAtom("x86_64", "libc.so.6"),
|
|
SonameAtom("x86_64", "libexpat.so.1")],
|
|
"dev-perl/libintl-perl": [SonameAtom("x86_64", "libc.so.6")],
|
|
"dev-util/boost-build": [SonameAtom("x86_64", "libc.so.6")],
|
|
"net-dns/dnsmasq": [SonameAtom("x86_64", "libc.so.6")],
|
|
"sys-apps/texinfo": [SonameAtom("x86_64", "libc.so.6"),
|
|
SonameAtom("x86_64", "libperl.so.5.26")],
|
|
|
|
# https://bugs.gentoo.org/show_bug.cgi?id=554582
|
|
"net-firewall/ebtables": [SonameAtom("arm_64", "libebt_802_3.so"),
|
|
SonameAtom("arm_64", "libebt_among.so"),
|
|
SonameAtom("arm_64", "libebt_arp.so"),
|
|
SonameAtom("arm_64", "libebt_arpreply.so"),
|
|
SonameAtom("arm_64", "libebt_ip.so"),
|
|
SonameAtom("arm_64", "libebt_ip6.so"),
|
|
SonameAtom("arm_64", "libebt_limit.so"),
|
|
SonameAtom("arm_64", "libebt_log.so"),
|
|
SonameAtom("arm_64", "libebt_mark.so"),
|
|
SonameAtom("arm_64", "libebt_mark_m.so"),
|
|
SonameAtom("arm_64", "libebt_nat.so"),
|
|
SonameAtom("arm_64", "libebt_nflog.so"),
|
|
SonameAtom("arm_64", "libebt_pkttype.so"),
|
|
SonameAtom("arm_64", "libebt_redirect.so"),
|
|
SonameAtom("arm_64", "libebt_standard.so"),
|
|
SonameAtom("arm_64", "libebt_stp.so"),
|
|
SonameAtom("arm_64", "libebt_ulog.so"),
|
|
SonameAtom("arm_64", "libebt_vlan.so"),
|
|
SonameAtom("arm_64", "libebtable_broute.so"),
|
|
SonameAtom("arm_64", "libebtable_filter.so"),
|
|
SonameAtom("arm_64", "libebtable_nat.so"),
|
|
SonameAtom("x86_64", "libebt_802_3.so"),
|
|
SonameAtom("x86_64", "libebt_among.so"),
|
|
SonameAtom("x86_64", "libebt_arp.so"),
|
|
SonameAtom("x86_64", "libebt_arpreply.so"),
|
|
SonameAtom("x86_64", "libebt_ip.so"),
|
|
SonameAtom("x86_64", "libebt_ip6.so"),
|
|
SonameAtom("x86_64", "libebt_limit.so"),
|
|
SonameAtom("x86_64", "libebt_log.so"),
|
|
SonameAtom("x86_64", "libebt_mark.so"),
|
|
SonameAtom("x86_64", "libebt_mark_m.so"),
|
|
SonameAtom("x86_64", "libebt_nat.so"),
|
|
SonameAtom("x86_64", "libebt_nflog.so"),
|
|
SonameAtom("x86_64", "libebt_pkttype.so"),
|
|
SonameAtom("x86_64", "libebt_redirect.so"),
|
|
SonameAtom("x86_64", "libebt_standard.so"),
|
|
SonameAtom("x86_64", "libebt_stp.so"),
|
|
SonameAtom("x86_64", "libebt_ulog.so"),
|
|
SonameAtom("x86_64", "libebt_vlan.so"),
|
|
SonameAtom("x86_64", "libebtable_broute.so"),
|
|
SonameAtom("x86_64", "libebtable_filter.so"),
|
|
SonameAtom("x86_64", "libebtable_nat.so")],
|
|
|
|
# Ignore the Rust libraries in their own libdir.
|
|
"dev-libs/rustlib": [SonameAtom("arm_64", "librustc_data_structures.so"),
|
|
SonameAtom("arm_64", "librustc_errors.so"),
|
|
SonameAtom("arm_64", "libserialize.so"),
|
|
SonameAtom("arm_64", "libstd.so"),
|
|
SonameAtom("arm_64", "libsyntax.so"),
|
|
SonameAtom("arm_64", "libsyntax_pos.so"),
|
|
SonameAtom("arm_64", "libterm.so"),
|
|
SonameAtom("x86_64", "librustc_data_structures.so"),
|
|
SonameAtom("x86_64", "librustc_errors.so"),
|
|
SonameAtom("x86_64", "libserialize.so"),
|
|
SonameAtom("x86_64", "libstd.so"),
|
|
SonameAtom("x86_64", "libsyntax.so"),
|
|
SonameAtom("x86_64", "libsyntax_pos.so"),
|
|
SonameAtom("x86_64", "libterm.so")],
|
|
|
|
"sys-kernel/coreos-modules": [SonameAtom("x86_64", "libc.so.6"),
|
|
SonameAtom("x86_64", "libcrypto.so.1.0.0")],
|
|
}
|
|
|
|
USR_LINKS = ("/bin/", "/sbin/", "/lib/", "/lib32/", "/lib64/")
|
|
|
|
IGNORE_SHEBANG = (
|
|
"*/python[0-9].[0-9]/cgi.py",
|
|
"*/usr/lib64/modules/*/source/scripts/*",
|
|
"*/usr/lib/modules/*/source/scripts/*",
|
|
"*/usr/share/nova-agent/*/etc/gentoo/nova-agent",
|
|
"*/tmp/*",
|
|
"*/Documentation/*",
|
|
"*/doc/*",
|
|
)
|
|
|
|
IGNORE_SYMLINK = (
|
|
# symlinks to sdk chroot
|
|
b"/build/*",
|
|
b"/var/tmp/portage/*",
|
|
b"/etc/portage/*",
|
|
|
|
# symlinks to /run
|
|
b"/usr/share/baselayout/motd",
|
|
b"/etc/issue",
|
|
b"/etc/motd",
|
|
|
|
# Other
|
|
b"/etc/lsb-release", # set later in the build process
|
|
b"/usr/share/coreos", # set later in the build process
|
|
b"/etc/coreos", # set later in the build process
|
|
b"/usr/src/linux-*/tools/testing/selftests/powerpc/copyloops/memcpy_mcsafe_64.S", # broken symlink in Kernel source tree
|
|
b"/usr/lib*/python3*/site-packages/*egg-info", # broken symlink from dev-python/certifi that is not filtered by INSTALL_MASK
|
|
)
|
|
|
|
|
|
def provided_sonames():
|
|
for cpv in VARDB.cpv_all():
|
|
raw = VARDB.aux_get(cpv, ["PROVIDES"])[0]
|
|
for atom in parse_soname_deps(raw):
|
|
yield atom
|
|
|
|
# soname.provided in PORTAGE_CONFIGROOT
|
|
for atom in VARDB.settings.soname_provided:
|
|
yield atom
|
|
|
|
|
|
def ignore_sonames(cpv):
|
|
for key in dep.match_to_list(cpv, IGNORE_MISSING.keys()):
|
|
for atom in IGNORE_MISSING[key]:
|
|
yield atom
|
|
|
|
|
|
def missing_sonames():
|
|
provided = frozenset(provided_sonames())
|
|
for cpv in VARDB.cpv_all():
|
|
raw = VARDB.aux_get(cpv, ["REQUIRES"])[0]
|
|
requires = frozenset(parse_soname_deps(raw))
|
|
ignore = frozenset(ignore_sonames(cpv))
|
|
missing = requires - provided - ignore
|
|
if missing:
|
|
yield (cpv, missing)
|
|
|
|
|
|
def usr_conflicts():
|
|
for cpv in VARDB.cpv_all():
|
|
raw = VARDB.aux_get(cpv, ["CONTENTS"])[0]
|
|
usr = set()
|
|
root = set()
|
|
|
|
# format is:
|
|
# obj /path goo 123
|
|
# dir /path/foo
|
|
# sym /this -> that 123
|
|
# and so on
|
|
for line in raw.split("\n"):
|
|
if line[:4] != "obj " and line[:4] != "sym ":
|
|
continue
|
|
|
|
# yeah, hard to read, trying to make it fast...
|
|
i = line.find("/", 5)
|
|
topdir = line[4:i+1]
|
|
if topdir == "/usr/":
|
|
j = line.find("/", 9)
|
|
nextdir = line[8:j+1]
|
|
if nextdir in USR_LINKS:
|
|
end = line.find(" ", 8)
|
|
usr.add(line[8:end])
|
|
elif topdir in USR_LINKS:
|
|
end = line.find(" ", 4)
|
|
root.add(line[4:end])
|
|
|
|
conflicts = frozenset(root).intersection(usr)
|
|
if conflicts:
|
|
yield (cpv, conflicts)
|
|
|
|
|
|
def check_libs():
|
|
ok = True
|
|
for cpv, sonames in missing_sonames():
|
|
error("%s is missing libraries:", cpv)
|
|
for soname in sonames:
|
|
error("\t%s", soname)
|
|
ok = False
|
|
return ok
|
|
|
|
|
|
def check_usr():
|
|
ok = True
|
|
for cpv, conflicts in usr_conflicts():
|
|
error("%s has paths that conflict with /usr", cpv)
|
|
for path in conflicts:
|
|
error("\t%s", path)
|
|
ok = False
|
|
return ok
|
|
|
|
|
|
def is_exe(path):
|
|
# just check other, assuming root or group only commands are not scripts.
|
|
perms = stat.S_IROTH | stat.S_IXOTH
|
|
mode = os.lstat(path).st_mode
|
|
return stat.S_ISREG(mode) and (mode & perms) == perms
|
|
|
|
|
|
def check_shebang():
|
|
ok = True
|
|
cache = {}
|
|
root = os.environ.get("ROOT", b"/")
|
|
for parent, _, files in os.walk(root):
|
|
for path in [os.path.join(parent, f) for f in files]:
|
|
if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SHEBANG):
|
|
continue
|
|
if not is_exe(path):
|
|
continue
|
|
with open(path, "rb") as fd:
|
|
line = fd.readline(80)
|
|
if not line.startswith(b"#!"):
|
|
continue
|
|
line = line.decode('utf-8')
|
|
args = line[2:].rstrip().split(None, 2)
|
|
cmd = args.pop(0)
|
|
if cmd in ('/usr/bin/env', '/bin/env') and args:
|
|
prog = args.pop(0)
|
|
if prog.startswith('-') and args:
|
|
prog = args.pop(0)
|
|
cmd = '(env)/%s' % prog
|
|
if cmd not in cache:
|
|
if cmd.startswith('(env)'):
|
|
cache[cmd] = False
|
|
for bindir in (root+'/usr/bin', root+'/usr/sbin'):
|
|
if os.path.exists(os.path.join(bindir, prog)):
|
|
cache[cmd] = True
|
|
break
|
|
else:
|
|
cache[cmd] = os.path.exists(root+cmd)
|
|
if not cache[cmd]:
|
|
relpath = path[len(root):]
|
|
error("%s: %s does not exist", relpath, cmd)
|
|
ok = False
|
|
return ok
|
|
|
|
|
|
class chrooted():
|
|
"""
|
|
chrooted provides a context so that it can be used via with.
|
|
For example:
|
|
|
|
with chrooted("/some/rootfs"):
|
|
do_operations_in_rootfs()
|
|
do_operations_not_in_rootfs()
|
|
"""
|
|
|
|
def __init__(self, path):
|
|
self.path = path
|
|
|
|
def __enter__(self):
|
|
self.restore_fd = os.open(b"/", os.O_RDONLY)
|
|
self.working_dir = os.getcwd()
|
|
|
|
os.chroot(self.path)
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
os.fchdir(self.restore_fd)
|
|
os.chroot(b".")
|
|
os.chdir(self.working_dir)
|
|
os.close(self.restore_fd)
|
|
|
|
|
|
def check_symlink():
|
|
if os.getuid() != 0:
|
|
error("symlink check must be run as root (chroot)")
|
|
return False
|
|
|
|
ok = True
|
|
root = os.environ.get("ROOT", b"/")
|
|
|
|
with chrooted(root):
|
|
for parent, dirs, files in os.walk(b"/"):
|
|
for path in [os.path.join(parent, p) for p in files + dirs]:
|
|
if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SYMLINK):
|
|
continue
|
|
|
|
if os.path.islink(path) and not os.path.exists(path):
|
|
ok = False
|
|
error("broken link: %s", path)
|
|
|
|
return ok
|
|
|
|
|
|
def error(fmt, *args):
|
|
sys.stderr.write(output.red(fmt % args))
|
|
sys.stderr.write("\n")
|
|
|
|
|
|
def main():
|
|
ok = True
|
|
check_funcs = {
|
|
"libs": check_libs,
|
|
"usr": check_usr,
|
|
"shebang": check_shebang,
|
|
"symlink": check_symlink,
|
|
}
|
|
|
|
if not sys.stderr.isatty():
|
|
output.nocolor()
|
|
|
|
checks = sys.argv[1:]
|
|
if not checks:
|
|
checks = check_funcs.keys()
|
|
|
|
for check in checks:
|
|
func = check_funcs.get(check)
|
|
if func:
|
|
ok = func() and ok
|
|
else:
|
|
error("Unknown test name '%s'", check)
|
|
error("Valid tests: %s", " ".join(check_funcs))
|
|
ok = False
|
|
|
|
return 0 if ok else 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|