-
-
Notifications
You must be signed in to change notification settings - Fork 32.4k
gh-110495: ensure ntpath.realpath()
result is a final, normalized pathname.
#110751
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
base: main
Are you sure you want to change the base?
gh-110495: ensure ntpath.realpath()
result is a final, normalized pathname.
#110751
Conversation
@eryksun can you review this? |
cc @serhiy-storchaka (os.path expert) & @barneygale (with the reproducer in the issue, |
1f8656e
to
3ae56f0
Compare
…-normalized-pathname
I'm not having much luck getting this to work with a common def _getfinalpathname_nonstrict(path):
# These error codes indicate that we should stop resolving the
# path and return the value we currently have.
allowed_winerror = (
1, # ERROR_INVALID_FUNCTION
2, # ERROR_FILE_NOT_FOUND
3, # ERROR_DIRECTORY_NOT_FOUND
5, # ERROR_ACCESS_DENIED
21, # ERROR_NOT_READY (e.g. drive with no media)
32, # ERROR_SHARING_VIOLATION (e.g. NTFS paging file)
50, # ERROR_NOT_SUPPORTED
53, # ERROR_BAD_NETPATH
65, # ERROR_NETWORK_ACCESS_DENIED
67, # ERROR_BAD_NET_NAME (e.g. remote server unavailable)
87, # ERROR_INVALID_PARAMETER
123, # ERROR_INVALID_NAME
161, # ERROR_BAD_PATHNAME
1920, # ERROR_CANT_ACCESS_FILE
1921, # ERROR_CANT_RESOLVE_FILENAME (e.g. can't follow symlink)
)
# Non-strict algorithm is to find as much of the target
# directory as we can and join the rest.
tail = path[:0]
seen = set()
while path and normcase(path) not in seen:
seen.add(normcase(path))
try:
path = _getfinalpathname(path)
return join(path, tail) if tail else path
except OSError as ex:
if ex.winerror not in allowed_winerror:
raise
# Split off the base name, and break if it's root.
parent_path, name = split(path)
if not name:
break
try:
# Try resolving the final component as a link. If
# successful, continue resolving the real path.
new_path = _readlink_deep(path)
if new_path != path:
path = new_path
continue
except OSError:
pass
# For some error cases, try to get the real name from
# the directory entry.
if ex.winerror in (1, 5, 32, 50, 87, 1920, 1921):
try:
name = _findfirstfile(path)
except OSError:
pass
path = parent_path
tail = join(name, tail) if tail else name
if path:
return join(path, tail) if tail else path
return tail |
Some of the checks in cpython/Lib/test/test_ntpath.py Lines 471 to 480 in 73dc1c6
New checks: self.assertPathEqual(ntpath.realpath(r"broken1"),
ABSTFN + r"\missing\bar")
self.assertPathEqual(ntpath.realpath(r"broken1\baz"),
ABSTFN + r"\missing\bar\baz")
self.assertPathEqual(ntpath.realpath("broken2"),
ABSTFN + r"\missing")
self.assertPathEqual(ntpath.realpath("broken3"),
ABSTFN + r"\missing") The corresponding |
By not breaking out of the loop when
_readlink_deep()
is successful, we can ensure that the result ofntpath.realpath()
is a final, normalized pathname.ntpath.realpath()
result is a final, normalized pathname. #110495