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