bpo-38894: Fix pathlib.Path.glob in the presence of symlinks and insufficient permissions (GH-18815)

Co-authored-by: Matt Wozniski <mwozniski@bloomberg.net>
(cherry picked from commit eb7560a73d)

Co-authored-by: Pablo Galindo <Pablogsal@gmail.com>
This commit is contained in:
Miss Islington (bot) 2020-03-07 10:11:24 -08:00 committed by GitHub
parent 92b72788ec
commit 928b4dd0ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 13 deletions

View File

@ -529,13 +529,17 @@ class _WildcardSelector(_Selector):
try: try:
entries = list(scandir(parent_path)) entries = list(scandir(parent_path))
for entry in entries: for entry in entries:
entry_is_dir = False if self.dironly:
try: try:
entry_is_dir = entry.is_dir() # "entry.is_dir()" can raise PermissionError
# in some cases (see bpo-38894), which is not
# among the errors ignored by _ignore_error()
if not entry.is_dir():
continue
except OSError as e: except OSError as e:
if not _ignore_error(e): if not _ignore_error(e):
raise raise
if not self.dironly or entry_is_dir: continue
name = entry.name name = entry.name
if self.match(name): if self.match(name):
path = parent_path._make_child_relpath(name) path = parent_path._make_child_relpath(name)
@ -545,7 +549,6 @@ class _WildcardSelector(_Selector):
return return
class _RecursiveWildcardSelector(_Selector): class _RecursiveWildcardSelector(_Selector):
def __init__(self, pat, child_parts, flavour): def __init__(self, pat, child_parts, flavour):

View File

@ -1508,6 +1508,42 @@ class _BasePathTest(object):
self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") }) self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
self.assertEqual(set(p.glob("../xyzzy")), set()) self.assertEqual(set(p.glob("../xyzzy")), set())
@support.skip_unless_symlink
def test_glob_permissions(self):
# See bpo-38894
P = self.cls
base = P(BASE) / 'permissions'
base.mkdir()
file1 = base / "file1"
file1.touch()
file2 = base / "file2"
file2.touch()
subdir = base / "subdir"
file3 = base / "file3"
file3.symlink_to(subdir / "other")
# Patching is needed to avoid relying on the filesystem
# to return the order of the files as the error will not
# happen if the symlink is the last item.
with mock.patch("os.scandir") as scandir:
scandir.return_value = sorted(os.scandir(base))
self.assertEqual(len(set(base.glob("*"))), 3)
subdir.mkdir()
with mock.patch("os.scandir") as scandir:
scandir.return_value = sorted(os.scandir(base))
self.assertEqual(len(set(base.glob("*"))), 4)
subdir.chmod(000)
with mock.patch("os.scandir") as scandir:
scandir.return_value = sorted(os.scandir(base))
self.assertEqual(len(set(base.glob("*"))), 4)
def _check_resolve(self, p, expected, strict=True): def _check_resolve(self, p, expected, strict=True):
q = p.resolve(strict) q = p.resolve(strict)

View File

@ -0,0 +1,4 @@
Fix a bug that was causing incomplete results when calling
``pathlib.Path.glob`` in the presence of symlinks that point
to files where the user does not have read access. Patch by Pablo
Galindo and Matt Wozniski.