Skip to content

Conversation

@moonsikpark
Copy link
Contributor

@moonsikparkmoonsikpark commented Oct 12, 2023

By not breaking out of the loop when _readlink_deep() is successful, we can ensure that the result of ntpath.realpath() is a final, normalized pathname.

@moonsikpark
Copy link
ContributorAuthor

@eryksun can you review this?

@encukou
Copy link
Member

cc @serhiy-storchaka (os.path expert) & @barneygale (with the reproducer in the issue, pathlib.Path('C:\\testdir\\symlink2\\filelink.txt').resolve() also shows the symlink rather than file)

@moonsikparkmoonsikparkforce-pushed the gh-110495-ensure-ntpath-realpath-final-normalized-pathname branch from 1f8656e to 3ae56f0CompareMarch 19, 2024 05:41
@eryksun
Copy link
Contributor

eryksun commented Jun 17, 2024

I'm not having much luck getting this to work with a common seen set. I think an independent set is needed in _getfinalpathname_nonstrict(). For example:

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_FUNCTION2, # ERROR_FILE_NOT_FOUND3, # ERROR_DIRECTORY_NOT_FOUND5, # ERROR_ACCESS_DENIED21, # ERROR_NOT_READY (e.g. drive with no media)32, # ERROR_SHARING_VIOLATION (e.g. NTFS paging file)50, # ERROR_NOT_SUPPORTED53, # ERROR_BAD_NETPATH65, # ERROR_NETWORK_ACCESS_DENIED67, # ERROR_BAD_NET_NAME (e.g. remote server unavailable)87, # ERROR_INVALID_PARAMETER123, # ERROR_INVALID_NAME161, # ERROR_BAD_PATHNAME1920, # ERROR_CANT_ACCESS_FILE1921, # 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() whilepathandnormcase(path) notinseen: seen.add(normcase(path)) try: path=_getfinalpathname(path) returnjoin(path, tail) iftailelsepathexceptOSErrorasex: ifex.winerrornotinallowed_winerror: raise# Split off the base name, and break if it's root.parent_path, name=split(path) ifnotname: breaktry: # Try resolving the final component as a link. If# successful, continue resolving the real path.new_path=_readlink_deep(path) ifnew_path!=path: path=new_pathcontinueexceptOSError: pass# For some error cases, try to get the real name from# the directory entry.ifex.winerrorin (1, 5, 32, 50, 87, 1920, 1921): try: name=_findfirstfile(path) exceptOSError: passpath=parent_pathtail=join(name, tail) iftailelsenameifpath: returnjoin(path, tail) iftailelsepathreturntail

@eryksun
Copy link
Contributor

Some of the checks in test_realpath_broken_symlinks() that validate an incomplete resolution of the final path would need to be modified.

# bpo-38453: We no longer recursively resolve segments of relative
# symlinks that the OS cannot resolve.
self.assertPathEqual(ntpath.realpath(r"broken1"),
ABSTFN+r"\broken\bar")
self.assertPathEqual(ntpath.realpath(r"broken1\baz"),
ABSTFN+r"\broken\bar\baz")
self.assertPathEqual(ntpath.realpath("broken2"),
ABSTFN+r"\self\self\missing")
self.assertPathEqual(ntpath.realpath("broken3"),
ABSTFN+r"\subdir\parent\subdir\parent\missing")

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 bytes path checks would also have to be updated.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@moonsikpark@encukou@eryksun@barneygale