s4:torture/smb2: add v2 lease requests
[nivanova/samba-autobuild/.git] / source4 / torture / smb2 / lease.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 leases
5
6    Copyright (C) Zachary Loafman 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include <tevent.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
29 #define CHECK_VAL(v, correct) do { \
30         if ((v) != (correct)) { \
31                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
32                                 __location__, #v, (int)(v), (int)(correct)); \
33                 ret = false; \
34         }} while (0)
35
36 #define CHECK_STATUS(status, correct) do { \
37         if (!NT_STATUS_EQUAL(status, correct)) { \
38                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
39                        nt_errstr(status), nt_errstr(correct)); \
40                 ret = false; \
41                 goto done; \
42         }} while (0)
43
44 #define CHECK_CREATED(__io, __created, __attribute)                     \
45         do {                                                            \
46                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
47                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
48                 CHECK_VAL((__io)->out.size, 0);                         \
49                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
50                 CHECK_VAL((__io)->out.reserved2, 0);                    \
51         } while(0)
52
53 #define CHECK_LEASE(__io, __state, __oplevel, __key)                    \
54         do {                                                            \
55                 if (__oplevel) {                                        \
56                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
57                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
58                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
59                         CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
60                 } else {                                                \
61                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
62                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
63                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
64                         CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
65                 }                                                       \
66                                                                         \
67                 CHECK_VAL((__io)->out.lease_response.lease_flags, 0);   \
68                 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
69         } while(0)
70
71 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags)        \
72         do {                                                            \
73                 if (__oplevel) {                                        \
74                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
75                         CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
76                         CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
77                         CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
78                 } else {                                                \
79                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
80                         CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
81                         CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
82                         CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
83                 }                                                       \
84                                                                         \
85                 CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
86                 CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
87         } while(0)
88
89 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
90 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
91 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
92 static const uint64_t LEASE4 = 0xBAD0FFEDD00DF00Dull;
93
94 #define NREQUEST_RESULTS 8
95 static const char *request_results[NREQUEST_RESULTS][2] = {
96         { "", "" },
97         { "R", "R" },
98         { "H", "" },
99         { "W", "" },
100         { "RH", "RH" },
101         { "RW", "RW" },
102         { "HW", "" },
103         { "RHW", "RHW" },
104 };
105
106 static bool test_lease_request(struct torture_context *tctx,
107                                struct smb2_tree *tree)
108 {
109         TALLOC_CTX *mem_ctx = talloc_new(tctx);
110         struct smb2_create io;
111         struct smb2_lease ls;
112         struct smb2_handle h1, h2;
113         NTSTATUS status;
114         const char *fname = "lease.dat";
115         const char *fname2 = "lease2.dat";
116         const char *sname = "lease.dat:stream";
117         const char *dname = "lease.dir";
118         bool ret = true;
119         int i;
120
121         smb2_util_unlink(tree, fname);
122         smb2_util_unlink(tree, fname2);
123         smb2_util_rmdir(tree, dname);
124
125         /* Win7 is happy to grant RHW leases on files. */
126         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
127         status = smb2_create(tree, mem_ctx, &io);
128         CHECK_STATUS(status, NT_STATUS_OK);
129         h1 = io.out.file.handle;
130         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
131         CHECK_LEASE(&io, "RHW", true, LEASE1);
132
133         /* But will reject leases on directories. */
134         smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
135         status = smb2_create(tree, mem_ctx, &io);
136         CHECK_STATUS(status, NT_STATUS_OK);
137         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
138         CHECK_LEASE(&io, "", false, 0);
139         smb2_util_close(tree, io.out.file.handle);
140
141         /* Also rejects multiple files leased under the same key. */
142         smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
143         status = smb2_create(tree, mem_ctx, &io);
144         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
145
146         /* And grants leases on streams (with separate leasekey). */
147         smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW"));
148         status = smb2_create(tree, mem_ctx, &io);
149         h2 = io.out.file.handle;
150         CHECK_STATUS(status, NT_STATUS_OK);
151         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
152         CHECK_LEASE(&io, "RHW", true, LEASE2);
153         smb2_util_close(tree, h2);
154
155         smb2_util_close(tree, h1);
156
157         /* Now see what combos are actually granted. */
158         for (i = 0; i < NREQUEST_RESULTS; i++) {
159                 torture_comment(tctx, "Requesting lease type %s(%x),"
160                     " expecting %s(%x)\n",
161                     request_results[i][0], smb2_util_lease_state(request_results[i][0]),
162                     request_results[i][1], smb2_util_lease_state(request_results[i][1]));
163                 smb2_lease_create(&io, &ls, false, fname, LEASE1,
164                     smb2_util_lease_state(request_results[i][0]));
165                 status = smb2_create(tree, mem_ctx, &io);
166                 h2 = io.out.file.handle;
167                 CHECK_STATUS(status, NT_STATUS_OK);
168                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
169                 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
170                 smb2_util_close(tree, io.out.file.handle);
171         }
172
173  done:
174         smb2_util_close(tree, h1);
175         smb2_util_close(tree, h2);
176
177         smb2_util_unlink(tree, fname);
178         smb2_util_unlink(tree, fname2);
179         smb2_util_rmdir(tree, dname);
180
181         talloc_free(mem_ctx);
182
183         return ret;
184 }
185
186 static bool test_lease_upgrade(struct torture_context *tctx,
187                                struct smb2_tree *tree)
188 {
189         TALLOC_CTX *mem_ctx = talloc_new(tctx);
190         struct smb2_create io;
191         struct smb2_lease ls;
192         struct smb2_handle h, hnew;
193         NTSTATUS status;
194         const char *fname = "lease.dat";
195         bool ret = true;
196
197         smb2_util_unlink(tree, fname);
198
199         /* Grab a RH lease. */
200         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
201         status = smb2_create(tree, mem_ctx, &io);
202         CHECK_STATUS(status, NT_STATUS_OK);
203         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
204         CHECK_LEASE(&io, "RH", true, LEASE1);
205         h = io.out.file.handle;
206
207         /* Upgrades (sidegrades?) to RW leave us with an RH. */
208         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW"));
209         status = smb2_create(tree, mem_ctx, &io);
210         CHECK_STATUS(status, NT_STATUS_OK);
211         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
212         CHECK_LEASE(&io, "RH", true, LEASE1);
213         hnew = io.out.file.handle;
214
215         smb2_util_close(tree, hnew);
216
217         /* Upgrade to RHW lease. */
218         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
219         status = smb2_create(tree, mem_ctx, &io);
220         CHECK_STATUS(status, NT_STATUS_OK);
221         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
222         CHECK_LEASE(&io, "RHW", true, LEASE1);
223         hnew = io.out.file.handle;
224
225         smb2_util_close(tree, h);
226         h = hnew;
227
228         /* Attempt to downgrade - original lease state is maintained. */
229         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
230         status = smb2_create(tree, mem_ctx, &io);
231         CHECK_STATUS(status, NT_STATUS_OK);
232         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
233         CHECK_LEASE(&io, "RHW", true, LEASE1);
234         hnew = io.out.file.handle;
235
236         smb2_util_close(tree, hnew);
237
238  done:
239         smb2_util_close(tree, h);
240         smb2_util_close(tree, hnew);
241
242         smb2_util_unlink(tree, fname);
243
244         talloc_free(mem_ctx);
245
246         return ret;
247 }
248
249 /**
250  * upgrade2 test.
251  * full matrix of lease upgrade combinations
252  */
253 struct lease_upgrade2_test {
254         const char *initial;
255         const char *upgrade_to;
256         const char *expected;
257 };
258
259 #define NUM_LEASE_TYPES 5
260 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
261 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
262         { "", "", "" },
263         { "", "R", "R" },
264         { "", "RH", "RH" },
265         { "", "RW", "RW" },
266         { "", "RWH", "RWH" },
267
268         { "R", "", "R" },
269         { "R", "R", "R" },
270         { "R", "RH", "RH" },
271         { "R", "RW", "RW" },
272         { "R", "RWH", "RWH" },
273
274         { "RH", "", "RH" },
275         { "RH", "R", "RH" },
276         { "RH", "RH", "RH" },
277         { "RH", "RW", "RH" },
278         { "RH", "RWH", "RWH" },
279
280         { "RW", "", "RW" },
281         { "RW", "R", "RW" },
282         { "RW", "RH", "RW" },
283         { "RW", "RW", "RW" },
284         { "RW", "RWH", "RWH" },
285
286         { "RWH", "", "RWH" },
287         { "RWH", "R", "RWH" },
288         { "RWH", "RH", "RWH" },
289         { "RWH", "RW", "RWH" },
290         { "RWH", "RWH", "RWH" },
291 };
292
293 static bool test_lease_upgrade2(struct torture_context *tctx,
294                                 struct smb2_tree *tree)
295 {
296         TALLOC_CTX *mem_ctx = talloc_new(tctx);
297         struct smb2_handle h, hnew;
298         NTSTATUS status;
299         struct smb2_create io;
300         struct smb2_lease ls;
301         const char *fname = "lease.dat";
302         bool ret = true;
303         int i;
304
305         for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
306                 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
307
308                 smb2_util_unlink(tree, fname);
309
310                 /* Grab a lease. */
311                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
312                 status = smb2_create(tree, mem_ctx, &io);
313                 CHECK_STATUS(status, NT_STATUS_OK);
314                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
315                 CHECK_LEASE(&io, t.initial, true, LEASE1);
316                 h = io.out.file.handle;
317
318                 /* Upgrade. */
319                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
320                 status = smb2_create(tree, mem_ctx, &io);
321                 CHECK_STATUS(status, NT_STATUS_OK);
322                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
323                 CHECK_LEASE(&io, t.expected, true, LEASE1);
324                 hnew = io.out.file.handle;
325
326                 smb2_util_close(tree, hnew);
327                 smb2_util_close(tree, h);
328         }
329
330  done:
331         smb2_util_close(tree, h);
332         smb2_util_close(tree, hnew);
333
334         smb2_util_unlink(tree, fname);
335
336         talloc_free(mem_ctx);
337
338         return ret;
339 }
340
341
342 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key)             \
343         do {                                                            \
344                 CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state));     \
345                 CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \
346                 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
347                 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
348         } while(0)
349
350 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key)                    \
351         do {                                                            \
352                 CHECK_VAL((__lba)->out.reserved, 0);                    \
353                 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
354                 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
355                 CHECK_VAL((__lba)->out.lease.lease_state, smb2_util_lease_state(__state)); \
356                 CHECK_VAL((__lba)->out.lease.lease_flags, 0);           \
357                 CHECK_VAL((__lba)->out.lease.lease_duration, 0);        \
358         } while(0)
359
360 static struct {
361         struct smb2_lease_break lease_break;
362         struct smb2_lease_break_ack lease_break_ack;
363         int count;
364         int failures;
365
366         struct smb2_handle oplock_handle;
367         uint8_t held_oplock_level;
368         uint8_t oplock_level;
369         int oplock_count;
370         int oplock_failures;
371 } break_info;
372
373 #define CHECK_BREAK_INFO(__oldstate, __state, __key)                    \
374         do {                                                            \
375                 CHECK_VAL(break_info.failures, 0);                      \
376                 CHECK_VAL(break_info.count, 1);                         \
377                 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
378                     (__state), (__key));                                \
379                 if (break_info.lease_break.break_flags &                \
380                     SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {        \
381                         CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
382                                               (__state), (__key));      \
383                 }                                                       \
384         } while(0)
385
386 static void torture_lease_break_callback(struct smb2_request *req)
387 {
388         NTSTATUS status;
389
390         status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
391         if (!NT_STATUS_IS_OK(status))
392                 break_info.failures++;
393
394         return;
395 }
396
397 /* a lease break request handler */
398 static bool torture_lease_handler(struct smb2_transport *transport,
399                                   const struct smb2_lease_break *lb,
400                                   void *private_data)
401 {
402         struct smb2_tree *tree = private_data;
403         struct smb2_lease_break_ack io;
404         struct smb2_request *req;
405
406         break_info.lease_break = *lb;
407         break_info.count++;
408
409         if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
410                 ZERO_STRUCT(io);
411                 io.in.lease.lease_key = lb->current_lease.lease_key;
412                 io.in.lease.lease_state = lb->new_lease_state;
413
414                 req = smb2_lease_break_ack_send(tree, &io);
415                 req->async.fn = torture_lease_break_callback;
416                 req->async.private_data = NULL;
417         }
418
419         return true;
420 }
421
422 /*
423    Timer handler function notifies the registering function that time is up
424 */
425 static void timeout_cb(struct tevent_context *ev,
426                        struct tevent_timer *te,
427                        struct timeval current_time,
428                        void *private_data)
429 {
430         bool *timesup = (bool *)private_data;
431         *timesup = true;
432         return;
433 }
434
435 /*
436    Wait a short period of time to receive a single oplock break request
437 */
438 static void torture_wait_for_lease_break(struct torture_context *tctx)
439 {
440         TALLOC_CTX *tmp_ctx = talloc_new(NULL);
441         struct tevent_timer *te = NULL;
442         struct timeval ne;
443         bool timesup = false;
444         int old_count = break_info.count;
445
446         /* Wait .1 seconds for an lease break */
447         ne = tevent_timeval_current_ofs(0, 100000);
448
449         te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
450         if (te == NULL) {
451                 torture_comment(tctx, "Failed to wait for an oplock break. "
452                                       "test results may not be accurate.");
453                 goto done;
454         }
455
456         while (!timesup && break_info.count < old_count + 1) {
457                 if (tevent_loop_once(tctx->ev) != 0) {
458                         torture_comment(tctx, "Failed to wait for an oplock "
459                                               "break. test results may not be "
460                                               "accurate.");
461                         goto done;
462                 }
463         }
464
465 done:
466         /* We don't know if the timed event fired and was freed, we received
467          * our oplock break, or some other event triggered the loop.  Thus,
468          * we create a tmp_ctx to be able to safely free/remove the timed
469          * event in all 3 cases. */
470         talloc_free(tmp_ctx);
471
472         return;
473 }
474
475 /*
476   break_results should be read as "held lease, new lease, hold broken to, new
477   grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
478   tries for RW, key1 will be broken to RH (in this case, not broken at all)
479   and key2 will be granted R.
480
481   Note: break_results only includes things that Win7 will actually grant (see
482   request_results above).
483  */
484 #define NBREAK_RESULTS 16
485 static const char *break_results[NBREAK_RESULTS][4] = {
486         {"R",   "R",    "R",    "R"},
487         {"R",   "RH",   "R",    "RH"},
488         {"R",   "RW",   "R",    "R"},
489         {"R",   "RHW",  "R",    "RH"},
490
491         {"RH",  "R",    "RH",   "R"},
492         {"RH",  "RH",   "RH",   "RH"},
493         {"RH",  "RW",   "RH",   "R"},
494         {"RH",  "RHW",  "RH",   "RH"},
495
496         {"RW",  "R",    "R",    "R"},
497         {"RW",  "RH",   "R",    "RH"},
498         {"RW",  "RW",   "R",    "R"},
499         {"RW",  "RHW",  "R",    "RH"},
500
501         {"RHW", "R",    "RH",   "R"},
502         {"RHW", "RH",   "RH",   "RH"},
503         {"RHW", "RW",   "RH",   "R"},
504         {"RHW", "RHW",  "RH",   "RH"},
505 };
506
507 static bool test_lease_break(struct torture_context *tctx,
508                                struct smb2_tree *tree)
509 {
510         TALLOC_CTX *mem_ctx = talloc_new(tctx);
511         struct smb2_create io;
512         struct smb2_lease ls;
513         struct smb2_handle h, h2, h3;
514         NTSTATUS status;
515         const char *fname = "lease.dat";
516         bool ret = true;
517         int i;
518
519         tree->session->transport->lease.handler = torture_lease_handler;
520         tree->session->transport->lease.private_data = tree;
521
522         smb2_util_unlink(tree, fname);
523
524         for (i = 0; i < NBREAK_RESULTS; i++) {
525                 const char *held = break_results[i][0];
526                 const char *contend = break_results[i][1];
527                 const char *brokento = break_results[i][2];
528                 const char *granted = break_results[i][3];
529                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
530                     "expecting break to %s(%x) and grant of %s(%x)\n",
531                     held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
532                     brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
533
534                 ZERO_STRUCT(break_info);
535
536                 /* Grab lease. */
537                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
538                 status = smb2_create(tree, mem_ctx, &io);
539                 CHECK_STATUS(status, NT_STATUS_OK);
540                 h = io.out.file.handle;
541                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
542                 CHECK_LEASE(&io, held, true, LEASE1);
543
544                 /* Possibly contend lease. */
545                 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
546                 status = smb2_create(tree, mem_ctx, &io);
547                 CHECK_STATUS(status, NT_STATUS_OK);
548                 h2 = io.out.file.handle;
549                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
550                 CHECK_LEASE(&io, granted, true, LEASE2);
551
552                 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
553                         CHECK_BREAK_INFO(held, brokento, LEASE1);
554                 } else {
555                         CHECK_VAL(break_info.count, 0);
556                         CHECK_VAL(break_info.failures, 0);
557                 }
558
559                 ZERO_STRUCT(break_info);
560
561                 /*
562                   Now verify that an attempt to upgrade LEASE1 results in no
563                   break and no change in LEASE1.
564                  */
565                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
566                 status = smb2_create(tree, mem_ctx, &io);
567                 CHECK_STATUS(status, NT_STATUS_OK);
568                 h3 = io.out.file.handle;
569                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
570                 CHECK_LEASE(&io, brokento, true, LEASE1);
571                 CHECK_VAL(break_info.count, 0);
572                 CHECK_VAL(break_info.failures, 0);
573
574                 smb2_util_close(tree, h);
575                 smb2_util_close(tree, h2);
576                 smb2_util_close(tree, h3);
577
578                 status = smb2_util_unlink(tree, fname);
579                 CHECK_STATUS(status, NT_STATUS_OK);
580         }
581
582  done:
583         smb2_util_close(tree, h);
584         smb2_util_close(tree, h2);
585
586         smb2_util_unlink(tree, fname);
587
588         talloc_free(mem_ctx);
589
590         return ret;
591 }
592
593 static void torture_oplock_break_callback(struct smb2_request *req)
594 {
595         NTSTATUS status;
596         struct smb2_break br;
597
598         ZERO_STRUCT(br);
599         status = smb2_break_recv(req, &br);
600         if (!NT_STATUS_IS_OK(status))
601                 break_info.oplock_failures++;
602
603         return;
604 }
605
606 /* a oplock break request handler */
607 static bool torture_oplock_handler(struct smb2_transport *transport,
608                                    const struct smb2_handle *handle,
609                                    uint8_t level, void *private_data)
610 {
611         struct smb2_tree *tree = private_data;
612         struct smb2_request *req;
613         struct smb2_break br;
614
615         break_info.oplock_handle = *handle;
616         break_info.oplock_level = level;
617         break_info.oplock_count++;
618
619         ZERO_STRUCT(br);
620         br.in.file.handle = *handle;
621         br.in.oplock_level = level;
622
623         if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
624                 req = smb2_break_send(tree, &br);
625                 req->async.fn = torture_oplock_break_callback;
626                 req->async.private_data = NULL;
627         }
628         break_info.held_oplock_level = level;
629
630         return true;
631 }
632
633 #define NOPLOCK_RESULTS 12
634 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
635         {"R",   "s",    "R",    "s"},
636         {"R",   "x",    "R",    "s"},
637         {"R",   "b",    "R",    "s"},
638
639         {"RH",  "s",    "RH",   ""},
640         {"RH",  "x",    "RH",   ""},
641         {"RH",  "b",    "RH",   ""},
642
643         {"RW",  "s",    "R",    "s"},
644         {"RW",  "x",    "R",    "s"},
645         {"RW",  "b",    "R",    "s"},
646
647         {"RHW", "s",    "RH",   ""},
648         {"RHW", "x",    "RH",   ""},
649         {"RHW", "b",    "RH",   ""},
650 };
651
652 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
653         {"s",   "R",    "s",    "R"},
654         {"s",   "RH",   "s",    "R"},
655         {"s",   "RW",   "s",    "R"},
656         {"s",   "RHW",  "s",    "R"},
657
658         {"x",   "R",    "s",    "R"},
659         {"x",   "RH",   "s",    "R"},
660         {"x",   "RW",   "s",    "R"},
661         {"x",   "RHW",  "s",    "R"},
662
663         {"b",   "R",    "s",    "R"},
664         {"b",   "RH",   "s",    "R"},
665         {"b",   "RW",   "s",    "R"},
666         {"b",   "RHW",  "s",    "R"},
667 };
668
669 static bool test_lease_oplock(struct torture_context *tctx,
670                               struct smb2_tree *tree)
671 {
672         TALLOC_CTX *mem_ctx = talloc_new(tctx);
673         struct smb2_create io;
674         struct smb2_lease ls;
675         struct smb2_handle h, h2;
676         NTSTATUS status;
677         const char *fname = "lease.dat";
678         bool ret = true;
679         int i;
680
681         tree->session->transport->lease.handler = torture_lease_handler;
682         tree->session->transport->lease.private_data = tree;
683         tree->session->transport->oplock.handler = torture_oplock_handler;
684         tree->session->transport->oplock.private_data = tree;
685
686         smb2_util_unlink(tree, fname);
687
688         for (i = 0; i < NOPLOCK_RESULTS; i++) {
689                 const char *held = oplock_results[i][0];
690                 const char *contend = oplock_results[i][1];
691                 const char *brokento = oplock_results[i][2];
692                 const char *granted = oplock_results[i][3];
693                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
694                     "expecting break to %s(%x) and grant of %s(%x)\n",
695                     held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
696                     brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
697
698                 ZERO_STRUCT(break_info);
699
700                 /* Grab lease. */
701                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
702                 status = smb2_create(tree, mem_ctx, &io);
703                 CHECK_STATUS(status, NT_STATUS_OK);
704                 h = io.out.file.handle;
705                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
706                 CHECK_LEASE(&io, held, true, LEASE1);
707
708                 /* Does an oplock contend the lease? */
709                 smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
710                 status = smb2_create(tree, mem_ctx, &io);
711                 CHECK_STATUS(status, NT_STATUS_OK);
712                 h2 = io.out.file.handle;
713                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
714                 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
715                 break_info.held_oplock_level = io.out.oplock_level;
716
717                 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
718                         CHECK_BREAK_INFO(held, brokento, LEASE1);
719                 } else {
720                         CHECK_VAL(break_info.count, 0);
721                         CHECK_VAL(break_info.failures, 0);
722                 }
723
724                 smb2_util_close(tree, h);
725                 smb2_util_close(tree, h2);
726
727                 status = smb2_util_unlink(tree, fname);
728                 CHECK_STATUS(status, NT_STATUS_OK);
729         }
730
731         for (i = 0; i < NOPLOCK_RESULTS; i++) {
732                 const char *held = oplock_results_2[i][0];
733                 const char *contend = oplock_results_2[i][1];
734                 const char *brokento = oplock_results_2[i][2];
735                 const char *granted = oplock_results_2[i][3];
736                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
737                     "expecting break to %s(%x) and grant of %s(%x)\n",
738                     held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
739                     brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
740
741                 ZERO_STRUCT(break_info);
742
743                 /* Grab an oplock. */
744                 smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
745                 status = smb2_create(tree, mem_ctx, &io);
746                 CHECK_STATUS(status, NT_STATUS_OK);
747                 h = io.out.file.handle;
748                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
749                 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
750                 break_info.held_oplock_level = io.out.oplock_level;
751
752                 /* Grab lease. */
753                 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
754                 status = smb2_create(tree, mem_ctx, &io);
755                 CHECK_STATUS(status, NT_STATUS_OK);
756                 h2 = io.out.file.handle;
757                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
758                 CHECK_LEASE(&io, granted, true, LEASE1);
759
760                 if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
761                         CHECK_VAL(break_info.oplock_count, 1);
762                         CHECK_VAL(break_info.oplock_failures, 0);
763                         CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento));
764                         break_info.held_oplock_level = break_info.oplock_level;
765                 } else {
766                         CHECK_VAL(break_info.oplock_count, 0);
767                         CHECK_VAL(break_info.oplock_failures, 0);
768                 }
769
770                 smb2_util_close(tree, h);
771                 smb2_util_close(tree, h2);
772
773                 status = smb2_util_unlink(tree, fname);
774                 CHECK_STATUS(status, NT_STATUS_OK);
775         }
776
777  done:
778         smb2_util_close(tree, h);
779         smb2_util_close(tree, h2);
780
781         smb2_util_unlink(tree, fname);
782
783         talloc_free(mem_ctx);
784
785         return ret;
786 }
787
788 static bool test_lease_multibreak(struct torture_context *tctx,
789                                   struct smb2_tree *tree)
790 {
791         TALLOC_CTX *mem_ctx = talloc_new(tctx);
792         struct smb2_create io;
793         struct smb2_lease ls;
794         struct smb2_handle h, h2, h3;
795         struct smb2_write w;
796         NTSTATUS status;
797         const char *fname = "lease.dat";
798         bool ret = true;
799
800         tree->session->transport->lease.handler = torture_lease_handler;
801         tree->session->transport->lease.private_data = tree;
802         tree->session->transport->oplock.handler = torture_oplock_handler;
803         tree->session->transport->oplock.private_data = tree;
804
805         smb2_util_unlink(tree, fname);
806
807         ZERO_STRUCT(break_info);
808
809         /* Grab lease, upgrade to RHW .. */
810         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
811         status = smb2_create(tree, mem_ctx, &io);
812         CHECK_STATUS(status, NT_STATUS_OK);
813         h = io.out.file.handle;
814         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
815         CHECK_LEASE(&io, "RH", true, LEASE1);
816
817         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
818         status = smb2_create(tree, mem_ctx, &io);
819         CHECK_STATUS(status, NT_STATUS_OK);
820         h2 = io.out.file.handle;
821         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
822         CHECK_LEASE(&io, "RHW", true, LEASE1);
823
824         /* Contend with LEASE2. */
825         smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
826         status = smb2_create(tree, mem_ctx, &io);
827         CHECK_STATUS(status, NT_STATUS_OK);
828         h3 = io.out.file.handle;
829         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
830         CHECK_LEASE(&io, "RH", true, LEASE2);
831
832         /* Verify that we were only sent one break. */
833         CHECK_BREAK_INFO("RHW", "RH", LEASE1);
834
835         /* Drop LEASE1 / LEASE2 */
836         status = smb2_util_close(tree, h);
837         CHECK_STATUS(status, NT_STATUS_OK);
838         status = smb2_util_close(tree, h2);
839         CHECK_STATUS(status, NT_STATUS_OK);
840         status = smb2_util_close(tree, h3);
841         CHECK_STATUS(status, NT_STATUS_OK);
842
843         ZERO_STRUCT(break_info);
844
845         /* Grab an R lease. */
846         smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
847         status = smb2_create(tree, mem_ctx, &io);
848         CHECK_STATUS(status, NT_STATUS_OK);
849         h = io.out.file.handle;
850         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
851         CHECK_LEASE(&io, "R", true, LEASE1);
852
853         /* Grab a level-II oplock. */
854         smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
855         status = smb2_create(tree, mem_ctx, &io);
856         CHECK_STATUS(status, NT_STATUS_OK);
857         h2 = io.out.file.handle;
858         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
859         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
860         break_info.held_oplock_level = io.out.oplock_level;
861
862         /* Verify no breaks. */
863         CHECK_VAL(break_info.count, 0);
864         CHECK_VAL(break_info.failures, 0);
865
866         /* Open for truncate, force a break. */
867         smb2_generic_create(&io, NULL, false, fname,
868             NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
869         status = smb2_create(tree, mem_ctx, &io);
870         CHECK_STATUS(status, NT_STATUS_OK);
871         h3 = io.out.file.handle;
872         CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
873         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
874         break_info.held_oplock_level = io.out.oplock_level;
875
876         /* Sleep, use a write to clear the recv queue. */
877         smb_msleep(250);
878         ZERO_STRUCT(w);
879         w.in.file.handle = h3;
880         w.in.offset      = 0;
881         w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
882         memset(w.in.data.data, 'o', w.in.data.length);
883         status = smb2_write(tree, &w);
884         CHECK_STATUS(status, NT_STATUS_OK);
885
886         /* Verify one oplock break, one lease break. */
887         CHECK_VAL(break_info.oplock_count, 1);
888         CHECK_VAL(break_info.oplock_failures, 0);
889         CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(""));
890         CHECK_BREAK_INFO("R", "", LEASE1);
891
892  done:
893         smb2_util_close(tree, h);
894         smb2_util_close(tree, h2);
895         smb2_util_close(tree, h3);
896
897         smb2_util_unlink(tree, fname);
898
899         talloc_free(mem_ctx);
900
901         return ret;
902 }
903
904 static bool test_lease_v2_request(struct torture_context *tctx,
905                                   struct smb2_tree *tree)
906 {
907         TALLOC_CTX *mem_ctx = talloc_new(tctx);
908         struct smb2_create io;
909         struct smb2_lease ls;
910         struct smb2_handle h1, h2, h3, h4, h5;
911         struct smb2_write w;
912         NTSTATUS status;
913         const char *fname = "lease.dat";
914         const char *dname = "lease.dir";
915         const char *dnamefname = "lease.dir\\lease.dat";
916         const char *dnamefname2 = "lease.dir\\lease2.dat";
917         bool ret = true;
918
919         smb2_util_unlink(tree, fname);
920         smb2_deltree(tree, dname);
921
922         tree->session->transport->lease.handler = torture_lease_handler;
923         tree->session->transport->lease.private_data = tree;
924         tree->session->transport->oplock.handler = torture_oplock_handler;
925         tree->session->transport->oplock.private_data = tree;
926
927         ZERO_STRUCT(break_info);
928
929         ZERO_STRUCT(io);
930         smb2_lease_v2_create_share(&io, &ls, false, fname,
931                                    smb2_util_share_access("RWD"),
932                                    LEASE1, NULL,
933                                    smb2_util_lease_state("RHW"),
934                                    0);
935
936         status = smb2_create(tree, mem_ctx, &io);
937         CHECK_STATUS(status, NT_STATUS_OK);
938         h1 = io.out.file.handle;
939         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
940         CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0);
941
942         ZERO_STRUCT(io);
943         smb2_lease_v2_create_share(&io, &ls, true, dname,
944                                    smb2_util_share_access("RWD"),
945                                    LEASE2, NULL,
946                                    smb2_util_lease_state("RHW"),
947                                    0);
948         status = smb2_create(tree, mem_ctx, &io);
949         CHECK_STATUS(status, NT_STATUS_OK);
950         h2 = io.out.file.handle;
951         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
952         CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
953
954         ZERO_STRUCT(io);
955         smb2_lease_v2_create_share(&io, &ls, false, dnamefname,
956                                    smb2_util_share_access("RWD"),
957                                    LEASE3, &LEASE2,
958                                    smb2_util_lease_state("RHW"),
959                                    0);
960         status = smb2_create(tree, mem_ctx, &io);
961         CHECK_STATUS(status, NT_STATUS_OK);
962         h3 = io.out.file.handle;
963         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
964         CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
965                        SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET);
966
967         torture_wait_for_lease_break(tctx);
968         CHECK_VAL(break_info.count, 0);
969         CHECK_VAL(break_info.failures, 0);
970
971         ZERO_STRUCT(io);
972         smb2_lease_v2_create_share(&io, &ls, false, dnamefname2,
973                                    smb2_util_share_access("RWD"),
974                                    LEASE4, NULL,
975                                    smb2_util_lease_state("RHW"),
976                                    0);
977         status = smb2_create(tree, mem_ctx, &io);
978         CHECK_STATUS(status, NT_STATUS_OK);
979         h4 = io.out.file.handle;
980         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
981         CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0);
982
983         torture_wait_for_lease_break(tctx);
984         torture_wait_for_lease_break(tctx);
985         CHECK_BREAK_INFO("RH", "", LEASE2);
986         torture_wait_for_lease_break(tctx);
987
988         ZERO_STRUCT(break_info);
989
990         ZERO_STRUCT(io);
991         smb2_lease_v2_create_share(&io, &ls, true, dname,
992                                    smb2_util_share_access("RWD"),
993                                    LEASE2, NULL,
994                                    smb2_util_lease_state("RHW"),
995                                    0);
996         io.in.create_disposition = NTCREATEX_DISP_OPEN;
997         status = smb2_create(tree, mem_ctx, &io);
998         CHECK_STATUS(status, NT_STATUS_OK);
999         h5 = io.out.file.handle;
1000         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY);
1001         CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
1002         smb2_util_close(tree, h5);
1003
1004         ZERO_STRUCT(w);
1005         w.in.file.handle = h4;
1006         w.in.offset      = 0;
1007         w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
1008         memset(w.in.data.data, 'o', w.in.data.length);
1009         status = smb2_write(tree, &w);
1010         CHECK_STATUS(status, NT_STATUS_OK);
1011
1012         smb_msleep(2000);
1013         torture_wait_for_lease_break(tctx);
1014         CHECK_VAL(break_info.count, 0);
1015         CHECK_VAL(break_info.failures, 0);
1016
1017         smb2_util_close(tree, h4);
1018         torture_wait_for_lease_break(tctx);
1019         torture_wait_for_lease_break(tctx);
1020         CHECK_BREAK_INFO("RH", "", LEASE2);
1021         torture_wait_for_lease_break(tctx);
1022
1023  done:
1024         smb2_util_close(tree, h1);
1025         smb2_util_close(tree, h2);
1026         smb2_util_close(tree, h3);
1027         smb2_util_close(tree, h4);
1028         smb2_util_close(tree, h5);
1029
1030         smb2_util_unlink(tree, fname);
1031         smb2_deltree(tree, dname);
1032
1033         talloc_free(mem_ctx);
1034
1035         return ret;
1036 }
1037
1038 struct torture_suite *torture_smb2_lease_init(void)
1039 {
1040         struct torture_suite *suite =
1041             torture_suite_create(talloc_autofree_context(), "lease");
1042
1043         torture_suite_add_1smb2_test(suite, "request", test_lease_request);
1044         torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
1045         torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
1046         torture_suite_add_1smb2_test(suite, "break", test_lease_break);
1047         torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
1048         torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
1049         torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
1050
1051         suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
1052
1053         return suite;
1054 }