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 |