default IPC$ NTVFS backend
Copyright (C) Andrew Tridgell 2003
- Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
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 "system/filesys.h"
-#include "dlinklist.h"
-#include "smb_server/smb_server.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/rap/rap.h"
+#include "ntvfs/ipc/proto.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 "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include <gssapi/gssapi.h>
+#include "system/locale.h"
/* this is the private structure used to keep the state of an open
ipc$ connection. It needs to keep information about all open
pipes */
struct ipc_private {
- struct idr_context *idtree_fnum;
+ struct ntvfs_module_context *ntvfs;
/* 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;
- uint16_t fnum;
- struct dcesrv_connection *dce_conn;
- uint16_t ipc_state;
- /* we need to remember the session it was opened on,
- as it is illegal to operate on someone elses fnum */
- struct smbsrv_session *session;
-
- /* we need to remember the client pid that
- opened the file so SMBexit works */
- uint16_t smbpid;
+ struct ntvfs_handle *handle;
+ 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 descriptor
+ find a open pipe give a file handle
*/
-static struct pipe_state *pipe_state_find(struct ipc_private *private, uint16_t fnum)
+static struct pipe_state *pipe_state_find(struct ipc_private *ipriv, struct ntvfs_handle *handle)
{
- return idr_find(private->idtree_fnum, fnum);
+ struct pipe_state *s;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, ipriv->ntvfs);
+ if (!p) return NULL;
+
+ s = talloc_get_type(p, struct pipe_state);
+ if (!s) return NULL;
+
+ return s;
+}
+
+/*
+ find a open pipe give a wire fnum
+*/
+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(ipriv->ntvfs, req, key);
+ if (!h) return NULL;
+
+ return pipe_state_find(ipriv, h);
}
connect to a share - always works
*/
static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, const char *sharename)
+ struct ntvfs_request *req,
+ union smb_tcon* tcon)
{
- struct smbsrv_tcon *tcon = req->tcon;
- struct ipc_private *private;
+ struct ipc_private *ipriv;
+ const char *sharename;
- tcon->fs_type = talloc_strdup(tcon, "IPC");
- tcon->dev_type = talloc_strdup(tcon, "IPC");
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
- /* prepare the private state for this connection */
- private = talloc_p(tcon, struct ipc_private);
- if (!private) {
- return NT_STATUS_NO_MEMORY;
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *p = strchr(sharename+2, '\\');
+ if (p) {
+ sharename = p + 1;
+ }
}
- ntvfs->private_data = private;
- private->pipe_list = NULL;
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
- private->idtree_fnum = idr_init(private);
- if (private->idtree_fnum == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
}
+ /* prepare the private state for this connection */
+ ipriv = talloc(ntvfs, struct ipc_private);
+ NT_STATUS_HAVE_NO_MEMORY(ipriv);
+
+ ntvfs->private_data = ipriv;
+
+ ipriv->ntvfs = ntvfs;
+ ipriv->pipe_list = NULL;
+
return NT_STATUS_OK;
}
/*
disconnect from a share
*/
-static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs,
- struct smbsrv_tcon *tcon)
+static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs)
{
- struct ipc_private *private = ntvfs->private_data;
-
- /* close any pipes that are open. Discard any unread data */
- while (private->pipe_list) {
- talloc_free(private->pipe_list);
- }
-
return NT_STATUS_OK;
}
delete a file
*/
static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_unlink *unl)
-{
- return NT_STATUS_ACCESS_DENIED;
-}
-
-
-/*
- ioctl interface - we don't do any
-*/
-static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_ioctl *io)
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
{
return NT_STATUS_ACCESS_DENIED;
}
check if a directory exists
*/
static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_chkpath *cp)
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
{
return NT_STATUS_ACCESS_DENIED;
}
return info on a pathname
*/
static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_fileinfo *info)
+ struct ntvfs_request *req, union smb_fileinfo *info)
{
- return NT_STATUS_ACCESS_DENIED;
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ case RAW_FILEINFO_GETATTR:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
}
/*
set info on a pathname
*/
static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_setfileinfo *st)
+ struct ntvfs_request *req, union smb_setfileinfo *st)
{
return NT_STATUS_ACCESS_DENIED;
}
/*
destroy a open pipe structure
*/
-static int ipc_fd_destructor(void *ptr)
+static int ipc_fd_destructor(struct pipe_state *p)
{
- struct pipe_state *p = ptr;
- idr_remove(p->private->idtree_fnum, p->fnum);
- DLIST_REMOVE(p->private->pipe_list, p);
- talloc_free(p->dce_conn);
+ DLIST_REMOVE(p->ipriv->pipe_list, p);
+ ntvfs_handle_remove_backend_data(p->handle, p->ipriv->ntvfs);
return 0;
}
+struct ipc_open_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_open *oi;
+ struct netr_SamInfo3 *info3;
+};
+
+static void ipc_open_done(struct tevent_req *subreq);
/*
- open a file backend - used for MSRPC pipes
+ check the pipename is valid
+ */
+static NTSTATUS validate_pipename(const char *name)
+{
+ while (*name) {
+ if (!isalnum(*name)) return NT_STATUS_INVALID_PARAMETER;
+ name++;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ open a file - used for MSRPC pipes
*/
-static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
- struct smbsrv_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 auth_session_info *session_info = NULL;
- struct ipc_private *private = ntvfs->private_data;
- int fnum;
+ 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;
+ DATA_BLOB delegated_creds = data_blob_null;
- p = talloc_p(req, struct pipe_state);
- if (!p) {
- return NT_STATUS_NO_MEMORY;
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ case RAW_OPEN_NTTRANS_CREATE:
+ 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:
+ return NT_STATUS_NOT_SUPPORTED;
}
+ 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);
+
while (fname[0] == '\\') fname++;
- p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
- if (!p->pipe_name) {
- return NT_STATUS_NO_MEMORY;
- }
+ /* check for valid characters in name */
+ fname = strlower_talloc(p, fname);
- fnum = idr_get_new(private->idtree_fnum, p, UINT16_MAX);
- if (fnum == -1) {
- return NT_STATUS_TOO_MANY_OPENED_FILES;
- }
+ status = validate_pipename(fname);
+ NT_STATUS_NOT_OK_RETURN(status);
- p->fnum = fnum;
- p->ipc_state = 0x5ff;
+ p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
+ NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
- /*
- 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.
-
- TODO: note that we aren't passing any credentials here. We
- will need to do that once the credentials infrastructure is
- finalised for Samba4
- */
- ep_description.transport = NCACN_NP;
- ep_description.endpoint = p->pipe_name;
-
- /* tell the RPC layer the session_info */
- if (req->session) {
- /* The session info is refcount-increased in the
- dcesrv_endpoint_search_connect() function */
- session_info = req->session->session_info;
- }
-
- status = dcesrv_endpoint_search_connect(req->smb_conn->dcesrv,
- &ep_description,
- session_info,
- &p->dce_conn);
- if (!NT_STATUS_IS_OK(status)) {
- idr_remove(private->idtree_fnum, p->fnum);
- return status;
- }
+ p->handle = h;
+ p->ipriv = ipriv;
- DLIST_ADD(private->pipe_list, p);
+ p->write_queue = tevent_queue_create(p, "ipc_write_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->write_queue);
- p->smbpid = req->smbpid;
- p->session = req->session;
- p->private = private;
+ p->read_queue = tevent_queue_create(p, "ipc_read_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->read_queue);
- *ps = p;
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->oi = oi;
- talloc_steal(private, p);
+ status = auth_convert_server_info_saminfo3(state,
+ req->session_info->server_info,
+ &state->info3);
+ NT_STATUS_NOT_OK_RETURN(status);
- talloc_set_destructor(p, ipc_fd_destructor);
+ client_sa = ntvfs_get_peer_addr(ntvfs, state);
+ if (!client_sa) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- return NT_STATUS_OK;
-}
+ server_sa = ntvfs_get_my_addr(ntvfs, state);
+ if (!server_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 smbsrv_request *req, union smb_open *oi)
-{
- struct pipe_state *p;
- NTSTATUS 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;
+ }
- 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",
+ server_sa->addr,
+ server_sa->port,
+ &server_addr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
return status;
}
- ZERO_STRUCT(oi->ntcreatex.out);
- oi->ntcreatex.out.fnum = p->fnum;
- oi->ntcreatex.out.ipc_state = p->ipc_state;
+ if (req->session_info->credentials) {
+ struct gssapi_creds_container *gcc;
+ OM_uint32 gret;
+ OM_uint32 minor_status;
+ gss_buffer_desc cred_token;
+
+ ret = cli_credentials_get_client_gss_creds(req->session_info->credentials,
+ ipriv->ntvfs->ctx->event_ctx,
+ ipriv->ntvfs->ctx->lp_ctx,
+ &gcc);
+ if (ret) {
+ goto skip;
+ }
- return status;
+ gret = gss_export_cred(&minor_status,
+ gcc->creds,
+ &cred_token);
+ if (gret != GSS_S_COMPLETE) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (cred_token.length) {
+ delegated_creds = data_blob_talloc(req,
+ cred_token.value,
+ cred_token.length);
+ gss_release_buffer(&minor_status, &cred_token);
+ NT_STATUS_HAVE_NO_MEMORY(delegated_creds.data);
+ }
+ }
+
+skip:
+
+ 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,
+ delegated_creds);
+ 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 openx - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
- struct smbsrv_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;
- const char *fname = oi->openx.in.fname;
- status = ipc_open_generic(ntvfs, req, fname, &p);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ 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;
}
- ZERO_STRUCT(oi->openx.out);
- oi->openx.out.fnum = p->fnum;
- oi->openx.out.ftype = 2;
- oi->openx.out.devstate = p->ipc_state;
-
- 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 smbsrv_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:
+ 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);
}
/*
create a directory
*/
static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_mkdir *md)
+ struct ntvfs_request *req, union smb_mkdir *md)
{
return NT_STATUS_ACCESS_DENIED;
}
remove a directory
*/
static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_rmdir *rd)
+ struct ntvfs_request *req, struct smb_rmdir *rd)
{
return NT_STATUS_ACCESS_DENIED;
}
rename a set of files
*/
static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_rename *ren)
+ struct ntvfs_request *req, union smb_rename *ren)
{
return NT_STATUS_ACCESS_DENIED;
}
copy a set of files
*/
static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_copy *cp)
+ struct ntvfs_request *req, struct smb_copy *cp)
{
return NT_STATUS_ACCESS_DENIED;
}
+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)
+{
+ ZERO_STRUCTP(s);
+
+ 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;
+ }
+
+ 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 smbsrv_request *req, union smb_read *rd)
+ struct ntvfs_request *req, union smb_read *rd)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
- uint16_t fnum;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- NTSTATUS status;
+ struct ipc_read_state *state;
+ struct tevent_req *subreq;
if (rd->generic.level != RAW_READ_GENERIC) {
- return ntvfs_map_read(req, rd, ntvfs);
+ return ntvfs_map_read(ntvfs, req, rd);
}
- fnum = rd->readx.in.fnum;
-
- p = pipe_state_find(private, fnum);
+ 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 = 0;
+ 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_blob(p->dce_conn, &data);
- 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 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);
+
/*
write to a file
*/
static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_write *wr)
+ struct ntvfs_request *req, union smb_write *wr)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
- uint16_t fnum;
+ 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(req, wr, ntvfs);
+ return ntvfs_map_write(ntvfs, req, wr);
}
- fnum = wr->writex.in.fnum;
- data.data = discard_const_p(void, wr->writex.in.data);
- data.length = wr->writex.in.count;
-
- p = pipe_state_find(private, fnum);
+ 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 = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_write_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+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;
}
- wr->writex.out.nwritten = data.length;
+ status = NT_STATUS_OK;
+
+ 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);
}
/*
seek in a file
*/
static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_seek *io)
+ struct ntvfs_request *req,
+ union smb_seek *io)
{
return NT_STATUS_ACCESS_DENIED;
}
flush a file
*/
static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_flush *io)
+ struct ntvfs_request *req,
+ union smb_flush *io)
{
return NT_STATUS_ACCESS_DENIED;
}
close a file
*/
static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_close *io)
+ 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(req, io, ntvfs);
+ return ntvfs_map_close(ntvfs, req, io);
}
- p = pipe_state_find(private, io->close.in.fnum);
+ p = pipe_state_find(ipriv, io->close.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
exit - closing files
*/
static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req)
+ 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->smbpid == req->smbpid) {
+ if (p->handle->session_info == req->session_info &&
+ p->handle->smbpid == req->smbpid) {
talloc_free(p);
}
}
logoff - closing files open by the user
*/
static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req)
+ 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->session == req->session) {
+ if (p->handle->session_info == req->session_info) {
talloc_free(p);
}
}
setup for an async call
*/
static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req,
- void *private)
+ struct ntvfs_request *req,
+ void *private_data)
{
return NT_STATUS_OK;
}
cancel an async call
*/
static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req)
+ struct ntvfs_request *req)
{
return NT_STATUS_UNSUCCESSFUL;
}
lock a byte range
*/
static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_lock *lck)
+ struct ntvfs_request *req, union smb_lock *lck)
{
return NT_STATUS_ACCESS_DENIED;
}
set info on a open file
*/
static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_setfileinfo *info)
+ struct ntvfs_request *req, union smb_setfileinfo *info)
{
return NT_STATUS_ACCESS_DENIED;
}
query info on a open file
*/
static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_fileinfo *info)
+ struct ntvfs_request *req, union smb_fileinfo *info)
{
+ 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;
+ }
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ {
+ ZERO_STRUCT(info->generic.out);
+ info->generic.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ info->generic.out.fname.s = strrchr(p->pipe_name, '\\');
+ info->generic.out.alloc_size = 4096;
+ info->generic.out.nlink = 1;
+ /* What the heck? Match Win2k3: IPC$ pipes are delete pending */
+ info->generic.out.delete_pending = 1;
+ return NT_STATUS_OK;
+ }
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return NT_STATUS_INVALID_PARAMETER;
+ case RAW_FILEINFO_ALL_EAS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
return NT_STATUS_ACCESS_DENIED;
}
return filesystem info
*/
static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_fsinfo *fs)
+ struct ntvfs_request *req, union smb_fsinfo *fs)
{
return NT_STATUS_ACCESS_DENIED;
}
return print queue info
*/
static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_lpq *lpq)
+ struct ntvfs_request *req, union smb_lpq *lpq)
{
return NT_STATUS_ACCESS_DENIED;
}
list files in a directory matching a wildcard pattern
*/
static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_search_first *io,
+ struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
continue listing files in a directory
*/
static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_search_next *io,
+ struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
end listing files in a directory
*/
static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, union smb_search_close *io)
+ struct ntvfs_request *req, union smb_search_close *io)
{
return NT_STATUS_ACCESS_DENIED;
}
+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;
+};
+
+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 smbsrv_request *req, struct smb_trans2 *trans)
+ 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] */
- p = pipe_state_find(private, trans->in.setup[1]);
+ /*
+ * the fnum is in setup[1], a 16 bit value
+ * the setup[*] values are already in host byteorder
+ * but ntvfs_handle_search_by_wire_key() expects
+ * network byteorder
+ */
+ SSVAL(&fnum, 0, trans->in.setup[1]);
+ fnum_key = data_blob_const(&fnum, 2);
+
+ 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;
- }
-
- /* 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_blob(p->dce_conn, &trans->out.data);
- if (NT_STATUS_IS_ERR(status)) {
- return status;
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
}
+ 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 smbsrv_request *req, struct smb_trans2 *trans)
+ 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] */
- p = pipe_state_find(private, trans->in.setup[1]);
+ fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
+
+ 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: pass this to the tstream_npa logic
+ */
+ p->device_state = SVAL(trans->in.params.data, 0);
trans->out.setup_count = 0;
trans->out.setup = NULL;
/* SMBtrans - used to provide access to SMB pipes */
static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
- struct smbsrv_request *req, struct smb_trans2 *trans)
+ struct ntvfs_request *req, struct smb_trans2 *trans)
{
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_ioctl_state *state;
+ struct tevent_req *subreq;
+
+ switch (io->smb2.in.function) {
+ case FSCTL_NAMED_PIPE_READ_WRITE:
+ break;
+
+ default:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+ }
+
+ p = pipe_state_find(ipriv, io->smb2.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /*
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ 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);
+
+ 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
+*/
+static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_SMB2:
+ return ipc_ioctl_smb2(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_ACCESS_DENIED;
+}
/*
{
NTSTATUS ret;
struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
ZERO_STRUCT(ops);
ops.chkpath = ipc_chkpath;
ops.qpathinfo = ipc_qpathinfo;
ops.setpathinfo = ipc_setpathinfo;
- ops.openfile = ipc_open;
+ ops.open = ipc_open;
ops.mkdir = ipc_mkdir;
ops.rmdir = ipc_rmdir;
ops.rename = ipc_rename;
ops.cancel = ipc_cancel;
/* register ourselves with the NTVFS subsystem. */
- ret = register_backend("ntvfs", &ops);
+ ret = ntvfs_register(&ops, &vers);
if (!NT_STATUS_IS_OK(ret)) {
DEBUG(0,("Failed to register IPC backend!\n"));