From deba880986b1029fa059fcfba9b2a72abf598a9b Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 4 May 2011 17:45:34 +0200 Subject: [PATCH] s3-torture: Add a test for notify upon read&x This makes sure that when a file is brought online by a read call we notify the client for FILE_NOTIFY_CHANGE_ATTRIBUTES. Autobuild-User: Volker Lendecke Autobuild-Date: Wed May 4 21:09:22 CEST 2011 on sn-devel-104 --- source3/Makefile.in | 1 + source3/torture/proto.h | 1 + source3/torture/test_notify_online.c | 293 +++++++++++++++++++++++++++++++++++ source3/torture/torture.c | 9 +- source3/wscript_build | 1 + 5 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 source3/torture/test_notify_online.c diff --git a/source3/Makefile.in b/source3/Makefile.in index c7d263670ac..bc5e76792ce 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -1228,6 +1228,7 @@ SMBTORTURE_OBJ1 = torture/torture.o torture/nbio.o torture/scanner.o torture/uta torture/nbench.o \ torture/test_async_echo.o \ torture/test_smbsock_any_connect.o \ + torture/test_notify_online.o \ torture/test_addrchange.o \ torture/test_case_insensitive.o \ torture/test_posix_append.o diff --git a/source3/torture/proto.h b/source3/torture/proto.h index a45aa153c43..e40e44c114d 100644 --- a/source3/torture/proto.h +++ b/source3/torture/proto.h @@ -87,5 +87,6 @@ bool run_nbench2(int dummy); bool run_async_echo(int dummy); bool run_smb_any_connect(int dummy); bool run_addrchange(int dummy); +bool run_notify_online(int dummy); #endif /* __TORTURE_H__ */ diff --git a/source3/torture/test_notify_online.c b/source3/torture/test_notify_online.c new file mode 100644 index 00000000000..a56cc1169a8 --- /dev/null +++ b/source3/torture/test_notify_online.c @@ -0,0 +1,293 @@ +/* + Unix SMB/CIFS implementation. + Make sure that for offline files pread and pwrite trigger a notify + Copyright (C) Volker Lendecke 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "torture/proto.h" +#include "libcli/security/security.h" +#include "lib/util/tevent_ntstatus.h" + +extern char *test_filename; + +struct notify_online_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t dnum; + const char *fname; + uint16_t fnum; + bool got_notify; +}; + +static void notify_online_opened_dir(struct tevent_req *subreq); +static void notify_online_notify_callback(struct tevent_req *subreq); +static void notify_online_opened_file(struct tevent_req *subreq); +static void notify_online_sent_read(struct tevent_req *subreq); +static void notify_online_sent_closefile(struct tevent_req *subreq); +static void notify_online_waited(struct tevent_req *subreq); +static void notify_online_sent_closedir(struct tevent_req *subreq); + +static struct tevent_req *notify_online_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, const char *dname, const char *fname) +{ + struct tevent_req *req, *subreq; + struct notify_online_state *state; + + req = tevent_req_create(mem_ctx, &state, struct notify_online_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->fname = fname; + + subreq = cli_ntcreate_send( + state, ev, cli, dname, EXTENDED_RESPONSE_REQUIRED, + SEC_FILE_READ_DATA, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, 0, 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, notify_online_opened_dir, req); + return req; +} + +static void notify_online_opened_dir(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->dnum); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + subreq = cli_notify_send(state, state->ev, state->cli, state->dnum, + 128, FILE_NOTIFY_CHANGE_ATTRIBUTES, false); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_notify_callback, req); + + subreq = cli_ntcreate_send( + state, state->ev, state->cli, state->fname, 0, + GENERIC_READ_ACCESS, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, FILE_NON_DIRECTORY_FILE, 0); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_opened_file, req); +} + +static void notify_online_notify_callback(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + uint32_t num_changes; + struct notify_change *changes; + + status = cli_notify_recv(subreq, state, &num_changes, &changes); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + if ((num_changes == 1) + && (changes[0].action == NOTIFY_ACTION_MODIFIED) + && (strcmp(changes[0].name, state->fname) == 0)) { + state->got_notify = true; + } + tevent_req_done(req); +} + +static void notify_online_opened_file(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->fnum); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + subreq = cli_read_andx_send( + state, state->ev, state->cli, state->fnum, 0, 1); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_sent_read, req); +} + +static void notify_online_sent_read(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + ssize_t received; + uint8_t *buf; + + status = cli_read_andx_recv(subreq, &received, &buf); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + subreq = cli_close_send( + state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_sent_closefile, req); +} + +static void notify_online_sent_closefile(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + subreq = tevent_wakeup_send( + state, state->ev, timeval_current_ofs(10, 0)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_waited, req); +} + +static void notify_online_waited(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + + tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + subreq = cli_close_send( + state, state->ev, state->cli, state->dnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, notify_online_sent_closedir, req); +} + +static void notify_online_sent_closedir(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } +} + +static NTSTATUS notify_online_recv(struct tevent_req *req, bool *got_notify) +{ + struct notify_online_state *state = tevent_req_data( + req, struct notify_online_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *got_notify = state->got_notify; + return NT_STATUS_OK; +} + +static NTSTATUS notify_online(struct cli_state *cli, + const char *dirname, const char *filename, + bool *got_notify) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct event_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = event_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = notify_online_send(frame, ev, cli, dirname, filename); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = notify_online_recv(req, got_notify); + fail: + TALLOC_FREE(frame); + return status; +} + +bool run_notify_online(int dummy) +{ + struct cli_state *cli; + NTSTATUS status; + char *p; + const char *dir; + const char *file; + bool got_notify = false; + + printf("Starting NOTIFY_ONLINE\n"); + + if (test_filename == NULL) { + fprintf(stderr, "<-f filename> missing\n"); + return false; + } + + if (!torture_open_connection(&cli, 0)) { + return false; + } + + p = strrchr(test_filename, '/'); + if (p != NULL) { + dir = SMB_STRNDUP(test_filename, p-test_filename); + file = SMB_STRDUP(p+1); + } else { + dir = ""; + file = test_filename; + } + + status = notify_online(cli, dir, file, &got_notify); + d_printf("notify_online returned %s (%d)\n", nt_errstr(status), + (int)got_notify); + torture_close_connection(cli); + return NT_STATUS_IS_OK(status) && got_notify; +} diff --git a/source3/torture/torture.c b/source3/torture/torture.c index 5dfcb7e97ce..fa54f605f45 100644 --- a/source3/torture/torture.c +++ b/source3/torture/torture.c @@ -58,6 +58,7 @@ static bool use_multishare_conn = False; static bool do_encrypt; static const char *local_path = NULL; static int signing_state = Undefined; +char *test_filename; bool torture_showall = False; @@ -8374,6 +8375,7 @@ static struct { { "NOTIFY-BENCH", run_notify_bench }, { "BAD-NBT-SESSION", run_bad_nbt_session }, { "SMB-ANY-CONNECT", run_smb_any_connect }, + { "NOTIFY-ONLINE", run_notify_online }, { "LOCAL-SUBSTITUTE", run_local_substitute, 0}, { "LOCAL-GENCACHE", run_local_gencache, 0}, { "LOCAL-TALLOC-DICT", run_local_talloc_dict, 0}, @@ -8467,6 +8469,7 @@ static void usage(void) printf("\t-p port\n"); printf("\t-s seed\n"); printf("\t-b unclist_filename specify multiple shares for multiple connections\n"); + printf("\t-f filename filename to test\n"); printf("\n\n"); printf("tests are:"); @@ -8544,7 +8547,8 @@ static void usage(void) fstrcpy(workgroup, lp_workgroup()); - while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ll:d:Aec:ks:b:B:")) != EOF) { + while ((opt = getopt(argc, argv, "p:hW:U:n:N:O:o:m:Ll:d:Aec:ks:b:B:f:")) + != EOF) { switch (opt) { case 'p': port_to_use = atoi(optarg); @@ -8613,6 +8617,9 @@ static void usage(void) case 'B': torture_blocksize = atoi(optarg); break; + case 'f': + test_filename = SMB_STRDUP(optarg); + break; default: printf("Unknown option %c (%d)\n", (char)opt, opt); usage(); diff --git a/source3/wscript_build b/source3/wscript_build index fbc66933461..adeff836afb 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -566,6 +566,7 @@ SMBTORTURE_SRC1 = '''torture/torture.c torture/nbio.c torture/scanner.c torture/ torture/test_addrchange.c torture/test_posix_append.c torture/test_case_insensitive.c + torture/test_notify_online.c torture/test_smbsock_any_connect.c''' SMBTORTURE_SRC = '''${SMBTORTURE_SRC1} -- 2.11.4.GIT