# Buggy code
def make_file_dict(filename):
    """Generate the data dictionary for the given RPath

    This is a global function so that os.name can be called locally,
    thus avoiding network lag and so that we only need to send the
    filename over the network, thus avoiding the need to pickle an
    (incomplete) rpath object.
    """

    def _readlink(filename):
        """FIXME wrapper function to workaround a bug in os.readlink on Windows
        not accepting bytes path. This function can be removed once pyinstaller
        supports Python 3.8 and a new release can be made.
        See https://github.com/pyinstaller/pyinstaller/issues/4311
        """

        if os.name == "nt" and not isinstance(filename, str):
            # we assume a bytes representation
            return os.fsencode(os.readlink(os.fsdecode(filename)))
        else:
            return os.readlink(filename)

    try:
        statblock = os.lstat(filename)
    except (FileNotFoundError, NotADirectoryError):
        return {"type": None}
    data = {}
    mode = statblock[stat.ST_MODE]

    if stat.S_ISREG(mode):
        type_ = "reg"
    elif stat.S_ISDIR(mode):
        type_ = "dir"
    elif stat.S_ISCHR(mode):
        type_ = "dev"
        s = statblock.st_rdev
        data["devnums"] = ("c",) + (s >> 8, s & 0xFF)
    elif stat.S_ISBLK(mode):
        type_ = "dev"
        s = statblock.st_rdev
        data["devnums"] = ("b",) + (s >> 8, s & 0xFF)
    elif stat.S_ISFIFO(mode):
        type_ = "fifo"
    elif stat.S_ISLNK(mode):
        type_ = "sym"
        # FIXME reverse once Python 3.8 can be used under Windows
        # data['linkname'] = os.readlink(filename)
        data["linkname"] = _readlink(filename)
    elif stat.S_ISSOCK(mode):
        type_ = "sock"
    else:
        raise C.UnknownFileError(filename)
    data["type"] = type_
    data["size"] = statblock[stat.ST_SIZE]
    data["perms"] = stat.S_IMODE(mode)
    data["uid"] = statblock[stat.ST_UID]
    data["gid"] = statblock[stat.ST_GID]
    data["inode"] = statblock[stat.ST_INO]
    data["devloc"] = statblock[stat.ST_DEV]
    data["nlink"] = statblock[stat.ST_NLINK]

    if os.name == "nt":
        attribs = win32api.GetFileAttributes(os.fsdecode(filename))
        if attribs & win32con.FILE_ATTRIBUTE_REPARSE_POINT:
            data["type"] = "sym"
            data["linkname"] = None

    if not (type_ == "sym" or type_ == "dev"):
        # mtimes on symlinks and dev files don't work consistently
        data["mtime"] = int(statblock[stat.ST_MTIME])
        data["atime"] = int(statblock[stat.ST_ATIME])
        data["ctime"] = int(statblock[stat.ST_CTIME])
    return data


# Traceback (pywintypes.error)
2020-06-05 11:41:40.304281 +0530  <CLIENT-8640>  Exception '(32, 'GetFileAttributes', 'The process cannot access the file because it is being used by another process.')' raised of class '<class 'pywintypes.error'>':
File "rdiff_backup\robust.py", line 35, in check_common_error
File "rdiff_backup\rpath.py", line 1412, in append
File "rdiff_backup\rpath.py", line 1082, in __init__
File "rdiff_backup\rpath.py", line 1108, in setdata
File "rdiff_backup\rpath.py", line 425, in make_file_dict

2020-06-05 11:41:40.304281 +0530  <CLIENT-8640>  Exception '(32, 'GetFileAttributes', 'The process cannot access the file because it is being used by another process.')' raised of class '<class 'pywintypes.error'>':
File "rdiff_backup\Main.py", line 393, in error_check_Main
File "rdiff_backup\Main.py", line 415, in Main
File "rdiff_backup\Main.py", line 351, in take_action
File "rdiff_backup\Main.py", line 440, in Backup
File "rdiff_backup\backup.py", line 39, in Mirror
File "rdiff_backup\backup.py", line 267, in patch
File "rdiff_backup\rorpiter.py", line 202, in FillInIter
File "rdiff_backup\backup.py", line 114, in get_diffs
File "rdiff_backup\backup.py", line 196, in get_sigs
File "rdiff_backup\backup.py", line 361, in __next__
File "rdiff_backup\rorpiter.py", line 109, in Collate2Iters
File "rdiff_backup\rorpiter.py", line 376, in __next__
File "rdiff_backup\selection.py", line 138, in Iterate_fast
File "rdiff_backup\selection.py", line 122, in diryield
File "rdiff_backup\robust.py", line 35, in check_common_error
File "rdiff_backup\rpath.py", line 1412, in append
File "rdiff_backup\rpath.py", line 1082, in __init__
File "rdiff_backup\rpath.py", line 1108, in setdata
File "rdiff_backup\rpath.py", line 425, in make_file_dict

Traceback (most recent call last):
File "rdiff_backup\rorpiter.py", line 105, in Collate2Iters
StopIteration

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "rdiff-backup", line 32, in <module>
File "rdiff_backup\Main.py", line 393, in error_check_Main
File "rdiff_backup\Main.py", line 415, in Main
File "rdiff_backup\Main.py", line 351, in take_action
File "rdiff_backup\Main.py", line 440, in Backup
File "rdiff_backup\backup.py", line 39, in Mirror
File "rdiff_backup\backup.py", line 267, in patch
File "rdiff_backup\rorpiter.py", line 202, in FillInIter
File "rdiff_backup\backup.py", line 114, in get_diffs
File "rdiff_backup\backup.py", line 196, in get_sigs
File "rdiff_backup\backup.py", line 361, in __next__
File "rdiff_backup\rorpiter.py", line 109, in Collate2Iters
File "rdiff_backup\rorpiter.py", line 376, in __next__
File "rdiff_backup\selection.py", line 138, in Iterate_fast
File "rdiff_backup\selection.py", line 122, in diryield
File "rdiff_backup\robust.py", line 35, in check_common_error
File "rdiff_backup\rpath.py", line 1412, in append
File "rdiff_backup\rpath.py", line 1082, in __init__
File "rdiff_backup\rpath.py", line 1108, in setdata
File "rdiff_backup\rpath.py", line 425, in make_file_dict
pywintypes.error: (32, 'GetFileAttributes', 'The process cannot access the file because it is being used by another process.')
[8640] Failed to execute script rdiff-backup

# Fixed code
def make_file_dict(filename):
    """Generate the data dictionary for the given RPath

    This is a global function so that os.name can be called locally,
    thus avoiding network lag and so that we only need to send the
    filename over the network, thus avoiding the need to pickle an
    (incomplete) rpath object.
    """

    def _readlink(filename):
        """FIXME wrapper function to workaround a bug in os.readlink on Windows
        not accepting bytes path. This function can be removed once pyinstaller
        supports Python 3.8 and a new release can be made.
        See https://github.com/pyinstaller/pyinstaller/issues/4311
        """

        if os.name == "nt" and not isinstance(filename, str):
            # we assume a bytes representation
            return os.fsencode(os.readlink(os.fsdecode(filename)))
        else:
            return os.readlink(filename)

    try:
        statblock = os.lstat(filename)
    except (FileNotFoundError, NotADirectoryError):
        # FIXME not sure if this shouldn't trigger a warning but doing it
        # generates (too) many messages during the tests
        # log.Log("Warning: missing file '%s' couldn't be assessed." % filename, 2)
        return {"type": None}
    data = {}
    mode = statblock[stat.ST_MODE]

    if stat.S_ISREG(mode):
        type_ = "reg"
    elif stat.S_ISDIR(mode):
        type_ = "dir"
    elif stat.S_ISCHR(mode):
        type_ = "dev"
        s = statblock.st_rdev
        data["devnums"] = ("c",) + (s >> 8, s & 0xFF)
    elif stat.S_ISBLK(mode):
        type_ = "dev"
        s = statblock.st_rdev
        data["devnums"] = ("b",) + (s >> 8, s & 0xFF)
    elif stat.S_ISFIFO(mode):
        type_ = "fifo"
    elif stat.S_ISLNK(mode):
        type_ = "sym"
        # FIXME reverse once Python 3.8 can be used under Windows
        # data['linkname'] = os.readlink(filename)
        data["linkname"] = _readlink(filename)
    elif stat.S_ISSOCK(mode):
        type_ = "sock"
    else:
        raise C.UnknownFileError(filename)
    data["type"] = type_
    data["size"] = statblock[stat.ST_SIZE]
    data["perms"] = stat.S_IMODE(mode)
    data["uid"] = statblock[stat.ST_UID]
    data["gid"] = statblock[stat.ST_GID]
    data["inode"] = statblock[stat.ST_INO]
    data["devloc"] = statblock[stat.ST_DEV]
    data["nlink"] = statblock[stat.ST_NLINK]

    if os.name == "nt":
        try:
            attribs = win32api.GetFileAttributes(os.fsdecode(filename))
        except pywintypes.error as exc:
            if exc.args[0] == 32:  # file in use
                # we could also ignore with: return {'type': None}
                # but this approach seems to be better handled
                attribs = 0
            else:
                # we replace the specific Windows exception by a generic
                # one also understood by a potential Linux client/server
                raise OSError(
                    None, exc.args[1] + " - " + exc.args[2], filename, exc.args[0]
                ) from None
        if attribs & win32con.FILE_ATTRIBUTE_REPARSE_POINT:
            data["type"] = "sym"
            data["linkname"] = None

    if not (type_ == "sym" or type_ == "dev"):
        # mtimes on symlinks and dev files don't work consistently
        data["mtime"] = int(statblock[stat.ST_MTIME])
        data["atime"] = int(statblock[stat.ST_ATIME])
        data["ctime"] = int(statblock[stat.ST_CTIME])
    return data