summaryrefslogtreecommitdiff
path: root/source3/torture
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2019-07-29 17:14:00 +0200
committerJeremy Allison <jra@samba.org>2019-07-30 22:37:25 +0000
commit57695ad44bf10d6ae1dc466551358bd826dac923 (patch)
tree6c01421f185f79ec1fdc6ae04a2a85bb5f87cdc0 /source3/torture
parentea7708d8c7fa674111ccea58b3cd0757765c702a (diff)
downloadsamba-57695ad44bf10d6ae1dc466551358bd826dac923.tar.gz
torture3: Add oplock5 kernel-oplock test
Show that the current smb1 server does not properly retry a nonblocking open of a kernel-oplocked file Bug: https://bugzilla.samba.org/show_bug.cgi?id=14060 Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/torture')
-rw-r--r--source3/torture/torture.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/source3/torture/torture.c b/source3/torture/torture.c
index 2cb32efea46..17020328eaf 100644
--- a/source3/torture/torture.c
+++ b/source3/torture/torture.c
@@ -45,6 +45,7 @@
#include "lib/util/base64.h"
#include "lib/util/time.h"
#include "lib/gencache.h"
+#include "lib/util/sys_rw.h"
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
@@ -4801,6 +4802,274 @@ static void oplock4_got_open(struct tevent_req *req)
}
}
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+
+struct oplock5_state {
+ int pipe_down_fd;
+};
+
+/*
+ * Async open the file that has a kernel oplock, do an echo to get
+ * that 100% across, close the file to signal to the child fd that the
+ * oplock can be dropped, wait for the open reply.
+ */
+
+static void oplock5_opened(struct tevent_req *subreq);
+static void oplock5_pong(struct tevent_req *subreq);
+static void oplock5_timedout(struct tevent_req *subreq);
+
+static struct tevent_req *oplock5_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct cli_state *cli,
+ const char *fname,
+ int pipe_down_fd)
+{
+ struct tevent_req *req = NULL, *subreq = NULL;
+ struct oplock5_state *state = NULL;
+ static uint8_t data = 0;
+
+ req = tevent_req_create(mem_ctx, &state, struct oplock5_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->pipe_down_fd = pipe_down_fd;
+
+ subreq = cli_ntcreate_send(
+ state,
+ ev,
+ cli,
+ fname,
+ 0, /* CreatFlags */
+ SEC_FILE_READ_DATA, /* DesiredAccess */
+ FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
+ FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ FILE_NON_DIRECTORY_FILE, /* CreateOptions */
+ 0, /* Impersonation */
+ 0); /* SecurityFlags */
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_opened, req);
+
+ subreq = cli_echo_send(
+ state,
+ ev,
+ cli,
+ 1,
+ (DATA_BLOB) { .data = &data, .length = sizeof(data) });
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_pong, req);
+
+ subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(20, 0));
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, oplock5_timedout, req);
+
+ return req;
+}
+
+static void oplock5_opened(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ NTSTATUS status;
+ uint16_t fnum;
+
+ status = cli_ntcreate_recv(subreq, &fnum, NULL);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static void oplock5_pong(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct oplock5_state *state = tevent_req_data(
+ req, struct oplock5_state);
+ NTSTATUS status;
+
+ status = cli_echo_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (tevent_req_nterror(req, status)) {
+ return;
+ }
+
+ close(state->pipe_down_fd);
+}
+
+static void oplock5_timedout(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ bool ok;
+
+ ok = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_oom(req);
+ return;
+ }
+ tevent_req_nterror(req, NT_STATUS_TIMEOUT);
+}
+
+static NTSTATUS oplock5_recv(struct tevent_req *req)
+{
+ return tevent_req_simple_recv_ntstatus(req);
+}
+
+static bool run_oplock5(int dummy)
+{
+ struct tevent_context *ev = NULL;
+ struct tevent_req *req = NULL;
+ struct cli_state *cli = NULL;
+ const char *fname = "oplock5.txt";
+ int pipe_down[2], pipe_up[2];
+ pid_t child_pid;
+ uint8_t c = '\0';
+ NTSTATUS status;
+ int ret;
+ bool ok;
+
+ printf("starting oplock5\n");
+
+ if (local_path == NULL) {
+ d_fprintf(stderr, "oplock5 must be given a local path via "
+ "-l <localpath>\n");
+ return false;
+ }
+
+ ret = pipe(pipe_down);
+ if (ret == -1) {
+ d_fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
+ return false;
+ }
+ ret = pipe(pipe_up);
+ if (ret == -1) {
+ d_fprintf(stderr, "pipe() failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ child_pid = fork();
+ if (child_pid == -1) {
+ d_fprintf(stderr, "fork() failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ if (child_pid == 0) {
+ char *local_file = NULL;
+ int fd;
+
+ close(pipe_down[1]);
+ close(pipe_up[0]);
+
+ local_file = talloc_asprintf(
+ talloc_tos(), "%s/%s", local_path, fname);
+ if (local_file == 0) {
+ c = 1;
+ goto do_write;
+ }
+ fd = open(local_file, O_RDWR|O_CREAT, 0644);
+ if (fd == -1) {
+ d_fprintf(stderr,
+ "open(%s) in child failed: %s\n",
+ local_file,
+ strerror(errno));
+ c = 2;
+ goto do_write;
+ }
+
+ signal(SIGIO, SIG_IGN);
+
+ ret = fcntl(fd, F_SETLEASE, F_WRLCK);
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "SETLEASE in child failed: %s\n",
+ strerror(errno));
+ c = 3;
+ goto do_write;
+ }
+
+ do_write:
+ ret = sys_write(pipe_up[1], &c, sizeof(c));
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "sys_write failed: %s\n",
+ strerror(errno));
+ exit(4);
+ }
+ ret = sys_read(pipe_down[0], &c, sizeof(c));
+ if (ret == -1) {
+ d_fprintf(stderr,
+ "sys_read failed: %s\n",
+ strerror(errno));
+ exit(5);
+ }
+ exit(0);
+ }
+
+ close(pipe_up[1]);
+ close(pipe_down[0]);
+
+ ret = sys_read(pipe_up[0], &c, sizeof(c));
+ if (ret != 1) {
+ d_fprintf(stderr,
+ "sys_read failed: %s\n",
+ strerror(errno));
+ return false;
+ }
+ if (c != 0) {
+ d_fprintf(stderr, "got error code %"PRIu8"\n", c);
+ return false;
+ }
+
+ ok = torture_open_connection(&cli, 0);
+ if (!ok) {
+ d_fprintf(stderr, "torture_open_connection failed\n");
+ return false;
+ }
+
+ ev = samba_tevent_context_init(talloc_tos());
+ if (ev == NULL) {
+ d_fprintf(stderr, "samba_tevent_context_init failed\n");
+ return false;
+ }
+
+ req = oplock5_send(ev, ev, cli, fname, pipe_down[1]);
+ if (req == NULL) {
+ d_fprintf(stderr, "oplock5_send failed\n");
+ return false;
+ }
+
+ ok = tevent_req_poll_ntstatus(req, ev, &status);
+ if (!ok) {
+ d_fprintf(stderr,
+ "tevent_req_poll_ntstatus failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ status = oplock5_recv(req);
+ TALLOC_FREE(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "oplock5 failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
+
/*
Test delete on close semantics.
*/
@@ -13547,6 +13816,10 @@ static struct {
.fn = run_oplock4,
},
{
+ .name = "OPLOCK5",
+ .fn = run_oplock5,
+ },
+ {
.name = "DIR",
.fn = run_dirtest,
},