8a1e5c7e272fa7dd0f16792621c2a6febae385bc
[obnox/samba/samba-obnox.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 (h != NULL) {
711                 smb2_util_close(tree2, *h);
712         }
713
714         smb2_util_unlink(tree2, fname);
715
716         talloc_free(tree);
717
718         talloc_free(mem_ctx);
719
720         return ret;
721 }
722
723 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
724                                                struct smb2_tree *tree)
725 {
726         NTSTATUS status;
727         TALLOC_CTX *mem_ctx = talloc_new(tctx);
728         char fname[256];
729         struct smb2_handle _h;
730         struct smb2_handle *h = NULL;
731         struct smb2_create io1, io2;
732         bool ret = true;
733         uint8_t b = 0;
734
735         /* Choose a random name in case the state is left a little funky. */
736         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
737                  generate_random_str(tctx, 8));
738
739         smb2_util_unlink(tree, fname);
740
741         smb2_oplock_create_share(&io1, fname,
742                                  smb2_util_share_access(""),
743                                  smb2_util_oplock_level("b"));
744         io1.in.durable_open = true;
745         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
746
747         status = smb2_create(tree, mem_ctx, &io1);
748         CHECK_STATUS(status, NT_STATUS_OK);
749         _h = io1.out.file.handle;
750         h = &_h;
751         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
752         CHECK_VAL(io1.out.durable_open, true);
753         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
754
755         status = smb2_util_write(tree, *h, &b, 0, 1);
756         CHECK_STATUS(status, NT_STATUS_OK);
757
758         /* disconnect, leaving the durable handle in place */
759         TALLOC_FREE(tree);
760
761         if (!torture_smb2_connection(tctx, &tree)) {
762                 torture_warning(tctx, "could not reconnect, bailing\n");
763                 ret = false;
764                 goto done;
765         }
766
767         /*
768          * Open the file on the new connection again
769          * and check that it has been newly created,
770          * i.e. delete on close was effective on the disconnected handle.
771          * Also check that the file is really empty,
772          * the previously written byte gone.
773          */
774         smb2_oplock_create_share(&io2, fname,
775                                  smb2_util_share_access(""),
776                                  smb2_util_oplock_level("b"));
777         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
778
779         status = smb2_create(tree, mem_ctx, &io2);
780         CHECK_STATUS(status, NT_STATUS_OK);
781         _h = io2.out.file.handle;
782         h = &_h;
783         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
784         CHECK_VAL(io2.out.durable_open, false);
785         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
786
787 done:
788         if (h != NULL) {
789                 smb2_util_close(tree, *h);
790         }
791
792         smb2_util_unlink(tree, fname);
793
794         talloc_free(tree);
795
796         talloc_free(mem_ctx);
797
798         return ret;
799 }
800
801
802 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
803                                                struct smb2_tree *tree)
804 {
805         NTSTATUS status;
806         TALLOC_CTX *mem_ctx = talloc_new(tctx);
807         char fname[256];
808         struct smb2_handle _h;
809         struct smb2_handle *h = NULL;
810         struct smb2_create io;
811         bool ret = true;
812         uint8_t b = 0;
813         uint64_t previous_session_id;
814         uint64_t alloc_size_step;
815
816         /* Choose a random name in case the state is left a little funky. */
817         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
818                  generate_random_str(tctx, 8));
819
820         smb2_util_unlink(tree, fname);
821
822         smb2_oplock_create_share(&io, fname,
823                                  smb2_util_share_access(""),
824                                  smb2_util_oplock_level("b"));
825         io.in.durable_open = true;
826         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
827
828         status = smb2_create(tree, mem_ctx, &io);
829         CHECK_STATUS(status, NT_STATUS_OK);
830         _h = io.out.file.handle;
831         h = &_h;
832         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
833         CHECK_VAL(io.out.durable_open, true);
834         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
835
836         status = smb2_util_write(tree, *h, &b, 0, 1);
837         CHECK_STATUS(status, NT_STATUS_OK);
838
839         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
840
841         /* disconnect, leaving the durable handle in place */
842         TALLOC_FREE(tree);
843
844         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
845                 torture_warning(tctx, "could not reconnect, bailing\n");
846                 ret = false;
847                 goto done;
848         }
849
850         ZERO_STRUCT(io);
851         io.in.fname = fname;
852         io.in.durable_handle = h;
853
854         status = smb2_create(tree, mem_ctx, &io);
855         CHECK_STATUS(status, NT_STATUS_OK);
856         _h = io.out.file.handle;
857         h = &_h;
858         alloc_size_step = io.out.alloc_size;
859         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
860         CHECK_VAL(io.out.durable_open, false);
861         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
862
863         /* close the file, thereby deleting it */
864         smb2_util_close(tree, *h);
865         status = smb2_logoff(tree->session);
866         TALLOC_FREE(tree);
867
868         if (!torture_smb2_connection(tctx, &tree)) {
869                 torture_warning(tctx, "could not reconnect, bailing\n");
870                 ret = false;
871                 goto done;
872         }
873
874         /*
875          * Open the file on the new connection again
876          * and check that it has been newly created,
877          * i.e. delete on close was effective on the reconnected handle.
878          * Also check that the file is really empty,
879          * the previously written byte gone.
880          */
881         smb2_oplock_create_share(&io, fname,
882                                  smb2_util_share_access(""),
883                                  smb2_util_oplock_level("b"));
884         io.in.durable_open = true;
885         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
886
887         status = smb2_create(tree, mem_ctx, &io);
888         CHECK_STATUS(status, NT_STATUS_OK);
889         _h = io.out.file.handle;
890         h = &_h;
891         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
892         CHECK_VAL(io.out.durable_open, true);
893         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
894
895 done:
896         if (h != NULL) {
897                 smb2_util_close(tree, *h);
898         }
899
900         smb2_util_unlink(tree, fname);
901
902         talloc_free(tree);
903
904         talloc_free(mem_ctx);
905
906         return ret;
907 }
908
909 /*
910    basic testing of SMB2 durable opens
911    regarding the position information on the handle
912 */
913 static bool test_durable_open_file_position(struct torture_context *tctx,
914                                             struct smb2_tree *tree)
915 {
916         TALLOC_CTX *mem_ctx = talloc_new(tctx);
917         struct smb2_handle h;
918         struct smb2_create io;
919         NTSTATUS status;
920         const char *fname = "durable_open_position.dat";
921         union smb_fileinfo qfinfo;
922         union smb_setfileinfo sfinfo;
923         bool ret = true;
924         uint64_t pos;
925         uint64_t previous_session_id;
926
927         smb2_util_unlink(tree, fname);
928
929         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
930         io.in.durable_open = true;
931
932         status = smb2_create(tree, mem_ctx, &io);
933         CHECK_STATUS(status, NT_STATUS_OK);
934         h = io.out.file.handle;
935         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
936         CHECK_VAL(io.out.durable_open, true);
937         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
938
939         /* TODO: check extra blob content */
940
941         ZERO_STRUCT(qfinfo);
942         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
943         qfinfo.generic.in.file.handle = h;
944         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
945         CHECK_STATUS(status, NT_STATUS_OK);
946         CHECK_VAL(qfinfo.position_information.out.position, 0);
947         pos = qfinfo.position_information.out.position;
948         torture_comment(tctx, "position: %llu\n",
949                         (unsigned long long)pos);
950
951         ZERO_STRUCT(sfinfo);
952         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
953         sfinfo.generic.in.file.handle = h;
954         sfinfo.position_information.in.position = 0x1000;
955         status = smb2_setinfo_file(tree, &sfinfo);
956         CHECK_STATUS(status, NT_STATUS_OK);
957
958         ZERO_STRUCT(qfinfo);
959         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
960         qfinfo.generic.in.file.handle = h;
961         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
962         CHECK_STATUS(status, NT_STATUS_OK);
963         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
964         pos = qfinfo.position_information.out.position;
965         torture_comment(tctx, "position: %llu\n",
966                         (unsigned long long)pos);
967
968         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
969
970         /* tcp disconnect */
971         talloc_free(tree);
972         tree = NULL;
973
974         /* do a session reconnect */
975         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
976                 torture_warning(tctx, "couldn't reconnect, bailing\n");
977                 ret = false;
978                 goto done;
979         }
980
981         ZERO_STRUCT(qfinfo);
982         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
983         qfinfo.generic.in.file.handle = h;
984         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
985         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
986
987         ZERO_STRUCT(io);
988         io.in.fname = fname;
989         io.in.durable_handle = &h;
990
991         status = smb2_create(tree, mem_ctx, &io);
992         CHECK_STATUS(status, NT_STATUS_OK);
993         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
994         CHECK_VAL(io.out.reserved, 0x00);
995         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
996         CHECK_VAL(io.out.alloc_size, 0);
997         CHECK_VAL(io.out.size, 0);
998         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
999         CHECK_VAL(io.out.reserved2, 0);
1000
1001         h = io.out.file.handle;
1002
1003         ZERO_STRUCT(qfinfo);
1004         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1005         qfinfo.generic.in.file.handle = h;
1006         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1007         CHECK_STATUS(status, NT_STATUS_OK);
1008         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1009         pos = qfinfo.position_information.out.position;
1010         torture_comment(tctx, "position: %llu\n",
1011                         (unsigned long long)pos);
1012
1013         smb2_util_close(tree, h);
1014
1015         talloc_free(mem_ctx);
1016
1017         smb2_util_unlink(tree, fname);
1018
1019 done:
1020         talloc_free(tree);
1021
1022         return ret;
1023 }
1024
1025 /*
1026   Open, disconnect, oplock break, reconnect.
1027 */
1028 static bool test_durable_open_oplock(struct torture_context *tctx,
1029                                      struct smb2_tree *tree1,
1030                                      struct smb2_tree *tree2)
1031 {
1032         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1033         struct smb2_create io1, io2;
1034         struct smb2_handle h1, h2;
1035         NTSTATUS status;
1036         char fname[256];
1037         bool ret = true;
1038
1039         /* Choose a random name in case the state is left a little funky. */
1040         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1041
1042         /* Clean slate */
1043         smb2_util_unlink(tree1, fname);
1044
1045         /* Create with batch oplock */
1046         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1047         io1.in.durable_open = true;
1048
1049         io2 = io1;
1050         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1051
1052         status = smb2_create(tree1, mem_ctx, &io1);
1053         CHECK_STATUS(status, NT_STATUS_OK);
1054         h1 = io1.out.file.handle;
1055         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1056         CHECK_VAL(io1.out.durable_open, true);
1057         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1058
1059         /* Disconnect after getting the batch */
1060         talloc_free(tree1);
1061         tree1 = NULL;
1062
1063         /*
1064          * Windows7 (build 7000) will break a batch oplock immediately if the
1065          * original client is gone. (ZML: This seems like a bug. It should give
1066          * some time for the client to reconnect!)
1067          */
1068         status = smb2_create(tree2, mem_ctx, &io2);
1069         CHECK_STATUS(status, NT_STATUS_OK);
1070         h2 = io2.out.file.handle;
1071         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1072         CHECK_VAL(io2.out.durable_open, true);
1073         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1074
1075         /* What if tree1 tries to come back and reclaim? */
1076         if (!torture_smb2_connection(tctx, &tree1)) {
1077                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1078                 ret = false;
1079                 goto done;
1080         }
1081
1082         ZERO_STRUCT(io1);
1083         io1.in.fname = fname;
1084         io1.in.durable_handle = &h1;
1085
1086         status = smb2_create(tree1, mem_ctx, &io1);
1087         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1088
1089  done:
1090         smb2_util_close(tree2, h2);
1091         smb2_util_unlink(tree2, fname);
1092
1093         talloc_free(tree1);
1094         talloc_free(tree2);
1095
1096         return ret;
1097 }
1098
1099 /*
1100   Open, disconnect, lease break, reconnect.
1101 */
1102 static bool test_durable_open_lease(struct torture_context *tctx,
1103                                     struct smb2_tree *tree1,
1104                                     struct smb2_tree *tree2)
1105 {
1106         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1107         struct smb2_create io1, io2;
1108         struct smb2_lease ls1, ls2;
1109         struct smb2_handle h1, h2;
1110         NTSTATUS status;
1111         char fname[256];
1112         bool ret = true;
1113         uint64_t lease1, lease2;
1114         uint32_t caps;
1115
1116         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1117         if (!(caps & SMB2_CAP_LEASING)) {
1118                 torture_skip(tctx, "leases are not supported");
1119         }
1120
1121         /*
1122          * Choose a random name and random lease in case the state is left a
1123          * little funky.
1124          */
1125         lease1 = random();
1126         lease2 = random();
1127         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1128
1129         /* Clean slate */
1130         smb2_util_unlink(tree1, fname);
1131
1132         /* Create with lease */
1133         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1134                           lease1, smb2_util_lease_state("RHW"));
1135         io1.in.durable_open = true;
1136
1137         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1138                           lease2, smb2_util_lease_state("RHW"));
1139         io2.in.durable_open = true;
1140         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1141
1142         status = smb2_create(tree1, mem_ctx, &io1);
1143         CHECK_STATUS(status, NT_STATUS_OK);
1144         h1 = io1.out.file.handle;
1145         CHECK_VAL(io1.out.durable_open, true);
1146         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1147
1148         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1149         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1150         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1151         CHECK_VAL(io1.out.lease_response.lease_state,
1152             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1153
1154         /* Disconnect after getting the lease */
1155         talloc_free(tree1);
1156         tree1 = NULL;
1157
1158         /*
1159          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1160          * even if the original client is gone. (ZML: This seems like a bug. It
1161          * should give some time for the client to reconnect! And why RH?)
1162          * 
1163          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1164          * Test is adapted accordingly.
1165          */
1166         status = smb2_create(tree2, mem_ctx, &io2);
1167         CHECK_STATUS(status, NT_STATUS_OK);
1168         h2 = io2.out.file.handle;
1169         CHECK_VAL(io2.out.durable_open, true);
1170         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1171
1172         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1173         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1174         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1175         CHECK_VAL(io2.out.lease_response.lease_state,
1176             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1177
1178         /* What if tree1 tries to come back and reclaim? */
1179         if (!torture_smb2_connection(tctx, &tree1)) {
1180                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1181                 ret = false;
1182                 goto done;
1183         }
1184
1185         ZERO_STRUCT(io1);
1186         io1.in.fname = fname;
1187         io1.in.durable_handle = &h1;
1188         io1.in.lease_request = &ls1;
1189
1190         status = smb2_create(tree1, mem_ctx, &io1);
1191         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1192
1193  done:
1194         smb2_util_close(tree2, h2);
1195         smb2_util_unlink(tree2, fname);
1196
1197         talloc_free(tree1);
1198         talloc_free(tree2);
1199
1200         return ret;
1201 }
1202
1203 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1204                                           struct smb2_tree *tree)
1205 {
1206         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1207         struct smb2_create io;
1208         struct smb2_handle h;
1209         struct smb2_lock lck;
1210         struct smb2_lock_element el[2];
1211         NTSTATUS status;
1212         char fname[256];
1213         bool ret = true;
1214
1215         /*
1216          */
1217         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1218
1219         /* Clean slate */
1220         smb2_util_unlink(tree, fname);
1221
1222         /* Create with lease */
1223
1224         smb2_oplock_create_share(&io, fname,
1225                                  smb2_util_share_access(""),
1226                                  smb2_util_oplock_level("b"));
1227         io.in.durable_open = true;
1228
1229         status = smb2_create(tree, mem_ctx, &io);
1230         CHECK_STATUS(status, NT_STATUS_OK);
1231         h = io.out.file.handle;
1232         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1233
1234         CHECK_VAL(io.out.durable_open, true);
1235         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1236
1237         ZERO_STRUCT(lck);
1238         ZERO_STRUCT(el);
1239         lck.in.locks            = el;
1240         lck.in.lock_count       = 0x0001;
1241         lck.in.lock_sequence    = 0x00000000;
1242         lck.in.file.handle      = h;
1243         el[0].offset            = 0;
1244         el[0].length            = 1;
1245         el[0].reserved          = 0x00000000;
1246         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1247         status = smb2_lock(tree, &lck);
1248         CHECK_STATUS(status, NT_STATUS_OK);
1249
1250         /* Disconnect/Reconnect. */
1251         talloc_free(tree);
1252         tree = NULL;
1253
1254         if (!torture_smb2_connection(tctx, &tree)) {
1255                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1256                 ret = false;
1257                 goto done;
1258         }
1259
1260         ZERO_STRUCT(io);
1261         io.in.fname = fname;
1262         io.in.durable_handle = &h;
1263
1264         status = smb2_create(tree, mem_ctx, &io);
1265         CHECK_STATUS(status, NT_STATUS_OK);
1266         h = io.out.file.handle;
1267
1268         lck.in.file.handle      = h;
1269         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1270         status = smb2_lock(tree, &lck);
1271         CHECK_STATUS(status, NT_STATUS_OK);
1272
1273  done:
1274         smb2_util_close(tree, h);
1275         smb2_util_unlink(tree, fname);
1276         talloc_free(tree);
1277
1278         return ret;
1279 }
1280
1281 /*
1282   Open, take BRL, disconnect, reconnect.
1283 */
1284 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1285                                          struct smb2_tree *tree)
1286 {
1287         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1288         struct smb2_create io;
1289         struct smb2_lease ls;
1290         struct smb2_handle h;
1291         struct smb2_lock lck;
1292         struct smb2_lock_element el[2];
1293         NTSTATUS status;
1294         char fname[256];
1295         bool ret = true;
1296         uint64_t lease;
1297         uint32_t caps;
1298
1299         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1300         if (!(caps & SMB2_CAP_LEASING)) {
1301                 torture_skip(tctx, "leases are not supported");
1302         }
1303
1304         /*
1305          * Choose a random name and random lease in case the state is left a
1306          * little funky.
1307          */
1308         lease = random();
1309         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1310
1311         /* Clean slate */
1312         smb2_util_unlink(tree, fname);
1313
1314         /* Create with lease */
1315
1316         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1317                           smb2_util_lease_state("RWH"));
1318         io.in.durable_open              = true;
1319
1320         status = smb2_create(tree, mem_ctx, &io);
1321         CHECK_STATUS(status, NT_STATUS_OK);
1322         h = io.out.file.handle;
1323         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1324
1325         CHECK_VAL(io.out.durable_open, true);
1326         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1327         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1328         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1329         CHECK_VAL(io.out.lease_response.lease_state,
1330             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1331
1332         ZERO_STRUCT(lck);
1333         ZERO_STRUCT(el);
1334         lck.in.locks            = el;
1335         lck.in.lock_count       = 0x0001;
1336         lck.in.lock_sequence    = 0x00000000;
1337         lck.in.file.handle      = h;
1338         el[0].offset            = 0;
1339         el[0].length            = 1;
1340         el[0].reserved          = 0x00000000;
1341         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1342         status = smb2_lock(tree, &lck);
1343         CHECK_STATUS(status, NT_STATUS_OK);
1344
1345         /* Disconnect/Reconnect. */
1346         talloc_free(tree);
1347         tree = NULL;
1348
1349         if (!torture_smb2_connection(tctx, &tree)) {
1350                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1351                 ret = false;
1352                 goto done;
1353         }
1354
1355         ZERO_STRUCT(io);
1356         io.in.fname = fname;
1357         io.in.durable_handle = &h;
1358         io.in.lease_request = &ls;
1359
1360         status = smb2_create(tree, mem_ctx, &io);
1361         CHECK_STATUS(status, NT_STATUS_OK);
1362         h = io.out.file.handle;
1363
1364         lck.in.file.handle      = h;
1365         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1366         status = smb2_lock(tree, &lck);
1367         CHECK_STATUS(status, NT_STATUS_OK);
1368
1369  done:
1370         smb2_util_close(tree, h);
1371         smb2_util_unlink(tree, fname);
1372         talloc_free(tree);
1373
1374         return ret;
1375 }
1376
1377 /**
1378  * Open with a RH lease, disconnect, open in another tree, reconnect.
1379  *
1380  * This test actually demonstrates a minimum level of respect for the durable
1381  * open in the face of another open. As long as this test shows an inability to
1382  * reconnect after an open, the oplock/lease tests above will certainly
1383  * demonstrate an error on reconnect.
1384  */
1385 static bool test_durable_open_open2_lease(struct torture_context *tctx,
1386                                           struct smb2_tree *tree1,
1387                                           struct smb2_tree *tree2)
1388 {
1389         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1390         struct smb2_create io1, io2;
1391         struct smb2_lease ls;
1392         struct smb2_handle h1, h2;
1393         NTSTATUS status;
1394         char fname[256];
1395         bool ret = true;
1396         uint64_t lease;
1397         uint32_t caps;
1398
1399         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1400         if (!(caps & SMB2_CAP_LEASING)) {
1401                 torture_skip(tctx, "leases are not supported");
1402         }
1403
1404         /*
1405          * Choose a random name and random lease in case the state is left a
1406          * little funky.
1407          */
1408         lease = random();
1409         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1410                  generate_random_str(tctx, 8));
1411
1412         /* Clean slate */
1413         smb2_util_unlink(tree1, fname);
1414
1415         /* Create with lease */
1416         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1417                                 smb2_util_share_access(""),
1418                                 lease,
1419                                 smb2_util_lease_state("RH"));
1420         io1.in.durable_open = true;
1421
1422         status = smb2_create(tree1, mem_ctx, &io1);
1423         CHECK_STATUS(status, NT_STATUS_OK);
1424         h1 = io1.out.file.handle;
1425         CHECK_VAL(io1.out.durable_open, true);
1426         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1427
1428         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1429         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1430         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1431         CHECK_VAL(io1.out.lease_response.lease_state,
1432                   smb2_util_lease_state("RH"));
1433
1434         /* Disconnect */
1435         talloc_free(tree1);
1436         tree1 = NULL;
1437
1438         /* Open the file in tree2 */
1439         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1440
1441         status = smb2_create(tree2, mem_ctx, &io2);
1442         CHECK_STATUS(status, NT_STATUS_OK);
1443         h2 = io2.out.file.handle;
1444         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1445
1446         /* Reconnect */
1447         if (!torture_smb2_connection(tctx, &tree1)) {
1448                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1449                 ret = false;
1450                 goto done;
1451         }
1452
1453         ZERO_STRUCT(io1);
1454         io1.in.fname = fname;
1455         io1.in.durable_handle = &h1;
1456         io1.in.lease_request = &ls;
1457
1458         /*
1459          * Windows7 (build 7000) will give away an open immediately if the
1460          * original client is gone. (ZML: This seems like a bug. It should give
1461          * some time for the client to reconnect!)
1462          */
1463         status = smb2_create(tree1, mem_ctx, &io1);
1464         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1465         h1 = io1.out.file.handle;
1466
1467  done:
1468         smb2_util_close(tree2, h2);
1469         smb2_util_unlink(tree2, fname);
1470         smb2_util_close(tree1, h1);
1471         smb2_util_unlink(tree1, fname);
1472
1473         talloc_free(tree1);
1474         talloc_free(tree2);
1475
1476         return ret;
1477 }
1478
1479 /**
1480  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1481  *
1482  * This test actually demonstrates a minimum level of respect for the durable
1483  * open in the face of another open. As long as this test shows an inability to
1484  * reconnect after an open, the oplock/lease tests above will certainly
1485  * demonstrate an error on reconnect.
1486  */
1487 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
1488                                            struct smb2_tree *tree1,
1489                                            struct smb2_tree *tree2)
1490 {
1491         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1492         struct smb2_create io1, io2;
1493         struct smb2_handle h1, h2;
1494         NTSTATUS status;
1495         char fname[256];
1496         bool ret = true;
1497
1498         /*
1499          * Choose a random name and random lease in case the state is left a
1500          * little funky.
1501          */
1502         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1503                  generate_random_str(tctx, 8));
1504
1505         /* Clean slate */
1506         smb2_util_unlink(tree1, fname);
1507
1508         /* Create with batch oplock */
1509         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1510         io1.in.durable_open = true;
1511
1512         status = smb2_create(tree1, mem_ctx, &io1);
1513         CHECK_STATUS(status, NT_STATUS_OK);
1514         h1 = io1.out.file.handle;
1515         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1516         CHECK_VAL(io1.out.durable_open, true);
1517         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1518
1519         /* Disconnect */
1520         talloc_free(tree1);
1521         tree1 = NULL;
1522
1523         /* Open the file in tree2 */
1524         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1525
1526         status = smb2_create(tree2, mem_ctx, &io2);
1527         CHECK_STATUS(status, NT_STATUS_OK);
1528         h2 = io2.out.file.handle;
1529         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1530
1531         /* Reconnect */
1532         if (!torture_smb2_connection(tctx, &tree1)) {
1533                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1534                 ret = false;
1535                 goto done;
1536         }
1537
1538         ZERO_STRUCT(io1);
1539         io1.in.fname = fname;
1540         io1.in.durable_handle = &h1;
1541
1542         status = smb2_create(tree1, mem_ctx, &io1);
1543         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1544         h1 = io1.out.file.handle;
1545
1546  done:
1547         smb2_util_close(tree2, h2);
1548         smb2_util_unlink(tree2, fname);
1549         smb2_util_close(tree1, h1);
1550         smb2_util_unlink(tree1, fname);
1551
1552         talloc_free(tree1);
1553         talloc_free(tree2);
1554
1555         return ret;
1556 }
1557
1558 /**
1559  * test behaviour with initial allocation size
1560  */
1561 static bool test_durable_open_alloc_size(struct torture_context *tctx,
1562                                          struct smb2_tree *tree)
1563 {
1564         NTSTATUS status;
1565         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1566         char fname[256];
1567         struct smb2_handle _h;
1568         struct smb2_handle *h = NULL;
1569         struct smb2_create io;
1570         bool ret = true;
1571         uint64_t previous_session_id;
1572         uint64_t alloc_size_step;
1573         uint64_t initial_alloc_size = 0x100;
1574         const uint8_t *b = NULL;
1575
1576         /* Choose a random name in case the state is left a little funky. */
1577         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
1578                  generate_random_str(tctx, 8));
1579
1580         smb2_util_unlink(tree, fname);
1581
1582         smb2_oplock_create_share(&io, fname,
1583                                  smb2_util_share_access(""),
1584                                  smb2_util_oplock_level("b"));
1585         io.in.durable_open = true;
1586         io.in.alloc_size = initial_alloc_size;
1587
1588         status = smb2_create(tree, mem_ctx, &io);
1589         CHECK_STATUS(status, NT_STATUS_OK);
1590         _h = io.out.file.handle;
1591         h = &_h;
1592         CHECK_NOT_VAL(io.out.alloc_size, 0);
1593         alloc_size_step = io.out.alloc_size;
1594         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
1595                            alloc_size_step, 0);
1596         CHECK_VAL(io.out.durable_open, true);
1597         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1598
1599         /* prepare buffer */
1600         b = talloc_zero_size(mem_ctx, alloc_size_step);
1601         CHECK_NOT_VAL(b, NULL);
1602
1603         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1604
1605         /* disconnect, reconnect and then do durable reopen */
1606         talloc_free(tree);
1607         tree = NULL;
1608
1609         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1610                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1611                 ret = false;
1612                 goto done;
1613         }
1614
1615         ZERO_STRUCT(io);
1616         io.in.fname = fname;
1617         io.in.durable_handle = h;
1618         h = NULL;
1619
1620         status = smb2_create(tree, mem_ctx, &io);
1621         CHECK_STATUS(status, NT_STATUS_OK);
1622         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1623                            alloc_size_step, 0);
1624         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1625         _h = io.out.file.handle;
1626         h = &_h;
1627
1628         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1629
1630         /* write one byte */
1631         status = smb2_util_write(tree, *h, b, 0, 1);
1632         CHECK_STATUS(status, NT_STATUS_OK);
1633
1634         /* disconnect, reconnect and then do durable reopen */
1635         talloc_free(tree);
1636         tree = NULL;
1637
1638         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1639                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1640                 ret = false;
1641                 goto done;
1642         }
1643
1644         ZERO_STRUCT(io);
1645         io.in.fname = fname;
1646         io.in.durable_handle = h;
1647         h = NULL;
1648
1649         status = smb2_create(tree, mem_ctx, &io);
1650         CHECK_STATUS(status, NT_STATUS_OK);
1651         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1652                            alloc_size_step, 1);
1653         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1654         _h = io.out.file.handle;
1655         h = &_h;
1656
1657         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1658
1659         /* write more byte than initial allocation size */
1660         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
1661
1662         /* disconnect, reconnect and then do durable reopen */
1663         talloc_free(tree);
1664         tree = NULL;
1665
1666         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1667                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1668                 ret = false;
1669                 goto done;
1670         }
1671
1672         ZERO_STRUCT(io);
1673         io.in.fname = fname;
1674         io.in.durable_handle = h;
1675         h = NULL;
1676
1677         status = smb2_create(tree, mem_ctx, &io);
1678         CHECK_STATUS(status, NT_STATUS_OK);
1679         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
1680                            alloc_size_step * 2, alloc_size_step + 1);
1681         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1682         _h = io.out.file.handle;
1683         h = &_h;
1684
1685 done:
1686         if (h != NULL) {
1687                 smb2_util_close(tree, *h);
1688         }
1689
1690         smb2_util_unlink(tree, fname);
1691
1692         talloc_free(tree);
1693
1694         talloc_free(mem_ctx);
1695
1696         return ret;
1697 }
1698
1699 /**
1700  * test behaviour when a disconnect happens while creating a read-only file
1701  */
1702 static bool test_durable_open_read_only(struct torture_context *tctx,
1703                                         struct smb2_tree *tree)
1704 {
1705         NTSTATUS status;
1706         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1707         char fname[256];
1708         struct smb2_handle _h;
1709         struct smb2_handle *h = NULL;
1710         struct smb2_create io;
1711         bool ret = true;
1712         uint64_t previous_session_id;
1713         const uint8_t b = 0;
1714         uint64_t alloc_size = 0;
1715
1716         /* Choose a random name in case the state is left a little funky. */
1717         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
1718                  generate_random_str(tctx, 8));
1719
1720         smb2_util_unlink(tree, fname);
1721
1722         smb2_oplock_create_share(&io, fname,
1723                                  smb2_util_share_access(""),
1724                                  smb2_util_oplock_level("b"));
1725         io.in.durable_open = true;
1726         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
1727
1728         status = smb2_create(tree, mem_ctx, &io);
1729         CHECK_STATUS(status, NT_STATUS_OK);
1730         _h = io.out.file.handle;
1731         h = &_h;
1732         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
1733         CHECK_VAL(io.out.durable_open, true);
1734         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1735
1736         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1737
1738         /* write one byte */
1739         status = smb2_util_write(tree, *h, &b, 0, 1);
1740         CHECK_STATUS(status, NT_STATUS_OK);
1741
1742         /* disconnect, reconnect and then do durable reopen */
1743         talloc_free(tree);
1744         tree = NULL;
1745
1746         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
1747                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1748                 ret = false;
1749                 goto done;
1750         }
1751
1752         ZERO_STRUCT(io);
1753         io.in.fname = fname;
1754         io.in.durable_handle = h;
1755         h = NULL;
1756
1757         status = smb2_create(tree, mem_ctx, &io);
1758         CHECK_STATUS(status, NT_STATUS_OK);
1759         alloc_size = io.out.alloc_size;
1760         CHECK_CREATED_SIZE(&io, EXISTED,
1761                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
1762                            alloc_size, 1);
1763         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1764         _h = io.out.file.handle;
1765         h = &_h;
1766
1767         /* write one byte */
1768         status = smb2_util_write(tree, *h, &b, 1, 1);
1769         CHECK_STATUS(status, NT_STATUS_OK);
1770
1771 done:
1772         if (h != NULL) {
1773                 union smb_setfileinfo sfinfo;
1774
1775                 ZERO_STRUCT(sfinfo);
1776                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
1777                 sfinfo.basic_info.in.file.handle = *h;
1778                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
1779                 smb2_setinfo_file(tree, &sfinfo);
1780
1781                 smb2_util_close(tree, *h);
1782         }
1783
1784         smb2_util_unlink(tree, fname);
1785
1786         talloc_free(tree);
1787
1788         talloc_free(mem_ctx);
1789
1790         return ret;
1791 }
1792
1793 /**
1794  * durable open with oplock, disconnect, exit
1795  */
1796 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
1797                                                 struct smb2_tree *tree)
1798 {
1799         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1800         struct smb2_create io;
1801         struct smb2_handle _h;
1802         struct smb2_handle *h = NULL;
1803         NTSTATUS status;
1804         char fname[256];
1805         bool ret = true;
1806
1807         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
1808                  generate_random_str(tctx, 8));
1809
1810         smb2_util_unlink(tree, fname);
1811
1812         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1813         io.in.durable_open = true;
1814
1815         status = smb2_create(tree, mem_ctx, &io);
1816         CHECK_STATUS(status, NT_STATUS_OK);
1817
1818         _h = io.out.file.handle;
1819         h = &_h;
1820
1821         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1822         CHECK_VAL(io.out.durable_open, true);
1823         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1824
1825         /* disconnect */
1826         talloc_free(tree);
1827         tree = NULL;
1828
1829 done:
1830         if (tree != NULL) {
1831                 if (h != NULL) {
1832                         smb2_util_close(tree, *h);
1833                 }
1834                 smb2_util_unlink(tree, fname);
1835         }
1836
1837         return ret;
1838 }
1839
1840
1841 struct torture_suite *torture_smb2_durable_open_init(void)
1842 {
1843         struct torture_suite *suite =
1844             torture_suite_create(talloc_autofree_context(), "durable-open");
1845
1846         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1847         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1848         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1849         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1850         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1851         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1852         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1853         torture_suite_add_1smb2_test(suite, "delete_on_close1",
1854                                      test_durable_open_delete_on_close1);
1855         torture_suite_add_1smb2_test(suite, "delete_on_close2",
1856                                      test_durable_open_delete_on_close2);
1857         torture_suite_add_1smb2_test(suite, "file-position",
1858             test_durable_open_file_position);
1859         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1860         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1861         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
1862         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
1863         torture_suite_add_2smb2_test(suite, "open2-lease",
1864                                      test_durable_open_open2_lease);
1865         torture_suite_add_2smb2_test(suite, "open2-oplock",
1866                                      test_durable_open_open2_oplock);
1867         torture_suite_add_1smb2_test(suite, "alloc-size",
1868                                      test_durable_open_alloc_size);
1869         torture_suite_add_1smb2_test(suite, "read-only",
1870                                      test_durable_open_read_only);
1871
1872         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1873
1874         return suite;
1875 }
1876
1877 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
1878 {
1879         struct torture_suite *suite =
1880             torture_suite_create(talloc_autofree_context(),
1881                                  "durable-open-disconnect");
1882
1883         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
1884                                      test_durable_open_oplock_disconnect);
1885
1886         suite->description = talloc_strdup(suite,
1887                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
1888
1889         return suite;
1890 }