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