Skip to content

Commit eb7560a

Browse files
pablogsalgodlygeek
andauthored
bpo-38894: Fix pathlib.Path.glob in the presence of symlinks and insufficient permissions (GH-18815)
Co-authored-by: Matt Wozniski <[email protected]>
1 parent aa450a0 commit eb7560a

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

Lib/pathlib.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -529,23 +529,26 @@ def _select_from(self, parent_path, is_dir, exists, scandir):
529529
try:
530530
entries = list(scandir(parent_path))
531531
for entry in entries:
532-
entry_is_dir = False
533-
try:
534-
entry_is_dir = entry.is_dir()
535-
except OSError as e:
536-
if not _ignore_error(e):
537-
raise
538-
if not self.dironly or entry_is_dir:
539-
name = entry.name
540-
if self.match(name):
541-
path = parent_path._make_child_relpath(name)
542-
for p in self.successor._select_from(path, is_dir, exists, scandir):
543-
yield p
532+
if self.dironly:
533+
try:
534+
# "entry.is_dir()" can raise PermissionError
535+
# in some cases (see bpo-38894), which is not
536+
# among the errors ignored by _ignore_error()
537+
if not entry.is_dir():
538+
continue
539+
except OSError as e:
540+
if not _ignore_error(e):
541+
raise
542+
continue
543+
name = entry.name
544+
if self.match(name):
545+
path = parent_path._make_child_relpath(name)
546+
for p in self.successor._select_from(path, is_dir, exists, scandir):
547+
yield p
544548
except PermissionError:
545549
return
546550

547551

548-
549552
class _RecursiveWildcardSelector(_Selector):
550553

551554
def __init__(self, pat, child_parts, flavour):

Lib/test/test_pathlib.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,42 @@ def test_glob_dotdot(self):
15951595
self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
15961596
self.assertEqual(set(p.glob("../xyzzy")), set())
15971597

1598+
@support.skip_unless_symlink
1599+
def test_glob_permissions(self):
1600+
# See bpo-38894
1601+
P = self.cls
1602+
base = P(BASE) / 'permissions'
1603+
base.mkdir()
1604+
1605+
file1 = base / "file1"
1606+
file1.touch()
1607+
file2 = base / "file2"
1608+
file2.touch()
1609+
1610+
subdir = base / "subdir"
1611+
1612+
file3 = base / "file3"
1613+
file3.symlink_to(subdir / "other")
1614+
1615+
# Patching is needed to avoid relying on the filesystem
1616+
# to return the order of the files as the error will not
1617+
# happen if the symlink is the last item.
1618+
1619+
with mock.patch("os.scandir") as scandir:
1620+
scandir.return_value = sorted(os.scandir(base))
1621+
self.assertEqual(len(set(base.glob("*"))), 3)
1622+
1623+
subdir.mkdir()
1624+
1625+
with mock.patch("os.scandir") as scandir:
1626+
scandir.return_value = sorted(os.scandir(base))
1627+
self.assertEqual(len(set(base.glob("*"))), 4)
1628+
1629+
subdir.chmod(000)
1630+
1631+
with mock.patch("os.scandir") as scandir:
1632+
scandir.return_value = sorted(os.scandir(base))
1633+
self.assertEqual(len(set(base.glob("*"))), 4)
15981634

15991635
def _check_resolve(self, p, expected, strict=True):
16001636
q = p.resolve(strict)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix a bug that was causing incomplete results when calling
2+
``pathlib.Path.glob`` in the presence of symlinks that point
3+
to files where the user does not have read access. Patch by Pablo
4+
Galindo and Matt Wozniski.

0 commit comments

Comments
 (0)