s3-smb2: split ioctl handlers into separate functions
authorDavid Disseldorp <ddiss@suse.de>
Wed, 28 Sep 2011 09:42:06 +0000 (11:42 +0200)
committerDavid Disseldorp <ddiss@suse.de>
Tue, 11 Oct 2011 09:45:40 +0000 (11:45 +0200)
source3/smbd/smb2_ioctl.c

index 5a766e1b1a52acfaca10ddad3520881339274de0..6cda298a9d96995d5baf038e001ce3125a41d667 100644 (file)
@@ -219,6 +219,247 @@ struct smbd_smb2_ioctl_state {
 static void smbd_smb2_ioctl_pipe_write_done(struct tevent_req *subreq);
 static void smbd_smb2_ioctl_pipe_read_done(struct tevent_req *subreq);
 
+static struct tevent_req *fsctl_dfs_get_refers_post(struct tevent_context *ev,
+                                                  struct tevent_req *req,
+                                       struct smbd_smb2_ioctl_state *state)
+{
+       uint16_t in_max_referral_level;
+       DATA_BLOB in_file_name_buffer;
+       char *in_file_name_string;
+       size_t in_file_name_string_size;
+       bool ok;
+       bool overflow = false;
+       NTSTATUS status;
+       int dfs_size;
+       char *dfs_data = NULL;
+
+       if (!IS_IPC(state->smbreq->conn)) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!lp_host_msdfs()) {
+               tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->in_input.length < (2 + 2)) {
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       in_max_referral_level = SVAL(state->in_input.data, 0);
+       in_file_name_buffer.data = state->in_input.data + 2;
+       in_file_name_buffer.length = state->in_input.length - 2;
+
+       ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
+                                  in_file_name_buffer.data,
+                                  in_file_name_buffer.length,
+                                  &in_file_name_string,
+                                  &in_file_name_string_size);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
+               return tevent_req_post(req, ev);
+       }
+
+       dfs_size = setup_dfs_referral(state->smbreq->conn,
+                                     in_file_name_string,
+                                     in_max_referral_level,
+                                     &dfs_data, &status);
+       if (dfs_size < 0) {
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       if (dfs_size > state->in_max_output) {
+               /*
+                * TODO: we need a testsuite for this
+                */
+               overflow = true;
+               dfs_size = state->in_max_output;
+       }
+
+       state->out_output = data_blob_talloc(state,
+                                            (uint8_t *)dfs_data,
+                                            dfs_size);
+       SAFE_FREE(dfs_data);
+       if (dfs_size > 0 &&
+           tevent_req_nomem(state->out_output.data, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       if (overflow) {
+               tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
+       } else {
+               tevent_req_done(req);
+       }
+       return tevent_req_post(req, ev);
+}
+
+static struct tevent_req *fsctl_pipe_transceive_post(struct tevent_context *ev,
+                                                    struct tevent_req *req,
+                                       struct smbd_smb2_ioctl_state *state)
+{
+       struct tevent_req *subreq;
+
+       if (!IS_IPC(state->smbreq->conn)) {
+               tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->fsp == NULL) {
+               tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
+               return tevent_req_post(req, ev);
+       }
+
+       if (!fsp_is_np(state->fsp)) {
+               tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
+               return tevent_req_post(req, ev);
+       }
+
+       DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
+               (unsigned int)state->in_input.length ));
+
+       subreq = np_write_send(state, ev,
+                              state->fsp->fake_file_handle,
+                              state->in_input.data,
+                              state->in_input.length);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq,
+                               smbd_smb2_ioctl_pipe_write_done,
+                               req);
+       return req;
+}
+
+/*
+ * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
+ * and return their volume names.  If max_data_count is 16, then it is just
+ * asking for the number of volumes and length of the combined names.
+ *
+ * pdata is the data allocated by our caller, but that uses
+ * total_data_count (which is 0 in our case) rather than max_data_count.
+ * Allocate the correct amount and return the pointer to let
+ * it be deallocated when we return.
+ */
+static struct tevent_req *fsctl_srv_enum_snaps_post(struct tevent_context *ev,
+                                                   struct tevent_req *req,
+                                       struct smbd_smb2_ioctl_state *state)
+{
+       struct shadow_copy_data *shadow_data = NULL;
+       bool labels = False;
+       uint32_t labels_data_count = 0;
+       uint32_t data_count;
+       uint32_t i;
+       char *pdata;
+       NTSTATUS status;
+
+       if (state->fsp == NULL) {
+               tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->in_max_output < 16) {
+               DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
+                        "state->in_max_output(%u) < 16 is invalid!\n",
+                        state->in_max_output));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       if (state->in_max_output > 16) {
+               labels = True;
+       }
+
+       shadow_data = talloc_zero(talloc_tos(),
+                                   struct shadow_copy_data);
+       if (tevent_req_nomem(shadow_data, req)) {
+               DEBUG(0,("TALLOC_ZERO() failed!\n"));
+               return tevent_req_post(req, ev);
+       }
+
+       /*
+        * Call the VFS routine to actually do the work.
+        */
+       if (SMB_VFS_GET_SHADOW_COPY_DATA(state->fsp, shadow_data, labels)
+           != 0) {
+               if (errno == ENOSYS) {
+                       DEBUG(5, ("FSCTL_GET_SHADOW_COPY_DATA: "
+                                 "connectpath %s, not supported.\n",
+                                 state->smbreq->conn->connectpath));
+                       status = NT_STATUS_NOT_SUPPORTED;
+               } else {
+                       DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
+                                "connectpath %s, failed.\n",
+                                state->smbreq->conn->connectpath));
+                       status = map_nt_error_from_unix(errno);
+               }
+               TALLOC_FREE(shadow_data);
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       labels_data_count =
+               (shadow_data->num_volumes*2*sizeof(SHADOW_COPY_LABEL))
+               + 2;
+
+       if (labels) {
+               data_count = 12+labels_data_count+4;
+       } else {
+               data_count = 16;
+       }
+
+       if (labels && (state->in_max_output < data_count)) {
+               DEBUG(0, ("FSCTL_GET_SHADOW_COPY_DATA: "
+                         "state->in_max_output(%u) too small (%u) bytes "
+                         "needed!\n", state->in_max_output, data_count));
+               TALLOC_FREE(shadow_data);
+               tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
+               return tevent_req_post(req, ev);
+       }
+
+       state->out_output = data_blob_talloc(state, NULL, data_count);
+       if (tevent_req_nomem(state->out_output.data, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       pdata = (char *)state->out_output.data;
+
+       /* num_volumes 4 bytes */
+       SIVAL(pdata, 0, shadow_data->num_volumes);
+
+       if (labels) {
+               /* num_labels 4 bytes */
+               SIVAL(pdata, 4, shadow_data->num_volumes);
+       }
+
+       /* needed_data_count 4 bytes */
+       SIVAL(pdata, 8, labels_data_count+4);
+
+       pdata += 12;
+
+       DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for "
+                 "path[%s].\n",
+                 shadow_data->num_volumes, fsp_str_dbg(state->fsp)));
+       if (labels && shadow_data->labels) {
+               for (i=0; i<shadow_data->num_volumes; i++) {
+                       srvstr_push(pdata, state->smbreq->flags2,
+                                   pdata, shadow_data->labels[i],
+                                   2*sizeof(SHADOW_COPY_LABEL),
+                                   STR_UNICODE|STR_TERMINATE);
+                       pdata += 2*sizeof(SHADOW_COPY_LABEL);
+                       DEBUGADD(10, ("Label[%u]: '%s'\n", i,
+                                     shadow_data->labels[i]));
+               }
+       }
+
+       TALLOC_FREE(shadow_data);
+
+       tevent_req_done(req);
+       return tevent_req_post(req, ev);
+}
+
 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
                                               struct tevent_context *ev,
                                               struct smbd_smb2_request *smb2req,
@@ -232,7 +473,6 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
        struct smbd_smb2_ioctl_state *state;
        struct smb_request *smbreq;
        files_struct *fsp = NULL;
-       struct tevent_req *subreq;
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbd_smb2_ioctl_state);
@@ -275,236 +515,11 @@ static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx,
 
        switch (in_ctl_code) {
        case 0x00060194: /* FSCTL_DFS_GET_REFERRALS */
-       {
-               uint16_t in_max_referral_level;
-               DATA_BLOB in_file_name_buffer;
-               char *in_file_name_string;
-               size_t in_file_name_string_size;
-               bool ok;
-               bool overflow = false;
-               NTSTATUS status;
-               int dfs_size;
-               char *dfs_data = NULL;
-
-               if (!IS_IPC(smbreq->conn)) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (!lp_host_msdfs()) {
-                       tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (in_input.length < (2 + 2)) {
-                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return tevent_req_post(req, ev);
-               }
-
-               in_max_referral_level = SVAL(in_input.data, 0);
-               in_file_name_buffer.data = in_input.data + 2;
-               in_file_name_buffer.length = in_input.length - 2;
-
-               ok = convert_string_talloc(state, CH_UTF16, CH_UNIX,
-                                          in_file_name_buffer.data,
-                                          in_file_name_buffer.length,
-                                          &in_file_name_string,
-                                          &in_file_name_string_size);
-               if (!ok) {
-                       tevent_req_nterror(req, NT_STATUS_ILLEGAL_CHARACTER);
-                       return tevent_req_post(req, ev);
-               }
-
-               dfs_size = setup_dfs_referral(smbreq->conn,
-                                             in_file_name_string,
-                                             in_max_referral_level,
-                                             &dfs_data, &status);
-               if (dfs_size < 0) {
-                       tevent_req_nterror(req, status);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (dfs_size > in_max_output) {
-                       /*
-                        * TODO: we need a testsuite for this
-                        */
-                       overflow = true;
-                       dfs_size = in_max_output;
-               }
-
-               state->out_output = data_blob_talloc(state,
-                                                    (uint8_t *)dfs_data,
-                                                    dfs_size);
-               SAFE_FREE(dfs_data);
-               if (dfs_size > 0 &&
-                   tevent_req_nomem(state->out_output.data, req)) {
-                       return tevent_req_post(req, ev);
-               }
-
-               if (overflow) {
-                       tevent_req_nterror(req, STATUS_BUFFER_OVERFLOW);
-               } else {
-                       tevent_req_done(req);
-               }
-               return tevent_req_post(req, ev);
-       }
+               return fsctl_dfs_get_refers_post(ev, req, state);
        case 0x0011C017: /* FSCTL_PIPE_TRANSCEIVE */
-
-               if (!IS_IPC(smbreq->conn)) {
-                       tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (fsp == NULL) {
-                       tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (!fsp_is_np(fsp)) {
-                       tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
-                       return tevent_req_post(req, ev);
-               }
-
-               DEBUG(10,("smbd_smb2_ioctl_send: np_write_send of size %u\n",
-                       (unsigned int)in_input.length ));
-
-               subreq = np_write_send(state, ev,
-                                      fsp->fake_file_handle,
-                                      in_input.data,
-                                      in_input.length);
-               if (tevent_req_nomem(subreq, req)) {
-                       return tevent_req_post(req, ev);
-               }
-               tevent_req_set_callback(subreq,
-                                       smbd_smb2_ioctl_pipe_write_done,
-                                       req);
-               return req;
-
-       case 0x00144064:        /* FSCTL_SRV_ENUMERATE_SNAPSHOTS */
-       {
-               /*
-                * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
-                * and return their volume names.  If max_data_count is 16, then it is just
-                * asking for the number of volumes and length of the combined names.
-                *
-                * pdata is the data allocated by our caller, but that uses
-                * total_data_count (which is 0 in our case) rather than max_data_count.
-                * Allocate the correct amount and return the pointer to let
-                * it be deallocated when we return.
-                */
-               struct shadow_copy_data *shadow_data = NULL;
-               bool labels = False;
-               uint32_t labels_data_count = 0;
-               uint32_t data_count;
-               uint32_t i;
-               char *pdata;
-               NTSTATUS status;
-
-               if (fsp == NULL) {
-                       tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (in_max_output < 16) {
-                       DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
-                                "in_max_output(%u) < 16 is invalid!\n",
-                                in_max_output));
-                       tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
-                       return tevent_req_post(req, ev);
-               }
-
-               if (in_max_output > 16) {
-                       labels = True;
-               }
-
-               shadow_data = talloc_zero(talloc_tos(),
-                                           struct shadow_copy_data);
-               if (tevent_req_nomem(shadow_data, req)) {
-                       DEBUG(0,("TALLOC_ZERO() failed!\n"));
-                       return tevent_req_post(req, ev);
-               }
-
-               /*
-                * Call the VFS routine to actually do the work.
-                */
-               if (SMB_VFS_GET_SHADOW_COPY_DATA(fsp, shadow_data, labels)
-                   != 0) {
-                       if (errno == ENOSYS) {
-                               DEBUG(5, ("FSCTL_GET_SHADOW_COPY_DATA: "
-                                         "connectpath %s, not supported.\n",
-                                         smbreq->conn->connectpath));
-                               status = NT_STATUS_NOT_SUPPORTED;
-                       } else {
-                               DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: "
-                                        "connectpath %s, failed.\n",
-                                        smbreq->conn->connectpath));
-                               status = map_nt_error_from_unix(errno);
-                       }
-                       TALLOC_FREE(shadow_data);
-                       tevent_req_nterror(req, status);
-                       return tevent_req_post(req, ev);
-               }
-
-               labels_data_count =
-                       (shadow_data->num_volumes*2*sizeof(SHADOW_COPY_LABEL))
-                       + 2;
-
-               if (labels) {
-                       data_count = 12+labels_data_count+4;
-               } else {
-                       data_count = 16;
-               }
-
-               if (labels && (in_max_output < data_count)) {
-                       DEBUG(0, ("FSCTL_GET_SHADOW_COPY_DATA: "
-                                 "in_max_output(%u) too small (%u) bytes "
-                                 "needed!\n", in_max_output, data_count));
-                       TALLOC_FREE(shadow_data);
-                       tevent_req_nterror(req, NT_STATUS_BUFFER_TOO_SMALL);
-                       return tevent_req_post(req, ev);
-               }
-
-               state->out_output = data_blob_talloc(state, NULL, data_count);
-               if (tevent_req_nomem(state->out_output.data, req)) {
-                       return tevent_req_post(req, ev);
-               }
-
-               pdata = (char *)state->out_output.data;
-
-               /* num_volumes 4 bytes */
-               SIVAL(pdata, 0, shadow_data->num_volumes);
-
-               if (labels) {
-                       /* num_labels 4 bytes */
-                       SIVAL(pdata, 4, shadow_data->num_volumes);
-               }
-
-               /* needed_data_count 4 bytes */
-               SIVAL(pdata, 8, labels_data_count+4);
-
-               pdata += 12;
-
-               DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for "
-                         "path[%s].\n",
-                         shadow_data->num_volumes, fsp_str_dbg(fsp)));
-               if (labels && shadow_data->labels) {
-                       for (i=0; i<shadow_data->num_volumes; i++) {
-                               srvstr_push(pdata, smbreq->flags2,
-                                           pdata, shadow_data->labels[i],
-                                           2*sizeof(SHADOW_COPY_LABEL),
-                                           STR_UNICODE|STR_TERMINATE);
-                               pdata += 2*sizeof(SHADOW_COPY_LABEL);
-                               DEBUGADD(10, ("Label[%u]: '%s'\n", i,
-                                             shadow_data->labels[i]));
-                       }
-               }
-
-               TALLOC_FREE(shadow_data);
-
-               tevent_req_done(req);
-               return tevent_req_post(req, ev);
-        }
-
+               return fsctl_pipe_transceive_post(ev, req, state);
+       case 0x00144064: /* FSCTL_SRV_ENUMERATE_SNAPSHOTS */
+               return fsctl_srv_enum_snaps_post(ev, req, state);
        default:
                if (IS_IPC(smbreq->conn)) {
                        tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED);