d64b0bed6dbdb7cd019e1f0f9874f978f91215b2
[kai/samba-autobuild/.git] / source4 / torture / smb2 / oplock_break_handler.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * test suite for SMB2 replay
5  *
6  * Copyright (C) Anubhav Rakshit 2014
7  * Copyright (C) Stefan Metzmacher 2014
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29 #include "oplock_break_handler.h"
30 #include "lib/events/events.h"
31
32 struct break_info break_info;
33
34 static void torture_oplock_ack_callback(struct smb2_request *req)
35 {
36         NTSTATUS status;
37
38         status = smb2_break_recv(req, &break_info.br);
39         if (!NT_STATUS_IS_OK(status)) {
40                 break_info.failures++;
41                 break_info.failure_status = status;
42         }
43 }
44
45 /**
46  * A general oplock break notification handler.  This should be used when a
47  * test expects to break from batch or exclusive to a lower level.
48  */
49
50 bool torture_oplock_ack_handler(struct smb2_transport *transport,
51                                 const struct smb2_handle *handle,
52                                 uint8_t level,
53                                 void *private_data)
54 {
55         struct smb2_tree *tree = private_data;
56         const char *name;
57         struct smb2_request *req;
58
59         ZERO_STRUCT(break_info.br);
60
61         break_info.handle       = *handle;
62         break_info.level        = level;
63         break_info.count++;
64
65         switch (level) {
66         case SMB2_OPLOCK_LEVEL_II:
67                 name = "level II";
68                 break;
69         case SMB2_OPLOCK_LEVEL_NONE:
70                 name = "none";
71                 break;
72         default:
73                 name = "unknown";
74                 break_info.failures++;
75         }
76         torture_comment(break_info.tctx,
77                         "transport[%p] Acking to %s [0x%02X] in oplock handler\n",
78                         transport, name, level);
79
80         break_info.br.in.file.handle    = *handle;
81         break_info.br.in.oplock_level   = level;
82         break_info.br.in.reserved       = 0;
83         break_info.br.in.reserved2      = 0;
84         break_info.received_transport = tree->session->transport;
85         SMB_ASSERT(tree->session->transport == transport);
86
87         req = smb2_break_send(tree, &break_info.br);
88         req->async.fn = torture_oplock_ack_callback;
89         req->async.private_data = NULL;
90
91         return true;
92 }
93
94 /**
95  * A oplock break handler designed to ignore incoming break requests.
96  * This is used when incoming oplock break requests need to be ignored
97  */
98 bool torture_oplock_ignore_handler(struct smb2_transport *transport,
99                                    const struct smb2_handle *handle,
100                                    uint8_t level, void *private_data)
101 {
102         return true;
103 }
104
105 /*
106  * Timer handler function notifies the registering function that time is up
107  */
108 static void timeout_cb(struct tevent_context *ev,
109                        struct tevent_timer *te,
110                        struct timeval current_time,
111                        void *private_data)
112 {
113         bool *timesup = (bool *)private_data;
114         *timesup = true;
115 }
116
117 /*
118  * Wait a short period of time to receive a single oplock break request
119  */
120 void torture_wait_for_oplock_break(struct torture_context *tctx)
121 {
122         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
123         struct tevent_timer *te = NULL;
124         struct timeval ne;
125         bool timesup = false;
126         int old_count = break_info.count;
127
128         /* Wait 1 second for an oplock break */
129         ne = tevent_timeval_current_ofs(0, 1000000);
130
131         te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
132         if (te == NULL) {
133                 torture_comment(tctx, "Failed to wait for an oplock break. "
134                                       "test results may not be accurate.");
135                 goto done;
136         }
137
138         while (!timesup && break_info.count < old_count + 1) {
139                 if (tevent_loop_once(tctx->ev) != 0) {
140                         torture_comment(tctx, "Failed to wait for an oplock "
141                                               "break. test results may not be "
142                                               "accurate.");
143                         goto done;
144                 }
145         }
146
147 done:
148         /* We don't know if the timed event fired and was freed, we received
149          * our oplock break, or some other event triggered the loop.  Thus,
150          * we create a tmp_ctx to be able to safely free/remove the timed
151          * event in all 3 cases.
152          */
153         talloc_free(tmp_ctx);
154 }