Write the contents of a filesystem to a tar file.

    Arguments:
        file (str or io.IOBase): Destination file, may be a file
            name or an open file object.
        compression (str, optional): Compression to use, or `None`
            for a plain Tar archive without compression.
        encoding(str): The encoding to use for filenames. The
            default is ``"utf-8"``.
        walker (~fs.walk.Walker, optional): A `Walker` instance, or
            `None` to use default walker. You can use this to specify
            which files you want to compress.

def write_tar(
    src_fs,  # type: FS
    file,  # type: Union[Text, BinaryIO]
    compression=None,  # type: Optional[Text]
    encoding="utf-8",  # type: Text
    walker=None,  # type: Optional[Walker]
):
    # type: (...) -> None
    """Write the contents of a filesystem to a tar file.

    Arguments:
        file (str or io.IOBase): Destination file, may be a file
            name or an open file object.
        compression (str, optional): Compression to use, or `None`
            for a plain Tar archive without compression.
        encoding(str): The encoding to use for filenames. The
            default is ``"utf-8"``.
        walker (~fs.walk.Walker, optional): A `Walker` instance, or
            `None` to use default walker. You can use this to specify
            which files you want to compress.

    """
    type_map = {
        ResourceType.block_special_file: tarfile.BLKTYPE,
        ResourceType.character: tarfile.CHRTYPE,
        ResourceType.directory: tarfile.DIRTYPE,
        ResourceType.fifo: tarfile.FIFOTYPE,
        ResourceType.file: tarfile.REGTYPE,
        ResourceType.socket: tarfile.AREGTYPE,  # no type for socket
        ResourceType.symlink: tarfile.SYMTYPE,
        ResourceType.unknown: tarfile.AREGTYPE,  # no type for unknown
    }

    tar_attr = [("uid", "uid"), ("gid", "gid"), ("uname", "user"), ("gname", "group")]

    mode = "w:{}".format(compression or "")
    if isinstance(file, (six.text_type, six.binary_type)):
        _tar = tarfile.open(file, mode=mode)
    else:
        _tar = tarfile.open(fileobj=file, mode=mode)

    current_time = time.time()
    walker = walker or Walker()
    with _tar:
        gen_walk = walker.info(src_fs, namespaces=["details", "stat", "access"])
        for path, info in gen_walk:
            # Tar names must be relative
            tar_name = relpath(path)
            if not six.PY3:
                # Python2 expects bytes filenames
                tar_name = tar_name.encode(encoding, "replace")

            tar_info = tarfile.TarInfo(tar_name)

            if info.has_namespace("stat"):
                mtime = info.get("stat", "st_mtime", current_time)
            else:
                mtime = info.modified or current_time

            if isinstance(mtime, datetime):
                mtime = datetime_to_epoch(mtime)
            if isinstance(mtime, float):
                mtime = int(mtime)
            tar_info.mtime = mtime

            for tarattr, infoattr in tar_attr:
                if getattr(info, infoattr, None) is not None:
                    setattr(tar_info, tarattr, getattr(info, infoattr, None))

            if info.has_namespace("access"):
                tar_info.mode = getattr(info.permissions, "mode", 0o420)

            if info.is_dir:
                tar_info.type = tarfile.DIRTYPE
                _tar.addfile(tar_info)
            else:
                tar_info.type = type_map.get(info.type, tarfile.REGTYPE)
                tar_info.size = info.size
                with src_fs.openbin(path) as bin_file:
                    _tar.addfile(tar_info, bin_file)