Gentoo Archives: gentoo-portage-dev

From: Jeff Chase <jnchase@××××××.com>
To: gentoo-portage-dev@l.g.o
Cc: Jeff Chase <jnchase@××××××.com>
Subject: [gentoo-portage-dev] [PATCH] install_mask: remove masked symlinks to directories (bug 678462)
Date: Thu, 06 May 2021 15:18:18
Message-Id: 20210506151801.1965931-1-jnchase@google.com
1 os.walk() categorizes symlinks to directories as directories so they
2 were being ignored by INSTALL_MASK. This change calls os.scandir()
3 instead which efficiently provides more control.
4
5 Signed-off-by: Jeff Chase <jnchase@××××××.com>
6 ---
7 lib/portage/tests/util/test_install_mask.py | 26 ++++++++++++++++++++-
8 lib/portage/util/install_mask.py | 18 ++++++++------
9 2 files changed, 36 insertions(+), 8 deletions(-)
10
11 diff --git a/lib/portage/tests/util/test_install_mask.py b/lib/portage/tests/util/test_install_mask.py
12 index 6a29db79a..665a8edac 100644
13 --- a/lib/portage/tests/util/test_install_mask.py
14 +++ b/lib/portage/tests/util/test_install_mask.py
15 @@ -1,8 +1,11 @@
16 # Copyright 2018 Gentoo Foundation
17 # Distributed under the terms of the GNU General Public License v2
18
19 +import tempfile
20 +from portage import os
21 +from portage import shutil
22 from portage.tests import TestCase
23 -from portage.util.install_mask import InstallMask
24 +from portage.util.install_mask import InstallMask, install_mask_dir
25
26
27 class InstallMaskTestCase(TestCase):
28 @@ -163,3 +166,24 @@ class InstallMaskTestCase(TestCase):
29 self.assertEqual(install_mask.match(path), expected,
30 'unexpected match result for "{}" with path {}'.\
31 format(install_mask_str, path))
32 +
33 + def testSymlinkDir(self):
34 + """
35 + Test that masked symlinks to directories are removed.
36 + """
37 + tmp_dir = tempfile.mkdtemp()
38 + try:
39 + base_dir = os.path.join(tmp_dir, 'foo')
40 + target_dir = os.path.join(tmp_dir, 'foo', 'bar')
41 + link_name = os.path.join(tmp_dir, 'foo', 'baz')
42 + os.mkdir(base_dir)
43 + os.mkdir(target_dir)
44 + os.symlink(target_dir, link_name)
45 + install_mask = InstallMask('/foo/')
46 + install_mask_dir(tmp_dir, install_mask)
47 + self.assertFalse(os.path.lexists(link_name),
48 + 'failed to remove {}'.format(link_name))
49 + self.assertFalse(os.path.lexists(base_dir),
50 + 'failed to remove {}'.format(base_dir))
51 + finally:
52 + shutil.rmtree(tmp_dir)
53 diff --git a/lib/portage/util/install_mask.py b/lib/portage/util/install_mask.py
54 index 9442128bd..6fd393483 100644
55 --- a/lib/portage/util/install_mask.py
56 +++ b/lib/portage/util/install_mask.py
57 @@ -165,22 +165,26 @@ def install_mask_dir(base_dir, install_mask, onerror=None):
58 dir_stack = []
59
60 # Remove masked files.
61 - for parent, dirs, files in os.walk(base_dir, onerror=onerror):
62 + todo = [base_dir]
63 + while todo:
64 + parent = todo.pop()
65 try:
66 parent = _unicode_decode(parent, errors='strict')
67 except UnicodeDecodeError:
68 continue
69 +
70 dir_stack.append(parent)
71 - for fname in files:
72 + for entry in os.scandir(parent):
73 try:
74 - fname = _unicode_decode(fname, errors='strict')
75 + abs_path = _unicode_decode(entry.path, errors='strict')
76 except UnicodeDecodeError:
77 continue
78 - abs_path = os.path.join(parent, fname)
79 - relative_path = abs_path[base_dir_len:]
80 - if install_mask.match(relative_path):
81 +
82 + if entry.is_dir(follow_symlinks=False):
83 + todo.append(entry.path)
84 + elif install_mask.match(abs_path[base_dir_len:]):
85 try:
86 - os.unlink(abs_path)
87 + os.unlink(entry.path)
88 except OSError as e:
89 onerror(e)
90
91 --
92 2.31.1.527.g47e6f16901-goog