e2f8d21db7e0d011a3208977d795dd17d34c95d0
[ddiss/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         io2.in.fname = fname;
447         io2.in.durable_handle = h;
448         h = NULL;
449
450         status = smb2_create(tree, mem_ctx, &io2);
451         CHECK_STATUS(status, NT_STATUS_OK);
452         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
453         CHECK_VAL(io2.out.durable_open, true);
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.durable_open, true);
528         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
529         _h = io2.out.file.handle;
530         h = &_h;
531
532 done:
533         if (h != NULL) {
534                 smb2_util_close(tree, *h);
535         }
536
537         smb2_util_unlink(tree, fname);
538
539         talloc_free(tree);
540
541         talloc_free(mem_ctx);
542
543         return ret;
544 }
545
546
547 /**
548  * basic test for doing a durable open:
549  * tdis, new tcon, try durable reopen (fails)
550  */
551 bool test_durable_open_reopen3(struct torture_context *tctx,
552                                struct smb2_tree *tree)
553 {
554         NTSTATUS status;
555         TALLOC_CTX *mem_ctx = talloc_new(tctx);
556         char fname[256];
557         struct smb2_handle _h;
558         struct smb2_handle *h = NULL;
559         struct smb2_create io1, io2;
560         bool ret = true;
561         struct smb2_tree *tree2;
562
563         /* Choose a random name in case the state is left a little funky. */
564         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
565                  generate_random_str(tctx, 8));
566
567         smb2_util_unlink(tree, fname);
568
569         smb2_oplock_create_share(&io1, fname,
570                                  smb2_util_share_access(""),
571                                  smb2_util_oplock_level("b"));
572         io1.in.durable_open = true;
573
574         status = smb2_create(tree, mem_ctx, &io1);
575         CHECK_STATUS(status, NT_STATUS_OK);
576         _h = io1.out.file.handle;
577         h = &_h;
578         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
579         CHECK_VAL(io1.out.durable_open, true);
580         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
581
582         /* disconnect, reconnect and then do durable reopen */
583         status = smb2_tdis(tree);
584         CHECK_STATUS(status, NT_STATUS_OK);
585
586         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
587                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
588                 ret = false;
589                 goto done;
590         }
591
592
593         ZERO_STRUCT(io2);
594         io2.in.fname = fname;
595         io2.in.durable_handle = h;
596
597         status = smb2_create(tree2, mem_ctx, &io2);
598         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
599
600 done:
601         if (h != NULL) {
602                 smb2_util_close(tree, *h);
603         }
604
605         smb2_util_unlink(tree2, fname);
606
607         talloc_free(tree);
608
609         talloc_free(mem_ctx);
610
611         return ret;
612 }
613
614 /**
615  * basic test for doing a durable open:
616  * logoff, create a new session, do a durable reopen (succeeds)
617  */
618 bool test_durable_open_reopen4(struct torture_context *tctx,
619                                struct smb2_tree *tree)
620 {
621         NTSTATUS status;
622         TALLOC_CTX *mem_ctx = talloc_new(tctx);
623         char fname[256];
624         struct smb2_handle _h;
625         struct smb2_handle *h = NULL;
626         struct smb2_create io1, io2;
627         bool ret = true;
628         struct smb2_transport *transport;
629         struct smb2_session *session2;
630         struct smb2_tree *tree2;
631
632         /* Choose a random name in case the state is left a little funky. */
633         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
634                  generate_random_str(tctx, 8));
635
636         smb2_util_unlink(tree, fname);
637
638         smb2_oplock_create_share(&io1, fname,
639                                  smb2_util_share_access(""),
640                                  smb2_util_oplock_level("b"));
641         io1.in.durable_open = true;
642         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
643
644         status = smb2_create(tree, mem_ctx, &io1);
645         CHECK_STATUS(status, NT_STATUS_OK);
646         _h = io1.out.file.handle;
647         h = &_h;
648         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
649         CHECK_VAL(io1.out.durable_open, true);
650         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
651
652         /*
653          * do a session logoff, establish a new session and tree
654          * connect on the same transport, and try a durable reopen
655          */
656         transport = tree->session->transport;
657         status = smb2_logoff(tree->session);
658         CHECK_STATUS(status, NT_STATUS_OK);
659
660         if (!torture_smb2_session_setup(tctx, transport,
661                                         0, /* previous_session_id */
662                                         mem_ctx, &session2))
663         {
664                 torture_warning(tctx, "session setup failed.\n");
665                 ret = false;
666                 goto done;
667         }
668
669         /*
670          * the session setup has talloc-stolen the transport,
671          * so we can safely free the old tree+session for clarity
672          */
673         TALLOC_FREE(tree);
674
675         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
676                 torture_warning(tctx, "tree connect failed.\n");
677                 ret = false;
678                 goto done;
679         }
680
681         ZERO_STRUCT(io2);
682         io2.in.fname = fname;
683         io2.in.durable_handle = h;
684         h = NULL;
685
686         status = smb2_create(tree2, mem_ctx, &io2);
687         CHECK_STATUS(status, NT_STATUS_OK);
688
689         _h = io2.out.file.handle;
690         h = &_h;
691         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
692         CHECK_VAL(io2.out.durable_open, true);
693         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
694
695 done:
696         if (h != NULL) {
697                 smb2_util_close(tree2, *h);
698         }
699
700         smb2_util_unlink(tree2, fname);
701
702         talloc_free(tree);
703
704         talloc_free(mem_ctx);
705
706         return ret;
707 }
708
709 bool test_durable_open_delete_on_close1(struct torture_context *tctx,
710                                         struct smb2_tree *tree)
711 {
712         NTSTATUS status;
713         TALLOC_CTX *mem_ctx = talloc_new(tctx);
714         char fname[256];
715         struct smb2_handle _h;
716         struct smb2_handle *h = NULL;
717         struct smb2_create io1, io2, io3;
718         bool ret = true;
719         struct smb2_transport *transport;
720         struct smb2_session *session2;
721         struct smb2_tree *tree2;
722         union smb_fileinfo info1, info2;
723
724         /* Choose a random name in case the state is left a little funky. */
725         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
726                  generate_random_str(tctx, 8));
727
728         smb2_util_unlink(tree, fname);
729
730         smb2_oplock_create_share(&io1, fname,
731                                  smb2_util_share_access(""),
732                                  smb2_util_oplock_level("b"));
733         io1.in.durable_open = true;
734         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
735
736         status = smb2_create(tree, mem_ctx, &io1);
737         CHECK_STATUS(status, NT_STATUS_OK);
738         _h = io1.out.file.handle;
739         h = &_h;
740         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
741         CHECK_VAL(io1.out.durable_open, true);
742         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
743
744         ZERO_STRUCT(info1);
745         info1.internal_information.level = RAW_FILEINFO_INTERNAL_INFORMATION;
746         info1.internal_information.in.file.handle = _h;
747         status = smb2_getinfo_file(tree, tree, &info1);
748         CHECK_STATUS(status, NT_STATUS_OK);
749
750         /*
751          * do a session logoff, establish a new session and tree
752          * connect on the same transport, and try a durable reopen
753          */
754         transport = tree->session->transport;
755         status = smb2_logoff(tree->session);
756         CHECK_STATUS(status, NT_STATUS_OK);
757
758         if (!torture_smb2_session_setup(tctx, transport,
759                                         0, /* previous_session_id */
760                                         mem_ctx, &session2))
761         {
762                 torture_warning(tctx, "session setup failed.\n");
763                 ret = false;
764                 goto done;
765         }
766
767         /*
768          * the session setup has talloc-stolen the transport,
769          * so we can safely free the old tree+session for clarity
770          */
771         TALLOC_FREE(tree);
772
773         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
774                 torture_warning(tctx, "tree connect failed.\n");
775                 ret = false;
776                 goto done;
777         }
778
779         ZERO_STRUCT(io3);
780         io3.in.fname = fname;
781         io3.in.durable_handle = h;
782         h = NULL;
783
784         smb2_oplock_create_share(&io2, fname,
785                                  smb2_util_share_access(""),
786                                  smb2_util_oplock_level("b"));
787         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
788
789         status = smb2_create(tree2, mem_ctx, &io2);
790         CHECK_STATUS(status, NT_STATUS_OK);
791         _h = io2.out.file.handle;
792         h = &_h;
793         CHECK_CREATED(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE);
794         CHECK_VAL(io2.out.durable_open, false);
795         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
796
797         ZERO_STRUCT(info2);
798         info2.internal_information.level = RAW_FILEINFO_INTERNAL_INFORMATION;
799         info2.internal_information.in.file.handle = _h;
800         status = smb2_getinfo_file(tree2, tree2, &info2);
801         CHECK_STATUS(status, NT_STATUS_OK);
802
803         CHECK_NOT_VAL(info1.internal_information.out.file_id,
804                       info2.internal_information.out.file_id);
805
806         status = smb2_create(tree2, mem_ctx, &io3);
807         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
808
809 done:
810         if (h != NULL) {
811                 smb2_util_close(tree2, *h);
812         }
813
814         smb2_util_unlink(tree2, fname);
815
816         talloc_free(tree);
817         talloc_free(tree2);
818
819         talloc_free(mem_ctx);
820
821         return ret;
822 }
823
824 /*
825    basic testing of SMB2 durable opens
826    regarding the position information on the handle
827 */
828 bool test_durable_open_file_position(struct torture_context *tctx,
829                                      struct smb2_tree *tree1,
830                                      struct smb2_tree *tree2)
831 {
832         TALLOC_CTX *mem_ctx = talloc_new(tctx);
833         struct smb2_handle h1, h2;
834         struct smb2_create io1, io2;
835         NTSTATUS status;
836         const char *fname = "durable_open_position.dat";
837         union smb_fileinfo qfinfo;
838         union smb_setfileinfo sfinfo;
839         bool ret = true;
840         uint64_t pos;
841
842         smb2_util_unlink(tree1, fname);
843
844         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
845         io1.in.durable_open = true;
846
847         status = smb2_create(tree1, mem_ctx, &io1);
848         CHECK_STATUS(status, NT_STATUS_OK);
849         h1 = io1.out.file.handle;
850         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
851         CHECK_VAL(io1.out.durable_open, true);
852         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
853
854         /* TODO: check extra blob content */
855
856         ZERO_STRUCT(qfinfo);
857         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
858         qfinfo.generic.in.file.handle = h1;
859         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
860         CHECK_STATUS(status, NT_STATUS_OK);
861         CHECK_VAL(qfinfo.position_information.out.position, 0);
862         pos = qfinfo.position_information.out.position;
863         torture_comment(tctx, "position: %llu\n",
864                         (unsigned long long)pos);
865
866         ZERO_STRUCT(sfinfo);
867         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
868         sfinfo.generic.in.file.handle = h1;
869         sfinfo.position_information.in.position = 0x1000;
870         status = smb2_setinfo_file(tree1, &sfinfo);
871         CHECK_STATUS(status, NT_STATUS_OK);
872
873         ZERO_STRUCT(qfinfo);
874         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
875         qfinfo.generic.in.file.handle = h1;
876         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
877         CHECK_STATUS(status, NT_STATUS_OK);
878         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
879         pos = qfinfo.position_information.out.position;
880         torture_comment(tctx, "position: %llu\n",
881                         (unsigned long long)pos);
882
883         talloc_free(tree1);
884         tree1 = NULL;
885
886         ZERO_STRUCT(qfinfo);
887         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
888         qfinfo.generic.in.file.handle = h1;
889         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
890         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
891
892         ZERO_STRUCT(io2);
893         io2.in.fname = fname;
894         io2.in.durable_handle = &h1;
895
896         status = smb2_create(tree2, mem_ctx, &io2);
897         CHECK_STATUS(status, NT_STATUS_OK);
898         CHECK_VAL(io2.out.durable_open, true);
899         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
900         CHECK_VAL(io2.out.reserved, 0x00);
901         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
902         CHECK_VAL(io2.out.alloc_size, 0);
903         CHECK_VAL(io2.out.size, 0);
904         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
905         CHECK_VAL(io2.out.reserved2, 0);
906
907         h2 = io2.out.file.handle;
908
909         ZERO_STRUCT(qfinfo);
910         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
911         qfinfo.generic.in.file.handle = h2;
912         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
913         CHECK_STATUS(status, NT_STATUS_OK);
914         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
915         pos = qfinfo.position_information.out.position;
916         torture_comment(tctx, "position: %llu\n",
917                         (unsigned long long)pos);
918
919         smb2_util_close(tree2, h2);
920
921         talloc_free(mem_ctx);
922
923         smb2_util_unlink(tree2, fname);
924 done:
925         talloc_free(tree1);
926         talloc_free(tree2);
927
928         return ret;
929 }
930
931 /*
932   Open, disconnect, oplock break, reconnect.
933 */
934 bool test_durable_open_oplock(struct torture_context *tctx,
935                               struct smb2_tree *tree1,
936                               struct smb2_tree *tree2)
937 {
938         TALLOC_CTX *mem_ctx = talloc_new(tctx);
939         struct smb2_create io1, io2;
940         struct smb2_handle h1, h2;
941         NTSTATUS status;
942         char fname[256];
943         bool ret = true;
944
945         /* Choose a random name in case the state is left a little funky. */
946         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
947
948         /* Clean slate */
949         smb2_util_unlink(tree1, fname);
950
951         /* Create with batch oplock */
952         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
953         io1.in.durable_open = true;
954
955         io2 = io1;
956         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
957
958         status = smb2_create(tree1, mem_ctx, &io1);
959         CHECK_STATUS(status, NT_STATUS_OK);
960         h1 = io1.out.file.handle;
961         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
962         CHECK_VAL(io1.out.durable_open, true);
963         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
964
965         /* Disconnect after getting the batch */
966         talloc_free(tree1);
967         tree1 = NULL;
968
969         /*
970          * Windows7 (build 7000) will break a batch oplock immediately if the
971          * original client is gone. (ZML: This seems like a bug. It should give
972          * some time for the client to reconnect!)
973          */
974         status = smb2_create(tree2, mem_ctx, &io2);
975         CHECK_STATUS(status, NT_STATUS_OK);
976         h2 = io2.out.file.handle;
977         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
978         CHECK_VAL(io2.out.durable_open, true);
979         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
980
981         /* What if tree1 tries to come back and reclaim? */
982         if (!torture_smb2_connection(tctx, &tree1)) {
983                 torture_warning(tctx, "couldn't reconnect, bailing\n");
984                 ret = false;
985                 goto done;
986         }
987
988         ZERO_STRUCT(io1);
989         io1.in.fname = fname;
990         io1.in.durable_handle = &h1;
991
992         status = smb2_create(tree1, mem_ctx, &io1);
993         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
994
995  done:
996         smb2_util_close(tree2, h2);
997         smb2_util_unlink(tree2, fname);
998
999         talloc_free(tree1);
1000         talloc_free(tree2);
1001
1002         return ret;
1003 }
1004
1005 /*
1006   Open, disconnect, lease break, reconnect.
1007 */
1008 bool test_durable_open_lease(struct torture_context *tctx,
1009                              struct smb2_tree *tree1,
1010                              struct smb2_tree *tree2)
1011 {
1012         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1013         struct smb2_create io1, io2;
1014         struct smb2_lease ls1, ls2;
1015         struct smb2_handle h1, h2;
1016         NTSTATUS status;
1017         char fname[256];
1018         bool ret = true;
1019         uint64_t lease1, lease2;
1020         uint32_t caps;
1021
1022         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1023         if (!(caps & SMB2_CAP_LEASING)) {
1024                 torture_skip(tctx, "leases are not supported");
1025         }
1026
1027         /*
1028          * Choose a random name and random lease in case the state is left a
1029          * little funky.
1030          */
1031         lease1 = random();
1032         lease2 = random();
1033         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1034
1035         /* Clean slate */
1036         smb2_util_unlink(tree1, fname);
1037
1038         /* Create with lease */
1039         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1040                           lease1, smb2_util_lease_state("RHW"));
1041         io1.in.durable_open = true;
1042
1043         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1044                           lease2, smb2_util_lease_state("RHW"));
1045         io2.in.durable_open = true;
1046         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1047
1048         status = smb2_create(tree1, mem_ctx, &io1);
1049         CHECK_STATUS(status, NT_STATUS_OK);
1050         h1 = io1.out.file.handle;
1051         CHECK_VAL(io1.out.durable_open, true);
1052         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1053
1054         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1055         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1056         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1057         CHECK_VAL(io1.out.lease_response.lease_state,
1058             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1059
1060         /* Disconnect after getting the lease */
1061         talloc_free(tree1);
1062         tree1 = NULL;
1063
1064         /*
1065          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1066          * even if the original client is gone. (ZML: This seems like a bug. It
1067          * should give some time for the client to reconnect! And why RH?)
1068          * 
1069          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1070          * Test is adapted accordingly.
1071          */
1072         status = smb2_create(tree2, mem_ctx, &io2);
1073         CHECK_STATUS(status, NT_STATUS_OK);
1074         h2 = io2.out.file.handle;
1075         CHECK_VAL(io2.out.durable_open, true);
1076         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1077
1078         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1079         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1080         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1081         CHECK_VAL(io2.out.lease_response.lease_state,
1082             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1083
1084         /* What if tree1 tries to come back and reclaim? */
1085         if (!torture_smb2_connection(tctx, &tree1)) {
1086                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1087                 ret = false;
1088                 goto done;
1089         }
1090
1091         ZERO_STRUCT(io1);
1092         io1.in.fname = fname;
1093         io1.in.durable_handle = &h1;
1094         io1.in.lease_request = &ls1;
1095
1096         status = smb2_create(tree1, mem_ctx, &io1);
1097         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1098
1099  done:
1100         smb2_util_close(tree2, h2);
1101         smb2_util_unlink(tree2, fname);
1102
1103         talloc_free(tree1);
1104         talloc_free(tree2);
1105
1106         return ret;
1107 }
1108
1109 /*
1110   Open, take BRL, disconnect, reconnect.
1111 */
1112 bool test_durable_open_lock_lease(struct torture_context *tctx,
1113                                   struct smb2_tree *tree)
1114 {
1115         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1116         struct smb2_create io;
1117         struct smb2_lease ls;
1118         struct smb2_handle h;
1119         struct smb2_lock lck;
1120         struct smb2_lock_element el[2];
1121         NTSTATUS status;
1122         char fname[256];
1123         bool ret = true;
1124         uint64_t lease;
1125         uint32_t caps;
1126
1127         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1128         if (!(caps & SMB2_CAP_LEASING)) {
1129                 torture_skip(tctx, "leases are not supported");
1130         }
1131
1132         /*
1133          * Choose a random name and random lease in case the state is left a
1134          * little funky.
1135          */
1136         lease = random();
1137         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1138
1139         /* Clean slate */
1140         smb2_util_unlink(tree, fname);
1141
1142         /* Create with lease */
1143
1144         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1145                           smb2_util_lease_state("RWH"));
1146         io.in.durable_open              = true;
1147
1148         status = smb2_create(tree, mem_ctx, &io);
1149         CHECK_STATUS(status, NT_STATUS_OK);
1150         h = io.out.file.handle;
1151         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1152
1153         CHECK_VAL(io.out.durable_open, true);
1154         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1155         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1156         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1157         CHECK_VAL(io.out.lease_response.lease_state,
1158             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1159
1160         ZERO_STRUCT(lck);
1161         ZERO_STRUCT(el);
1162         lck.in.locks            = el;
1163         lck.in.lock_count       = 0x0001;
1164         lck.in.lock_sequence    = 0x00000000;
1165         lck.in.file.handle      = h;
1166         el[0].offset            = 0;
1167         el[0].length            = 1;
1168         el[0].reserved          = 0x00000000;
1169         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1170         status = smb2_lock(tree, &lck);
1171         CHECK_STATUS(status, NT_STATUS_OK);
1172
1173         /* Disconnect/Reconnect. */
1174         talloc_free(tree);
1175         tree = NULL;
1176
1177         if (!torture_smb2_connection(tctx, &tree)) {
1178                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1179                 ret = false;
1180                 goto done;
1181         }
1182
1183         ZERO_STRUCT(io);
1184         io.in.fname = fname;
1185         io.in.durable_handle = &h;
1186         io.in.lease_request = &ls;
1187
1188         status = smb2_create(tree, mem_ctx, &io);
1189         CHECK_STATUS(status, NT_STATUS_OK);
1190         h = io.out.file.handle;
1191
1192         lck.in.file.handle      = h;
1193         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1194         status = smb2_lock(tree, &lck);
1195         CHECK_STATUS(status, NT_STATUS_OK);
1196
1197  done:
1198         smb2_util_close(tree, h);
1199         smb2_util_unlink(tree, fname);
1200         talloc_free(tree);
1201
1202         return ret;
1203 }
1204
1205 /**
1206  * Open with a RH lease, disconnect, open in another tree, reconnect.
1207  *
1208  * This test actually demonstrates a minimum level of respect for the durable
1209  * open in the face of another open. As long as this test shows an inability to
1210  * reconnect after an open, the oplock/lease tests above will certainly
1211  * demonstrate an error on reconnect.
1212  */
1213 bool test_durable_open_open2_lease(struct torture_context *tctx,
1214                                   struct smb2_tree *tree1,
1215                                   struct smb2_tree *tree2)
1216 {
1217         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1218         struct smb2_create io1, io2;
1219         struct smb2_lease ls;
1220         struct smb2_handle h1, h2;
1221         NTSTATUS status;
1222         char fname[256];
1223         bool ret = true;
1224         uint64_t lease;
1225         uint32_t caps;
1226
1227         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1228         if (!(caps & SMB2_CAP_LEASING)) {
1229                 torture_skip(tctx, "leases are not supported");
1230         }
1231
1232         /*
1233          * Choose a random name and random lease in case the state is left a
1234          * little funky.
1235          */
1236         lease = random();
1237         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1238                  generate_random_str(tctx, 8));
1239
1240         /* Clean slate */
1241         smb2_util_unlink(tree1, fname);
1242
1243         /* Create with lease */
1244         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1245                                 smb2_util_share_access(""),
1246                                 lease,
1247                                 smb2_util_lease_state("RH"));
1248         io1.in.durable_open = true;
1249
1250         status = smb2_create(tree1, mem_ctx, &io1);
1251         CHECK_STATUS(status, NT_STATUS_OK);
1252         h1 = io1.out.file.handle;
1253         CHECK_VAL(io1.out.durable_open, true);
1254         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1255
1256         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1257         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1258         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1259         CHECK_VAL(io1.out.lease_response.lease_state,
1260                   smb2_util_lease_state("RH"));
1261
1262         /* Disconnect */
1263         talloc_free(tree1);
1264         tree1 = NULL;
1265
1266         /* Open the file in tree2 */
1267         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1268
1269         status = smb2_create(tree2, mem_ctx, &io2);
1270         CHECK_STATUS(status, NT_STATUS_OK);
1271         h2 = io2.out.file.handle;
1272         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1273
1274         /* Reconnect */
1275         if (!torture_smb2_connection(tctx, &tree1)) {
1276                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1277                 ret = false;
1278                 goto done;
1279         }
1280
1281         ZERO_STRUCT(io1);
1282         io1.in.fname = fname;
1283         io1.in.durable_handle = &h1;
1284         io1.in.lease_request = &ls;
1285
1286         /*
1287          * Windows7 (build 7000) will give away an open immediately if the
1288          * original client is gone. (ZML: This seems like a bug. It should give
1289          * some time for the client to reconnect!)
1290          */
1291         status = smb2_create(tree1, mem_ctx, &io1);
1292         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1293         h1 = io1.out.file.handle;
1294
1295  done:
1296         smb2_util_close(tree2, h2);
1297         smb2_util_unlink(tree2, fname);
1298         smb2_util_close(tree1, h1);
1299         smb2_util_unlink(tree1, fname);
1300
1301         talloc_free(tree1);
1302         talloc_free(tree2);
1303
1304         return ret;
1305 }
1306
1307 /**
1308  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1309  *
1310  * This test actually demonstrates a minimum level of respect for the durable
1311  * open in the face of another open. As long as this test shows an inability to
1312  * reconnect after an open, the oplock/lease tests above will certainly
1313  * demonstrate an error on reconnect.
1314  */
1315 bool test_durable_open_open2_oplock(struct torture_context *tctx,
1316                                     struct smb2_tree *tree1,
1317                                     struct smb2_tree *tree2)
1318 {
1319         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1320         struct smb2_create io1, io2;
1321         struct smb2_handle h1, h2;
1322         NTSTATUS status;
1323         char fname[256];
1324         bool ret = true;
1325
1326         /*
1327          * Choose a random name and random lease in case the state is left a
1328          * little funky.
1329          */
1330         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1331                  generate_random_str(tctx, 8));
1332
1333         /* Clean slate */
1334         smb2_util_unlink(tree1, fname);
1335
1336         /* Create with batch oplock */
1337         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1338         io1.in.durable_open = true;
1339
1340         status = smb2_create(tree1, mem_ctx, &io1);
1341         CHECK_STATUS(status, NT_STATUS_OK);
1342         h1 = io1.out.file.handle;
1343         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1344         CHECK_VAL(io1.out.durable_open, true);
1345         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1346
1347         /* Disconnect */
1348         talloc_free(tree1);
1349         tree1 = NULL;
1350
1351         /* Open the file in tree2 */
1352         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1353
1354         status = smb2_create(tree2, mem_ctx, &io2);
1355         CHECK_STATUS(status, NT_STATUS_OK);
1356         h2 = io2.out.file.handle;
1357         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1358
1359         /* Reconnect */
1360         if (!torture_smb2_connection(tctx, &tree1)) {
1361                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1362                 ret = false;
1363                 goto done;
1364         }
1365
1366         ZERO_STRUCT(io1);
1367         io1.in.fname = fname;
1368         io1.in.durable_handle = &h1;
1369
1370         status = smb2_create(tree1, mem_ctx, &io1);
1371         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1372         h1 = io1.out.file.handle;
1373
1374  done:
1375         smb2_util_close(tree2, h2);
1376         smb2_util_unlink(tree2, fname);
1377         smb2_util_close(tree1, h1);
1378         smb2_util_unlink(tree1, fname);
1379
1380         talloc_free(tree1);
1381         talloc_free(tree2);
1382
1383         return ret;
1384 }
1385
1386 struct torture_suite *torture_smb2_durable_open_init(void)
1387 {
1388         struct torture_suite *suite =
1389             torture_suite_create(talloc_autofree_context(), "durable-open");
1390
1391         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1392         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1393         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1394         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1395         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1396         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1397         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1398         torture_suite_add_1smb2_test(suite, "delete_on_close1",
1399                                      test_durable_open_delete_on_close1);
1400         torture_suite_add_2smb2_test(suite, "file-position",
1401             test_durable_open_file_position);
1402         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1403         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1404         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
1405         torture_suite_add_2smb2_test(suite, "open2-lease",
1406                                      test_durable_open_open2_lease);
1407         torture_suite_add_2smb2_test(suite, "open2-oplock",
1408                                      test_durable_open_open2_oplock);
1409
1410         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1411
1412         return suite;
1413 }