8524fd6a4fd76addec62718f128c125feb0fedc5
[kai/samba.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
63 /**
64  * basic durable_open test.
65  * durable state should only be granted when requested
66  * along with a batch oplock or a handle lease.
67  *
68  * This test tests durable open with all possible oplock types.
69  */
70
71 struct durable_open_vs_oplock {
72         const char *level;
73         const char *share_mode;
74         bool expected;
75 };
76
77 #define NUM_OPLOCK_TYPES 4
78 #define NUM_SHARE_MODES 8
79 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
80 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
81 {
82         { "", "", false },
83         { "", "R", false },
84         { "", "W", false },
85         { "", "D", false },
86         { "", "RD", false },
87         { "", "RW", false },
88         { "", "WD", false },
89         { "", "RWD", false },
90
91         { "s", "", false },
92         { "s", "R", false },
93         { "s", "W", false },
94         { "s", "D", false },
95         { "s", "RD", false },
96         { "s", "RW", false },
97         { "s", "WD", false },
98         { "s", "RWD", false },
99
100         { "x", "", false },
101         { "x", "R", false },
102         { "x", "W", false },
103         { "x", "D", false },
104         { "x", "RD", false },
105         { "x", "RW", false },
106         { "x", "WD", false },
107         { "x", "RWD", false },
108
109         { "b", "", true },
110         { "b", "R", true },
111         { "b", "W", true },
112         { "b", "D", true },
113         { "b", "RD", true },
114         { "b", "RW", true },
115         { "b", "WD", true },
116         { "b", "RWD", true },
117 };
118
119 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
120                                               struct smb2_tree *tree,
121                                               const char *fname,
122                                               struct durable_open_vs_oplock test)
123 {
124         NTSTATUS status;
125         TALLOC_CTX *mem_ctx = talloc_new(tctx);
126         struct smb2_handle _h;
127         struct smb2_handle *h = NULL;
128         bool ret = true;
129         struct smb2_create io;
130
131         smb2_util_unlink(tree, fname);
132
133         smb2_oplock_create_share(&io, fname,
134                                  smb2_util_share_access(test.share_mode),
135                                  smb2_util_oplock_level(test.level));
136         io.in.durable_open = true;
137
138         status = smb2_create(tree, mem_ctx, &io);
139         CHECK_STATUS(status, NT_STATUS_OK);
140         _h = io.out.file.handle;
141         h = &_h;
142         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
143         CHECK_VAL(io.out.durable_open, test.expected);
144         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
145
146 done:
147         if (h != NULL) {
148                 smb2_util_close(tree, *h);
149         }
150         smb2_util_unlink(tree, fname);
151         talloc_free(mem_ctx);
152
153         return ret;
154 }
155
156 bool test_durable_open_open_oplock(struct torture_context *tctx,
157                                    struct smb2_tree *tree)
158 {
159         TALLOC_CTX *mem_ctx = talloc_new(tctx);
160         char fname[256];
161         bool ret = true;
162         int i;
163
164         /* Choose a random name in case the state is left a little funky. */
165         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
166
167         smb2_util_unlink(tree, fname);
168
169         /* test various oplock levels with durable open */
170
171         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
172                 ret = test_one_durable_open_open_oplock(tctx,
173                                                         tree,
174                                                         fname,
175                                                         durable_open_vs_oplock_table[i]);
176                 if (ret == false) {
177                         goto done;
178                 }
179         }
180
181 done:
182         smb2_util_unlink(tree, fname);
183         talloc_free(tree);
184         talloc_free(mem_ctx);
185
186         return ret;
187 }
188
189 /**
190  * basic durable_open test.
191  * durable state should only be granted when requested
192  * along with a batch oplock or a handle lease.
193  *
194  * This test tests durable open with all valid lease types.
195  */
196
197 struct durable_open_vs_lease {
198         const char *type;
199         const char *share_mode;
200         bool expected;
201 };
202
203 #define NUM_LEASE_TYPES 5
204 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
205 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
206 {
207         { "", "", false },
208         { "", "R", false },
209         { "", "W", false },
210         { "", "D", false },
211         { "", "RW", false },
212         { "", "RD", false },
213         { "", "WD", false },
214         { "", "RWD", false },
215
216         { "R", "", false },
217         { "R", "R", false },
218         { "R", "W", false },
219         { "R", "D", false },
220         { "R", "RW", false },
221         { "R", "RD", false },
222         { "R", "DW", false },
223         { "R", "RWD", false },
224
225         { "RW", "", false },
226         { "RW", "R", false },
227         { "RW", "W", false },
228         { "RW", "D", false },
229         { "RW", "RW", false },
230         { "RW", "RD", false },
231         { "RW", "WD", false },
232         { "RW", "RWD", false },
233
234         { "RH", "", true },
235         { "RH", "R", true },
236         { "RH", "W", true },
237         { "RH", "D", true },
238         { "RH", "RW", true },
239         { "RH", "RD", true },
240         { "RH", "WD", true },
241         { "RH", "RWD", true },
242
243         { "RHW", "", true },
244         { "RHW", "R", true },
245         { "RHW", "W", true },
246         { "RHW", "D", true },
247         { "RHW", "RW", true },
248         { "RHW", "RD", true },
249         { "RHW", "WD", true },
250         { "RHW", "RWD", true },
251 };
252
253 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
254                                              struct smb2_tree *tree,
255                                              const char *fname,
256                                              struct durable_open_vs_lease test)
257 {
258         NTSTATUS status;
259         TALLOC_CTX *mem_ctx = talloc_new(tctx);
260         struct smb2_handle _h;
261         struct smb2_handle *h = NULL;
262         bool ret = true;
263         struct smb2_create io;
264         struct smb2_lease ls;
265         uint64_t lease;
266         uint32_t caps;
267
268         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
269         if (!(caps & SMB2_CAP_LEASING)) {
270                 torture_skip(tctx, "leases are not supported");
271         }
272
273         smb2_util_unlink(tree, fname);
274
275         lease = random();
276
277         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
278                                 smb2_util_share_access(test.share_mode),
279                                 lease,
280                                 smb2_util_lease_state(test.type));
281         io.in.durable_open = true;
282
283         status = smb2_create(tree, mem_ctx, &io);
284         CHECK_STATUS(status, NT_STATUS_OK);
285         _h = io.out.file.handle;
286         h = &_h;
287         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
288         CHECK_VAL(io.out.durable_open, test.expected);
289         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
290         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
291         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
292         CHECK_VAL(io.out.lease_response.lease_state,
293                   smb2_util_lease_state(test.type));
294 done:
295         if (h != NULL) {
296                 smb2_util_close(tree, *h);
297         }
298         smb2_util_unlink(tree, fname);
299         talloc_free(mem_ctx);
300
301         return ret;
302 }
303
304 bool test_durable_open_open_lease(struct torture_context *tctx,
305                                   struct smb2_tree *tree)
306 {
307         TALLOC_CTX *mem_ctx = talloc_new(tctx);
308         char fname[256];
309         bool ret = true;
310         int i;
311         uint32_t caps;
312
313         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
314         if (!(caps & SMB2_CAP_LEASING)) {
315                 torture_skip(tctx, "leases are not supported");
316         }
317
318         /* Choose a random name in case the state is left a little funky. */
319         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
320
321         smb2_util_unlink(tree, fname);
322
323
324         /* test various oplock levels with durable open */
325
326         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
327                 ret = test_one_durable_open_open_lease(tctx,
328                                                        tree,
329                                                        fname,
330                                                        durable_open_vs_lease_table[i]);
331                 if (ret == false) {
332                         goto done;
333                 }
334         }
335
336 done:
337         smb2_util_unlink(tree, fname);
338         talloc_free(tree);
339         talloc_free(mem_ctx);
340
341         return ret;
342 }
343
344 /**
345  * basic test for doing a durable open
346  * and do a durable reopen on the same connection
347  * while the first open is still active (fails)
348  */
349 bool test_durable_open_reopen1(struct torture_context *tctx,
350                                struct smb2_tree *tree)
351 {
352         NTSTATUS status;
353         TALLOC_CTX *mem_ctx = talloc_new(tctx);
354         char fname[256];
355         struct smb2_handle _h;
356         struct smb2_handle *h = NULL;
357         struct smb2_create io1, io2;
358         bool ret = true;
359
360         /* Choose a random name in case the state is left a little funky. */
361         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
362                  generate_random_str(tctx, 8));
363
364         smb2_util_unlink(tree, fname);
365
366         smb2_oplock_create_share(&io1, fname,
367                                  smb2_util_share_access(""),
368                                  smb2_util_oplock_level("b"));
369         io1.in.durable_open = true;
370
371         status = smb2_create(tree, mem_ctx, &io1);
372         CHECK_STATUS(status, NT_STATUS_OK);
373         _h = io1.out.file.handle;
374         h = &_h;
375         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
376         CHECK_VAL(io1.out.durable_open, true);
377         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
378
379         /* try a durable reconnect while the file is still open */
380         ZERO_STRUCT(io2);
381         io2.in.fname = fname;
382         io2.in.durable_handle = h;
383
384         status = smb2_create(tree, mem_ctx, &io2);
385         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
386
387 done:
388         if (h != NULL) {
389                 smb2_util_close(tree, *h);
390         }
391
392         smb2_util_unlink(tree, fname);
393
394         talloc_free(tree);
395
396         talloc_free(mem_ctx);
397
398         return ret;
399 }
400
401 /**
402  * basic test for doing a durable open
403  * tcp disconnect, reconnect, do a durable reopen (succeeds)
404  */
405 bool test_durable_open_reopen2(struct torture_context *tctx,
406                                struct smb2_tree *tree)
407 {
408         NTSTATUS status;
409         TALLOC_CTX *mem_ctx = talloc_new(tctx);
410         char fname[256];
411         struct smb2_handle _h;
412         struct smb2_handle *h = NULL;
413         struct smb2_create io1, io2;
414         bool ret = true;
415
416         /* Choose a random name in case the state is left a little funky. */
417         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
418                  generate_random_str(tctx, 8));
419
420         smb2_util_unlink(tree, fname);
421
422         smb2_oplock_create_share(&io1, fname,
423                                  smb2_util_share_access(""),
424                                  smb2_util_oplock_level("b"));
425         io1.in.durable_open = true;
426
427         status = smb2_create(tree, mem_ctx, &io1);
428         CHECK_STATUS(status, NT_STATUS_OK);
429         _h = io1.out.file.handle;
430         h = &_h;
431         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
432         CHECK_VAL(io1.out.durable_open, true);
433         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
434
435         /* disconnect, reconnect and then do durable reopen */
436         talloc_free(tree);
437         tree = NULL;
438
439         if (!torture_smb2_connection(tctx, &tree)) {
440                 torture_warning(tctx, "couldn't reconnect, bailing\n");
441                 ret = false;
442                 goto done;
443         }
444
445         ZERO_STRUCT(io2);
446         /* the path name is ignored by the server */
447         io2.in.fname = "__non_existing_fname__";
448         io2.in.durable_handle = h;
449         h = NULL;
450
451         status = smb2_create(tree, mem_ctx, &io2);
452         CHECK_STATUS(status, NT_STATUS_OK);
453         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
454         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
455         _h = io2.out.file.handle;
456         h = &_h;
457
458 done:
459         if (h != NULL) {
460                 smb2_util_close(tree, *h);
461         }
462
463         smb2_util_unlink(tree, fname);
464
465         talloc_free(tree);
466
467         talloc_free(mem_ctx);
468
469         return ret;
470 }
471
472 /**
473  * basic test for doing a durable open
474  * tcp disconnect, reconnect with a session reconnect and
475  * do a durable reopen (succeeds)
476  */
477 bool test_durable_open_reopen2a(struct torture_context *tctx,
478                                 struct smb2_tree *tree)
479 {
480         NTSTATUS status;
481         TALLOC_CTX *mem_ctx = talloc_new(tctx);
482         char fname[256];
483         struct smb2_handle _h;
484         struct smb2_handle *h = NULL;
485         struct smb2_create io1, io2;
486         uint64_t previous_session_id;
487         bool ret = true;
488
489         /* Choose a random name in case the state is left a little funky. */
490         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
491                  generate_random_str(tctx, 8));
492
493         smb2_util_unlink(tree, fname);
494
495         smb2_oplock_create_share(&io1, fname,
496                                  smb2_util_share_access(""),
497                                  smb2_util_oplock_level("b"));
498         io1.in.durable_open = true;
499
500         status = smb2_create(tree, mem_ctx, &io1);
501         CHECK_STATUS(status, NT_STATUS_OK);
502         _h = io1.out.file.handle;
503         h = &_h;
504         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
505         CHECK_VAL(io1.out.durable_open, true);
506         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
507
508         /* disconnect, reconnect and then do durable reopen */
509         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
510         talloc_free(tree);
511         tree = NULL;
512
513         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
514                 torture_warning(tctx, "couldn't reconnect, bailing\n");
515                 ret = false;
516                 goto done;
517         }
518
519         ZERO_STRUCT(io2);
520         io2.in.fname = fname;
521         io2.in.durable_handle = h;
522         h = NULL;
523
524         status = smb2_create(tree, mem_ctx, &io2);
525         CHECK_STATUS(status, NT_STATUS_OK);
526         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
527         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
528         _h = io2.out.file.handle;
529         h = &_h;
530
531 done:
532         if (h != NULL) {
533                 smb2_util_close(tree, *h);
534         }
535
536         smb2_util_unlink(tree, fname);
537
538         talloc_free(tree);
539
540         talloc_free(mem_ctx);
541
542         return ret;
543 }
544
545
546 /**
547  * basic test for doing a durable open:
548  * tdis, new tcon, try durable reopen (fails)
549  */
550 bool test_durable_open_reopen3(struct torture_context *tctx,
551                                struct smb2_tree *tree)
552 {
553         NTSTATUS status;
554         TALLOC_CTX *mem_ctx = talloc_new(tctx);
555         char fname[256];
556         struct smb2_handle _h;
557         struct smb2_handle *h = NULL;
558         struct smb2_create io1, io2;
559         bool ret = true;
560         struct smb2_tree *tree2;
561
562         /* Choose a random name in case the state is left a little funky. */
563         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
564                  generate_random_str(tctx, 8));
565
566         smb2_util_unlink(tree, fname);
567
568         smb2_oplock_create_share(&io1, fname,
569                                  smb2_util_share_access(""),
570                                  smb2_util_oplock_level("b"));
571         io1.in.durable_open = true;
572
573         status = smb2_create(tree, mem_ctx, &io1);
574         CHECK_STATUS(status, NT_STATUS_OK);
575         _h = io1.out.file.handle;
576         h = &_h;
577         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
578         CHECK_VAL(io1.out.durable_open, true);
579         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
580
581         /* disconnect, reconnect and then do durable reopen */
582         status = smb2_tdis(tree);
583         CHECK_STATUS(status, NT_STATUS_OK);
584
585         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
586                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
587                 ret = false;
588                 goto done;
589         }
590
591
592         ZERO_STRUCT(io2);
593         io2.in.fname = fname;
594         io2.in.durable_handle = h;
595
596         status = smb2_create(tree2, mem_ctx, &io2);
597         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
598
599 done:
600         if (h != NULL) {
601                 smb2_util_close(tree, *h);
602         }
603
604         smb2_util_unlink(tree2, fname);
605
606         talloc_free(tree);
607
608         talloc_free(mem_ctx);
609
610         return ret;
611 }
612
613 /**
614  * basic test for doing a durable open:
615  * logoff, create a new session, do a durable reopen (succeeds)
616  */
617 bool test_durable_open_reopen4(struct torture_context *tctx,
618                                struct smb2_tree *tree)
619 {
620         NTSTATUS status;
621         TALLOC_CTX *mem_ctx = talloc_new(tctx);
622         char fname[256];
623         struct smb2_handle _h;
624         struct smb2_handle *h = NULL;
625         struct smb2_create io1, io2;
626         bool ret = true;
627         struct smb2_transport *transport;
628         struct smb2_session *session2;
629         struct smb2_tree *tree2;
630
631         /* Choose a random name in case the state is left a little funky. */
632         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
633                  generate_random_str(tctx, 8));
634
635         smb2_util_unlink(tree, fname);
636
637         smb2_oplock_create_share(&io1, fname,
638                                  smb2_util_share_access(""),
639                                  smb2_util_oplock_level("b"));
640         io1.in.durable_open = true;
641         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
642
643         status = smb2_create(tree, mem_ctx, &io1);
644         CHECK_STATUS(status, NT_STATUS_OK);
645         _h = io1.out.file.handle;
646         h = &_h;
647         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
648         CHECK_VAL(io1.out.durable_open, true);
649         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
650
651         /*
652          * do a session logoff, establish a new session and tree
653          * connect on the same transport, and try a durable reopen
654          */
655         transport = tree->session->transport;
656         status = smb2_logoff(tree->session);
657         CHECK_STATUS(status, NT_STATUS_OK);
658
659         if (!torture_smb2_session_setup(tctx, transport,
660                                         0, /* previous_session_id */
661                                         mem_ctx, &session2))
662         {
663                 torture_warning(tctx, "session setup failed.\n");
664                 ret = false;
665                 goto done;
666         }
667
668         /*
669          * the session setup has talloc-stolen the transport,
670          * so we can safely free the old tree+session for clarity
671          */
672         TALLOC_FREE(tree);
673
674         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
675                 torture_warning(tctx, "tree connect failed.\n");
676                 ret = false;
677                 goto done;
678         }
679
680         ZERO_STRUCT(io2);
681         io2.in.fname = fname;
682         io2.in.durable_handle = h;
683         h = NULL;
684
685         status = smb2_create(tree2, mem_ctx, &io2);
686         CHECK_STATUS(status, NT_STATUS_OK);
687
688         _h = io2.out.file.handle;
689         h = &_h;
690         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
691         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
692
693 done:
694         if (h != NULL) {
695                 smb2_util_close(tree2, *h);
696         }
697
698         smb2_util_unlink(tree2, fname);
699
700         talloc_free(tree);
701
702         talloc_free(mem_ctx);
703
704         return ret;
705 }
706
707 bool test_durable_open_delete_on_close1(struct torture_context *tctx,
708                                         struct smb2_tree *tree)
709 {
710         NTSTATUS status;
711         TALLOC_CTX *mem_ctx = talloc_new(tctx);
712         char fname[256];
713         struct smb2_handle _h;
714         struct smb2_handle *h = NULL;
715         struct smb2_create io1, io2, io3;
716         bool ret = true;
717         struct smb2_transport *transport;
718         struct smb2_session *session2;
719         struct smb2_tree *tree2;
720         union smb_fileinfo info1, info2;
721
722         /* Choose a random name in case the state is left a little funky. */
723         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
724                  generate_random_str(tctx, 8));
725
726         smb2_util_unlink(tree, fname);
727
728         smb2_oplock_create_share(&io1, fname,
729                                  smb2_util_share_access(""),
730                                  smb2_util_oplock_level("b"));
731         io1.in.durable_open = true;
732         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
733
734         status = smb2_create(tree, mem_ctx, &io1);
735         CHECK_STATUS(status, NT_STATUS_OK);
736         _h = io1.out.file.handle;
737         h = &_h;
738         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
739         CHECK_VAL(io1.out.durable_open, true);
740         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
741
742         ZERO_STRUCT(info1);
743         info1.internal_information.level = RAW_FILEINFO_INTERNAL_INFORMATION;
744         info1.internal_information.in.file.handle = _h;
745         status = smb2_getinfo_file(tree, tree, &info1);
746         CHECK_STATUS(status, NT_STATUS_OK);
747
748         /*
749          * do a session logoff, establish a new session and tree
750          * connect on the same transport, and try a durable reopen
751          */
752         transport = tree->session->transport;
753         status = smb2_logoff(tree->session);
754         CHECK_STATUS(status, NT_STATUS_OK);
755
756         if (!torture_smb2_session_setup(tctx, transport,
757                                         0, /* previous_session_id */
758                                         mem_ctx, &session2))
759         {
760                 torture_warning(tctx, "session setup failed.\n");
761                 ret = false;
762                 goto done;
763         }
764
765         /*
766          * the session setup has talloc-stolen the transport,
767          * so we can safely free the old tree+session for clarity
768          */
769         TALLOC_FREE(tree);
770
771         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
772                 torture_warning(tctx, "tree connect failed.\n");
773                 ret = false;
774                 goto done;
775         }
776
777         ZERO_STRUCT(io3);
778         io3.in.fname = fname;
779         io3.in.durable_handle = h;
780         h = NULL;
781
782         smb2_oplock_create_share(&io2, fname,
783                                  smb2_util_share_access(""),
784                                  smb2_util_oplock_level("b"));
785         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
786
787         status = smb2_create(tree2, mem_ctx, &io2);
788         CHECK_STATUS(status, NT_STATUS_OK);
789         _h = io2.out.file.handle;
790         h = &_h;
791         CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
792         CHECK_VAL(io2.out.durable_open, false);
793         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
794
795         ZERO_STRUCT(info2);
796         info2.internal_information.level = RAW_FILEINFO_INTERNAL_INFORMATION;
797         info2.internal_information.in.file.handle = _h;
798         status = smb2_getinfo_file(tree2, tree2, &info2);
799         CHECK_STATUS(status, NT_STATUS_OK);
800
801         CHECK_NOT_VAL(info1.internal_information.out.file_id,
802                       info2.internal_information.out.file_id);
803
804         status = smb2_create(tree2, mem_ctx, &io3);
805         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
806
807 done:
808         if (h != NULL) {
809                 smb2_util_close(tree2, *h);
810         }
811
812         smb2_util_unlink(tree2, fname);
813
814         talloc_free(tree);
815         talloc_free(tree2);
816
817         talloc_free(mem_ctx);
818
819         return ret;
820 }
821
822 /*
823    basic testing of SMB2 durable opens
824    regarding the position information on the handle
825 */
826 bool test_durable_open_file_position(struct torture_context *tctx,
827                                      struct smb2_tree *tree1,
828                                      struct smb2_tree *tree2)
829 {
830         TALLOC_CTX *mem_ctx = talloc_new(tctx);
831         struct smb2_handle h1, h2;
832         struct smb2_create io1, io2;
833         NTSTATUS status;
834         const char *fname = "durable_open_position.dat";
835         union smb_fileinfo qfinfo;
836         union smb_setfileinfo sfinfo;
837         bool ret = true;
838         uint64_t pos;
839
840         smb2_util_unlink(tree1, fname);
841
842         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
843         io1.in.durable_open = true;
844
845         status = smb2_create(tree1, mem_ctx, &io1);
846         CHECK_STATUS(status, NT_STATUS_OK);
847         h1 = io1.out.file.handle;
848         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
849         CHECK_VAL(io1.out.durable_open, true);
850         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
851
852         /* TODO: check extra blob content */
853
854         ZERO_STRUCT(qfinfo);
855         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
856         qfinfo.generic.in.file.handle = h1;
857         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
858         CHECK_STATUS(status, NT_STATUS_OK);
859         CHECK_VAL(qfinfo.position_information.out.position, 0);
860         pos = qfinfo.position_information.out.position;
861         torture_comment(tctx, "position: %llu\n",
862                         (unsigned long long)pos);
863
864         ZERO_STRUCT(sfinfo);
865         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
866         sfinfo.generic.in.file.handle = h1;
867         sfinfo.position_information.in.position = 0x1000;
868         status = smb2_setinfo_file(tree1, &sfinfo);
869         CHECK_STATUS(status, NT_STATUS_OK);
870
871         ZERO_STRUCT(qfinfo);
872         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
873         qfinfo.generic.in.file.handle = h1;
874         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
875         CHECK_STATUS(status, NT_STATUS_OK);
876         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
877         pos = qfinfo.position_information.out.position;
878         torture_comment(tctx, "position: %llu\n",
879                         (unsigned long long)pos);
880
881         talloc_free(tree1);
882         tree1 = NULL;
883
884         ZERO_STRUCT(qfinfo);
885         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
886         qfinfo.generic.in.file.handle = h1;
887         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
888         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
889
890         ZERO_STRUCT(io2);
891         io2.in.fname = fname;
892         io2.in.durable_handle = &h1;
893
894         status = smb2_create(tree2, mem_ctx, &io2);
895         CHECK_STATUS(status, NT_STATUS_OK);
896         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
897         CHECK_VAL(io2.out.reserved, 0x00);
898         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
899         CHECK_VAL(io2.out.alloc_size, 0);
900         CHECK_VAL(io2.out.size, 0);
901         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
902         CHECK_VAL(io2.out.reserved2, 0);
903
904         h2 = io2.out.file.handle;
905
906         ZERO_STRUCT(qfinfo);
907         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
908         qfinfo.generic.in.file.handle = h2;
909         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
910         CHECK_STATUS(status, NT_STATUS_OK);
911         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
912         pos = qfinfo.position_information.out.position;
913         torture_comment(tctx, "position: %llu\n",
914                         (unsigned long long)pos);
915
916         smb2_util_close(tree2, h2);
917
918         talloc_free(mem_ctx);
919
920         smb2_util_unlink(tree2, fname);
921 done:
922         talloc_free(tree1);
923         talloc_free(tree2);
924
925         return ret;
926 }
927
928 /*
929   Open, disconnect, oplock break, reconnect.
930 */
931 bool test_durable_open_oplock(struct torture_context *tctx,
932                               struct smb2_tree *tree1,
933                               struct smb2_tree *tree2)
934 {
935         TALLOC_CTX *mem_ctx = talloc_new(tctx);
936         struct smb2_create io1, io2;
937         struct smb2_handle h1, h2;
938         NTSTATUS status;
939         char fname[256];
940         bool ret = true;
941
942         /* Choose a random name in case the state is left a little funky. */
943         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
944
945         /* Clean slate */
946         smb2_util_unlink(tree1, fname);
947
948         /* Create with batch oplock */
949         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
950         io1.in.durable_open = true;
951
952         io2 = io1;
953         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
954
955         status = smb2_create(tree1, mem_ctx, &io1);
956         CHECK_STATUS(status, NT_STATUS_OK);
957         h1 = io1.out.file.handle;
958         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
959         CHECK_VAL(io1.out.durable_open, true);
960         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
961
962         /* Disconnect after getting the batch */
963         talloc_free(tree1);
964         tree1 = NULL;
965
966         /*
967          * Windows7 (build 7000) will break a batch oplock immediately if the
968          * original client is gone. (ZML: This seems like a bug. It should give
969          * some time for the client to reconnect!)
970          */
971         status = smb2_create(tree2, mem_ctx, &io2);
972         CHECK_STATUS(status, NT_STATUS_OK);
973         h2 = io2.out.file.handle;
974         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
975         CHECK_VAL(io2.out.durable_open, true);
976         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
977
978         /* What if tree1 tries to come back and reclaim? */
979         if (!torture_smb2_connection(tctx, &tree1)) {
980                 torture_warning(tctx, "couldn't reconnect, bailing\n");
981                 ret = false;
982                 goto done;
983         }
984
985         ZERO_STRUCT(io1);
986         io1.in.fname = fname;
987         io1.in.durable_handle = &h1;
988
989         status = smb2_create(tree1, mem_ctx, &io1);
990         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
991
992  done:
993         smb2_util_close(tree2, h2);
994         smb2_util_unlink(tree2, fname);
995
996         talloc_free(tree1);
997         talloc_free(tree2);
998
999         return ret;
1000 }
1001
1002 /*
1003   Open, disconnect, lease break, reconnect.
1004 */
1005 bool test_durable_open_lease(struct torture_context *tctx,
1006                              struct smb2_tree *tree1,
1007                              struct smb2_tree *tree2)
1008 {
1009         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1010         struct smb2_create io1, io2;
1011         struct smb2_lease ls1, ls2;
1012         struct smb2_handle h1, h2;
1013         NTSTATUS status;
1014         char fname[256];
1015         bool ret = true;
1016         uint64_t lease1, lease2;
1017         uint32_t caps;
1018
1019         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1020         if (!(caps & SMB2_CAP_LEASING)) {
1021                 torture_skip(tctx, "leases are not supported");
1022         }
1023
1024         /*
1025          * Choose a random name and random lease in case the state is left a
1026          * little funky.
1027          */
1028         lease1 = random();
1029         lease2 = random();
1030         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1031
1032         /* Clean slate */
1033         smb2_util_unlink(tree1, fname);
1034
1035         /* Create with lease */
1036         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1037                           lease1, smb2_util_lease_state("RHW"));
1038         io1.in.durable_open = true;
1039
1040         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1041                           lease2, smb2_util_lease_state("RHW"));
1042         io2.in.durable_open = true;
1043         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1044
1045         status = smb2_create(tree1, mem_ctx, &io1);
1046         CHECK_STATUS(status, NT_STATUS_OK);
1047         h1 = io1.out.file.handle;
1048         CHECK_VAL(io1.out.durable_open, true);
1049         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1050
1051         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1052         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1053         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1054         CHECK_VAL(io1.out.lease_response.lease_state,
1055             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1056
1057         /* Disconnect after getting the lease */
1058         talloc_free(tree1);
1059         tree1 = NULL;
1060
1061         /*
1062          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1063          * even if the original client is gone. (ZML: This seems like a bug. It
1064          * should give some time for the client to reconnect! And why RH?)
1065          * 
1066          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1067          * Test is adapted accordingly.
1068          */
1069         status = smb2_create(tree2, mem_ctx, &io2);
1070         CHECK_STATUS(status, NT_STATUS_OK);
1071         h2 = io2.out.file.handle;
1072         CHECK_VAL(io2.out.durable_open, true);
1073         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1074
1075         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1076         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1077         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1078         CHECK_VAL(io2.out.lease_response.lease_state,
1079             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1080
1081         /* What if tree1 tries to come back and reclaim? */
1082         if (!torture_smb2_connection(tctx, &tree1)) {
1083                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1084                 ret = false;
1085                 goto done;
1086         }
1087
1088         ZERO_STRUCT(io1);
1089         io1.in.fname = fname;
1090         io1.in.durable_handle = &h1;
1091         io1.in.lease_request = &ls1;
1092
1093         status = smb2_create(tree1, mem_ctx, &io1);
1094         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1095
1096  done:
1097         smb2_util_close(tree2, h2);
1098         smb2_util_unlink(tree2, fname);
1099
1100         talloc_free(tree1);
1101         talloc_free(tree2);
1102
1103         return ret;
1104 }
1105
1106 bool test_durable_open_lock_oplock(struct torture_context *tctx,
1107                                    struct smb2_tree *tree)
1108 {
1109         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1110         struct smb2_create io;
1111         struct smb2_handle h;
1112         struct smb2_lock lck;
1113         struct smb2_lock_element el[2];
1114         NTSTATUS status;
1115         char fname[256];
1116         bool ret = true;
1117
1118         /*
1119          */
1120         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1121
1122         /* Clean slate */
1123         smb2_util_unlink(tree, fname);
1124
1125         /* Create with lease */
1126
1127         smb2_oplock_create_share(&io, fname,
1128                                  smb2_util_share_access(""),
1129                                  smb2_util_oplock_level("b"));
1130         io.in.durable_open = true;
1131
1132         status = smb2_create(tree, mem_ctx, &io);
1133         CHECK_STATUS(status, NT_STATUS_OK);
1134         h = io.out.file.handle;
1135         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1136
1137         CHECK_VAL(io.out.durable_open, true);
1138         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1139
1140         ZERO_STRUCT(lck);
1141         ZERO_STRUCT(el);
1142         lck.in.locks            = el;
1143         lck.in.lock_count       = 0x0001;
1144         lck.in.lock_sequence    = 0x00000000;
1145         lck.in.file.handle      = h;
1146         el[0].offset            = 0;
1147         el[0].length            = 1;
1148         el[0].reserved          = 0x00000000;
1149         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1150         status = smb2_lock(tree, &lck);
1151         CHECK_STATUS(status, NT_STATUS_OK);
1152
1153         /* Disconnect/Reconnect. */
1154         talloc_free(tree);
1155         tree = NULL;
1156
1157         if (!torture_smb2_connection(tctx, &tree)) {
1158                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1159                 ret = false;
1160                 goto done;
1161         }
1162
1163         ZERO_STRUCT(io);
1164         io.in.fname = fname;
1165         io.in.durable_handle = &h;
1166
1167         status = smb2_create(tree, mem_ctx, &io);
1168         CHECK_STATUS(status, NT_STATUS_OK);
1169         h = io.out.file.handle;
1170
1171         lck.in.file.handle      = h;
1172         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1173         status = smb2_lock(tree, &lck);
1174         CHECK_STATUS(status, NT_STATUS_OK);
1175
1176  done:
1177         smb2_util_close(tree, h);
1178         smb2_util_unlink(tree, fname);
1179         talloc_free(tree);
1180
1181         return ret;
1182 }
1183
1184 /*
1185   Open, take BRL, disconnect, reconnect.
1186 */
1187 bool test_durable_open_lock_lease(struct torture_context *tctx,
1188                                   struct smb2_tree *tree)
1189 {
1190         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1191         struct smb2_create io;
1192         struct smb2_lease ls;
1193         struct smb2_handle h;
1194         struct smb2_lock lck;
1195         struct smb2_lock_element el[2];
1196         NTSTATUS status;
1197         char fname[256];
1198         bool ret = true;
1199         uint64_t lease;
1200         uint32_t caps;
1201
1202         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1203         if (!(caps & SMB2_CAP_LEASING)) {
1204                 torture_skip(tctx, "leases are not supported");
1205         }
1206
1207         /*
1208          * Choose a random name and random lease in case the state is left a
1209          * little funky.
1210          */
1211         lease = random();
1212         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1213
1214         /* Clean slate */
1215         smb2_util_unlink(tree, fname);
1216
1217         /* Create with lease */
1218
1219         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1220                           smb2_util_lease_state("RWH"));
1221         io.in.durable_open              = true;
1222
1223         status = smb2_create(tree, mem_ctx, &io);
1224         CHECK_STATUS(status, NT_STATUS_OK);
1225         h = io.out.file.handle;
1226         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1227
1228         CHECK_VAL(io.out.durable_open, true);
1229         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1230         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1231         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1232         CHECK_VAL(io.out.lease_response.lease_state,
1233             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1234
1235         ZERO_STRUCT(lck);
1236         ZERO_STRUCT(el);
1237         lck.in.locks            = el;
1238         lck.in.lock_count       = 0x0001;
1239         lck.in.lock_sequence    = 0x00000000;
1240         lck.in.file.handle      = h;
1241         el[0].offset            = 0;
1242         el[0].length            = 1;
1243         el[0].reserved          = 0x00000000;
1244         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1245         status = smb2_lock(tree, &lck);
1246         CHECK_STATUS(status, NT_STATUS_OK);
1247
1248         /* Disconnect/Reconnect. */
1249         talloc_free(tree);
1250         tree = NULL;
1251
1252         if (!torture_smb2_connection(tctx, &tree)) {
1253                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1254                 ret = false;
1255                 goto done;
1256         }
1257
1258         ZERO_STRUCT(io);
1259         io.in.fname = fname;
1260         io.in.durable_handle = &h;
1261         io.in.lease_request = &ls;
1262
1263         status = smb2_create(tree, mem_ctx, &io);
1264         CHECK_STATUS(status, NT_STATUS_OK);
1265         h = io.out.file.handle;
1266
1267         lck.in.file.handle      = h;
1268         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1269         status = smb2_lock(tree, &lck);
1270         CHECK_STATUS(status, NT_STATUS_OK);
1271
1272  done:
1273         smb2_util_close(tree, h);
1274         smb2_util_unlink(tree, fname);
1275         talloc_free(tree);
1276
1277         return ret;
1278 }
1279
1280 /**
1281  * Open with a RH lease, disconnect, open in another tree, reconnect.
1282  *
1283  * This test actually demonstrates a minimum level of respect for the durable
1284  * open in the face of another open. As long as this test shows an inability to
1285  * reconnect after an open, the oplock/lease tests above will certainly
1286  * demonstrate an error on reconnect.
1287  */
1288 bool test_durable_open_open2_lease(struct torture_context *tctx,
1289                                   struct smb2_tree *tree1,
1290                                   struct smb2_tree *tree2)
1291 {
1292         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1293         struct smb2_create io1, io2;
1294         struct smb2_lease ls;
1295         struct smb2_handle h1, h2;
1296         NTSTATUS status;
1297         char fname[256];
1298         bool ret = true;
1299         uint64_t lease;
1300         uint32_t caps;
1301
1302         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1303         if (!(caps & SMB2_CAP_LEASING)) {
1304                 torture_skip(tctx, "leases are not supported");
1305         }
1306
1307         /*
1308          * Choose a random name and random lease in case the state is left a
1309          * little funky.
1310          */
1311         lease = random();
1312         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1313                  generate_random_str(tctx, 8));
1314
1315         /* Clean slate */
1316         smb2_util_unlink(tree1, fname);
1317
1318         /* Create with lease */
1319         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1320                                 smb2_util_share_access(""),
1321                                 lease,
1322                                 smb2_util_lease_state("RH"));
1323         io1.in.durable_open = true;
1324
1325         status = smb2_create(tree1, mem_ctx, &io1);
1326         CHECK_STATUS(status, NT_STATUS_OK);
1327         h1 = io1.out.file.handle;
1328         CHECK_VAL(io1.out.durable_open, true);
1329         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1330
1331         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1332         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1333         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1334         CHECK_VAL(io1.out.lease_response.lease_state,
1335                   smb2_util_lease_state("RH"));
1336
1337         /* Disconnect */
1338         talloc_free(tree1);
1339         tree1 = NULL;
1340
1341         /* Open the file in tree2 */
1342         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1343
1344         status = smb2_create(tree2, mem_ctx, &io2);
1345         CHECK_STATUS(status, NT_STATUS_OK);
1346         h2 = io2.out.file.handle;
1347         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1348
1349         /* Reconnect */
1350         if (!torture_smb2_connection(tctx, &tree1)) {
1351                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1352                 ret = false;
1353                 goto done;
1354         }
1355
1356         ZERO_STRUCT(io1);
1357         io1.in.fname = fname;
1358         io1.in.durable_handle = &h1;
1359         io1.in.lease_request = &ls;
1360
1361         /*
1362          * Windows7 (build 7000) will give away an open immediately if the
1363          * original client is gone. (ZML: This seems like a bug. It should give
1364          * some time for the client to reconnect!)
1365          */
1366         status = smb2_create(tree1, mem_ctx, &io1);
1367         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1368         h1 = io1.out.file.handle;
1369
1370  done:
1371         smb2_util_close(tree2, h2);
1372         smb2_util_unlink(tree2, fname);
1373         smb2_util_close(tree1, h1);
1374         smb2_util_unlink(tree1, fname);
1375
1376         talloc_free(tree1);
1377         talloc_free(tree2);
1378
1379         return ret;
1380 }
1381
1382 /**
1383  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1384  *
1385  * This test actually demonstrates a minimum level of respect for the durable
1386  * open in the face of another open. As long as this test shows an inability to
1387  * reconnect after an open, the oplock/lease tests above will certainly
1388  * demonstrate an error on reconnect.
1389  */
1390 bool test_durable_open_open2_oplock(struct torture_context *tctx,
1391                                     struct smb2_tree *tree1,
1392                                     struct smb2_tree *tree2)
1393 {
1394         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1395         struct smb2_create io1, io2;
1396         struct smb2_handle h1, h2;
1397         NTSTATUS status;
1398         char fname[256];
1399         bool ret = true;
1400
1401         /*
1402          * Choose a random name and random lease in case the state is left a
1403          * little funky.
1404          */
1405         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1406                  generate_random_str(tctx, 8));
1407
1408         /* Clean slate */
1409         smb2_util_unlink(tree1, fname);
1410
1411         /* Create with batch oplock */
1412         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1413         io1.in.durable_open = true;
1414
1415         status = smb2_create(tree1, mem_ctx, &io1);
1416         CHECK_STATUS(status, NT_STATUS_OK);
1417         h1 = io1.out.file.handle;
1418         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1419         CHECK_VAL(io1.out.durable_open, true);
1420         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1421
1422         /* Disconnect */
1423         talloc_free(tree1);
1424         tree1 = NULL;
1425
1426         /* Open the file in tree2 */
1427         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1428
1429         status = smb2_create(tree2, mem_ctx, &io2);
1430         CHECK_STATUS(status, NT_STATUS_OK);
1431         h2 = io2.out.file.handle;
1432         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1433
1434         /* Reconnect */
1435         if (!torture_smb2_connection(tctx, &tree1)) {
1436                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1437                 ret = false;
1438                 goto done;
1439         }
1440
1441         ZERO_STRUCT(io1);
1442         io1.in.fname = fname;
1443         io1.in.durable_handle = &h1;
1444
1445         status = smb2_create(tree1, mem_ctx, &io1);
1446         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1447         h1 = io1.out.file.handle;
1448
1449  done:
1450         smb2_util_close(tree2, h2);
1451         smb2_util_unlink(tree2, fname);
1452         smb2_util_close(tree1, h1);
1453         smb2_util_unlink(tree1, fname);
1454
1455         talloc_free(tree1);
1456         talloc_free(tree2);
1457
1458         return ret;
1459 }
1460
1461 struct torture_suite *torture_smb2_durable_open_init(void)
1462 {
1463         struct torture_suite *suite =
1464             torture_suite_create(talloc_autofree_context(), "durable-open");
1465
1466         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1467         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1468         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1469         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1470         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1471         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1472         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1473         torture_suite_add_1smb2_test(suite, "delete_on_close1",
1474                                      test_durable_open_delete_on_close1);
1475         torture_suite_add_2smb2_test(suite, "file-position",
1476             test_durable_open_file_position);
1477         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1478         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1479         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
1480         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
1481         torture_suite_add_2smb2_test(suite, "open2-lease",
1482                                      test_durable_open_open2_lease);
1483         torture_suite_add_2smb2_test(suite, "open2-oplock",
1484                                      test_durable_open_open2_oplock);
1485
1486         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1487
1488         return suite;
1489 }