diff --git a/build_library/check_root b/build_library/check_root index a6af980ccb..2e5ed60c59 100755 --- a/build_library/check_root +++ b/build_library/check_root @@ -84,6 +84,22 @@ IGNORE_SHEBANG = ( b"*/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 +) + + def provided_sonames(): for cpv in VARDB.cpv_all(): raw = VARDB.aux_get(cpv, ["PROVIDES"])[0] @@ -94,11 +110,13 @@ def provided_sonames(): for atom in VARDB.settings.soname_provided: yield atom + def ignore_sonames(cpv): for key in dep.match_to_list(cpv, IGNORE_MISSING.iterkeys()): for atom in IGNORE_MISSING[key]: yield atom + def missing_sonames(): provided = frozenset(provided_sonames()) for cpv in VARDB.cpv_all(): @@ -109,6 +127,7 @@ def missing_sonames(): if missing: yield (cpv, missing) + def usr_conflicts(): for cpv in VARDB.cpv_all(): raw = VARDB.aux_get(cpv, ["CONTENTS"])[0] @@ -141,6 +160,7 @@ def usr_conflicts(): if conflicts: yield (cpv, conflicts) + def check_libs(): ok = True for cpv, sonames in missing_sonames(): @@ -150,6 +170,7 @@ def check_libs(): ok = False return ok + def check_usr(): ok = True for cpv, conflicts in usr_conflicts(): @@ -159,11 +180,13 @@ def check_usr(): 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 + perms = stat.S_IROTH | stat.S_IXOTH mode = os.lstat(path).st_mode - return stat.S_ISREG(mode) and mode&perms == perms + return stat.S_ISREG(mode) and (mode & perms) == perms + def check_shebang(): ok = True @@ -171,7 +194,7 @@ def check_shebang(): 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): + if any(fnmatch.fnmatchcase(path, i) for i in IGNORE_SHEBANG): continue if not is_exe(path): continue @@ -201,16 +224,66 @@ def check_shebang(): 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(): diff --git a/build_library/test_image_content.sh b/build_library/test_image_content.sh index 2136811941..2189e5ec17 100644 --- a/build_library/test_image_content.sh +++ b/build_library/test_image_content.sh @@ -56,6 +56,11 @@ test_image_content() { #returncode=1 fi + if ! sudo ROOT="$root" "$check_root" symlink; then + error "test_image_content: Failed symlink check" + returncode=1 + fi + if ! ROOT="$root" glsa_image; then returncode=1 fi