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