8000 GH-118447: Fix handling of unreadable symlinks in `os.path.realpath()… · python/cpython@caf6064 · GitHub
[go: up one dir, main page]

Skip to content

Commit caf6064

Browse files
GH-118447: Fix handling of unreadable symlinks in os.path.realpath() (#118489)
Co-authored-by: Nice Zombies <nineteendo19d0@gmail.com>
1 parent 30b4e9f commit caf6064

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

Lib/posixpath.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -471,26 +471,26 @@ def realpath(filename, *, strict=False):
471471
if not stat.S_ISLNK(st.st_mode):
472472
path = newpath
473473
continue
474+
if newpath in seen:
475+
# Already seen this path
476+
path = seen[newpath]
477+
if path is not None:
478+
# use cached value
479+
continue
480+
# The symlink is not resolved, so we must have a symlink loop.
481+
if strict:
482+
# Raise OSError(errno.ELOOP)
483+
os.stat(newpath)
484+
path = newpath
485+
continue
486+
target = os.readlink(newpath)
474487
except OSError:
475488
if strict:
476489
raise
477490
path = newpath
478491
continue
479492
# Resolve the symbolic link
480-
if newpath in s 10000 een:
481-
# Already seen this path
482-
path = seen[newpath]
483-
if path is not None:
484-
# use cached value
485-
continue
486-
# The symlink is not resolved, so we must have a symlink loop.
487-
if strict:
488-
# Raise OSError(errno.ELOOP)
489-
os.stat(newpath)
490-
path = newpath
491-
continue
492493
seen[newpath] = None # not resolved symlink
493-
target = os.readlink(newpath)
494494
if target.startswith(sep):
495495
# Symlink target is absolute; reset resolved path.
496496
path = sep

Lib/test/test_posixpath.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,23 @@ def test_realpath_resolve_first(self):
660660
safe_rmdir(ABSTFN + "/k")
661661
safe_rmdir(ABSTFN)
662662

663+
@os_helper.skip_unless_symlink
664+
@skip_if_ABSTFN_contains_backslash
665+
@unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions")
666+
def test_realpath_unreadable_symlink(self):
667+
try:
668+
os.symlink(ABSTFN+"1", ABSTFN)
669+
os.chmod(ABSTFN, 0o000, follow_symlinks=False)
670+
self.assertEqual(realpath(ABSTFN), ABSTFN)
671+
self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo')
672+
self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + '/foo')
673+
self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN)
674+
with self.assertRaises(PermissionError):
675+
realpath(ABSTFN, strict=True)
676+
finally:
677+
os.chmod(ABSTFN, 0o755, follow_symlinks=False)
678+
os.unlink(ABSTFN)
679+
663680
def test_relpath(self):
664681
(real_getcwd, os.getcwd) = (os.getcwd, lambda: r"/home/user/bar")
665682
try:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:func:`os.path.realpath` now suppresses any :exc:`OSError` from
2+
:func:`os.readlink` when *strict* mode is disabled (the default).

0 commit comments

Comments
 (0)
0