s4:torture:smb2: add a new test durable-v2-open.app-instance
[metze/samba/wip.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         const char *name;
73         struct smb2_request *req;
74
75         break_info.count++;
76
77         ZERO_STRUCT(break_info.cl);
78         break_info.cl.in.file.handle = *handle;
79
80         req = smb2_close_send(tree, &break_info.cl);
81         req->async.fn = torture_oplock_close_callback;
82         req->async.private_data = NULL;
83         return true;
84 }
85
86 /**
87  * basic durable_open test.
88  * durable state should only be granted when requested
89  * along with a batch oplock or a handle lease.
90  *
91  * This test tests durable open with all possible oplock types.
92  */
93
94 struct durable_open_vs_oplock {
95         const char *level;
96         const char *share_mode;
97         bool durable;
98         bool persistent;
99 };
100
101 #define NUM_OPLOCK_TYPES 4
102 #define NUM_SHARE_MODES 8
103 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
104 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
105 {
106         { "", "", false, false },
107         { "", "R", false, false },
108         { "", "W", false, false },
109         { "", "D", false, false },
110         { "", "RD", false, false },
111         { "", "RW", false, false },
112         { "", "WD", false, false },
113         { "", "RWD", false, false },
114
115         { "s", "", false, false },
116         { "s", "R", false, false },
117         { "s", "W", false, false },
118         { "s", "D", false, false },
119         { "s", "RD", false, false },
120         { "s", "RW", false, false },
121         { "s", "WD", false, false },
122         { "s", "RWD", false, false },
123
124         { "x", "", false, false },
125         { "x", "R", false, false },
126         { "x", "W", false, false },
127         { "x", "D", false, false },
128         { "x", "RD", false, false },
129         { "x", "RW", false, false },
130         { "x", "WD", false, false },
131         { "x", "RWD", false, false },
132
133         { "b", "", true, false },
134         { "b", "R", true, false },
135         { "b", "W", true, false },
136         { "b", "D", true, false },
137         { "b", "RD", true, false },
138         { "b", "RW", true, false },
139         { "b", "WD", true, false },
140         { "b", "RWD", true, false },
141 };
142
143 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
144                                             struct smb2_tree *tree,
145                                             const char *fname,
146                                             bool request_persistent,
147                                             struct durable_open_vs_oplock test)
148 {
149         NTSTATUS status;
150         TALLOC_CTX *mem_ctx = talloc_new(tctx);
151         struct smb2_handle _h;
152         struct smb2_handle *h = NULL;
153         bool ret = true;
154         struct smb2_create io;
155
156         smb2_util_unlink(tree, fname);
157
158         smb2_oplock_create_share(&io, fname,
159                                  smb2_util_share_access(test.share_mode),
160                                  smb2_util_oplock_level(test.level));
161         io.in.durable_open = false;
162         io.in.durable_open_v2 = true;
163         io.in.persistent_open = request_persistent;
164         io.in.create_guid = GUID_random();
165
166         status = smb2_create(tree, mem_ctx, &io);
167         CHECK_STATUS(status, NT_STATUS_OK);
168         _h = io.out.file.handle;
169         h = &_h;
170         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
171         CHECK_VAL(io.out.durable_open, false);
172         CHECK_VAL(io.out.durable_open_v2, test.durable);
173         CHECK_VAL(io.out.persistent_open, test.persistent);
174         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
175
176 done:
177         if (h != NULL) {
178                 smb2_util_close(tree, *h);
179         }
180         smb2_util_unlink(tree, fname);
181         talloc_free(mem_ctx);
182
183         return ret;
184 }
185
186 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
187                                               struct smb2_tree *tree,
188                                               const char *fname,
189                                               bool request_persistent,
190                                               struct durable_open_vs_oplock *table,
191                                               uint8_t num_tests)
192 {
193         bool ret = true;
194         uint8_t i;
195
196         smb2_util_unlink(tree, fname);
197
198         for (i = 0; i < num_tests; i++) {
199                 ret = test_one_durable_v2_open_oplock(tctx,
200                                                       tree,
201                                                       fname,
202                                                       request_persistent,
203                                                       table[i]);
204                 if (ret == false) {
205                         goto done;
206                 }
207         }
208
209 done:
210         smb2_util_unlink(tree, fname);
211
212         return ret;
213 }
214
215 bool test_durable_v2_open_oplock(struct torture_context *tctx,
216                                  struct smb2_tree *tree)
217 {
218         bool ret;
219         char fname[256];
220
221         /* Choose a random name in case the state is left a little funky. */
222         snprintf(fname, 256, "durable_open_oplock_%s.dat",
223                  generate_random_str(tctx, 8));
224
225         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
226                                                 false, /* request_persistent */
227                                                 durable_open_vs_oplock_table,
228                                                 NUM_OPLOCK_OPEN_TESTS);
229
230         talloc_free(tree);
231
232         return ret;
233 }
234
235 /**
236  * basic durable handle open test.
237  * persistent state should only be granted when requested
238  * along with a batch oplock or a handle lease.
239  *
240  * This test tests persistent open with all valid lease types.
241  */
242
243 struct durable_open_vs_lease {
244         const char *type;
245         const char *share_mode;
246         bool durable;
247         bool persistent;
248 };
249
250 #define NUM_LEASE_TYPES 5
251 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
252 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
253 {
254         { "", "", false, false },
255         { "", "R", false, false },
256         { "", "W", false, false },
257         { "", "D", false, false },
258         { "", "RW", false, false },
259         { "", "RD", false, false },
260         { "", "WD", false, false },
261         { "", "RWD", false, false },
262
263         { "R", "", false, false },
264         { "R", "R", false, false },
265         { "R", "W", false, false },
266         { "R", "D", false, false },
267         { "R", "RW", false, false },
268         { "R", "RD", false, false },
269         { "R", "DW", false, false },
270         { "R", "RWD", false, false },
271
272         { "RW", "", false, false },
273         { "RW", "R", false, false },
274         { "RW", "W", false, false },
275         { "RW", "D", false, false },
276         { "RW", "RW", false, false },
277         { "RW", "RD", false, false },
278         { "RW", "WD", false, false },
279         { "RW", "RWD", false, false },
280
281         { "RH", "", true, false },
282         { "RH", "R", true, false },
283         { "RH", "W", true, false },
284         { "RH", "D", true, false },
285         { "RH", "RW", true, false },
286         { "RH", "RD", true, false },
287         { "RH", "WD", true, false },
288         { "RH", "RWD", true, false },
289
290         { "RHW", "", true, false },
291         { "RHW", "R", true, false },
292         { "RHW", "W", true, false },
293         { "RHW", "D", true, false },
294         { "RHW", "RW", true, false },
295         { "RHW", "RD", true, false },
296         { "RHW", "WD", true, false },
297         { "RHW", "RWD", true, false },
298 };
299
300 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
301                                            struct smb2_tree *tree,
302                                            const char *fname,
303                                            bool request_persistent,
304                                            struct durable_open_vs_lease test)
305 {
306         NTSTATUS status;
307         TALLOC_CTX *mem_ctx = talloc_new(tctx);
308         struct smb2_handle _h;
309         struct smb2_handle *h = NULL;
310         bool ret = true;
311         struct smb2_create io;
312         struct smb2_lease ls;
313         uint64_t lease;
314
315         smb2_util_unlink(tree, fname);
316
317         lease = random();
318
319         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
320                                 smb2_util_share_access(test.share_mode),
321                                 lease,
322                                 smb2_util_lease_state(test.type));
323         io.in.durable_open = false;
324         io.in.durable_open_v2 = true;
325         io.in.persistent_open = request_persistent;
326         io.in.create_guid = GUID_random();
327
328         status = smb2_create(tree, mem_ctx, &io);
329         CHECK_STATUS(status, NT_STATUS_OK);
330         _h = io.out.file.handle;
331         h = &_h;
332         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
333         CHECK_VAL(io.out.durable_open, false);
334         CHECK_VAL(io.out.durable_open_v2, test.durable);
335         CHECK_VAL(io.out.persistent_open, test.persistent);
336         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
337         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
338         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
339         CHECK_VAL(io.out.lease_response.lease_state,
340                   smb2_util_lease_state(test.type));
341 done:
342         if (h != NULL) {
343                 smb2_util_close(tree, *h);
344         }
345         smb2_util_unlink(tree, fname);
346         talloc_free(mem_ctx);
347
348         return ret;
349 }
350
351 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
352                                              struct smb2_tree *tree,
353                                              const char *fname,
354                                              bool request_persistent,
355                                              struct durable_open_vs_lease *table,
356                                              uint8_t num_tests)
357 {
358         bool ret = true;
359         uint8_t i;
360
361         smb2_util_unlink(tree, fname);
362
363         for (i = 0; i < num_tests; i++) {
364                 ret = test_one_durable_v2_open_lease(tctx,
365                                                      tree,
366                                                      fname,
367                                                      request_persistent,
368                                                      table[i]);
369                 if (ret == false) {
370                         goto done;
371                 }
372         }
373
374 done:
375         smb2_util_unlink(tree, fname);
376
377         return ret;
378 }
379
380 bool test_durable_v2_open_lease(struct torture_context *tctx,
381                                 struct smb2_tree *tree)
382 {
383         char fname[256];
384         bool ret = true;
385         uint32_t caps;
386
387         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
388         if (!(caps & SMB2_CAP_LEASING)) {
389                 torture_skip(tctx, "leases are not supported");
390         }
391
392         /* Choose a random name in case the state is left a little funky. */
393         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
394
395         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
396                                                false, /* request_persistent */
397                                                durable_open_vs_lease_table,
398                                                NUM_LEASE_OPEN_TESTS);
399
400         talloc_free(tree);
401         return ret;
402 }
403
404 /**
405  * basic test for doing a durable open
406  * and do a durable reopen on the same connection
407  * while the first open is still active (fails)
408  */
409 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
410                                   struct smb2_tree *tree)
411 {
412         NTSTATUS status;
413         TALLOC_CTX *mem_ctx = talloc_new(tctx);
414         char fname[256];
415         struct smb2_handle _h;
416         struct smb2_handle *h = NULL;
417         struct smb2_create io;
418         struct GUID create_guid = GUID_random();
419         bool ret = true;
420
421         /* Choose a random name in case the state is left a little funky. */
422         snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
423                  generate_random_str(tctx, 8));
424
425         smb2_util_unlink(tree, fname);
426
427         smb2_oplock_create_share(&io, fname,
428                                  smb2_util_share_access(""),
429                                  smb2_util_oplock_level("b"));
430         io.in.durable_open = false;
431         io.in.durable_open_v2 = true;
432         io.in.persistent_open = false;
433         io.in.create_guid = create_guid;
434         io.in.timeout = UINT32_MAX;
435
436         status = smb2_create(tree, mem_ctx, &io);
437         CHECK_STATUS(status, NT_STATUS_OK);
438         _h = io.out.file.handle;
439         h = &_h;
440         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
441         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
442         CHECK_VAL(io.out.durable_open, false);
443         CHECK_VAL(io.out.durable_open_v2, true);
444         CHECK_VAL(io.out.persistent_open, false);
445         CHECK_VAL(io.out.timeout, io.in.timeout);
446
447         /* try a durable reconnect while the file is still open */
448         ZERO_STRUCT(io);
449         io.in.fname = "";
450         io.in.durable_handle_v2 = h;
451         io.in.create_guid = create_guid;
452         status = smb2_create(tree, mem_ctx, &io);
453         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
454
455 done:
456         if (h != NULL) {
457                 smb2_util_close(tree, *h);
458         }
459
460         smb2_util_unlink(tree, fname);
461
462         talloc_free(tree);
463
464         talloc_free(mem_ctx);
465
466         return ret;
467 }
468
469 /**
470  * basic test for doing a durable open
471  * tcp disconnect, reconnect, do a durable reopen (succeeds)
472  */
473 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
474                                   struct smb2_tree *tree)
475 {
476         NTSTATUS status;
477         TALLOC_CTX *mem_ctx = talloc_new(tctx);
478         char fname[256];
479         struct smb2_handle _h;
480         struct smb2_handle *h = NULL;
481         struct smb2_create io;
482         struct GUID create_guid = 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, reconnect and then do durable reopen */
512         talloc_free(tree);
513         tree = NULL;
514
515         if (!torture_smb2_connection(tctx, &tree)) {
516                 torture_warning(tctx, "couldn't reconnect, bailing\n");
517                 ret = false;
518                 goto done;
519         }
520
521         ZERO_STRUCT(io);
522         io.in.fname = "";
523         io.in.durable_handle_v2 = h;
524         status = smb2_create(tree, mem_ctx, &io);
525         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
526
527         ZERO_STRUCT(io);
528         io.in.fname = "__non_existing_fname__";
529         io.in.durable_handle_v2 = h;
530         status = smb2_create(tree, mem_ctx, &io);
531         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
532
533         ZERO_STRUCT(io);
534         io.in.fname = fname;
535         io.in.durable_handle_v2 = h;
536         status = smb2_create(tree, mem_ctx, &io);
537         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
538
539         ZERO_STRUCT(io);
540         /*
541          * These are completely ignored by the server
542          */
543         io.in.security_flags = 0x78;
544         io.in.oplock_level = 0x78;
545         io.in.impersonation_level = 0x12345678;
546         io.in.create_flags = 0x12345678;
547         io.in.reserved = 0x12345678;
548         io.in.desired_access = 0x12345678;
549         io.in.file_attributes = 0x12345678;
550         io.in.share_access = 0x12345678;
551         io.in.create_disposition = 0x12345678;
552         io.in.create_options = 0x12345678;
553         io.in.fname = "__non_existing_fname__";
554
555         /*
556          * only io.in.durable_handle_v2 and
557          * io.in.create_guid are checked
558          */
559         io.in.durable_open_v2 = false;
560         io.in.durable_handle_v2 = h;
561         io.in.create_guid = create_guid;
562         h = NULL;
563
564         status = smb2_create(tree, mem_ctx, &io);
565         CHECK_STATUS(status, NT_STATUS_OK);
566         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
567         CHECK_VAL(io.out.durable_open, false);
568         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
569         CHECK_VAL(io.out.persistent_open, false);
570         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
571         _h = io.out.file.handle;
572         h = &_h;
573
574 done:
575         if (h != NULL) {
576                 smb2_util_close(tree, *h);
577         }
578
579         smb2_util_unlink(tree, fname);
580
581         talloc_free(tree);
582
583         talloc_free(mem_ctx);
584
585         return ret;
586 }
587
588 /**
589  * Test durable request / reconnect with AppInstanceId
590  */
591 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
592                                        struct smb2_tree *tree1,
593                                        struct smb2_tree *tree2)
594 {
595         NTSTATUS status;
596         TALLOC_CTX *mem_ctx = talloc_new(tctx);
597         char fname[256];
598         struct smb2_handle _h1, _h2;
599         struct smb2_handle *h1 = NULL, *h2 = NULL;
600         struct smb2_create io1, io2;
601         bool ret = true;
602         struct GUID create_guid_1 = GUID_random();
603         struct GUID create_guid_2 = GUID_random();
604         struct GUID app_instance_id = GUID_random();
605
606         /* Choose a random name in case the state is left a little funky. */
607         snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
608                  generate_random_str(tctx, 8));
609
610         smb2_util_unlink(tree1, fname);
611
612         ZERO_STRUCT(break_info);
613         tree1->session->transport->oplock.handler = torture_oplock_handler;
614         tree1->session->transport->oplock.private_data = tree1;
615
616         smb2_oplock_create_share(&io1, fname,
617                                  smb2_util_share_access(""),
618                                  smb2_util_oplock_level("b"));
619         io1.in.durable_open = false;
620         io1.in.durable_open_v2 = true;
621         io1.in.persistent_open = false;
622         io1.in.create_guid = create_guid_1;
623         io1.in.app_instance_id = &app_instance_id;
624         io1.in.timeout = UINT32_MAX;
625
626         status = smb2_create(tree1, mem_ctx, &io1);
627         CHECK_STATUS(status, NT_STATUS_OK);
628         _h1 = io1.out.file.handle;
629         h1 = &_h1;
630         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
631         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
632         CHECK_VAL(io1.out.durable_open, false);
633         CHECK_VAL(io1.out.durable_open_v2, true);
634         CHECK_VAL(io1.out.persistent_open, false);
635         CHECK_VAL(io1.out.timeout, io1.in.timeout);
636
637         /*
638          * try to open the file as durable from a second tree with
639          * a different create guid but the same app_instance_id
640          * while the first handle is still open.
641          */
642
643         smb2_oplock_create_share(&io2, fname,
644                                  smb2_util_share_access(""),
645                                  smb2_util_oplock_level("b"));
646         io2.in.durable_open = false;
647         io2.in.durable_open_v2 = true;
648         io2.in.persistent_open = false;
649         io2.in.create_guid = create_guid_2;
650         io2.in.app_instance_id = &app_instance_id;
651         io2.in.timeout = UINT32_MAX;
652
653         status = smb2_create(tree2, mem_ctx, &io2);
654         CHECK_STATUS(status, NT_STATUS_OK);
655         _h2 = io2.out.file.handle;
656         h2 = &_h2;
657         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
658         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
659         CHECK_VAL(io2.out.durable_open, false);
660         CHECK_VAL(io2.out.durable_open_v2, true);
661         CHECK_VAL(io2.out.persistent_open, false);
662         CHECK_VAL(io2.out.timeout, io2.in.timeout);
663
664         CHECK_VAL(break_info.count, 0);
665
666         status = smb2_util_close(tree1, *h1);
667         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
668         h1 = NULL;
669
670 done:
671         if (h1 != NULL) {
672                 smb2_util_close(tree1, *h1);
673         }
674         if (h2 != NULL) {
675                 smb2_util_close(tree2, *h2);
676         }
677
678         smb2_util_unlink(tree2, fname);
679
680         talloc_free(tree1);
681         talloc_free(tree2);
682
683         talloc_free(mem_ctx);
684
685         return ret;
686 }
687
688
689 /**
690  * basic persistent open test.
691  *
692  * This test tests durable open with all possible oplock types.
693  */
694
695 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
696 {
697         { "", "", true, true },
698         { "", "R", true, true },
699         { "", "W", true, true },
700         { "", "D", true, true },
701         { "", "RD", true, true },
702         { "", "RW", true, true },
703         { "", "WD", true, true },
704         { "", "RWD", true, true },
705
706         { "s", "", true, true },
707         { "s", "R", true, true },
708         { "s", "W", true, true },
709         { "s", "D", true, true },
710         { "s", "RD", true, true },
711         { "s", "RW", true, true },
712         { "s", "WD", true, true },
713         { "s", "RWD", true, true },
714
715         { "x", "", true, true },
716         { "x", "R", true, true },
717         { "x", "W", true, true },
718         { "x", "D", true, true },
719         { "x", "RD", true, true },
720         { "x", "RW", true, true },
721         { "x", "WD", true, true },
722         { "x", "RWD", true, true },
723
724         { "b", "", true, true },
725         { "b", "R", true, true },
726         { "b", "W", true, true },
727         { "b", "D", true, true },
728         { "b", "RD", true, true },
729         { "b", "RW", true, true },
730         { "b", "WD", true, true },
731         { "b", "RWD", true, true },
732 };
733
734 bool test_persistent_open_oplock(struct torture_context *tctx,
735                                  struct smb2_tree *tree)
736 {
737         char fname[256];
738         bool ret = true;
739         uint32_t share_capabilities;
740         bool share_is_ca = false;
741         struct durable_open_vs_oplock *table;
742
743         /* Choose a random name in case the state is left a little funky. */
744         snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
745
746         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
747         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
748
749         if (share_is_ca) {
750                 table = persistent_open_oplock_ca_table;
751         } else {
752                 table = durable_open_vs_oplock_table;
753         }
754
755         ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
756                                                 true, /* request_persistent */
757                                                 table,
758                                                 NUM_OPLOCK_OPEN_TESTS);
759
760         talloc_free(tree);
761
762         return ret;
763 }
764
765 /**
766  * basic persistent handle open test.
767  * persistent state should only be granted when requested
768  * along with a batch oplock or a handle lease.
769  *
770  * This test tests persistent open with all valid lease types.
771  */
772
773 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
774 {
775         { "", "", true, true },
776         { "", "R", true, true },
777         { "", "W", true, true },
778         { "", "D", true, true },
779         { "", "RW", true, true },
780         { "", "RD", true, true },
781         { "", "WD", true, true },
782         { "", "RWD", true, true },
783
784         { "R", "", true, true },
785         { "R", "R", true, true },
786         { "R", "W", true, true },
787         { "R", "D", true, true },
788         { "R", "RW", true, true },
789         { "R", "RD", true, true },
790         { "R", "DW", true, true },
791         { "R", "RWD", true, true },
792
793         { "RW", "", true, true },
794         { "RW", "R", true, true },
795         { "RW", "W", true, true },
796         { "RW", "D", true, true },
797         { "RW", "RW", true, true },
798         { "RW", "RD", true, true },
799         { "RW", "WD", true, true },
800         { "RW", "RWD", true, true },
801
802         { "RH", "", true, true },
803         { "RH", "R", true, true },
804         { "RH", "W", true, true },
805         { "RH", "D", true, true },
806         { "RH", "RW", true, true },
807         { "RH", "RD", true, true },
808         { "RH", "WD", true, true },
809         { "RH", "RWD", true, true },
810
811         { "RHW", "", true, true },
812         { "RHW", "R", true, true },
813         { "RHW", "W", true, true },
814         { "RHW", "D", true, true },
815         { "RHW", "RW", true, true },
816         { "RHW", "RD", true, true },
817         { "RHW", "WD", true, true },
818         { "RHW", "RWD", true, true },
819 };
820
821 bool test_persistent_open_lease(struct torture_context *tctx,
822                                 struct smb2_tree *tree)
823 {
824         char fname[256];
825         bool ret = true;
826         uint32_t caps;
827         uint32_t share_capabilities;
828         bool share_is_ca;
829         struct durable_open_vs_lease *table;
830
831         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
832         if (!(caps & SMB2_CAP_LEASING)) {
833                 torture_skip(tctx, "leases are not supported");
834         }
835
836         /* Choose a random name in case the state is left a little funky. */
837         snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
838
839         share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
840         share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
841
842         if (share_is_ca) {
843                 table = persistent_open_lease_ca_table;
844         } else {
845                 table = durable_open_vs_lease_table;
846         }
847
848         ret = test_durable_v2_open_lease_table(tctx, tree, fname,
849                                                true, /* request_persistent */
850                                                table,
851                                                NUM_LEASE_OPEN_TESTS);
852
853         talloc_free(tree);
854
855         return ret;
856 }
857
858 struct torture_suite *torture_smb2_durable_v2_open_init(void)
859 {
860         struct torture_suite *suite =
861             torture_suite_create(talloc_autofree_context(), "durable-v2-open");
862
863         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
864         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
865         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
866         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
867         torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
868         torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
869         torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
870
871         suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
872
873         return suite;
874 }