s4:torture: add a torture_smb2_session_setup() utility function
[samba.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27
28 #define CHECK_VAL(v, correct) do { \
29         if ((v) != (correct)) { \
30                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
31                                 __location__, #v, (int)v, (int)correct); \
32                 ret = false; \
33         }} while (0)
34
35 #define CHECK_STATUS(status, correct) do { \
36         if (!NT_STATUS_EQUAL(status, correct)) { \
37                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
38                        nt_errstr(status), nt_errstr(correct)); \
39                 ret = false; \
40                 goto done; \
41         }} while (0)
42
43 #define CHECK_CREATED(__io, __created, __attribute)                     \
44         do {                                                            \
45                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
47                 CHECK_VAL((__io)->out.size, 0);                         \
48                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
49                 CHECK_VAL((__io)->out.reserved2, 0);                    \
50         } while(0)
51
52
53 /**
54  * basic durable_open test.
55  * durable state should only be granted when requested
56  * along with a batch oplock or a handle lease.
57  *
58  * This test tests durable open with all possible oplock types.
59  */
60
61 struct durable_open_vs_oplock {
62         const char *level;
63         const char *share_mode;
64         bool expected;
65 };
66
67 #define NUM_OPLOCK_TYPES 4
68 #define NUM_SHARE_MODES 8
69 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
70 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
71 {
72         { "", "", false },
73         { "", "R", false },
74         { "", "W", false },
75         { "", "D", false },
76         { "", "RD", false },
77         { "", "RW", false },
78         { "", "WD", false },
79         { "", "RWD", false },
80
81         { "s", "", false },
82         { "s", "R", false },
83         { "s", "W", false },
84         { "s", "D", false },
85         { "s", "RD", false },
86         { "s", "RW", false },
87         { "s", "WD", false },
88         { "s", "RWD", false },
89
90         { "x", "", false },
91         { "x", "R", false },
92         { "x", "W", false },
93         { "x", "D", false },
94         { "x", "RD", false },
95         { "x", "RW", false },
96         { "x", "WD", false },
97         { "x", "RWD", false },
98
99         { "b", "", true },
100         { "b", "R", true },
101         { "b", "W", true },
102         { "b", "D", true },
103         { "b", "RD", true },
104         { "b", "RW", true },
105         { "b", "WD", true },
106         { "b", "RWD", true },
107 };
108
109 static bool test_one_durable_open_open1(struct torture_context *tctx,
110                                         struct smb2_tree *tree,
111                                         const char *fname,
112                                         struct durable_open_vs_oplock test)
113 {
114         NTSTATUS status;
115         TALLOC_CTX *mem_ctx = talloc_new(tctx);
116         struct smb2_handle _h;
117         struct smb2_handle *h = NULL;
118         bool ret = true;
119         struct smb2_create io;
120
121         smb2_util_unlink(tree, fname);
122
123         smb2_oplock_create_share(&io, fname,
124                                  smb2_util_share_access(test.share_mode),
125                                  smb2_util_oplock_level(test.level));
126         io.in.durable_open = true;
127
128         status = smb2_create(tree, mem_ctx, &io);
129         CHECK_STATUS(status, NT_STATUS_OK);
130         _h = io.out.file.handle;
131         h = &_h;
132         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
133         CHECK_VAL(io.out.durable_open, test.expected);
134         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
135
136 done:
137         if (h != NULL) {
138                 smb2_util_close(tree, *h);
139         }
140         smb2_util_unlink(tree, fname);
141         talloc_free(mem_ctx);
142
143         return ret;
144 }
145
146 bool test_durable_open_open1(struct torture_context *tctx,
147                              struct smb2_tree *tree)
148 {
149         TALLOC_CTX *mem_ctx = talloc_new(tctx);
150         char fname[256];
151         bool ret = true;
152         int i;
153
154         /* Choose a random name in case the state is left a little funky. */
155         snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
156
157         smb2_util_unlink(tree, fname);
158
159         /* test various oplock levels with durable open */
160
161         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
162                 ret = test_one_durable_open_open1(tctx,
163                                                   tree,
164                                                   fname,
165                                                   durable_open_vs_oplock_table[i]);
166                 if (ret == false) {
167                         goto done;
168                 }
169         }
170
171 done:
172         smb2_util_unlink(tree, fname);
173         talloc_free(tree);
174         talloc_free(mem_ctx);
175
176         return ret;
177 }
178
179 /**
180  * basic durable_open test.
181  * durable state should only be granted when requested
182  * along with a batch oplock or a handle lease.
183  *
184  * This test tests durable open with all valid lease types.
185  */
186
187 struct durable_open_vs_lease {
188         const char *type;
189         const char *share_mode;
190         bool expected;
191 };
192
193 #define NUM_LEASE_TYPES 5
194 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
195 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
196 {
197         { "", "", false },
198         { "", "R", false },
199         { "", "W", false },
200         { "", "D", false },
201         { "", "RW", false },
202         { "", "RD", false },
203         { "", "WD", false },
204         { "", "RWD", false },
205
206         { "R", "", false },
207         { "R", "R", false },
208         { "R", "W", false },
209         { "R", "D", false },
210         { "R", "RW", false },
211         { "R", "RD", false },
212         { "R", "DW", false },
213         { "R", "RWD", false },
214
215         { "RW", "", false },
216         { "RW", "R", false },
217         { "RW", "W", false },
218         { "RW", "D", false },
219         { "RW", "RW", false },
220         { "RW", "RD", false },
221         { "RW", "WD", false },
222         { "RW", "RWD", false },
223
224         { "RH", "", true },
225         { "RH", "R", true },
226         { "RH", "W", true },
227         { "RH", "D", true },
228         { "RH", "RW", true },
229         { "RH", "RD", true },
230         { "RH", "WD", true },
231         { "RH", "RWD", true },
232
233         { "RHW", "", true },
234         { "RHW", "R", true },
235         { "RHW", "W", true },
236         { "RHW", "D", true },
237         { "RHW", "RW", true },
238         { "RHW", "RD", true },
239         { "RHW", "WD", true },
240         { "RHW", "RWD", true },
241 };
242
243 static bool test_one_durable_open_open2(struct torture_context *tctx,
244                                         struct smb2_tree *tree,
245                                         const char *fname,
246                                         struct durable_open_vs_lease test)
247 {
248         NTSTATUS status;
249         TALLOC_CTX *mem_ctx = talloc_new(tctx);
250         struct smb2_handle _h;
251         struct smb2_handle *h = NULL;
252         bool ret = true;
253         struct smb2_create io;
254         struct smb2_lease ls;
255         uint64_t lease;
256
257         smb2_util_unlink(tree, fname);
258
259         lease = random();
260
261         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
262                                 smb2_util_share_access(test.share_mode),
263                                 lease,
264                                 smb2_util_lease_state(test.type));
265         io.in.durable_open = true;
266
267         status = smb2_create(tree, mem_ctx, &io);
268         CHECK_STATUS(status, NT_STATUS_OK);
269         _h = io.out.file.handle;
270         h = &_h;
271         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
272         CHECK_VAL(io.out.durable_open, test.expected);
273         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
274         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
275         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
276         CHECK_VAL(io.out.lease_response.lease_state,
277                   smb2_util_lease_state(test.type));
278 done:
279         if (h != NULL) {
280                 smb2_util_close(tree, *h);
281         }
282         smb2_util_unlink(tree, fname);
283         talloc_free(mem_ctx);
284
285         return ret;
286 }
287
288 bool test_durable_open_open2(struct torture_context *tctx,
289                              struct smb2_tree *tree)
290 {
291         TALLOC_CTX *mem_ctx = talloc_new(tctx);
292         char fname[256];
293         bool ret = true;
294         int i;
295
296         /* Choose a random name in case the state is left a little funky. */
297         snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
298
299         smb2_util_unlink(tree, fname);
300
301
302         /* test various oplock levels with durable open */
303
304         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
305                 ret = test_one_durable_open_open2(tctx,
306                                                   tree,
307                                                   fname,
308                                                   durable_open_vs_lease_table[i]);
309                 if (ret == false) {
310                         goto done;
311                 }
312         }
313
314 done:
315         smb2_util_unlink(tree, fname);
316         talloc_free(tree);
317         talloc_free(mem_ctx);
318
319         return ret;
320 }
321
322 /**
323  * basic test for doing a durable open
324  * and do a durable reopen on the same connection
325  */
326 bool test_durable_open_reopen1(struct torture_context *tctx,
327                                struct smb2_tree *tree)
328 {
329         NTSTATUS status;
330         TALLOC_CTX *mem_ctx = talloc_new(tctx);
331         char fname[256];
332         struct smb2_handle _h;
333         struct smb2_handle *h = NULL;
334         struct smb2_create io1, io2;
335         bool ret = true;
336
337         /* Choose a random name in case the state is left a little funky. */
338         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
339                  generate_random_str(tctx, 8));
340
341         smb2_util_unlink(tree, fname);
342
343         smb2_oplock_create_share(&io1, fname,
344                                  smb2_util_share_access(""),
345                                  smb2_util_oplock_level("b"));
346         io1.in.durable_open = true;
347
348         status = smb2_create(tree, mem_ctx, &io1);
349         CHECK_STATUS(status, NT_STATUS_OK);
350         _h = io1.out.file.handle;
351         h = &_h;
352         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
353         CHECK_VAL(io1.out.durable_open, true);
354         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
355
356         /* try a durable reconnect while the file is still open */
357         ZERO_STRUCT(io2);
358         io2.in.fname = fname;
359         io2.in.durable_handle = h;
360
361         status = smb2_create(tree, mem_ctx, &io2);
362         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
363
364 done:
365         if (h != NULL) {
366                 smb2_util_close(tree, *h);
367         }
368
369         smb2_util_unlink(tree, fname);
370
371         talloc_free(tree);
372
373         talloc_free(mem_ctx);
374
375         return ret;
376 }
377
378 /**
379  * basic test for doing a durable open
380  * tcp disconnect, reconnect, do a durable reopen (succeeds)
381  */
382 bool test_durable_open_reopen2(struct torture_context *tctx,
383                                struct smb2_tree *tree)
384 {
385         NTSTATUS status;
386         TALLOC_CTX *mem_ctx = talloc_new(tctx);
387         char fname[256];
388         struct smb2_handle _h;
389         struct smb2_handle *h = NULL;
390         struct smb2_create io1, io2;
391         bool ret = true;
392
393         /* Choose a random name in case the state is left a little funky. */
394         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
395                  generate_random_str(tctx, 8));
396
397         smb2_util_unlink(tree, fname);
398
399         smb2_oplock_create_share(&io1, fname,
400                                  smb2_util_share_access(""),
401                                  smb2_util_oplock_level("b"));
402         io1.in.durable_open = true;
403
404         status = smb2_create(tree, mem_ctx, &io1);
405         CHECK_STATUS(status, NT_STATUS_OK);
406         _h = io1.out.file.handle;
407         h = &_h;
408         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
409         CHECK_VAL(io1.out.durable_open, true);
410         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
411
412         /* disconnect, reconnect and then do durable reopen */
413         talloc_free(tree);
414         tree = NULL;
415
416         if (!torture_smb2_connection(tctx, &tree)) {
417                 torture_warning(tctx, "couldn't reconnect, bailing\n");
418                 ret = false;
419                 goto done;
420         }
421
422         ZERO_STRUCT(io2);
423         io2.in.fname = fname;
424         io2.in.durable_handle = h;
425
426         status = smb2_create(tree, mem_ctx, &io2);
427         CHECK_STATUS(status, NT_STATUS_OK);
428         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
429         CHECK_VAL(io2.out.durable_open, true);
430         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
431         _h = io2.out.file.handle;
432         h = &_h;
433
434 done:
435         if (h != NULL) {
436                 smb2_util_close(tree, *h);
437         }
438
439         smb2_util_unlink(tree, fname);
440
441         talloc_free(tree);
442
443         talloc_free(mem_ctx);
444
445         return ret;
446 }
447
448 /**
449  * basic test for doing a durable open:
450  * tdis, new tcon, try durable reopen (fails)
451  */
452 bool test_durable_open_reopen3(struct torture_context *tctx,
453                                struct smb2_tree *tree)
454 {
455         NTSTATUS status;
456         TALLOC_CTX *mem_ctx = talloc_new(tctx);
457         char fname[256];
458         struct smb2_handle _h;
459         struct smb2_handle *h = NULL;
460         struct smb2_create io1, io2;
461         bool ret = true;
462         struct smb2_tree *tree2;
463
464         /* Choose a random name in case the state is left a little funky. */
465         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
466                  generate_random_str(tctx, 8));
467
468         smb2_util_unlink(tree, fname);
469
470         smb2_oplock_create_share(&io1, fname,
471                                  smb2_util_share_access(""),
472                                  smb2_util_oplock_level("b"));
473         io1.in.durable_open = true;
474
475         status = smb2_create(tree, mem_ctx, &io1);
476         CHECK_STATUS(status, NT_STATUS_OK);
477         _h = io1.out.file.handle;
478         h = &_h;
479         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
480         CHECK_VAL(io1.out.durable_open, true);
481         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
482
483         /* disconnect, reconnect and then do durable reopen */
484         status = smb2_tdis(tree);
485         CHECK_STATUS(status, NT_STATUS_OK);
486
487         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
488                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
489                 ret = false;
490                 goto done;
491         }
492
493
494         ZERO_STRUCT(io2);
495         io2.in.fname = fname;
496         io2.in.durable_handle = h;
497
498         status = smb2_create(tree2, mem_ctx, &io2);
499         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
500
501 done:
502         if (h != NULL) {
503                 smb2_util_close(tree, *h);
504         }
505
506         smb2_util_unlink(tree2, fname);
507
508         talloc_free(tree);
509
510         talloc_free(mem_ctx);
511
512         return ret;
513 }
514
515 /*
516    basic testing of SMB2 durable opens
517    regarding the position information on the handle
518 */
519 bool test_durable_open_file_position(struct torture_context *tctx,
520                                      struct smb2_tree *tree1,
521                                      struct smb2_tree *tree2)
522 {
523         TALLOC_CTX *mem_ctx = talloc_new(tctx);
524         struct smb2_handle h1, h2;
525         struct smb2_create io1, io2;
526         NTSTATUS status;
527         const char *fname = "durable_open_position.dat";
528         union smb_fileinfo qfinfo;
529         union smb_setfileinfo sfinfo;
530         bool ret = true;
531         uint64_t pos;
532
533         smb2_util_unlink(tree1, fname);
534
535         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
536         io1.in.durable_open = true;
537
538         status = smb2_create(tree1, mem_ctx, &io1);
539         CHECK_STATUS(status, NT_STATUS_OK);
540         h1 = io1.out.file.handle;
541         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
542         CHECK_VAL(io1.out.durable_open, true);
543         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
544
545         /* TODO: check extra blob content */
546
547         ZERO_STRUCT(qfinfo);
548         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
549         qfinfo.generic.in.file.handle = h1;
550         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
551         CHECK_STATUS(status, NT_STATUS_OK);
552         CHECK_VAL(qfinfo.position_information.out.position, 0);
553         pos = qfinfo.position_information.out.position;
554         torture_comment(tctx, "position: %llu\n",
555                         (unsigned long long)pos);
556
557         ZERO_STRUCT(sfinfo);
558         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
559         sfinfo.generic.in.file.handle = h1;
560         sfinfo.position_information.in.position = 0x1000;
561         status = smb2_setinfo_file(tree1, &sfinfo);
562         CHECK_STATUS(status, NT_STATUS_OK);
563
564         ZERO_STRUCT(qfinfo);
565         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
566         qfinfo.generic.in.file.handle = h1;
567         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
568         CHECK_STATUS(status, NT_STATUS_OK);
569         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
570         pos = qfinfo.position_information.out.position;
571         torture_comment(tctx, "position: %llu\n",
572                         (unsigned long long)pos);
573
574         talloc_free(tree1);
575         tree1 = NULL;
576
577         ZERO_STRUCT(qfinfo);
578         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
579         qfinfo.generic.in.file.handle = h1;
580         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
581         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
582
583         ZERO_STRUCT(io2);
584         io2.in.fname = fname;
585         io2.in.durable_handle = &h1;
586
587         status = smb2_create(tree2, mem_ctx, &io2);
588         CHECK_STATUS(status, NT_STATUS_OK);
589         CHECK_VAL(io2.out.durable_open, true);
590         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
591         CHECK_VAL(io2.out.reserved, 0x00);
592         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
593         CHECK_VAL(io2.out.alloc_size, 0);
594         CHECK_VAL(io2.out.size, 0);
595         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
596         CHECK_VAL(io2.out.reserved2, 0);
597
598         h2 = io2.out.file.handle;
599
600         ZERO_STRUCT(qfinfo);
601         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
602         qfinfo.generic.in.file.handle = h2;
603         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
604         CHECK_STATUS(status, NT_STATUS_OK);
605         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
606         pos = qfinfo.position_information.out.position;
607         torture_comment(tctx, "position: %llu\n",
608                         (unsigned long long)pos);
609
610         smb2_util_close(tree2, h2);
611
612         talloc_free(mem_ctx);
613
614         smb2_util_unlink(tree2, fname);
615 done:
616         talloc_free(tree1);
617         talloc_free(tree2);
618
619         return ret;
620 }
621
622 /*
623   Open, disconnect, oplock break, reconnect.
624 */
625 bool test_durable_open_oplock(struct torture_context *tctx,
626                               struct smb2_tree *tree1,
627                               struct smb2_tree *tree2)
628 {
629         TALLOC_CTX *mem_ctx = talloc_new(tctx);
630         struct smb2_create io1, io2;
631         struct smb2_handle h1, h2;
632         NTSTATUS status;
633         char fname[256];
634         bool ret = true;
635
636         /* Choose a random name in case the state is left a little funky. */
637         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
638
639         /* Clean slate */
640         smb2_util_unlink(tree1, fname);
641
642         /* Create with batch oplock */
643         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
644         io1.in.durable_open = true;
645
646         io2 = io1;
647         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
648
649         status = smb2_create(tree1, mem_ctx, &io1);
650         CHECK_STATUS(status, NT_STATUS_OK);
651         h1 = io1.out.file.handle;
652         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
653         CHECK_VAL(io1.out.durable_open, true);
654         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
655
656         /* Disconnect after getting the batch */
657         talloc_free(tree1);
658         tree1 = NULL;
659
660         /*
661          * Windows7 (build 7000) will break a batch oplock immediately if the
662          * original client is gone. (ZML: This seems like a bug. It should give
663          * some time for the client to reconnect!)
664          */
665         status = smb2_create(tree2, mem_ctx, &io2);
666         CHECK_STATUS(status, NT_STATUS_OK);
667         h2 = io2.out.file.handle;
668         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
669         CHECK_VAL(io2.out.durable_open, true);
670         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
671
672         /* What if tree1 tries to come back and reclaim? */
673         if (!torture_smb2_connection(tctx, &tree1)) {
674                 torture_warning(tctx, "couldn't reconnect, bailing\n");
675                 ret = false;
676                 goto done;
677         }
678
679         ZERO_STRUCT(io1);
680         io1.in.fname = fname;
681         io1.in.durable_handle = &h1;
682
683         status = smb2_create(tree1, mem_ctx, &io1);
684         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
685
686  done:
687         smb2_util_close(tree2, h2);
688         smb2_util_unlink(tree2, fname);
689
690         talloc_free(tree1);
691         talloc_free(tree2);
692
693         return ret;
694 }
695
696 /*
697   Open, disconnect, lease break, reconnect.
698 */
699 bool test_durable_open_lease(struct torture_context *tctx,
700                              struct smb2_tree *tree1,
701                              struct smb2_tree *tree2)
702 {
703         TALLOC_CTX *mem_ctx = talloc_new(tctx);
704         struct smb2_create io1, io2;
705         struct smb2_lease ls1, ls2;
706         struct smb2_handle h1, h2;
707         NTSTATUS status;
708         char fname[256];
709         bool ret = true;
710         uint64_t lease1, lease2;
711
712         /*
713          * Choose a random name and random lease in case the state is left a
714          * little funky.
715          */
716         lease1 = random();
717         lease2 = random();
718         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
719
720         /* Clean slate */
721         smb2_util_unlink(tree1, fname);
722
723         /* Create with lease */
724         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
725                           lease1, smb2_util_lease_state("RHW"));
726         io1.in.durable_open = true;
727
728         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
729                           lease2, smb2_util_lease_state("RHW"));
730         io2.in.durable_open = true;
731         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
732
733         status = smb2_create(tree1, mem_ctx, &io1);
734         CHECK_STATUS(status, NT_STATUS_OK);
735         h1 = io1.out.file.handle;
736         CHECK_VAL(io1.out.durable_open, true);
737         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
738
739         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
740         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
741         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
742         CHECK_VAL(io1.out.lease_response.lease_state,
743             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
744
745         /* Disconnect after getting the lease */
746         talloc_free(tree1);
747         tree1 = NULL;
748
749         /*
750          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
751          * even if the original client is gone. (ZML: This seems like a bug. It
752          * should give some time for the client to reconnect! And why RH?)
753          * 
754          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
755          * Test is adapted accordingly.
756          */
757         status = smb2_create(tree2, mem_ctx, &io2);
758         CHECK_STATUS(status, NT_STATUS_OK);
759         h2 = io2.out.file.handle;
760         CHECK_VAL(io2.out.durable_open, true);
761         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
762
763         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
764         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
765         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
766         CHECK_VAL(io2.out.lease_response.lease_state,
767             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
768
769         /* What if tree1 tries to come back and reclaim? */
770         if (!torture_smb2_connection(tctx, &tree1)) {
771                 torture_warning(tctx, "couldn't reconnect, bailing\n");
772                 ret = false;
773                 goto done;
774         }
775
776         ZERO_STRUCT(io1);
777         io1.in.fname = fname;
778         io1.in.durable_handle = &h1;
779         io1.in.lease_request = &ls1;
780
781         status = smb2_create(tree1, mem_ctx, &io1);
782         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
783
784  done:
785         smb2_util_close(tree2, h2);
786         smb2_util_unlink(tree2, fname);
787
788         talloc_free(tree1);
789         talloc_free(tree2);
790
791         return ret;
792 }
793
794 /*
795   Open, take BRL, disconnect, reconnect.
796 */
797 bool test_durable_open_lock(struct torture_context *tctx,
798                             struct smb2_tree *tree)
799 {
800         TALLOC_CTX *mem_ctx = talloc_new(tctx);
801         struct smb2_create io;
802         struct smb2_lease ls;
803         struct smb2_handle h;
804         struct smb2_lock lck;
805         struct smb2_lock_element el[2];
806         NTSTATUS status;
807         char fname[256];
808         bool ret = true;
809         uint64_t lease;
810
811         /*
812          * Choose a random name and random lease in case the state is left a
813          * little funky.
814          */
815         lease = random();
816         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
817
818         /* Clean slate */
819         smb2_util_unlink(tree, fname);
820
821         /* Create with lease */
822
823         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
824                           smb2_util_lease_state("RWH"));
825         io.in.durable_open              = true;
826
827         status = smb2_create(tree, mem_ctx, &io);
828         CHECK_STATUS(status, NT_STATUS_OK);
829         h = io.out.file.handle;
830         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
831
832         CHECK_VAL(io.out.durable_open, true);
833         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
834         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
835         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
836         CHECK_VAL(io.out.lease_response.lease_state,
837             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
838
839         ZERO_STRUCT(lck);
840         ZERO_STRUCT(el);
841         lck.in.locks            = el;
842         lck.in.lock_count       = 0x0001;
843         lck.in.lock_sequence    = 0x00000000;
844         lck.in.file.handle      = h;
845         el[0].offset            = 0;
846         el[0].length            = 1;
847         el[0].reserved          = 0x00000000;
848         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
849         status = smb2_lock(tree, &lck);
850         CHECK_STATUS(status, NT_STATUS_OK);
851
852         /* Disconnect/Reconnect. */
853         talloc_free(tree);
854         tree = NULL;
855
856         if (!torture_smb2_connection(tctx, &tree)) {
857                 torture_warning(tctx, "couldn't reconnect, bailing\n");
858                 ret = false;
859                 goto done;
860         }
861
862         ZERO_STRUCT(io);
863         io.in.fname = fname;
864         io.in.durable_handle = &h;
865         io.in.lease_request = &ls;
866
867         status = smb2_create(tree, mem_ctx, &io);
868         CHECK_STATUS(status, NT_STATUS_OK);
869         h = io.out.file.handle;
870
871         lck.in.file.handle      = h;
872         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
873         status = smb2_lock(tree, &lck);
874         CHECK_STATUS(status, NT_STATUS_OK);
875
876  done:
877         smb2_util_close(tree, h);
878         smb2_util_unlink(tree, fname);
879         talloc_free(tree);
880
881         return ret;
882 }
883
884 /*
885   Open, disconnect, open in another tree, reconnect.
886
887   This test actually demonstrates a minimum level of respect for the durable
888   open in the face of another open. As long as this test shows an inability to
889   reconnect after an open, the oplock/lease tests above will certainly
890   demonstrate an error on reconnect.
891 */
892 bool test_durable_open_open(struct torture_context *tctx,
893                             struct smb2_tree *tree1,
894                             struct smb2_tree *tree2)
895 {
896         TALLOC_CTX *mem_ctx = talloc_new(tctx);
897         struct smb2_create io1, io2;
898         struct smb2_lease ls;
899         struct smb2_handle h1, h2;
900         NTSTATUS status;
901         char fname[256];
902         bool ret = true;
903         uint64_t lease;
904
905         /*
906          * Choose a random name and random lease in case the state is left a
907          * little funky.
908          */
909         lease = random();
910         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
911
912         /* Clean slate */
913         smb2_util_unlink(tree1, fname);
914
915         /* Create with lease */
916         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
917                                 smb2_util_share_access(""),
918                                 lease,
919                                 smb2_util_lease_state("RH"));
920         io1.in.durable_open = true;
921
922         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
923
924         status = smb2_create(tree1, mem_ctx, &io1);
925         CHECK_STATUS(status, NT_STATUS_OK);
926         h1 = io1.out.file.handle;
927         CHECK_VAL(io1.out.durable_open, true);
928         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
929
930         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
931         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
932         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
933         CHECK_VAL(io1.out.lease_response.lease_state,
934                   smb2_util_lease_state("RH"));
935
936         /* Disconnect */
937         talloc_free(tree1);
938         tree1 = NULL;
939
940         /* Open the file in tree2 */
941         status = smb2_create(tree2, mem_ctx, &io2);
942         CHECK_STATUS(status, NT_STATUS_OK);
943         h2 = io2.out.file.handle;
944         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
945
946         /* Reconnect */
947         if (!torture_smb2_connection(tctx, &tree1)) {
948                 torture_warning(tctx, "couldn't reconnect, bailing\n");
949                 ret = false;
950                 goto done;
951         }
952
953         ZERO_STRUCT(io1);
954         io1.in.fname = fname;
955         io1.in.durable_handle = &h1;
956         io1.in.lease_request = &ls;
957
958         /*
959          * Windows7 (build 7000) will give away an open immediately if the
960          * original client is gone. (ZML: This seems like a bug. It should give
961          * some time for the client to reconnect!)
962          */
963         status = smb2_create(tree1, mem_ctx, &io1);
964         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
965         h1 = io1.out.file.handle;
966
967  done:
968         smb2_util_close(tree2, h2);
969         smb2_util_unlink(tree2, fname);
970         smb2_util_close(tree1, h1);
971         smb2_util_unlink(tree1, fname);
972
973         talloc_free(tree1);
974         talloc_free(tree2);
975
976         return ret;
977 }
978
979 struct torture_suite *torture_smb2_durable_open_init(void)
980 {
981         struct torture_suite *suite =
982             torture_suite_create(talloc_autofree_context(), "durable-open");
983
984         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
985         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
986         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
987         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
988         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
989         torture_suite_add_2smb2_test(suite, "file-position",
990             test_durable_open_file_position);
991         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
992         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
993         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
994         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
995
996         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
997
998         return suite;
999 }