This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author eryksun
Recipients eryksun, nijave, paul.moore, steve.dower, tim.golden, zach.ware
Date 2022-02-17.04:52:54
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1645073574.78.0.275164761848.issue46763@roundup.psfhosted.org>
In-reply-to
Content
Sample implementation:

    import os
    import msvcrt
    import win32file 

    def samefile(f1, f2):
        """Test whether two paths refer to the same file or directory."""
        s1 = os.stat(f1)
        s2 = os.stat(f2)
        return _common_same_file(f1, f2, s1, s2)


    def sameopenfile(fd1, fd2):
        """Test whether two file descriptors refer to the same file."""
        s1 = os.fstat(fd1)
        s2 = os.fstat(fd2)
        return _common_same_file(fd1, fd2, s1, s2)


    def _common_same_file(f1, f2, s1, s2):
        if s1.st_ino != s2.st_ino or s1.st_dev != s2.st_dev:
            return False

        # (st_dev, st_ino) may be insufficient on its own. Use the final
        # NT path of each file to refine the comparison.
        p = _get_final_nt_paths([f1, f2])
        
        # The stat result is unreliable if the volume serial number (st_dev)
        # or file ID (st_ino) is 0.
        if 0 in (s1.st_dev, s1.st_ino):
            if None in p:
                return False
            return p[0] == p[1]

        # A volume shadow copy has the same volume serial number as the
        # base volume. In this case, the device names have to be compared.
        d = _get_device_names(p)
        if any('volumeshadowcopy' in n for n in d if n):
            return d[0] == d[1]

        return True


    def _get_final_nt_paths(files):
        result = []
        nt_normal = 0x2 # VOLUME_NAME_NT | FILE_NAME_NORMALIZED
        nt_opened = 0xA # VOLUME_NAME_NT | FILE_NAME_OPENED
        for f in files:
            p = None
            if f is not None:
                try:
                    p = _getfinalpathname(f, nt_normal)
                except OSError:
                    try:
                        p = _getfinalpathname(f, nt_opened)
                    except OSError:
                        pass
            result.append(p)
        return result


    def _get_device_names(paths):
        # Look for "\Device\{device name}[\]".
        result = []
        for p in paths:
            d = None
            if p is not None:
                q = p.split('\\', 3)
                if len(q) > 2 and q[1].lower() == 'device' and q[2]:
                    d = q[2].lower()
            result.append(d)
        return result


    def _getfinalpathname(p, flags=0):
        try:
            if isinstance(p, int):
                h = msvcrt.get_osfhandle(p)
            else:
                h = win32file.CreateFile(p, 0, 0, None, win32file.OPEN_EXISTING,
                        win32file.FILE_FLAG_BACKUP_SEMANTICS, None)
            return win32file.GetFinalPathNameByHandle(h, flags)
        except win32file.error as e:
            strerror = e.strerror.rstrip('\r\n .')
            raise OSError(0, strerror, p, e.winerror) from None
History
Date User Action Args
2022-02-17 04:52:54eryksunsetrecipients: + eryksun, paul.moore, tim.golden, zach.ware, steve.dower, nijave
2022-02-17 04:52:54eryksunsetmessageid: <1645073574.78.0.275164761848.issue46763@roundup.psfhosted.org>
2022-02-17 04:52:54eryksunlinkissue46763 messages
2022-02-17 04:52:54eryksuncreate