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