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