Exemplo n.º 1
0
/* struct smbd_smb2_read_state destructor. Send the SMB2_READ data. */
static int smb2_sendfile_send_data(struct smbd_smb2_read_state *state)
{
	struct lock_struct lock;
	uint32_t in_length = state->in_length;
	uint64_t in_offset = state->in_offset;
	files_struct *fsp = state->fsp;
	ssize_t nread;

	nread = SMB_VFS_SENDFILE(fsp->conn->sconn->sock,
					fsp,
					NULL,
					in_offset,
					in_length);
	DEBUG(10,("smb2_sendfile_send_data: SMB_VFS_SENDFILE returned %d on file %s\n",
		(int)nread,
		fsp_str_dbg(fsp) ));

	if (nread == -1) {
		if (errno == ENOSYS || errno == EINTR) {
			/*
			 * Special hack for broken systems with no working
			 * sendfile. Fake this up by doing read/write calls.
			*/
			set_use_sendfile(SNUM(fsp->conn), false);
			nread = fake_sendfile(fsp, in_offset, in_length);
			if (nread == -1) {
				DEBUG(0,("smb2_sendfile_send_data: "
					"fake_sendfile failed for "
					"file %s (%s).\n",
					fsp_str_dbg(fsp),
					strerror(errno)));
				exit_server_cleanly("smb2_sendfile_send_data: "
					"fake_sendfile failed");
			}
			goto out;
		}

		DEBUG(0,("smb2_sendfile_send_data: sendfile failed for file "
			"%s (%s). Terminating\n",
			fsp_str_dbg(fsp),
			strerror(errno)));
		exit_server_cleanly("smb2_sendfile_send_data: sendfile failed");
	} else if (nread == 0) {
		/*
		 * Some sendfile implementations return 0 to indicate
		 * that there was a short read, but nothing was
		 * actually written to the socket.  In this case,
		 * fallback to the normal read path so the header gets
		 * the correct byte count.
		 */
		DEBUG(3, ("send_file_readX: sendfile sent zero bytes "
			"falling back to the normal read: %s\n",
			fsp_str_dbg(fsp)));

		nread = fake_sendfile(fsp, in_offset, in_length);
		if (nread == -1) {
			DEBUG(0,("smb2_sendfile_send_data: "
				"fake_sendfile failed for file "
				"%s (%s). Terminating\n",
				fsp_str_dbg(fsp),
				strerror(errno)));
			exit_server_cleanly("smb2_sendfile_send_data: "
				"fake_sendfile failed");
		}
	}

  out:

	if (nread < in_length) {
		sendfile_short_send(fsp, nread, 0, in_length);
	}

	init_strict_lock_struct(fsp,
				state->in_file_id_volatile,
				in_offset,
				in_length,
				READ_LOCK,
				&lock);

	SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lock);
	return 0;
}
Exemplo n.º 2
0
static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx,
					      struct tevent_context *ev,
					      struct smbd_smb2_request *smb2req,
					      uint32_t in_smbpid,
					      uint64_t in_file_id_volatile,
					      uint32_t in_length,
					      uint64_t in_offset,
					      uint32_t in_minimum,
					      uint32_t in_remaining)
{
	NTSTATUS status;
	struct tevent_req *req = NULL;
	struct smbd_smb2_read_state *state = NULL;
	struct smb_request *smbreq = NULL;
	connection_struct *conn = smb2req->tcon->compat_conn;
	files_struct *fsp = NULL;
	ssize_t nread = -1;
	struct lock_struct lock;
	int saved_errno;

	req = tevent_req_create(mem_ctx, &state,
				struct smbd_smb2_read_state);
	if (req == NULL) {
		return NULL;
	}
	state->smb2req = smb2req;
	state->in_length = in_length;
	state->in_offset = in_offset;
	state->in_minimum = in_minimum;
	state->out_data = data_blob_null;
	state->out_remaining = 0;

	DEBUG(10,("smbd_smb2_read: file_id[0x%016llX]\n",
		  (unsigned long long)in_file_id_volatile));

	smbreq = smbd_smb2_fake_smb_request(smb2req);
	if (tevent_req_nomem(smbreq, req)) {
		return tevent_req_post(req, ev);
	}

	fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile);
	if (fsp == NULL) {
		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
		return tevent_req_post(req, ev);
	}
	if (conn != fsp->conn) {
		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
		return tevent_req_post(req, ev);
	}
	if (smb2req->session->vuid != fsp->vuid) {
		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
		return tevent_req_post(req, ev);
	}
	if (fsp->is_directory) {
		tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
		return tevent_req_post(req, ev);
	}

	state->fsp = fsp;
	state->in_file_id_volatile = in_file_id_volatile;

	if (IS_IPC(smbreq->conn)) {
		struct tevent_req *subreq = NULL;

		state->out_data = data_blob_talloc(state, NULL, in_length);
		if (in_length > 0 && tevent_req_nomem(state->out_data.data, req)) {
			return tevent_req_post(req, ev);
		}

		if (!fsp_is_np(fsp)) {
			tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
			return tevent_req_post(req, ev);
		}

		subreq = np_read_send(state, server_event_context(),
				      fsp->fake_file_handle,
				      state->out_data.data,
				      state->out_data.length);
		if (tevent_req_nomem(subreq, req)) {
			return tevent_req_post(req, ev);
		}
		tevent_req_set_callback(subreq,
					smbd_smb2_read_pipe_done,
					req);
		return req;
	}

	if (!CHECK_READ(fsp, smbreq)) {
		tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
		return tevent_req_post(req, ev);
	}

	status = schedule_smb2_aio_read(fsp->conn,
				smbreq,
				fsp,
				state,
				&state->out_data,
				(SMB_OFF_T)in_offset,
				(size_t)in_length);

	if (NT_STATUS_IS_OK(status)) {
		/*
		 * Doing an async read. Don't
		 * send a "gone async" message
		 * as we expect this to be less
		 * than the client timeout period.
		 * JRA. FIXME for offline files..
		 * FIXME. Add cancel code..
		 */
		smb2req->async = true;
		return req;
	}

	if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
		/* Real error in setting up aio. Fail. */
		tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
		return tevent_req_post(req, ev);
	}

	/* Fallback to synchronous. */

	init_strict_lock_struct(fsp,
				in_file_id_volatile,
				in_offset,
				in_length,
				READ_LOCK,
				&lock);

	if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) {
		tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
		return tevent_req_post(req, ev);
	}

	/* Try sendfile in preference. */
	status = schedule_smb2_sendfile_read(smb2req, state);
	if (NT_STATUS_IS_OK(status)) {
		tevent_req_done(req);
		return tevent_req_post(req, ev);
	} else {
		if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
			SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);
			tevent_req_nterror(req, status);
			return tevent_req_post(req, ev);
		}
	}

	/* Ok, read into memory. Allocate the out buffer. */
	state->out_data = data_blob_talloc(state, NULL, in_length);
	if (in_length > 0 && tevent_req_nomem(state->out_data.data, req)) {
		SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);
		return tevent_req_post(req, ev);
	}

	nread = read_file(fsp,
			  (char *)state->out_data.data,
			  in_offset,
			  in_length);

	saved_errno = errno;

	SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);

	DEBUG(10,("smbd_smb2_read: file %s handle [0x%016llX] offset=%llu "
		"len=%llu returned %lld\n",
		fsp_str_dbg(fsp),
		(unsigned long long)in_file_id_volatile,
		(unsigned long long)in_offset,
		(unsigned long long)in_length,
		(long long)nread));

	status = smb2_read_complete(req, nread, saved_errno);
	if (!NT_STATUS_IS_OK(status)) {
		tevent_req_nterror(req, status);
	} else {
		/* Success. */
		tevent_req_done(req);
	}
	return tevent_req_post(req, ev);
}
Exemplo n.º 3
0
Arquivo: aio.c Projeto: rti7743/samba
NTSTATUS schedule_aio_write_and_X(connection_struct *conn,
			      struct smb_request *smbreq,
			      files_struct *fsp, const char *data,
			      SMB_OFF_T startpos,
			      size_t numtowrite)
{
	struct aio_extra *aio_ex;
	SMB_STRUCT_AIOCB *a;
	size_t bufsize;
	size_t min_aio_write_size = lp_aio_write_size(SNUM(conn));
	int ret;

	/* Ensure aio is initialized. */
	if (!initialize_async_io_handler()) {
		return NT_STATUS_RETRY;
	}

	if (fsp->base_fsp != NULL) {
		/* No AIO on streams yet */
		DEBUG(10, ("AIO on streams not yet supported\n"));
		return NT_STATUS_RETRY;
	}

	if ((!min_aio_write_size || (numtowrite < min_aio_write_size))
	    && !SMB_VFS_AIO_FORCE(fsp)) {
		/* Too small a write for aio request. */
		DEBUG(10,("schedule_aio_write_and_X: write size (%u) too "
			  "small for minimum aio_write of %u\n",
			  (unsigned int)numtowrite,
			  (unsigned int)min_aio_write_size ));
		return NT_STATUS_RETRY;
	}

	/* Only do this on non-chained and non-chaining writes not using the
	 * write cache. */
        if (req_is_in_chain(smbreq) || (lp_write_cache_size(SNUM(conn)) != 0)) {
		return NT_STATUS_RETRY;
	}

	if (outstanding_aio_calls >= aio_pending_size) {
		DEBUG(3,("schedule_aio_write_and_X: Already have %d aio "
			 "activities outstanding.\n",
			  outstanding_aio_calls ));
		DEBUG(10,("schedule_aio_write_and_X: failed to schedule "
			  "aio_write for file %s, offset %.0f, len = %u "
			  "(mid = %u)\n",
			  fsp_str_dbg(fsp), (double)startpos,
			  (unsigned int)numtowrite,
			  (unsigned int)smbreq->mid ));
		return NT_STATUS_RETRY;
	}

	bufsize = smb_size + 6*2;

	if (!(aio_ex = create_aio_extra(NULL, fsp, bufsize))) {
		DEBUG(0,("schedule_aio_write_and_X: malloc fail.\n"));
		return NT_STATUS_NO_MEMORY;
	}
	aio_ex->handle_completion = handle_aio_write_complete;
	aio_ex->write_through = BITSETW(smbreq->vwv+7,0);

	construct_reply_common_req(smbreq, (char *)aio_ex->outbuf.data);
	srv_set_message((char *)aio_ex->outbuf.data, 6, 0, True);
	SCVAL(aio_ex->outbuf.data,smb_vwv0,0xFF); /* Never a chained reply. */

	init_strict_lock_struct(fsp, (uint64_t)smbreq->smbpid,
		(uint64_t)startpos, (uint64_t)numtowrite, WRITE_LOCK,
		&aio_ex->lock);

	/* Take the lock until the AIO completes. */
	if (!SMB_VFS_STRICT_LOCK(conn, fsp, &aio_ex->lock)) {
		TALLOC_FREE(aio_ex);
		return NT_STATUS_FILE_LOCK_CONFLICT;
	}

	a = &aio_ex->acb;

	/* Now set up the aio record for the write call. */

	a->aio_fildes = fsp->fh->fd;
	a->aio_buf = discard_const_p(char, data);
	a->aio_nbytes = numtowrite;
	a->aio_offset = startpos;
	a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
	a->aio_sigevent.sigev_signo  = RT_SIGNAL_AIO;
	a->aio_sigevent.sigev_value.sival_ptr = aio_ex;

	ret = SMB_VFS_AIO_WRITE(fsp, a);
	if (ret == -1) {
		DEBUG(3,("schedule_aio_wrote_and_X: aio_write failed. "
			 "Error %s\n", strerror(errno) ));
		SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock);
		TALLOC_FREE(aio_ex);
		return NT_STATUS_RETRY;
	}

	outstanding_aio_calls++;
	aio_ex->smbreq = talloc_move(aio_ex, &smbreq);

	/* This should actually be improved to span the write. */
	contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_WRITE);
	contend_level2_oplocks_end(fsp, LEVEL2_CONTEND_WRITE);

	if (!aio_ex->write_through && !lp_syncalways(SNUM(fsp->conn))
	    && fsp->aio_write_behind) {
		/* Lie to the client and immediately claim we finished the
		 * write. */
	        SSVAL(aio_ex->outbuf.data,smb_vwv2,numtowrite);
                SSVAL(aio_ex->outbuf.data,smb_vwv4,(numtowrite>>16)&1);
		show_msg((char *)aio_ex->outbuf.data);
		if (!srv_send_smb(aio_ex->smbreq->sconn,
				(char *)aio_ex->outbuf.data,
				true, aio_ex->smbreq->seqnum+1,
				IS_CONN_ENCRYPTED(fsp->conn),
				&aio_ex->smbreq->pcd)) {
			exit_server_cleanly("schedule_aio_write_and_X: "
					    "srv_send_smb failed.");
		}
		DEBUG(10,("schedule_aio_write_and_X: scheduled aio_write "
			  "behind for file %s\n", fsp_str_dbg(fsp)));
	}
Exemplo n.º 4
0
Arquivo: aio.c Projeto: rti7743/samba
NTSTATUS schedule_aio_read_and_X(connection_struct *conn,
			     struct smb_request *smbreq,
			     files_struct *fsp, SMB_OFF_T startpos,
			     size_t smb_maxcnt)
{
	struct aio_extra *aio_ex;
	SMB_STRUCT_AIOCB *a;
	size_t bufsize;
	size_t min_aio_read_size = lp_aio_read_size(SNUM(conn));
	int ret;

	/* Ensure aio is initialized. */
	if (!initialize_async_io_handler()) {
		return NT_STATUS_RETRY;
	}

	if (fsp->base_fsp != NULL) {
		/* No AIO on streams yet */
		DEBUG(10, ("AIO on streams not yet supported\n"));
		return NT_STATUS_RETRY;
	}

	if ((!min_aio_read_size || (smb_maxcnt < min_aio_read_size))
	    && !SMB_VFS_AIO_FORCE(fsp)) {
		/* Too small a read for aio request. */
		DEBUG(10,("schedule_aio_read_and_X: read size (%u) too small "
			  "for minimum aio_read of %u\n",
			  (unsigned int)smb_maxcnt,
			  (unsigned int)min_aio_read_size ));
		return NT_STATUS_RETRY;
	}

	/* Only do this on non-chained and non-chaining reads not using the
	 * write cache. */
        if (req_is_in_chain(smbreq) || (lp_write_cache_size(SNUM(conn)) != 0)) {
		return NT_STATUS_RETRY;
	}

	if (outstanding_aio_calls >= aio_pending_size) {
		DEBUG(10,("schedule_aio_read_and_X: Already have %d aio "
			  "activities outstanding.\n",
			  outstanding_aio_calls ));
		return NT_STATUS_RETRY;
	}

	/* The following is safe from integer wrap as we've already checked
	   smb_maxcnt is 128k or less. Wct is 12 for read replies */

	bufsize = smb_size + 12 * 2 + smb_maxcnt;

	if ((aio_ex = create_aio_extra(NULL, fsp, bufsize)) == NULL) {
		DEBUG(10,("schedule_aio_read_and_X: malloc fail.\n"));
		return NT_STATUS_NO_MEMORY;
	}
	aio_ex->handle_completion = handle_aio_read_complete;

	construct_reply_common_req(smbreq, (char *)aio_ex->outbuf.data);
	srv_set_message((char *)aio_ex->outbuf.data, 12, 0, True);
	SCVAL(aio_ex->outbuf.data,smb_vwv0,0xFF); /* Never a chained reply. */

	init_strict_lock_struct(fsp, (uint64_t)smbreq->smbpid,
		(uint64_t)startpos, (uint64_t)smb_maxcnt, READ_LOCK,
		&aio_ex->lock);

	/* Take the lock until the AIO completes. */
	if (!SMB_VFS_STRICT_LOCK(conn, fsp, &aio_ex->lock)) {
		TALLOC_FREE(aio_ex);
		return NT_STATUS_FILE_LOCK_CONFLICT;
	}

	a = &aio_ex->acb;

	/* Now set up the aio record for the read call. */

	a->aio_fildes = fsp->fh->fd;
	a->aio_buf = smb_buf(aio_ex->outbuf.data);
	a->aio_nbytes = smb_maxcnt;
	a->aio_offset = startpos;
	a->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
	a->aio_sigevent.sigev_signo  = RT_SIGNAL_AIO;
	a->aio_sigevent.sigev_value.sival_ptr = aio_ex;

	ret = SMB_VFS_AIO_READ(fsp, a);
	if (ret == -1) {
		DEBUG(0,("schedule_aio_read_and_X: aio_read failed. "
			 "Error %s\n", strerror(errno) ));
		SMB_VFS_STRICT_UNLOCK(conn, fsp, &aio_ex->lock);
		TALLOC_FREE(aio_ex);
		return NT_STATUS_RETRY;
	}

	outstanding_aio_calls++;
	aio_ex->smbreq = talloc_move(aio_ex, &smbreq);

	DEBUG(10,("schedule_aio_read_and_X: scheduled aio_read for file %s, "
		  "offset %.0f, len = %u (mid = %u)\n",
		  fsp_str_dbg(fsp), (double)startpos, (unsigned int)smb_maxcnt,
		  (unsigned int)aio_ex->smbreq->mid ));

	return NT_STATUS_OK;
}
Exemplo n.º 5
0
static NTSTATUS fsctl_zero_data(TALLOC_CTX *mem_ctx,
				struct tevent_context *ev,
				struct files_struct *fsp,
				DATA_BLOB *in_input)
{
	struct file_zero_data_info zdata_info;
	enum ndr_err_code ndr_ret;
	struct lock_struct lck;
	int mode;
	uint64_t len;
	int ret;
	NTSTATUS status;

	if (fsp == NULL) {
		return NT_STATUS_FILE_CLOSED;
	}

	/* WRITE_DATA permission is required */
	status = check_access_fsp(fsp, FILE_WRITE_DATA);
	if (!NT_STATUS_IS_OK(status)) {
		return status;
	}

	/* allow regardless of whether FS supports sparse or not */

	ndr_ret = ndr_pull_struct_blob(in_input, mem_ctx, &zdata_info,
			(ndr_pull_flags_fn_t)ndr_pull_file_zero_data_info);
	if (ndr_ret != NDR_ERR_SUCCESS) {
		DEBUG(0, ("failed to unmarshall zero data request\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}

	if (zdata_info.beyond_final_zero < zdata_info.file_off) {
		DEBUG(0, ("invalid zero data params: off %lu, bfz, %lu\n",
			  (unsigned long)zdata_info.file_off,
			  (unsigned long)zdata_info.beyond_final_zero));
		return NT_STATUS_INVALID_PARAMETER;
	}

	/* convert strange "beyond final zero" param into length */
	len = zdata_info.beyond_final_zero - zdata_info.file_off;

	if (len == 0) {
		DEBUG(2, ("zero data called with zero length range\n"));
		return NT_STATUS_OK;
	}

	init_strict_lock_struct(fsp,
				fsp->op->global->open_persistent_id,
				zdata_info.file_off,
				len,
				WRITE_LOCK,
				&lck);

	if (!SMB_VFS_STRICT_LOCK(fsp->conn, fsp, &lck)) {
		DEBUG(2, ("failed to lock range for zero-data\n"));
		return NT_STATUS_FILE_LOCK_CONFLICT;
	}

	/*
	 * MS-FSCC <58> Section 2.3.65
	 * This FSCTL sets the range of bytes to zero (0) without extending the
	 * file size.
	 *
	 * The VFS_FALLOCATE_FL_KEEP_SIZE flag is used to satisfy this
	 * constraint.
	 */

	mode = VFS_FALLOCATE_FL_PUNCH_HOLE | VFS_FALLOCATE_FL_KEEP_SIZE;
	ret = SMB_VFS_FALLOCATE(fsp, mode, zdata_info.file_off, len);
	if (ret == -1)  {
		status = map_nt_error_from_unix_common(errno);
		DEBUG(2, ("zero-data fallocate(0x%x) failed: %s\n", mode,
		      strerror(errno)));
		SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck);
		return status;
	}

	if (!fsp->is_sparse && lp_strict_allocate(SNUM(fsp->conn))) {
		/*
		 * File marked non-sparse and "strict allocate" is enabled -
		 * allocate the range that we just punched out.
		 * In future FALLOC_FL_ZERO_RANGE could be used exclusively for
		 * this, but it's currently only supported on XFS and ext4.
		 *
		 * The newly allocated range still won't be found by SEEK_DATA
		 * for QAR, but stat.st_blocks will reflect it.
		 */
		ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_FL_KEEP_SIZE,
					zdata_info.file_off, len);
		if (ret == -1)  {
			status = map_nt_error_from_unix_common(errno);
			DEBUG(0, ("fallocate failed: %s\n", strerror(errno)));
			SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck);
			return status;
		}
	}

	SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lck);
	return NT_STATUS_OK;
}
static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx,
					      struct tevent_context *ev,
					      struct smbd_smb2_request *smb2req,
					      struct files_struct *fsp,
					      uint32_t in_length,
					      uint64_t in_offset,
					      uint32_t in_minimum,
					      uint32_t in_remaining)
{
	NTSTATUS status;
	struct tevent_req *req = NULL;
	struct smbd_smb2_read_state *state = NULL;
	struct smb_request *smbreq = NULL;
	connection_struct *conn = smb2req->tcon->compat;
	ssize_t nread = -1;
	struct lock_struct lock;
	int saved_errno;

	req = tevent_req_create(mem_ctx, &state,
				struct smbd_smb2_read_state);
	if (req == NULL) {
		return NULL;
	}
	state->smb2req = smb2req;
	state->in_length = in_length;
	state->in_offset = in_offset;
	state->in_minimum = in_minimum;
	state->out_data = data_blob_null;
	state->out_remaining = 0;

	DEBUG(10,("smbd_smb2_read: %s - %s\n",
		  fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));

	smbreq = smbd_smb2_fake_smb_request(smb2req);
	if (tevent_req_nomem(smbreq, req)) {
		return tevent_req_post(req, ev);
	}
	state->smbreq = smbreq;

	if (fsp->is_directory) {
		tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
		return tevent_req_post(req, ev);
	}

	state->fsp = fsp;

	if (IS_IPC(smbreq->conn)) {
		struct tevent_req *subreq = NULL;

		state->out_data = data_blob_talloc(state, NULL, in_length);
		if (in_length > 0 && tevent_req_nomem(state->out_data.data, req)) {
			return tevent_req_post(req, ev);
		}

		if (!fsp_is_np(fsp)) {
			tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
			return tevent_req_post(req, ev);
		}

		subreq = np_read_send(state, ev,
				      fsp->fake_file_handle,
				      state->out_data.data,
				      state->out_data.length);
		if (tevent_req_nomem(subreq, req)) {
			return tevent_req_post(req, ev);
		}
		tevent_req_set_callback(subreq,
					smbd_smb2_read_pipe_done,
					req);
		return req;
	}

	if (!CHECK_READ(fsp, smbreq)) {
		tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
		return tevent_req_post(req, ev);
	}

	status = schedule_smb2_aio_read(fsp->conn,
				smbreq,
				fsp,
				state,
				&state->out_data,
				(off_t)in_offset,
				(size_t)in_length);

	if (NT_STATUS_IS_OK(status)) {
		/*
		 * Doing an async read, allow this
		 * request to be canceled
		 */
		tevent_req_set_cancel_fn(req, smbd_smb2_read_cancel);
		return req;
	}

	if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
		/* Real error in setting up aio. Fail. */
		tevent_req_nterror(req, status);
		return tevent_req_post(req, ev);
	}

	/* Fallback to synchronous. */

	init_strict_lock_struct(fsp,
				fsp->op->global->open_persistent_id,
				in_offset,
				in_length,
				READ_LOCK,
				&lock);

	if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) {
		tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
		return tevent_req_post(req, ev);
	}

	/* Try sendfile in preference. */
	status = schedule_smb2_sendfile_read(smb2req, state);
	if (NT_STATUS_IS_OK(status)) {
		tevent_req_done(req);
		return tevent_req_post(req, ev);
	} else {
		if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
			SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);
			tevent_req_nterror(req, status);
			return tevent_req_post(req, ev);
		}
	}

	/* Ok, read into memory. Allocate the out buffer. */
	state->out_data = data_blob_talloc(state, NULL, in_length);
	if (in_length > 0 && tevent_req_nomem(state->out_data.data, req)) {
		SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);
		return tevent_req_post(req, ev);
	}

	nread = read_file(fsp,
			  (char *)state->out_data.data,
			  in_offset,
			  in_length);

	saved_errno = errno;

	SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock);

	DEBUG(10,("smbd_smb2_read: file %s, %s, offset=%llu "
		"len=%llu returned %lld\n",
		fsp_str_dbg(fsp),
		fsp_fnum_dbg(fsp),
		(unsigned long long)in_offset,
		(unsigned long long)in_length,
		(long long)nread));

	status = smb2_read_complete(req, nread, saved_errno);
	if (!NT_STATUS_IS_OK(status)) {
		tevent_req_nterror(req, status);
	} else {
		/* Success. */
		tevent_req_done(req);
	}
	return tevent_req_post(req, ev);
}
/* struct smbd_smb2_read_state destructor. Send the SMB2_READ data. */
static int smb2_sendfile_send_data(struct smbd_smb2_read_state *state)
{
	struct lock_struct lock;
	uint32_t in_length = state->in_length;
	uint64_t in_offset = state->in_offset;
	files_struct *fsp = state->fsp;
	const DATA_BLOB *hdr = state->smb2req->queue_entry.sendfile_header;
	NTSTATUS *pstatus = state->smb2req->queue_entry.sendfile_status;
	struct smbXsrv_connection *xconn = state->smb2req->xconn;
	ssize_t nread;
	ssize_t ret;
	int saved_errno;

	nread = SMB_VFS_SENDFILE(xconn->transport.sock,
				 fsp,
				 hdr,
				 in_offset,
				 in_length);
	DEBUG(10,("smb2_sendfile_send_data: SMB_VFS_SENDFILE returned %d on file %s\n",
		(int)nread,
		fsp_str_dbg(fsp) ));

	if (nread == -1) {
		saved_errno = errno;

		/*
		 * Returning ENOSYS means no data at all was sent.
		   Do this as a normal read. */
		if (errno == ENOSYS) {
			goto normal_read;
		}

		if (errno == EINTR) {
			/*
			 * Special hack for broken Linux with no working sendfile. If we
			 * return EINTR we sent the header but not the rest of the data.
			 * Fake this up by doing read/write calls.
			 */
			set_use_sendfile(SNUM(fsp->conn), false);
			nread = fake_sendfile(xconn, fsp, in_offset, in_length);
			if (nread == -1) {
				saved_errno = errno;
				DEBUG(0,("smb2_sendfile_send_data: fake_sendfile "
					 "failed for file %s (%s) for client %s. "
					 "Terminating\n",
					 fsp_str_dbg(fsp), strerror(saved_errno),
					 smbXsrv_connection_dbg(xconn)));
				*pstatus = map_nt_error_from_unix_common(saved_errno);
				return 0;
			}
			goto out;
		}

		DEBUG(0,("smb2_sendfile_send_data: sendfile failed for file "
			 "%s (%s) for client %s. Terminating\n",
			 fsp_str_dbg(fsp), strerror(saved_errno),
			 smbXsrv_connection_dbg(xconn)));
		*pstatus = map_nt_error_from_unix_common(saved_errno);
		return 0;
	} else if (nread == 0) {
		/*
		 * Some sendfile implementations return 0 to indicate
		 * that there was a short read, but nothing was
		 * actually written to the socket.  In this case,
		 * fallback to the normal read path so the header gets
		 * the correct byte count.
		 */
		DEBUG(3, ("send_file_readX: sendfile sent zero bytes "
			"falling back to the normal read: %s\n",
			fsp_str_dbg(fsp)));
		goto normal_read;
	}

	/*
	 * We got a short read
	 */
	goto out;

normal_read:
	/* Send out the header. */
	ret = write_data(xconn->transport.sock,
			 (const char *)hdr->data, hdr->length);
	if (ret != hdr->length) {
		saved_errno = errno;
		DEBUG(0,("smb2_sendfile_send_data: write_data failed for file "
			 "%s (%s) for client %s. Terminating\n",
			 fsp_str_dbg(fsp), strerror(saved_errno),
			 smbXsrv_connection_dbg(xconn)));
		*pstatus = map_nt_error_from_unix_common(saved_errno);
		return 0;
	}
	nread = fake_sendfile(xconn, fsp, in_offset, in_length);
	if (nread == -1) {
		saved_errno = errno;
		DEBUG(0,("smb2_sendfile_send_data: fake_sendfile "
			 "failed for file %s (%s) for client %s. "
			 "Terminating\n",
			 fsp_str_dbg(fsp), strerror(saved_errno),
			 smbXsrv_connection_dbg(xconn)));
		*pstatus = map_nt_error_from_unix_common(saved_errno);
		return 0;
	}

  out:

	if (nread < in_length) {
		ret = sendfile_short_send(xconn, fsp, nread,
					  hdr->length, in_length);
		if (ret == -1) {
			saved_errno = errno;
			DEBUG(0,("%s: sendfile_short_send "
				 "failed for file %s (%s) for client %s. "
				 "Terminating\n",
				 __func__,
				 fsp_str_dbg(fsp), strerror(saved_errno),
				 smbXsrv_connection_dbg(xconn)));
			*pstatus = map_nt_error_from_unix_common(saved_errno);
			return 0;
		}
	}

	init_strict_lock_struct(fsp,
				fsp->op->global->open_persistent_id,
				in_offset,
				in_length,
				READ_LOCK,
				&lock);

	SMB_VFS_STRICT_UNLOCK(fsp->conn, fsp, &lock);

	*pstatus = NT_STATUS_OK;
	return 0;
}