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