s4:torture/smb2: Add torture tests for lease breaks, durable opens.
[ira/wip.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 "lib/events/events.h"
24 #include "librpc/gen_ndr/security.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29
30 static inline uint32_t lease(const char *ls) {
31         uint32_t val = 0;
32         int i;
33
34         for (i = 0; i < strlen(ls); i++) {
35                 switch (ls[i]) {
36                 case 'R':
37                         val |= SMB2_LEASE_READ;
38                         break;
39                 case 'H':
40                         val |= SMB2_LEASE_HANDLE;
41                         break;
42                 case 'W':
43                         val |= SMB2_LEASE_WRITE;
44                         break;
45                 }
46         }
47
48         return val;
49 }
50
51 #define CHECK_VAL(v, correct) do { \
52         if ((v) != (correct)) { \
53                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
54                                 __location__, #v, (int)(v), (int)(correct)); \
55                 ret = false; \
56         }} while (0)
57
58 #define CHECK_STATUS(status, correct) do { \
59         if (!NT_STATUS_EQUAL(status, correct)) { \
60                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
61                        nt_errstr(status), nt_errstr(correct)); \
62                 ret = false; \
63                 goto done; \
64         }} while (0)
65
66 static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
67                                 bool dir, const char *name, uint32_t disposition,
68                                 uint32_t oplock, uint64_t leasekey,
69                                 uint32_t leasestate)
70 {
71         ZERO_STRUCT(*io);
72         io->in.security_flags           = 0x00;
73         io->in.oplock_level             = oplock;
74         io->in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
75         io->in.create_flags             = 0x00000000;
76         io->in.reserved                 = 0x00000000;
77         io->in.desired_access           = SEC_RIGHTS_FILE_ALL;
78         io->in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
79         io->in.share_access             = NTCREATEX_SHARE_ACCESS_READ |
80                                           NTCREATEX_SHARE_ACCESS_WRITE |
81                                           NTCREATEX_SHARE_ACCESS_DELETE;
82         io->in.create_disposition       = disposition;
83         io->in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
84                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
85                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
86                                           0x00200000;
87         io->in.fname                    = name;
88
89         if (dir) {
90                 io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
91                 io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
92                 io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
93                 io->in.create_disposition = NTCREATEX_DISP_CREATE;
94         }
95
96         if (ls) {
97                 ZERO_STRUCT(*ls);
98                 ls->lease_key.data[0] = leasekey;
99                 ls->lease_key.data[1] = ~leasekey;
100                 ls->lease_state = leasestate;
101                 io->in.lease_request = ls;
102         }
103 }
104
105 static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
106                               bool dir, const char *name, uint64_t leasekey,
107                               uint32_t leasestate)
108 {
109         smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
110             SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
111 }
112
113 static void smb2_oplock_create(struct smb2_create *io, const char *name,
114                                uint32_t oplock)
115 {
116         smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
117             oplock, 0, 0);
118 }
119
120 #define CHECK_CREATED(__io, __created, __attribute)                     \
121         do {                                                            \
122                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
123                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
124                 CHECK_VAL((__io)->out.size, 0);                         \
125                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
126                 CHECK_VAL((__io)->out.reserved2, 0);                    \
127         } while(0)
128
129 #define CHECK_LEASE(__io, __state, __oplevel, __key)                    \
130         do {                                                            \
131                 if (__oplevel) {                                        \
132                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
133                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
134                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
135                         CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
136                 } else {                                                \
137                         CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
138                         CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
139                         CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
140                         CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
141                 }                                                       \
142                                                                         \
143                 CHECK_VAL((__io)->out.lease_response.lease_flags, 0);   \
144                 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
145         } while(0)                                                      \
146
147 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
148 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
149 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
150
151 #define NREQUEST_RESULTS 8
152 static const char *request_results[NREQUEST_RESULTS][2] = {
153         { "", "" },
154         { "R", "R" },
155         { "H", "" },
156         { "W", "" },
157         { "RH", "RH" },
158         { "RW", "RW" },
159         { "HW", "" },
160         { "RHW", "RHW" },
161 };
162
163 static bool test_lease_request(struct torture_context *tctx,
164                                struct smb2_tree *tree)
165 {
166         TALLOC_CTX *mem_ctx = talloc_new(tctx);
167         struct smb2_create io;
168         struct smb2_lease ls;
169         struct smb2_handle h1, h2;
170         NTSTATUS status;
171         const char *fname = "lease.dat";
172         const char *fname2 = "lease2.dat";
173         const char *sname = "lease.dat:stream";
174         const char *dname = "lease.dir";
175         bool ret = true;
176         int i;
177
178         smb2_util_unlink(tree, fname);
179         smb2_util_unlink(tree, fname2);
180         smb2_util_rmdir(tree, dname);
181
182         /* Win7 is happy to grant RHW leases on files. */
183         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
184         status = smb2_create(tree, mem_ctx, &io);
185         CHECK_STATUS(status, NT_STATUS_OK);
186         h1 = io.out.file.handle;
187         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
188         CHECK_LEASE(&io, "RHW", true, LEASE1);
189
190         /* But will reject leases on directories. */
191         smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
192         status = smb2_create(tree, mem_ctx, &io);
193         CHECK_STATUS(status, NT_STATUS_OK);
194         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
195         CHECK_LEASE(&io, "", false, 0);
196         smb2_util_close(tree, io.out.file.handle);
197
198         /* Also rejects multiple files leased under the same key. */
199         smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
200         status = smb2_create(tree, mem_ctx, &io);
201         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
202
203         /* And grants leases on streams (with separate leasekey). */
204         smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
205         status = smb2_create(tree, mem_ctx, &io);
206         h2 = io.out.file.handle;
207         CHECK_STATUS(status, NT_STATUS_OK);
208         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
209         CHECK_LEASE(&io, "RHW", true, LEASE2);
210         smb2_util_close(tree, h2);
211
212         smb2_util_close(tree, h1);
213
214         /* Now see what combos are actually granted. */
215         for (i = 0; i < NREQUEST_RESULTS; i++) {
216                 torture_comment(tctx, "Requesting lease type %s(%x),"
217                     " expecting %s(%x)\n",
218                     request_results[i][0], lease(request_results[i][0]),
219                     request_results[i][1], lease(request_results[i][1]));
220                 smb2_lease_create(&io, &ls, false, fname, LEASE1,
221                     lease(request_results[i][0]));
222                 status = smb2_create(tree, mem_ctx, &io);
223                 h2 = io.out.file.handle;
224                 CHECK_STATUS(status, NT_STATUS_OK);
225                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
226                 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
227                 smb2_util_close(tree, io.out.file.handle);
228         }
229
230  done:
231         smb2_util_close(tree, h1);
232         smb2_util_close(tree, h2);
233
234         smb2_util_unlink(tree, fname);
235         smb2_util_unlink(tree, fname2);
236         smb2_util_rmdir(tree, dname);
237
238         talloc_free(mem_ctx);
239
240         return ret;
241 }
242
243 static bool test_lease_upgrade(struct torture_context *tctx,
244                                struct smb2_tree *tree)
245 {
246         TALLOC_CTX *mem_ctx = talloc_new(tctx);
247         struct smb2_create io;
248         struct smb2_lease ls;
249         struct smb2_handle h, hnew;
250         NTSTATUS status;
251         const char *fname = "lease.dat";
252         bool ret = true;
253
254         smb2_util_unlink(tree, fname);
255
256         /* Grab a RH lease. */
257         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
258         status = smb2_create(tree, mem_ctx, &io);
259         CHECK_STATUS(status, NT_STATUS_OK);
260         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
261         CHECK_LEASE(&io, "RH", true, LEASE1);
262         h = io.out.file.handle;
263
264         /* Upgrades (sidegrades?) to RW leave us with an RH. */
265         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
266         status = smb2_create(tree, mem_ctx, &io);
267         CHECK_STATUS(status, NT_STATUS_OK);
268         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
269         CHECK_LEASE(&io, "RH", true, LEASE1);
270         hnew = io.out.file.handle;
271
272         smb2_util_close(tree, hnew);
273
274         /* Upgrade to RHW lease. */
275         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
276         status = smb2_create(tree, mem_ctx, &io);
277         CHECK_STATUS(status, NT_STATUS_OK);
278         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
279         CHECK_LEASE(&io, "RHW", true, LEASE1);
280         hnew = io.out.file.handle;
281
282         smb2_util_close(tree, h);
283         h = hnew;
284
285         /* Attempt to downgrade - original lease state is maintained. */
286         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
287         status = smb2_create(tree, mem_ctx, &io);
288         CHECK_STATUS(status, NT_STATUS_OK);
289         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
290         CHECK_LEASE(&io, "RHW", true, LEASE1);
291         hnew = io.out.file.handle;
292
293         smb2_util_close(tree, hnew);
294
295  done:
296         smb2_util_close(tree, h);
297         smb2_util_close(tree, hnew);
298
299         smb2_util_unlink(tree, fname);
300
301         talloc_free(mem_ctx);
302
303         return ret;
304 }
305
306 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key)             \
307         do {                                                            \
308                 CHECK_VAL((__lb)->new_lease_state, lease(__state));     \
309                 CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
310                 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
311                 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
312         } while(0)
313
314 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key)                    \
315         do {                                                            \
316                 CHECK_VAL((__lba)->out.reserved, 0);                    \
317                 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
318                 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
319                 CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
320                 CHECK_VAL((__lba)->out.lease.lease_flags, 0);           \
321                 CHECK_VAL((__lba)->out.lease.lease_duration, 0);        \
322         } while(0)
323
324 static struct {
325         struct smb2_lease_break lease_break;
326         struct smb2_lease_break_ack lease_break_ack;
327         int count;
328         int failures;
329
330         struct smb2_handle oplock_handle;
331         int held_oplock_level;
332         int oplock_level;
333         int oplock_count;
334         int oplock_failures;
335 } break_info;
336
337 #define CHECK_BREAK_INFO(__oldstate, __state, __key)                    \
338         do {                                                            \
339                 CHECK_VAL(break_info.failures, 0);                      \
340                 CHECK_VAL(break_info.count, 1);                         \
341                 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
342                     (__state), (__key));                                \
343                 if (break_info.lease_break.break_flags &                \
344                     SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {        \
345                         CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
346                                               (__state), (__key));      \
347                 }                                                       \
348         } while(0)
349
350 static void torture_lease_break_callback(struct smb2_request *req)
351 {
352         NTSTATUS status;
353
354         status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
355         if (!NT_STATUS_IS_OK(status))
356                 break_info.failures++;
357
358         return;
359 }
360
361 /* a lease break request handler */
362 static bool torture_lease_handler(struct smb2_transport *transport,
363                                   const struct smb2_lease_break *lb,
364                                   void *private_data)
365 {
366         struct smb2_tree *tree = private_data;
367         struct smb2_lease_break_ack io;
368         struct smb2_request *req;
369
370         break_info.lease_break = *lb;
371         break_info.count++;
372
373         if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
374                 ZERO_STRUCT(io);
375                 io.in.lease.lease_key = lb->current_lease.lease_key;
376                 io.in.lease.lease_state = lb->new_lease_state;
377
378                 req = smb2_lease_break_ack_send(tree, &io);
379                 req->async.fn = torture_lease_break_callback;
380                 req->async.private_data = NULL;
381         }
382
383         return true;
384 }
385
386 /*
387   break_results should be read as "held lease, new lease, hold broken to, new
388   grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
389   tries for RW, key1 will be broken to RH (in this case, not broken at all)
390   and key2 will be granted R.
391
392   Note: break_results only includes things that Win7 will actually grant (see
393   request_results above).
394  */
395 #define NBREAK_RESULTS 16
396 static const char *break_results[NBREAK_RESULTS][4] = {
397         {"R",   "R",    "R",    "R"},
398         {"R",   "RH",   "R",    "RH"},
399         {"R",   "RW",   "R",    "R"},
400         {"R",   "RHW",  "R",    "RH"},
401
402         {"RH",  "R",    "RH",   "R"},
403         {"RH",  "RH",   "RH",   "RH"},
404         {"RH",  "RW",   "RH",   "R"},
405         {"RH",  "RHW",  "RH",   "RH"},
406
407         {"RW",  "R",    "R",    "R"},
408         {"RW",  "RH",   "R",    "RH"},
409         {"RW",  "RW",   "R",    "R"},
410         {"RW",  "RHW",  "R",    "RH"},
411
412         {"RHW", "R",    "RH",   "R"},
413         {"RHW", "RH",   "RH",   "RH"},
414         {"RHW", "RW",   "RH",   "R"},
415         {"RHW", "RHW",  "RH",   "RH"},
416 };
417
418 static bool test_lease_break(struct torture_context *tctx,
419                                struct smb2_tree *tree)
420 {
421         TALLOC_CTX *mem_ctx = talloc_new(tctx);
422         struct smb2_create io;
423         struct smb2_lease ls;
424         struct smb2_handle h, h2, h3;
425         NTSTATUS status;
426         const char *fname = "lease.dat";
427         bool ret = true;
428         int i;
429
430         tree->session->transport->lease.handler = torture_lease_handler;
431         tree->session->transport->lease.private_data = tree;
432
433         smb2_util_unlink(tree, fname);
434
435         for (i = 0; i < NBREAK_RESULTS; i++) {
436                 const char *held = break_results[i][0];
437                 const char *contend = break_results[i][1];
438                 const char *brokento = break_results[i][2];
439                 const char *granted = break_results[i][3];
440                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
441                     "expecting break to %s(%x) and grant of %s(%x)\n",
442                     held, lease(held), contend, lease(contend),
443                     brokento, lease(brokento), granted, lease(granted));
444
445                 ZERO_STRUCT(break_info);
446
447                 /* Grab lease. */
448                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
449                 status = smb2_create(tree, mem_ctx, &io);
450                 CHECK_STATUS(status, NT_STATUS_OK);
451                 h = io.out.file.handle;
452                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
453                 CHECK_LEASE(&io, held, true, LEASE1);
454
455                 /* Possibly contend lease. */
456                 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
457                 status = smb2_create(tree, mem_ctx, &io);
458                 CHECK_STATUS(status, NT_STATUS_OK);
459                 h2 = io.out.file.handle;
460                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
461                 CHECK_LEASE(&io, granted, true, LEASE2);
462
463                 if (lease(held) != lease(brokento)) {
464                         CHECK_BREAK_INFO(held, brokento, LEASE1);
465                 } else {
466                         CHECK_VAL(break_info.count, 0);
467                         CHECK_VAL(break_info.failures, 0);
468                 }
469
470                 ZERO_STRUCT(break_info);
471
472                 /*
473                   Now verify that an attempt to upgrade LEASE1 results in no
474                   break and no change in LEASE1.
475                  */
476                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
477                 status = smb2_create(tree, mem_ctx, &io);
478                 CHECK_STATUS(status, NT_STATUS_OK);
479                 h3 = io.out.file.handle;
480                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
481                 CHECK_LEASE(&io, brokento, true, LEASE1);
482                 CHECK_VAL(break_info.count, 0);
483                 CHECK_VAL(break_info.failures, 0);
484
485                 smb2_util_close(tree, h);
486                 smb2_util_close(tree, h2);
487                 smb2_util_close(tree, h3);
488
489                 status = smb2_util_unlink(tree, fname);
490                 CHECK_STATUS(status, NT_STATUS_OK);
491         }
492
493  done:
494         smb2_util_close(tree, h);
495         smb2_util_close(tree, h2);
496
497         smb2_util_unlink(tree, fname);
498
499         talloc_free(mem_ctx);
500
501         return ret;
502 }
503
504 static void torture_oplock_break_callback(struct smb2_request *req)
505 {
506         NTSTATUS status;
507         struct smb2_break br;
508
509         ZERO_STRUCT(br);
510         status = smb2_break_recv(req, &br);
511         if (!NT_STATUS_IS_OK(status))
512                 break_info.oplock_failures++;
513
514         return;
515 }
516
517 /* a oplock break request handler */
518 static bool torture_oplock_handler(struct smb2_transport *transport,
519                                    const struct smb2_handle *handle,
520                                    uint8_t level, void *private_data)
521 {
522         struct smb2_tree *tree = private_data;
523         struct smb2_request *req;
524         struct smb2_break br;
525
526         break_info.oplock_handle = *handle;
527         break_info.oplock_level = level;
528         break_info.oplock_count++;
529
530         ZERO_STRUCT(br);
531         br.in.file.handle = *handle;
532         br.in.oplock_level = level;
533
534         if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
535                 req = smb2_break_send(tree, &br);
536                 req->async.fn = torture_oplock_break_callback;
537                 req->async.private_data = NULL;
538         }
539         break_info.held_oplock_level = level;
540
541         return true;
542 }
543
544 static inline uint32_t oplock(const char *op) {
545         uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
546         int i;
547
548         for (i = 0; i < strlen(op); i++) {
549                 switch (op[i]) {
550                 case 's':
551                         return SMB2_OPLOCK_LEVEL_II;
552                 case 'x':
553                         return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
554                 case 'b':
555                         return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
556                 default:
557                         continue;
558                 }
559         }
560
561         return val;
562 }
563
564 #define NOPLOCK_RESULTS 12
565 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
566         {"R",   "s",    "R",    "s"},
567         {"R",   "x",    "R",    "s"},
568         {"R",   "b",    "R",    "s"},
569
570         {"RH",  "s",    "RH",   ""},
571         {"RH",  "x",    "RH",   ""},
572         {"RH",  "b",    "RH",   ""},
573
574         {"RW",  "s",    "R",    "s"},
575         {"RW",  "x",    "R",    "s"},
576         {"RW",  "b",    "R",    "s"},
577
578         {"RHW", "s",    "RH",   ""},
579         {"RHW", "x",    "RH",   ""},
580         {"RHW", "b",    "RH",   ""},
581 };
582
583 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
584         {"s",   "R",    "s",    "R"},
585         {"s",   "RH",   "s",    "R"},
586         {"s",   "RW",   "s",    "R"},
587         {"s",   "RHW",  "s",    "R"},
588
589         {"x",   "R",    "s",    "R"},
590         {"x",   "RH",   "s",    "R"},
591         {"x",   "RW",   "s",    "R"},
592         {"x",   "RHW",  "s",    "R"},
593
594         {"b",   "R",    "s",    "R"},
595         {"b",   "RH",   "s",    "R"},
596         {"b",   "RW",   "s",    "R"},
597         {"b",   "RHW",  "s",    "R"},
598 };
599
600 static bool test_lease_oplock(struct torture_context *tctx,
601                               struct smb2_tree *tree)
602 {
603         TALLOC_CTX *mem_ctx = talloc_new(tctx);
604         struct smb2_create io;
605         struct smb2_lease ls;
606         struct smb2_handle h, h2;
607         NTSTATUS status;
608         const char *fname = "lease.dat";
609         bool ret = true;
610         int i;
611
612         tree->session->transport->lease.handler = torture_lease_handler;
613         tree->session->transport->lease.private_data = tree;
614         tree->session->transport->oplock.handler = torture_oplock_handler;
615         tree->session->transport->oplock.private_data = tree;
616
617         smb2_util_unlink(tree, fname);
618
619         for (i = 0; i < NOPLOCK_RESULTS; i++) {
620                 const char *held = oplock_results[i][0];
621                 const char *contend = oplock_results[i][1];
622                 const char *brokento = oplock_results[i][2];
623                 const char *granted = oplock_results[i][3];
624                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
625                     "expecting break to %s(%x) and grant of %s(%x)\n",
626                     held, lease(held), contend, oplock(contend),
627                     brokento, lease(brokento), granted, oplock(granted));
628
629                 ZERO_STRUCT(break_info);
630
631                 /* Grab lease. */
632                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
633                 status = smb2_create(tree, mem_ctx, &io);
634                 CHECK_STATUS(status, NT_STATUS_OK);
635                 h = io.out.file.handle;
636                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
637                 CHECK_LEASE(&io, held, true, LEASE1);
638
639                 /* Does an oplock contend the lease? */
640                 smb2_oplock_create(&io, fname, oplock(contend));
641                 status = smb2_create(tree, mem_ctx, &io);
642                 CHECK_STATUS(status, NT_STATUS_OK);
643                 h2 = io.out.file.handle;
644                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
645                 CHECK_VAL(io.out.oplock_level, oplock(granted));
646                 break_info.held_oplock_level = io.out.oplock_level;
647
648                 if (lease(held) != lease(brokento)) {
649                         CHECK_BREAK_INFO(held, brokento, LEASE1);
650                 } else {
651                         CHECK_VAL(break_info.count, 0);
652                         CHECK_VAL(break_info.failures, 0);
653                 }
654
655                 smb2_util_close(tree, h);
656                 smb2_util_close(tree, h2);
657
658                 status = smb2_util_unlink(tree, fname);
659                 CHECK_STATUS(status, NT_STATUS_OK);
660         }
661
662         for (i = 0; i < NOPLOCK_RESULTS; i++) {
663                 const char *held = oplock_results_2[i][0];
664                 const char *contend = oplock_results_2[i][1];
665                 const char *brokento = oplock_results_2[i][2];
666                 const char *granted = oplock_results_2[i][3];
667                 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
668                     "expecting break to %s(%x) and grant of %s(%x)\n",
669                     held, oplock(held), contend, lease(contend),
670                     brokento, oplock(brokento), granted, lease(granted));
671
672                 ZERO_STRUCT(break_info);
673
674                 /* Grab an oplock. */
675                 smb2_oplock_create(&io, fname, oplock(held));
676                 status = smb2_create(tree, mem_ctx, &io);
677                 CHECK_STATUS(status, NT_STATUS_OK);
678                 h = io.out.file.handle;
679                 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
680                 CHECK_VAL(io.out.oplock_level, oplock(held));
681                 break_info.held_oplock_level = io.out.oplock_level;
682
683                 /* Grab lease. */
684                 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
685                 status = smb2_create(tree, mem_ctx, &io);
686                 CHECK_STATUS(status, NT_STATUS_OK);
687                 h2 = io.out.file.handle;
688                 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
689                 CHECK_LEASE(&io, granted, true, LEASE1);
690
691                 if (oplock(held) != oplock(brokento)) {
692                         CHECK_VAL(break_info.oplock_count, 1);
693                         CHECK_VAL(break_info.oplock_failures, 0);
694                         CHECK_VAL(break_info.oplock_level, oplock(brokento));
695                         break_info.held_oplock_level = break_info.oplock_level;
696                 } else {
697                         CHECK_VAL(break_info.oplock_count, 0);
698                         CHECK_VAL(break_info.oplock_failures, 0);
699                 }
700
701                 smb2_util_close(tree, h);
702                 smb2_util_close(tree, h2);
703
704                 status = smb2_util_unlink(tree, fname);
705                 CHECK_STATUS(status, NT_STATUS_OK);
706         }
707
708  done:
709         smb2_util_close(tree, h);
710         smb2_util_close(tree, h2);
711
712         smb2_util_unlink(tree, fname);
713
714         talloc_free(mem_ctx);
715
716         return ret;
717 }
718
719 static bool test_lease_multibreak(struct torture_context *tctx,
720                                   struct smb2_tree *tree)
721 {
722         TALLOC_CTX *mem_ctx = talloc_new(tctx);
723         struct smb2_create io;
724         struct smb2_lease ls;
725         struct smb2_handle h, h2, h3;
726         struct smb2_write w;
727         NTSTATUS status;
728         const char *fname = "lease.dat";
729         bool ret = true;
730
731         tree->session->transport->lease.handler = torture_lease_handler;
732         tree->session->transport->lease.private_data = tree;
733         tree->session->transport->oplock.handler = torture_oplock_handler;
734         tree->session->transport->oplock.private_data = tree;
735
736         smb2_util_unlink(tree, fname);
737
738         ZERO_STRUCT(break_info);
739
740         /* Grab lease, upgrade to RHW .. */
741         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
742         status = smb2_create(tree, mem_ctx, &io);
743         CHECK_STATUS(status, NT_STATUS_OK);
744         h = io.out.file.handle;
745         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
746         CHECK_LEASE(&io, "RH", true, LEASE1);
747
748         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
749         status = smb2_create(tree, mem_ctx, &io);
750         CHECK_STATUS(status, NT_STATUS_OK);
751         h2 = io.out.file.handle;
752         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
753         CHECK_LEASE(&io, "RHW", true, LEASE1);
754
755         /* Contend with LEASE2. */
756         smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
757         status = smb2_create(tree, mem_ctx, &io);
758         CHECK_STATUS(status, NT_STATUS_OK);
759         h3 = io.out.file.handle;
760         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
761         CHECK_LEASE(&io, "RH", true, LEASE2);
762
763         /* Verify that we were only sent one break. */
764         CHECK_BREAK_INFO("RHW", "RH", LEASE1);
765
766         /* Drop LEASE1 / LEASE2 */
767         status = smb2_util_close(tree, h);
768         CHECK_STATUS(status, NT_STATUS_OK);
769         status = smb2_util_close(tree, h2);
770         CHECK_STATUS(status, NT_STATUS_OK);
771         status = smb2_util_close(tree, h3);
772         CHECK_STATUS(status, NT_STATUS_OK);
773
774         ZERO_STRUCT(break_info);
775
776         /* Grab an R lease. */
777         smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
778         status = smb2_create(tree, mem_ctx, &io);
779         CHECK_STATUS(status, NT_STATUS_OK);
780         h = io.out.file.handle;
781         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
782         CHECK_LEASE(&io, "R", true, LEASE1);
783
784         /* Grab a level-II oplock. */
785         smb2_oplock_create(&io, fname, oplock("s"));
786         status = smb2_create(tree, mem_ctx, &io);
787         CHECK_STATUS(status, NT_STATUS_OK);
788         h2 = io.out.file.handle;
789         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
790         CHECK_VAL(io.out.oplock_level, oplock("s"));
791         break_info.held_oplock_level = io.out.oplock_level;
792
793         /* Verify no breaks. */
794         CHECK_VAL(break_info.count, 0);
795         CHECK_VAL(break_info.failures, 0);
796
797         /* Open for truncate, force a break. */
798         smb2_generic_create(&io, NULL, false, fname,
799             NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
800         status = smb2_create(tree, mem_ctx, &io);
801         CHECK_STATUS(status, NT_STATUS_OK);
802         h3 = io.out.file.handle;
803         CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
804         CHECK_VAL(io.out.oplock_level, oplock(""));
805         break_info.held_oplock_level = io.out.oplock_level;
806
807         /* Sleep, use a write to clear the recv queue. */
808         msleep(250);
809         ZERO_STRUCT(w);
810         w.in.file.handle = h3;
811         w.in.offset      = 0;
812         w.in.data        = data_blob_talloc(mem_ctx, NULL, 4096);
813         status = smb2_write(tree, &w);
814         CHECK_STATUS(status, NT_STATUS_OK);
815
816         /* Verify one oplock break, one lease break. */
817         CHECK_VAL(break_info.oplock_count, 1);
818         CHECK_VAL(break_info.oplock_failures, 0);
819         CHECK_VAL(break_info.oplock_level, oplock(""));
820         CHECK_BREAK_INFO("R", "", LEASE1);
821
822  done:
823         smb2_util_close(tree, h);
824         smb2_util_close(tree, h2);
825         smb2_util_close(tree, h3);
826
827         smb2_util_unlink(tree, fname);
828
829         talloc_free(mem_ctx);
830
831         return ret;
832 }
833
834 struct torture_suite *torture_smb2_lease_init(void)
835 {
836         struct torture_suite *suite =
837             torture_suite_create(talloc_autofree_context(), "LEASE");
838
839         torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request);
840         torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade);
841         torture_suite_add_1smb2_test(suite, "BREAK", test_lease_break);
842         torture_suite_add_1smb2_test(suite, "OPLOCK", test_lease_oplock);
843         torture_suite_add_1smb2_test(suite, "MULTIBREAK", test_lease_multibreak);
844
845         suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
846
847         return suite;
848 }