*/
#include "includes.h"
+#include "dlinklist.h"
+#include "smb_server/smb_server.h"
+
#define CHECK_MIN_BLOB_SIZE(blob, size) do { \
/* grow the data allocation size of a trans2 reply - this guarantees
that requests to grow the data size later will not change the
pointer */
-static void trans2_grow_data_allocation(struct smbsrv_request *req,
+static BOOL trans2_grow_data_allocation(struct smbsrv_request *req,
struct smb_trans2 *trans,
- uint16_t new_size)
+ uint32_t new_size)
{
if (new_size <= trans->out.data.length) {
- return;
+ return True;
}
- trans->out.data.data = talloc_realloc(trans->out.data.data, new_size);
+ trans->out.data.data = talloc_realloc(req, trans->out.data.data, new_size);
+ return (trans->out.data.data != NULL);
}
/* grow the data size of a trans2 reply */
-static void trans2_grow_data(struct smbsrv_request *req,
+static BOOL trans2_grow_data(struct smbsrv_request *req,
struct smb_trans2 *trans,
- uint16_t new_size)
+ uint32_t new_size)
{
- trans2_grow_data_allocation(req, trans, new_size);
+ if (!trans2_grow_data_allocation(req, trans, new_size)) {
+ return False;
+ }
trans->out.data.length = new_size;
+ return True;
}
/* grow the data, zero filling any new bytes */
-static void trans2_grow_data_fill(struct smbsrv_request *req,
+static BOOL trans2_grow_data_fill(struct smbsrv_request *req,
struct smb_trans2 *trans,
- uint16_t new_size)
+ uint32_t new_size)
{
- uint16_t old_size = trans->out.data.length;
- trans2_grow_data(req, trans, new_size);
+ uint32_t old_size = trans->out.data.length;
+ if (!trans2_grow_data(req, trans, new_size)) {
+ return False;
+ }
if (new_size > old_size) {
memset(trans->out.data.data + old_size, 0, new_size - old_size);
}
+ return True;
}
{
trans->out.setup_count = setup_count;
if (setup_count != 0) {
- trans->out.setup = talloc_zero(req, sizeof(uint16_t) * setup_count);
+ trans->out.setup = talloc_zero_array_p(req, uint16_t, setup_count);
}
trans->out.params = data_blob_talloc(req, NULL, param_size);
trans->out.data = data_blob_talloc(req, NULL, data_size);
*/
static size_t trans2_push_data_string(struct smbsrv_request *req,
struct smb_trans2 *trans,
- uint16_t len_offset,
- uint16_t offset,
+ uint32_t len_offset,
+ uint32_t offset,
const WIRE_STRING *str,
int dest_len,
int flags)
int flags)
{
size_t ret;
- uint16_t offset;
+ uint32_t offset;
const int max_bytes_per_char = 3;
offset = trans->out.data.length;
trans2_grow_data(req, trans, offset + ret);
}
+/*
+ align the end of the data section of a trans reply on an even boundary
+*/
+static void trans2_align_data(struct smbsrv_request *req, struct smb_trans2 *trans)
+{
+ if ((trans->out.data.length & 1) == 0) {
+ return;
+ }
+ trans2_grow_data(req, trans, trans->out.data.length+1);
+ SCVAL(trans->out.data.data, trans->out.data.length-1, 0);
+}
+
/*
trans2 qfsinfo implementation
case SMB_QFS_ALLOCATION:
fsinfo.allocation.level = RAW_QFS_ALLOCATION;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_VOLUME:
fsinfo.volume.level = RAW_QFS_VOLUME;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_VOLUME_INFORMATION:
fsinfo.volume_info.level = RAW_QFS_VOLUME_INFO;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_SIZE_INFORMATION:
fsinfo.size_info.level = RAW_QFS_SIZE_INFO;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_DEVICE_INFORMATION:
fsinfo.device_info.level = RAW_QFS_DEVICE_INFO;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_ATTRIBUTE_INFORMATION:
fsinfo.attribute_info.level = RAW_QFS_ATTRIBUTE_INFO;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_QUOTA_INFORMATION:
fsinfo.quota_information.level = RAW_QFS_QUOTA_INFORMATION;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_FULL_SIZE_INFORMATION:
fsinfo.full_size_information.level = RAW_QFS_FULL_SIZE_INFORMATION;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case SMB_QFS_OBJECTID_INFORMATION:
fsinfo.objectid_information.level = RAW_QFS_OBJECTID_INFORMATION;
- status = req->tcon->ntvfs_ops->fsinfo(req, &fsinfo);
+ status = ntvfs_fsinfo(req, &fsinfo);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return status;
}
- memcpy(trans->out.data.data, guid_blob.data, GUID_SIZE);
+ memcpy(trans->out.data.data, guid_blob.data, guid_blob.length);
for (i=0;i<6;i++) {
SBVAL(trans->out.data.data, 16 + 8*i, fsinfo.objectid_information.out.unknown[i]);
return NT_STATUS_INVALID_LEVEL;
}
+
+/*
+ trans2 open implementation
+*/
+static NTSTATUS trans2_open(struct smbsrv_request *req, struct smb_trans2 *trans)
+{
+ union smb_open *io;
+ NTSTATUS status;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 29) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc_p(req, union smb_open);
+ if (io == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io->t2open.level = RAW_OPEN_T2OPEN;
+ io->t2open.in.flags = SVAL(trans->in.params.data, VWV(0));
+ io->t2open.in.open_mode = SVAL(trans->in.params.data, VWV(1));
+ io->t2open.in.file_attrs = SVAL(trans->in.params.data, VWV(3));
+ io->t2open.in.write_time = srv_pull_dos_date(req->smb_conn,
+ trans->in.params.data + VWV(4));;
+ io->t2open.in.open_func = SVAL(trans->in.params.data, VWV(6));
+ io->t2open.in.size = IVAL(trans->in.params.data, VWV(7));
+ io->t2open.in.timeout = IVAL(trans->in.params.data, VWV(9));
+ io->t2open.in.num_eas = 0;
+ io->t2open.in.eas = NULL;
+
+ trans2_pull_blob_string(req, &trans->in.params, 28, &io->t2open.in.fname, 0);
+
+ status = ea_pull_list(&trans->in.data, io, &io->t2open.in.num_eas, &io->t2open.in.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntvfs_openfile(req, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ trans2_setup_reply(req, trans, 30, 0, 0);
+
+ SSVAL(trans->out.params.data, VWV(0), io->t2open.out.fnum);
+ SSVAL(trans->out.params.data, VWV(1), io->t2open.out.attrib);
+ srv_push_dos_date3(req->smb_conn, trans->out.params.data,
+ VWV(2), io->t2open.out.write_time);
+ SIVAL(trans->out.params.data, VWV(4), io->t2open.out.size);
+ SSVAL(trans->out.params.data, VWV(6), io->t2open.out.access);
+ SSVAL(trans->out.params.data, VWV(7), io->t2open.out.ftype);
+ SSVAL(trans->out.params.data, VWV(8), io->t2open.out.devstate);
+ SSVAL(trans->out.params.data, VWV(9), io->t2open.out.action);
+ SIVAL(trans->out.params.data, VWV(10), 0); /* reserved */
+ SSVAL(trans->out.params.data, VWV(12), 0); /* EaErrorOffset */
+ SIVAL(trans->out.params.data, VWV(13), 0); /* EaLength */
+
+ return status;
+}
+
+
+/*
+ trans2 mkdir implementation
+*/
+static NTSTATUS trans2_mkdir(struct smbsrv_request *req, struct smb_trans2 *trans)
+{
+ union smb_mkdir *io;
+ NTSTATUS status;
+
+ /* make sure we got enough parameters */
+ if (trans->in.params.length < 5) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ io = talloc_p(req, union smb_mkdir);
+ if (io == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io->t2mkdir.level = RAW_MKDIR_T2MKDIR;
+ trans2_pull_blob_string(req, &trans->in.params, 4, &io->t2mkdir.in.path, 0);
+
+ status = ea_pull_list(&trans->in.data, io,
+ &io->t2mkdir.in.num_eas,
+ &io->t2mkdir.in.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntvfs_mkdir(req, io);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ trans2_setup_reply(req, trans, 2, 0, 0);
+
+ SSVAL(trans->out.params.data, VWV(0), 0);
+
+ return status;
+}
+
/*
fill in the reply from a qpathinfo or qfileinfo call
*/
union smb_fileinfo *st)
{
uint_t i;
+ uint32_t list_size;
switch (st->generic.level) {
case RAW_FILEINFO_GENERIC:
case RAW_FILEINFO_GETATTR:
case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_SEC_DESC:
/* handled elsewhere */
return NT_STATUS_INVALID_LEVEL;
st->alignment_information.out.alignment_requirement);
return NT_STATUS_OK;
+ case RAW_FILEINFO_EA_LIST:
+ list_size = ea_list_size(st->ea_list.out.num_eas,
+ st->ea_list.out.eas);
+ trans2_setup_reply(req, trans, 2, list_size, 0);
+ SSVAL(trans->out.params.data, 0, 0);
+ ea_put_list(trans->out.data.data,
+ st->ea_list.out.num_eas, st->ea_list.out.eas);
+ return NT_STATUS_OK;
+
case RAW_FILEINFO_ALL_EAS:
- if (st->all_eas.out.num_eas == 0) {
- trans2_setup_reply(req, trans, 2, 4, 0);
- SSVAL(trans->out.params.data, 0, 0);
- SIVAL(trans->out.data.data, 0, 0);
- } else {
- uint32_t list_size = ea_list_size(st->all_eas.out.num_eas,
- st->all_eas.out.eas);
- trans2_setup_reply(req, trans, 2, list_size, 0);
- SSVAL(trans->out.params.data, 0, 0);
- ea_put_list(trans->out.data.data,
- st->all_eas.out.num_eas, st->all_eas.out.eas);
- }
+ list_size = ea_list_size(st->all_eas.out.num_eas,
+ st->all_eas.out.eas);
+ trans2_setup_reply(req, trans, 2, list_size, 0);
+ SSVAL(trans->out.params.data, 0, 0);
+ ea_put_list(trans->out.data.data,
+ st->all_eas.out.num_eas, st->all_eas.out.eas);
return NT_STATUS_OK;
case RAW_FILEINFO_ACCESS_INFORMATION:
SSVAL(trans->out.params.data, 0, 0);
for (i=0;i<st->stream_info.out.num_streams;i++) {
- uint16_t data_size = trans->out.data.length;
- char *data;
+ uint32_t data_size = trans->out.data.length;
+ uint8_t *data;
trans2_grow_data(req, trans, data_size + 24);
data = trans->out.data.data + data_size;
uint16_t level;
/* make sure we got enough parameters */
- if (trans->in.params.length < 8) {
+ if (trans->in.params.length < 2) {
return NT_STATUS_FOOBAR;
}
return NT_STATUS_INVALID_LEVEL;
}
+ if (st.generic.level == RAW_FILEINFO_EA_LIST) {
+ status = ea_pull_name_list(&trans->in.data, req,
+ &st.ea_list.in.num_names,
+ &st.ea_list.in.ea_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
/* call the backend */
- status = req->tcon->ntvfs_ops->qpathinfo(req, &st);
+ status = ntvfs_qpathinfo(req, &st);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return NT_STATUS_INVALID_LEVEL;
}
+ if (st.generic.level == RAW_FILEINFO_EA_LIST) {
+ status = ea_pull_name_list(&trans->in.data, req,
+ &st.ea_list.in.num_names,
+ &st.ea_list.in.ea_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
/* call the backend */
- status = req->tcon->ntvfs_ops->qfileinfo(req, &st);
+ status = ntvfs_qfileinfo(req, &st);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
case RAW_SFILEINFO_GENERIC:
case RAW_SFILEINFO_SETATTR:
case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_SEC_DESC:
/* handled elsewhere */
return NT_STATUS_INVALID_LEVEL;
return NT_STATUS_OK;
case RAW_SFILEINFO_EA_SET:
- CHECK_MIN_BLOB_SIZE(blob, 4);
- len = IVAL(blob->data, 0);
- if (len > blob->length || len < 4) {
- return NT_STATUS_INFO_LENGTH_MISMATCH;
- }
- {
- DATA_BLOB blob2;
- blob2.data = blob->data+4;
- blob2.length = len-4;
- len = ea_pull_struct(&blob2, req, &st->ea_set.in.ea);
- }
- if (len == 0) {
- return NT_STATUS_INVALID_PARAMETER;
- }
- return NT_STATUS_OK;
+ return ea_pull_list(blob, req,
+ &st->ea_set.in.num_eas,
+ &st->ea_set.in.eas);
case SMB_SFILEINFO_BASIC_INFO:
case SMB_SFILEINFO_BASIC_INFORMATION:
return status;
}
- status = req->tcon->ntvfs_ops->setfileinfo(req, &st);
+ status = ntvfs_setfileinfo(req, &st);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
return status;
}
- status = req->tcon->ntvfs_ops->setpathinfo(req, &st);
+ status = ntvfs_setpathinfo(req, &st);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
/*
fill a single entry in a trans2 find reply
*/
-static void find_fill_info(struct smbsrv_request *req,
+static BOOL find_fill_info(struct smbsrv_request *req,
struct smb_trans2 *trans,
struct find_state *state,
union smb_search_data *file)
{
- char *data;
+ uint8_t *data;
uint_t ofs = trans->out.data.length;
+ uint32_t ea_size;
switch (state->level) {
case RAW_SEARCH_SEARCH:
trans->out.data.data[trans->out.data.length-1] = 0;
break;
+ case RAW_SEARCH_EA_LIST:
+ ea_size = ea_list_size(file->ea_list.eas.num_eas, file->ea_list.eas.eas);
+ if (state->flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) {
+ if (!trans2_grow_data(req, trans, ofs + 27 + ea_size)) {
+ return False;
+ }
+ SIVAL(trans->out.data.data, ofs, file->ea_list.resume_key);
+ ofs += 4;
+ } else {
+ if (!trans2_grow_data(req, trans, ofs + 23 + ea_size)) {
+ return False;
+ }
+ }
+ data = trans->out.data.data + ofs;
+ srv_push_dos_date2(req->smb_conn, data, 0, file->ea_list.create_time);
+ srv_push_dos_date2(req->smb_conn, data, 4, file->ea_list.access_time);
+ srv_push_dos_date2(req->smb_conn, data, 8, file->ea_list.write_time);
+ SIVAL(data, 12, file->ea_list.size);
+ SIVAL(data, 16, file->ea_list.alloc_size);
+ SSVAL(data, 20, file->ea_list.attrib);
+ ea_put_list(data+22, file->ea_list.eas.num_eas, file->ea_list.eas.eas);
+ trans2_append_data_string(req, trans, &file->ea_list.name,
+ ofs + 22 + ea_size, STR_LEN8BIT | STR_NOALIGN);
+ trans2_grow_data(req, trans, trans->out.data.length + 1);
+ trans->out.data.data[trans->out.data.length-1] = 0;
+ break;
+
case RAW_SEARCH_DIRECTORY_INFO:
trans2_grow_data(req, trans, ofs + 64);
data = trans->out.data.data + ofs;
24, STR_UNICODE | STR_LEN8BIT);
trans2_append_data_string(req, trans, &file->both_directory_info.name,
ofs + 60, STR_TERMINATE_ASCII);
+ trans2_align_data(req, trans);
data = trans->out.data.data + ofs;
SIVAL(data, 0, trans->out.data.length - ofs);
break;
SIVAL(data, 0, trans->out.data.length - ofs);
break;
}
+
+ return True;
}
/* callback function for trans2 findfirst/findnext */
old_length = trans->out.data.length;
- find_fill_info(state->req, trans, state, file);
-
- /* see if we have gone beyond the user specified maximum */
- if (trans->out.data.length > trans->in.max_data) {
+ if (!find_fill_info(state->req, trans, state, file) ||
+ trans->out.data.length > trans->in.max_data) {
/* restore the old length and tell the backend to stop */
trans2_grow_data(state->req, trans, old_length);
return False;
union smb_search_first search;
NTSTATUS status;
uint16_t level;
- char *param;
+ uint8_t *param;
struct find_state state;
/* make sure we got all the parameters */
return NT_STATUS_INVALID_LEVEL;
}
- /* setup the private state structure that the backend will give us in the callback */
+ if (search.t2ffirst.level == RAW_SEARCH_EA_LIST) {
+ status = ea_pull_name_list(&trans->in.data, req,
+ &search.t2ffirst.in.num_names,
+ &search.t2ffirst.in.ea_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* setup the private state structure that the backend will
+ give us in the callback */
state.req = req;
state.trans = trans;
state.level = search.t2ffirst.level;
trans2_setup_reply(req, trans, 10, 0, 0);
/* call the backend */
- status = req->tcon->ntvfs_ops->search_first(req, &search, &state, find_callback);
+ status = ntvfs_search_first(req, &search, &state, find_callback);
if (!NT_STATUS_IS_OK(status)) {
trans2_setup_reply(req, trans, 0, 0, 0);
return status;
union smb_search_next search;
NTSTATUS status;
uint16_t level;
- char *param;
+ uint8_t *param;
struct find_state state;
/* make sure we got all the parameters */
return NT_STATUS_INVALID_LEVEL;
}
+ if (search.t2fnext.level == RAW_SEARCH_EA_LIST) {
+ status = ea_pull_name_list(&trans->in.data, req,
+ &search.t2fnext.in.num_names,
+ &search.t2fnext.in.ea_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
/* setup the private state structure that the backend will give us in the callback */
state.req = req;
state.trans = trans;
trans2_setup_reply(req, trans, 8, 0, 0);
/* call the backend */
- status = req->tcon->ntvfs_ops->search_next(req, &search, &state, find_callback);
+ status = ntvfs_search_next(req, &search, &state, find_callback);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
*/
static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *trans)
{
- if (req->tcon->ntvfs_ops->trans2 != NULL) {
- /* direct trans2 pass thru */
- return req->tcon->ntvfs_ops->trans2(req, trans);
+ NTSTATUS status;
+
+ /* direct trans2 pass thru */
+ status = ntvfs_trans2(req, trans);
+ if (!NT_STATUS_EQUAL(NT_STATUS_NOT_IMPLEMENTED, status)) {
+ return status;
}
/* must have at least one setup word */
return trans2_setpathinfo(req, trans);
case TRANSACT2_QFSINFO:
return trans2_qfsinfo(req, trans);
+ case TRANSACT2_OPEN:
+ return trans2_open(req, trans);
+ case TRANSACT2_MKDIR:
+ return trans2_mkdir(req, trans);
}
/* an unknown trans2 command */
/*
- backend for trans requests
+ send a continue request
*/
-static NTSTATUS trans_backend(struct smbsrv_request *req, struct smb_trans2 *trans)
-{
- if (!req->tcon->ntvfs_ops->trans) {
- return NT_STATUS_NOT_IMPLEMENTED;
- }
- return req->tcon->ntvfs_ops->trans(req, trans);
-}
-
-
-/****************************************************************************
- Reply to an SMBtrans or SMBtrans2 request
-****************************************************************************/
-void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
+static void reply_trans_continue(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
{
- struct smb_trans2 trans;
- int i;
- uint16_t param_ofs, data_ofs;
- uint16_t param_count, data_count;
- uint16_t params_left, data_left;
- uint16_t param_total, data_total;
- char *params, *data;
- NTSTATUS status;
+ struct smbsrv_trans_partial *tp;
+ int count;
- /* parse request */
- if (req->in.wct < 14) {
- req_reply_error(req, NT_STATUS_FOOBAR);
+ /* make sure they don't flood us */
+ for (count=0,tp=req->smb_conn->trans_partial;tp;tp=tp->next) count++;
+ if (count > 100) {
+ req_reply_error(req, NT_STATUS_INSUFFICIENT_RESOURCES);
return;
}
- param_total = SVAL(req->in.vwv, VWV(0));
- data_total = SVAL(req->in.vwv, VWV(1));
- trans.in.max_param = SVAL(req->in.vwv, VWV(2));
- trans.in.max_data = SVAL(req->in.vwv, VWV(3));
- trans.in.max_setup = CVAL(req->in.vwv, VWV(4));
- trans.in.flags = SVAL(req->in.vwv, VWV(5));
- trans.in.timeout = IVAL(req->in.vwv, VWV(6));
- param_count = SVAL(req->in.vwv, VWV(9));
- param_ofs = SVAL(req->in.vwv, VWV(10));
- data_count = SVAL(req->in.vwv, VWV(11));
- data_ofs = SVAL(req->in.vwv, VWV(12));
- trans.in.setup_count = CVAL(req->in.vwv, VWV(13));
-
- if (req->in.wct != 14 + trans.in.setup_count) {
- req_reply_dos_error(req, ERRSRV, ERRerror);
- return;
- }
+ tp = talloc_p(req, struct smbsrv_trans_partial);
- /* parse out the setup words */
- trans.in.setup = talloc(req, trans.in.setup_count * sizeof(uint16_t));
- if (trans.in.setup_count && !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(14+i));
- }
+ tp->req = talloc_reference(tp, req);
+ tp->trans = trans;
+ tp->command = command;
- if (command == SMBtrans) {
- req_pull_string(req, &trans.in.trans_name, req->in.data, -1, STR_TERMINATE);
- }
+ DLIST_ADD(req->smb_conn->trans_partial, tp);
- 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;
- }
+ /* send a 'please continue' reply */
+ req_setup_reply(req, 0, 0);
+ req_send_reply(req);
+}
- /* 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 trans requests!\n"));
- return;
- }
+
+/*
+ answer a reconstructed trans request
+*/
+static void reply_trans_complete(struct smbsrv_request *req, uint8_t command,
+ struct smb_trans2 *trans)
+{
+ uint16_t params_left, data_left;
+ uint8_t *params, *data;
+ NTSTATUS status;
+ int i;
/* its a full request, give it to the backend */
if (command == SMBtrans) {
- status = trans_backend(req, &trans);
+ status = ntvfs_trans(req, trans);
} else {
- status = trans2_backend(req, &trans);
+ status = trans2_backend(req, trans);
}
if (NT_STATUS_IS_ERR(status)) {
return;
}
- params_left = trans.out.params.length;
- data_left = trans.out.data.length;
- params = trans.out.params.data;
- data = trans.out.data.data;
+ 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;
+ req_setup_reply(req, 10 + trans->out.setup_count, 0);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ req_setup_error(req, status);
+ }
/* we need to divide up the reply into chunks that fit into
the negotiated buffer size */
do {
uint16_t this_data, this_param, max_bytes;
uint_t align1 = 1, align2 = (params_left ? 2 : 0);
+ struct smbsrv_request *this_req;
- req_setup_reply(req, 10 + trans.out.setup_count, 0);
-
- if (!NT_STATUS_IS_OK(status)) {
- req_setup_error(req, status);
- }
-
max_bytes = req_max_data(req) - (align1 + align2);
this_param = params_left;
this_data = max_bytes;
}
- req_grow_data(req, this_param + this_data + (align1 + align2));
+ /* don't destroy unless this is the last chunk */
+ if (params_left - this_param != 0 ||
+ data_left - this_data != 0) {
+ this_req = req_setup_secondary(req);
+ } else {
+ this_req = req;
+ }
+
+ req_grow_data(this_req, this_param + this_data + (align1 + align2));
- SSVAL(req->out.vwv, VWV(0), trans.out.params.length);
- SSVAL(req->out.vwv, VWV(1), trans.out.data.length);
- SSVAL(req->out.vwv, VWV(2), 0);
+ SSVAL(this_req->out.vwv, VWV(0), trans->out.params.length);
+ SSVAL(this_req->out.vwv, VWV(1), trans->out.data.length);
+ SSVAL(this_req->out.vwv, VWV(2), 0);
- SSVAL(req->out.vwv, VWV(3), this_param);
- SSVAL(req->out.vwv, VWV(4), align1 + PTR_DIFF(req->out.data, req->out.hdr));
- SSVAL(req->out.vwv, VWV(5), PTR_DIFF(params, trans.out.params.data));
+ SSVAL(this_req->out.vwv, VWV(3), this_param);
+ SSVAL(this_req->out.vwv, VWV(4), align1 + PTR_DIFF(this_req->out.data, this_req->out.hdr));
+ SSVAL(this_req->out.vwv, VWV(5), PTR_DIFF(params, trans->out.params.data));
- SSVAL(req->out.vwv, VWV(6), this_data);
- SSVAL(req->out.vwv, VWV(7), align1 + align2 +
- PTR_DIFF(req->out.data + this_param, req->out.hdr));
- SSVAL(req->out.vwv, VWV(8), PTR_DIFF(data, trans.out.data.data));
+ SSVAL(this_req->out.vwv, VWV(6), this_data);
+ SSVAL(this_req->out.vwv, VWV(7), align1 + align2 +
+ PTR_DIFF(this_req->out.data + this_param, this_req->out.hdr));
+ SSVAL(this_req->out.vwv, VWV(8), PTR_DIFF(data, trans->out.data.data));
- SSVAL(req->out.vwv, VWV(9), trans.out.setup_count);
- for (i=0;i<trans.out.setup_count;i++) {
- SSVAL(req->out.vwv, VWV(10+i), trans.out.setup[i]);
+ SSVAL(this_req->out.vwv, VWV(9), trans->out.setup_count);
+ for (i=0;i<trans->out.setup_count;i++) {
+ SSVAL(this_req->out.vwv, VWV(10+i), trans->out.setup[i]);
}
- memset(req->out.data, 0, align1);
+ memset(this_req->out.data, 0, align1);
if (this_param != 0) {
- memcpy(req->out.data + align1, params, this_param);
+ memcpy(this_req->out.data + align1, params, this_param);
}
- memset(req->out.data+this_param+align1, 0, align2);
+ memset(this_req->out.data+this_param+align1, 0, align2);
if (this_data != 0) {
- memcpy(req->out.data+this_param+align1+align2, data, this_data);
+ memcpy(this_req->out.data+this_param+align1+align2, data, this_data);
}
params_left -= this_param;
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(this_req);
+ } while (params_left != 0 || data_left != 0);
+}
+
+
+/*
+ Reply to an SMBtrans or SMBtrans2 request
+*/
+void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smb_trans2 *trans;
+ int i;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_total, data_total;
+
+ trans = talloc_p(req, struct smb_trans2);
+ if (trans == NULL) {
+ req_reply_error(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ /* parse request */
+ if (req->in.wct < 14) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ trans->in.max_param = SVAL(req->in.vwv, VWV(2));
+ trans->in.max_data = SVAL(req->in.vwv, VWV(3));
+ trans->in.max_setup = CVAL(req->in.vwv, VWV(4));
+ trans->in.flags = SVAL(req->in.vwv, VWV(5));
+ trans->in.timeout = IVAL(req->in.vwv, VWV(6));
+ param_count = SVAL(req->in.vwv, VWV(9));
+ param_ofs = SVAL(req->in.vwv, VWV(10));
+ data_count = SVAL(req->in.vwv, VWV(11));
+ data_ofs = SVAL(req->in.vwv, VWV(12));
+ trans->in.setup_count = CVAL(req->in.vwv, VWV(13));
+
+ if (req->in.wct != 14 + trans->in.setup_count) {
+ req_reply_dos_error(req, ERRSRV, ERRerror);
+ return;
+ }
+
+ /* parse out the setup words */
+ trans->in.setup = talloc_array_p(req, uint16_t, trans->in.setup_count);
+ if (trans->in.setup_count && !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(14+i));
+ }
+
+ if (command == SMBtrans) {
+ req_pull_string(req, &trans->in.trans_name, req->in.data, -1, STR_TERMINATE);
+ }
+
+ 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) {
+ reply_trans_continue(req, command, trans);
+ return;
+ }
+
+ reply_trans_complete(req, command, trans);
+}
+
+
+/*
+ Reply to an SMBtranss2 request
+*/
+static void reply_transs_generic(struct smbsrv_request *req, uint8_t command)
+{
+ struct smbsrv_trans_partial *tp;
+ struct smb_trans2 *trans = NULL;
+ uint16_t param_ofs, data_ofs;
+ uint16_t param_count, data_count;
+ uint16_t param_disp, data_disp;
+ uint16_t param_total, data_total;
+ DATA_BLOB params, data;
+
+ for (tp=req->smb_conn->trans_partial;tp;tp=tp->next) {
+ if (tp->command == command &&
+ SVAL(tp->req->in.hdr, HDR_MID) == SVAL(req->in.hdr, HDR_MID)) {
+ break;
}
+ }
- req_send_reply(req);
- } while (params_left != 0 || data_left != 0);
+ if (tp == NULL) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ trans = tp->trans;
+
+ /* parse request */
+ if (req->in.wct < 8) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ param_total = SVAL(req->in.vwv, VWV(0));
+ data_total = SVAL(req->in.vwv, VWV(1));
+ param_count = SVAL(req->in.vwv, VWV(2));
+ param_ofs = SVAL(req->in.vwv, VWV(3));
+ param_disp = SVAL(req->in.vwv, VWV(4));
+ data_count = SVAL(req->in.vwv, VWV(5));
+ data_ofs = SVAL(req->in.vwv, VWV(6));
+ data_disp = SVAL(req->in.vwv, VWV(7));
+
+ if (!req_pull_blob(req, req->in.hdr + param_ofs, param_count, ¶ms) ||
+ !req_pull_blob(req, req->in.hdr + data_ofs, data_count, &data)) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* only allow contiguous requests */
+ if ((param_count != 0 &&
+ param_disp != trans->in.params.length) ||
+ (data_count != 0 &&
+ data_disp != trans->in.data.length)) {
+ req_reply_error(req, NT_STATUS_INVALID_PARAMETER);
+ return;
+ }
+
+ /* add to the existing request */
+ if (param_count != 0) {
+ trans->in.params.data = talloc_realloc_p(trans,
+ trans->in.params.data,
+ uint8_t,
+ param_disp + param_count);
+ if (trans->in.params.data == NULL) {
+ goto failed;
+ }
+ trans->in.params.length = param_disp + param_count;
+ }
+
+ if (data_count != 0) {
+ trans->in.data.data = talloc_realloc_p(trans,
+ trans->in.data.data,
+ uint8_t,
+ data_disp + data_count);
+ if (trans->in.data.data == NULL) {
+ goto failed;
+ }
+ trans->in.data.length = data_disp + data_count;
+ }
+
+ memcpy(trans->in.params.data + param_disp, params.data, params.length);
+ memcpy(trans->in.data.data + data_disp, data.data, data.length);
+
+ /* the sequence number of the reply is taken from the last secondary
+ response */
+ tp->req->seq_num = req->seq_num;
+
+ /* we don't reply to Transs2 requests */
+ talloc_free(req);
+
+ if (trans->in.params.length == param_total &&
+ trans->in.data.length == data_total) {
+ /* its now complete */
+ reply_trans_complete(tp->req, command, trans);
+ DLIST_REMOVE(tp->req->smb_conn->trans_partial, tp);
+ talloc_free(tp);
+ }
+ return;
+
+failed:
+ req_reply_error(tp->req, NT_STATUS_NO_MEMORY);
+ DLIST_REMOVE(req->smb_conn->trans_partial, tp);
+ talloc_free(req);
+ talloc_free(tp);
}
-/****************************************************************************
- Reply to an SMBtrans2
-****************************************************************************/
+/*
+ Reply to an SMBtrans2
+*/
void reply_trans2(struct smbsrv_request *req)
{
reply_trans_generic(req, SMBtrans2);
}
-/****************************************************************************
- Reply to an SMBtrans
-****************************************************************************/
+/*
+ Reply to an SMBtrans
+*/
void reply_trans(struct smbsrv_request *req)
{
reply_trans_generic(req, SMBtrans);
}
-/****************************************************************************
- Reply to an SMBtranss2 request
-****************************************************************************/
-void reply_transs2(struct smbsrv_request *req)
+/*
+ Reply to an SMBtranss request
+*/
+void reply_transs(struct smbsrv_request *req)
{
- req_reply_error(req, NT_STATUS_FOOBAR);
+ reply_transs_generic(req, SMBtrans);
}
+/*
+ Reply to an SMBtranss2 request
+*/
+void reply_transs2(struct smbsrv_request *req)
+{
+ reply_transs_generic(req, SMBtrans2);
+}