Eryk Sun <eryk...@gmail.com> added the comment:
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) ---------- _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue33935> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com