New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GH-118447: Fix handling of unreadable symlinks in os.path.realpath()
#118489
base: main
Are you sure you want to change the base?
Conversation
os.path.realpath()
os.path.realpath()
|
# use cached value | ||
continue | ||
# The symlink is not resolved, so we must have a symlink loop. | ||
raise OSError(errno.ELOOP, "Symlink loop", newpath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not keep os.stat(newpath)
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the way we raise NotADirectoryError
in #118290, and to save the small cost of a stat()
@@ -660,6 +660,20 @@ def test_realpath_resolve_first(self): | |||
safe_rmdir(ABSTFN + "/k") | |||
safe_rmdir(ABSTFN) | |||
|
|||
@os_helper.skip_unless_symlink | |||
@skip_if_ABSTFN_contains_backslash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it needed? How does the test work on Windows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It won't work on Windows because ABSTFN
is an absolute path with backslash separators. These decorators are also used for other tests in this suite.
try: | ||
os.symlink(ABSTFN+"1", ABSTFN) | ||
os.chmod(ABSTFN, 0o000, follow_symlinks=False) | ||
self.assertEqual(realpath(ABSTFN), ABSTFN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please test also cases where non-readable symlink is followed by other path components (including ..
), e.g. badlink/foo
, badlink/../foo
, badlink/foo/..
, etc.
If the following ..
removes badlink
, try to test something that involves a loop containing badlink
, if this is possible. Your code does not set seen[newpath] = None
before calling readlink()
, I wonder what effect does it have.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.assertEqual(realpath(ABSTFN), ABSTFN) | |
self.assertEqual(realpath(ABSTFN), ABSTFN) | |
self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo') | |
self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + '/foo') | |
self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, you're right, it shouldn't raise an error. That's what strict
is for.
I think that it is reasonable to expect that |
posixpath.realpath('secretlink')
raises #118447