Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
75e06496
Commit
75e06496
authored
Aug 21, 2019
by
Steve Dower
Committed by
GitHub
Aug 21, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpo-9949: Enable symlink traversal for ntpath.realpath (GH-15287)
parent
e1c638da
Changes
8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
304 additions
and
32 deletions
+304
-32
Doc/library/os.path.rst
Doc/library/os.path.rst
+9
-1
Doc/whatsnew/3.8.rst
Doc/whatsnew/3.8.rst
+3
-0
Lib/ntpath.py
Lib/ntpath.py
+88
-19
Lib/test/test_ntpath.py
Lib/test/test_ntpath.py
+195
-3
Lib/test/test_os.py
Lib/test/test_os.py
+1
-4
Lib/test/test_shutil.py
Lib/test/test_shutil.py
+1
-5
Lib/unittest/test/test_discovery.py
Lib/unittest/test/test_discovery.py
+6
-0
Misc/NEWS.d/next/Windows/2019-08-14-13-40-15.bpo-9949.zW45Ks.rst
...WS.d/next/Windows/2019-08-14-13-40-15.bpo-9949.zW45Ks.rst
+1
-0
No files found.
Doc/library/os.path.rst
View file @
75e06496
...
...
@@ -350,11 +350,19 @@ the :mod:`glob` module.)
.. function:: realpath(path)
Return the canonical path of the specified filename, eliminating any symbolic
links encountered in the path (if they are supported by the operating system).
links encountered in the path (if they are supported by the operating
system).
.. note::
When symbolic link cycles occur, the returned path will be one member of
the cycle, but no guarantee is made about which member that will be.
.. versionchanged:: 3.6
Accepts a :term:`path-like object`.
.. versionchanged:: 3.8
Symbolic links and junctions are now resolved on Windows.
.. function:: relpath(path, start=os.curdir)
...
...
Doc/whatsnew/3.8.rst
View file @
75e06496
...
...
@@ -824,6 +824,9 @@ characters or bytes unrepresentable at the OS level.
environment variable and does not use :envvar:`HOME`, which is not normally set
for regular user accounts.
:func:`~os.path.realpath` on Windows now resolves reparse points, including
symlinks and directory junctions.
ncurses
-------
...
...
Lib/ntpath.py
View file @
75e06496
...
...
@@ -519,8 +519,94 @@ else: # use native Windows method on Windows
except
(
OSError
,
ValueError
):
return
_abspath_fallback
(
path
)
# realpath is a no-op on systems without islink support
realpath
=
abspath
try
:
from
nt
import
_getfinalpathname
,
readlink
as
_nt_readlink
except
ImportError
:
# realpath is a no-op on systems without _getfinalpathname support.
realpath
=
abspath
else
:
def
_readlink_deep
(
path
,
seen
=
None
):
if
seen
is
None
:
seen
=
set
()
while
normcase
(
path
)
not
in
seen
:
seen
.
add
(
normcase
(
path
))
try
:
path
=
_nt_readlink
(
path
)
except
OSError
as
ex
:
# Stop on file (2) or directory (3) not found, or
# paths that are not reparse points (4390)
if
ex
.
winerror
in
(
2
,
3
,
4390
):
break
raise
except
ValueError
:
# Stop on reparse points that are not symlinks
break
return
path
def
_getfinalpathname_nonstrict
(
path
):
# Fast path to get the final path name. If this succeeds, there
# is no need to go any further.
try
:
return
_getfinalpathname
(
path
)
except
OSError
:
pass
# Allow file (2) or directory (3) not found, invalid syntax (123),
# and symlinks that cannot be followed (1921)
allowed_winerror
=
2
,
3
,
123
,
1921
# Non-strict algorithm is to find as much of the target directory
# as we can and join the rest.
tail
=
''
seen
=
set
()
while
path
:
try
:
path
=
_readlink_deep
(
path
,
seen
)
path
=
_getfinalpathname
(
path
)
return
join
(
path
,
tail
)
if
tail
else
path
except
OSError
as
ex
:
if
ex
.
winerror
not
in
allowed_winerror
:
raise
path
,
name
=
split
(
path
)
if
path
and
not
name
:
return
abspath
(
path
+
tail
)
tail
=
join
(
name
,
tail
)
if
tail
else
name
return
abspath
(
tail
)
def
realpath
(
path
):
path
=
os
.
fspath
(
path
)
if
isinstance
(
path
,
bytes
):
prefix
=
b'
\
\
\
\
?
\
\
'
unc_prefix
=
b'
\
\
\
\
?
\
\
UNC
\
\
'
new_unc_prefix
=
b'
\
\
\
\
'
cwd
=
os
.
getcwdb
()
else
:
prefix
=
'
\
\
\
\
?
\
\
'
unc_prefix
=
'
\
\
\
\
?
\
\
UNC
\
\
'
new_unc_prefix
=
'
\
\
\
\
'
cwd
=
os
.
getcwd
()
had_prefix
=
path
.
startswith
(
prefix
)
path
=
_getfinalpathname_nonstrict
(
path
)
# The path returned by _getfinalpathname will always start with \\?\ -
# strip off that prefix unless it was already provided on the original
# path.
if
not
had_prefix
and
path
.
startswith
(
prefix
):
# For UNC paths, the prefix will actually be \\?\UNC\
# Handle that case as well.
if
path
.
startswith
(
unc_prefix
):
spath
=
new_unc_prefix
+
path
[
len
(
unc_prefix
):]
else
:
spath
=
path
[
len
(
prefix
):]
# Ensure that the non-prefixed path resolves to the same path
try
:
if
_getfinalpathname
(
spath
)
==
path
:
path
=
spath
except
OSError
as
ex
:
pass
return
path
# Win9x family and earlier have no Unicode filename support.
supports_unicode_filenames
=
(
hasattr
(
sys
,
"getwindowsversion"
)
and
sys
.
getwindowsversion
()[
3
]
>=
2
)
...
...
@@ -633,23 +719,6 @@ def commonpath(paths):
raise
# determine if two files are in fact the same file
try
:
# GetFinalPathNameByHandle is available starting with Windows 6.0.
# Windows XP and non-Windows OS'es will mock _getfinalpathname.
if
sys
.
getwindowsversion
()[:
2
]
>=
(
6
,
0
):
from
nt
import
_getfinalpathname
else
:
raise
ImportError
except
(
AttributeError
,
ImportError
):
# On Windows XP and earlier, two files are the same if their absolute
# pathnames are the same.
# Non-Windows operating systems fake this method with an XP
# approximation.
def
_getfinalpathname
(
f
):
return
normcase
(
abspath
(
f
))
try
:
# The genericpath.isdir implementation uses os.stat and checks the mode
# attribute to tell whether or not the path is a directory.
...
...
Lib/test/test_ntpath.py
View file @
75e06496
This diff is collapsed.
Click to expand it.
Lib/test/test_os.py
View file @
75e06496
...
...
@@ -3358,10 +3358,7 @@ class OSErrorTests(unittest.TestCase):
if
hasattr
(
os
,
"lchmod"
):
funcs
.
append
((
self
.
filenames
,
os
.
lchmod
,
0o777
))
if
hasattr
(
os
,
"readlink"
):
if
sys
.
platform
==
"win32"
:
funcs
.
append
((
self
.
unicode_filenames
,
os
.
readlink
,))
else
:
funcs
.
append
((
self
.
filenames
,
os
.
readlink
,))
funcs
.
append
((
self
.
filenames
,
os
.
readlink
,))
for
filenames
,
func
,
*
func_args
in
funcs
:
...
...
Lib/test/test_shutil.py
View file @
75e06496
...
...
@@ -1871,11 +1871,7 @@ class TestMove(unittest.TestCase):
dst_link
=
os
.
path
.
join
(
self
.
dst_dir
,
'quux'
)
shutil
.
move
(
dst
,
dst_link
)
self
.
assertTrue
(
os
.
path
.
islink
(
dst_link
))
# On Windows, os.path.realpath does not follow symlinks (issue #9949)
if
os
.
name
==
'nt'
:
self
.
assertEqual
(
os
.
path
.
realpath
(
src
),
os
.
readlink
(
dst_link
))
else
:
self
.
assertEqual
(
os
.
path
.
realpath
(
src
),
os
.
path
.
realpath
(
dst_link
))
self
.
assertEqual
(
os
.
path
.
realpath
(
src
),
os
.
path
.
realpath
(
dst_link
))
@
support
.
skip_unless_symlink
@
mock_rename
...
...
Lib/unittest/test/test_discovery.py
View file @
75e06496
...
...
@@ -723,11 +723,13 @@ class TestDiscovery(unittest.TestCase):
original_listdir
=
os
.
listdir
original_isfile
=
os
.
path
.
isfile
original_isdir
=
os
.
path
.
isdir
original_realpath
=
os
.
path
.
realpath
def
cleanup
():
os
.
listdir
=
original_listdir
os
.
path
.
isfile
=
original_isfile
os
.
path
.
isdir
=
original_isdir
os
.
path
.
realpath
=
original_realpath
del
sys
.
modules
[
'foo'
]
if
full_path
in
sys
.
path
:
sys
.
path
.
remove
(
full_path
)
...
...
@@ -742,6 +744,10 @@ class TestDiscovery(unittest.TestCase):
os
.
listdir
=
listdir
os
.
path
.
isfile
=
isfile
os
.
path
.
isdir
=
isdir
if
os
.
name
==
'nt'
:
# ntpath.realpath may inject path prefixes when failing to
# resolve real files, so we substitute abspath() here instead.
os
.
path
.
realpath
=
os
.
path
.
abspath
return
full_path
def
test_detect_module_clash
(
self
):
...
...
Misc/NEWS.d/next/Windows/2019-08-14-13-40-15.bpo-9949.zW45Ks.rst
0 → 100644
View file @
75e06496
Enable support for following symlinks in :func:`os.realpath`.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment