[PATCHES] smbclient - add support for superseding the destination file on rename

Uri Simchoni uri at samba.org
Tue Mar 21 22:09:06 UTC 2017


Hi,

This smbclient patch set supports asking the server to replace an
existing destination file when renaming. This is a feature of SMB2
protocol only - haven't found a parallel in SMB1.

Review appreciated,
Uri.
-------------- next part --------------
From c54e4b20cab2bfe9374611e3b1aecf409a50c063 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 21 Mar 2017 23:02:48 +0200
Subject: [PATCH 1/4] s3: libsmb: add replace support to SMB2 rename

SMB2 rename operation supports replacing the
destination file if it exists.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/libsmb/cli_smb2_fnum.c | 9 +++++++--
 source3/libsmb/cli_smb2_fnum.h | 5 +++--
 source3/libsmb/clifile.c       | 4 +---
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 848e077..351fccf 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2010,8 +2010,9 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
 ***************************************************************/
 
 NTSTATUS cli_smb2_rename(struct cli_state *cli,
-			const char *fname_src,
-			const char *fname_dst)
+			 const char *fname_src,
+			 const char *fname_dst,
+			 bool replace)
 {
 	NTSTATUS status;
 	DATA_BLOB inbuf = data_blob_null;
@@ -2091,6 +2092,10 @@ NTSTATUS cli_smb2_rename(struct cli_state *cli,
 		goto fail;
 	}
 
+	if (replace) {
+		SCVAL(inbuf.data, 0, 1);
+	}
+
 	SIVAL(inbuf.data, 16, converted_size_bytes);
 	memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
 
diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
index 12c42a2..43e0471 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -132,8 +132,9 @@ NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
 			uint32_t sec_info,
 			const struct security_descriptor *sd);
 NTSTATUS cli_smb2_rename(struct cli_state *cli,
-			const char *fname_src,
-			const char *fname_dst);
+			 const char *fname_src,
+			 const char *fname_dst,
+			 bool replace);
 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
 			uint16_t fnum,
 			const char *ea_name,
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index 6b32bf1..e6bc40c 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1082,9 +1082,7 @@ NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fn
 	NTSTATUS status = NT_STATUS_OK;
 
 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		return cli_smb2_rename(cli,
-					fname_src,
-					fname_dst);
+		return cli_smb2_rename(cli, fname_src, fname_dst, false);
 	}
 
 	frame = talloc_stackframe();
-- 
2.9.3


From 4f9d770258671c6e051bd489a2e0b7032479812c Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 21 Mar 2017 23:13:07 +0200
Subject: [PATCH 2/4] s3: libsmb: add replace support to cli_rename()

Adds support for replacing the destination file at
the higher-level cli_rename(). This is actually supported
only by SMB2, and fails with invalid parameter with SMB1.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/client/client.c     |  2 +-
 source3/libsmb/clifile.c    | 15 +++++++++++++--
 source3/libsmb/libsmb_dir.c | 10 +++++++---
 source3/libsmb/proto.h      |  5 ++++-
 source3/torture/nbio.c      |  2 +-
 source3/torture/torture.c   | 14 +++++++-------
 6 files changed, 33 insertions(+), 15 deletions(-)

diff --git a/source3/client/client.c b/source3/client/client.c
index 226eb27..78945f9 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -3870,7 +3870,7 @@ static int cmd_rename(void)
 		return 1;
 	}
 
-	status = cli_rename(targetcli, targetsrc, targetdest);
+	status = cli_rename(targetcli, targetsrc, targetdest, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		d_printf("%s renaming files %s -> %s \n",
 			nt_errstr(status),
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index e6bc40c..8f96e04 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -1074,7 +1074,10 @@ NTSTATUS cli_rename_recv(struct tevent_req *req)
 	return tevent_req_simple_recv_ntstatus(req);
 }
 
-NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst)
+NTSTATUS cli_rename(struct cli_state *cli,
+		    const char *fname_src,
+		    const char *fname_dst,
+		    bool replace)
 {
 	TALLOC_CTX *frame = NULL;
 	struct tevent_context *ev;
@@ -1082,7 +1085,7 @@ NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fn
 	NTSTATUS status = NT_STATUS_OK;
 
 	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
-		return cli_smb2_rename(cli, fname_src, fname_dst, false);
+		return cli_smb2_rename(cli, fname_src, fname_dst, replace);
 	}
 
 	frame = talloc_stackframe();
@@ -1095,6 +1098,14 @@ NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fn
 		goto fail;
 	}
 
+	if (replace) {
+		/*
+		 * SMB1 doesn't support replace
+		 */
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto fail;
+	}
+
 	ev = samba_tevent_context_init(frame);
 	if (ev == NULL) {
 		status = NT_STATUS_NO_MEMORY;
diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c
index 8bf3c6b..4a4e084 100644
--- a/source3/libsmb/libsmb_dir.c
+++ b/source3/libsmb/libsmb_dir.c
@@ -2032,12 +2032,16 @@ SMBC_rename_ctx(SMBCCTX *ocontext,
 		return -1;
 	}
 
-	if (!NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1, targetpath2))) {
+	if (!NT_STATUS_IS_OK(
+		cli_rename(targetcli1, targetpath1, targetpath2, false))) {
 		int eno = SMBC_errno(ocontext, targetcli1);
 
 		if (eno != EEXIST ||
-		    !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)) ||
-		    !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1, targetpath2))) {
+		    !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2,
+						FILE_ATTRIBUTE_SYSTEM |
+						    FILE_ATTRIBUTE_HIDDEN)) ||
+		    !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1,
+						targetpath2, false))) {
 
 			errno = eno;
 			TALLOC_FREE(frame);
diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
index 764f3fc..57a45e3 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -330,7 +330,10 @@ struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx,
                                 const char *fname_src,
                                 const char *fname_dst);
 NTSTATUS cli_rename_recv(struct tevent_req *req);
-NTSTATUS cli_rename(struct cli_state *cli, const char *fname_src, const char *fname_dst);
+NTSTATUS cli_rename(struct cli_state *cli,
+		    const char *fname_src,
+		    const char *fname_dst,
+		    bool replace);
 struct tevent_req *cli_ntrename_send(TALLOC_CTX *mem_ctx,
                                 struct tevent_context *ev,
                                 struct cli_state *cli,
diff --git a/source3/torture/nbio.c b/source3/torture/nbio.c
index 6c87f9a..861f874 100644
--- a/source3/torture/nbio.c
+++ b/source3/torture/nbio.c
@@ -261,7 +261,7 @@ void nb_rename(const char *oldname, const char *newname)
 {
 	NTSTATUS status;
 
-	status = cli_rename(c, oldname, newname);
+	status = cli_rename(c, oldname, newname, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("ERROR: rename %s %s failed (%s)\n", 
 		       oldname, newname, nt_errstr(status));
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index dbbd072..0d9a653 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -4703,7 +4703,7 @@ static bool run_rename(int dummy)
 		return False;
 	}
 
-	status = cli_rename(cli1, fname, fname1);
+	status = cli_rename(cli1, fname, fname1, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("First rename failed (SHARE_READ) (this is correct) - %s\n", nt_errstr(status));
 	} else {
@@ -4731,7 +4731,7 @@ static bool run_rename(int dummy)
 		return False;
 	}
 
-	status = cli_rename(cli1, fname, fname1);
+	status = cli_rename(cli1, fname, fname1, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("Second rename failed (SHARE_DELETE | SHARE_READ) - this should have succeeded - %s\n", nt_errstr(status));
 		correct = False;
@@ -4778,7 +4778,7 @@ static bool run_rename(int dummy)
   }
 #endif
 
-	status = cli_rename(cli1, fname, fname1);
+	status = cli_rename(cli1, fname, fname1, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("Third rename failed (SHARE_NONE) - this should have succeeded - %s\n", nt_errstr(status));
 		correct = False;
@@ -4806,7 +4806,7 @@ static bool run_rename(int dummy)
 		return False;
 	}
 
-	status = cli_rename(cli1, fname, fname1);
+	status = cli_rename(cli1, fname, fname1, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("Fourth rename failed (SHARE_READ | SHARE_WRITE) (this is correct) - %s\n", nt_errstr(status));
 	} else {
@@ -4834,7 +4834,7 @@ static bool run_rename(int dummy)
 		return False;
 	}
 
-	status = cli_rename(cli1, fname, fname1);
+	status = cli_rename(cli1, fname, fname1, false);
 	if (!NT_STATUS_IS_OK(status)) {
 		printf("Fifth rename failed (SHARE_READ | SHARE_WRITE | SHARE_DELETE) - this should have succeeded - %s ! \n", nt_errstr(status));
 		correct = False;
@@ -5043,13 +5043,13 @@ static bool run_rename_access(int dummy)
 	 * dst directory should fail.
 	 */
 
-	status = cli_rename(cli, src, dst);
+	status = cli_rename(cli, src, dst, false);
 	if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
 		printf("rename of %s -> %s should be ACCESS denied, was %s\n",
 			src, dst, nt_errstr(status));
 		goto fail;
 	}
-	status = cli_rename(cli, dsrc, ddst);
+	status = cli_rename(cli, dsrc, ddst, false);
 	if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
 		printf("rename of %s -> %s should be ACCESS denied, was %s\n",
 			src, dst, nt_errstr(status));
-- 
2.9.3


From 0e9715a586dd3747db49623a9ec49bc50a18f3f4 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 21 Mar 2017 23:26:05 +0200
Subject: [PATCH 3/4] smbclient: add -f option to rename command

This option causes the rename to request that the
destination file / directory be replaced if it exists.

Supported only in SMB2 and higher protocol.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/client/client.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/source3/client/client.c b/source3/client/client.c
index 78945f9..d2ebea0 100644
--- a/source3/client/client.c
+++ b/source3/client/client.c
@@ -3833,10 +3833,11 @@ static int cmd_rename(void)
 	char *targetsrc;
 	char *targetdest;
         NTSTATUS status;
+	bool replace = false;
 
 	if (!next_token_talloc(ctx, &cmd_ptr,&buf,NULL) ||
 	    !next_token_talloc(ctx, &cmd_ptr,&buf2,NULL)) {
-		d_printf("rename <src> <dest>\n");
+		d_printf("rename <src> <dest> [-f]\n");
 		return 1;
 	}
 
@@ -3856,6 +3857,11 @@ static int cmd_rename(void)
 		return 1;
 	}
 
+	if (next_token_talloc(ctx, &cmd_ptr, &buf, NULL) &&
+	    strcsequal(buf, "-f")) {
+		replace = true;
+	}
+
 	status = cli_resolve_path(ctx, "", auth_info, cli, src, &targetcli,
 				  &targetsrc);
 	if (!NT_STATUS_IS_OK(status)) {
@@ -3870,7 +3876,7 @@ static int cmd_rename(void)
 		return 1;
 	}
 
-	status = cli_rename(targetcli, targetsrc, targetdest, false);
+	status = cli_rename(targetcli, targetsrc, targetdest, replace);
 	if (!NT_STATUS_IS_OK(status)) {
 		d_printf("%s renaming files %s -> %s \n",
 			nt_errstr(status),
-- 
2.9.3


From ddca64ea4472451a7b50ea9f3a0e54ebf9eba642 Mon Sep 17 00:00:00 2001
From: Uri Simchoni <uri at samba.org>
Date: Tue, 21 Mar 2017 23:56:35 +0200
Subject: [PATCH 4/4] manpages: update smbclient manpage with rename -f option

Document the -f option of the rename command.

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 docs-xml/manpages/smbclient.1.xml | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/docs-xml/manpages/smbclient.1.xml b/docs-xml/manpages/smbclient.1.xml
index c8f4e40..059e14c 100644
--- a/docs-xml/manpages/smbclient.1.xml
+++ b/docs-xml/manpages/smbclient.1.xml
@@ -1000,10 +1000,13 @@
 		</varlistentry>
 
 		<varlistentry>
-		<term>rename <old filename> <new filename></term>
+		<term>rename <old filename> <new filename> [-f]</term>
 		<listitem><para>Rename files in the current working directory on the
 		server from <replaceable>old filename</replaceable> to
-		<replaceable>new filename</replaceable>. </para></listitem>
+		<replaceable>new filename</replaceable>. The optional
+		-f switch is supported only by SMB2 protocol and beyond,
+		and allows for superseding the destination file,
+		if it exists.</para></listitem>
 		</varlistentry>
 
 		<varlistentry>
-- 
2.9.3



More information about the samba-technical mailing list