s4:torture/smb2: add smb2.durable-open.lock-oplock
[metze/samba/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7    Copyright (C) Michael Adam 2011-2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 #define CHECK_VAL(v, correct) do { \
32         if ((v) != (correct)) { \
33                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35                 ret = false; \
36         }} while (0)
37
38 #define CHECK_NOT_VAL(v, correct) do { \
39         if ((v) == (correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
42                 ret = false; \
43         }} while (0)
44
45 #define CHECK_STATUS(status, correct) do { \
46         if (!NT_STATUS_EQUAL(status, correct)) { \
47                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48                        nt_errstr(status), nt_errstr(correct)); \
49                 ret = false; \
50                 goto done; \
51         }} while (0)
52
53 #define CHECK_CREATED(__io, __created, __attribute)                     \
54         do {                                                            \
55                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
57                 CHECK_VAL((__io)->out.size, 0);                         \
58                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
59                 CHECK_VAL((__io)->out.reserved2, 0);                    \
60         } while(0)
61
62
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 bool test_durable_open_lock_oplock(struct torture_context *tctx,
1110                                    struct smb2_tree *tree)
1111 {
1112         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1113         struct smb2_create io;
1114         struct smb2_handle h;
1115         struct smb2_lock lck;
1116         struct smb2_lock_element el[2];
1117         NTSTATUS status;
1118         char fname[256];
1119         bool ret = true;
1120
1121         /*
1122          */
1123         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1124
1125         /* Clean slate */
1126         smb2_util_unlink(tree, fname);
1127
1128         /* Create with lease */
1129
1130         smb2_oplock_create_share(&io, fname,
1131                                  smb2_util_share_access(""),
1132                                  smb2_util_oplock_level("b"));
1133         io.in.durable_open = true;
1134
1135         status = smb2_create(tree, mem_ctx, &io);
1136         CHECK_STATUS(status, NT_STATUS_OK);
1137         h = io.out.file.handle;
1138         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1139
1140         CHECK_VAL(io.out.durable_open, true);
1141         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1142
1143         ZERO_STRUCT(lck);
1144         ZERO_STRUCT(el);
1145         lck.in.locks            = el;
1146         lck.in.lock_count       = 0x0001;
1147         lck.in.lock_sequence    = 0x00000000;
1148         lck.in.file.handle      = h;
1149         el[0].offset            = 0;
1150         el[0].length            = 1;
1151         el[0].reserved          = 0x00000000;
1152         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1153         status = smb2_lock(tree, &lck);
1154         CHECK_STATUS(status, NT_STATUS_OK);
1155
1156         /* Disconnect/Reconnect. */
1157         talloc_free(tree);
1158         tree = NULL;
1159
1160         if (!torture_smb2_connection(tctx, &tree)) {
1161                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1162                 ret = false;
1163                 goto done;
1164         }
1165
1166         ZERO_STRUCT(io);
1167         io.in.fname = fname;
1168         io.in.durable_handle = &h;
1169
1170         status = smb2_create(tree, mem_ctx, &io);
1171         CHECK_STATUS(status, NT_STATUS_OK);
1172         h = io.out.file.handle;
1173
1174         lck.in.file.handle      = h;
1175         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1176         status = smb2_lock(tree, &lck);
1177         CHECK_STATUS(status, NT_STATUS_OK);
1178
1179  done:
1180         smb2_util_close(tree, h);
1181         smb2_util_unlink(tree, fname);
1182         talloc_free(tree);
1183
1184         return ret;
1185 }
1186
1187 /*
1188   Open, take BRL, disconnect, reconnect.
1189 */
1190 bool test_durable_open_lock_lease(struct torture_context *tctx,
1191                                   struct smb2_tree *tree)
1192 {
1193         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1194         struct smb2_create io;
1195         struct smb2_lease ls;
1196         struct smb2_handle h;
1197         struct smb2_lock lck;
1198         struct smb2_lock_element el[2];
1199         NTSTATUS status;
1200         char fname[256];
1201         bool ret = true;
1202         uint64_t lease;
1203         uint32_t caps;
1204
1205         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1206         if (!(caps & SMB2_CAP_LEASING)) {
1207                 torture_skip(tctx, "leases are not supported");
1208         }
1209
1210         /*
1211          * Choose a random name and random lease in case the state is left a
1212          * little funky.
1213          */
1214         lease = random();
1215         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1216
1217         /* Clean slate */
1218         smb2_util_unlink(tree, fname);
1219
1220         /* Create with lease */
1221
1222         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1223                           smb2_util_lease_state("RWH"));
1224         io.in.durable_open              = true;
1225
1226         status = smb2_create(tree, mem_ctx, &io);
1227         CHECK_STATUS(status, NT_STATUS_OK);
1228         h = io.out.file.handle;
1229         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1230
1231         CHECK_VAL(io.out.durable_open, true);
1232         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1233         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1234         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1235         CHECK_VAL(io.out.lease_response.lease_state,
1236             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1237
1238         ZERO_STRUCT(lck);
1239         ZERO_STRUCT(el);
1240         lck.in.locks            = el;
1241         lck.in.lock_count       = 0x0001;
1242         lck.in.lock_sequence    = 0x00000000;
1243         lck.in.file.handle      = h;
1244         el[0].offset            = 0;
1245         el[0].length            = 1;
1246         el[0].reserved          = 0x00000000;
1247         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1248         status = smb2_lock(tree, &lck);
1249         CHECK_STATUS(status, NT_STATUS_OK);
1250
1251         /* Disconnect/Reconnect. */
1252         talloc_free(tree);
1253         tree = NULL;
1254
1255         if (!torture_smb2_connection(tctx, &tree)) {
1256                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1257                 ret = false;
1258                 goto done;
1259         }
1260
1261         ZERO_STRUCT(io);
1262         io.in.fname = fname;
1263         io.in.durable_handle = &h;
1264         io.in.lease_request = &ls;
1265
1266         status = smb2_create(tree, mem_ctx, &io);
1267         CHECK_STATUS(status, NT_STATUS_OK);
1268         h = io.out.file.handle;
1269
1270         lck.in.file.handle      = h;
1271         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1272         status = smb2_lock(tree, &lck);
1273         CHECK_STATUS(status, NT_STATUS_OK);
1274
1275  done:
1276         smb2_util_close(tree, h);
1277         smb2_util_unlink(tree, fname);
1278         talloc_free(tree);
1279
1280         return ret;
1281 }
1282
1283 /**
1284  * Open with a RH lease, disconnect, open in another tree, reconnect.
1285  *
1286  * This test actually demonstrates a minimum level of respect for the durable
1287  * open in the face of another open. As long as this test shows an inability to
1288  * reconnect after an open, the oplock/lease tests above will certainly
1289  * demonstrate an error on reconnect.
1290  */
1291 bool test_durable_open_open2_lease(struct torture_context *tctx,
1292                                   struct smb2_tree *tree1,
1293                                   struct smb2_tree *tree2)
1294 {
1295         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1296         struct smb2_create io1, io2;
1297         struct smb2_lease ls;
1298         struct smb2_handle h1, h2;
1299         NTSTATUS status;
1300         char fname[256];
1301         bool ret = true;
1302         uint64_t lease;
1303         uint32_t caps;
1304
1305         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1306         if (!(caps & SMB2_CAP_LEASING)) {
1307                 torture_skip(tctx, "leases are not supported");
1308         }
1309
1310         /*
1311          * Choose a random name and random lease in case the state is left a
1312          * little funky.
1313          */
1314         lease = random();
1315         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1316                  generate_random_str(tctx, 8));
1317
1318         /* Clean slate */
1319         smb2_util_unlink(tree1, fname);
1320
1321         /* Create with lease */
1322         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1323                                 smb2_util_share_access(""),
1324                                 lease,
1325                                 smb2_util_lease_state("RH"));
1326         io1.in.durable_open = true;
1327
1328         status = smb2_create(tree1, mem_ctx, &io1);
1329         CHECK_STATUS(status, NT_STATUS_OK);
1330         h1 = io1.out.file.handle;
1331         CHECK_VAL(io1.out.durable_open, true);
1332         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1333
1334         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1335         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1336         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1337         CHECK_VAL(io1.out.lease_response.lease_state,
1338                   smb2_util_lease_state("RH"));
1339
1340         /* Disconnect */
1341         talloc_free(tree1);
1342         tree1 = NULL;
1343
1344         /* Open the file in tree2 */
1345         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1346
1347         status = smb2_create(tree2, mem_ctx, &io2);
1348         CHECK_STATUS(status, NT_STATUS_OK);
1349         h2 = io2.out.file.handle;
1350         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1351
1352         /* Reconnect */
1353         if (!torture_smb2_connection(tctx, &tree1)) {
1354                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1355                 ret = false;
1356                 goto done;
1357         }
1358
1359         ZERO_STRUCT(io1);
1360         io1.in.fname = fname;
1361         io1.in.durable_handle = &h1;
1362         io1.in.lease_request = &ls;
1363
1364         /*
1365          * Windows7 (build 7000) will give away an open immediately if the
1366          * original client is gone. (ZML: This seems like a bug. It should give
1367          * some time for the client to reconnect!)
1368          */
1369         status = smb2_create(tree1, mem_ctx, &io1);
1370         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1371         h1 = io1.out.file.handle;
1372
1373  done:
1374         smb2_util_close(tree2, h2);
1375         smb2_util_unlink(tree2, fname);
1376         smb2_util_close(tree1, h1);
1377         smb2_util_unlink(tree1, fname);
1378
1379         talloc_free(tree1);
1380         talloc_free(tree2);
1381
1382         return ret;
1383 }
1384
1385 /**
1386  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1387  *
1388  * This test actually demonstrates a minimum level of respect for the durable
1389  * open in the face of another open. As long as this test shows an inability to
1390  * reconnect after an open, the oplock/lease tests above will certainly
1391  * demonstrate an error on reconnect.
1392  */
1393 bool test_durable_open_open2_oplock(struct torture_context *tctx,
1394                                     struct smb2_tree *tree1,
1395                                     struct smb2_tree *tree2)
1396 {
1397         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1398         struct smb2_create io1, io2;
1399         struct smb2_handle h1, h2;
1400         NTSTATUS status;
1401         char fname[256];
1402         bool ret = true;
1403
1404         /*
1405          * Choose a random name and random lease in case the state is left a
1406          * little funky.
1407          */
1408         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1409                  generate_random_str(tctx, 8));
1410
1411         /* Clean slate */
1412         smb2_util_unlink(tree1, fname);
1413
1414         /* Create with batch oplock */
1415         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1416         io1.in.durable_open = true;
1417
1418         status = smb2_create(tree1, mem_ctx, &io1);
1419         CHECK_STATUS(status, NT_STATUS_OK);
1420         h1 = io1.out.file.handle;
1421         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1422         CHECK_VAL(io1.out.durable_open, true);
1423         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1424
1425         /* Disconnect */
1426         talloc_free(tree1);
1427         tree1 = NULL;
1428
1429         /* Open the file in tree2 */
1430         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1431
1432         status = smb2_create(tree2, mem_ctx, &io2);
1433         CHECK_STATUS(status, NT_STATUS_OK);
1434         h2 = io2.out.file.handle;
1435         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1436
1437         /* Reconnect */
1438         if (!torture_smb2_connection(tctx, &tree1)) {
1439                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1440                 ret = false;
1441                 goto done;
1442         }
1443
1444         ZERO_STRUCT(io1);
1445         io1.in.fname = fname;
1446         io1.in.durable_handle = &h1;
1447
1448         status = smb2_create(tree1, mem_ctx, &io1);
1449         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1450         h1 = io1.out.file.handle;
1451
1452  done:
1453         smb2_util_close(tree2, h2);
1454         smb2_util_unlink(tree2, fname);
1455         smb2_util_close(tree1, h1);
1456         smb2_util_unlink(tree1, fname);
1457
1458         talloc_free(tree1);
1459         talloc_free(tree2);
1460
1461         return ret;
1462 }
1463
1464 struct torture_suite *torture_smb2_durable_open_init(void)
1465 {
1466         struct torture_suite *suite =
1467             torture_suite_create(talloc_autofree_context(), "durable-open");
1468
1469         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1470         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1471         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1472         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1473         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1474         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1475         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1476         torture_suite_add_1smb2_test(suite, "delete_on_close1",
1477                                      test_durable_open_delete_on_close1);
1478         torture_suite_add_2smb2_test(suite, "file-position",
1479             test_durable_open_file_position);
1480         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1481         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1482         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
1483         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
1484         torture_suite_add_2smb2_test(suite, "open2-lease",
1485                                      test_durable_open_open2_lease);
1486         torture_suite_add_2smb2_test(suite, "open2-oplock",
1487                                      test_durable_open_open2_oplock);
1488
1489         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1490
1491         return suite;
1492 }