s3-libsmb: support rename and replace for SMB1
authorUri Simchoni <uri@samba.org>
Sun, 26 Mar 2017 09:02:09 +0000 (12:02 +0300)
committerJeremy Allison <jra@samba.org>
Tue, 28 Mar 2017 15:45:19 +0000 (17:45 +0200)
Add cli_smb1_rename_send() which renames a file via
setting FileRenameInformation.

Curretly this path is invoked only if replacing
an existing file is requested. This is because as far
as I can see, Windows uses CIFS rename for anything below
SMB2.

Signed-off-by: Uri Simchoni <uri@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/libsmb/clifile.c

index 44fde38bc3aa8b745c6cf161b4b37294e01bc97b..cc1d1e416652baa98b1afc24e8ad50263b608d24 100644 (file)
@@ -999,6 +999,13 @@ static struct tevent_req *cli_cifs_rename_send(TALLOC_CTX *mem_ctx,
                                               const char *fname_dst,
                                               bool replace);
 
+static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace);
+
 struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
                                   struct tevent_context *ev,
                                   struct cli_state *cli,
@@ -1006,8 +1013,91 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
                                   const char *fname_dst,
                                   bool replace)
 {
-       return cli_cifs_rename_send(mem_ctx, ev, cli, fname_src, fname_dst,
-                                   replace);
+       if (replace && smbXcli_conn_support_passthrough(cli->conn)) {
+               return cli_smb1_rename_send(mem_ctx, ev, cli, fname_src,
+                                           fname_dst, replace);
+       } else {
+               return cli_cifs_rename_send(mem_ctx, ev, cli, fname_src,
+                                           fname_dst, replace);
+       }
+}
+
+struct cli_smb1_rename_state {
+       uint8_t *data;
+};
+
+static void cli_smb1_rename_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx,
+                                              struct tevent_context *ev,
+                                              struct cli_state *cli,
+                                              const char *fname_src,
+                                              const char *fname_dst,
+                                              bool replace)
+{
+       NTSTATUS status;
+       struct tevent_req *req = NULL, *subreq = NULL;
+       struct cli_smb1_rename_state *state = NULL;
+       smb_ucs2_t *converted_str = NULL;
+       size_t converted_size_bytes = 0;
+
+       req = tevent_req_create(mem_ctx, &state, struct cli_smb1_rename_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       if (!push_ucs2_talloc(talloc_tos(), &converted_str, fname_dst,
+                             &converted_size_bytes)) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+
+       /* W2K8 insists the dest name is not null
+          terminated. Remove the last 2 zero bytes
+          and reduce the name length. */
+
+       if (converted_size_bytes < 2) {
+               status = NT_STATUS_INVALID_PARAMETER;
+               goto fail;
+       }
+       converted_size_bytes -= 2;
+
+       state->data =
+           talloc_zero_array(state, uint8_t, 12 + converted_size_bytes);
+       if (state->data == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       if (replace) {
+               SCVAL(state->data, 0, 1);
+       }
+
+       SIVAL(state->data, 8, converted_size_bytes);
+       memcpy(state->data + 12, converted_str, converted_size_bytes);
+
+       TALLOC_FREE(converted_str);
+
+       subreq = cli_setpathinfo_send(
+           state, ev, cli, SMB_FILE_RENAME_INFORMATION, fname_src, state->data,
+           talloc_get_size(state->data));
+       if (tevent_req_nomem(subreq, req)) {
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+       tevent_req_set_callback(subreq, cli_smb1_rename_done, req);
+       return req;
+
+fail:
+       TALLOC_FREE(converted_str);
+       tevent_req_nterror(req, status);
+       return tevent_req_post(req, ev);
+}
+
+static void cli_smb1_rename_done(struct tevent_req *subreq)
+{
+       NTSTATUS status = cli_setpathinfo_recv(subreq);
+       tevent_req_simple_finish_ntstatus(subreq, status);
 }
 
 static void cli_cifs_rename_done(struct tevent_req *subreq);