This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
this implements the IPC$ backend, called by the NTVFS subsystem to
#include "includes.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "ntvfs/ntvfs.h"
#include "libcli/rap/rap.h"
#include "ntvfs/ipc/proto.h"
-#include "rpc_server/dcerpc_server.h"
#include "libcli/raw/ioctl.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "lib/socket/socket.h"
+#include "../lib/util/tevent_unix.h"
+#include "../lib/util/tevent_coroutine.h"
/* this is the private structure used to keep the state of an open
ipc$ connection. It needs to keep information about all open
struct ipc_private {
struct ntvfs_module_context *ntvfs;
- struct dcesrv_context *dcesrv;
-
/* a list of open pipes */
struct pipe_state {
struct pipe_state *next, *prev;
- struct ipc_private *private;
+ struct ipc_private *ipriv;
const char *pipe_name;
struct ntvfs_handle *handle;
- struct dcesrv_connection *dce_conn;
- uint16_t ipc_state;
+ struct tstream_context *npipe;
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t allocation_size;
+ struct tevent_queue *write_queue;
+ struct tevent_queue *read_queue;
} *pipe_list;
};
/*
find a open pipe give a file handle
*/
-static struct pipe_state *pipe_state_find(struct ipc_private *private, struct ntvfs_handle *handle)
+static struct pipe_state *pipe_state_find(struct ipc_private *ipriv, struct ntvfs_handle *handle)
{
struct pipe_state *s;
void *p;
- p = ntvfs_handle_get_backend_data(handle, private->ntvfs);
+ p = ntvfs_handle_get_backend_data(handle, ipriv->ntvfs);
if (!p) return NULL;
s = talloc_get_type(p, struct pipe_state);
/*
find a open pipe give a wire fnum
*/
-static struct pipe_state *pipe_state_find_key(struct ipc_private *private, struct ntvfs_request *req, const DATA_BLOB *key)
+static struct pipe_state *pipe_state_find_key(struct ipc_private *ipriv, struct ntvfs_request *req, const DATA_BLOB *key)
{
struct ntvfs_handle *h;
- h = ntvfs_handle_search_by_wire_key(private->ntvfs, req, key);
+ h = ntvfs_handle_search_by_wire_key(ipriv->ntvfs, req, key);
if (!h) return NULL;
- return pipe_state_find(private, h);
+ return pipe_state_find(ipriv, h);
}
static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, const char *sharename)
{
- NTSTATUS status;
- struct ipc_private *private;
+ struct ipc_private *ipriv;
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
/* prepare the private state for this connection */
- private = talloc(ntvfs, struct ipc_private);
- NT_STATUS_HAVE_NO_MEMORY(private);
+ ipriv = talloc(ntvfs, struct ipc_private);
+ NT_STATUS_HAVE_NO_MEMORY(ipriv);
- ntvfs->private_data = private;
+ ntvfs->private_data = ipriv;
- private->ntvfs = ntvfs;
- private->pipe_list = NULL;
-
- /* setup the DCERPC server subsystem */
- status = dcesrv_init_ipc_context(private, &private->dcesrv);
- NT_STATUS_NOT_OK_RETURN(status);
+ ipriv->ntvfs = ntvfs;
+ ipriv->pipe_list = NULL;
return NT_STATUS_OK;
}
*/
static int ipc_fd_destructor(struct pipe_state *p)
{
- DLIST_REMOVE(p->private->pipe_list, p);
+ DLIST_REMOVE(p->ipriv->pipe_list, p);
+ ntvfs_handle_remove_backend_data(p->handle, p->ipriv->ntvfs);
return 0;
}
-static struct socket_address *ipc_get_my_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
-{
- struct ipc_private *private = dce_conn->transport.private_data;
-
- return ntvfs_get_my_addr(private->ntvfs, mem_ctx);
-}
-
-static struct socket_address *ipc_get_peer_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
-{
- struct ipc_private *private = dce_conn->transport.private_data;
+struct ipc_open_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_open *oi;
+ struct netr_SamInfo3 *info3;
+};
- return ntvfs_get_peer_addr(private->ntvfs, mem_ctx);
-}
+static void ipc_open_done(struct tevent_req *subreq);
/*
- open a file backend - used for MSRPC pipes
+ open a file - used for MSRPC pipes
*/
-static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, const char *fname,
- struct pipe_state **ps)
+static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
{
- struct pipe_state *p;
NTSTATUS status;
- struct dcerpc_binding *ep_description;
- struct ipc_private *private = ntvfs->private_data;
+ struct pipe_state *p;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct smb_iconv_convenience *smb_ic
+ = lp_iconv_convenience(ipriv->ntvfs->ctx->lp_ctx);
struct ntvfs_handle *h;
+ struct ipc_open_state *state;
+ struct tevent_req *subreq;
+ const char *fname;
+ const char *directory;
+ struct socket_address *client_sa;
+ struct tsocket_address *client_addr;
+ struct socket_address *server_sa;
+ struct tsocket_address *server_addr;
+ int ret;
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ fname = oi->ntcreatex.in.fname;
+ break;
+ case RAW_OPEN_OPENX:
+ fname = oi->openx.in.fname;
+ break;
+ case RAW_OPEN_SMB2:
+ fname = oi->smb2.in.fname;
+ break;
+ default:
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ directory = talloc_asprintf(req, "%s/np",
+ lp_ncalrpc_dir(ipriv->ntvfs->ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(directory);
+
+ state = talloc(req, struct ipc_open_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
status = ntvfs_handle_new(ntvfs, req, &h);
NT_STATUS_NOT_OK_RETURN(status);
p = talloc(h, struct pipe_state);
NT_STATUS_HAVE_NO_MEMORY(p);
- ep_description = talloc(req, struct dcerpc_binding);
- NT_STATUS_HAVE_NO_MEMORY(ep_description);
-
while (fname[0] == '\\') fname++;
p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
p->handle = h;
- p->ipc_state = 0x5ff;
+ p->ipriv = ipriv;
- /*
- we're all set, now ask the dcerpc server subsystem to open the
- endpoint. At this stage the pipe isn't bound, so we don't
- know what interface the user actually wants, just that they want
- one of the interfaces attached to this pipe endpoint.
- */
- ep_description->transport = NCACN_NP;
- ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
-
- /* The session info is refcount-increased in the
- * dcesrv_endpoint_search_connect() function
- */
- status = dcesrv_endpoint_search_connect(private->dcesrv,
- p,
- ep_description,
- h->session_info,
- ntvfs->ctx->event_ctx,
- ntvfs->ctx->msg_ctx,
- ntvfs->ctx->server_id,
- 0,
- &p->dce_conn);
- NT_STATUS_NOT_OK_RETURN(status);
+ p->write_queue = tevent_queue_create(p, "ipc_write_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->write_queue);
- p->dce_conn->transport.private_data = private;
- p->dce_conn->transport.report_output_data = NULL;
- p->dce_conn->transport.get_my_addr = ipc_get_my_addr;
- p->dce_conn->transport.get_peer_addr = ipc_get_peer_addr;
-
- DLIST_ADD(private->pipe_list, p);
+ p->read_queue = tevent_queue_create(p, "ipc_read_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->read_queue);
- p->private = private;
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->oi = oi;
- talloc_set_destructor(p, ipc_fd_destructor);
-
- status = ntvfs_handle_set_backend_data(h, private->ntvfs, p);
+ status = auth_convert_server_info_saminfo3(state,
+ req->session_info->server_info,
+ &state->info3);
NT_STATUS_NOT_OK_RETURN(status);
- *ps = p;
- return NT_STATUS_OK;
-}
+ client_sa = ntvfs_get_peer_addr(ntvfs, state);
+ if (!client_sa) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
-/*
- open a file with ntcreatex - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
-{
- struct pipe_state *p;
- NTSTATUS status;
+ server_sa = ntvfs_get_my_addr(ntvfs, state);
+ if (!server_sa) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
- if (!NT_STATUS_IS_OK(status)) {
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ client_sa->addr,
+ client_sa->port,
+ &client_addr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
return status;
}
- ZERO_STRUCT(oi->ntcreatex.out);
- oi->ntcreatex.out.file.ntvfs= p->handle;
- oi->ntcreatex.out.ipc_state = p->ipc_state;
- oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
-
- return status;
-}
-
-/*
- open a file with openx - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
-{
- struct pipe_state *p;
- NTSTATUS status;
- const char *fname = oi->openx.in.fname;
-
- status = ipc_open_generic(ntvfs, req, fname, &p);
- if (!NT_STATUS_IS_OK(status)) {
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ server_sa->addr,
+ server_sa->port,
+ &server_addr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
return status;
}
- ZERO_STRUCT(oi->openx.out);
- oi->openx.out.file.ntvfs= p->handle;
- oi->openx.out.ftype = 2;
- oi->openx.out.devstate = p->ipc_state;
-
- return status;
+ subreq = tstream_npa_connect_send(p,
+ ipriv->ntvfs->ctx->event_ctx,
+ smb_ic,
+ directory,
+ fname,
+ client_addr,
+ NULL,
+ server_addr,
+ NULL,
+ state->info3,
+ req->session_info->session_key);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_open_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
}
-/*
- open a file with SMB2 Create - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_smb2(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
+static void ipc_open_done(struct tevent_req *subreq)
{
- struct pipe_state *p;
+ struct ipc_open_state *state = tevent_req_callback_data(subreq,
+ struct ipc_open_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ union smb_open *oi = state->oi;
+ int ret;
+ int sys_errno;
NTSTATUS status;
- status = ipc_open_generic(ntvfs, req, oi->smb2.in.fname, &p);
- NT_STATUS_NOT_OK_RETURN(status);
-
- oi->smb2.out.file.ntvfs = p->handle;
- oi->smb2.out.oplock_flags = oi->smb2.in.oplock_flags;
- oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
- oi->smb2.out.create_time = 0;
- oi->smb2.out.access_time = 0;
- oi->smb2.out.write_time = 0;
- oi->smb2.out.change_time = 0;
- oi->smb2.out.alloc_size = 4096;
- oi->smb2.out.size = 0;
- oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
- oi->smb2.out._pad = 0;
- oi->smb2.out.blob = data_blob(NULL, 0);
+ ret = tstream_npa_connect_recv(subreq, &sys_errno,
+ p, &p->npipe,
+ &p->file_type,
+ &p->device_state,
+ &p->allocation_size);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
- return status;
-}
+ DLIST_ADD(ipriv->pipe_list, p);
+ talloc_set_destructor(p, ipc_fd_destructor);
-/*
- open a file - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
-{
- NTSTATUS status;
+ status = ntvfs_handle_set_backend_data(p->handle, ipriv->ntvfs, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto reply;
+ }
switch (oi->generic.level) {
case RAW_OPEN_NTCREATEX:
- status = ipc_open_ntcreatex(ntvfs, req, oi);
+ ZERO_STRUCT(oi->ntcreatex.out);
+ oi->ntcreatex.out.file.ntvfs = p->handle;
+ oi->ntcreatex.out.oplock_level = 0;
+ oi->ntcreatex.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->ntcreatex.out.create_time = 0;
+ oi->ntcreatex.out.access_time = 0;
+ oi->ntcreatex.out.write_time = 0;
+ oi->ntcreatex.out.change_time = 0;
+ oi->ntcreatex.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->ntcreatex.out.alloc_size = p->allocation_size;
+ oi->ntcreatex.out.size = 0;
+ oi->ntcreatex.out.file_type = p->file_type;
+ oi->ntcreatex.out.ipc_state = p->device_state;
+ oi->ntcreatex.out.is_directory = 0;
break;
case RAW_OPEN_OPENX:
- status = ipc_open_openx(ntvfs, req, oi);
+ ZERO_STRUCT(oi->openx.out);
+ oi->openx.out.file.ntvfs = p->handle;
+ oi->openx.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->openx.out.write_time = 0;
+ oi->openx.out.size = 0;
+ oi->openx.out.access = 0;
+ oi->openx.out.ftype = p->file_type;
+ oi->openx.out.devstate = p->device_state;
+ oi->openx.out.action = 0;
+ oi->openx.out.unique_fid = 0;
+ oi->openx.out.access_mask = 0;
+ oi->openx.out.unknown = 0;
break;
case RAW_OPEN_SMB2:
- status = ipc_open_smb2(ntvfs, req, oi);
+ ZERO_STRUCT(oi->smb2.out);
+ oi->smb2.out.file.ntvfs = p->handle;
+ oi->smb2.out.oplock_level = oi->smb2.in.oplock_level;
+ oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->smb2.out.create_time = 0;
+ oi->smb2.out.access_time = 0;
+ oi->smb2.out.write_time = 0;
+ oi->smb2.out.change_time = 0;
+ oi->smb2.out.alloc_size = p->allocation_size;
+ oi->smb2.out.size = 0;
+ oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
+ oi->smb2.out.reserved2 = 0;
break;
default:
- status = NT_STATUS_NOT_SUPPORTED;
break;
}
- return status;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
/*
return NT_STATUS_ACCESS_DENIED;
}
-static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
+struct ipc_readv_next_vector_state {
+ uint8_t *buf;
+ size_t len;
+ off_t ofs;
+ size_t remaining;
+};
+
+static void ipc_readv_next_vector_init(struct ipc_readv_next_vector_state *s,
+ uint8_t *buf, size_t len)
{
- DATA_BLOB *blob = private_data;
+ ZERO_STRUCTP(s);
- if (out->length < blob->length) {
- blob->length = out->length;
+ s->buf = buf;
+ s->len = MIN(len, UINT16_MAX);
+ //DEBUG(0,("readv_next_vector_init[%u 0x%04X]\n", s->len, s->len));
+}
+
+static int ipc_readv_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
+{
+ struct ipc_readv_next_vector_state *state =
+ (struct ipc_readv_next_vector_state *)private_data;
+ struct iovec *vector;
+ ssize_t pending;
+ size_t wanted;
+
+ if (state->ofs == state->len) {
+ *_vector = NULL;
+ *count = 0;
+// DEBUG(0,("readv_next_vector done ofs[%u 0x%04X]\n",
+// state->ofs, state->ofs));
+ return 0;
}
- memcpy(blob->data, out->data, blob->length);
- *nwritten = blob->length;
- return NT_STATUS_OK;
+
+ pending = tstream_pending_bytes(stream);
+ if (pending == -1) {
+ return -1;
+ }
+
+ if (pending == 0 && state->ofs != 0) {
+ /* return a short read */
+ *_vector = NULL;
+ *count = 0;
+// DEBUG(0,("readv_next_vector short read ofs[%u 0x%04X]\n",
+// state->ofs, state->ofs));
+ return 0;
+ }
+
+ if (pending == 0) {
+ /* we want at least one byte and recheck again */
+ wanted = 1;
+ } else {
+ size_t missing = state->len - state->ofs;
+ if (pending > missing) {
+ /* there's more available */
+ state->remaining = pending - missing;
+ wanted = missing;
+ } else {
+ /* read what we can get and recheck in the next cycle */
+ wanted = pending;
+ }
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = state->buf + state->ofs;
+ vector[0].iov_len = wanted;
+
+ state->ofs += wanted;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
}
+struct ipc_read_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_read *rd;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_read_done(struct tevent_req *subreq);
+
/*
read from a file
*/
static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_read *rd)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- NTSTATUS status = NT_STATUS_OK;
+ struct ipc_read_state *state;
+ struct tevent_req *subreq;
if (rd->generic.level != RAW_READ_GENERIC) {
return ntvfs_map_read(ntvfs, req, rd);
}
- p = pipe_state_find(private, rd->readx.in.file.ntvfs);
+ p = pipe_state_find(ipriv, rd->readx.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- data.length = rd->readx.in.maxcnt;
- data.data = rd->readx.out.data;
- if (data.length > UINT16_MAX) {
- data.length = UINT16_MAX;
+ state = talloc(req, struct ipc_read_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->rd = rd;
+
+ /* rd->readx.out.data is already allocated */
+ ipc_readv_next_vector_init(&state->next_vector,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt);
+
+ subreq = tstream_readv_pdu_queue_send(req,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_read_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_read_done(struct tevent_req *subreq)
+{
+ struct ipc_read_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_read_state);
+ struct ntvfs_request *req = state->req;
+ union smb_read *rd = state->rd;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
}
- if (data.length != 0) {
- status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
- if (NT_STATUS_IS_ERR(status)) {
- return status;
- }
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
}
- rd->readx.out.remaining = 0;
+ rd->readx.out.remaining = state->next_vector.remaining;
rd->readx.out.compaction_mode = 0;
- rd->readx.out.nread = data.length;
+ rd->readx.out.nread = ret;
- return status;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+struct example_ocor_state {
+ const char *string;
+};
+
+static struct tevent_coroutine_result *example_ocor_body(struct tevent_coroutine *coro,
+ struct tevent_context *ev,
+ void *private_data);
+
+static struct tevent_req *example_ocor_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *string)
+{
+ struct tevent_req *req;
+ struct example_ocor_state *state;
+ struct tevent_coroutine *coro;
+
+ req = tevent_req_create(mem_ctx, &state, struct example_ocor_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->string = string;
+
+ coro = tevent_coroutine_create(req, ev, example_ocor_body);
+ if (tevent_req_nomem(coro, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static struct tevent_coroutine_result *example_ocor_body(struct tevent_coroutine *coro,
+ struct tevent_context *ev,
+ void *private_data)
+{
+ struct example_ocor_state *state = talloc_get_type_abort(private_data,
+ struct example_ocor_state);
+ struct tevent_req *subreq;
+ bool ok;
+
+ DEBUG(0,("%s[%p]: 1. %s\n", __location__, coro, state->string));
+
+ subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0,500));
+ if (!tevent_coroutine_yield(coro, subreq)) {
+ return tevent_coroutine_return(coro);
+ }
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_coroutine_error(coro, ENOTSUP);
+ return tevent_coroutine_return(coro);
+ }
+
+ DEBUG(0,("%s[%p]: 2. %s wakeup[%d]\n", __location__, coro, state->string, ok));
+
+ tevent_coroutine_done(coro);
+ return tevent_coroutine_return(coro);
+}
+
+static int example_ocor_recv(struct tevent_req *req, int *perrno)
+{
+ if (tevent_req_is_unix_error(req, perrno)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+struct example_coro_state {
+ const char *string;
+};
+
+static struct tevent_coroutine_result *example_coro_body(struct tevent_coroutine *coro,
+ struct tevent_context *ev,
+ void *private_data);
+
+static struct tevent_req *example_coro_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ const char *string)
+{
+ struct tevent_req *req;
+ struct example_coro_state *state;
+ struct tevent_coroutine *coro;
+
+ req = tevent_req_create(mem_ctx, &state, struct example_coro_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->string = string;
+
+ coro = tevent_coroutine_create(req, ev, example_coro_body);
+ if (tevent_req_nomem(coro, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ return req;
}
+static struct tevent_coroutine_result *example_coro_body(struct tevent_coroutine *coro,
+ struct tevent_context *ev,
+ void *private_data)
+{
+ struct example_coro_state *state = talloc_get_type_abort(private_data,
+ struct example_coro_state);
+ struct tevent_req *subreq;
+ int ret;
+ int sys_errno;
+
+ DEBUG(0,("%s:%s[%p]: 1. %s\n", __location__, __FUNCTION__, coro, state->string));
+
+ subreq = example_ocor_send(state, ev, state->string);
+ if (!tevent_coroutine_yield(coro, subreq)) {
+ return tevent_coroutine_return(coro);
+ }
+ ret = example_ocor_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_coroutine_error(coro, sys_errno);
+ return tevent_coroutine_return(coro);
+ }
+
+ DEBUG(0,("%s:%s[%p]: 2. %s example_ocor[%d]\n", __location__, __FUNCTION__, coro, state->string, ret));
+
+ subreq = example_ocor_send(state, ev, state->string);
+ if (!tevent_coroutine_yield(coro, subreq)) {
+ return tevent_coroutine_return(coro);
+ }
+ ret = example_ocor_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_coroutine_error(coro, sys_errno);
+ return tevent_coroutine_return(coro);
+ }
+
+ DEBUG(0,("%s:%s[%p]: 3. %s example_ocor[%d]\n", __location__, __FUNCTION__, coro, state->string, ret));
+
+ tevent_coroutine_done(coro);
+ return tevent_coroutine_return(coro);
+}
+
+static int example_coro_recv(struct tevent_req *req, int *perrno)
+{
+ if (tevent_req_is_unix_error(req, perrno)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+struct ipc_write_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_write *wr;
+ struct iovec iov;
+};
+
+static void ipc_write_done(struct tevent_req *subreq);
+
+static void ipc_write_coro_done(struct tevent_req *subreq);
+
/*
write to a file
*/
static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_write *wr)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- NTSTATUS status;
+ struct tevent_req *subreq;
+ struct ipc_write_state *state;
if (wr->generic.level != RAW_WRITE_GENERIC) {
return ntvfs_map_write(ntvfs, req, wr);
}
- data.data = discard_const_p(void, wr->writex.in.data);
- data.length = wr->writex.in.count;
-
- p = pipe_state_find(private, wr->writex.in.file.ntvfs);
+ p = pipe_state_find(ipriv, wr->writex.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- status = dcesrv_input(p->dce_conn, &data);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ state = talloc(req, struct ipc_write_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->wr = wr;
+ state->iov.iov_base = discard_const_p(void, wr->writex.in.data);
+ state->iov.iov_len = wr->writex.in.count;
+
+ subreq = example_coro_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ "write_start");
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_write_coro_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_write_coro_done(struct tevent_req *subreq)
+{
+ struct ipc_write_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_write_state);
+ struct pipe_state *p = state->p;
+ struct ipc_private *ipriv = state->ipriv;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = example_coro_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->iov, 1);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
}
+ tevent_req_set_callback(subreq, ipc_write_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+static void ipc_write_done(struct tevent_req *subreq)
+{
+ struct ipc_write_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_write_state);
+ struct ntvfs_request *req = state->req;
+ union smb_write *wr = state->wr;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
- wr->writex.out.nwritten = data.length;
+ wr->writex.out.nwritten = ret;
wr->writex.out.remaining = 0;
- return NT_STATUS_OK;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
/*
static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_close *io)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
if (io->generic.level != RAW_CLOSE_CLOSE) {
return ntvfs_map_close(ntvfs, req, io);
}
- p = pipe_state_find(private, io->close.in.file.ntvfs);
+ p = pipe_state_find(ipriv, io->close.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p, *next;
- for (p=private->pipe_list; p; p=next) {
+ for (p=ipriv->pipe_list; p; p=next) {
next = p->next;
if (p->handle->session_info == req->session_info &&
p->handle->smbpid == req->smbpid) {
static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p, *next;
- for (p=private->pipe_list; p; p=next) {
+ for (p=ipriv->pipe_list; p; p=next) {
next = p->next;
if (p->handle->session_info == req->session_info) {
talloc_free(p);
*/
static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
- void *private)
+ void *private_data)
{
return NT_STATUS_OK;
}
static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fileinfo *info)
{
- struct ipc_private *private = ntvfs->private_data;
- struct pipe_state *p = pipe_state_find(private, info->generic.in.file.ntvfs);
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p = pipe_state_find(ipriv, info->generic.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
- BOOL (*callback)(void *, const union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
- BOOL (*callback)(void *, const union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_ACCESS_DENIED;
}
-static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
-{
- NTSTATUS status = NT_STATUS_OK;
- DATA_BLOB *blob = private_data;
+struct ipc_trans_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ struct smb_trans2 *trans;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
- if (out->length > blob->length) {
- status = STATUS_BUFFER_OVERFLOW;
- }
-
- if (out->length < blob->length) {
- blob->length = out->length;
- }
- memcpy(blob->data, out->data, blob->length);
- *nwritten = blob->length;
- return status;
-}
+static void ipc_trans_writev_done(struct tevent_req *subreq);
+static void ipc_trans_readv_done(struct tevent_req *subreq);
/* SMBtrans - handle a DCERPC command */
static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_trans2 *trans)
{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- struct ipc_private *private = ntvfs->private_data;
- NTSTATUS status;
DATA_BLOB fnum_key;
uint16_t fnum;
+ struct ipc_trans_state *state;
+ struct tevent_req *subreq;
/*
* the fnum is in setup[1], a 16 bit value
SSVAL(&fnum, 0, trans->in.setup[1]);
fnum_key = data_blob_const(&fnum, 2);
- p = pipe_state_find_key(private, req, &fnum_key);
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
- if (!trans->out.data.data) {
- return NT_STATUS_NO_MEMORY;
+ /* TODO: check if this os the correct logic */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
}
- /* pass the data to the dcerpc server. Note that we don't
- expect this to fail, and things like NDR faults are not
- reported at this stage. Those sorts of errors happen in the
- dcesrv_output stage */
- status = dcesrv_input(p->dce_conn, &trans->in.data);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- /*
- now ask the dcerpc system for some output. This doesn't yet handle
- async calls. Again, we only expect NT_STATUS_OK. If the call fails then
- the error is encoded at the dcerpc level
- */
- status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
- if (NT_STATUS_IS_ERR(status)) {
- return status;
- }
+ state = talloc(req, struct ipc_trans_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
trans->out.setup_count = 0;
trans->out.setup = NULL;
trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->trans = trans;
+ state->writev_iov.iov_base = trans->in.data.data;
+ state->writev_iov.iov_len = trans->in.data.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ trans->out.data.data,
+ trans->out.data.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_trans_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
- return status;
+static void ipc_trans_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == 0) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ goto reply;
+ } else if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_trans_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
+static void ipc_trans_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ntvfs_request *req = state->req;
+ struct smb_trans2 *trans = state->trans;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ trans->out.data.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
/* SMBtrans - set named pipe state */
static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_trans2 *trans)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
DATA_BLOB fnum_key;
/* the fnum is in setup[1] */
fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
- p = pipe_state_find_key(private, req, &fnum_key);
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
if (trans->in.params.length != 2) {
return NT_STATUS_INVALID_PARAMETER;
}
- p->ipc_state = SVAL(trans->in.params.data, 0);
+ // TODO...
+ p->device_state = SVAL(trans->in.params.data, 0);
trans->out.setup_count = 0;
trans->out.setup = NULL;
NTSTATUS status;
if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
- return ipc_rap_call(req, trans);
+ return ipc_rap_call(req, ntvfs->ctx->event_ctx, ntvfs->ctx->lp_ctx, trans);
if (trans->in.setup_count != 2) {
return NT_STATUS_INVALID_PARAMETER;
return status;
}
+struct ipc_ioctl_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_ioctl *io;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq);
+static void ipc_ioctl_readv_done(struct tevent_req *subreq);
+
static NTSTATUS ipc_ioctl_smb2(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_ioctl *io)
{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- struct ipc_private *private = ntvfs->private_data;
- NTSTATUS status;
+ struct ipc_ioctl_state *state;
+ struct tevent_req *subreq;
switch (io->smb2.in.function) {
case FSCTL_NAMED_PIPE_READ_WRITE:
return NT_STATUS_FS_DRIVER_REQUIRED;
}
- p = pipe_state_find(private, io->smb2.in.file.ntvfs);
+ p = pipe_state_find(ipriv, io->smb2.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_response_size);
- NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
-
- /* pass the data to the dcerpc server. Note that we don't
- expect this to fail, and things like NDR faults are not
- reported at this stage. Those sorts of errors happen in the
- dcesrv_output stage */
- status = dcesrv_input(p->dce_conn, &io->smb2.in.out);
- NT_STATUS_NOT_OK_RETURN(status);
+ /* TODO: check if this os the correct logic */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
+ }
- /*
- now ask the dcerpc system for some output. This doesn't yet handle
- async calls. Again, we only expect NT_STATUS_OK. If the call fails then
- the error is encoded at the dcerpc level
- */
- status = dcesrv_output(p->dce_conn, &io->smb2.out.out, ipc_trans_dcesrv_output);
- NT_STATUS_IS_ERR_RETURN(status);
+ state = talloc(req, struct ipc_ioctl_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
io->smb2.out._pad = 0;
io->smb2.out.function = io->smb2.in.function;
io->smb2.out.unknown2 = 0;
io->smb2.out.unknown3 = 0;
io->smb2.out.in = io->smb2.in.out;
+ io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_response_size);
+ NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
- return status;
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->io = io;
+ state->writev_iov.iov_base = io->smb2.in.out.data;
+ state->writev_iov.iov_len = io->smb2.in.out.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ io->smb2.out.out.data,
+ io->smb2.out.out.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_ioctl_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_ioctl_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
+static void ipc_ioctl_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ntvfs_request *req = state->req;
+ union smb_ioctl *io = state->io;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ io->smb2.out.out.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+
/*
ioctl interface
*/