static bool ad_convert_xattr(vfs_handle_struct *handle,
			     struct adouble *ad,
			     const struct smb_filename *smb_fname,
			     const char *catia_mappings,
			     bool *converted_xattr)
{
	static struct char_mappings **string_replace_cmaps = NULL;
	uint16_t i;
	int saved_errno = 0;
	NTSTATUS status;
	int rc;
	bool ok;

	*converted_xattr = false;

	if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
		return true;
	}

	if (string_replace_cmaps == NULL) {
		const char **mappings = NULL;

		mappings = str_list_make_v3_const(
			talloc_tos(), catia_mappings, NULL);
		if (mappings == NULL) {
			return false;
		}
		string_replace_cmaps = string_replace_init_map(
			handle->conn->sconn, mappings);
		TALLOC_FREE(mappings);
	}

	for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
		struct ad_xattr_entry *e = &ad->adx_entries[i];
		char *mapped_name = NULL;
		char *tmp = NULL;
		struct smb_filename *stream_name = NULL;
		files_struct *fsp = NULL;
		ssize_t nwritten;

		status = string_replace_allocate(handle->conn,
						 e->adx_name,
						 string_replace_cmaps,
						 talloc_tos(),
						 &mapped_name,
						 vfs_translate_to_windows);
		if (!NT_STATUS_IS_OK(status) &&
		    !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
		{
			DBG_ERR("string_replace_allocate failed\n");
			ok = false;
			goto fail;
		}

		tmp = mapped_name;
		mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
		TALLOC_FREE(tmp);
		if (mapped_name == NULL) {
			ok = false;
			goto fail;
		}

		stream_name = synthetic_smb_fname(talloc_tos(),
						  smb_fname->base_name,
						  mapped_name,
						  NULL,
						  smb_fname->twrp,
						  smb_fname->flags);
		TALLOC_FREE(mapped_name);
		if (stream_name == NULL) {
			DBG_ERR("synthetic_smb_fname failed\n");
			ok = false;
			goto fail;
		}

		DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));

		rc = vfs_stat(handle->conn, stream_name);
		if (rc == -1 && errno != ENOENT) {
			ok = false;
			goto fail;
		}

		status = openat_pathref_fsp(handle->conn->cwd_fsp, stream_name);
		if (!NT_STATUS_IS_OK(status) &&
		    !NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND))
		{
			ok = false;
			goto fail;
		}

		status = SMB_VFS_CREATE_FILE(
			handle->conn,			/* conn */
			NULL,				/* req */
			stream_name,			/* fname */
			FILE_GENERIC_WRITE,		/* access_mask */
			FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
			FILE_OPEN_IF,			/* create_disposition */
			0,				/* create_options */
			0,				/* file_attributes */
			INTERNAL_OPEN_ONLY,		/* oplock_request */
			NULL,				/* lease */
			0,				/* allocation_size */
			0,				/* private_flags */
			NULL,				/* sd */
			NULL,				/* ea_list */
			&fsp,				/* result */
			NULL,				/* psbuf */
			NULL, NULL);			/* create context */
		TALLOC_FREE(stream_name);
		if (!NT_STATUS_IS_OK(status)) {
			DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
			ok = false;
			goto fail;
		}

		nwritten = SMB_VFS_PWRITE(fsp,
					  ad->ad_data + e->adx_offset,
					  e->adx_length,
					  0);
		if (nwritten == -1) {
			DBG_ERR("SMB_VFS_PWRITE failed\n");
			saved_errno = errno;
			close_file(NULL, fsp, ERROR_CLOSE);
			errno = saved_errno;
			ok = false;
			goto fail;
		}

		status = close_file(NULL, fsp, NORMAL_CLOSE);
		if (!NT_STATUS_IS_OK(status)) {
			ok = false;
			goto fail;
		}
		fsp = NULL;
	}

	ad->adx_header.adx_num_attrs = 0;
	TALLOC_FREE(ad->adx_entries);

	ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);

	rc = ad_fset(handle, ad, ad->ad_fsp);
	if (rc != 0) {
		DBG_ERR("ad_fset on [%s] failed: %s\n",
			fsp_str_dbg(ad->ad_fsp), strerror(errno));
		ok = false;
		goto fail;
	}

	ok = ad_convert_move_reso(handle, ad, smb_fname);
	if (!ok) {
		goto fail;
	}

	*converted_xattr = true;
	ok = true;

fail:
	return ok;
}