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