commit: 5cc166489106da93c20ad3c5bb138ba8a359de2c Author: Jeff Chase <jnchase <AT> google <DOT> com> AuthorDate: Tue May 4 17:29:58 2021 +0000 Commit: Sam James <sam <AT> gentoo <DOT> org> CommitDate: Fri Apr 15 04:26:22 2022 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=5cc16648
install_mask: remove masked symlinks to directories (bug 678462) os.walk() categorizes symlinks to directories as directories so they were being ignored by INSTALL_MASK. This change calls os.scandir() instead which efficiently provides more control. [sam: cherry-picked from chromiumos' third_party/portage_tool repo] (cherry picked from commit 6473079d07351dda49a906cb89d24a9a39526bf7) Bug: https://bugs.gentoo.org/678462 Signed-off-by: Sam James <sam <AT> gentoo.org> Closes: https://github.com/gentoo/portage/pull/820 Signed-off-by: Sam James <sam <AT> gentoo.org> lib/portage/tests/util/test_install_mask.py | 33 +++++++++++++++++++++++++++-- lib/portage/util/install_mask.py | 18 ++++++++++------ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/lib/portage/tests/util/test_install_mask.py b/lib/portage/tests/util/test_install_mask.py index d9558a857..0ed98b3a5 100644 --- a/lib/portage/tests/util/test_install_mask.py +++ b/lib/portage/tests/util/test_install_mask.py @@ -1,8 +1,11 @@ -# Copyright 2018 Gentoo Foundation +# Copyright 2018-2022 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 +import tempfile +from portage import os +from portage import shutil from portage.tests import TestCase -from portage.util.install_mask import InstallMask +from portage.util.install_mask import InstallMask, install_mask_dir class InstallMaskTestCase(TestCase): @@ -166,3 +169,29 @@ class InstallMaskTestCase(TestCase): install_mask_str, path ), ) + + def testSymlinkDir(self): + """ + Test that masked symlinks to directories are removed. + """ + tmp_dir = tempfile.mkdtemp() + + try: + base_dir = os.path.join(tmp_dir, "foo") + target_dir = os.path.join(tmp_dir, "foo", "bar") + link_name = os.path.join(tmp_dir, "foo", "baz") + + os.mkdir(base_dir) + os.mkdir(target_dir) + os.symlink(target_dir, link_name) + + install_mask = InstallMask("/foo/") + install_mask_dir(tmp_dir, install_mask) + self.assertFalse( + os.path.lexists(link_name), "failed to remove {}".format(link_name) + ) + self.assertFalse( + os.path.lexists(base_dir), "failed to remove {}".format(base_dir) + ) + finally: + shutil.rmtree(tmp_dir) diff --git a/lib/portage/util/install_mask.py b/lib/portage/util/install_mask.py index 2b65fc230..638c150ff 100644 --- a/lib/portage/util/install_mask.py +++ b/lib/portage/util/install_mask.py @@ -171,22 +171,26 @@ def install_mask_dir(base_dir, install_mask, onerror=None): dir_stack = [] # Remove masked files. - for parent, dirs, files in os.walk(base_dir, onerror=onerror): + todo = [base_dir] + while todo: + parent = todo.pop() try: parent = _unicode_decode(parent, errors="strict") except UnicodeDecodeError: continue + dir_stack.append(parent) - for fname in files: + for entry in os.scandir(parent): try: - fname = _unicode_decode(fname, errors="strict") + abs_path = _unicode_decode(entry.path, errors="strict") except UnicodeDecodeError: continue - abs_path = os.path.join(parent, fname) - relative_path = abs_path[base_dir_len:] - if install_mask.match(relative_path): + + if entry.is_dir(follow_symlinks=False): + todo.append(entry.path) + elif install_mask.match(abs_path[base_dir_len:]): try: - os.unlink(abs_path) + os.unlink(entry.path) except OSError as e: onerror(e)
