s4:torture:smb2: rewrite the durable-open.lease test to use smb2_lease_create()
[metze/samba/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 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         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
451         io1.in.durable_open = true;
452
453         io2 = io1;
454         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
455
456         status = smb2_create(tree1, mem_ctx, &io1);
457         CHECK_STATUS(status, NT_STATUS_OK);
458         h1 = io1.out.file.handle;
459         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
460         CHECK_VAL(io1.out.durable_open, true);
461         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
462
463         /* Disconnect after getting the batch */
464         talloc_free(tree1);
465         tree1 = NULL;
466
467         /*
468          * Windows7 (build 7000) will break a batch oplock immediately if the
469          * original client is gone. (ZML: This seems like a bug. It should give
470          * some time for the client to reconnect!)
471          */
472         status = smb2_create(tree2, mem_ctx, &io2);
473         CHECK_STATUS(status, NT_STATUS_OK);
474         h2 = io2.out.file.handle;
475         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
476         CHECK_VAL(io2.out.durable_open, true);
477         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
478
479         /* What if tree1 tries to come back and reclaim? */
480         if (!torture_smb2_connection(tctx, &tree1)) {
481                 torture_warning(tctx, "couldn't reconnect, bailing\n");
482                 ret = false;
483                 goto done;
484         }
485
486         ZERO_STRUCT(io1);
487         io1.in.fname = fname;
488         io1.in.durable_handle = &h1;
489
490         status = smb2_create(tree1, mem_ctx, &io1);
491         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
492
493  done:
494         smb2_util_close(tree2, h2);
495         smb2_util_unlink(tree2, fname);
496
497         talloc_free(tree1);
498         talloc_free(tree2);
499
500         return ret;
501 }
502
503 /*
504   Open, disconnect, lease break, reconnect.
505 */
506 bool test_durable_open_lease(struct torture_context *tctx,
507                              struct smb2_tree *tree1,
508                              struct smb2_tree *tree2)
509 {
510         TALLOC_CTX *mem_ctx = talloc_new(tctx);
511         struct smb2_create io1, io2;
512         struct smb2_lease ls1, ls2;
513         struct smb2_handle h1, h2;
514         NTSTATUS status;
515         char fname[256];
516         bool ret = true;
517         uint64_t lease1, lease2;
518
519         /*
520          * Choose a random name and random lease in case the state is left a
521          * little funky.
522          */
523         lease1 = random();
524         lease2 = random();
525         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
526
527         /* Clean slate */
528         smb2_util_unlink(tree1, fname);
529
530         /* Create with lease */
531         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
532                           lease1, smb2_util_lease_state("RHW"));
533         io1.in.durable_open = true;
534
535         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
536                           lease2, smb2_util_lease_state("RHW"));
537         io2.in.durable_open = true;
538         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
539
540         status = smb2_create(tree1, mem_ctx, &io1);
541         CHECK_STATUS(status, NT_STATUS_OK);
542         h1 = io1.out.file.handle;
543         CHECK_VAL(io1.out.durable_open, true);
544         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
545
546         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
547         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
548         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
549         CHECK_VAL(io1.out.lease_response.lease_state,
550             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
551
552         /* Disconnect after getting the lease */
553         talloc_free(tree1);
554         tree1 = NULL;
555
556         /*
557          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
558          * even if the original client is gone. (ZML: This seems like a bug. It
559          * should give some time for the client to reconnect! And why RH?)
560          * 
561          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
562          * Test is adapted accordingly.
563          */
564         status = smb2_create(tree2, mem_ctx, &io2);
565         CHECK_STATUS(status, NT_STATUS_OK);
566         h2 = io2.out.file.handle;
567         CHECK_VAL(io2.out.durable_open, true);
568         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
569
570         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
571         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
572         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
573         CHECK_VAL(io2.out.lease_response.lease_state,
574             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
575
576         /* What if tree1 tries to come back and reclaim? */
577         if (!torture_smb2_connection(tctx, &tree1)) {
578                 torture_warning(tctx, "couldn't reconnect, bailing\n");
579                 ret = false;
580                 goto done;
581         }
582
583         ZERO_STRUCT(io1);
584         io1.in.fname = fname;
585         io1.in.durable_handle = &h1;
586         io1.in.lease_request = &ls1;
587
588         status = smb2_create(tree1, mem_ctx, &io1);
589         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
590
591  done:
592         smb2_util_close(tree2, h2);
593         smb2_util_unlink(tree2, fname);
594
595         talloc_free(tree1);
596         talloc_free(tree2);
597
598         return ret;
599 }
600
601 /*
602   Open, take BRL, disconnect, reconnect.
603 */
604 bool test_durable_open_lock(struct torture_context *tctx,
605                             struct smb2_tree *tree)
606 {
607         TALLOC_CTX *mem_ctx = talloc_new(tctx);
608         struct smb2_create io;
609         struct smb2_lease ls;
610         struct smb2_handle h;
611         struct smb2_lock lck;
612         struct smb2_lock_element el[2];
613         NTSTATUS status;
614         char fname[256];
615         bool ret = true;
616         uint64_t lease;
617
618         /*
619          * Choose a random name and random lease in case the state is left a
620          * little funky.
621          */
622         lease = random();
623         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
624
625         /* Clean slate */
626         smb2_util_unlink(tree, fname);
627
628         /* Create with lease */
629         ZERO_STRUCT(io);
630         io.in.security_flags            = 0x00;
631         io.in.oplock_level              = SMB2_OPLOCK_LEVEL_LEASE;
632         io.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
633         io.in.create_flags              = 0x00000000;
634         io.in.reserved                  = 0x00000000;
635         io.in.desired_access            = SEC_RIGHTS_FILE_ALL;
636         io.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
637         io.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
638                                           NTCREATEX_SHARE_ACCESS_WRITE |
639                                           NTCREATEX_SHARE_ACCESS_DELETE;
640         io.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
641         io.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
642                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
643                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
644                                           0x00200000;
645         io.in.fname                     = fname;
646         io.in.durable_open              = true;
647
648         ZERO_STRUCT(ls);
649         ls.lease_key.data[0] = lease;
650         ls.lease_key.data[1] = ~lease;
651         ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
652         io.in.lease_request = &ls;
653
654         status = smb2_create(tree, mem_ctx, &io);
655         CHECK_STATUS(status, NT_STATUS_OK);
656         h = io.out.file.handle;
657         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
658
659         CHECK_VAL(io.out.durable_open, true);
660         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
661         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
662         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
663         CHECK_VAL(io.out.lease_response.lease_state,
664             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
665
666         ZERO_STRUCT(lck);
667         ZERO_STRUCT(el);
668         lck.in.locks            = el;
669         lck.in.lock_count       = 0x0001;
670         lck.in.lock_sequence    = 0x00000000;
671         lck.in.file.handle      = h;
672         el[0].offset            = 0;
673         el[0].length            = 1;
674         el[0].reserved          = 0x00000000;
675         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
676         status = smb2_lock(tree, &lck);
677         CHECK_STATUS(status, NT_STATUS_OK);
678
679         /* Disconnect/Reconnect. */
680         talloc_free(tree);
681         tree = NULL;
682
683         if (!torture_smb2_connection(tctx, &tree)) {
684                 torture_warning(tctx, "couldn't reconnect, bailing\n");
685                 ret = false;
686                 goto done;
687         }
688
689         ZERO_STRUCT(io);
690         io.in.fname = fname;
691         io.in.durable_handle = &h;
692         io.in.lease_request = &ls;
693
694         status = smb2_create(tree, mem_ctx, &io);
695         CHECK_STATUS(status, NT_STATUS_OK);
696         h = io.out.file.handle;
697
698         lck.in.file.handle      = h;
699         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
700         status = smb2_lock(tree, &lck);
701         CHECK_STATUS(status, NT_STATUS_OK);
702
703  done:
704         smb2_util_close(tree, h);
705         smb2_util_unlink(tree, fname);
706         talloc_free(tree);
707
708         return ret;
709 }
710
711 /*
712   Open, disconnect, open in another tree, reconnect.
713
714   This test actually demonstrates a minimum level of respect for the durable
715   open in the face of another open. As long as this test shows an inability to
716   reconnect after an open, the oplock/lease tests above will certainly
717   demonstrate an error on reconnect.
718 */
719 bool test_durable_open_open(struct torture_context *tctx,
720                             struct smb2_tree *tree1,
721                             struct smb2_tree *tree2)
722 {
723         TALLOC_CTX *mem_ctx = talloc_new(tctx);
724         struct smb2_create io1, io2;
725         struct smb2_lease ls;
726         struct smb2_handle h1, h2;
727         NTSTATUS status;
728         char fname[256];
729         bool ret = true;
730         uint64_t lease;
731
732         /*
733          * Choose a random name and random lease in case the state is left a
734          * little funky.
735          */
736         lease = random();
737         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
738
739         /* Clean slate */
740         smb2_util_unlink(tree1, fname);
741
742         /* Create with lease */
743         ZERO_STRUCT(io1);
744         io1.in.security_flags           = 0x00;
745         io1.in.oplock_level             = SMB2_OPLOCK_LEVEL_LEASE;
746         io1.in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
747         io1.in.create_flags             = 0x00000000;
748         io1.in.reserved                 = 0x00000000;
749         io1.in.desired_access           = SEC_RIGHTS_FILE_ALL;
750         io1.in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
751         io1.in.share_access             = NTCREATEX_SHARE_ACCESS_NONE;
752         io1.in.create_disposition       = NTCREATEX_DISP_OPEN_IF;
753         io1.in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
754                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
755                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
756                                           0x00200000;
757         io1.in.fname                    = fname;
758         io1.in.durable_open             = true;
759
760         io2 = io1;
761         io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
762         io2.in.durable_open = false;
763
764         ZERO_STRUCT(ls);
765         ls.lease_key.data[0] = lease;
766         ls.lease_key.data[1] = ~lease;
767         ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
768         io1.in.lease_request = &ls;
769
770         status = smb2_create(tree1, mem_ctx, &io1);
771         CHECK_STATUS(status, NT_STATUS_OK);
772         h1 = io1.out.file.handle;
773         CHECK_VAL(io1.out.durable_open, true);
774         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
775
776         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
777         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
778         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
779         CHECK_VAL(io1.out.lease_response.lease_state,
780             SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
781
782         /* Disconnect */
783         talloc_free(tree1);
784         tree1 = NULL;
785
786         /* Open the file in tree2 */
787         status = smb2_create(tree2, mem_ctx, &io2);
788         CHECK_STATUS(status, NT_STATUS_OK);
789         h2 = io2.out.file.handle;
790         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
791
792         /* Reconnect */
793         if (!torture_smb2_connection(tctx, &tree1)) {
794                 torture_warning(tctx, "couldn't reconnect, bailing\n");
795                 ret = false;
796                 goto done;
797         }
798
799         ZERO_STRUCT(io1);
800         io1.in.fname = fname;
801         io1.in.durable_handle = &h1;
802         io1.in.lease_request = &ls;
803
804         /*
805          * Windows7 (build 7000) will give away an open immediately if the
806          * original client is gone. (ZML: This seems like a bug. It should give
807          * some time for the client to reconnect!)
808          */
809         status = smb2_create(tree1, mem_ctx, &io1);
810         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
811         h1 = io1.out.file.handle;
812
813  done:
814         smb2_util_close(tree2, h2);
815         smb2_util_unlink(tree2, fname);
816         smb2_util_close(tree1, h1);
817         smb2_util_unlink(tree1, fname);
818
819         talloc_free(tree1);
820         talloc_free(tree2);
821
822         return ret;
823 }
824
825 struct torture_suite *torture_smb2_durable_open_init(void)
826 {
827         struct torture_suite *suite =
828             torture_suite_create(talloc_autofree_context(), "durable-open");
829
830         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
831         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
832         torture_suite_add_2smb2_test(suite, "file-position",
833             test_durable_open_file_position);
834         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
835         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
836         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
837         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
838
839         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
840
841         return suite;
842 }