From 42c6a2548a658a198f128cdce36b9fcf869c33c8 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 4 Dec 2003 11:01:58 +0000 Subject: [PATCH] merged more updates from Jim Myers (This used to be commit 03bf30659640d684073f92d64da6e911edb65a73) --- source4/Makefile.in | 2 +- source4/ntvfs/ntvfs_generic.c | 115 ++++++++++++-- source4/smbd/nttrans.c | 273 ++++++++++++++++++++++++++++++++++ source4/smbd/process_thread.c | 20 ++- source4/smbd/reply.c | 47 ++---- source4/smbd/request.c | 29 +++- source4/smbd/search.c | 229 ++++++++++++++++++++++++++++ 7 files changed, 662 insertions(+), 53 deletions(-) create mode 100644 source4/smbd/nttrans.c create mode 100644 source4/smbd/search.c diff --git a/source4/Makefile.in b/source4/Makefile.in index f156262b97f..9e43bca695c 100644 --- a/source4/Makefile.in +++ b/source4/Makefile.in @@ -292,7 +292,7 @@ SMBD_OBJ_SRV = smbd/connection.o \ smbd/password.o smbd/conn.o \ smbd/negprot.o smbd/request.o \ smbd/reply.o smbd/sesssetup.o \ - smbd/trans2.o \ + smbd/trans2.o smbd/search.o smbd/nttrans.o \ lib/sysacls.o lib/server_mutex.o \ smbd/build_options.o smbd/service.o \ smbd/rewrite.o \ diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c index 08c8b88d30e..ea62f8c9a65 100644 --- a/source4/ntvfs/ntvfs_generic.c +++ b/source4/ntvfs/ntvfs_generic.c @@ -178,7 +178,8 @@ NTSTATUS ntvfs_map_open(struct request_context *req, union smb_open *io) break; case OPEN_FLAGS_OPEN_RDWR: case 0xf: /* FCB mode */ - io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ | + GENERIC_RIGHTS_FILE_WRITE; io->open.out.rmode = DOS_OPEN_RDWR; /* assume we got r/w */ break; default: @@ -369,12 +370,13 @@ NTSTATUS ntvfs_map_fsinfo(struct request_context *req, union smb_fsinfo *fs) */ NTSTATUS ntvfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, union smb_fileinfo *info2) { + int i; /* and convert it to the required level using results in info2 */ switch (info->generic.level) { case RAW_FILEINFO_GENERIC: return NT_STATUS_INVALID_LEVEL; case RAW_FILEINFO_GETATTR: - info->getattr.out.attrib = info2->generic.out.attrib & 0x3f; + info->getattr.out.attrib = info2->generic.out.attrib & 0xff; info->getattr.out.size = info2->generic.out.size; info->getattr.out.write_time = nt_time_to_unix(&info2->generic.out.write_time); return NT_STATUS_OK; @@ -468,29 +470,118 @@ NTSTATUS ntvfs_map_fileinfo(struct request_context *req, union smb_fileinfo *inf case RAW_FILEINFO_STREAM_INFO: case RAW_FILEINFO_STREAM_INFORMATION: - /* setup a single data stream */ info->stream_info.out.num_streams = info2->generic.out.num_streams; - info->stream_info.out.streams = talloc(req->mem_ctx, sizeof(info2->stream_info.out.streams[0])); - if (!info->stream_info.out.streams) { - return NT_STATUS_NO_MEMORY; + if (info->stream_info.out.num_streams > 0) { + info->stream_info.out.streams = talloc(req->mem_ctx, + info->stream_info.out.num_streams * sizeof(struct stream_struct)); + if (!info->stream_info.out.streams) { + DEBUG(2,("ntvfs_map_fileinfo: no memory for %d streams\n", + info->stream_info.out.num_streams)); + return NT_STATUS_NO_MEMORY; + } + for (i=0; i < info->stream_info.out.num_streams; i++) { + info->stream_info.out.streams[i] = info2->generic.out.streams[i]; + info->stream_info.out.streams[i].stream_name.s = + talloc_strdup(req->mem_ctx, info2->generic.out.streams[i].stream_name.s); + if (!info->stream_info.out.streams[i].stream_name.s) { + DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n")); + return NT_STATUS_NO_MEMORY; + } + } } - info->stream_info.out.streams[0].size = info2->generic.out.streams[0].size; - info->stream_info.out.streams[0].alloc_size = info2->generic.out.streams[0].alloc_size; - info->stream_info.out.streams[0].stream_name.s = info2->generic.out.streams[0].stream_name.s; - info->stream_info.out.streams[0].stream_name.private_length = info->generic.out.streams[0].stream_name.private_length; return NT_STATUS_OK; case RAW_FILEINFO_NAME_INFO: case RAW_FILEINFO_NAME_INFORMATION: - info->name_info.out.fname.s = info2->generic.out.fname.s; + info->name_info.out.fname.s = talloc_strdup(req->mem_ctx, info2->generic.out.fname.s); info->name_info.out.fname.private_length = info2->generic.out.fname.private_length; return NT_STATUS_OK; case RAW_FILEINFO_ALT_NAME_INFO: case RAW_FILEINFO_ALT_NAME_INFORMATION: - info->alt_name_info.out.fname.s = info2->generic.out.alt_fname.s; + info->alt_name_info.out.fname.s = talloc_strdup(req->mem_ctx, info2->generic.out.alt_fname.s); info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length; return NT_STATUS_OK; + + case RAW_FILEINFO_POSITION_INFORMATION: + info->position_information.out.position = info2->generic.out.position; + return NT_STATUS_OK; + + case RAW_FILEINFO_ALL_EAS: + info->all_eas.out.num_eas = info2->generic.out.num_eas; + if (info->all_eas.out.num_eas > 0) { + info->all_eas.out.eas = talloc(req->mem_ctx, + info->all_eas.out.num_eas * sizeof(struct ea_struct)); + if (!info->all_eas.out.eas) { + DEBUG(2,("ntvfs_map_fileinfo: no memory for %d eas\n", + info->all_eas.out.num_eas)); + return NT_STATUS_NO_MEMORY; + } + for (i = 0; i < info->all_eas.out.num_eas; i++) { + info->all_eas.out.eas[i] = info2->generic.out.eas[i]; + info->all_eas.out.eas[i].name.s = + talloc_strdup(req->mem_ctx, info2->generic.out.eas[i].name.s); + if (!info->all_eas.out.eas[i].name.s) { + DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n")); + return NT_STATUS_NO_MEMORY; + } + info->all_eas.out.eas[i].value.data = + talloc_memdup(req->mem_ctx, + info2->generic.out.eas[i].value.data, + info2->generic.out.eas[i].value.length); + if (!info->all_eas.out.eas[i].value.data) { + DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n")); + return NT_STATUS_NO_MEMORY; + } + } + } + return NT_STATUS_OK; + + case RAW_FILEINFO_IS_NAME_VALID: + return NT_STATUS_OK; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + info->compression_info.out.compressed_size = info2->generic.out.compressed_size; + info->compression_info.out.format = info2->generic.out.format; + info->compression_info.out.unit_shift = info2->generic.out.unit_shift; + info->compression_info.out.chunk_shift = info2->generic.out.chunk_shift; + info->compression_info.out.cluster_shift = info2->generic.out.cluster_shift; + return NT_STATUS_OK; + + case RAW_FILEINFO_ACCESS_INFORMATION: + info->access_information.out.access_flags = info2->generic.out.access_flags; + return NT_STATUS_OK; + + case RAW_FILEINFO_MODE_INFORMATION: + info->mode_information.out.mode = info2->generic.out.mode; + return NT_STATUS_OK; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + info->alignment_information.out.alignment_requirement = + info2->generic.out.alignment_requirement; + return NT_STATUS_OK; +#if 0 + case RAW_FILEINFO_UNIX_BASIC: + info->unix_basic_info.out.end_of_file = info2->generic.out.end_of_file; + info->unix_basic_info.out.num_bytes = info2->generic.out.size; + info->unix_basic_info.out.status_change_time = info2->generic.out.change_time; + info->unix_basic_info.out.access_time = info2->generic.out.access_time; + info->unix_basic_info.out.change_time = info2->generic.out.change_time; + info->unix_basic_info.out.uid = info2->generic.out.uid; + info->unix_basic_info.out.gid = info2->generic.out.gid; + info->unix_basic_info.out.file_type = info2->generic.out.file_type; + info->unix_basic_info.out.dev_major = info2->generic.out.device; + info->unix_basic_info.out.dev_minor = info2->generic.out.device; + info->unix_basic_info.out.unique_id = info2->generic.out.inode; + info->unix_basic_info.out.permissions = info2->generic.out.permissions; + info->unix_basic_info.out.nlink = info2->generic.out.nlink; + return NT_STATUS_OK; + + case RAW_FILEINFO_UNIX_LINK: + info->unix_link_info.out.link_dest = info2->generic.out.link_dest; + return NT_STATUS_OK; +#endif } return NT_STATUS_INVALID_LEVEL; diff --git a/source4/smbd/nttrans.c b/source4/smbd/nttrans.c new file mode 100644 index 00000000000..13afdeeea28 --- /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 + + 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;iin.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;iout.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 346c362c5ad..9c312e0d1fa 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; iout.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 3eb849d11f8..65bf71051f4 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 00000000000..9d01a0e98f3 --- /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 + + 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; +}