Message387519
On second thought, I think it's cleaner and avoids potential race conditions to implement ntpath.sameopenfile(). Then use that to implement ntpath.samefile(). Here's a summary of suggested changes, with sample code to flesh out the concepts:
* Extend nt._getfinalpathname to support file descriptors via
_get_osfhandle(), and to support a flags parameter that defaults
to 0 (i.e. VOLUME_NAME_DOS | FILE_NAME_NORMALIZED).
* Add the constants VOLUME_NAME_NONE, VOLUME_NAME_NT, VOLUME_NAME_GUID,
and FILE_NAME_OPENED.
* Fix _winapi.CreateFile to link with CreateFileW instead of CreateFileA,
and add the constant FILE_FLAG_BACKUP_SEMANTICS.
* Implement ntpath.sameopenfile():
def _getfinalpathname_try_norm(file, flags):
flags &= ~_nt.FILE_NAME_OPENED
try:
return _getfinalpathname(file, flags)
except OSError as e:
# 1: ERROR_INVALID_FUNCTION
# 50: ERROR_NOT_SUPPORTED
# 87: ERROR_INVALID_PARAMETER
if e.winerror not in (1, 87, 50):
raise
# Try to resolve the path as opened, which may contain
# legacy 8.3 short names.
return _getfinalpathname(file, flags | _nt.FILE_NAME_OPENED)
def sameopenfile(fp1, fp2):
"""Test whether two open file objects reference the same file"""
s1 = os.fstat(fp1)
s2 = os.fstat(fp2)
if s1.st_ino != s2.st_ino or s1.st_dev != s2.st_dev:
return False
if s1.st_dev and s1.st_ino:
return True
# st_dev or st_ino is 0, e.g. both are 0 for a WebDAV filesystem.
# Compare the final paths instead.
if s1.st_dev or s1.st_ino:
# Get the final paths without the device name.
r1 = _getfinalpathname_try_norm(fp1, _nt.VOLUME_NAME_NONE)
r2 = _getfinalpathname_try_norm(fp2, _nt.VOLUME_NAME_NONE)
if s1.st_dev:
# st_dev is non-zero, so just compare these
# device-relative paths.
return r1 == r2
n1 = _getfinalpathname_try_norm(fp1, _nt.VOLUME_NAME_NT)
n2 = _getfinalpathname_try_norm(fp2, _nt.VOLUME_NAME_NT)
if s1.st_ino:
# st_ino is non-zero, so ignore the paths on the device(s),
# which could be different hardlink paths for the same file.
# Just compare the device names.
index1, index2 = n1.rfind(r1), n2.rfind(r2)
if index1 == -1 or index2 == -1:
# This should never happen, but never say never.
index1 = index2 = None
d1 = n1[:index1]
d2 = n2[:index2]
return d1 == d2
# st_dev and st_ino are both 0. Compare the full NT paths.
return n1 == n2
* Implement ntpath.samefile() via sameopenfile():
def samefile(fn1, fn2):
"""Test whether two file names reference the same file"""
# Open the files to avoid race conditions.
to_close = []
if not isinstance(fn1, int):
h = _winapi.CreateFile(
os.fsdecode(fn1), 0, 0, 0, _winapi.OPEN_EXISTING,
_winapi.FILE_FLAG_BACKUP_SEMANTICS, 0)
try:
fn1 = _msvcrt.open_osfhandle(h, 0)
except:
_winapi.CloseHandle(h)
raise
to_close.append(fn1)
try:
if not isinstance(fn2, int):
h = _winapi.CreateFile(
os.fsdecode(fn2), 0, 0, 0, _winapi.OPEN_EXISTING,
_winapi.FILE_FLAG_BACKUP_SEMANTICS, 0)
try:
fn2 = _msvcrt.open_osfhandle(h, 0)
except:
_winapi.CloseHandle(h)
raise
to_close.append(fn2)
return sameopenfile(fn1, fn2)
finally:
for fd in to_close:
os.close(fd) |
|
Date |
User |
Action |
Args |
2021-02-22 17:28:06 | eryksun | set | recipients:
+ eryksun, paul.moore, giampaolo.rodola, tim.golden, r.david.murray, zach.ware, steve.dower, Deniz Bozyigit, gary ruben, alexkowel |
2021-02-22 17:28:06 | eryksun | set | messageid: <1614014886.14.0.743642695304.issue33935@roundup.psfhosted.org> |
2021-02-22 17:28:06 | eryksun | link | issue33935 messages |
2021-02-22 17:28:05 | eryksun | create | |
|