static bool ad_collect_one_stream(struct vfs_handle_struct *handle,
				  struct char_mappings **cmaps,
				  struct smb_filename *smb_fname,
				  const struct stream_struct *stream,
				  struct adouble *ad,
				  struct ad_collect_state *state)
{
	struct smb_filename *sname = NULL;
	files_struct *fsp = NULL;
	struct ad_xattr_entry *e = NULL;
	char *mapped_name = NULL;
	char *p = NULL;
	size_t needed_size;
	ssize_t nread;
	NTSTATUS status;
	int ret;
	bool ok;

	sname = synthetic_smb_fname(ad,
				    smb_fname->base_name,
				    stream->name,
				    NULL,
				    smb_fname->twrp,
				    0);
	if (sname == NULL) {
		return false;
	}

	if (is_ntfs_default_stream_smb_fname(sname)) {
		TALLOC_FREE(sname);
		return true;
	}

	DBG_DEBUG("Collecting stream [%s]\n", smb_fname_str_dbg(sname));

	ret = SMB_VFS_STAT(handle->conn, sname);
	if (ret != 0) {
		DBG_ERR("SMB_VFS_STAT [%s] failed\n", smb_fname_str_dbg(sname));
		ok = false;
		goto out;
	}

	status = openat_pathref_fsp(handle->conn->cwd_fsp, sname);
	if (!NT_STATUS_IS_OK(status)) {
		ok = false;
		goto out;
	}

	status = SMB_VFS_CREATE_FILE(
		handle->conn,
		NULL,				/* req */
		sname,
		FILE_READ_DATA|DELETE_ACCESS,
		FILE_SHARE_READ,
		FILE_OPEN,
		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,
		NULL,				/* info */
		NULL, NULL);			/* create context */
	if (!NT_STATUS_IS_OK(status)) {
		DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed\n",
			smb_fname_str_dbg(sname));
		ok = false;
		goto out;
	}

	if (is_afpinfo_stream(stream->name)) {
		char buf[AFP_INFO_SIZE];

		if (stream->size != AFP_INFO_SIZE) {
			DBG_ERR("Bad size [%zd] on [%s]\n",
				(ssize_t)stream->size,
				smb_fname_str_dbg(sname));
			ok = false;
			goto out;
		}

		nread = SMB_VFS_PREAD(fsp, buf, stream->size, 0);
		if (nread != AFP_INFO_SIZE) {
			DBG_ERR("Bad size [%zd] on [%s]\n",
				(ssize_t)stream->size,
				smb_fname_str_dbg(sname));
			ok = false;
			goto out;
		}

		memcpy(ad->ad_data + ADEDOFF_FINDERI_DOT_UND,
		       buf + AFP_OFF_FinderInfo,
		       AFP_FinderSize);

		ok = set_delete_on_close(fsp,
					 true,
					 fsp->conn->session_info->security_token,
					 fsp->conn->session_info->unix_token);
		if (!ok) {
			DBG_ERR("Deleting [%s] failed\n",
				smb_fname_str_dbg(sname));
			ok = false;
			goto out;
		}
		ok = true;
		goto out;
	}

	if (is_afpresource_stream(stream->name)) {
		ad->ad_rsrc_data = talloc_size(ad, stream->size);
		if (ad->ad_rsrc_data == NULL) {
			ok = false;
			goto out;
		}

		nread = SMB_VFS_PREAD(fsp,
				      ad->ad_rsrc_data,
				      stream->size,
				      0);
		if (nread != stream->size) {
			DBG_ERR("Bad size [%zd] on [%s]\n",
				(ssize_t)stream->size,
				smb_fname_str_dbg(sname));
			ok = false;
			goto out;
		}

		ad_setentrylen(ad, ADEID_RFORK, stream->size);

		if (!state->have_adfile) {
			/*
			 * We have a resource *stream* but no AppleDouble
			 * sidecar file, this means the share is configured with
			 * fruit:resource=stream. So we should delete the
			 * resource stream.
			 */
			ok = set_delete_on_close(
				fsp,
				true,
				fsp->conn->session_info->security_token,
				fsp->conn->session_info->unix_token);
			if (!ok) {
				DBG_ERR("Deleting [%s] failed\n",
					smb_fname_str_dbg(sname));
				ok = false;
				goto out;
			}
		}
		ok = true;
		goto out;
	}

	ad->adx_entries = talloc_realloc(ad,
					 ad->adx_entries,
					 struct ad_xattr_entry,
					 ad->adx_header.adx_num_attrs + 1);
	if (ad->adx_entries == NULL) {
		ok = false;
		goto out;
	}

	e = &ad->adx_entries[ad->adx_header.adx_num_attrs];
	*e = (struct ad_xattr_entry) {
		.adx_length = stream->size,
	};
	e->adx_name = talloc_strdup(ad, stream->name + 1);
	if (e->adx_name == NULL) {
		ok = false;
		goto out;
	}
	p = strchr(e->adx_name, ':');
	if (p != NULL) {
		*p = '\0';
	}

	status = string_replace_allocate(handle->conn,
					 e->adx_name,
					 cmaps,
					 ad,
					 &mapped_name,
					 vfs_translate_to_unix);
	if (!NT_STATUS_IS_OK(status) &&
	    !NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
	{
		DBG_ERR("string_replace_allocate failed\n");
		ok = false;
		goto out;
	}

	e->adx_name = mapped_name;
	e->adx_namelen = strlen(e->adx_name) + 1,

	DBG_DEBUG("%u: name (%s) size (%zu)\n",
		  ad->adx_header.adx_num_attrs,
		  e->adx_name,
		  (size_t)e->adx_length);

	ad->adx_header.adx_num_attrs++;

	needed_size = state->adx_data_off + stream->size;
	if (needed_size > talloc_get_size(ad->adx_data)) {
		ad->adx_data = talloc_realloc(ad,
					      ad->adx_data,
					      char,
					      needed_size);
		if (ad->adx_data == NULL) {
			ok = false;
			goto out;
		}
	}

	nread = SMB_VFS_PREAD(fsp,
			      ad->adx_data + state->adx_data_off,
			      stream->size,
			      0);
	if (nread != stream->size) {
		DBG_ERR("Bad size [%zd] on [%s]\n",
			(ssize_t)stream->size,
			smb_fname_str_dbg(sname));
		ok = false;
		goto out;
	}
	state->adx_data_off += nread;

	ok = set_delete_on_close(fsp,
				 true,
				 fsp->conn->session_info->security_token,
				 fsp->conn->session_info->unix_token);
	if (!ok) {
		DBG_ERR("Deleting [%s] failed\n",
			smb_fname_str_dbg(sname));
		ok = false;
		goto out;
	}

out:
	TALLOC_FREE(sname);
	if (fsp != NULL) {
		status = close_file(NULL, fsp, NORMAL_CLOSE);
		if (!NT_STATUS_IS_OK(status)) {
			DBG_ERR("close_file [%s] failed: %s\n",
				smb_fname_str_dbg(smb_fname),
				nt_errstr(status));
			ok = false;
		}
	}

	return ok;
}