Author eryksun
Recipients Deniz Bozyigit, alexkowel, eryksun, gary ruben, giampaolo.rodola, paul.moore, r.david.murray, steve.dower, tim.golden, zach.ware
Date 2021-02-22.17:28:05
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1614014886.14.0.743642695304.issue33935@roundup.psfhosted.org>
In-reply-to
Content
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)
History
Date User Action Args
2021-02-22 17:28:06eryksunsetrecipients: + eryksun, paul.moore, giampaolo.rodola, tim.golden, r.david.murray, zach.ware, steve.dower, Deniz Bozyigit, gary ruben, alexkowel
2021-02-22 17:28:06eryksunsetmessageid: <1614014886.14.0.743642695304.issue33935@roundup.psfhosted.org>
2021-02-22 17:28:06eryksunlinkissue33935 messages
2021-02-22 17:28:05eryksuncreate