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