bpo-38894: Fix pathlib.Path.glob in the presence of symlinks and insufficient permissions (GH-18815)
Co-authored-by: Matt Wozniski <mwozniski@bloomberg.net>
This commit is contained in:
parent
aa450a0364
commit
eb7560a73d
|
@ -529,23 +529,26 @@ 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
|
||||||
except OSError as e:
|
# in some cases (see bpo-38894), which is not
|
||||||
if not _ignore_error(e):
|
# among the errors ignored by _ignore_error()
|
||||||
raise
|
if not entry.is_dir():
|
||||||
if not self.dironly or entry_is_dir:
|
continue
|
||||||
name = entry.name
|
except OSError as e:
|
||||||
if self.match(name):
|
if not _ignore_error(e):
|
||||||
path = parent_path._make_child_relpath(name)
|
raise
|
||||||
for p in self.successor._select_from(path, is_dir, exists, scandir):
|
continue
|
||||||
yield p
|
name = entry.name
|
||||||
|
if self.match(name):
|
||||||
|
path = parent_path._make_child_relpath(name)
|
||||||
|
for p in self.successor._select_from(path, is_dir, exists, scandir):
|
||||||
|
yield p
|
||||||
except PermissionError:
|
except PermissionError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class _RecursiveWildcardSelector(_Selector):
|
class _RecursiveWildcardSelector(_Selector):
|
||||||
|
|
||||||
def __init__(self, pat, child_parts, flavour):
|
def __init__(self, pat, child_parts, flavour):
|
||||||
|
|
|
@ -1595,6 +1595,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)
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue