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