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