s4:torture:smb2: add new durable-open.reopen1a test
[metze/samba/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7    Copyright (C) Michael Adam 2011-2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 #define CHECK_VAL(v, correct) do { \
32         if ((v) != (correct)) { \
33                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35                 ret = false; \
36         }} while (0)
37
38 #define CHECK_NOT_VAL(v, correct) do { \
39         if ((v) == (correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
42                 ret = false; \
43         }} while (0)
44
45 #define CHECK_STATUS(status, correct) do { \
46         if (!NT_STATUS_EQUAL(status, correct)) { \
47                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48                        nt_errstr(status), nt_errstr(correct)); \
49                 ret = false; \
50                 goto done; \
51         }} while (0)
52
53 #define CHECK_CREATED(__io, __created, __attribute)                     \
54         do {                                                            \
55                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
57                 CHECK_VAL((__io)->out.size, 0);                         \
58                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
59                 CHECK_VAL((__io)->out.reserved2, 0);                    \
60         } while(0)
61
62 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size)  \
63         do {                                                                    \
64                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
65                 CHECK_VAL((__io)->out.alloc_size, (__alloc_size));              \
66                 CHECK_VAL((__io)->out.size, (__size));                          \
67                 CHECK_VAL((__io)->out.file_attr, (__attribute));                \
68                 CHECK_VAL((__io)->out.reserved2, 0);                            \
69         } while(0)
70
71
72
73 /**
74  * basic durable_open test.
75  * durable state should only be granted when requested
76  * along with a batch oplock or a handle lease.
77  *
78  * This test tests durable open with all possible oplock types.
79  */
80
81 struct durable_open_vs_oplock {
82         const char *level;
83         const char *share_mode;
84         bool expected;
85 };
86
87 #define NUM_OPLOCK_TYPES 4
88 #define NUM_SHARE_MODES 8
89 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
90 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
91 {
92         { "", "", false },
93         { "", "R", false },
94         { "", "W", false },
95         { "", "D", false },
96         { "", "RD", false },
97         { "", "RW", false },
98         { "", "WD", false },
99         { "", "RWD", false },
100
101         { "s", "", false },
102         { "s", "R", false },
103         { "s", "W", false },
104         { "s", "D", false },
105         { "s", "RD", false },
106         { "s", "RW", false },
107         { "s", "WD", false },
108         { "s", "RWD", false },
109
110         { "x", "", false },
111         { "x", "R", false },
112         { "x", "W", false },
113         { "x", "D", false },
114         { "x", "RD", false },
115         { "x", "RW", false },
116         { "x", "WD", false },
117         { "x", "RWD", false },
118
119         { "b", "", true },
120         { "b", "R", true },
121         { "b", "W", true },
122         { "b", "D", true },
123         { "b", "RD", true },
124         { "b", "RW", true },
125         { "b", "WD", true },
126         { "b", "RWD", true },
127 };
128
129 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
130                                               struct smb2_tree *tree,
131                                               const char *fname,
132                                               struct durable_open_vs_oplock test)
133 {
134         NTSTATUS status;
135         TALLOC_CTX *mem_ctx = talloc_new(tctx);
136         struct smb2_handle _h;
137         struct smb2_handle *h = NULL;
138         bool ret = true;
139         struct smb2_create io;
140
141         smb2_util_unlink(tree, fname);
142
143         smb2_oplock_create_share(&io, fname,
144                                  smb2_util_share_access(test.share_mode),
145                                  smb2_util_oplock_level(test.level));
146         io.in.durable_open = true;
147
148         status = smb2_create(tree, mem_ctx, &io);
149         CHECK_STATUS(status, NT_STATUS_OK);
150         _h = io.out.file.handle;
151         h = &_h;
152         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
153         CHECK_VAL(io.out.durable_open, test.expected);
154         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
155
156 done:
157         if (h != NULL) {
158                 smb2_util_close(tree, *h);
159         }
160         smb2_util_unlink(tree, fname);
161         talloc_free(mem_ctx);
162
163         return ret;
164 }
165
166 static bool test_durable_open_open_oplock(struct torture_context *tctx,
167                                           struct smb2_tree *tree)
168 {
169         TALLOC_CTX *mem_ctx = talloc_new(tctx);
170         char fname[256];
171         bool ret = true;
172         int i;
173
174         /* Choose a random name in case the state is left a little funky. */
175         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
176
177         smb2_util_unlink(tree, fname);
178
179         /* test various oplock levels with durable open */
180
181         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
182                 ret = test_one_durable_open_open_oplock(tctx,
183                                                         tree,
184                                                         fname,
185                                                         durable_open_vs_oplock_table[i]);
186                 if (ret == false) {
187                         goto done;
188                 }
189         }
190
191 done:
192         smb2_util_unlink(tree, fname);
193         talloc_free(tree);
194         talloc_free(mem_ctx);
195
196         return ret;
197 }
198
199 /**
200  * basic durable_open test.
201  * durable state should only be granted when requested
202  * along with a batch oplock or a handle lease.
203  *
204  * This test tests durable open with all valid lease types.
205  */
206
207 struct durable_open_vs_lease {
208         const char *type;
209         const char *share_mode;
210         bool expected;
211 };
212
213 #define NUM_LEASE_TYPES 5
214 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
215 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
216 {
217         { "", "", false },
218         { "", "R", false },
219         { "", "W", false },
220         { "", "D", false },
221         { "", "RW", false },
222         { "", "RD", false },
223         { "", "WD", false },
224         { "", "RWD", false },
225
226         { "R", "", false },
227         { "R", "R", false },
228         { "R", "W", false },
229         { "R", "D", false },
230         { "R", "RW", false },
231         { "R", "RD", false },
232         { "R", "DW", false },
233         { "R", "RWD", false },
234
235         { "RW", "", false },
236         { "RW", "R", false },
237         { "RW", "W", false },
238         { "RW", "D", false },
239         { "RW", "RW", false },
240         { "RW", "RD", false },
241         { "RW", "WD", false },
242         { "RW", "RWD", false },
243
244         { "RH", "", true },
245         { "RH", "R", true },
246         { "RH", "W", true },
247         { "RH", "D", true },
248         { "RH", "RW", true },
249         { "RH", "RD", true },
250         { "RH", "WD", true },
251         { "RH", "RWD", true },
252
253         { "RHW", "", true },
254         { "RHW", "R", true },
255         { "RHW", "W", true },
256         { "RHW", "D", true },
257         { "RHW", "RW", true },
258         { "RHW", "RD", true },
259         { "RHW", "WD", true },
260         { "RHW", "RWD", true },
261 };
262
263 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
264                                              struct smb2_tree *tree,
265                                              const char *fname,
266                                              struct durable_open_vs_lease test)
267 {
268         NTSTATUS status;
269         TALLOC_CTX *mem_ctx = talloc_new(tctx);
270         struct smb2_handle _h;
271         struct smb2_handle *h = NULL;
272         bool ret = true;
273         struct smb2_create io;
274         struct smb2_lease ls;
275         uint64_t lease;
276         uint32_t caps;
277
278         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
279         if (!(caps & SMB2_CAP_LEASING)) {
280                 torture_skip(tctx, "leases are not supported");
281         }
282
283         smb2_util_unlink(tree, fname);
284
285         lease = random();
286
287         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
288                                 smb2_util_share_access(test.share_mode),
289                                 lease,
290                                 smb2_util_lease_state(test.type));
291         io.in.durable_open = true;
292
293         status = smb2_create(tree, mem_ctx, &io);
294         CHECK_STATUS(status, NT_STATUS_OK);
295         _h = io.out.file.handle;
296         h = &_h;
297         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
298         CHECK_VAL(io.out.durable_open, test.expected);
299         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
300         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
301         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
302         CHECK_VAL(io.out.lease_response.lease_state,
303                   smb2_util_lease_state(test.type));
304 done:
305         if (h != NULL) {
306                 smb2_util_close(tree, *h);
307         }
308         smb2_util_unlink(tree, fname);
309         talloc_free(mem_ctx);
310
311         return ret;
312 }
313
314 static bool test_durable_open_open_lease(struct torture_context *tctx,
315                                          struct smb2_tree *tree)
316 {
317         TALLOC_CTX *mem_ctx = talloc_new(tctx);
318         char fname[256];
319         bool ret = true;
320         int i;
321         uint32_t caps;
322
323         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
324         if (!(caps & SMB2_CAP_LEASING)) {
325                 torture_skip(tctx, "leases are not supported");
326         }
327
328         /* Choose a random name in case the state is left a little funky. */
329         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
330
331         smb2_util_unlink(tree, fname);
332
333
334         /* test various oplock levels with durable open */
335
336         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
337                 ret = test_one_durable_open_open_lease(tctx,
338                                                        tree,
339                                                        fname,
340                                                        durable_open_vs_lease_table[i]);
341                 if (ret == false) {
342                         goto done;
343                 }
344         }
345
346 done:
347         smb2_util_unlink(tree, fname);
348         talloc_free(tree);
349         talloc_free(mem_ctx);
350
351         return ret;
352 }
353
354 /**
355  * basic test for doing a durable open
356  * and do a durable reopen on the same connection
357  * while the first open is still active (fails)
358  */
359 static bool test_durable_open_reopen1(struct torture_context *tctx,
360                                       struct smb2_tree *tree)
361 {
362         NTSTATUS status;
363         TALLOC_CTX *mem_ctx = talloc_new(tctx);
364         char fname[256];
365         struct smb2_handle _h;
366         struct smb2_handle *h = NULL;
367         struct smb2_create io1, io2;
368         bool ret = true;
369
370         /* Choose a random name in case the state is left a little funky. */
371         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
372                  generate_random_str(tctx, 8));
373
374         smb2_util_unlink(tree, fname);
375
376         smb2_oplock_create_share(&io1, fname,
377                                  smb2_util_share_access(""),
378                                  smb2_util_oplock_level("b"));
379         io1.in.durable_open = true;
380
381         status = smb2_create(tree, mem_ctx, &io1);
382         CHECK_STATUS(status, NT_STATUS_OK);
383         _h = io1.out.file.handle;
384         h = &_h;
385         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
386         CHECK_VAL(io1.out.durable_open, true);
387         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
388
389         /* try a durable reconnect while the file is still open */
390         ZERO_STRUCT(io2);
391         io2.in.fname = fname;
392         io2.in.durable_handle = h;
393
394         status = smb2_create(tree, mem_ctx, &io2);
395         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
396
397 done:
398         if (h != NULL) {
399                 smb2_util_close(tree, *h);
400         }
401
402         smb2_util_unlink(tree, fname);
403
404         talloc_free(tree);
405
406         talloc_free(mem_ctx);
407
408         return ret;
409 }
410
411 /**
412  * Basic test for doing a durable open
413  * and do a session reconnect while the first
414  * session is still active and the handle is
415  * still open in the client.
416  * This closes the original session and  a
417  * durable reconnect on the new session succeeds.
418  */
419 static bool test_durable_open_reopen1a(struct torture_context *tctx,
420                                        struct smb2_tree *tree)
421 {
422         NTSTATUS status;
423         TALLOC_CTX *mem_ctx = talloc_new(tctx);
424         char fname[256];
425         struct smb2_handle _h;
426         struct smb2_handle *h = NULL;
427         struct smb2_create io1, io2;
428         bool ret = true;
429         struct smb2_tree *tree2 = NULL;
430         uint64_t previous_session_id;
431         struct smbcli_options options;
432
433         options = tree->session->transport->options;
434
435         /* Choose a random name in case the state is left a little funky. */
436         snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
437                  generate_random_str(tctx, 8));
438
439         smb2_util_unlink(tree, fname);
440
441         smb2_oplock_create_share(&io1, fname,
442                                  smb2_util_share_access(""),
443                                  smb2_util_oplock_level("b"));
444         io1.in.durable_open = true;
445
446         status = smb2_create(tree, mem_ctx, &io1);
447         CHECK_STATUS(status, NT_STATUS_OK);
448         _h = io1.out.file.handle;
449         h = &_h;
450         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
451         CHECK_VAL(io1.out.durable_open, true);
452         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
453
454         /*
455          * a session reconnect on a second tcp connection
456          */
457
458         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
459
460         if (!torture_smb2_connection_ext(tctx, previous_session_id,
461                                          &options, &tree2))
462         {
463                 torture_warning(tctx, "couldn't reconnect, bailing\n");
464                 ret = false;
465                 goto done;
466         }
467
468         /*
469          * check that this has deleted the old session
470          */
471
472         ZERO_STRUCT(io2);
473         io2.in.fname = fname;
474         io2.in.durable_handle = h;
475
476         status = smb2_create(tree, mem_ctx, &io2);
477         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
478
479         /*
480          * but a durable reconnect on the new session succeeds:
481          */
482
483         ZERO_STRUCT(io2);
484         io2.in.fname = fname;
485         io2.in.durable_handle = h;
486
487         status = smb2_create(tree2, mem_ctx, &io2);
488         CHECK_STATUS(status, NT_STATUS_OK);
489         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
490         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
491         _h = io2.out.file.handle;
492         h = &_h;
493
494 done:
495         if (h != NULL) {
496                 smb2_util_close(tree2, *h);
497         }
498
499         smb2_util_unlink(tree2, fname);
500
501         talloc_free(tree2);
502
503         talloc_free(tree);
504
505         talloc_free(mem_ctx);
506
507         return ret;
508 }
509
510 /**
511  * basic test for doing a durable open
512  * tcp disconnect, reconnect, do a durable reopen (succeeds)
513  */
514 static bool test_durable_open_reopen2(struct torture_context *tctx,
515                                       struct smb2_tree *tree)
516 {
517         NTSTATUS status;
518         TALLOC_CTX *mem_ctx = talloc_new(tctx);
519         char fname[256];
520         struct smb2_handle _h;
521         struct smb2_handle *h = NULL;
522         struct smb2_create io;
523         bool ret = true;
524
525         /* Choose a random name in case the state is left a little funky. */
526         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
527                  generate_random_str(tctx, 8));
528
529         smb2_util_unlink(tree, fname);
530
531         smb2_oplock_create_share(&io, fname,
532                                  smb2_util_share_access(""),
533                                  smb2_util_oplock_level("b"));
534         io.in.durable_open = true;
535
536         status = smb2_create(tree, mem_ctx, &io);
537         CHECK_STATUS(status, NT_STATUS_OK);
538         _h = io.out.file.handle;
539         h = &_h;
540         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
541         CHECK_VAL(io.out.durable_open, true);
542         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
543
544         /* disconnect, leaving the durable in place */
545         TALLOC_FREE(tree);
546
547         if (!torture_smb2_connection(tctx, &tree)) {
548                 torture_warning(tctx, "couldn't reconnect, bailing\n");
549                 ret = false;
550                 goto done;
551         }
552
553         ZERO_STRUCT(io);
554         /* the path name is ignored by the server */
555         io.in.fname = fname;
556         io.in.durable_handle = h; /* durable v1 reconnect request */
557         h = NULL;
558
559         status = smb2_create(tree, mem_ctx, &io);
560         CHECK_STATUS(status, NT_STATUS_OK);
561         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
562         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
563         _h = io.out.file.handle;
564         h = &_h;
565
566         /* disconnect again, leaving the durable in place */
567         TALLOC_FREE(tree);
568
569         if (!torture_smb2_connection(tctx, &tree)) {
570                 torture_warning(tctx, "couldn't reconnect, bailing\n");
571                 ret = false;
572                 goto done;
573         }
574
575         /*
576          * show that the filename and many other fields
577          * are ignored. only the reconnect request blob
578          * is important.
579          */
580         ZERO_STRUCT(io);
581         /* the path name is 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         io.in.durable_handle = h; /* durable v1 reconnect request */
594         h = NULL;
595
596         status = smb2_create(tree, mem_ctx, &io);
597         CHECK_STATUS(status, NT_STATUS_OK);
598         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
599         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
600         _h = io.out.file.handle;
601         h = &_h;
602
603         /* disconnect, leaving the durable in place */
604         TALLOC_FREE(tree);
605
606         if (!torture_smb2_connection(tctx, &tree)) {
607                 torture_warning(tctx, "couldn't reconnect, bailing\n");
608                 ret = false;
609                 goto done;
610         }
611
612         /*
613          * show that an additionally specified durable v1 request
614          * is ignored by the server.
615          * See MS-SMB2, 3.3.5.9.7
616          * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
617          */
618         ZERO_STRUCT(io);
619         /* the path name is ignored by the server */
620         io.in.fname = fname;
621         io.in.durable_handle = h;  /* durable v1 reconnect request */
622         io.in.durable_open = true; /* durable v1 handle request */
623         h = NULL;
624
625         status = smb2_create(tree, mem_ctx, &io);
626         CHECK_STATUS(status, NT_STATUS_OK);
627         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
628         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
629         _h = io.out.file.handle;
630         h = &_h;
631
632 done:
633         if (tree != NULL) {
634                 if (h != NULL) {
635                         smb2_util_close(tree, *h);
636                 }
637
638                 smb2_util_unlink(tree, fname);
639
640                 talloc_free(tree);
641         }
642
643         talloc_free(mem_ctx);
644
645         return ret;
646 }
647
648 /**
649  * lease variant of reopen2
650  * basic test for doing a durable open
651  * tcp disconnect, reconnect, do a durable reopen (succeeds)
652  */
653 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
654                                             struct smb2_tree *tree)
655 {
656         NTSTATUS status;
657         TALLOC_CTX *mem_ctx = talloc_new(tctx);
658         char fname[256];
659         struct smb2_handle _h;
660         struct smb2_handle *h = NULL;
661         struct smb2_create io;
662         struct smb2_lease ls;
663         uint64_t lease_key;
664         bool ret = true;
665         struct smbcli_options options;
666         uint32_t caps;
667
668         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
669         if (!(caps & SMB2_CAP_LEASING)) {
670                 torture_skip(tctx, "leases are not supported");
671         }
672
673         options = tree->session->transport->options;
674
675         /* Choose a random name in case the state is left a little funky. */
676         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
677                  generate_random_str(tctx, 8));
678
679         smb2_util_unlink(tree, fname);
680
681         lease_key = random();
682         smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
683                           smb2_util_lease_state("RWH"));
684         io.in.durable_open = true;
685
686         status = smb2_create(tree, mem_ctx, &io);
687         CHECK_STATUS(status, NT_STATUS_OK);
688         _h = io.out.file.handle;
689         h = &_h;
690         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
691
692         CHECK_VAL(io.out.durable_open, true);
693         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
694         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
695         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
696         CHECK_VAL(io.out.lease_response.lease_state,
697                   smb2_util_lease_state("RWH"));
698         CHECK_VAL(io.out.lease_response.lease_flags, 0);
699         CHECK_VAL(io.out.lease_response.lease_duration, 0);
700
701         /* disconnect, reconnect and then do durable reopen */
702         TALLOC_FREE(tree);
703
704         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
705                 torture_warning(tctx, "couldn't reconnect, bailing\n");
706                 ret = false;
707                 goto done;
708         }
709
710
711         /* a few failure tests: */
712
713         /*
714          * several attempts without lease attached:
715          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
716          * irrespective of file name provided
717          */
718
719         ZERO_STRUCT(io);
720         io.in.fname = "";
721         io.in.durable_handle = h;
722         status = smb2_create(tree, mem_ctx, &io);
723         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
724
725         ZERO_STRUCT(io);
726         io.in.fname = "__non_existing_fname__";
727         io.in.durable_handle = h;
728         status = smb2_create(tree, mem_ctx, &io);
729         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
730
731         ZERO_STRUCT(io);
732         io.in.fname = fname;
733         io.in.durable_handle = h;
734         status = smb2_create(tree, mem_ctx, &io);
735         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
736
737         /*
738          * attempt with lease provided, but
739          * with a changed lease key. => fails
740          */
741         ZERO_STRUCT(io);
742         io.in.fname = fname;
743         io.in.durable_open = false;
744         io.in.durable_handle = h;
745         io.in.lease_request = &ls;
746         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
747         /* a wrong lease key lets the request fail */
748         ls.lease_key.data[0]++;
749
750         status = smb2_create(tree, mem_ctx, &io);
751         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
752
753         /* restore the correct lease key */
754         ls.lease_key.data[0]--;
755
756         /*
757          * this last failing attempt is almost correct:
758          * only problem is: we use the wrong filename...
759          * Note that this gives INVALID_PARAMETER.
760          * This is different from oplocks!
761          */
762         ZERO_STRUCT(io);
763         io.in.fname = "__non_existing_fname__";
764         io.in.durable_open = false;
765         io.in.durable_handle = h;
766         io.in.lease_request = &ls;
767         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
768
769         status = smb2_create(tree, mem_ctx, &io);
770         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
771
772         /*
773          * Now for a succeeding reconnect:
774          */
775
776         ZERO_STRUCT(io);
777         io.in.fname = fname;
778         io.in.durable_open = false;
779         io.in.durable_handle = h;
780         io.in.lease_request = &ls;
781         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
782
783         /* the requested lease state is irrelevant */
784         ls.lease_state = smb2_util_lease_state("");
785
786         h = NULL;
787
788         status = smb2_create(tree, mem_ctx, &io);
789         CHECK_STATUS(status, NT_STATUS_OK);
790
791         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
792         CHECK_VAL(io.out.durable_open, false);
793         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
794         CHECK_VAL(io.out.persistent_open, false);
795         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
796         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
797         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
798         CHECK_VAL(io.out.lease_response.lease_state,
799                   smb2_util_lease_state("RWH"));
800         CHECK_VAL(io.out.lease_response.lease_flags, 0);
801         CHECK_VAL(io.out.lease_response.lease_duration, 0);
802         _h = io.out.file.handle;
803         h = &_h;
804
805         /* disconnect one more time */
806         TALLOC_FREE(tree);
807
808         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
809                 torture_warning(tctx, "couldn't reconnect, bailing\n");
810                 ret = false;
811                 goto done;
812         }
813
814         /*
815          * demonstrate that various parameters are ignored
816          * in the reconnect
817          */
818
819         ZERO_STRUCT(io);
820         /*
821          * These are completely ignored by the server
822          */
823         io.in.security_flags = 0x78;
824         io.in.oplock_level = 0x78;
825         io.in.impersonation_level = 0x12345678;
826         io.in.create_flags = 0x12345678;
827         io.in.reserved = 0x12345678;
828         io.in.desired_access = 0x12345678;
829         io.in.file_attributes = 0x12345678;
830         io.in.share_access = 0x12345678;
831         io.in.create_disposition = 0x12345678;
832         io.in.create_options = 0x12345678;
833
834         /*
835          * only these are checked:
836          * - io.in.fname
837          * - io.in.durable_handle,
838          * - io.in.lease_request->lease_key
839          */
840
841         io.in.fname = fname;
842         io.in.durable_open_v2 = false;
843         io.in.durable_handle_v2 = h;
844         io.in.lease_request = &ls;
845
846         /* the requested lease state is irrelevant */
847         ls.lease_state = smb2_util_lease_state("");
848
849         h = NULL;
850
851         status = smb2_create(tree, mem_ctx, &io);
852         CHECK_STATUS(status, NT_STATUS_OK);
853
854         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
855         CHECK_VAL(io.out.durable_open, false);
856         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
857         CHECK_VAL(io.out.persistent_open, false);
858         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
859         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
860         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
861         CHECK_VAL(io.out.lease_response.lease_state,
862                   smb2_util_lease_state("RWH"));
863         CHECK_VAL(io.out.lease_response.lease_flags, 0);
864         CHECK_VAL(io.out.lease_response.lease_duration, 0);
865
866         _h = io.out.file.handle;
867         h = &_h;
868
869 done:
870         if (tree != NULL) {
871                 if (h != NULL) {
872                         smb2_util_close(tree, *h);
873                 }
874
875                 smb2_util_unlink(tree, fname);
876
877                 talloc_free(tree);
878         }
879
880         talloc_free(mem_ctx);
881
882         return ret;
883 }
884
885 /**
886  * lease v2 variant of reopen2
887  * basic test for doing a durable open
888  * tcp disconnect, reconnect, do a durable reopen (succeeds)
889  */
890 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
891                                                struct smb2_tree *tree)
892 {
893         NTSTATUS status;
894         TALLOC_CTX *mem_ctx = talloc_new(tctx);
895         char fname[256];
896         struct smb2_handle _h;
897         struct smb2_handle *h = NULL;
898         struct smb2_create io;
899         struct smb2_lease ls;
900         uint64_t lease_key;
901         bool ret = true;
902         struct smbcli_options options;
903         uint32_t caps;
904
905         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
906         if (!(caps & SMB2_CAP_LEASING)) {
907                 torture_skip(tctx, "leases are not supported");
908         }
909
910         options = tree->session->transport->options;
911
912         /* Choose a random name in case the state is left a little funky. */
913         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
914                  generate_random_str(tctx, 8));
915
916         smb2_util_unlink(tree, fname);
917
918         lease_key = random();
919         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
920                              lease_key, 0, /* parent lease key */
921                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
922         io.in.durable_open = true;
923
924         status = smb2_create(tree, mem_ctx, &io);
925         CHECK_STATUS(status, NT_STATUS_OK);
926         _h = io.out.file.handle;
927         h = &_h;
928         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
929
930         CHECK_VAL(io.out.durable_open, true);
931         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
932         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
933         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
934         CHECK_VAL(io.out.lease_response_v2.lease_state,
935                   smb2_util_lease_state("RWH"));
936         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
937         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
938
939         /* disconnect, reconnect and then do durable reopen */
940         TALLOC_FREE(tree);
941
942         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
943                 torture_warning(tctx, "couldn't reconnect, bailing\n");
944                 ret = false;
945                 goto done;
946         }
947
948         /* a few failure tests: */
949
950         /*
951          * several attempts without lease attached:
952          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
953          * irrespective of file name provided
954          */
955
956         ZERO_STRUCT(io);
957         io.in.fname = "";
958         io.in.durable_handle = h;
959         status = smb2_create(tree, mem_ctx, &io);
960         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
961
962         ZERO_STRUCT(io);
963         io.in.fname = "__non_existing_fname__";
964         io.in.durable_handle = h;
965         status = smb2_create(tree, mem_ctx, &io);
966         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
967
968         ZERO_STRUCT(io);
969         io.in.fname = fname;
970         io.in.durable_handle = h;
971         status = smb2_create(tree, mem_ctx, &io);
972         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
973
974         /*
975          * attempt with lease provided, but
976          * with a changed lease key. => fails
977          */
978         ZERO_STRUCT(io);
979         io.in.fname = fname;
980         io.in.durable_open = false;
981         io.in.durable_handle = h;
982         io.in.lease_request_v2 = &ls;
983         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
984         /* a wrong lease key lets the request fail */
985         ls.lease_key.data[0]++;
986
987         status = smb2_create(tree, mem_ctx, &io);
988         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
989
990         /* restore the correct lease key */
991         ls.lease_key.data[0]--;
992
993         /*
994          * this last failing attempt is almost correct:
995          * only problem is: we use the wrong filename...
996          * Note that this gives INVALID_PARAMETER.
997          * This is different from oplocks!
998          */
999         ZERO_STRUCT(io);
1000         io.in.fname = "__non_existing_fname__";
1001         io.in.durable_open = false;
1002         io.in.durable_handle = h;
1003         io.in.lease_request_v2 = &ls;
1004         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1005
1006         status = smb2_create(tree, mem_ctx, &io);
1007         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1008
1009         /*
1010          * Now for a succeeding reconnect:
1011          */
1012
1013         ZERO_STRUCT(io);
1014         io.in.fname = fname;
1015         io.in.durable_open = false;
1016         io.in.durable_handle = h;
1017         io.in.lease_request_v2 = &ls;
1018         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1019
1020         /* the requested lease state is irrelevant */
1021         ls.lease_state = smb2_util_lease_state("");
1022
1023         h = NULL;
1024
1025         status = smb2_create(tree, mem_ctx, &io);
1026         CHECK_STATUS(status, NT_STATUS_OK);
1027
1028         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1029         CHECK_VAL(io.out.durable_open, false);
1030         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1031         CHECK_VAL(io.out.persistent_open, false);
1032         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1033         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1034         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1035         CHECK_VAL(io.out.lease_response_v2.lease_state,
1036                   smb2_util_lease_state("RWH"));
1037         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1038         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1039         _h = io.out.file.handle;
1040         h = &_h;
1041
1042         /* disconnect one more time */
1043         TALLOC_FREE(tree);
1044
1045         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1046                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1047                 ret = false;
1048                 goto done;
1049         }
1050
1051         /*
1052          * demonstrate that various parameters are ignored
1053          * in the reconnect
1054          */
1055
1056         ZERO_STRUCT(io);
1057         /*
1058          * These are completely ignored by the server
1059          */
1060         io.in.security_flags = 0x78;
1061         io.in.oplock_level = 0x78;
1062         io.in.impersonation_level = 0x12345678;
1063         io.in.create_flags = 0x12345678;
1064         io.in.reserved = 0x12345678;
1065         io.in.desired_access = 0x12345678;
1066         io.in.file_attributes = 0x12345678;
1067         io.in.share_access = 0x12345678;
1068         io.in.create_disposition = 0x12345678;
1069         io.in.create_options = 0x12345678;
1070
1071         /*
1072          * only these are checked:
1073          * - io.in.fname
1074          * - io.in.durable_handle,
1075          * - io.in.lease_request->lease_key
1076          */
1077
1078         io.in.fname = fname;
1079         io.in.durable_open_v2 = false;
1080         io.in.durable_handle_v2 = h;
1081         io.in.lease_request_v2 = &ls;
1082
1083         /* the requested lease state is irrelevant */
1084         ls.lease_state = smb2_util_lease_state("");
1085
1086         h = NULL;
1087
1088         status = smb2_create(tree, mem_ctx, &io);
1089         CHECK_STATUS(status, NT_STATUS_OK);
1090
1091         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1092         CHECK_VAL(io.out.durable_open, false);
1093         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1094         CHECK_VAL(io.out.persistent_open, false);
1095         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1096         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1097         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1098         CHECK_VAL(io.out.lease_response_v2.lease_state,
1099                   smb2_util_lease_state("RWH"));
1100         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1101         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1102
1103         _h = io.out.file.handle;
1104         h = &_h;
1105
1106 done:
1107         if (tree != NULL) {
1108                 if (h != NULL) {
1109                         smb2_util_close(tree, *h);
1110                 }
1111
1112                 smb2_util_unlink(tree, fname);
1113
1114                 talloc_free(tree);
1115         }
1116
1117         talloc_free(mem_ctx);
1118
1119         return ret;
1120 }
1121
1122 /**
1123  * basic test for doing a durable open
1124  * tcp disconnect, reconnect with a session reconnect and
1125  * do a durable reopen (succeeds)
1126  */
1127 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1128                                        struct smb2_tree *tree)
1129 {
1130         NTSTATUS status;
1131         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1132         char fname[256];
1133         struct smb2_handle _h;
1134         struct smb2_handle *h = NULL;
1135         struct smb2_create io1, io2;
1136         uint64_t previous_session_id;
1137         bool ret = true;
1138         struct smbcli_options options;
1139
1140         options = tree->session->transport->options;
1141
1142         /* Choose a random name in case the state is left a little funky. */
1143         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1144                  generate_random_str(tctx, 8));
1145
1146         smb2_util_unlink(tree, fname);
1147
1148         smb2_oplock_create_share(&io1, fname,
1149                                  smb2_util_share_access(""),
1150                                  smb2_util_oplock_level("b"));
1151         io1.in.durable_open = true;
1152
1153         status = smb2_create(tree, mem_ctx, &io1);
1154         CHECK_STATUS(status, NT_STATUS_OK);
1155         _h = io1.out.file.handle;
1156         h = &_h;
1157         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1158         CHECK_VAL(io1.out.durable_open, true);
1159         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1160
1161         /* disconnect, reconnect and then do durable reopen */
1162         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1163         talloc_free(tree);
1164         tree = NULL;
1165
1166         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1167                                          &options, &tree))
1168         {
1169                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1170                 ret = false;
1171                 goto done;
1172         }
1173
1174         ZERO_STRUCT(io2);
1175         io2.in.fname = fname;
1176         io2.in.durable_handle = h;
1177         h = NULL;
1178
1179         status = smb2_create(tree, mem_ctx, &io2);
1180         CHECK_STATUS(status, NT_STATUS_OK);
1181         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1182         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1183         _h = io2.out.file.handle;
1184         h = &_h;
1185
1186 done:
1187         if (tree != NULL) {
1188                 if (h != NULL) {
1189                         smb2_util_close(tree, *h);
1190                 }
1191
1192                 smb2_util_unlink(tree, fname);
1193
1194                 talloc_free(tree);
1195         }
1196
1197         talloc_free(mem_ctx);
1198
1199         return ret;
1200 }
1201
1202
1203 /**
1204  * basic test for doing a durable open:
1205  * tdis, new tcon, try durable reopen (fails)
1206  */
1207 static bool test_durable_open_reopen3(struct torture_context *tctx,
1208                                       struct smb2_tree *tree)
1209 {
1210         NTSTATUS status;
1211         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1212         char fname[256];
1213         struct smb2_handle _h;
1214         struct smb2_handle *h = NULL;
1215         struct smb2_create io1, io2;
1216         bool ret = true;
1217         struct smb2_tree *tree2;
1218
1219         /* Choose a random name in case the state is left a little funky. */
1220         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1221                  generate_random_str(tctx, 8));
1222
1223         smb2_util_unlink(tree, fname);
1224
1225         smb2_oplock_create_share(&io1, fname,
1226                                  smb2_util_share_access(""),
1227                                  smb2_util_oplock_level("b"));
1228         io1.in.durable_open = true;
1229
1230         status = smb2_create(tree, mem_ctx, &io1);
1231         CHECK_STATUS(status, NT_STATUS_OK);
1232         _h = io1.out.file.handle;
1233         h = &_h;
1234         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1235         CHECK_VAL(io1.out.durable_open, true);
1236         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1237
1238         /* disconnect, reconnect and then do durable reopen */
1239         status = smb2_tdis(tree);
1240         CHECK_STATUS(status, NT_STATUS_OK);
1241
1242         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1243                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1244                 ret = false;
1245                 goto done;
1246         }
1247
1248
1249         ZERO_STRUCT(io2);
1250         io2.in.fname = fname;
1251         io2.in.durable_handle = h;
1252
1253         status = smb2_create(tree2, mem_ctx, &io2);
1254         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1255
1256 done:
1257         if (tree != NULL) {
1258                 if (h != NULL) {
1259                         smb2_util_close(tree, *h);
1260                 }
1261
1262                 smb2_util_unlink(tree2, fname);
1263
1264                 talloc_free(tree);
1265         }
1266
1267         talloc_free(mem_ctx);
1268
1269         return ret;
1270 }
1271
1272 /**
1273  * basic test for doing a durable open:
1274  * logoff, create a new session, do a durable reopen (succeeds)
1275  */
1276 static bool test_durable_open_reopen4(struct torture_context *tctx,
1277                                       struct smb2_tree *tree)
1278 {
1279         NTSTATUS status;
1280         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1281         char fname[256];
1282         struct smb2_handle _h;
1283         struct smb2_handle *h = NULL;
1284         struct smb2_create io1, io2;
1285         bool ret = true;
1286         struct smb2_transport *transport;
1287         struct smb2_session *session2;
1288         struct smb2_tree *tree2;
1289
1290         /* Choose a random name in case the state is left a little funky. */
1291         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1292                  generate_random_str(tctx, 8));
1293
1294         smb2_util_unlink(tree, fname);
1295
1296         smb2_oplock_create_share(&io1, fname,
1297                                  smb2_util_share_access(""),
1298                                  smb2_util_oplock_level("b"));
1299         io1.in.durable_open = true;
1300         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1301
1302         status = smb2_create(tree, mem_ctx, &io1);
1303         CHECK_STATUS(status, NT_STATUS_OK);
1304         _h = io1.out.file.handle;
1305         h = &_h;
1306         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1307         CHECK_VAL(io1.out.durable_open, true);
1308         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1309
1310         /*
1311          * do a session logoff, establish a new session and tree
1312          * connect on the same transport, and try a durable reopen
1313          */
1314         transport = tree->session->transport;
1315         status = smb2_logoff(tree->session);
1316         CHECK_STATUS(status, NT_STATUS_OK);
1317
1318         if (!torture_smb2_session_setup(tctx, transport,
1319                                         0, /* previous_session_id */
1320                                         mem_ctx, &session2))
1321         {
1322                 torture_warning(tctx, "session setup failed.\n");
1323                 ret = false;
1324                 goto done;
1325         }
1326
1327         /*
1328          * the session setup has talloc-stolen the transport,
1329          * so we can safely free the old tree+session for clarity
1330          */
1331         TALLOC_FREE(tree);
1332
1333         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1334                 torture_warning(tctx, "tree connect failed.\n");
1335                 ret = false;
1336                 goto done;
1337         }
1338
1339         ZERO_STRUCT(io2);
1340         io2.in.fname = fname;
1341         io2.in.durable_handle = h;
1342         h = NULL;
1343
1344         status = smb2_create(tree2, mem_ctx, &io2);
1345         CHECK_STATUS(status, NT_STATUS_OK);
1346
1347         _h = io2.out.file.handle;
1348         h = &_h;
1349         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1350         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1351
1352 done:
1353         if (tree != NULL) {
1354                 if (h != NULL) {
1355                         smb2_util_close(tree2, *h);
1356                 }
1357
1358                 smb2_util_unlink(tree2, fname);
1359
1360                 talloc_free(tree);
1361         }
1362
1363         talloc_free(mem_ctx);
1364
1365         return ret;
1366 }
1367
1368 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1369                                                struct smb2_tree *tree)
1370 {
1371         NTSTATUS status;
1372         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1373         char fname[256];
1374         struct smb2_handle _h;
1375         struct smb2_handle *h = NULL;
1376         struct smb2_create io1, io2;
1377         bool ret = true;
1378         uint8_t b = 0;
1379
1380         /* Choose a random name in case the state is left a little funky. */
1381         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1382                  generate_random_str(tctx, 8));
1383
1384         smb2_util_unlink(tree, fname);
1385
1386         smb2_oplock_create_share(&io1, fname,
1387                                  smb2_util_share_access(""),
1388                                  smb2_util_oplock_level("b"));
1389         io1.in.durable_open = true;
1390         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1391
1392         status = smb2_create(tree, mem_ctx, &io1);
1393         CHECK_STATUS(status, NT_STATUS_OK);
1394         _h = io1.out.file.handle;
1395         h = &_h;
1396         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1397         CHECK_VAL(io1.out.durable_open, true);
1398         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1399
1400         status = smb2_util_write(tree, *h, &b, 0, 1);
1401         CHECK_STATUS(status, NT_STATUS_OK);
1402
1403         /* disconnect, leaving the durable handle in place */
1404         TALLOC_FREE(tree);
1405
1406         if (!torture_smb2_connection(tctx, &tree)) {
1407                 torture_warning(tctx, "could not reconnect, bailing\n");
1408                 ret = false;
1409                 goto done;
1410         }
1411
1412         /*
1413          * Open the file on the new connection again
1414          * and check that it has been newly created,
1415          * i.e. delete on close was effective on the disconnected handle.
1416          * Also check that the file is really empty,
1417          * the previously written byte gone.
1418          */
1419         smb2_oplock_create_share(&io2, fname,
1420                                  smb2_util_share_access(""),
1421                                  smb2_util_oplock_level("b"));
1422         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1423
1424         status = smb2_create(tree, mem_ctx, &io2);
1425         CHECK_STATUS(status, NT_STATUS_OK);
1426         _h = io2.out.file.handle;
1427         h = &_h;
1428         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1429         CHECK_VAL(io2.out.durable_open, false);
1430         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1431
1432 done:
1433         if (tree != NULL) {
1434                 if (h != NULL) {
1435                         smb2_util_close(tree, *h);
1436                 }
1437
1438                 smb2_util_unlink(tree, fname);
1439
1440                 talloc_free(tree);
1441         }
1442
1443         talloc_free(mem_ctx);
1444
1445         return ret;
1446 }
1447
1448
1449 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1450                                                struct smb2_tree *tree)
1451 {
1452         NTSTATUS status;
1453         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1454         char fname[256];
1455         struct smb2_handle _h;
1456         struct smb2_handle *h = NULL;
1457         struct smb2_create io;
1458         bool ret = true;
1459         uint8_t b = 0;
1460         uint64_t previous_session_id;
1461         uint64_t alloc_size_step;
1462         struct smbcli_options options;
1463
1464         options = tree->session->transport->options;
1465
1466         /* Choose a random name in case the state is left a little funky. */
1467         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1468                  generate_random_str(tctx, 8));
1469
1470         smb2_util_unlink(tree, fname);
1471
1472         smb2_oplock_create_share(&io, fname,
1473                                  smb2_util_share_access(""),
1474                                  smb2_util_oplock_level("b"));
1475         io.in.durable_open = true;
1476         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1477
1478         status = smb2_create(tree, mem_ctx, &io);
1479         CHECK_STATUS(status, NT_STATUS_OK);
1480         _h = io.out.file.handle;
1481         h = &_h;
1482         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1483         CHECK_VAL(io.out.durable_open, true);
1484         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1485
1486         status = smb2_util_write(tree, *h, &b, 0, 1);
1487         CHECK_STATUS(status, NT_STATUS_OK);
1488
1489         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1490
1491         /* disconnect, leaving the durable handle in place */
1492         TALLOC_FREE(tree);
1493
1494         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1495                                          &options, &tree))
1496         {
1497                 torture_warning(tctx, "could not reconnect, bailing\n");
1498                 ret = false;
1499                 goto done;
1500         }
1501
1502         ZERO_STRUCT(io);
1503         io.in.fname = fname;
1504         io.in.durable_handle = h;
1505
1506         status = smb2_create(tree, mem_ctx, &io);
1507         CHECK_STATUS(status, NT_STATUS_OK);
1508         _h = io.out.file.handle;
1509         h = &_h;
1510         alloc_size_step = io.out.alloc_size;
1511         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1512         CHECK_VAL(io.out.durable_open, false);
1513         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1514
1515         /* close the file, thereby deleting it */
1516         smb2_util_close(tree, *h);
1517         status = smb2_logoff(tree->session);
1518         TALLOC_FREE(tree);
1519
1520         if (!torture_smb2_connection(tctx, &tree)) {
1521                 torture_warning(tctx, "could not reconnect, bailing\n");
1522                 ret = false;
1523                 goto done;
1524         }
1525
1526         /*
1527          * Open the file on the new connection again
1528          * and check that it has been newly created,
1529          * i.e. delete on close was effective on the reconnected handle.
1530          * Also check that the file is really empty,
1531          * the previously written byte gone.
1532          */
1533         smb2_oplock_create_share(&io, fname,
1534                                  smb2_util_share_access(""),
1535                                  smb2_util_oplock_level("b"));
1536         io.in.durable_open = true;
1537         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1538
1539         status = smb2_create(tree, mem_ctx, &io);
1540         CHECK_STATUS(status, NT_STATUS_OK);
1541         _h = io.out.file.handle;
1542         h = &_h;
1543         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1544         CHECK_VAL(io.out.durable_open, true);
1545         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1546
1547 done:
1548         if (tree != NULL) {
1549                 if (h != NULL) {
1550                         smb2_util_close(tree, *h);
1551                 }
1552
1553                 smb2_util_unlink(tree, fname);
1554
1555                 talloc_free(tree);
1556         }
1557
1558         talloc_free(mem_ctx);
1559
1560         return ret;
1561 }
1562
1563 /*
1564    basic testing of SMB2 durable opens
1565    regarding the position information on the handle
1566 */
1567 static bool test_durable_open_file_position(struct torture_context *tctx,
1568                                             struct smb2_tree *tree)
1569 {
1570         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1571         struct smb2_handle h;
1572         struct smb2_create io;
1573         NTSTATUS status;
1574         const char *fname = "durable_open_position.dat";
1575         union smb_fileinfo qfinfo;
1576         union smb_setfileinfo sfinfo;
1577         bool ret = true;
1578         uint64_t pos;
1579         uint64_t previous_session_id;
1580         struct smbcli_options options;
1581
1582         options = tree->session->transport->options;
1583
1584         smb2_util_unlink(tree, fname);
1585
1586         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1587         io.in.durable_open = true;
1588
1589         status = smb2_create(tree, mem_ctx, &io);
1590         CHECK_STATUS(status, NT_STATUS_OK);
1591         h = io.out.file.handle;
1592         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1593         CHECK_VAL(io.out.durable_open, true);
1594         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1595
1596         /* TODO: check extra blob content */
1597
1598         ZERO_STRUCT(qfinfo);
1599         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1600         qfinfo.generic.in.file.handle = h;
1601         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1602         CHECK_STATUS(status, NT_STATUS_OK);
1603         CHECK_VAL(qfinfo.position_information.out.position, 0);
1604         pos = qfinfo.position_information.out.position;
1605         torture_comment(tctx, "position: %llu\n",
1606                         (unsigned long long)pos);
1607
1608         ZERO_STRUCT(sfinfo);
1609         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1610         sfinfo.generic.in.file.handle = h;
1611         sfinfo.position_information.in.position = 0x1000;
1612         status = smb2_setinfo_file(tree, &sfinfo);
1613         CHECK_STATUS(status, NT_STATUS_OK);
1614
1615         ZERO_STRUCT(qfinfo);
1616         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1617         qfinfo.generic.in.file.handle = h;
1618         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1619         CHECK_STATUS(status, NT_STATUS_OK);
1620         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1621         pos = qfinfo.position_information.out.position;
1622         torture_comment(tctx, "position: %llu\n",
1623                         (unsigned long long)pos);
1624
1625         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1626
1627         /* tcp disconnect */
1628         talloc_free(tree);
1629         tree = NULL;
1630
1631         /* do a session reconnect */
1632         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1633                                          &options, &tree))
1634         {
1635                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1636                 ret = false;
1637                 goto done;
1638         }
1639
1640         ZERO_STRUCT(qfinfo);
1641         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1642         qfinfo.generic.in.file.handle = h;
1643         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1644         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1645
1646         ZERO_STRUCT(io);
1647         io.in.fname = fname;
1648         io.in.durable_handle = &h;
1649
1650         status = smb2_create(tree, mem_ctx, &io);
1651         CHECK_STATUS(status, NT_STATUS_OK);
1652         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1653         CHECK_VAL(io.out.reserved, 0x00);
1654         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1655         CHECK_VAL(io.out.alloc_size, 0);
1656         CHECK_VAL(io.out.size, 0);
1657         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1658         CHECK_VAL(io.out.reserved2, 0);
1659
1660         h = io.out.file.handle;
1661
1662         ZERO_STRUCT(qfinfo);
1663         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1664         qfinfo.generic.in.file.handle = h;
1665         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1666         CHECK_STATUS(status, NT_STATUS_OK);
1667         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1668         pos = qfinfo.position_information.out.position;
1669         torture_comment(tctx, "position: %llu\n",
1670                         (unsigned long long)pos);
1671
1672         smb2_util_close(tree, h);
1673
1674         talloc_free(mem_ctx);
1675
1676         smb2_util_unlink(tree, fname);
1677
1678 done:
1679         talloc_free(tree);
1680
1681         return ret;
1682 }
1683
1684 /*
1685   Open, disconnect, oplock break, reconnect.
1686 */
1687 static bool test_durable_open_oplock(struct torture_context *tctx,
1688                                      struct smb2_tree *tree1,
1689                                      struct smb2_tree *tree2)
1690 {
1691         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1692         struct smb2_create io1, io2;
1693         struct smb2_handle h1, h2;
1694         NTSTATUS status;
1695         char fname[256];
1696         bool ret = true;
1697
1698         /* Choose a random name in case the state is left a little funky. */
1699         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1700
1701         /* Clean slate */
1702         smb2_util_unlink(tree1, fname);
1703
1704         /* Create with batch oplock */
1705         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1706         io1.in.durable_open = true;
1707
1708         io2 = io1;
1709         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1710
1711         status = smb2_create(tree1, mem_ctx, &io1);
1712         CHECK_STATUS(status, NT_STATUS_OK);
1713         h1 = io1.out.file.handle;
1714         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1715         CHECK_VAL(io1.out.durable_open, true);
1716         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1717
1718         /* Disconnect after getting the batch */
1719         talloc_free(tree1);
1720         tree1 = NULL;
1721
1722         /*
1723          * Windows7 (build 7000) will break a batch oplock immediately if the
1724          * original client is gone. (ZML: This seems like a bug. It should give
1725          * some time for the client to reconnect!)
1726          */
1727         status = smb2_create(tree2, mem_ctx, &io2);
1728         CHECK_STATUS(status, NT_STATUS_OK);
1729         h2 = io2.out.file.handle;
1730         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1731         CHECK_VAL(io2.out.durable_open, true);
1732         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1733
1734         /* What if tree1 tries to come back and reclaim? */
1735         if (!torture_smb2_connection(tctx, &tree1)) {
1736                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1737                 ret = false;
1738                 goto done;
1739         }
1740
1741         ZERO_STRUCT(io1);
1742         io1.in.fname = fname;
1743         io1.in.durable_handle = &h1;
1744
1745         status = smb2_create(tree1, mem_ctx, &io1);
1746         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1747
1748  done:
1749         smb2_util_close(tree2, h2);
1750         smb2_util_unlink(tree2, fname);
1751
1752         talloc_free(tree1);
1753         talloc_free(tree2);
1754
1755         return ret;
1756 }
1757
1758 /*
1759   Open, disconnect, lease break, reconnect.
1760 */
1761 static bool test_durable_open_lease(struct torture_context *tctx,
1762                                     struct smb2_tree *tree1,
1763                                     struct smb2_tree *tree2)
1764 {
1765         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1766         struct smb2_create io1, io2;
1767         struct smb2_lease ls1, ls2;
1768         struct smb2_handle h1, h2;
1769         NTSTATUS status;
1770         char fname[256];
1771         bool ret = true;
1772         uint64_t lease1, lease2;
1773         uint32_t caps;
1774
1775         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1776         if (!(caps & SMB2_CAP_LEASING)) {
1777                 torture_skip(tctx, "leases are not supported");
1778         }
1779
1780         /*
1781          * Choose a random name and random lease in case the state is left a
1782          * little funky.
1783          */
1784         lease1 = random();
1785         lease2 = random();
1786         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1787
1788         /* Clean slate */
1789         smb2_util_unlink(tree1, fname);
1790
1791         /* Create with lease */
1792         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1793                           lease1, smb2_util_lease_state("RHW"));
1794         io1.in.durable_open = true;
1795
1796         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1797                           lease2, smb2_util_lease_state("RHW"));
1798         io2.in.durable_open = true;
1799         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1800
1801         status = smb2_create(tree1, mem_ctx, &io1);
1802         CHECK_STATUS(status, NT_STATUS_OK);
1803         h1 = io1.out.file.handle;
1804         CHECK_VAL(io1.out.durable_open, true);
1805         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1806
1807         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1808         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1809         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1810         CHECK_VAL(io1.out.lease_response.lease_state,
1811             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1812
1813         /* Disconnect after getting the lease */
1814         talloc_free(tree1);
1815         tree1 = NULL;
1816
1817         /*
1818          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1819          * even if the original client is gone. (ZML: This seems like a bug. It
1820          * should give some time for the client to reconnect! And why RH?)
1821          * 
1822          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1823          * Test is adapted accordingly.
1824          */
1825         status = smb2_create(tree2, mem_ctx, &io2);
1826         CHECK_STATUS(status, NT_STATUS_OK);
1827         h2 = io2.out.file.handle;
1828         CHECK_VAL(io2.out.durable_open, true);
1829         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1830
1831         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1832         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1833         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1834         CHECK_VAL(io2.out.lease_response.lease_state,
1835             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1836
1837         /* What if tree1 tries to come back and reclaim? */
1838         if (!torture_smb2_connection(tctx, &tree1)) {
1839                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1840                 ret = false;
1841                 goto done;
1842         }
1843
1844         ZERO_STRUCT(io1);
1845         io1.in.fname = fname;
1846         io1.in.durable_handle = &h1;
1847         io1.in.lease_request = &ls1;
1848
1849         status = smb2_create(tree1, mem_ctx, &io1);
1850         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1851
1852  done:
1853         smb2_util_close(tree2, h2);
1854         smb2_util_unlink(tree2, fname);
1855
1856         talloc_free(tree1);
1857         talloc_free(tree2);
1858
1859         return ret;
1860 }
1861
1862 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1863                                           struct smb2_tree *tree)
1864 {
1865         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1866         struct smb2_create io;
1867         struct smb2_handle h;
1868         struct smb2_lock lck;
1869         struct smb2_lock_element el[2];
1870         NTSTATUS status;
1871         char fname[256];
1872         bool ret = true;
1873
1874         /*
1875          */
1876         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1877
1878         /* Clean slate */
1879         smb2_util_unlink(tree, fname);
1880
1881         /* Create with oplock */
1882
1883         smb2_oplock_create_share(&io, fname,
1884                                  smb2_util_share_access(""),
1885                                  smb2_util_oplock_level("b"));
1886         io.in.durable_open = true;
1887
1888         status = smb2_create(tree, mem_ctx, &io);
1889         CHECK_STATUS(status, NT_STATUS_OK);
1890         h = io.out.file.handle;
1891         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1892
1893         CHECK_VAL(io.out.durable_open, true);
1894         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1895
1896         ZERO_STRUCT(lck);
1897         ZERO_STRUCT(el);
1898         lck.in.locks            = el;
1899         lck.in.lock_count       = 0x0001;
1900         lck.in.lock_sequence    = 0x00000000;
1901         lck.in.file.handle      = h;
1902         el[0].offset            = 0;
1903         el[0].length            = 1;
1904         el[0].reserved          = 0x00000000;
1905         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1906         status = smb2_lock(tree, &lck);
1907         CHECK_STATUS(status, NT_STATUS_OK);
1908
1909         /* Disconnect/Reconnect. */
1910         talloc_free(tree);
1911         tree = NULL;
1912
1913         if (!torture_smb2_connection(tctx, &tree)) {
1914                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1915                 ret = false;
1916                 goto done;
1917         }
1918
1919         ZERO_STRUCT(io);
1920         io.in.fname = fname;
1921         io.in.durable_handle = &h;
1922
1923         status = smb2_create(tree, mem_ctx, &io);
1924         CHECK_STATUS(status, NT_STATUS_OK);
1925         h = io.out.file.handle;
1926
1927         lck.in.file.handle      = h;
1928         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1929         status = smb2_lock(tree, &lck);
1930         CHECK_STATUS(status, NT_STATUS_OK);
1931
1932  done:
1933         smb2_util_close(tree, h);
1934         smb2_util_unlink(tree, fname);
1935         talloc_free(tree);
1936
1937         return ret;
1938 }
1939
1940 /*
1941   Open, take BRL, disconnect, reconnect.
1942 */
1943 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1944                                          struct smb2_tree *tree)
1945 {
1946         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1947         struct smb2_create io;
1948         struct smb2_lease ls;
1949         struct smb2_handle h;
1950         struct smb2_lock lck;
1951         struct smb2_lock_element el[2];
1952         NTSTATUS status;
1953         char fname[256];
1954         bool ret = true;
1955         uint64_t lease;
1956         uint32_t caps;
1957         struct smbcli_options options;
1958
1959         options = tree->session->transport->options;
1960
1961         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1962         if (!(caps & SMB2_CAP_LEASING)) {
1963                 torture_skip(tctx, "leases are not supported");
1964         }
1965
1966         /*
1967          * Choose a random name and random lease in case the state is left a
1968          * little funky.
1969          */
1970         lease = random();
1971         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1972
1973         /* Clean slate */
1974         smb2_util_unlink(tree, fname);
1975
1976         /* Create with lease */
1977
1978         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1979                           smb2_util_lease_state("RWH"));
1980         io.in.durable_open              = true;
1981
1982         status = smb2_create(tree, mem_ctx, &io);
1983         CHECK_STATUS(status, NT_STATUS_OK);
1984         h = io.out.file.handle;
1985         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1986
1987         CHECK_VAL(io.out.durable_open, true);
1988         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1989         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1990         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1991         CHECK_VAL(io.out.lease_response.lease_state,
1992             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1993
1994         ZERO_STRUCT(lck);
1995         ZERO_STRUCT(el);
1996         lck.in.locks            = el;
1997         lck.in.lock_count       = 0x0001;
1998         lck.in.lock_sequence    = 0x00000000;
1999         lck.in.file.handle      = h;
2000         el[0].offset            = 0;
2001         el[0].length            = 1;
2002         el[0].reserved          = 0x00000000;
2003         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
2004         status = smb2_lock(tree, &lck);
2005         CHECK_STATUS(status, NT_STATUS_OK);
2006
2007         /* Disconnect/Reconnect. */
2008         talloc_free(tree);
2009         tree = NULL;
2010
2011         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2012                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2013                 ret = false;
2014                 goto done;
2015         }
2016
2017         ZERO_STRUCT(io);
2018         io.in.fname = fname;
2019         io.in.durable_handle = &h;
2020         io.in.lease_request = &ls;
2021
2022         status = smb2_create(tree, mem_ctx, &io);
2023         CHECK_STATUS(status, NT_STATUS_OK);
2024         h = io.out.file.handle;
2025
2026         lck.in.file.handle      = h;
2027         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
2028         status = smb2_lock(tree, &lck);
2029         CHECK_STATUS(status, NT_STATUS_OK);
2030
2031  done:
2032         smb2_util_close(tree, h);
2033         smb2_util_unlink(tree, fname);
2034         talloc_free(tree);
2035
2036         return ret;
2037 }
2038
2039 /**
2040  * Open with a RH lease, disconnect, open in another tree, reconnect.
2041  *
2042  * This test actually demonstrates a minimum level of respect for the durable
2043  * open in the face of another open. As long as this test shows an inability to
2044  * reconnect after an open, the oplock/lease tests above will certainly
2045  * demonstrate an error on reconnect.
2046  */
2047 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2048                                           struct smb2_tree *tree1,
2049                                           struct smb2_tree *tree2)
2050 {
2051         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2052         struct smb2_create io1, io2;
2053         struct smb2_lease ls;
2054         struct smb2_handle h1, h2;
2055         NTSTATUS status;
2056         char fname[256];
2057         bool ret = true;
2058         uint64_t lease;
2059         uint32_t caps;
2060         struct smbcli_options options;
2061
2062         options = tree1->session->transport->options;
2063
2064         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2065         if (!(caps & SMB2_CAP_LEASING)) {
2066                 torture_skip(tctx, "leases are not supported");
2067         }
2068
2069         /*
2070          * Choose a random name and random lease in case the state is left a
2071          * little funky.
2072          */
2073         lease = random();
2074         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2075                  generate_random_str(tctx, 8));
2076
2077         /* Clean slate */
2078         smb2_util_unlink(tree1, fname);
2079
2080         /* Create with lease */
2081         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2082                                 smb2_util_share_access(""),
2083                                 lease,
2084                                 smb2_util_lease_state("RH"));
2085         io1.in.durable_open = true;
2086
2087         status = smb2_create(tree1, mem_ctx, &io1);
2088         CHECK_STATUS(status, NT_STATUS_OK);
2089         h1 = io1.out.file.handle;
2090         CHECK_VAL(io1.out.durable_open, true);
2091         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2092
2093         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2094         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2095         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2096         CHECK_VAL(io1.out.lease_response.lease_state,
2097                   smb2_util_lease_state("RH"));
2098
2099         /* Disconnect */
2100         talloc_free(tree1);
2101         tree1 = NULL;
2102
2103         /* Open the file in tree2 */
2104         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2105
2106         status = smb2_create(tree2, mem_ctx, &io2);
2107         CHECK_STATUS(status, NT_STATUS_OK);
2108         h2 = io2.out.file.handle;
2109         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2110
2111         /* Reconnect */
2112         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2113                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2114                 ret = false;
2115                 goto done;
2116         }
2117
2118         ZERO_STRUCT(io1);
2119         io1.in.fname = fname;
2120         io1.in.durable_handle = &h1;
2121         io1.in.lease_request = &ls;
2122
2123         /*
2124          * Windows7 (build 7000) will give away an open immediately if the
2125          * original client is gone. (ZML: This seems like a bug. It should give
2126          * some time for the client to reconnect!)
2127          */
2128         status = smb2_create(tree1, mem_ctx, &io1);
2129         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2130         h1 = io1.out.file.handle;
2131
2132  done:
2133         smb2_util_close(tree2, h2);
2134         smb2_util_unlink(tree2, fname);
2135         smb2_util_close(tree1, h1);
2136         smb2_util_unlink(tree1, fname);
2137
2138         talloc_free(tree1);
2139         talloc_free(tree2);
2140
2141         return ret;
2142 }
2143
2144 /**
2145  * Open with a batch oplock, disconnect, open in another tree, reconnect.
2146  *
2147  * This test actually demonstrates a minimum level of respect for the durable
2148  * open in the face of another open. As long as this test shows an inability to
2149  * reconnect after an open, the oplock/lease tests above will certainly
2150  * demonstrate an error on reconnect.
2151  */
2152 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2153                                            struct smb2_tree *tree1,
2154                                            struct smb2_tree *tree2)
2155 {
2156         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2157         struct smb2_create io1, io2;
2158         struct smb2_handle h1, h2;
2159         NTSTATUS status;
2160         char fname[256];
2161         bool ret = true;
2162
2163         /*
2164          * Choose a random name and random lease in case the state is left a
2165          * little funky.
2166          */
2167         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2168                  generate_random_str(tctx, 8));
2169
2170         /* Clean slate */
2171         smb2_util_unlink(tree1, fname);
2172
2173         /* Create with batch oplock */
2174         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2175         io1.in.durable_open = true;
2176
2177         status = smb2_create(tree1, mem_ctx, &io1);
2178         CHECK_STATUS(status, NT_STATUS_OK);
2179         h1 = io1.out.file.handle;
2180         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2181         CHECK_VAL(io1.out.durable_open, true);
2182         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2183
2184         /* Disconnect */
2185         talloc_free(tree1);
2186         tree1 = NULL;
2187
2188         /* Open the file in tree2 */
2189         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2190
2191         status = smb2_create(tree2, mem_ctx, &io2);
2192         CHECK_STATUS(status, NT_STATUS_OK);
2193         h2 = io2.out.file.handle;
2194         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2195
2196         /* Reconnect */
2197         if (!torture_smb2_connection(tctx, &tree1)) {
2198                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2199                 ret = false;
2200                 goto done;
2201         }
2202
2203         ZERO_STRUCT(io1);
2204         io1.in.fname = fname;
2205         io1.in.durable_handle = &h1;
2206
2207         status = smb2_create(tree1, mem_ctx, &io1);
2208         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2209         h1 = io1.out.file.handle;
2210
2211  done:
2212         smb2_util_close(tree2, h2);
2213         smb2_util_unlink(tree2, fname);
2214         if (tree1 != NULL) {
2215                 smb2_util_close(tree1, h1);
2216                 smb2_util_unlink(tree1, fname);
2217         }
2218
2219         talloc_free(tree1);
2220         talloc_free(tree2);
2221
2222         return ret;
2223 }
2224
2225 /**
2226  * test behaviour with initial allocation size
2227  */
2228 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2229                                          struct smb2_tree *tree)
2230 {
2231         NTSTATUS status;
2232         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2233         char fname[256];
2234         struct smb2_handle _h;
2235         struct smb2_handle *h = NULL;
2236         struct smb2_create io;
2237         bool ret = true;
2238         uint64_t previous_session_id;
2239         uint64_t alloc_size_step;
2240         uint64_t initial_alloc_size = 0x100;
2241         const uint8_t *b = NULL;
2242         struct smbcli_options options;
2243
2244         options = tree->session->transport->options;
2245
2246         /* Choose a random name in case the state is left a little funky. */
2247         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2248                  generate_random_str(tctx, 8));
2249
2250         smb2_util_unlink(tree, fname);
2251
2252         smb2_oplock_create_share(&io, fname,
2253                                  smb2_util_share_access(""),
2254                                  smb2_util_oplock_level("b"));
2255         io.in.durable_open = true;
2256         io.in.alloc_size = initial_alloc_size;
2257
2258         status = smb2_create(tree, mem_ctx, &io);
2259         CHECK_STATUS(status, NT_STATUS_OK);
2260         _h = io.out.file.handle;
2261         h = &_h;
2262         CHECK_NOT_VAL(io.out.alloc_size, 0);
2263         alloc_size_step = io.out.alloc_size;
2264         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2265                            alloc_size_step, 0);
2266         CHECK_VAL(io.out.durable_open, true);
2267         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2268
2269         /* prepare buffer */
2270         b = talloc_zero_size(mem_ctx, alloc_size_step);
2271         CHECK_NOT_VAL(b, NULL);
2272
2273         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2274
2275         /* disconnect, reconnect and then do durable reopen */
2276         talloc_free(tree);
2277         tree = NULL;
2278
2279         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2280                                          &options, &tree))
2281         {
2282                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2283                 ret = false;
2284                 goto done;
2285         }
2286
2287         ZERO_STRUCT(io);
2288         io.in.fname = fname;
2289         io.in.durable_handle = h;
2290         h = NULL;
2291
2292         status = smb2_create(tree, mem_ctx, &io);
2293         CHECK_STATUS(status, NT_STATUS_OK);
2294         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2295                            alloc_size_step, 0);
2296         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2297         _h = io.out.file.handle;
2298         h = &_h;
2299
2300         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2301
2302         /* write one byte */
2303         status = smb2_util_write(tree, *h, b, 0, 1);
2304         CHECK_STATUS(status, NT_STATUS_OK);
2305
2306         /* disconnect, reconnect and then do durable reopen */
2307         talloc_free(tree);
2308         tree = NULL;
2309
2310         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2311                                          &options, &tree))
2312         {
2313                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2314                 ret = false;
2315                 goto done;
2316         }
2317
2318         ZERO_STRUCT(io);
2319         io.in.fname = fname;
2320         io.in.durable_handle = h;
2321         h = NULL;
2322
2323         status = smb2_create(tree, mem_ctx, &io);
2324         CHECK_STATUS(status, NT_STATUS_OK);
2325         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2326                            alloc_size_step, 1);
2327         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2328         _h = io.out.file.handle;
2329         h = &_h;
2330
2331         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2332
2333         /* write more byte than initial allocation size */
2334         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2335
2336         /* disconnect, reconnect and then do durable reopen */
2337         talloc_free(tree);
2338         tree = NULL;
2339
2340         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2341                                          &options, &tree))
2342         {
2343                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2344                 ret = false;
2345                 goto done;
2346         }
2347
2348         ZERO_STRUCT(io);
2349         io.in.fname = fname;
2350         io.in.durable_handle = h;
2351         h = NULL;
2352
2353         status = smb2_create(tree, mem_ctx, &io);
2354         CHECK_STATUS(status, NT_STATUS_OK);
2355         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2356                            alloc_size_step * 2, alloc_size_step + 1);
2357         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2358         _h = io.out.file.handle;
2359         h = &_h;
2360
2361 done:
2362         if (h != NULL) {
2363                 smb2_util_close(tree, *h);
2364         }
2365
2366         smb2_util_unlink(tree, fname);
2367
2368         talloc_free(tree);
2369
2370         talloc_free(mem_ctx);
2371
2372         return ret;
2373 }
2374
2375 /**
2376  * test behaviour when a disconnect happens while creating a read-only file
2377  */
2378 static bool test_durable_open_read_only(struct torture_context *tctx,
2379                                         struct smb2_tree *tree)
2380 {
2381         NTSTATUS status;
2382         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2383         char fname[256];
2384         struct smb2_handle _h;
2385         struct smb2_handle *h = NULL;
2386         struct smb2_create io;
2387         bool ret = true;
2388         uint64_t previous_session_id;
2389         const uint8_t b = 0;
2390         uint64_t alloc_size = 0;
2391         struct smbcli_options options;
2392
2393         options = tree->session->transport->options;
2394
2395         /* Choose a random name in case the state is left a little funky. */
2396         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2397                  generate_random_str(tctx, 8));
2398
2399         smb2_util_unlink(tree, fname);
2400
2401         smb2_oplock_create_share(&io, fname,
2402                                  smb2_util_share_access(""),
2403                                  smb2_util_oplock_level("b"));
2404         io.in.durable_open = true;
2405         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2406
2407         status = smb2_create(tree, mem_ctx, &io);
2408         CHECK_STATUS(status, NT_STATUS_OK);
2409         _h = io.out.file.handle;
2410         h = &_h;
2411         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2412         CHECK_VAL(io.out.durable_open, true);
2413         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2414
2415         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2416
2417         /* write one byte */
2418         status = smb2_util_write(tree, *h, &b, 0, 1);
2419         CHECK_STATUS(status, NT_STATUS_OK);
2420
2421         /* disconnect, reconnect and then do durable reopen */
2422         talloc_free(tree);
2423         tree = NULL;
2424
2425         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2426                                          &options, &tree))
2427         {
2428                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2429                 ret = false;
2430                 goto done;
2431         }
2432
2433         ZERO_STRUCT(io);
2434         io.in.fname = fname;
2435         io.in.durable_handle = h;
2436         h = NULL;
2437
2438         status = smb2_create(tree, mem_ctx, &io);
2439         CHECK_STATUS(status, NT_STATUS_OK);
2440         alloc_size = io.out.alloc_size;
2441         CHECK_CREATED_SIZE(&io, EXISTED,
2442                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2443                            alloc_size, 1);
2444         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2445         _h = io.out.file.handle;
2446         h = &_h;
2447
2448         /* write one byte */
2449         status = smb2_util_write(tree, *h, &b, 1, 1);
2450         CHECK_STATUS(status, NT_STATUS_OK);
2451
2452 done:
2453         if (h != NULL) {
2454                 union smb_setfileinfo sfinfo;
2455
2456                 ZERO_STRUCT(sfinfo);
2457                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2458                 sfinfo.basic_info.in.file.handle = *h;
2459                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2460                 smb2_setinfo_file(tree, &sfinfo);
2461
2462                 smb2_util_close(tree, *h);
2463         }
2464
2465         smb2_util_unlink(tree, fname);
2466
2467         talloc_free(tree);
2468
2469         talloc_free(mem_ctx);
2470
2471         return ret;
2472 }
2473
2474 /**
2475  * durable open with oplock, disconnect, exit
2476  */
2477 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2478                                                 struct smb2_tree *tree)
2479 {
2480         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2481         struct smb2_create io;
2482         struct smb2_handle _h;
2483         struct smb2_handle *h = NULL;
2484         NTSTATUS status;
2485         char fname[256];
2486         bool ret = true;
2487
2488         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2489                  generate_random_str(mem_ctx, 8));
2490
2491         smb2_util_unlink(tree, fname);
2492
2493         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2494         io.in.durable_open = true;
2495
2496         status = smb2_create(tree, mem_ctx, &io);
2497         CHECK_STATUS(status, NT_STATUS_OK);
2498
2499         _h = io.out.file.handle;
2500         h = &_h;
2501
2502         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2503         CHECK_VAL(io.out.durable_open, true);
2504         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2505
2506         /* disconnect */
2507         talloc_free(tree);
2508         tree = NULL;
2509
2510 done:
2511         if (tree != NULL) {
2512                 if (h != NULL) {
2513                         smb2_util_close(tree, *h);
2514                 }
2515                 smb2_util_unlink(tree, fname);
2516         }
2517         talloc_free(mem_ctx);
2518         return ret;
2519 }
2520
2521
2522 struct torture_suite *torture_smb2_durable_open_init(void)
2523 {
2524         struct torture_suite *suite =
2525             torture_suite_create(talloc_autofree_context(), "durable-open");
2526
2527         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2528         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2529         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2530         torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2531         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2532         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2533         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2534         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2535         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2536         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2537         torture_suite_add_1smb2_test(suite, "delete_on_close1",
2538                                      test_durable_open_delete_on_close1);
2539         torture_suite_add_1smb2_test(suite, "delete_on_close2",
2540                                      test_durable_open_delete_on_close2);
2541         torture_suite_add_1smb2_test(suite, "file-position",
2542             test_durable_open_file_position);
2543         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2544         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2545         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2546         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2547         torture_suite_add_2smb2_test(suite, "open2-lease",
2548                                      test_durable_open_open2_lease);
2549         torture_suite_add_2smb2_test(suite, "open2-oplock",
2550                                      test_durable_open_open2_oplock);
2551         torture_suite_add_1smb2_test(suite, "alloc-size",
2552                                      test_durable_open_alloc_size);
2553         torture_suite_add_1smb2_test(suite, "read-only",
2554                                      test_durable_open_read_only);
2555
2556         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2557
2558         return suite;
2559 }
2560
2561 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
2562 {
2563         struct torture_suite *suite =
2564             torture_suite_create(talloc_autofree_context(),
2565                                  "durable-open-disconnect");
2566
2567         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2568                                      test_durable_open_oplock_disconnect);
2569
2570         suite->description = talloc_strdup(suite,
2571                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
2572
2573         return suite;
2574 }