gh-82367: Use FindFirstFile Win32 API in ntpath.realpath() (GH-110298)

* Use `FindFirstFile` Win32 API to fix a bug where `ntpath.realpath()`
breaks out of traversing a series of paths where a (handled)
`ERROR_ACCESS_DENIED` or `ERROR_SHARING_VIOLATION` occurs.
* Update docs to reflect that `ntpath.realpath()` eliminates MS-DOS
style names.
This commit is contained in:
박문식 2023-10-05 23:49:07 +09:00 committed by GitHub
parent 2cb62c6437
commit d33aa18f15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 8 deletions

View file

@ -2,6 +2,7 @@ import inspect
import ntpath
import os
import string
import subprocess
import sys
import unittest
import warnings
@ -637,6 +638,48 @@ class TestNtpath(NtpathTestCase):
with os_helper.change_cwd(test_dir_short):
self.assertPathEqual(test_file_long, ntpath.realpath("file.txt"))
@unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname')
def test_realpath_permission(self):
# Test whether python can resolve the real filename of a
# shortened file name even if it does not have permission to access it.
ABSTFN = ntpath.realpath(os_helper.TESTFN)
os_helper.unlink(ABSTFN)
os_helper.rmtree(ABSTFN)
os.mkdir(ABSTFN)
self.addCleanup(os_helper.rmtree, ABSTFN)
test_file = ntpath.join(ABSTFN, "LongFileName123.txt")
test_file_short = ntpath.join(ABSTFN, "LONGFI~1.TXT")
with open(test_file, "wb") as f:
f.write(b"content")
# Automatic generation of short names may be disabled on
# NTFS volumes for the sake of performance.
# They're not supported at all on ReFS and exFAT.
subprocess.run(
# Try to set the short name manually.
['fsutil.exe', 'file', 'setShortName', test_file, 'LONGFI~1.TXT'],
creationflags=subprocess.DETACHED_PROCESS
)
try:
self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
except AssertionError:
raise unittest.SkipTest('the filesystem seems to lack support for short filenames')
# Deny the right to [S]YNCHRONIZE on the file to
# force nt._getfinalpathname to fail with ERROR_ACCESS_DENIED.
p = subprocess.run(
['icacls.exe', test_file, '/deny', '*S-1-5-32-545:(S)'],
creationflags=subprocess.DETACHED_PROCESS
)
if p.returncode:
raise unittest.SkipTest('failed to deny access to the test file')
self.assertPathEqual(test_file, ntpath.realpath(test_file_short))
def test_expandvars(self):
with os_helper.EnvironmentVarGuard() as env:
env.clear()