summaryrefslogtreecommitdiff
path: root/source4/smbd
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2003-12-04 11:01:58 +0000
committerAndrew Tridgell <tridge@samba.org>2003-12-04 11:01:58 +0000
commit42c6a2548a658a198f128cdce36b9fcf869c33c8 (patch)
tree45addf4f9e93d6d12b4f4d6fa44b8f4a9e16119d /source4/smbd
parent2e5ae6f0650962db86fcdb1290f9a7c8b07b1410 (diff)
downloadsamba-42c6a2548a658a198f128cdce36b9fcf869c33c8.tar.gz
samba-42c6a2548a658a198f128cdce36b9fcf869c33c8.tar.bz2
samba-42c6a2548a658a198f128cdce36b9fcf869c33c8.zip
merged more updates from Jim Myers
(This used to be commit 03bf30659640d684073f92d64da6e911edb65a73)
Diffstat (limited to 'source4/smbd')
-rw-r--r--source4/smbd/nttrans.c273
-rw-r--r--source4/smbd/process_thread.c20
-rw-r--r--source4/smbd/reply.c47
-rw-r--r--source4/smbd/request.c29
-rw-r--r--source4/smbd/search.c229
5 files changed, 558 insertions, 40 deletions
diff --git a/source4/smbd/nttrans.c b/source4/smbd/nttrans.c
new file mode 100644
index 0000000000..13afdeeea2
--- /dev/null
+++ b/source4/smbd/nttrans.c
@@ -0,0 +1,273 @@
+/*
+ Unix SMB/CIFS implementation.
+ NT transaction handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+
+
+#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
+ if ((blob)->length < (size)) { \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+ }} while (0)
+
+
+/* setup a nttrans reply, given the data and params sizes */
+static void nttrans_setup_reply(struct request_context *req,
+ struct smb_nttrans *trans,
+ uint16 param_size, uint16 data_size,
+ uint16 setup_count)
+{
+ trans->out.setup_count = setup_count;
+ if (setup_count != 0) {
+ trans->out.setup = talloc_zero(req->mem_ctx, sizeof(uint16) * setup_count);
+ }
+ trans->out.params = data_blob_talloc(req->mem_ctx, NULL, param_size);
+ trans->out.data = data_blob_talloc(req->mem_ctx, NULL, data_size);
+}
+
+
+/* parse NTTRANS_CREATE request
+ */
+static NTSTATUS nttrans_create(struct request_context *req,
+ struct smb_nttrans *trans)
+{
+ return NT_STATUS_FOOBAR;
+}
+
+/* parse NTTRANS_RENAME request
+ */
+static NTSTATUS nttrans_rename(struct request_context *req,
+ struct smb_nttrans *trans)
+{
+ return NT_STATUS_FOOBAR;
+}
+/* parse NTTRANS_IOCTL request
+ */
+static NTSTATUS nttrans_ioctl(struct request_context *req,
+ struct smb_nttrans *trans)
+{
+ union smb_ioctl nt;
+ uint32 function;
+ uint16 fnum;
+ uint8 filter;
+ BOOL fsctl;
+ DATA_BLOB *blob;
+
+ /* should have at least 4 setup words */
+ if (trans->in.setup_count != 4) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ function = IVAL(trans->in.setup, 0);
+ fnum = SVAL(trans->in.setup, 4);
+ fsctl = CVAL(trans->in.setup, 6);
+ filter = CVAL(trans->in.setup, 7);
+
+ blob = &trans->in.data;
+
+ nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
+ nt.ntioctl.in.fnum = fnum;
+ nt.ntioctl.in.function = function;
+ nt.ntioctl.in.fsctl = fsctl;
+ nt.ntioctl.in.filter = filter;
+
+ nttrans_setup_reply(req, trans, 0, 0, 1);
+ trans->out.setup[0] = 0;
+
+ return req->conn->ntvfs_ops->ioctl(req, &nt);
+}
+
+/*
+ backend for nttrans requests
+*/
+static NTSTATUS nttrans_backend(struct request_context *req,
+ struct smb_nttrans *trans)
+{
+ DEBUG(9,("nttrans_backend: setup_count=%d function=%d\n",
+ trans->in.setup_count, trans->in.function));
+ /* must have at least one setup word */
+ if (trans->in.setup_count < 1) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* the nttrans command is in function */
+ switch (trans->in.function) {
+ case NT_TRANSACT_CREATE:
+ return nttrans_create(req, trans);
+ case NT_TRANSACT_IOCTL:
+ return nttrans_ioctl(req, trans);
+ case NT_TRANSACT_RENAME:
+ return nttrans_rename(req, trans);
+ }
+
+ /* an unknown nttrans command */
+ return NT_STATUS_FOOBAR;
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttrans request
+****************************************************************************/
+void reply_nttrans(struct request_context *req)
+{
+ struct smb_nttrans trans;
+ int i;
+ uint16 param_ofs, data_ofs;
+ uint16 param_count, data_count;
+ uint16 params_left, data_left;
+ uint16 param_total, data_total;
+ char *params, *data;
+ NTSTATUS status;
+
+ /* parse request */
+ if (req->in.wct < 19) {
+ req_reply_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ trans.in.max_setup = CVAL(req->in.vwv, 0);
+ param_total = IVAL(req->in.vwv, 3);
+ data_total = IVAL(req->in.vwv, 7);
+ trans.in.max_param = IVAL(req->in.vwv, 11);
+ trans.in.max_data = IVAL(req->in.vwv, 15);
+ param_count = IVAL(req->in.vwv, 19);
+ param_ofs = IVAL(req->in.vwv, 23);
+ data_count = IVAL(req->in.vwv, 27);
+ data_ofs = IVAL(req->in.vwv, 31);
+ trans.in.setup_count = CVAL(req->in.vwv, 35);
+ trans.in.function = SVAL(req->in.vwv, 36);
+
+ if (req->in.wct != 19 + trans.in.setup_count) {
+ req_reply_dos_error(req, ERRSRV, ERRerror);
+ return;
+ }
+
+ /* parse out the setup words */
+ trans.in.setup = talloc(req->mem_ctx, trans.in.setup_count * sizeof(uint16));
+ if (!trans.in.setup) {
+ req_reply_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ for (i=0;i<trans.in.setup_count;i++) {
+ trans.in.setup[i] = SVAL(req->in.vwv, VWV(19+i));
+ }
+
+ if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, &trans.in.params) ||
+ !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &trans.in.data)) {
+ req_reply_error(req, NT_STATUS_FOOBAR);
+ return;
+ }
+
+ /* is it a partial request? if so, then send a 'send more' message */
+ if (param_total > param_count ||
+ data_total > data_count) {
+ DEBUG(0,("REWRITE: not handling partial nttrans requests!\n"));
+ return;
+ }
+
+ /* its a full request, give it to the backend */
+ status = nttrans_backend(req, &trans);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req_reply_error(req, status);
+ return;
+ }
+
+ params_left = trans.out.params.length;
+ data_left = trans.out.data.length;
+ params = trans.out.params.data;
+ data = trans.out.data.data;
+
+ req->control_flags |= REQ_CONTROL_PROTECTED;
+
+ /* we need to divide up the reply into chunks that fit into
+ the negotiated buffer size */
+ do {
+ uint16 this_data, this_param, max_bytes;
+ uint_t align1 = 1, align2 = (params_left ? 2 : 0);
+
+ req_setup_reply(req, 18 + trans.out.setup_count, 0);
+
+ max_bytes = req_max_data(req) - (align1 + align2);
+
+ this_param = params_left;
+ if (this_param > max_bytes) {
+ this_param = max_bytes;
+ }
+ max_bytes -= this_param;
+
+ this_data = data_left;
+ if (this_data > max_bytes) {
+ this_data = max_bytes;
+ }
+
+ req_grow_data(req, this_param + this_data + (align1 + align2));
+
+ SIVAL(req->out.vwv, 3, trans.out.params.length);
+ SIVAL(req->out.vwv, 7, trans.out.data.length);
+
+ SIVAL(req->out.vwv, 11, this_param);
+ SIVAL(req->out.vwv, 15, align1 + PTR_DIFF(req->out.data, req->out.hdr));
+ SIVAL(req->out.vwv, 19, PTR_DIFF(params, trans.out.params.data));
+
+ SIVAL(req->out.vwv, 23, this_data);
+ SIVAL(req->out.vwv, 27, align1 + align2 +
+ PTR_DIFF(req->out.data + this_param, req->out.hdr));
+ SIVAL(req->out.vwv, 31, PTR_DIFF(data, trans.out.data.data));
+
+ SCVAL(req->out.vwv, 35, trans.out.setup_count);
+ for (i=0;i<trans.out.setup_count;i++) {
+ SSVAL(req->out.vwv, VWV(18+i)+1, trans.out.setup[i]);
+ }
+
+ memset(req->out.data, 0, align1);
+ if (this_param != 0) {
+ memcpy(req->out.data + align1, params, this_param);
+ }
+ memset(req->out.data+this_param+align1, 0, align2);
+ if (this_data != 0) {
+ memcpy(req->out.data+this_param+align1+align2, data, this_data);
+ }
+
+ params_left -= this_param;
+ data_left -= this_data;
+ params += this_param;
+ data += this_data;
+
+ /* if this is the last chunk then the request can be destroyed */
+ if (params_left == 0 && data_left == 0) {
+ req->control_flags &= ~REQ_CONTROL_PROTECTED;
+ }
+
+ req_send_reply(req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+
+/****************************************************************************
+ Reply to an SMBnttranss request
+****************************************************************************/
+void reply_nttranss(struct request_context *req)
+{
+ req_reply_error(req, NT_STATUS_FOOBAR);
+}
diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c
index 346c362c5a..9c312e0d1f 100644
--- a/source4/smbd/process_thread.c
+++ b/source4/smbd/process_thread.c
@@ -21,7 +21,9 @@
#include "includes.h"
#include "pthread.h"
+#ifdef HAVE_BACKTRACE
#include "execinfo.h"
+#endif
static void *connection_thread(void *thread_parm)
{
@@ -267,12 +269,14 @@ static void thread_log_suspicious_usage(const char* from, const char* info)
char **bt_symbols;
DEBUG(1,("log_suspicious_usage: from %s info='%s'\n", from, info));
+#ifdef HAVE_BACKTRACE
num_addresses = backtrace(addresses, 8);
bt_symbols = backtrace_symbols(addresses, num_addresses);
for (i=0; i<num_addresses; i++) {
DEBUG(1,("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]));
}
free(bt_symbols);
+#endif
}
/*****************************************************************
@@ -286,19 +290,29 @@ static void thread_print_suspicious_usage(const char* from, const char* info)
char **bt_symbols;
printf("log_suspicious_usage: from %s info='%s'\n", from, info);
+#ifdef HAVE_BACKTRACE
num_addresses = backtrace(addresses, 8);
bt_symbols = backtrace_symbols(addresses, num_addresses);
for (i=0; i<num_addresses; i++) {
printf("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]);
}
free(bt_symbols);
+#endif
}
-uint32 thread_get_task_id(void)
+static uint32 thread_get_task_id(void)
{
return (uint32)pthread_self();
}
+static void thread_log_task_id(int fd)
+{
+ char *s;
+
+ asprintf(&s, "thread %u: ", (uint32)pthread_self());
+ write(fd, s, strlen(s));
+ free(s);
+}
/****************************************************************************
catch serious errors
****************************************************************************/
@@ -342,13 +356,14 @@ static void thread_fault_handler(int sig)
DEBUG(0,("INTERNAL ERROR: Signal %d in thread %lu (%s)\n",sig,(unsigned long int)pthread_self(),SAMBA_VERSION));
DEBUG(0,("Please read the file BUGS.txt in the distribution\n"));
DEBUG(0,("===============================================================\n"));
-
+#ifdef HAVE_BACKTRACE
num_addresses = backtrace(addresses, 10);
bt_symbols = backtrace_symbols(addresses, num_addresses);
for (i=0; i<num_addresses; i++) {
DEBUG(9,("fault_report: %s\n", bt_symbols[i]));
}
free(bt_symbols);
+#endif
pthread_exit(NULL); /* terminate failing thread only */
}
@@ -384,6 +399,7 @@ static void model_startup(void)
d_ops.log_suspicious_usage = thread_log_suspicious_usage;
d_ops.print_suspicious_usage = thread_print_suspicious_usage;
d_ops.get_task_id = thread_get_task_id;
+ d_ops.log_task_id = thread_log_task_id;
register_debug_handlers("thread", &d_ops);
}
diff --git a/source4/smbd/reply.c b/source4/smbd/reply.c
index 3d237d964d..759ec1ef05 100644
--- a/source4/smbd/reply.c
+++ b/source4/smbd/reply.c
@@ -709,6 +709,7 @@ void reply_readbraw(struct request_context *req)
if (req->out.buffer == NULL) {
goto failed;
}
+ SIVAL(req->out.buffer, 0, 0); /* init NBT header */
/* tell the backend where to put the data */
io.readbraw.out.data = req->out.buffer + NBT_HDR_SIZE;
@@ -723,11 +724,13 @@ void reply_readbraw(struct request_context *req)
req->out.size = io.readbraw.out.nread + NBT_HDR_SIZE;
req_send_reply(req);
+ return;
failed:
/* any failure in readbraw is equivalent to reading zero bytes */
req->out.size = 4;
req->out.buffer = talloc(req->mem_ctx, req->out.size);
+ SIVAL(req->out.buffer, 0, 0); /* init NBT header */
req_send_reply(req);
}
@@ -742,6 +745,8 @@ static void reply_lockread_send(struct request_context *req)
CHECK_ASYNC_STATUS;
/* trim packet */
+ io->lockread.out.nread = MIN(io->lockread.out.nread,
+ req_max_data(req) - 3);
req_grow_data(req, 3 + io->lockread.out.nread);
/* construct reply */
@@ -800,6 +805,8 @@ static void reply_read_send(struct request_context *req)
CHECK_ASYNC_STATUS;
/* trim packet */
+ io->read.out.nread = MIN(io->read.out.nread,
+ req_max_data(req) - 3);
req_grow_data(req, 3 + io->read.out.nread);
/* construct reply */
@@ -833,7 +840,7 @@ void reply_read(struct request_context *req)
req_setup_reply(req, 5, 3 + io->read.in.count);
/* tell the backend where to put the data */
- io->lockread.out.data = req->out.data + 3;
+ io->read.out.data = req->out.data + 3;
req->async.send_fn = reply_read_send;
req->async.private = io;
@@ -856,6 +863,8 @@ static void reply_read_and_X_send(struct request_context *req)
CHECK_ASYNC_STATUS;
/* trim the packet to the right size */
+ io->readx.out.nread = MIN(io->readx.out.nread,
+ req_max_data(req) - 1);
req_grow_data(req, 1 + io->readx.out.nread);
/* construct reply */
@@ -1922,26 +1931,6 @@ void reply_getattrE(struct request_context *req)
REQ_ASYNC_TAIL;
}
-/****************************************************************************
- Reply to a search.
- Can be called from SMBsearch, SMBffirst or SMBfunique.
-****************************************************************************/
-void reply_search(struct request_context *req)
-{
- /* do this one later */
- req_reply_error(req, NT_STATUS_FOOBAR);
-}
-
-
-/****************************************************************************
- Reply to a fclose (stop directory search).
-****************************************************************************/
-void reply_fclose(struct request_context *req)
-{
- /* skip this one for now too */
- req_reply_error(req, NT_STATUS_FOOBAR);
-}
-
/****************************************************************************
reply to an old style session setup command
@@ -2196,22 +2185,6 @@ void reply_transs2(struct request_context *req)
req_reply_error(req, NT_STATUS_FOOBAR);
}
-/****************************************************************************
- Reply to an SMBnttrans request
-****************************************************************************/
-void reply_nttrans(struct request_context *req)
-{
- req_reply_error(req, NT_STATUS_FOOBAR);
-}
-
-/****************************************************************************
- Reply to an SMBnttranss request
-****************************************************************************/
-void reply_nttranss(struct request_context *req)
-{
- req_reply_error(req, NT_STATUS_FOOBAR);
-}
-
/****************************************************************************
Reply to an SMBfindclose request
diff --git a/source4/smbd/request.c b/source4/smbd/request.c
index 3eb849d11f..65bf71051f 100644
--- a/source4/smbd/request.c
+++ b/source4/smbd/request.c
@@ -362,7 +362,34 @@ size_t req_push_str(struct request_context *req, char *dest, const char *str, in
return len;
}
-
+/*
+ append raw bytes into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t req_append_bytes(struct request_context *req,
+ const uint8 *bytes, size_t byte_len)
+{
+ req_grow_allocation(req, byte_len + req->out.data_size);
+ memcpy(req->out.data + req->out.data_size, bytes, byte_len);
+ req_grow_data(req, byte_len + req->out.data_size);
+ return byte_len;
+}
+/*
+ append variable block (type 5 buffer) into the data portion of the request packet
+ return the number of bytes added
+*/
+size_t req_append_var_block(struct request_context *req,
+ const uint8 *bytes, uint16 byte_len)
+{
+ req_grow_allocation(req, byte_len + 3 + req->out.data_size);
+ SCVAL(req->out.data + req->out.data_size, 0, 5);
+ SSVAL(req->out.data + req->out.data_size, 1, byte_len); /* add field length */
+ if (byte_len > 0) {
+ memcpy(req->out.data + req->out.data_size + 3, bytes, byte_len);
+ }
+ req_grow_data(req, byte_len + 3 + req->out.data_size);
+ return byte_len + 3;
+}
/*
pull a UCS2 string from a request packet, returning a talloced unix string
diff --git a/source4/smbd/search.c b/source4/smbd/search.c
new file mode 100644
index 0000000000..9d01a0e98f
--- /dev/null
+++ b/source4/smbd/search.c
@@ -0,0 +1,229 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMBsearch handling
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ 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.
+*/
+/*
+ This file handles the parsing of transact2 requests
+*/
+
+#include "includes.h"
+
+/* check req->async.status and if not OK then send an error reply */
+#define CHECK_ASYNC_STATUS do { \
+ if (!NT_STATUS_IS_OK(req->async.status)) { \
+ req_reply_error(req, req->async.status); \
+ return; \
+ }} while (0)
+
+/*
+ check if the backend wants to handle the request asynchronously.
+ if it wants it handled synchronously then call the send function
+ immediately
+*/
+#define REQ_ASYNC_TAIL do { \
+ if (!(req->control_flags & REQ_CONTROL_ASYNC)) { \
+ req->async.send_fn(req); \
+ }} while (0)
+
+/* useful wrapper for talloc with NO_MEMORY reply */
+#define REQ_TALLOC(ptr, size) do { \
+ ptr = talloc(req->mem_ctx, size); \
+ if (!ptr) { \
+ req_reply_error(req, NT_STATUS_NO_MEMORY); \
+ return; \
+ }} while (0)
+
+#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
+ if ((blob)->length < (size)) { \
+ return NT_STATUS_INFO_LENGTH_MISMATCH; \
+ }} while (0)
+
+/* a structure to encapsulate the state information about
+ * an in-progress search first/next operation */
+struct search_state {
+ struct request_context *req;
+ union smb_search_data *file;
+ uint16 last_entry_offset;
+};
+
+/*
+ fill a single entry in a search find reply
+*/
+static void find_fill_info(struct request_context *req,
+ union smb_search_data *file)
+{
+ char *p = req->out.data + req->out.data_size;
+ uint32 dos_date;
+ char search_name[13];
+
+ DEBUG(9,("find_fill_info: input file data: attr=0x%x size=%u time=0x%x name=%13s\n",
+ file->search.attrib, file->search.size,
+ (uint32)file->search.write_time, file->search.name));
+
+ p += req_append_bytes(req, file->search.search_id.data, 21);
+ p += req_append_bytes(req, (char*)&file->search.attrib, 1);
+ put_dos_date((char*)&dos_date, 0, file->search.write_time);
+ p += req_append_bytes(req, (char*)&dos_date, 4);
+ p += req_append_bytes(req, (char*)&file->search.size, 4);
+ memset(&search_name[0], ' ', 13);
+ memcpy(&search_name[0], file->search.name,
+ MAX(13, strlen(file->search.name)));
+ p += req_append_bytes(req, &search_name[0], 13);
+}
+
+/* callback function for search first/next */
+static BOOL find_callback(void *private, union smb_search_data *file)
+{
+ struct search_state *state = (struct search_state *)private;
+
+ find_fill_info(state->req, file);
+
+ return True;
+}
+
+/****************************************************************************
+ Reply to a search.
+****************************************************************************/
+void reply_search(struct request_context *req)
+{
+ union smb_search_first *sf;
+ union smb_search_next *sn;
+ DATA_BLOB resume_key;
+ uint16 resume_key_length;
+ struct search_state state;
+ char *p;
+
+ REQ_TALLOC(sf, sizeof(*sf));
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ p = req->in.data;
+ p += req_pull_ascii4(req, &sf->search_first.in.pattern,
+ p, STR_TERMINATE);
+ if (!sf->search_first.in.pattern) {
+ req_reply_error(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ return;
+ }
+ /* pull in type 5 byte and length */
+ if (!req_pull_blob(req, p, 3, &resume_key))
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ resume_key_length = SVAL(resume_key.data, 1);
+ p += 3;
+ DEBUG(19,("reply_search: pattern=%s, key_length=%d\n",
+ sf->search_first.in.pattern, resume_key_length));
+
+ /* setup state for callback */
+ state.req = req;
+ state.file = NULL;
+ state.last_entry_offset = 0;
+
+ /* construct reply */
+ req_setup_reply(req, 1, 0);
+ req_append_var_block(req, NULL, 0);
+
+ if (resume_key_length > 0) {
+ /* do a search next operation */
+ REQ_TALLOC(sn, sizeof(*sn));
+ sn->search_next.level = RAW_SEARCH_SEARCH;
+ req->async.private = sn;
+ if (!req_pull_blob(req, req->in.data, resume_key_length,
+ &(sn->search_next.in.search_id)))
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ sn->search_next.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sn->search_next.in.max_count = SVAL(req->in.vwv, VWV(0));
+
+ /* call backend */
+ req->async.status = req->conn->ntvfs_ops->search_next(req,
+ sn, &state, find_callback);
+ SSVAL(req->out.vwv, VWV(0), sn->search_next.out.count);
+ } else {
+ /* do a search first operation */
+ req->async.private = sf;
+ sf->search_first.level = RAW_SEARCH_SEARCH;
+ sf->search_first.in.search_attrib = SVAL(req->in.vwv, VWV(1));
+ sf->search_first.in.max_count = SVAL(req->in.vwv, VWV(0));
+
+ /* call backend */
+ req->async.status = req->conn->ntvfs_ops->search_first(req,
+ sf, &state, find_callback);
+ SSVAL(req->out.vwv, VWV(0), sf->search_first.out.count);
+ }
+
+ req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to a fclose (async reply)
+****************************************************************************/
+static void reply_fclose_send(struct request_context *req)
+{
+ CHECK_ASYNC_STATUS;
+
+ /* construct reply */
+ req_setup_reply(req, 1, 0);
+
+ req_send_reply(req);
+}
+
+
+/****************************************************************************
+ Reply to fclose (stop directory search).
+****************************************************************************/
+void reply_fclose(struct request_context *req)
+{
+ union smb_search_next *sn;
+ DATA_BLOB resume_key;
+ uint16 resume_key_length;
+
+ REQ_TALLOC(sn, sizeof(*sn));
+
+ /* parse request */
+ if (req->in.wct != 2) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ sn->search_next.level = RAW_SEARCH_FCLOSE;
+
+ /* pull in type 5 byte and length */
+ if (!req_pull_blob(req, req->in.data, 3, &resume_key))
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ resume_key_length = SVAL(resume_key.data, 1);
+ if (resume_key_length > 0) {
+ /* do a search close operation */
+ if (!req_pull_blob(req, req->in.data, resume_key_length,
+ &(sn->search_next.in.search_id)))
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ } else
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+
+ req->async.send_fn = reply_fclose_send;
+ req->async.private = sn;
+
+ /* call backend */
+ req->async.status = req->conn->ntvfs_ops->search_next(req, sn,
+ NULL, NULL);
+
+ REQ_ASYNC_TAIL;
+}