r4383: in order to cope with overfilled buffers on trans2 findfirst we need to use...
[samba.git] / source4 / smb_server / trans2.c
index a5033f9b5690d37ad6b2d46db2e8c63d410a032a..8aa60daa6df43d7028caa215b94fa73477eaf74b 100644 (file)
@@ -22,6 +22,9 @@
 */
 
 #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;
 }
 
 
@@ -73,7 +83,7 @@ static void trans2_setup_reply(struct smbsrv_request *req,
 {
        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);
@@ -109,8 +119,8 @@ static size_t trans2_pull_blob_string(struct smbsrv_request *req,
 */
 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)
@@ -183,7 +193,7 @@ static void trans2_append_data_string(struct smbsrv_request *req,
                                        int flags)
 {
        size_t ret;
-       uint16_t offset;
+       uint32_t offset;
        const int max_bytes_per_char = 3;
 
        offset = trans->out.data.length;
@@ -192,6 +202,18 @@ static void trans2_append_data_string(struct smbsrv_request *req,
        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
@@ -215,7 +237,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -233,7 +255,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -253,7 +275,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -273,7 +295,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -291,7 +313,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -305,7 +327,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -326,7 +348,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -346,7 +368,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -364,7 +386,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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;
                }
@@ -378,7 +400,7 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
                        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]);
@@ -389,6 +411,108 @@ static NTSTATUS trans2_qfsinfo(struct smbsrv_request *req, struct smb_trans2 *tr
        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
 */
@@ -396,11 +520,13 @@ static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_tran
                                     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;
 
@@ -495,19 +621,22 @@ static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_tran
                      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:
@@ -589,8 +718,8 @@ static NTSTATUS trans2_fileinfo_fill(struct smbsrv_request *req, struct smb_tran
                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;
@@ -623,7 +752,7 @@ static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct smb_trans2 *
        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;
        }
 
@@ -640,8 +769,17 @@ static NTSTATUS trans2_qpathinfo(struct smbsrv_request *req, struct smb_trans2 *
                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;
        }
@@ -676,8 +814,17 @@ static NTSTATUS trans2_qfileinfo(struct smbsrv_request *req, struct smb_trans2 *
                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;
        }
@@ -702,6 +849,7 @@ static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req,
        case RAW_SFILEINFO_GENERIC:
        case RAW_SFILEINFO_SETATTR:
        case RAW_SFILEINFO_SETATTRE:
+       case RAW_SFILEINFO_SEC_DESC:
                /* handled elsewhere */
                return NT_STATUS_INVALID_LEVEL;
 
@@ -713,21 +861,9 @@ static NTSTATUS trans2_parse_sfileinfo(struct smbsrv_request *req,
                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:
@@ -813,7 +949,7 @@ static NTSTATUS trans2_setfileinfo(struct smbsrv_request *req, struct smb_trans2
                return status;
        }
 
-       status = req->tcon->ntvfs_ops->setfileinfo(req, &st);
+       status = ntvfs_setfileinfo(req, &st);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -852,7 +988,7 @@ static NTSTATUS trans2_setpathinfo(struct smbsrv_request *req, struct smb_trans2
                return status;
        }
 
-       status = req->tcon->ntvfs_ops->setpathinfo(req, &st);
+       status = ntvfs_setpathinfo(req, &st);
        if (!NT_STATUS_IS_OK(status)) {
                return status;
        }
@@ -875,13 +1011,14 @@ struct find_state {
 /*
   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:
@@ -932,6 +1069,33 @@ static void find_fill_info(struct smbsrv_request *req,
                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;
@@ -997,6 +1161,7 @@ static void find_fill_info(struct smbsrv_request *req,
                                        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;
@@ -1046,6 +1211,8 @@ static void find_fill_info(struct smbsrv_request *req,
                SIVAL(data,          0, trans->out.data.length - ofs);
                break;
        }
+
+       return True;
 }
 
 /* callback function for trans2 findfirst/findnext */
@@ -1057,10 +1224,8 @@ static BOOL find_callback(void *private, union smb_search_data *file)
 
        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;
@@ -1079,7 +1244,7 @@ static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct smb_trans2 *
        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 */
@@ -1103,7 +1268,17 @@ static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct smb_trans2 *
                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;
@@ -1114,7 +1289,7 @@ static NTSTATUS trans2_findfirst(struct smbsrv_request *req, struct smb_trans2 *
        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;
@@ -1140,7 +1315,7 @@ static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *t
        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 */
@@ -1164,6 +1339,15 @@ static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *t
                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;
@@ -1175,7 +1359,7 @@ static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *t
        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;
        }
@@ -1196,9 +1380,12 @@ static NTSTATUS trans2_findnext(struct smbsrv_request *req, struct smb_trans2 *t
 */
 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 */
@@ -1222,6 +1409,10 @@ static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *tr
                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 */
@@ -1230,87 +1421,51 @@ static NTSTATUS trans2_backend(struct smbsrv_request *req, struct smb_trans2 *tr
 
 
 /*
-  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)) {
@@ -1318,25 +1473,24 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
                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;
@@ -1350,33 +1504,41 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
                        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;
@@ -1384,38 +1546,218 @@ void reply_trans_generic(struct smbsrv_request *req, uint8_t command)
                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, &params) ||
+           !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);
+}