[PATCH] smbd: Fix snapshot query on shares with DFS enabled
Jeremy Allison
jra at samba.org
Tue Aug 16 22:29:36 UTC 2016
On Tue, Aug 16, 2016 at 03:24:48PM -0700, Jeremy Allison wrote:
> On Tue, Aug 16, 2016 at 08:40:15AM -0700, Jeremy Allison wrote:
> > On Tue, Aug 16, 2016 at 08:05:55AM +0300, Uri Simchoni wrote:
> > > On 08/16/2016 02:44 AM, Jeremy Allison wrote:
> > > >>
> > > >> I will try to take a closer look tomorrow, maybe that can be easily
> > > >> handled in the shadowcopy2 module. The backport should probably wait
> > > >> until this is resolved
> > > >
> > > > We should probably add a smbclient-based torture test
> > > > for both file and directory inside source3/script/tests/test_shadow_copy.sh
> > > > once this gets fixed to make sure we don't regress.
> > > >
> > > Looking again at the shadow_copy2 test, it's a great step forward vs no
> > > test, but it is lacking in some areas.
> > >
> > > The current test list previous versions of a file, which queries
> > > snapshot file information.
> > > a. Not sure it's complete enough - maybe successfully reading the file
> > > will be safer
> > > b. It does not handle directories at all
> > > c. It only runs on SMB1, and with respect to paths, SMB2 may behave
> > > differently. Strangely, the test seems to pass on SMB3 (adding -m SMB3),
> > > although AFAICT smbclient uses SMB1 calls to list snapshots (so it
> > > should have yielded 0 snapshots). I'll have to look into it.
> > >
> > > The SMB3 test must, IMHO, access snapshot files using TWrp create
> > > context (I don't mind doing it once I have time and it shouldn't stop
> > > any bugfixes if it's been reasonably verified that they don't break
> > > things, so it's a note to self).
> >
> > For the current change, it would be enough to test over SMB2 using
> > path with /@GMT-YYYY... appended to the end, as that's exactly
> > what the server now does.
> >
> > Further tests can add access to the Twrp context, but I don't
> > think they're a blocker for adding tests (one for file, one
> > for directory) for this specific case.
>
> OK, I now have working SMB2 FSCTL_GET_SHADOW_COPY_DATA client
> code, but for SMB2 it seems that Twrp contexts are required
> to get at the shadow copies on Windows (I can list 'em, but
> doing path/@GMT-YYYY etc. fails with OBJECT_PATH_NOT_FOUND).
>
> Shall I add code into the SMB2 client open that converts
> a /@GMT-YYYY... trailing path into a Twrp token ?
>
> What do you think ?
FYI. Here is the libsmb client code that plumbs SMB2
FSCTL_GET_SHADOW_COPY_DATA into cli_shadow_copy_data()
if you want to play with it.
Jeremy.
-------------- next part --------------
From 76059048b156928111a92a4ed07c4b42f0571974 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 16 Aug 2016 15:26:53 -0700
Subject: [PATCH 1/2] s3: libsmb: Add cli_smb2_shadow_copy_data() function that
gets shadow copy info over SMB2.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=12150
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/cli_smb2_fnum.c | 223 +++++++++++++++++++++++++++++++++++++++++
source3/libsmb/cli_smb2_fnum.h | 6 ++
2 files changed, 229 insertions(+)
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index c5b1434..6837489 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -39,6 +39,7 @@
#include "../libcli/security/security.h"
#include "lib/util_ea.h"
#include "librpc/gen_ndr/ndr_ioctl.h"
+#include "ntioctl.h"
struct smb2_hnd {
uint64_t fid_persistent;
@@ -2873,3 +2874,225 @@ NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
tevent_req_received(req);
return NT_STATUS_OK;
}
+
+/***************************************************************
+ SMB2 enum shadow copy data.
+***************************************************************/
+
+struct cli_smb2_shadow_copy_data_fnum_state {
+ struct cli_state *cli;
+ uint16_t fnum;
+ struct smb2_hnd *ph;
+ DATA_BLOB out_input_buffer;
+ DATA_BLOB out_output_buffer;
+};
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
+
+static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names)
+{
+ struct tevent_req *req, *subreq;
+ struct cli_smb2_close_fnum_state *state;
+ NTSTATUS status;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct cli_smb2_shadow_copy_data_fnum_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ state->cli = cli;
+ state->fnum = fnum;
+
+ status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
+ if (tevent_req_nterror(req, status)) {
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * TODO. Under SMB2 we should send a zero max_output_length
+ * ioctl to get the required size, then send another ioctl
+ * to get the data, but the current SMB1 implementation just
+ * does one roundtrip with a 64K buffer size. Do the same
+ * for now. JRA.
+ */
+
+ subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
+ state->cli->timeout,
+ state->cli->smb2.session,
+ state->cli->smb2.tcon,
+ state->ph->fid_persistent, /* in_fid_persistent */
+ state->ph->fid_volatile, /* in_fid_volatile */
+ FSCTL_GET_SHADOW_COPY_DATA,
+ 0, /* in_max_input_length */
+ NULL, /* in_input_buffer */
+ get_names ?
+ CLI_BUFFER_SIZE : 16, /* in_max_output_length */
+ NULL, /* in_output_buffer */
+ SMB2_IOCTL_FLAG_IS_FSCTL);
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq,
+ cli_smb2_shadow_copy_data_fnum_done,
+ req);
+
+ return req;
+}
+
+static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ NTSTATUS status;
+
+ status = smb2cli_ioctl_recv(subreq, state,
+ &state->out_input_buffer,
+ &state->out_output_buffer);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
+ req, struct cli_smb2_shadow_copy_data_fnum_state);
+ char **names = NULL;
+ uint32_t num_names = 0;
+ uint32_t num_names_returned = 0;
+ uint32_t dlength = 0;
+ uint32_t i;
+ uint8_t *endp = NULL;
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (state->out_output_buffer.length < 16) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ num_names = IVAL(state->out_output_buffer.data, 0);
+ num_names_returned = IVAL(state->out_output_buffer.data, 4);
+ dlength = IVAL(state->out_output_buffer.data, 8);
+
+ if (num_names > 0x7FFFFFFF) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (get_names == false) {
+ *pnum_names = (int)num_names;
+ return NT_STATUS_OK;
+ }
+ if (num_names != num_names_returned) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (dlength + 12 < 12) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (dlength + 12 > state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (state->out_output_buffer.length +
+ (2 * sizeof(SHADOW_COPY_LABEL)) <
+ state->out_output_buffer.length) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ names = talloc_array(mem_ctx, char *, num_names_returned);
+ if (names == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ endp = state->out_output_buffer.data +
+ state->out_output_buffer.length;
+
+ for (i=0; i<num_names_returned; i++) {
+ bool ret;
+ uint8_t *src;
+ size_t converted_size;
+
+ src = state->out_output_buffer.data + 12 +
+ (i * 2 * sizeof(SHADOW_COPY_LABEL));
+
+ if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ ret = convert_string_talloc(
+ names, CH_UTF16LE, CH_UNIX,
+ src, 2 * sizeof(SHADOW_COPY_LABEL),
+ &names[i], &converted_size);
+ if (!ret) {
+ TALLOC_FREE(names);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+ *pnum_names = num_names;
+ *pnames = names;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
+
+ if (smbXcli_conn_has_async_calls(cli->conn)) {
+ /*
+ * Can't use sync call while an async call is in flight
+ */
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+ req = cli_smb2_shadow_copy_data_fnum_send(frame,
+ ev,
+ cli,
+ fnum,
+ get_names);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll_ntstatus(req, ev, &status)) {
+ goto fail;
+ }
+ status = cli_smb2_shadow_copy_data_fnum_recv(req,
+ mem_ctx,
+ get_names,
+ pnames,
+ pnum_names);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
index ceb5629..0436c68 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -184,4 +184,10 @@ struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
off_t size, off_t src_offset, off_t dst_offset,
int (*splice_cb)(off_t n, void *priv), void *priv);
NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written);
+NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
+ struct cli_state *cli,
+ uint16_t fnum,
+ bool get_names,
+ char ***pnames,
+ int *pnum_names);
#endif /* __SMB2CLI_FNUM_H__ */
--
2.8.0.rc3.226.g39d4020
From 85f3f173880d136f3e104728fd69fde85546f399 Mon Sep 17 00:00:00 2001
From: Jeremy Allison <jra at samba.org>
Date: Tue, 16 Aug 2016 15:27:55 -0700
Subject: [PATCH 2/2] s3: libsmb: Plumb new SMB2 shadow copy call into
cli_shadow_copy_data().
https://bugzilla.samba.org/show_bug.cgi?id=12150
Signed-off-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/clifile.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c
index 684f263..4f9f2eb 100644
--- a/source3/libsmb/clifile.c
+++ b/source3/libsmb/clifile.c
@@ -5989,11 +5989,22 @@ NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli,
uint16_t fnum, bool get_names,
char ***pnames, int *pnum_names)
{
- TALLOC_CTX *frame = talloc_stackframe();
+ TALLOC_CTX *frame = NULL;
struct tevent_context *ev;
struct tevent_req *req;
NTSTATUS status = NT_STATUS_NO_MEMORY;
+ if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ return cli_smb2_shadow_copy_data(mem_ctx,
+ cli,
+ fnum,
+ get_names,
+ pnames,
+ pnum_names);
+ }
+
+ frame = talloc_stackframe();
+
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
* Can't use sync call while an async call is in flight
--
2.8.0.rc3.226.g39d4020
More information about the samba-technical
mailing list