s4:torture: add smb2.durable_open test reopen1
[ira/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7
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 testing of SMB2 durable opens
380    regarding the position information on the handle
381 */
382 bool test_durable_open_file_position(struct torture_context *tctx,
383                                      struct smb2_tree *tree1,
384                                      struct smb2_tree *tree2)
385 {
386         TALLOC_CTX *mem_ctx = talloc_new(tctx);
387         struct smb2_handle h1, h2;
388         struct smb2_create io1, io2;
389         NTSTATUS status;
390         const char *fname = "durable_open_position.dat";
391         union smb_fileinfo qfinfo;
392         union smb_setfileinfo sfinfo;
393         bool ret = true;
394         uint64_t pos;
395
396         smb2_util_unlink(tree1, fname);
397
398         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
399         io1.in.durable_open = true;
400
401         status = smb2_create(tree1, mem_ctx, &io1);
402         CHECK_STATUS(status, NT_STATUS_OK);
403         h1 = io1.out.file.handle;
404         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
405         CHECK_VAL(io1.out.durable_open, true);
406         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
407
408         /* TODO: check extra blob content */
409
410         ZERO_STRUCT(qfinfo);
411         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
412         qfinfo.generic.in.file.handle = h1;
413         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
414         CHECK_STATUS(status, NT_STATUS_OK);
415         CHECK_VAL(qfinfo.position_information.out.position, 0);
416         pos = qfinfo.position_information.out.position;
417         torture_comment(tctx, "position: %llu\n",
418                         (unsigned long long)pos);
419
420         ZERO_STRUCT(sfinfo);
421         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
422         sfinfo.generic.in.file.handle = h1;
423         sfinfo.position_information.in.position = 0x1000;
424         status = smb2_setinfo_file(tree1, &sfinfo);
425         CHECK_STATUS(status, NT_STATUS_OK);
426
427         ZERO_STRUCT(qfinfo);
428         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
429         qfinfo.generic.in.file.handle = h1;
430         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
431         CHECK_STATUS(status, NT_STATUS_OK);
432         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
433         pos = qfinfo.position_information.out.position;
434         torture_comment(tctx, "position: %llu\n",
435                         (unsigned long long)pos);
436
437         talloc_free(tree1);
438         tree1 = NULL;
439
440         ZERO_STRUCT(qfinfo);
441         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
442         qfinfo.generic.in.file.handle = h1;
443         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
444         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
445
446         ZERO_STRUCT(io2);
447         io2.in.fname = fname;
448         io2.in.durable_handle = &h1;
449
450         status = smb2_create(tree2, mem_ctx, &io2);
451         CHECK_STATUS(status, NT_STATUS_OK);
452         CHECK_VAL(io2.out.durable_open, true);
453         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
454         CHECK_VAL(io2.out.reserved, 0x00);
455         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
456         CHECK_VAL(io2.out.alloc_size, 0);
457         CHECK_VAL(io2.out.size, 0);
458         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
459         CHECK_VAL(io2.out.reserved2, 0);
460
461         h2 = io2.out.file.handle;
462
463         ZERO_STRUCT(qfinfo);
464         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
465         qfinfo.generic.in.file.handle = h2;
466         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
467         CHECK_STATUS(status, NT_STATUS_OK);
468         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
469         pos = qfinfo.position_information.out.position;
470         torture_comment(tctx, "position: %llu\n",
471                         (unsigned long long)pos);
472
473         smb2_util_close(tree2, h2);
474
475         talloc_free(mem_ctx);
476
477         smb2_util_unlink(tree2, fname);
478 done:
479         talloc_free(tree1);
480         talloc_free(tree2);
481
482         return ret;
483 }
484
485 /*
486   Open, disconnect, oplock break, reconnect.
487 */
488 bool test_durable_open_oplock(struct torture_context *tctx,
489                               struct smb2_tree *tree1,
490                               struct smb2_tree *tree2)
491 {
492         TALLOC_CTX *mem_ctx = talloc_new(tctx);
493         struct smb2_create io1, io2;
494         struct smb2_handle h1, h2;
495         NTSTATUS status;
496         char fname[256];
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_oplock_%s.dat", generate_random_str(tctx, 8));
501
502         /* Clean slate */
503         smb2_util_unlink(tree1, fname);
504
505         /* Create with batch oplock */
506         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
507         io1.in.durable_open = true;
508
509         io2 = io1;
510         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
511
512         status = smb2_create(tree1, mem_ctx, &io1);
513         CHECK_STATUS(status, NT_STATUS_OK);
514         h1 = io1.out.file.handle;
515         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
516         CHECK_VAL(io1.out.durable_open, true);
517         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
518
519         /* Disconnect after getting the batch */
520         talloc_free(tree1);
521         tree1 = NULL;
522
523         /*
524          * Windows7 (build 7000) will break a batch oplock immediately if the
525          * original client is gone. (ZML: This seems like a bug. It should give
526          * some time for the client to reconnect!)
527          */
528         status = smb2_create(tree2, mem_ctx, &io2);
529         CHECK_STATUS(status, NT_STATUS_OK);
530         h2 = io2.out.file.handle;
531         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
532         CHECK_VAL(io2.out.durable_open, true);
533         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
534
535         /* What if tree1 tries to come back and reclaim? */
536         if (!torture_smb2_connection(tctx, &tree1)) {
537                 torture_warning(tctx, "couldn't reconnect, bailing\n");
538                 ret = false;
539                 goto done;
540         }
541
542         ZERO_STRUCT(io1);
543         io1.in.fname = fname;
544         io1.in.durable_handle = &h1;
545
546         status = smb2_create(tree1, mem_ctx, &io1);
547         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
548
549  done:
550         smb2_util_close(tree2, h2);
551         smb2_util_unlink(tree2, fname);
552
553         talloc_free(tree1);
554         talloc_free(tree2);
555
556         return ret;
557 }
558
559 /*
560   Open, disconnect, lease break, reconnect.
561 */
562 bool test_durable_open_lease(struct torture_context *tctx,
563                              struct smb2_tree *tree1,
564                              struct smb2_tree *tree2)
565 {
566         TALLOC_CTX *mem_ctx = talloc_new(tctx);
567         struct smb2_create io1, io2;
568         struct smb2_lease ls1, ls2;
569         struct smb2_handle h1, h2;
570         NTSTATUS status;
571         char fname[256];
572         bool ret = true;
573         uint64_t lease1, lease2;
574
575         /*
576          * Choose a random name and random lease in case the state is left a
577          * little funky.
578          */
579         lease1 = random();
580         lease2 = random();
581         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
582
583         /* Clean slate */
584         smb2_util_unlink(tree1, fname);
585
586         /* Create with lease */
587         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
588                           lease1, smb2_util_lease_state("RHW"));
589         io1.in.durable_open = true;
590
591         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
592                           lease2, smb2_util_lease_state("RHW"));
593         io2.in.durable_open = true;
594         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
595
596         status = smb2_create(tree1, mem_ctx, &io1);
597         CHECK_STATUS(status, NT_STATUS_OK);
598         h1 = io1.out.file.handle;
599         CHECK_VAL(io1.out.durable_open, true);
600         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
601
602         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
603         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
604         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
605         CHECK_VAL(io1.out.lease_response.lease_state,
606             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
607
608         /* Disconnect after getting the lease */
609         talloc_free(tree1);
610         tree1 = NULL;
611
612         /*
613          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
614          * even if the original client is gone. (ZML: This seems like a bug. It
615          * should give some time for the client to reconnect! And why RH?)
616          * 
617          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
618          * Test is adapted accordingly.
619          */
620         status = smb2_create(tree2, mem_ctx, &io2);
621         CHECK_STATUS(status, NT_STATUS_OK);
622         h2 = io2.out.file.handle;
623         CHECK_VAL(io2.out.durable_open, true);
624         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
625
626         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
627         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
628         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
629         CHECK_VAL(io2.out.lease_response.lease_state,
630             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
631
632         /* What if tree1 tries to come back and reclaim? */
633         if (!torture_smb2_connection(tctx, &tree1)) {
634                 torture_warning(tctx, "couldn't reconnect, bailing\n");
635                 ret = false;
636                 goto done;
637         }
638
639         ZERO_STRUCT(io1);
640         io1.in.fname = fname;
641         io1.in.durable_handle = &h1;
642         io1.in.lease_request = &ls1;
643
644         status = smb2_create(tree1, mem_ctx, &io1);
645         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
646
647  done:
648         smb2_util_close(tree2, h2);
649         smb2_util_unlink(tree2, fname);
650
651         talloc_free(tree1);
652         talloc_free(tree2);
653
654         return ret;
655 }
656
657 /*
658   Open, take BRL, disconnect, reconnect.
659 */
660 bool test_durable_open_lock(struct torture_context *tctx,
661                             struct smb2_tree *tree)
662 {
663         TALLOC_CTX *mem_ctx = talloc_new(tctx);
664         struct smb2_create io;
665         struct smb2_lease ls;
666         struct smb2_handle h;
667         struct smb2_lock lck;
668         struct smb2_lock_element el[2];
669         NTSTATUS status;
670         char fname[256];
671         bool ret = true;
672         uint64_t lease;
673
674         /*
675          * Choose a random name and random lease in case the state is left a
676          * little funky.
677          */
678         lease = random();
679         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
680
681         /* Clean slate */
682         smb2_util_unlink(tree, fname);
683
684         /* Create with lease */
685
686         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
687                           smb2_util_lease_state("RWH"));
688         io.in.durable_open              = true;
689
690         status = smb2_create(tree, mem_ctx, &io);
691         CHECK_STATUS(status, NT_STATUS_OK);
692         h = io.out.file.handle;
693         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
694
695         CHECK_VAL(io.out.durable_open, true);
696         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
697         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
698         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
699         CHECK_VAL(io.out.lease_response.lease_state,
700             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
701
702         ZERO_STRUCT(lck);
703         ZERO_STRUCT(el);
704         lck.in.locks            = el;
705         lck.in.lock_count       = 0x0001;
706         lck.in.lock_sequence    = 0x00000000;
707         lck.in.file.handle      = h;
708         el[0].offset            = 0;
709         el[0].length            = 1;
710         el[0].reserved          = 0x00000000;
711         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
712         status = smb2_lock(tree, &lck);
713         CHECK_STATUS(status, NT_STATUS_OK);
714
715         /* Disconnect/Reconnect. */
716         talloc_free(tree);
717         tree = NULL;
718
719         if (!torture_smb2_connection(tctx, &tree)) {
720                 torture_warning(tctx, "couldn't reconnect, bailing\n");
721                 ret = false;
722                 goto done;
723         }
724
725         ZERO_STRUCT(io);
726         io.in.fname = fname;
727         io.in.durable_handle = &h;
728         io.in.lease_request = &ls;
729
730         status = smb2_create(tree, mem_ctx, &io);
731         CHECK_STATUS(status, NT_STATUS_OK);
732         h = io.out.file.handle;
733
734         lck.in.file.handle      = h;
735         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
736         status = smb2_lock(tree, &lck);
737         CHECK_STATUS(status, NT_STATUS_OK);
738
739  done:
740         smb2_util_close(tree, h);
741         smb2_util_unlink(tree, fname);
742         talloc_free(tree);
743
744         return ret;
745 }
746
747 /*
748   Open, disconnect, open in another tree, reconnect.
749
750   This test actually demonstrates a minimum level of respect for the durable
751   open in the face of another open. As long as this test shows an inability to
752   reconnect after an open, the oplock/lease tests above will certainly
753   demonstrate an error on reconnect.
754 */
755 bool test_durable_open_open(struct torture_context *tctx,
756                             struct smb2_tree *tree1,
757                             struct smb2_tree *tree2)
758 {
759         TALLOC_CTX *mem_ctx = talloc_new(tctx);
760         struct smb2_create io1, io2;
761         struct smb2_lease ls;
762         struct smb2_handle h1, h2;
763         NTSTATUS status;
764         char fname[256];
765         bool ret = true;
766         uint64_t lease;
767
768         /*
769          * Choose a random name and random lease in case the state is left a
770          * little funky.
771          */
772         lease = random();
773         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
774
775         /* Clean slate */
776         smb2_util_unlink(tree1, fname);
777
778         /* Create with lease */
779         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
780                                 smb2_util_share_access(""),
781                                 lease,
782                                 smb2_util_lease_state("RH"));
783         io1.in.durable_open = true;
784
785         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
786
787         status = smb2_create(tree1, mem_ctx, &io1);
788         CHECK_STATUS(status, NT_STATUS_OK);
789         h1 = io1.out.file.handle;
790         CHECK_VAL(io1.out.durable_open, true);
791         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
792
793         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
794         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
795         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
796         CHECK_VAL(io1.out.lease_response.lease_state,
797                   smb2_util_lease_state("RH"));
798
799         /* Disconnect */
800         talloc_free(tree1);
801         tree1 = NULL;
802
803         /* Open the file in tree2 */
804         status = smb2_create(tree2, mem_ctx, &io2);
805         CHECK_STATUS(status, NT_STATUS_OK);
806         h2 = io2.out.file.handle;
807         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
808
809         /* Reconnect */
810         if (!torture_smb2_connection(tctx, &tree1)) {
811                 torture_warning(tctx, "couldn't reconnect, bailing\n");
812                 ret = false;
813                 goto done;
814         }
815
816         ZERO_STRUCT(io1);
817         io1.in.fname = fname;
818         io1.in.durable_handle = &h1;
819         io1.in.lease_request = &ls;
820
821         /*
822          * Windows7 (build 7000) will give away an open immediately if the
823          * original client is gone. (ZML: This seems like a bug. It should give
824          * some time for the client to reconnect!)
825          */
826         status = smb2_create(tree1, mem_ctx, &io1);
827         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
828         h1 = io1.out.file.handle;
829
830  done:
831         smb2_util_close(tree2, h2);
832         smb2_util_unlink(tree2, fname);
833         smb2_util_close(tree1, h1);
834         smb2_util_unlink(tree1, fname);
835
836         talloc_free(tree1);
837         talloc_free(tree2);
838
839         return ret;
840 }
841
842 struct torture_suite *torture_smb2_durable_open_init(void)
843 {
844         struct torture_suite *suite =
845             torture_suite_create(talloc_autofree_context(), "durable-open");
846
847         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
848         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
849         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
850         torture_suite_add_2smb2_test(suite, "file-position",
851             test_durable_open_file_position);
852         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
853         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
854         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
855         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
856
857         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
858
859         return suite;
860 }