s4:torture:smb2: fix segfault on error condition in the durable-open.reopen4 test
[metze/samba/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7    Copyright (C) Michael Adam 2011-2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
30
31 #define CHECK_VAL(v, correct) do { \
32         if ((v) != (correct)) { \
33                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35                 ret = false; \
36         }} while (0)
37
38 #define CHECK_NOT_VAL(v, correct) do { \
39         if ((v) == (correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
42                 ret = false; \
43         }} while (0)
44
45 #define CHECK_STATUS(status, correct) do { \
46         if (!NT_STATUS_EQUAL(status, correct)) { \
47                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48                        nt_errstr(status), nt_errstr(correct)); \
49                 ret = false; \
50                 goto done; \
51         }} while (0)
52
53 #define CHECK_CREATED(__io, __created, __attribute)                     \
54         do {                                                            \
55                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
57                 CHECK_VAL((__io)->out.size, 0);                         \
58                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
59                 CHECK_VAL((__io)->out.reserved2, 0);                    \
60         } while(0)
61
62 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size)  \
63         do {                                                                    \
64                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
65                 CHECK_VAL((__io)->out.alloc_size, (__alloc_size));              \
66                 CHECK_VAL((__io)->out.size, (__size));                          \
67                 CHECK_VAL((__io)->out.file_attr, (__attribute));                \
68                 CHECK_VAL((__io)->out.reserved2, 0);                            \
69         } while(0)
70
71
72
73 /**
74  * basic durable_open test.
75  * durable state should only be granted when requested
76  * along with a batch oplock or a handle lease.
77  *
78  * This test tests durable open with all possible oplock types.
79  */
80
81 struct durable_open_vs_oplock {
82         const char *level;
83         const char *share_mode;
84         bool expected;
85 };
86
87 #define NUM_OPLOCK_TYPES 4
88 #define NUM_SHARE_MODES 8
89 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
90 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
91 {
92         { "", "", false },
93         { "", "R", false },
94         { "", "W", false },
95         { "", "D", false },
96         { "", "RD", false },
97         { "", "RW", false },
98         { "", "WD", false },
99         { "", "RWD", false },
100
101         { "s", "", false },
102         { "s", "R", false },
103         { "s", "W", false },
104         { "s", "D", false },
105         { "s", "RD", false },
106         { "s", "RW", false },
107         { "s", "WD", false },
108         { "s", "RWD", false },
109
110         { "x", "", false },
111         { "x", "R", false },
112         { "x", "W", false },
113         { "x", "D", false },
114         { "x", "RD", false },
115         { "x", "RW", false },
116         { "x", "WD", false },
117         { "x", "RWD", false },
118
119         { "b", "", true },
120         { "b", "R", true },
121         { "b", "W", true },
122         { "b", "D", true },
123         { "b", "RD", true },
124         { "b", "RW", true },
125         { "b", "WD", true },
126         { "b", "RWD", true },
127 };
128
129 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
130                                               struct smb2_tree *tree,
131                                               const char *fname,
132                                               struct durable_open_vs_oplock test)
133 {
134         NTSTATUS status;
135         TALLOC_CTX *mem_ctx = talloc_new(tctx);
136         struct smb2_handle _h;
137         struct smb2_handle *h = NULL;
138         bool ret = true;
139         struct smb2_create io;
140
141         smb2_util_unlink(tree, fname);
142
143         smb2_oplock_create_share(&io, fname,
144                                  smb2_util_share_access(test.share_mode),
145                                  smb2_util_oplock_level(test.level));
146         io.in.durable_open = true;
147
148         status = smb2_create(tree, mem_ctx, &io);
149         CHECK_STATUS(status, NT_STATUS_OK);
150         _h = io.out.file.handle;
151         h = &_h;
152         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
153         CHECK_VAL(io.out.durable_open, test.expected);
154         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
155
156 done:
157         if (h != NULL) {
158                 smb2_util_close(tree, *h);
159         }
160         smb2_util_unlink(tree, fname);
161         talloc_free(mem_ctx);
162
163         return ret;
164 }
165
166 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  * basic test for doing a durable open
486  * tcp disconnect, reconnect with a session reconnect and
487  * do a durable reopen (succeeds)
488  */
489 static bool test_durable_open_reopen2a(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 io1, io2;
498         uint64_t previous_session_id;
499         bool ret = true;
500
501         /* Choose a random name in case the state is left a little funky. */
502         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
503                  generate_random_str(tctx, 8));
504
505         smb2_util_unlink(tree, fname);
506
507         smb2_oplock_create_share(&io1, fname,
508                                  smb2_util_share_access(""),
509                                  smb2_util_oplock_level("b"));
510         io1.in.durable_open = true;
511
512         status = smb2_create(tree, mem_ctx, &io1);
513         CHECK_STATUS(status, NT_STATUS_OK);
514         _h = io1.out.file.handle;
515         h = &_h;
516         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
517         CHECK_VAL(io1.out.durable_open, true);
518         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
519
520         /* disconnect, reconnect and then do durable reopen */
521         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
522         talloc_free(tree);
523         tree = NULL;
524
525         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
526                 torture_warning(tctx, "couldn't reconnect, bailing\n");
527                 ret = false;
528                 goto done;
529         }
530
531         ZERO_STRUCT(io2);
532         io2.in.fname = fname;
533         io2.in.durable_handle = h;
534         h = NULL;
535
536         status = smb2_create(tree, mem_ctx, &io2);
537         CHECK_STATUS(status, NT_STATUS_OK);
538         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
539         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
540         _h = io2.out.file.handle;
541         h = &_h;
542
543 done:
544         if (tree != NULL) {
545                 if (h != NULL) {
546                         smb2_util_close(tree, *h);
547                 }
548
549                 smb2_util_unlink(tree, fname);
550
551                 talloc_free(tree);
552         }
553
554         talloc_free(mem_ctx);
555
556         return ret;
557 }
558
559
560 /**
561  * basic test for doing a durable open:
562  * tdis, new tcon, try durable reopen (fails)
563  */
564 static bool test_durable_open_reopen3(struct torture_context *tctx,
565                                       struct smb2_tree *tree)
566 {
567         NTSTATUS status;
568         TALLOC_CTX *mem_ctx = talloc_new(tctx);
569         char fname[256];
570         struct smb2_handle _h;
571         struct smb2_handle *h = NULL;
572         struct smb2_create io1, io2;
573         bool ret = true;
574         struct smb2_tree *tree2;
575
576         /* Choose a random name in case the state is left a little funky. */
577         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
578                  generate_random_str(tctx, 8));
579
580         smb2_util_unlink(tree, fname);
581
582         smb2_oplock_create_share(&io1, fname,
583                                  smb2_util_share_access(""),
584                                  smb2_util_oplock_level("b"));
585         io1.in.durable_open = true;
586
587         status = smb2_create(tree, mem_ctx, &io1);
588         CHECK_STATUS(status, NT_STATUS_OK);
589         _h = io1.out.file.handle;
590         h = &_h;
591         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
592         CHECK_VAL(io1.out.durable_open, true);
593         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
594
595         /* disconnect, reconnect and then do durable reopen */
596         status = smb2_tdis(tree);
597         CHECK_STATUS(status, NT_STATUS_OK);
598
599         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
600                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
601                 ret = false;
602                 goto done;
603         }
604
605
606         ZERO_STRUCT(io2);
607         io2.in.fname = fname;
608         io2.in.durable_handle = h;
609
610         status = smb2_create(tree2, mem_ctx, &io2);
611         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
612
613 done:
614         if (tree != NULL) {
615                 if (h != NULL) {
616                         smb2_util_close(tree, *h);
617                 }
618
619                 smb2_util_unlink(tree2, fname);
620
621                 talloc_free(tree);
622         }
623
624         talloc_free(mem_ctx);
625
626         return ret;
627 }
628
629 /**
630  * basic test for doing a durable open:
631  * logoff, create a new session, do a durable reopen (succeeds)
632  */
633 static bool test_durable_open_reopen4(struct torture_context *tctx,
634                                       struct smb2_tree *tree)
635 {
636         NTSTATUS status;
637         TALLOC_CTX *mem_ctx = talloc_new(tctx);
638         char fname[256];
639         struct smb2_handle _h;
640         struct smb2_handle *h = NULL;
641         struct smb2_create io1, io2;
642         bool ret = true;
643         struct smb2_transport *transport;
644         struct smb2_session *session2;
645         struct smb2_tree *tree2;
646
647         /* Choose a random name in case the state is left a little funky. */
648         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
649                  generate_random_str(tctx, 8));
650
651         smb2_util_unlink(tree, fname);
652
653         smb2_oplock_create_share(&io1, fname,
654                                  smb2_util_share_access(""),
655                                  smb2_util_oplock_level("b"));
656         io1.in.durable_open = true;
657         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
658
659         status = smb2_create(tree, mem_ctx, &io1);
660         CHECK_STATUS(status, NT_STATUS_OK);
661         _h = io1.out.file.handle;
662         h = &_h;
663         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
664         CHECK_VAL(io1.out.durable_open, true);
665         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
666
667         /*
668          * do a session logoff, establish a new session and tree
669          * connect on the same transport, and try a durable reopen
670          */
671         transport = tree->session->transport;
672         status = smb2_logoff(tree->session);
673         CHECK_STATUS(status, NT_STATUS_OK);
674
675         if (!torture_smb2_session_setup(tctx, transport,
676                                         0, /* previous_session_id */
677                                         mem_ctx, &session2))
678         {
679                 torture_warning(tctx, "session setup failed.\n");
680                 ret = false;
681                 goto done;
682         }
683
684         /*
685          * the session setup has talloc-stolen the transport,
686          * so we can safely free the old tree+session for clarity
687          */
688         TALLOC_FREE(tree);
689
690         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
691                 torture_warning(tctx, "tree connect failed.\n");
692                 ret = false;
693                 goto done;
694         }
695
696         ZERO_STRUCT(io2);
697         io2.in.fname = fname;
698         io2.in.durable_handle = h;
699         h = NULL;
700
701         status = smb2_create(tree2, mem_ctx, &io2);
702         CHECK_STATUS(status, NT_STATUS_OK);
703
704         _h = io2.out.file.handle;
705         h = &_h;
706         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
707         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
708
709 done:
710         if (tree != NULL) {
711                 if (h != NULL) {
712                         smb2_util_close(tree2, *h);
713                 }
714
715                 smb2_util_unlink(tree2, fname);
716
717                 talloc_free(tree);
718         }
719
720         talloc_free(mem_ctx);
721
722         return ret;
723 }
724
725 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
726                                                struct smb2_tree *tree)
727 {
728         NTSTATUS status;
729         TALLOC_CTX *mem_ctx = talloc_new(tctx);
730         char fname[256];
731         struct smb2_handle _h;
732         struct smb2_handle *h = NULL;
733         struct smb2_create io1, io2;
734         bool ret = true;
735         uint8_t b = 0;
736
737         /* Choose a random name in case the state is left a little funky. */
738         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
739                  generate_random_str(tctx, 8));
740
741         smb2_util_unlink(tree, fname);
742
743         smb2_oplock_create_share(&io1, fname,
744                                  smb2_util_share_access(""),
745                                  smb2_util_oplock_level("b"));
746         io1.in.durable_open = true;
747         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
748
749         status = smb2_create(tree, mem_ctx, &io1);
750         CHECK_STATUS(status, NT_STATUS_OK);
751         _h = io1.out.file.handle;
752         h = &_h;
753         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
754         CHECK_VAL(io1.out.durable_open, true);
755         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
756
757         status = smb2_util_write(tree, *h, &b, 0, 1);
758         CHECK_STATUS(status, NT_STATUS_OK);
759
760         /* disconnect, leaving the durable handle in place */
761         TALLOC_FREE(tree);
762
763         if (!torture_smb2_connection(tctx, &tree)) {
764                 torture_warning(tctx, "could not reconnect, bailing\n");
765                 ret = false;
766                 goto done;
767         }
768
769         /*
770          * Open the file on the new connection again
771          * and check that it has been newly created,
772          * i.e. delete on close was effective on the disconnected handle.
773          * Also check that the file is really empty,
774          * the previously written byte gone.
775          */
776         smb2_oplock_create_share(&io2, fname,
777                                  smb2_util_share_access(""),
778                                  smb2_util_oplock_level("b"));
779         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
780
781         status = smb2_create(tree, mem_ctx, &io2);
782         CHECK_STATUS(status, NT_STATUS_OK);
783         _h = io2.out.file.handle;
784         h = &_h;
785         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
786         CHECK_VAL(io2.out.durable_open, false);
787         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
788
789 done:
790         if (h != NULL) {
791                 smb2_util_close(tree, *h);
792         }
793
794         smb2_util_unlink(tree, fname);
795
796         talloc_free(tree);
797
798         talloc_free(mem_ctx);
799
800         return ret;
801 }
802
803
804 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
805                                                struct smb2_tree *tree)
806 {
807         NTSTATUS status;
808         TALLOC_CTX *mem_ctx = talloc_new(tctx);
809         char fname[256];
810         struct smb2_handle _h;
811         struct smb2_handle *h = NULL;
812         struct smb2_create io;
813         bool ret = true;
814         uint8_t b = 0;
815         uint64_t previous_session_id;
816         uint64_t alloc_size_step;
817
818         /* Choose a random name in case the state is left a little funky. */
819         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
820                  generate_random_str(tctx, 8));
821
822         smb2_util_unlink(tree, fname);
823
824         smb2_oplock_create_share(&io, fname,
825                                  smb2_util_share_access(""),
826                                  smb2_util_oplock_level("b"));
827         io.in.durable_open = true;
828         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
829
830         status = smb2_create(tree, mem_ctx, &io);
831         CHECK_STATUS(status, NT_STATUS_OK);
832         _h = io.out.file.handle;
833         h = &_h;
834         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
835         CHECK_VAL(io.out.durable_open, true);
836         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
837
838         status = smb2_util_write(tree, *h, &b, 0, 1);
839         CHECK_STATUS(status, NT_STATUS_OK);
840
841         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
842
843         /* disconnect, leaving the durable handle in place */
844         TALLOC_FREE(tree);
845
846         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
847                 torture_warning(tctx, "could not reconnect, bailing\n");
848                 ret = false;
849                 goto done;
850         }
851
852         ZERO_STRUCT(io);
853         io.in.fname = fname;
854         io.in.durable_handle = h;
855
856         status = smb2_create(tree, mem_ctx, &io);
857         CHECK_STATUS(status, NT_STATUS_OK);
858         _h = io.out.file.handle;
859         h = &_h;
860         alloc_size_step = io.out.alloc_size;
861         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
862         CHECK_VAL(io.out.durable_open, false);
863         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
864
865         /* close the file, thereby deleting it */
866         smb2_util_close(tree, *h);
867         status = smb2_logoff(tree->session);
868         TALLOC_FREE(tree);
869
870         if (!torture_smb2_connection(tctx, &tree)) {
871                 torture_warning(tctx, "could not reconnect, bailing\n");
872                 ret = false;
873                 goto done;
874         }
875
876         /*
877          * Open the file on the new connection again
878          * and check that it has been newly created,
879          * i.e. delete on close was effective on the reconnected handle.
880          * Also check that the file is really empty,
881          * the previously written byte gone.
882          */
883         smb2_oplock_create_share(&io, fname,
884                                  smb2_util_share_access(""),
885                                  smb2_util_oplock_level("b"));
886         io.in.durable_open = true;
887         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
888
889         status = smb2_create(tree, mem_ctx, &io);
890         CHECK_STATUS(status, NT_STATUS_OK);
891         _h = io.out.file.handle;
892         h = &_h;
893         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
894         CHECK_VAL(io.out.durable_open, true);
895         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
896
897 done:
898         if (h != NULL) {
899                 smb2_util_close(tree, *h);
900         }
901
902         smb2_util_unlink(tree, fname);
903
904         talloc_free(tree);
905
906         talloc_free(mem_ctx);
907
908         return ret;
909 }
910
911 /*
912    basic testing of SMB2 durable opens
913    regarding the position information on the handle
914 */
915 static bool test_durable_open_file_position(struct torture_context *tctx,
916                                             struct smb2_tree *tree)
917 {
918         TALLOC_CTX *mem_ctx = talloc_new(tctx);
919         struct smb2_handle h;
920         struct smb2_create io;
921         NTSTATUS status;
922         const char *fname = "durable_open_position.dat";
923         union smb_fileinfo qfinfo;
924         union smb_setfileinfo sfinfo;
925         bool ret = true;
926         uint64_t pos;
927         uint64_t previous_session_id;
928
929         smb2_util_unlink(tree, fname);
930
931         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
932         io.in.durable_open = true;
933
934         status = smb2_create(tree, mem_ctx, &io);
935         CHECK_STATUS(status, NT_STATUS_OK);
936         h = io.out.file.handle;
937         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
938         CHECK_VAL(io.out.durable_open, true);
939         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
940
941         /* TODO: check extra blob content */
942
943         ZERO_STRUCT(qfinfo);
944         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
945         qfinfo.generic.in.file.handle = h;
946         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
947         CHECK_STATUS(status, NT_STATUS_OK);
948         CHECK_VAL(qfinfo.position_information.out.position, 0);
949         pos = qfinfo.position_information.out.position;
950         torture_comment(tctx, "position: %llu\n",
951                         (unsigned long long)pos);
952
953         ZERO_STRUCT(sfinfo);
954         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
955         sfinfo.generic.in.file.handle = h;
956         sfinfo.position_information.in.position = 0x1000;
957         status = smb2_setinfo_file(tree, &sfinfo);
958         CHECK_STATUS(status, NT_STATUS_OK);
959
960         ZERO_STRUCT(qfinfo);
961         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
962         qfinfo.generic.in.file.handle = h;
963         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
964         CHECK_STATUS(status, NT_STATUS_OK);
965         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
966         pos = qfinfo.position_information.out.position;
967         torture_comment(tctx, "position: %llu\n",
968                         (unsigned long long)pos);
969
970         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
971
972         /* tcp disconnect */
973         talloc_free(tree);
974         tree = NULL;
975
976         /* do a session reconnect */
977         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
978                 torture_warning(tctx, "couldn't reconnect, bailing\n");
979                 ret = false;
980                 goto done;
981         }
982
983         ZERO_STRUCT(qfinfo);
984         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
985         qfinfo.generic.in.file.handle = h;
986         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
987         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
988
989         ZERO_STRUCT(io);
990         io.in.fname = fname;
991         io.in.durable_handle = &h;
992
993         status = smb2_create(tree, mem_ctx, &io);
994         CHECK_STATUS(status, NT_STATUS_OK);
995         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
996         CHECK_VAL(io.out.reserved, 0x00);
997         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
998         CHECK_VAL(io.out.alloc_size, 0);
999         CHECK_VAL(io.out.size, 0);
1000         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1001         CHECK_VAL(io.out.reserved2, 0);
1002
1003         h = io.out.file.handle;
1004
1005         ZERO_STRUCT(qfinfo);
1006         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1007         qfinfo.generic.in.file.handle = h;
1008         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1009         CHECK_STATUS(status, NT_STATUS_OK);
1010         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1011         pos = qfinfo.position_information.out.position;
1012         torture_comment(tctx, "position: %llu\n",
1013                         (unsigned long long)pos);
1014
1015         smb2_util_close(tree, h);
1016
1017         talloc_free(mem_ctx);
1018
1019         smb2_util_unlink(tree, fname);
1020
1021 done:
1022         talloc_free(tree);
1023
1024         return ret;
1025 }
1026
1027 /*
1028   Open, disconnect, oplock break, reconnect.
1029 */
1030 static bool test_durable_open_oplock(struct torture_context *tctx,
1031                                      struct smb2_tree *tree1,
1032                                      struct smb2_tree *tree2)
1033 {
1034         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1035         struct smb2_create io1, io2;
1036         struct smb2_handle h1, h2;
1037         NTSTATUS status;
1038         char fname[256];
1039         bool ret = true;
1040
1041         /* Choose a random name in case the state is left a little funky. */
1042         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1043
1044         /* Clean slate */
1045         smb2_util_unlink(tree1, fname);
1046
1047         /* Create with batch oplock */
1048         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1049         io1.in.durable_open = true;
1050
1051         io2 = io1;
1052         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1053
1054         status = smb2_create(tree1, mem_ctx, &io1);
1055         CHECK_STATUS(status, NT_STATUS_OK);
1056         h1 = io1.out.file.handle;
1057         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1058         CHECK_VAL(io1.out.durable_open, true);
1059         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1060
1061         /* Disconnect after getting the batch */
1062         talloc_free(tree1);
1063         tree1 = NULL;
1064
1065         /*
1066          * Windows7 (build 7000) will break a batch oplock immediately if the
1067          * original client is gone. (ZML: This seems like a bug. It should give
1068          * some time for the client to reconnect!)
1069          */
1070         status = smb2_create(tree2, mem_ctx, &io2);
1071         CHECK_STATUS(status, NT_STATUS_OK);
1072         h2 = io2.out.file.handle;
1073         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1074         CHECK_VAL(io2.out.durable_open, true);
1075         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1076
1077         /* What if tree1 tries to come back and reclaim? */
1078         if (!torture_smb2_connection(tctx, &tree1)) {
1079                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1080                 ret = false;
1081                 goto done;
1082         }
1083
1084         ZERO_STRUCT(io1);
1085         io1.in.fname = fname;
1086         io1.in.durable_handle = &h1;
1087
1088         status = smb2_create(tree1, mem_ctx, &io1);
1089         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1090
1091  done:
1092         smb2_util_close(tree2, h2);
1093         smb2_util_unlink(tree2, fname);
1094
1095         talloc_free(tree1);
1096         talloc_free(tree2);
1097
1098         return ret;
1099 }
1100
1101 /*
1102   Open, disconnect, lease break, reconnect.
1103 */
1104 static bool test_durable_open_lease(struct torture_context *tctx,
1105                                     struct smb2_tree *tree1,
1106                                     struct smb2_tree *tree2)
1107 {
1108         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1109         struct smb2_create io1, io2;
1110         struct smb2_lease ls1, ls2;
1111         struct smb2_handle h1, h2;
1112         NTSTATUS status;
1113         char fname[256];
1114         bool ret = true;
1115         uint64_t lease1, lease2;
1116         uint32_t caps;
1117
1118         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1119         if (!(caps & SMB2_CAP_LEASING)) {
1120                 torture_skip(tctx, "leases are not supported");
1121         }
1122
1123         /*
1124          * Choose a random name and random lease in case the state is left a
1125          * little funky.
1126          */
1127         lease1 = random();
1128         lease2 = random();
1129         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1130
1131         /* Clean slate */
1132         smb2_util_unlink(tree1, fname);
1133
1134         /* Create with lease */
1135         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1136                           lease1, smb2_util_lease_state("RHW"));
1137         io1.in.durable_open = true;
1138
1139         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1140                           lease2, smb2_util_lease_state("RHW"));
1141         io2.in.durable_open = true;
1142         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1143
1144         status = smb2_create(tree1, mem_ctx, &io1);
1145         CHECK_STATUS(status, NT_STATUS_OK);
1146         h1 = io1.out.file.handle;
1147         CHECK_VAL(io1.out.durable_open, true);
1148         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1149
1150         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1151         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1152         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1153         CHECK_VAL(io1.out.lease_response.lease_state,
1154             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1155
1156         /* Disconnect after getting the lease */
1157         talloc_free(tree1);
1158         tree1 = NULL;
1159
1160         /*
1161          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1162          * even if the original client is gone. (ZML: This seems like a bug. It
1163          * should give some time for the client to reconnect! And why RH?)
1164          * 
1165          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1166          * Test is adapted accordingly.
1167          */
1168         status = smb2_create(tree2, mem_ctx, &io2);
1169         CHECK_STATUS(status, NT_STATUS_OK);
1170         h2 = io2.out.file.handle;
1171         CHECK_VAL(io2.out.durable_open, true);
1172         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1173
1174         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1175         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1176         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1177         CHECK_VAL(io2.out.lease_response.lease_state,
1178             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1179
1180         /* What if tree1 tries to come back and reclaim? */
1181         if (!torture_smb2_connection(tctx, &tree1)) {
1182                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1183                 ret = false;
1184                 goto done;
1185         }
1186
1187         ZERO_STRUCT(io1);
1188         io1.in.fname = fname;
1189         io1.in.durable_handle = &h1;
1190         io1.in.lease_request = &ls1;
1191
1192         status = smb2_create(tree1, mem_ctx, &io1);
1193         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1194
1195  done:
1196         smb2_util_close(tree2, h2);
1197         smb2_util_unlink(tree2, fname);
1198
1199         talloc_free(tree1);
1200         talloc_free(tree2);
1201
1202         return ret;
1203 }
1204
1205 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1206                                           struct smb2_tree *tree)
1207 {
1208         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1209         struct smb2_create io;
1210         struct smb2_handle h;
1211         struct smb2_lock lck;
1212         struct smb2_lock_element el[2];
1213         NTSTATUS status;
1214         char fname[256];
1215         bool ret = true;
1216
1217         /*
1218          */
1219         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1220
1221         /* Clean slate */
1222         smb2_util_unlink(tree, fname);
1223
1224         /* Create with lease */
1225
1226         smb2_oplock_create_share(&io, fname,
1227                                  smb2_util_share_access(""),
1228                                  smb2_util_oplock_level("b"));
1229         io.in.durable_open = true;
1230
1231         status = smb2_create(tree, mem_ctx, &io);
1232         CHECK_STATUS(status, NT_STATUS_OK);
1233         h = io.out.file.handle;
1234         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1235
1236         CHECK_VAL(io.out.durable_open, true);
1237         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1238
1239         ZERO_STRUCT(lck);
1240         ZERO_STRUCT(el);
1241         lck.in.locks            = el;
1242         lck.in.lock_count       = 0x0001;
1243         lck.in.lock_sequence    = 0x00000000;
1244         lck.in.file.handle      = h;
1245         el[0].offset            = 0;
1246         el[0].length            = 1;
1247         el[0].reserved          = 0x00000000;
1248         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1249         status = smb2_lock(tree, &lck);
1250         CHECK_STATUS(status, NT_STATUS_OK);
1251
1252         /* Disconnect/Reconnect. */
1253         talloc_free(tree);
1254         tree = NULL;
1255
1256         if (!torture_smb2_connection(tctx, &tree)) {
1257                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1258                 ret = false;
1259                 goto done;
1260         }
1261
1262         ZERO_STRUCT(io);
1263         io.in.fname = fname;
1264         io.in.durable_handle = &h;
1265
1266         status = smb2_create(tree, mem_ctx, &io);
1267         CHECK_STATUS(status, NT_STATUS_OK);
1268         h = io.out.file.handle;
1269
1270         lck.in.file.handle      = h;
1271         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1272         status = smb2_lock(tree, &lck);
1273         CHECK_STATUS(status, NT_STATUS_OK);
1274
1275  done:
1276         smb2_util_close(tree, h);
1277         smb2_util_unlink(tree, fname);
1278         talloc_free(tree);
1279
1280         return ret;
1281 }
1282
1283 /*
1284   Open, take BRL, disconnect, reconnect.
1285 */
1286 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1287                                          struct smb2_tree *tree)
1288 {
1289         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1290         struct smb2_create io;
1291         struct smb2_lease ls;
1292         struct smb2_handle h;
1293         struct smb2_lock lck;
1294         struct smb2_lock_element el[2];
1295         NTSTATUS status;
1296         char fname[256];
1297         bool ret = true;
1298         uint64_t lease;
1299         uint32_t caps;
1300
1301         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1302         if (!(caps & SMB2_CAP_LEASING)) {
1303                 torture_skip(tctx, "leases are not supported");
1304         }
1305
1306         /*
1307          * Choose a random name and random lease in case the state is left a
1308          * little funky.
1309          */
1310         lease = random();
1311         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1312
1313         /* Clean slate */
1314         smb2_util_unlink(tree, fname);
1315
1316         /* Create with lease */
1317
1318         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1319                           smb2_util_lease_state("RWH"));
1320         io.in.durable_open              = true;
1321
1322         status = smb2_create(tree, mem_ctx, &io);
1323         CHECK_STATUS(status, NT_STATUS_OK);
1324         h = io.out.file.handle;
1325         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1326
1327         CHECK_VAL(io.out.durable_open, true);
1328         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1329         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1330         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1331         CHECK_VAL(io.out.lease_response.lease_state,
1332             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1333
1334         ZERO_STRUCT(lck);
1335         ZERO_STRUCT(el);
1336         lck.in.locks            = el;
1337         lck.in.lock_count       = 0x0001;
1338         lck.in.lock_sequence    = 0x00000000;
1339         lck.in.file.handle      = h;
1340         el[0].offset            = 0;
1341         el[0].length            = 1;
1342         el[0].reserved          = 0x00000000;
1343         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1344         status = smb2_lock(tree, &lck);
1345         CHECK_STATUS(status, NT_STATUS_OK);
1346
1347         /* Disconnect/Reconnect. */
1348         talloc_free(tree);
1349         tree = NULL;
1350
1351         if (!torture_smb2_connection(tctx, &tree)) {
1352                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1353                 ret = false;
1354                 goto done;
1355         }
1356
1357         ZERO_STRUCT(io);
1358         io.in.fname = fname;
1359         io.in.durable_handle = &h;
1360         io.in.lease_request = &ls;
1361
1362         status = smb2_create(tree, mem_ctx, &io);
1363         CHECK_STATUS(status, NT_STATUS_OK);
1364         h = io.out.file.handle;
1365
1366         lck.in.file.handle      = h;
1367         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1368         status = smb2_lock(tree, &lck);
1369         CHECK_STATUS(status, NT_STATUS_OK);
1370
1371  done:
1372         smb2_util_close(tree, h);
1373         smb2_util_unlink(tree, fname);
1374         talloc_free(tree);
1375
1376         return ret;
1377 }
1378
1379 /**
1380  * Open with a RH lease, disconnect, open in another tree, reconnect.
1381  *
1382  * This test actually demonstrates a minimum level of respect for the durable
1383  * open in the face of another open. As long as this test shows an inability to
1384  * reconnect after an open, the oplock/lease tests above will certainly
1385  * demonstrate an error on reconnect.
1386  */
1387 static bool test_durable_open_open2_lease(struct torture_context *tctx,
1388                                           struct smb2_tree *tree1,
1389                                           struct smb2_tree *tree2)
1390 {
1391         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1392         struct smb2_create io1, io2;
1393         struct smb2_lease ls;
1394         struct smb2_handle h1, h2;
1395         NTSTATUS status;
1396         char fname[256];
1397         bool ret = true;
1398         uint64_t lease;
1399         uint32_t caps;
1400
1401         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1402         if (!(caps & SMB2_CAP_LEASING)) {
1403                 torture_skip(tctx, "leases are not supported");
1404         }
1405
1406         /*
1407          * Choose a random name and random lease in case the state is left a
1408          * little funky.
1409          */
1410         lease = random();
1411         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1412                  generate_random_str(tctx, 8));
1413
1414         /* Clean slate */
1415         smb2_util_unlink(tree1, fname);
1416
1417         /* Create with lease */
1418         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1419                                 smb2_util_share_access(""),
1420                                 lease,
1421                                 smb2_util_lease_state("RH"));
1422         io1.in.durable_open = true;
1423
1424         status = smb2_create(tree1, mem_ctx, &io1);
1425         CHECK_STATUS(status, NT_STATUS_OK);
1426         h1 = io1.out.file.handle;
1427         CHECK_VAL(io1.out.durable_open, true);
1428         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1429
1430         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1431         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1432         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1433         CHECK_VAL(io1.out.lease_response.lease_state,
1434                   smb2_util_lease_state("RH"));
1435
1436         /* Disconnect */
1437         talloc_free(tree1);
1438         tree1 = NULL;
1439
1440         /* Open the file in tree2 */
1441         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1442
1443         status = smb2_create(tree2, mem_ctx, &io2);
1444         CHECK_STATUS(status, NT_STATUS_OK);
1445         h2 = io2.out.file.handle;
1446         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1447
1448         /* Reconnect */
1449         if (!torture_smb2_connection(tctx, &tree1)) {
1450                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1451                 ret = false;
1452                 goto done;
1453         }
1454
1455         ZERO_STRUCT(io1);
1456         io1.in.fname = fname;
1457         io1.in.durable_handle = &h1;
1458         io1.in.lease_request = &ls;
1459
1460         /*
1461          * Windows7 (build 7000) will give away an open immediately if the
1462          * original client is gone. (ZML: This seems like a bug. It should give
1463          * some time for the client to reconnect!)
1464          */
1465         status = smb2_create(tree1, mem_ctx, &io1);
1466         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1467         h1 = io1.out.file.handle;
1468
1469  done:
1470         smb2_util_close(tree2, h2);
1471         smb2_util_unlink(tree2, fname);
1472         smb2_util_close(tree1, h1);
1473         smb2_util_unlink(tree1, fname);
1474
1475         talloc_free(tree1);
1476         talloc_free(tree2);
1477
1478         return ret;
1479 }
1480
1481 /**
1482  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1483  *
1484  * This test actually demonstrates a minimum level of respect for the durable
1485  * open in the face of another open. As long as this test shows an inability to
1486  * reconnect after an open, the oplock/lease tests above will certainly
1487  * demonstrate an error on reconnect.
1488  */
1489 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
1490                                            struct smb2_tree *tree1,
1491                                            struct smb2_tree *tree2)
1492 {
1493         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1494         struct smb2_create io1, io2;
1495         struct smb2_handle h1, h2;
1496         NTSTATUS status;
1497         char fname[256];
1498         bool ret = true;
1499
1500         /*
1501          * Choose a random name and random lease in case the state is left a
1502          * little funky.
1503          */
1504         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1505                  generate_random_str(tctx, 8));
1506
1507         /* Clean slate */
1508         smb2_util_unlink(tree1, fname);
1509
1510         /* Create with batch oplock */
1511         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1512         io1.in.durable_open = true;
1513
1514         status = smb2_create(tree1, mem_ctx, &io1);
1515         CHECK_STATUS(status, NT_STATUS_OK);
1516         h1 = io1.out.file.handle;
1517         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1518         CHECK_VAL(io1.out.durable_open, true);
1519         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1520
1521         /* Disconnect */
1522         talloc_free(tree1);
1523         tree1 = NULL;
1524
1525         /* Open the file in tree2 */
1526         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1527
1528         status = smb2_create(tree2, mem_ctx, &io2);
1529         CHECK_STATUS(status, NT_STATUS_OK);
1530         h2 = io2.out.file.handle;
1531         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1532
1533         /* Reconnect */
1534         if (!torture_smb2_connection(tctx, &tree1)) {
1535                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1536                 ret = false;
1537                 goto done;
1538         }
1539
1540         ZERO_STRUCT(io1);
1541         io1.in.fname = fname;
1542         io1.in.durable_handle = &h1;
1543
1544         status = smb2_create(tree1, mem_ctx, &io1);
1545         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1546         h1 = io1.out.file.handle;
1547
1548  done:
1549         smb2_util_close(tree2, h2);
1550         smb2_util_unlink(tree2, fname);
1551         smb2_util_close(tree1, h1);
1552         smb2_util_unlink(tree1, fname);
1553
1554         talloc_free(tree1);
1555         talloc_free(tree2);
1556
1557         return ret;
1558 }
1559
1560 /**
1561  * test behaviour with initial allocation size
1562  */
1563 static bool test_durable_open_alloc_size(struct torture_context *tctx,
1564                                          struct smb2_tree *tree)
1565 {
1566         NTSTATUS status;
1567         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1568         char fname[256];
1569         struct smb2_handle _h;
1570         struct smb2_handle *h = NULL;
1571         struct smb2_create io;
1572         bool ret = true;
1573         uint64_t previous_session_id;
1574         uint64_t alloc_size_step;
1575         uint64_t initial_alloc_size = 0x100;
1576         const uint8_t *b = NULL;
1577
1578         /* Choose a random name in case the state is left a little funky. */
1579         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
1580                  generate_random_str(tctx, 8));
1581
1582         smb2_util_unlink(tree, fname);
1583
1584         smb2_oplock_create_share(&io, fname,
1585                                  smb2_util_share_access(""),
1586                                  smb2_util_oplock_level("b"));
1587         io.in.durable_open = true;
1588         io.in.alloc_size = initial_alloc_size;
1589
1590         status = smb2_create(tree, mem_ctx, &io);
1591         CHECK_STATUS(status, NT_STATUS_OK);
1592         _h = io.out.file.handle;
1593         h = &_h;
1594         CHECK_NOT_VAL(io.out.alloc_size, 0);
1595         alloc_size_step = io.out.alloc_size;
1596         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
1597                            alloc_size_step, 0);
1598         CHECK_VAL(io.out.durable_open, true);
1599         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1600
1601         /* prepare buffer */
1602         b = talloc_zero_size(mem_ctx, alloc_size_step);
1603         CHECK_NOT_VAL(b, NULL);
1604
1605         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1606
1607         /* disconnect, reconnect and then do durable reopen */
1608         talloc_free(tree);
1609         tree = NULL;
1610
1611         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1612                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1613                 ret = false;
1614                 goto done;
1615         }
1616
1617         ZERO_STRUCT(io);
1618         io.in.fname = fname;
1619         io.in.durable_handle = h;
1620         h = NULL;
1621
1622         status = smb2_create(tree, mem_ctx, &io);
1623         CHECK_STATUS(status, NT_STATUS_OK);
1624         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1625                            alloc_size_step, 0);
1626         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1627         _h = io.out.file.handle;
1628         h = &_h;
1629
1630         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1631
1632         /* write one byte */
1633         status = smb2_util_write(tree, *h, b, 0, 1);
1634         CHECK_STATUS(status, NT_STATUS_OK);
1635
1636         /* disconnect, reconnect and then do durable reopen */
1637         talloc_free(tree);
1638         tree = NULL;
1639
1640         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1641                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1642                 ret = false;
1643                 goto done;
1644         }
1645
1646         ZERO_STRUCT(io);
1647         io.in.fname = fname;
1648         io.in.durable_handle = h;
1649         h = NULL;
1650
1651         status = smb2_create(tree, mem_ctx, &io);
1652         CHECK_STATUS(status, NT_STATUS_OK);
1653         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1654                            alloc_size_step, 1);
1655         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1656         _h = io.out.file.handle;
1657         h = &_h;
1658
1659         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1660
1661         /* write more byte than initial allocation size */
1662         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
1663
1664         /* disconnect, reconnect and then do durable reopen */
1665         talloc_free(tree);
1666         tree = NULL;
1667
1668         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1669                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1670                 ret = false;
1671                 goto done;
1672         }
1673
1674         ZERO_STRUCT(io);
1675         io.in.fname = fname;
1676         io.in.durable_handle = h;
1677         h = NULL;
1678
1679         status = smb2_create(tree, mem_ctx, &io);
1680         CHECK_STATUS(status, NT_STATUS_OK);
1681         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1682                            alloc_size_step * 2, alloc_size_step + 1);
1683         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1684         _h = io.out.file.handle;
1685         h = &_h;
1686
1687 done:
1688         if (h != NULL) {
1689                 smb2_util_close(tree, *h);
1690         }
1691
1692         smb2_util_unlink(tree, fname);
1693
1694         talloc_free(tree);
1695
1696         talloc_free(mem_ctx);
1697
1698         return ret;
1699 }
1700
1701 /**
1702  * test behaviour when a disconnect happens while creating a read-only file
1703  */
1704 static bool test_durable_open_read_only(struct torture_context *tctx,
1705                                         struct smb2_tree *tree)
1706 {
1707         NTSTATUS status;
1708         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1709         char fname[256];
1710         struct smb2_handle _h;
1711         struct smb2_handle *h = NULL;
1712         struct smb2_create io;
1713         bool ret = true;
1714         uint64_t previous_session_id;
1715         const uint8_t b = 0;
1716         uint64_t alloc_size = 0;
1717
1718         /* Choose a random name in case the state is left a little funky. */
1719         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
1720                  generate_random_str(tctx, 8));
1721
1722         smb2_util_unlink(tree, fname);
1723
1724         smb2_oplock_create_share(&io, fname,
1725                                  smb2_util_share_access(""),
1726                                  smb2_util_oplock_level("b"));
1727         io.in.durable_open = true;
1728         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
1729
1730         status = smb2_create(tree, mem_ctx, &io);
1731         CHECK_STATUS(status, NT_STATUS_OK);
1732         _h = io.out.file.handle;
1733         h = &_h;
1734         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
1735         CHECK_VAL(io.out.durable_open, true);
1736         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1737
1738         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1739
1740         /* write one byte */
1741         status = smb2_util_write(tree, *h, &b, 0, 1);
1742         CHECK_STATUS(status, NT_STATUS_OK);
1743
1744         /* disconnect, reconnect and then do durable reopen */
1745         talloc_free(tree);
1746         tree = NULL;
1747
1748         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1749                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1750                 ret = false;
1751                 goto done;
1752         }
1753
1754         ZERO_STRUCT(io);
1755         io.in.fname = fname;
1756         io.in.durable_handle = h;
1757         h = NULL;
1758
1759         status = smb2_create(tree, mem_ctx, &io);
1760         CHECK_STATUS(status, NT_STATUS_OK);
1761         alloc_size = io.out.alloc_size;
1762         CHECK_CREATED_SIZE(&io, EXISTED,
1763                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
1764                            alloc_size, 1);
1765         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1766         _h = io.out.file.handle;
1767         h = &_h;
1768
1769         /* write one byte */
1770         status = smb2_util_write(tree, *h, &b, 1, 1);
1771         CHECK_STATUS(status, NT_STATUS_OK);
1772
1773 done:
1774         if (h != NULL) {
1775                 union smb_setfileinfo sfinfo;
1776
1777                 ZERO_STRUCT(sfinfo);
1778                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
1779                 sfinfo.basic_info.in.file.handle = *h;
1780                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
1781                 smb2_setinfo_file(tree, &sfinfo);
1782
1783                 smb2_util_close(tree, *h);
1784         }
1785
1786         smb2_util_unlink(tree, fname);
1787
1788         talloc_free(tree);
1789
1790         talloc_free(mem_ctx);
1791
1792         return ret;
1793 }
1794
1795 /**
1796  * durable open with oplock, disconnect, exit
1797  */
1798 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
1799                                                 struct smb2_tree *tree)
1800 {
1801         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1802         struct smb2_create io;
1803         struct smb2_handle _h;
1804         struct smb2_handle *h = NULL;
1805         NTSTATUS status;
1806         char fname[256];
1807         bool ret = true;
1808
1809         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
1810                  generate_random_str(tctx, 8));
1811
1812         smb2_util_unlink(tree, fname);
1813
1814         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1815         io.in.durable_open = true;
1816
1817         status = smb2_create(tree, mem_ctx, &io);
1818         CHECK_STATUS(status, NT_STATUS_OK);
1819
1820         _h = io.out.file.handle;
1821         h = &_h;
1822
1823         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1824         CHECK_VAL(io.out.durable_open, true);
1825         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1826
1827         /* disconnect */
1828         talloc_free(tree);
1829         tree = NULL;
1830
1831 done:
1832         if (tree != NULL) {
1833                 if (h != NULL) {
1834                         smb2_util_close(tree, *h);
1835                 }
1836                 smb2_util_unlink(tree, fname);
1837         }
1838
1839         return ret;
1840 }
1841
1842
1843 struct torture_suite *torture_smb2_durable_open_init(void)
1844 {
1845         struct torture_suite *suite =
1846             torture_suite_create(talloc_autofree_context(), "durable-open");
1847
1848         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1849         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1850         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1851         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1852         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1853         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1854         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1855         torture_suite_add_1smb2_test(suite, "delete_on_close1",
1856                                      test_durable_open_delete_on_close1);
1857         torture_suite_add_1smb2_test(suite, "delete_on_close2",
1858                                      test_durable_open_delete_on_close2);
1859         torture_suite_add_1smb2_test(suite, "file-position",
1860             test_durable_open_file_position);
1861         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1862         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1863         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
1864         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
1865         torture_suite_add_2smb2_test(suite, "open2-lease",
1866                                      test_durable_open_open2_lease);
1867         torture_suite_add_2smb2_test(suite, "open2-oplock",
1868                                      test_durable_open_open2_oplock);
1869         torture_suite_add_1smb2_test(suite, "alloc-size",
1870                                      test_durable_open_alloc_size);
1871         torture_suite_add_1smb2_test(suite, "read-only",
1872                                      test_durable_open_read_only);
1873
1874         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1875
1876         return suite;
1877 }
1878
1879 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
1880 {
1881         struct torture_suite *suite =
1882             torture_suite_create(talloc_autofree_context(),
1883                                  "durable-open-disconnect");
1884
1885         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
1886                                      test_durable_open_oplock_disconnect);
1887
1888         suite->description = talloc_strdup(suite,
1889                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
1890
1891         return suite;
1892 }