s4:torture:smb2: extend the durable-v2-open.reopen2 test
[sfrench/samba-autobuild/.git] / source4 / torture / smb2 / durable_v2_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 version two of durable opens
5
6    Copyright (C) Michael Adam 2012
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 "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/ndr/libndr.h"
29
30 #define CHECK_VAL(v, correct) do { \
31         if ((v) != (correct)) { \
32                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33                                 __location__, #v, (int)v, (int)correct); \
34                 ret = false; \
35         }} while (0)
36
37 #define CHECK_STATUS(status, correct) do { \
38         if (!NT_STATUS_EQUAL(status, correct)) { \
39                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40                        nt_errstr(status), nt_errstr(correct)); \
41                 ret = false; \
42                 goto done; \
43         }} while (0)
44
45 #define CHECK_CREATED(__io, __created, __attribute)                     \
46         do {                                                            \
47                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
49                 CHECK_VAL((__io)->out.size, 0);                         \
50                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
51                 CHECK_VAL((__io)->out.reserved2, 0);                    \
52         } while(0)
53
54 static struct {
55         int count;
56         struct smb2_close cl;
57 } break_info;
58
59 static void torture_oplock_close_callback(struct smb2_request *req)
60 {
61         smb2_close_recv(req, &break_info.cl);
62 }
63
64 /* A general oplock break notification handler.  This should be used when a
65  * test expects to break from batch or exclusive to a lower level. */
66 static bool torture_oplock_handler(struct smb2_transport *transport,
67                                    const struct smb2_handle *handle,
68                                    uint8_t level,
69                                    void *private_data)
70 {
71         struct smb2_tree *tree = private_data;
72         struct smb2_request *req;
73
74         break_info.count++;
75
76         ZERO_STRUCT(break_info.cl);
77         break_info.cl.in.file.handle = *handle;
78
79         req = smb2_close_send(tree, &break_info.cl);
80         req->async.fn = torture_oplock_close_callback;
81         req->async.private_data = NULL;
82         return true;
83 }
84
85 /**
86  * basic durable_open test.
87  * durable state should only be granted when requested
88  * along with a batch oplock or a handle lease.
89  *
90  * This test tests durable open with all possible oplock types.
91  */
92
93 struct durable_open_vs_oplock {
94         const char *level;
95         const char *share_mode;
96         bool durable;
97         bool persistent;
98 };
99
100 #define NUM_OPLOCK_TYPES 4
101 #define NUM_SHARE_MODES 8
102 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
103 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
104 {
105         { "", "", false, false },
106         { "", "R", false, false },
107         { "", "W", false, false },
108         { "", "D", false, false },
109         { "", "RD", false, false },
110         { "", "RW", false, false },
111         { "", "WD", false, false },
112         { "", "RWD", false, false },
113
114         { "s", "", false, false },
115         { "s", "R", false, false },
116         { "s", "W", false, false },
117         { "s", "D", false, false },
118         { "s", "RD", false, false },
119         { "s", "RW", false, false },
120         { "s", "WD", false, false },
121         { "s", "RWD", false, false },
122
123         { "x", "", false, false },
124         { "x", "R", false, false },
125         { "x", "W", false, false },
126         { "x", "D", false, false },
127         { "x", "RD", false, false },
128         { "x", "RW", false, false },
129         { "x", "WD", false, false },
130         { "x", "RWD", false, false },
131
132         { "b", "", true, false },
133         { "b", "R", true, false },
134         { "b", "W", true, false },
135         { "b", "D", true, false },
136         { "b", "RD", true, false },
137         { "b", "RW", true, false },
138         { "b", "WD", true, false },
139         { "b", "RWD", true, false },
140 };
141
142 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
143                                             struct smb2_tree *tree,
144                                             const char *fname,
145                                             bool request_persistent,
146                                             struct durable_open_vs_oplock test)
147 {
148         NTSTATUS status;
149         TALLOC_CTX *mem_ctx = talloc_new(tctx);
150         struct smb2_handle _h;
151         struct smb2_handle *h = NULL;
152         bool ret = true;
153         struct smb2_create io;
154
155         smb2_util_unlink(tree, fname);
156
157         smb2_oplock_create_share(&io, fname,
158                                  smb2_util_share_access(test.share_mode),
159                                  smb2_util_oplock_level(test.level));
160         io.in.durable_open = false;
161         io.in.durable_open_v2 = true;
162         io.in.persistent_open = request_persistent;
163         io.in.create_guid = GUID_random();
164
165         status = smb2_create(tree, mem_ctx, &io);
166         CHECK_STATUS(status, NT_STATUS_OK);
167         _h = io.out.file.handle;
168         h = &_h;
169         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
170         CHECK_VAL(io.out.durable_open, false);
171         CHECK_VAL(io.out.durable_open_v2, test.durable);
172         CHECK_VAL(io.out.persistent_open, test.persistent);
173         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
174
175 done:
176         if (h != NULL) {
177                 smb2_util_close(tree, *h);
178         }
179         smb2_util_unlink(tree, fname);
180         talloc_free(mem_ctx);
181
182         return ret;
183 }
184
185 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
186                                               struct smb2_tree *tree,
187                                               const char *fname,
188                                               bool request_persistent,
189                                               struct durable_open_vs_oplock *table,
190                                               uint8_t num_tests)
191 {
192         bool ret = true;
193         uint8_t i;
194
195         smb2_util_unlink(tree, fname);
196
197         for (i = 0; i < num_tests; i++) {
198                 ret = test_one_durable_v2_open_oplock(tctx,
199                                                       tree,
200                                                       fname,
201                                                       request_persistent,
202                                                       table[i]);
203                 if (ret == false) {
204                         goto done;
205                 }
206         }
207
208 done:
209         smb2_util_unlink(tree, fname);
210
211         return ret;
212 }
213
214 bool test_durable_v2_open_oplock(struct torture_context *tctx,
215                                  struct smb2_tree *tree)
216 {
217         bool ret;
218         char fname[256];
219
220         /* Choose a random name in case the state is left a little funky. */
221         snprintf(fname, 256, "durable_open_oplock_%s.dat",
222                  generate_random_str(tctx, 8));
223
224         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
225                                                 false, /* request_persistent */
226                                                 durable_open_vs_oplock_table,
227                                                 NUM_OPLOCK_OPEN_TESTS);
228
229         talloc_free(tree);
230
231         return ret;
232 }
233
234 /**
235  * basic durable handle open test.
236  * persistent state should only be granted when requested
237  * along with a batch oplock or a handle lease.
238  *
239  * This test tests persistent open with all valid lease types.
240  */
241
242 struct durable_open_vs_lease {
243         const char *type;
244         const char *share_mode;
245         bool durable;
246         bool persistent;
247 };
248
249 #define NUM_LEASE_TYPES 5
250 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
251 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
252 {
253         { "", "", false, false },
254         { "", "R", false, false },
255         { "", "W", false, false },
256         { "", "D", false, false },
257         { "", "RW", false, false },
258         { "", "RD", false, false },
259         { "", "WD", false, false },
260         { "", "RWD", false, false },
261
262         { "R", "", false, false },
263         { "R", "R", false, false },
264         { "R", "W", false, false },
265         { "R", "D", false, false },
266         { "R", "RW", false, false },
267         { "R", "RD", false, false },
268         { "R", "DW", false, false },
269         { "R", "RWD", false, false },
270
271         { "RW", "", false, false },
272         { "RW", "R", false, false },
273         { "RW", "W", false, false },
274         { "RW", "D", false, false },
275         { "RW", "RW", false, false },
276         { "RW", "RD", false, false },
277         { "RW", "WD", false, false },
278         { "RW", "RWD", false, false },
279
280         { "RH", "", true, false },
281         { "RH", "R", true, false },
282         { "RH", "W", true, false },
283         { "RH", "D", true, false },
284         { "RH", "RW", true, false },
285         { "RH", "RD", true, false },
286         { "RH", "WD", true, false },
287         { "RH", "RWD", true, false },
288
289         { "RHW", "", true, false },
290         { "RHW", "R", true, false },
291         { "RHW", "W", true, false },
292         { "RHW", "D", true, false },
293         { "RHW", "RW", true, false },
294         { "RHW", "RD", true, false },
295         { "RHW", "WD", true, false },
296         { "RHW", "RWD", true, false },
297 };
298
299 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
300                                            struct smb2_tree *tree,
301                                            const char *fname,
302                                            bool request_persistent,
303                                            struct durable_open_vs_lease test)
304 {
305         NTSTATUS status;
306         TALLOC_CTX *mem_ctx = talloc_new(tctx);
307         struct smb2_handle _h;
308         struct smb2_handle *h = NULL;
309         bool ret = true;
310         struct smb2_create io;
311         struct smb2_lease ls;
312         uint64_t lease;
313
314         smb2_util_unlink(tree, fname);
315
316         lease = random();
317
318         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
319                                 smb2_util_share_access(test.share_mode),
320                                 lease,
321                                 smb2_util_lease_state(test.type));
322         io.in.durable_open = false;
323         io.in.durable_open_v2 = true;
324         io.in.persistent_open = request_persistent;
325         io.in.create_guid = GUID_random();
326
327         status = smb2_create(tree, mem_ctx, &io);
328         CHECK_STATUS(status, NT_STATUS_OK);
329         _h = io.out.file.handle;
330         h = &_h;
331         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
332         CHECK_VAL(io.out.durable_open, false);
333         CHECK_VAL(io.out.durable_open_v2, test.durable);
334         CHECK_VAL(io.out.persistent_open, test.persistent);
335         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
336         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
337         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
338         CHECK_VAL(io.out.lease_response.lease_state,
339                   smb2_util_lease_state(test.type));
340 done:
341         if (h != NULL) {
342                 smb2_util_close(tree, *h);
343         }
344         smb2_util_unlink(tree, fname);
345         talloc_free(mem_ctx);
346
347         return ret;
348 }
349
350 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
351                                              struct smb2_tree *tree,
352                                              const char *fname,
353                                              bool request_persistent,
354                                              struct durable_open_vs_lease *table,
355                                              uint8_t num_tests)
356 {
357         bool ret = true;
358         uint8_t i;
359
360         smb2_util_unlink(tree, fname);
361
362         for (i = 0; i < num_tests; i++) {
363                 ret = test_one_durable_v2_open_lease(tctx,
364                                                      tree,
365                                                      fname,
366                                                      request_persistent,
367                                                      table[i]);
368                 if (ret == false) {
369                         goto done;
370                 }
371         }
372
373 done:
374         smb2_util_unlink(tree, fname);
375
376         return ret;
377 }
378
379 bool test_durable_v2_open_lease(struct torture_context *tctx,
380                                 struct smb2_tree *tree)
381 {
382         char fname[256];
383         bool ret = true;
384         uint32_t caps;
385
386         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
387         if (!(caps & SMB2_CAP_LEASING)) {
388                 torture_skip(tctx, "leases are not supported");
389         }
390
391         /* Choose a random name in case the state is left a little funky. */
392         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
393
394         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
395                                                false, /* request_persistent */
396                                                durable_open_vs_lease_table,
397                                                NUM_LEASE_OPEN_TESTS);
398
399         talloc_free(tree);
400         return ret;
401 }
402
403 /**
404  * basic test for doing a durable open
405  * and do a durable reopen on the same connection
406  * while the first open is still active (fails)
407  */
408 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
409                                   struct smb2_tree *tree)
410 {
411         NTSTATUS status;
412         TALLOC_CTX *mem_ctx = talloc_new(tctx);
413         char fname[256];
414         struct smb2_handle _h;
415         struct smb2_handle *h = NULL;
416         struct smb2_create io;
417         struct GUID create_guid = GUID_random();
418         bool ret = true;
419
420         /* Choose a random name in case the state is left a little funky. */
421         snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
422                  generate_random_str(tctx, 8));
423
424         smb2_util_unlink(tree, fname);
425
426         smb2_oplock_create_share(&io, fname,
427                                  smb2_util_share_access(""),
428                                  smb2_util_oplock_level("b"));
429         io.in.durable_open = false;
430         io.in.durable_open_v2 = true;
431         io.in.persistent_open = false;
432         io.in.create_guid = create_guid;
433         io.in.timeout = UINT32_MAX;
434
435         status = smb2_create(tree, mem_ctx, &io);
436         CHECK_STATUS(status, NT_STATUS_OK);
437         _h = io.out.file.handle;
438         h = &_h;
439         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
440         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
441         CHECK_VAL(io.out.durable_open, false);
442         CHECK_VAL(io.out.durable_open_v2, true);
443         CHECK_VAL(io.out.persistent_open, false);
444         CHECK_VAL(io.out.timeout, io.in.timeout);
445
446         /* try a durable reconnect while the file is still open */
447         ZERO_STRUCT(io);
448         io.in.fname = "";
449         io.in.durable_handle_v2 = h;
450         io.in.create_guid = create_guid;
451         status = smb2_create(tree, mem_ctx, &io);
452         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
453
454 done:
455         if (h != NULL) {
456                 smb2_util_close(tree, *h);
457         }
458
459         smb2_util_unlink(tree, fname);
460
461         talloc_free(tree);
462
463         talloc_free(mem_ctx);
464
465         return ret;
466 }
467
468 /**
469  * basic test for doing a durable open
470  * tcp disconnect, reconnect, do a durable reopen (succeeds)
471  */
472 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
473                                   struct smb2_tree *tree)
474 {
475         NTSTATUS status;
476         TALLOC_CTX *mem_ctx = talloc_new(tctx);
477         char fname[256];
478         struct smb2_handle _h;
479         struct smb2_handle *h = NULL;
480         struct smb2_create io;
481         struct GUID create_guid = GUID_random();
482         struct GUID create_guid_invalid = GUID_random();
483         bool ret = true;
484
485         /* Choose a random name in case the state is left a little funky. */
486         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
487                  generate_random_str(tctx, 8));
488
489         smb2_util_unlink(tree, fname);
490
491         smb2_oplock_create_share(&io, fname,
492                                  smb2_util_share_access(""),
493                                  smb2_util_oplock_level("b"));
494         io.in.durable_open = false;
495         io.in.durable_open_v2 = true;
496         io.in.persistent_open = false;
497         io.in.create_guid = create_guid;
498         io.in.timeout = UINT32_MAX;
499
500         status = smb2_create(tree, mem_ctx, &io);
501         CHECK_STATUS(status, NT_STATUS_OK);
502         _h = io.out.file.handle;
503         h = &_h;
504         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
505         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
506         CHECK_VAL(io.out.durable_open, false);
507         CHECK_VAL(io.out.durable_open_v2, true);
508         CHECK_VAL(io.out.persistent_open, false);
509         CHECK_VAL(io.out.timeout, io.in.timeout);
510
511         /* disconnect, leaving the durable open */
512         TALLOC_FREE(tree);
513
514         if (!torture_smb2_connection(tctx, &tree)) {
515                 torture_warning(tctx, "couldn't reconnect, bailing\n");
516                 ret = false;
517                 goto done;
518         }
519
520         /*
521          * first a few failure cases
522          */
523
524         ZERO_STRUCT(io);
525         io.in.fname = "";
526         io.in.durable_handle_v2 = h;
527         status = smb2_create(tree, mem_ctx, &io);
528         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
529
530         ZERO_STRUCT(io);
531         io.in.fname = "__non_existing_fname__";
532         io.in.durable_handle_v2 = h;
533         status = smb2_create(tree, mem_ctx, &io);
534         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
535
536         ZERO_STRUCT(io);
537         io.in.fname = fname;
538         io.in.durable_handle_v2 = h;
539         status = smb2_create(tree, mem_ctx, &io);
540         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
541
542         /* a non-zero but non-matching create_guid does not change it: */
543         ZERO_STRUCT(io);
544         io.in.fname = fname;
545         io.in.durable_handle_v2 = h;
546         io.in.create_guid = create_guid_invalid;
547         status = smb2_create(tree, mem_ctx, &io);
548         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
549
550         /*
551          * now success:
552          * The important difference is that the create_guid is provided.
553          */
554         ZERO_STRUCT(io);
555         io.in.fname = fname;
556         io.in.durable_open_v2 = false;
557         io.in.durable_handle_v2 = h;
558         io.in.create_guid = create_guid;
559         h = NULL;
560
561         status = smb2_create(tree, mem_ctx, &io);
562         CHECK_STATUS(status, NT_STATUS_OK);
563         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
564         CHECK_VAL(io.out.durable_open, false);
565         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
566         CHECK_VAL(io.out.persistent_open, false);
567         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
568         _h = io.out.file.handle;
569         h = &_h;
570
571         /* disconnect one more time */
572         TALLOC_FREE(tree);
573
574         if (!torture_smb2_connection(tctx, &tree)) {
575                 torture_warning(tctx, "couldn't reconnect, bailing\n");
576                 ret = false;
577                 goto done;
578         }
579
580         ZERO_STRUCT(io);
581         /* These are completely ignored by the server */
582         io.in.security_flags = 0x78;
583         io.in.oplock_level = 0x78;
584         io.in.impersonation_level = 0x12345678;
585         io.in.create_flags = 0x12345678;
586         io.in.reserved = 0x12345678;
587         io.in.desired_access = 0x12345678;
588         io.in.file_attributes = 0x12345678;
589         io.in.share_access = 0x12345678;
590         io.in.create_disposition = 0x12345678;
591         io.in.create_options = 0x12345678;
592         io.in.fname = "__non_existing_fname__";
593
594         /*
595          * only io.in.durable_handle_v2 and
596          * io.in.create_guid are checked
597          */
598         io.in.durable_open_v2 = false;
599         io.in.durable_handle_v2 = h;
600         io.in.create_guid = create_guid;
601         h = NULL;
602
603         status = smb2_create(tree, mem_ctx, &io);
604         CHECK_STATUS(status, NT_STATUS_OK);
605         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
606         CHECK_VAL(io.out.durable_open, false);
607         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
608         CHECK_VAL(io.out.persistent_open, false);
609         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
610         _h = io.out.file.handle;
611         h = &_h;
612
613 done:
614         if (h != NULL) {
615                 smb2_util_close(tree, *h);
616         }
617
618         smb2_util_unlink(tree, fname);
619
620         talloc_free(tree);
621
622         talloc_free(mem_ctx);
623
624         return ret;
625 }
626
627 /**
628  * lease variant of reopen2
629  * basic test for doing a durable open
630  * tcp disconnect, reconnect, do a durable reopen (succeeds)
631  */
632 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
633                                         struct smb2_tree *tree)
634 {
635         NTSTATUS status;
636         TALLOC_CTX *mem_ctx = talloc_new(tctx);
637         char fname[256];
638         struct smb2_handle _h;
639         struct smb2_handle *h = NULL;
640         struct smb2_create io;
641         struct GUID create_guid = GUID_random();
642         struct smb2_lease ls;
643         uint64_t lease_key;
644         bool ret = true;
645         struct smbcli_options options;
646         uint32_t caps;
647
648         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
649         if (!(caps & SMB2_CAP_LEASING)) {
650                 torture_skip(tctx, "leases are not supported");
651         }
652
653         options = tree->session->transport->options;
654
655         /* Choose a random name in case the state is left a little funky. */
656         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
657                  generate_random_str(tctx, 8));
658
659         smb2_util_unlink(tree, fname);
660
661         lease_key = random();
662         smb2_lease_create(&io, &ls, false /* dir */, fname,
663                           lease_key, smb2_util_lease_state("RWH"));
664         io.in.durable_open = false;
665         io.in.durable_open_v2 = true;
666         io.in.persistent_open = false;
667         io.in.create_guid = create_guid;
668         io.in.timeout = UINT32_MAX;
669
670         status = smb2_create(tree, mem_ctx, &io);
671         CHECK_STATUS(status, NT_STATUS_OK);
672         _h = io.out.file.handle;
673         h = &_h;
674         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
675         CHECK_VAL(io.out.durable_open, false);
676         CHECK_VAL(io.out.durable_open_v2, true);
677         CHECK_VAL(io.out.persistent_open, false);
678         CHECK_VAL(io.out.timeout, io.in.timeout);
679         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
680         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
681         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
682         CHECK_VAL(io.out.lease_response.lease_state,
683                   smb2_util_lease_state("RWH"));
684         CHECK_VAL(io.out.lease_response.lease_flags, 0);
685         CHECK_VAL(io.out.lease_response.lease_duration, 0);
686
687         /* disconnect, reconnect and then do durable reopen */
688         TALLOC_FREE(tree);
689
690         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
691                 torture_warning(tctx, "couldn't reconnect, bailing\n");
692                 ret = false;
693                 goto done;
694         }
695
696         /* a few failure tests: */
697
698         /*
699          * several attempts without lease attached:
700          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
701          * irrespective of file name provided
702          */
703
704         ZERO_STRUCT(io);
705         io.in.fname = "";
706         io.in.durable_handle_v2 = h;
707         status = smb2_create(tree, mem_ctx, &io);
708         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
709
710         ZERO_STRUCT(io);
711         io.in.fname = "__non_existing_fname__";
712         io.in.durable_handle_v2 = h;
713         status = smb2_create(tree, mem_ctx, &io);
714         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
715
716         ZERO_STRUCT(io);
717         io.in.fname = fname;
718         io.in.durable_handle_v2 = h;
719         status = smb2_create(tree, mem_ctx, &io);
720         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
721
722         /*
723          * attempt with lease provided, but
724          * with a changed lease key. => fails
725          */
726         ZERO_STRUCT(io);
727         io.in.fname = fname;
728         io.in.durable_open_v2 = false;
729         io.in.durable_handle_v2 = h;
730         io.in.create_guid = create_guid;
731         io.in.lease_request = &ls;
732         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
733         /* a wrong lease key lets the request fail */
734         ls.lease_key.data[0]++;
735
736         status = smb2_create(tree, mem_ctx, &io);
737         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
738
739         /* restore the correct lease key */
740         ls.lease_key.data[0]--;
741
742         /*
743          * this last failing attempt is almost correct:
744          * only problem is: we use the wrong filename...
745          * Note that this gives INVALID_PARAMETER.
746          * This is different from oplocks!
747          */
748         ZERO_STRUCT(io);
749         io.in.fname = "__non_existing_fname__";
750         io.in.durable_open_v2 = false;
751         io.in.durable_handle_v2 = h;
752         io.in.create_guid = create_guid;
753         io.in.lease_request = &ls;
754         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
755
756         status = smb2_create(tree, mem_ctx, &io);
757         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
758
759         /*
760          * Now for a succeeding reconnect:
761          */
762
763         ZERO_STRUCT(io);
764         io.in.fname = fname;
765         io.in.durable_open_v2 = false;
766         io.in.durable_handle_v2 = h;
767         io.in.create_guid = create_guid;
768         io.in.lease_request = &ls;
769         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
770
771         /* the requested lease state is irrelevant */
772         ls.lease_state = smb2_util_lease_state("");
773
774         h = NULL;
775
776         status = smb2_create(tree, mem_ctx, &io);
777         CHECK_STATUS(status, NT_STATUS_OK);
778
779         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
780         CHECK_VAL(io.out.durable_open, false);
781         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
782         CHECK_VAL(io.out.persistent_open, false);
783         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
784         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
785         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
786         CHECK_VAL(io.out.lease_response.lease_state,
787                   smb2_util_lease_state("RWH"));
788         CHECK_VAL(io.out.lease_response.lease_flags, 0);
789         CHECK_VAL(io.out.lease_response.lease_duration, 0);
790         _h = io.out.file.handle;
791         h = &_h;
792
793         /* disconnect one more time */
794         TALLOC_FREE(tree);
795
796         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
797                 torture_warning(tctx, "couldn't reconnect, bailing\n");
798                 ret = false;
799                 goto done;
800         }
801
802         /*
803          * demonstrate that various parameters are ignored
804          * in the reconnect
805          */
806
807         ZERO_STRUCT(io);
808         /*
809          * These are completely ignored by the server
810          */
811         io.in.security_flags = 0x78;
812         io.in.oplock_level = 0x78;
813         io.in.impersonation_level = 0x12345678;
814         io.in.create_flags = 0x12345678;
815         io.in.reserved = 0x12345678;
816         io.in.desired_access = 0x12345678;
817         io.in.file_attributes = 0x12345678;
818         io.in.share_access = 0x12345678;
819         io.in.create_disposition = 0x12345678;
820         io.in.create_options = 0x12345678;
821
822         /*
823          * only these are checked:
824          * - io.in.fname
825          * - io.in.durable_handle_v2,
826          * - io.in.create_guid
827          * - io.in.lease_request->lease_key
828          */
829
830         io.in.fname = fname;
831         io.in.durable_open_v2 = false;
832         io.in.durable_handle_v2 = h;
833         io.in.create_guid = create_guid;
834         io.in.lease_request = &ls;
835
836         /* the requested lease state is irrelevant */
837         ls.lease_state = smb2_util_lease_state("");
838
839         h = NULL;
840
841         status = smb2_create(tree, mem_ctx, &io);
842         CHECK_STATUS(status, NT_STATUS_OK);
843
844         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
845         CHECK_VAL(io.out.durable_open, false);
846         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
847         CHECK_VAL(io.out.persistent_open, false);
848         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
849         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
850         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
851         CHECK_VAL(io.out.lease_response.lease_state,
852                   smb2_util_lease_state("RWH"));
853         CHECK_VAL(io.out.lease_response.lease_flags, 0);
854         CHECK_VAL(io.out.lease_response.lease_duration, 0);
855
856         _h = io.out.file.handle;
857         h = &_h;
858
859 done:
860         if (h != NULL) {
861                 smb2_util_close(tree, *h);
862         }
863
864         smb2_util_unlink(tree, fname);
865
866         talloc_free(tree);
867
868         talloc_free(mem_ctx);
869
870         return ret;
871 }
872
873 /**
874  * lease_v2 variant of reopen2
875  * basic test for doing a durable open
876  * tcp disconnect, reconnect, do a durable reopen (succeeds)
877  */
878 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
879                                            struct smb2_tree *tree)
880 {
881         NTSTATUS status;
882         TALLOC_CTX *mem_ctx = talloc_new(tctx);
883         char fname[256];
884         struct smb2_handle _h;
885         struct smb2_handle *h = NULL;
886         struct smb2_create io;
887         struct GUID create_guid = GUID_random();
888         struct smb2_lease ls;
889         uint64_t lease_key;
890         bool ret = true;
891         struct smbcli_options options;
892         uint32_t caps;
893
894         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
895         if (!(caps & SMB2_CAP_LEASING)) {
896                 torture_skip(tctx, "leases are not supported");
897         }
898
899         options = tree->session->transport->options;
900
901         /* Choose a random name in case the state is left a little funky. */
902         snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
903                  generate_random_str(tctx, 8));
904
905         smb2_util_unlink(tree, fname);
906
907         lease_key = random();
908         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
909                              lease_key, 0, /* parent lease key */
910                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
911         io.in.durable_open = false;
912         io.in.durable_open_v2 = true;
913         io.in.persistent_open = false;
914         io.in.create_guid = create_guid;
915         io.in.timeout = UINT32_MAX;
916
917         status = smb2_create(tree, mem_ctx, &io);
918         CHECK_STATUS(status, NT_STATUS_OK);
919         _h = io.out.file.handle;
920         h = &_h;
921         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
922         CHECK_VAL(io.out.durable_open, false);
923         CHECK_VAL(io.out.durable_open_v2, true);
924         CHECK_VAL(io.out.persistent_open, false);
925         CHECK_VAL(io.out.timeout, io.in.timeout);
926         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
927         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
928         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
929
930         /* disconnect, reconnect and then do durable reopen */
931         TALLOC_FREE(tree);
932
933         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
934                 torture_warning(tctx, "couldn't reconnect, bailing\n");
935                 ret = false;
936                 goto done;
937         }
938
939         /* a few failure tests: */
940
941         /*
942          * several attempts without lease attached:
943          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
944          * irrespective of file name provided
945          */
946
947         ZERO_STRUCT(io);
948         io.in.fname = "";
949         io.in.durable_handle_v2 = h;
950         status = smb2_create(tree, mem_ctx, &io);
951         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
952
953         ZERO_STRUCT(io);
954         io.in.fname = "__non_existing_fname__";
955         io.in.durable_handle_v2 = h;
956         status = smb2_create(tree, mem_ctx, &io);
957         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
958
959         ZERO_STRUCT(io);
960         io.in.fname = fname;
961         io.in.durable_handle_v2 = h;
962         status = smb2_create(tree, mem_ctx, &io);
963         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
964
965         /*
966          * attempt with lease provided, but
967          * with a changed lease key. => fails
968          */
969         ZERO_STRUCT(io);
970         io.in.fname = fname;
971         io.in.durable_open_v2 = false;
972         io.in.durable_handle_v2 = h;
973         io.in.create_guid = create_guid;
974         io.in.lease_request_v2 = &ls;
975         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
976         /* a wrong lease key lets the request fail */
977         ls.lease_key.data[0]++;
978
979         status = smb2_create(tree, mem_ctx, &io);
980         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
981
982         /* restore the correct lease key */
983         ls.lease_key.data[0]--;
984
985
986         /*
987          * this last failing attempt is almost correct:
988          * only problem is: we use the wrong filename...
989          * Note that this gives INVALID_PARAMETER.
990          * This is different from oplocks!
991          */
992         ZERO_STRUCT(io);
993         io.in.fname = "__non_existing_fname__";
994         io.in.durable_open_v2 = false;
995         io.in.durable_handle_v2 = h;
996         io.in.create_guid = create_guid;
997         io.in.lease_request_v2 = &ls;
998         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
999
1000         status = smb2_create(tree, mem_ctx, &io);
1001         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1002
1003         /*
1004          * Now for a succeeding reconnect:
1005          */
1006
1007         ZERO_STRUCT(io);
1008         io.in.fname = fname;
1009         io.in.durable_open_v2 = false;
1010         io.in.durable_handle_v2 = h;
1011         io.in.create_guid = create_guid;
1012         io.in.lease_request_v2 = &ls;
1013         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1014
1015         /* the requested lease state is irrelevant */
1016         ls.lease_state = smb2_util_lease_state("");
1017
1018         h = NULL;
1019
1020         status = smb2_create(tree, mem_ctx, &io);
1021         CHECK_STATUS(status, NT_STATUS_OK);
1022
1023         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1024         CHECK_VAL(io.out.durable_open, false);
1025         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1026         CHECK_VAL(io.out.persistent_open, false);
1027         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1028         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1029         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1030         CHECK_VAL(io.out.lease_response_v2.lease_state,
1031                   smb2_util_lease_state("RWH"));
1032         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1033         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1034         _h = io.out.file.handle;
1035         h = &_h;
1036
1037         /* disconnect one more time */
1038         TALLOC_FREE(tree);
1039
1040         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1041                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1042                 ret = false;
1043                 goto done;
1044         }
1045
1046         /*
1047          * demonstrate that various parameters are ignored
1048          * in the reconnect
1049          */
1050
1051         ZERO_STRUCT(io);
1052         /*
1053          * These are completely ignored by the server
1054          */
1055         io.in.security_flags = 0x78;
1056         io.in.oplock_level = 0x78;
1057         io.in.impersonation_level = 0x12345678;
1058         io.in.create_flags = 0x12345678;
1059         io.in.reserved = 0x12345678;
1060         io.in.desired_access = 0x12345678;
1061         io.in.file_attributes = 0x12345678;
1062         io.in.share_access = 0x12345678;
1063         io.in.create_disposition = 0x12345678;
1064         io.in.create_options = 0x12345678;
1065         io.in.fname = "__non_existing_fname__";
1066
1067         /*
1068          * only these are checked:
1069          * - io.in.fname
1070          * - io.in.durable_handle_v2,
1071          * - io.in.create_guid
1072          * - io.in.lease_request_v2->lease_key
1073          */
1074
1075         io.in.fname = fname;
1076         io.in.durable_open_v2 = false;
1077         io.in.durable_handle_v2 = h;
1078         io.in.create_guid = create_guid;
1079         io.in.lease_request_v2 = &ls;
1080
1081         /* the requested lease state is irrelevant */
1082         ls.lease_state = smb2_util_lease_state("");
1083
1084         h = NULL;
1085
1086         status = smb2_create(tree, mem_ctx, &io);
1087         CHECK_STATUS(status, NT_STATUS_OK);
1088         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1089         CHECK_VAL(io.out.durable_open, false);
1090         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1091         CHECK_VAL(io.out.persistent_open, false);
1092         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1093         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1094         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1095         CHECK_VAL(io.out.lease_response_v2.lease_state,
1096                   smb2_util_lease_state("RWH"));
1097         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1098         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1099
1100         _h = io.out.file.handle;
1101         h = &_h;
1102
1103 done:
1104         if (h != NULL) {
1105                 smb2_util_close(tree, *h);
1106         }
1107
1108         smb2_util_unlink(tree, fname);
1109
1110         talloc_free(tree);
1111
1112         talloc_free(mem_ctx);
1113
1114         return ret;
1115 }
1116
1117 /**
1118  * Test durable request / reconnect with AppInstanceId
1119  */
1120 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1121                                        struct smb2_tree *tree1,
1122                                        struct smb2_tree *tree2)
1123 {
1124         NTSTATUS status;
1125         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1126         char fname[256];
1127         struct smb2_handle _h1, _h2;
1128         struct smb2_handle *h1 = NULL, *h2 = NULL;
1129         struct smb2_create io1, io2;
1130         bool ret = true;
1131         struct GUID create_guid_1 = GUID_random();
1132         struct GUID create_guid_2 = GUID_random();
1133         struct GUID app_instance_id = GUID_random();
1134
1135         /* Choose a random name in case the state is left a little funky. */
1136         snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1137                  generate_random_str(tctx, 8));
1138
1139         smb2_util_unlink(tree1, fname);
1140
1141         ZERO_STRUCT(break_info);
1142         tree1->session->transport->oplock.handler = torture_oplock_handler;
1143         tree1->session->transport->oplock.private_data = tree1;
1144
1145         smb2_oplock_create_share(&io1, fname,
1146                                  smb2_util_share_access(""),
1147                                  smb2_util_oplock_level("b"));
1148         io1.in.durable_open = false;
1149         io1.in.durable_open_v2 = true;
1150         io1.in.persistent_open = false;
1151         io1.in.create_guid = create_guid_1;
1152         io1.in.app_instance_id = &app_instance_id;
1153         io1.in.timeout = UINT32_MAX;
1154
1155         status = smb2_create(tree1, mem_ctx, &io1);
1156         CHECK_STATUS(status, NT_STATUS_OK);
1157         _h1 = io1.out.file.handle;
1158         h1 = &_h1;
1159         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1160         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1161         CHECK_VAL(io1.out.durable_open, false);
1162         CHECK_VAL(io1.out.durable_open_v2, true);
1163         CHECK_VAL(io1.out.persistent_open, false);
1164         CHECK_VAL(io1.out.timeout, io1.in.timeout);
1165
1166         /*
1167          * try to open the file as durable from a second tree with
1168          * a different create guid but the same app_instance_id
1169          * while the first handle is still open.
1170          */
1171
1172         smb2_oplock_create_share(&io2, fname,
1173                                  smb2_util_share_access(""),
1174                                  smb2_util_oplock_level("b"));
1175         io2.in.durable_open = false;
1176         io2.in.durable_open_v2 = true;
1177         io2.in.persistent_open = false;
1178         io2.in.create_guid = create_guid_2;
1179         io2.in.app_instance_id = &app_instance_id;
1180         io2.in.timeout = UINT32_MAX;
1181
1182         status = smb2_create(tree2, mem_ctx, &io2);
1183         CHECK_STATUS(status, NT_STATUS_OK);
1184         _h2 = io2.out.file.handle;
1185         h2 = &_h2;
1186         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1187         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1188         CHECK_VAL(io2.out.durable_open, false);
1189         CHECK_VAL(io2.out.durable_open_v2, true);
1190         CHECK_VAL(io2.out.persistent_open, false);
1191         CHECK_VAL(io2.out.timeout, io2.in.timeout);
1192
1193         CHECK_VAL(break_info.count, 0);
1194
1195         status = smb2_util_close(tree1, *h1);
1196         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1197         h1 = NULL;
1198
1199 done:
1200         if (h1 != NULL) {
1201                 smb2_util_close(tree1, *h1);
1202         }
1203         if (h2 != NULL) {
1204                 smb2_util_close(tree2, *h2);
1205         }
1206
1207         smb2_util_unlink(tree2, fname);
1208
1209         talloc_free(tree1);
1210         talloc_free(tree2);
1211
1212         talloc_free(mem_ctx);
1213
1214         return ret;
1215 }
1216
1217
1218 /**
1219  * basic persistent open test.
1220  *
1221  * This test tests durable open with all possible oplock types.
1222  */
1223
1224 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1225 {
1226         { "", "", true, true },
1227         { "", "R", true, true },
1228         { "", "W", true, true },
1229         { "", "D", true, true },
1230         { "", "RD", true, true },
1231         { "", "RW", true, true },
1232         { "", "WD", true, true },
1233         { "", "RWD", true, true },
1234
1235         { "s", "", true, true },
1236         { "s", "R", true, true },
1237         { "s", "W", true, true },
1238         { "s", "D", true, true },
1239         { "s", "RD", true, true },
1240         { "s", "RW", true, true },
1241         { "s", "WD", true, true },
1242         { "s", "RWD", true, true },
1243
1244         { "x", "", true, true },
1245         { "x", "R", true, true },
1246         { "x", "W", true, true },
1247         { "x", "D", true, true },
1248         { "x", "RD", true, true },
1249         { "x", "RW", true, true },
1250         { "x", "WD", true, true },
1251         { "x", "RWD", true, true },
1252
1253         { "b", "", true, true },
1254         { "b", "R", true, true },
1255         { "b", "W", true, true },
1256         { "b", "D", true, true },
1257         { "b", "RD", true, true },
1258         { "b", "RW", true, true },
1259         { "b", "WD", true, true },
1260         { "b", "RWD", true, true },
1261 };
1262
1263 bool test_persistent_open_oplock(struct torture_context *tctx,
1264                                  struct smb2_tree *tree)
1265 {
1266         char fname[256];
1267         bool ret = true;
1268         uint32_t share_capabilities;
1269         bool share_is_ca = false;
1270         struct durable_open_vs_oplock *table;
1271
1272         /* Choose a random name in case the state is left a little funky. */
1273         snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1274
1275         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1276         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1277
1278         if (share_is_ca) {
1279                 table = persistent_open_oplock_ca_table;
1280         } else {
1281                 table = durable_open_vs_oplock_table;
1282         }
1283
1284         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1285                                                 true, /* request_persistent */
1286                                                 table,
1287                                                 NUM_OPLOCK_OPEN_TESTS);
1288
1289         talloc_free(tree);
1290
1291         return ret;
1292 }
1293
1294 /**
1295  * basic persistent handle open test.
1296  * persistent state should only be granted when requested
1297  * along with a batch oplock or a handle lease.
1298  *
1299  * This test tests persistent open with all valid lease types.
1300  */
1301
1302 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1303 {
1304         { "", "", true, true },
1305         { "", "R", true, true },
1306         { "", "W", true, true },
1307         { "", "D", true, true },
1308         { "", "RW", true, true },
1309         { "", "RD", true, true },
1310         { "", "WD", true, true },
1311         { "", "RWD", true, true },
1312
1313         { "R", "", true, true },
1314         { "R", "R", true, true },
1315         { "R", "W", true, true },
1316         { "R", "D", true, true },
1317         { "R", "RW", true, true },
1318         { "R", "RD", true, true },
1319         { "R", "DW", true, true },
1320         { "R", "RWD", true, true },
1321
1322         { "RW", "", true, true },
1323         { "RW", "R", true, true },
1324         { "RW", "W", true, true },
1325         { "RW", "D", true, true },
1326         { "RW", "RW", true, true },
1327         { "RW", "RD", true, true },
1328         { "RW", "WD", true, true },
1329         { "RW", "RWD", true, true },
1330
1331         { "RH", "", true, true },
1332         { "RH", "R", true, true },
1333         { "RH", "W", true, true },
1334         { "RH", "D", true, true },
1335         { "RH", "RW", true, true },
1336         { "RH", "RD", true, true },
1337         { "RH", "WD", true, true },
1338         { "RH", "RWD", true, true },
1339
1340         { "RHW", "", true, true },
1341         { "RHW", "R", true, true },
1342         { "RHW", "W", true, true },
1343         { "RHW", "D", true, true },
1344         { "RHW", "RW", true, true },
1345         { "RHW", "RD", true, true },
1346         { "RHW", "WD", true, true },
1347         { "RHW", "RWD", true, true },
1348 };
1349
1350 bool test_persistent_open_lease(struct torture_context *tctx,
1351                                 struct smb2_tree *tree)
1352 {
1353         char fname[256];
1354         bool ret = true;
1355         uint32_t caps;
1356         uint32_t share_capabilities;
1357         bool share_is_ca;
1358         struct durable_open_vs_lease *table;
1359
1360         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1361         if (!(caps & SMB2_CAP_LEASING)) {
1362                 torture_skip(tctx, "leases are not supported");
1363         }
1364
1365         /* Choose a random name in case the state is left a little funky. */
1366         snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1367
1368         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1369         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1370
1371         if (share_is_ca) {
1372                 table = persistent_open_lease_ca_table;
1373         } else {
1374                 table = durable_open_vs_lease_table;
1375         }
1376
1377         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1378                                                true, /* request_persistent */
1379                                                table,
1380                                                NUM_LEASE_OPEN_TESTS);
1381
1382         talloc_free(tree);
1383
1384         return ret;
1385 }
1386
1387 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1388 {
1389         struct torture_suite *suite =
1390             torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1391
1392         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1393         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1394         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1395         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1396         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1397         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
1398         torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1399         torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1400         torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1401
1402         suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
1403
1404         return suite;
1405 }