s4:torture: add a smb2.durable_open.reopen4 test
[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 test for doing a durable open
324  * and do a durable reopen on the same connection
325  */
326 bool test_durable_open_reopen1(struct torture_context *tctx,
327                                struct smb2_tree *tree)
328 {
329         NTSTATUS status;
330         TALLOC_CTX *mem_ctx = talloc_new(tctx);
331         char fname[256];
332         struct smb2_handle _h;
333         struct smb2_handle *h = NULL;
334         struct smb2_create io1, io2;
335         bool ret = true;
336
337         /* Choose a random name in case the state is left a little funky. */
338         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
339                  generate_random_str(tctx, 8));
340
341         smb2_util_unlink(tree, fname);
342
343         smb2_oplock_create_share(&io1, fname,
344                                  smb2_util_share_access(""),
345                                  smb2_util_oplock_level("b"));
346         io1.in.durable_open = true;
347
348         status = smb2_create(tree, mem_ctx, &io1);
349         CHECK_STATUS(status, NT_STATUS_OK);
350         _h = io1.out.file.handle;
351         h = &_h;
352         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
353         CHECK_VAL(io1.out.durable_open, true);
354         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
355
356         /* try a durable reconnect while the file is still open */
357         ZERO_STRUCT(io2);
358         io2.in.fname = fname;
359         io2.in.durable_handle = h;
360
361         status = smb2_create(tree, mem_ctx, &io2);
362         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
363
364 done:
365         if (h != NULL) {
366                 smb2_util_close(tree, *h);
367         }
368
369         smb2_util_unlink(tree, fname);
370
371         talloc_free(tree);
372
373         talloc_free(mem_ctx);
374
375         return ret;
376 }
377
378 /**
379  * basic test for doing a durable open
380  * tcp disconnect, reconnect, do a durable reopen (succeeds)
381  */
382 bool test_durable_open_reopen2(struct torture_context *tctx,
383                                struct smb2_tree *tree)
384 {
385         NTSTATUS status;
386         TALLOC_CTX *mem_ctx = talloc_new(tctx);
387         char fname[256];
388         struct smb2_handle _h;
389         struct smb2_handle *h = NULL;
390         struct smb2_create io1, io2;
391         bool ret = true;
392
393         /* Choose a random name in case the state is left a little funky. */
394         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
395                  generate_random_str(tctx, 8));
396
397         smb2_util_unlink(tree, fname);
398
399         smb2_oplock_create_share(&io1, fname,
400                                  smb2_util_share_access(""),
401                                  smb2_util_oplock_level("b"));
402         io1.in.durable_open = true;
403
404         status = smb2_create(tree, mem_ctx, &io1);
405         CHECK_STATUS(status, NT_STATUS_OK);
406         _h = io1.out.file.handle;
407         h = &_h;
408         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
409         CHECK_VAL(io1.out.durable_open, true);
410         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
411
412         /* disconnect, reconnect and then do durable reopen */
413         talloc_free(tree);
414         tree = NULL;
415
416         if (!torture_smb2_connection(tctx, &tree)) {
417                 torture_warning(tctx, "couldn't reconnect, bailing\n");
418                 ret = false;
419                 goto done;
420         }
421
422         ZERO_STRUCT(io2);
423         io2.in.fname = fname;
424         io2.in.durable_handle = h;
425
426         status = smb2_create(tree, mem_ctx, &io2);
427         CHECK_STATUS(status, NT_STATUS_OK);
428         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
429         CHECK_VAL(io2.out.durable_open, true);
430         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
431         _h = io2.out.file.handle;
432         h = &_h;
433
434 done:
435         if (h != NULL) {
436                 smb2_util_close(tree, *h);
437         }
438
439         smb2_util_unlink(tree, fname);
440
441         talloc_free(tree);
442
443         talloc_free(mem_ctx);
444
445         return ret;
446 }
447
448 /**
449  * basic test for doing a durable open:
450  * tdis, new tcon, try durable reopen (fails)
451  */
452 bool test_durable_open_reopen3(struct torture_context *tctx,
453                                struct smb2_tree *tree)
454 {
455         NTSTATUS status;
456         TALLOC_CTX *mem_ctx = talloc_new(tctx);
457         char fname[256];
458         struct smb2_handle _h;
459         struct smb2_handle *h = NULL;
460         struct smb2_create io1, io2;
461         bool ret = true;
462         struct smb2_tree *tree2;
463
464         /* Choose a random name in case the state is left a little funky. */
465         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
466                  generate_random_str(tctx, 8));
467
468         smb2_util_unlink(tree, fname);
469
470         smb2_oplock_create_share(&io1, fname,
471                                  smb2_util_share_access(""),
472                                  smb2_util_oplock_level("b"));
473         io1.in.durable_open = true;
474
475         status = smb2_create(tree, mem_ctx, &io1);
476         CHECK_STATUS(status, NT_STATUS_OK);
477         _h = io1.out.file.handle;
478         h = &_h;
479         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
480         CHECK_VAL(io1.out.durable_open, true);
481         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
482
483         /* disconnect, reconnect and then do durable reopen */
484         status = smb2_tdis(tree);
485         CHECK_STATUS(status, NT_STATUS_OK);
486
487         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
488                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
489                 ret = false;
490                 goto done;
491         }
492
493
494         ZERO_STRUCT(io2);
495         io2.in.fname = fname;
496         io2.in.durable_handle = h;
497
498         status = smb2_create(tree2, mem_ctx, &io2);
499         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
500
501 done:
502         if (h != NULL) {
503                 smb2_util_close(tree, *h);
504         }
505
506         smb2_util_unlink(tree2, fname);
507
508         talloc_free(tree);
509
510         talloc_free(mem_ctx);
511
512         return ret;
513 }
514
515 /**
516  * basic test for doing a durable open:
517  * logoff, create a new session, do a durable reopen (succeeds)
518  */
519 bool test_durable_open_reopen4(struct torture_context *tctx,
520                                struct smb2_tree *tree)
521 {
522         NTSTATUS status;
523         TALLOC_CTX *mem_ctx = talloc_new(tctx);
524         char fname[256];
525         struct smb2_handle _h;
526         struct smb2_handle *h = NULL;
527         struct smb2_create io1, io2;
528         bool ret = true;
529         struct smb2_transport *transport;
530         struct smb2_tree *tree2;
531
532         /* Choose a random name in case the state is left a little funky. */
533         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
534                  generate_random_str(tctx, 8));
535
536         smb2_util_unlink(tree, fname);
537
538         smb2_oplock_create_share(&io1, fname,
539                                  smb2_util_share_access(""),
540                                  smb2_util_oplock_level("b"));
541         io1.in.durable_open = true;
542         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
543
544         status = smb2_create(tree, mem_ctx, &io1);
545         CHECK_STATUS(status, NT_STATUS_OK);
546         _h = io1.out.file.handle;
547         h = &_h;
548         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
549         CHECK_VAL(io1.out.durable_open, true);
550         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
551
552         /* disconnect, reconnect and then do durable reopen */
553         transport = tree->session->transport;
554         status = smb2_logoff(tree->session);
555         CHECK_STATUS(status, NT_STATUS_OK);
556
557         if (!torture_smb2_session_setup(tctx, transport, mem_ctx, &tree->session)) {
558                 torture_warning(tctx, "session setup failed.\n");
559                 ret = false;
560                 goto done;
561         }
562
563         ZERO_STRUCT(io2);
564         io2.in.fname = fname;
565         io2.in.durable_handle = h;
566
567         status = smb2_create(tree, mem_ctx, &io2);
568         CHECK_STATUS(status, NT_STATUS_NETWORK_NAME_DELETED);
569
570         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
571                 torture_warning(tctx, "tree connect failed.\n");
572                 ret = false;
573                 goto done;
574         }
575
576         ZERO_STRUCT(io2);
577         io2.in.fname = fname;
578         io2.in.durable_handle = h;
579
580         status = smb2_create(tree2, mem_ctx, &io2);
581         CHECK_STATUS(status, NT_STATUS_OK);
582
583         _h = io2.out.file.handle;
584         h = &_h;
585         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
586         CHECK_VAL(io2.out.durable_open, true);
587         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
588
589 done:
590         if (h != NULL) {
591                 smb2_util_close(tree, *h);
592         }
593
594         smb2_util_unlink(tree2, fname);
595
596         talloc_free(tree);
597
598         talloc_free(mem_ctx);
599
600         return ret;
601 }
602
603 /*
604    basic testing of SMB2 durable opens
605    regarding the position information on the handle
606 */
607 bool test_durable_open_file_position(struct torture_context *tctx,
608                                      struct smb2_tree *tree1,
609                                      struct smb2_tree *tree2)
610 {
611         TALLOC_CTX *mem_ctx = talloc_new(tctx);
612         struct smb2_handle h1, h2;
613         struct smb2_create io1, io2;
614         NTSTATUS status;
615         const char *fname = "durable_open_position.dat";
616         union smb_fileinfo qfinfo;
617         union smb_setfileinfo sfinfo;
618         bool ret = true;
619         uint64_t pos;
620
621         smb2_util_unlink(tree1, fname);
622
623         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
624         io1.in.durable_open = true;
625
626         status = smb2_create(tree1, mem_ctx, &io1);
627         CHECK_STATUS(status, NT_STATUS_OK);
628         h1 = io1.out.file.handle;
629         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
630         CHECK_VAL(io1.out.durable_open, true);
631         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
632
633         /* TODO: check extra blob content */
634
635         ZERO_STRUCT(qfinfo);
636         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
637         qfinfo.generic.in.file.handle = h1;
638         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
639         CHECK_STATUS(status, NT_STATUS_OK);
640         CHECK_VAL(qfinfo.position_information.out.position, 0);
641         pos = qfinfo.position_information.out.position;
642         torture_comment(tctx, "position: %llu\n",
643                         (unsigned long long)pos);
644
645         ZERO_STRUCT(sfinfo);
646         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
647         sfinfo.generic.in.file.handle = h1;
648         sfinfo.position_information.in.position = 0x1000;
649         status = smb2_setinfo_file(tree1, &sfinfo);
650         CHECK_STATUS(status, NT_STATUS_OK);
651
652         ZERO_STRUCT(qfinfo);
653         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
654         qfinfo.generic.in.file.handle = h1;
655         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
656         CHECK_STATUS(status, NT_STATUS_OK);
657         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
658         pos = qfinfo.position_information.out.position;
659         torture_comment(tctx, "position: %llu\n",
660                         (unsigned long long)pos);
661
662         talloc_free(tree1);
663         tree1 = NULL;
664
665         ZERO_STRUCT(qfinfo);
666         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
667         qfinfo.generic.in.file.handle = h1;
668         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
669         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
670
671         ZERO_STRUCT(io2);
672         io2.in.fname = fname;
673         io2.in.durable_handle = &h1;
674
675         status = smb2_create(tree2, mem_ctx, &io2);
676         CHECK_STATUS(status, NT_STATUS_OK);
677         CHECK_VAL(io2.out.durable_open, true);
678         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
679         CHECK_VAL(io2.out.reserved, 0x00);
680         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
681         CHECK_VAL(io2.out.alloc_size, 0);
682         CHECK_VAL(io2.out.size, 0);
683         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
684         CHECK_VAL(io2.out.reserved2, 0);
685
686         h2 = io2.out.file.handle;
687
688         ZERO_STRUCT(qfinfo);
689         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
690         qfinfo.generic.in.file.handle = h2;
691         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
692         CHECK_STATUS(status, NT_STATUS_OK);
693         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
694         pos = qfinfo.position_information.out.position;
695         torture_comment(tctx, "position: %llu\n",
696                         (unsigned long long)pos);
697
698         smb2_util_close(tree2, h2);
699
700         talloc_free(mem_ctx);
701
702         smb2_util_unlink(tree2, fname);
703 done:
704         talloc_free(tree1);
705         talloc_free(tree2);
706
707         return ret;
708 }
709
710 /*
711   Open, disconnect, oplock break, reconnect.
712 */
713 bool test_durable_open_oplock(struct torture_context *tctx,
714                               struct smb2_tree *tree1,
715                               struct smb2_tree *tree2)
716 {
717         TALLOC_CTX *mem_ctx = talloc_new(tctx);
718         struct smb2_create io1, io2;
719         struct smb2_handle h1, h2;
720         NTSTATUS status;
721         char fname[256];
722         bool ret = true;
723
724         /* Choose a random name in case the state is left a little funky. */
725         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
726
727         /* Clean slate */
728         smb2_util_unlink(tree1, fname);
729
730         /* Create with batch oplock */
731         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
732         io1.in.durable_open = true;
733
734         io2 = io1;
735         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
736
737         status = smb2_create(tree1, mem_ctx, &io1);
738         CHECK_STATUS(status, NT_STATUS_OK);
739         h1 = io1.out.file.handle;
740         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
741         CHECK_VAL(io1.out.durable_open, true);
742         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
743
744         /* Disconnect after getting the batch */
745         talloc_free(tree1);
746         tree1 = NULL;
747
748         /*
749          * Windows7 (build 7000) will break a batch oplock immediately if the
750          * original client is gone. (ZML: This seems like a bug. It should give
751          * some time for the client to reconnect!)
752          */
753         status = smb2_create(tree2, mem_ctx, &io2);
754         CHECK_STATUS(status, NT_STATUS_OK);
755         h2 = io2.out.file.handle;
756         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
757         CHECK_VAL(io2.out.durable_open, true);
758         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
759
760         /* What if tree1 tries to come back and reclaim? */
761         if (!torture_smb2_connection(tctx, &tree1)) {
762                 torture_warning(tctx, "couldn't reconnect, bailing\n");
763                 ret = false;
764                 goto done;
765         }
766
767         ZERO_STRUCT(io1);
768         io1.in.fname = fname;
769         io1.in.durable_handle = &h1;
770
771         status = smb2_create(tree1, mem_ctx, &io1);
772         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
773
774  done:
775         smb2_util_close(tree2, h2);
776         smb2_util_unlink(tree2, fname);
777
778         talloc_free(tree1);
779         talloc_free(tree2);
780
781         return ret;
782 }
783
784 /*
785   Open, disconnect, lease break, reconnect.
786 */
787 bool test_durable_open_lease(struct torture_context *tctx,
788                              struct smb2_tree *tree1,
789                              struct smb2_tree *tree2)
790 {
791         TALLOC_CTX *mem_ctx = talloc_new(tctx);
792         struct smb2_create io1, io2;
793         struct smb2_lease ls1, ls2;
794         struct smb2_handle h1, h2;
795         NTSTATUS status;
796         char fname[256];
797         bool ret = true;
798         uint64_t lease1, lease2;
799
800         /*
801          * Choose a random name and random lease in case the state is left a
802          * little funky.
803          */
804         lease1 = random();
805         lease2 = random();
806         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
807
808         /* Clean slate */
809         smb2_util_unlink(tree1, fname);
810
811         /* Create with lease */
812         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
813                           lease1, smb2_util_lease_state("RHW"));
814         io1.in.durable_open = true;
815
816         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
817                           lease2, smb2_util_lease_state("RHW"));
818         io2.in.durable_open = true;
819         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
820
821         status = smb2_create(tree1, mem_ctx, &io1);
822         CHECK_STATUS(status, NT_STATUS_OK);
823         h1 = io1.out.file.handle;
824         CHECK_VAL(io1.out.durable_open, true);
825         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
826
827         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
828         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
829         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
830         CHECK_VAL(io1.out.lease_response.lease_state,
831             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
832
833         /* Disconnect after getting the lease */
834         talloc_free(tree1);
835         tree1 = NULL;
836
837         /*
838          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
839          * even if the original client is gone. (ZML: This seems like a bug. It
840          * should give some time for the client to reconnect! And why RH?)
841          * 
842          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
843          * Test is adapted accordingly.
844          */
845         status = smb2_create(tree2, mem_ctx, &io2);
846         CHECK_STATUS(status, NT_STATUS_OK);
847         h2 = io2.out.file.handle;
848         CHECK_VAL(io2.out.durable_open, true);
849         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
850
851         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
852         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
853         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
854         CHECK_VAL(io2.out.lease_response.lease_state,
855             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
856
857         /* What if tree1 tries to come back and reclaim? */
858         if (!torture_smb2_connection(tctx, &tree1)) {
859                 torture_warning(tctx, "couldn't reconnect, bailing\n");
860                 ret = false;
861                 goto done;
862         }
863
864         ZERO_STRUCT(io1);
865         io1.in.fname = fname;
866         io1.in.durable_handle = &h1;
867         io1.in.lease_request = &ls1;
868
869         status = smb2_create(tree1, mem_ctx, &io1);
870         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
871
872  done:
873         smb2_util_close(tree2, h2);
874         smb2_util_unlink(tree2, fname);
875
876         talloc_free(tree1);
877         talloc_free(tree2);
878
879         return ret;
880 }
881
882 /*
883   Open, take BRL, disconnect, reconnect.
884 */
885 bool test_durable_open_lock(struct torture_context *tctx,
886                             struct smb2_tree *tree)
887 {
888         TALLOC_CTX *mem_ctx = talloc_new(tctx);
889         struct smb2_create io;
890         struct smb2_lease ls;
891         struct smb2_handle h;
892         struct smb2_lock lck;
893         struct smb2_lock_element el[2];
894         NTSTATUS status;
895         char fname[256];
896         bool ret = true;
897         uint64_t lease;
898
899         /*
900          * Choose a random name and random lease in case the state is left a
901          * little funky.
902          */
903         lease = random();
904         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
905
906         /* Clean slate */
907         smb2_util_unlink(tree, fname);
908
909         /* Create with lease */
910
911         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
912                           smb2_util_lease_state("RWH"));
913         io.in.durable_open              = true;
914
915         status = smb2_create(tree, mem_ctx, &io);
916         CHECK_STATUS(status, NT_STATUS_OK);
917         h = io.out.file.handle;
918         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
919
920         CHECK_VAL(io.out.durable_open, true);
921         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
922         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
923         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
924         CHECK_VAL(io.out.lease_response.lease_state,
925             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
926
927         ZERO_STRUCT(lck);
928         ZERO_STRUCT(el);
929         lck.in.locks            = el;
930         lck.in.lock_count       = 0x0001;
931         lck.in.lock_sequence    = 0x00000000;
932         lck.in.file.handle      = h;
933         el[0].offset            = 0;
934         el[0].length            = 1;
935         el[0].reserved          = 0x00000000;
936         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
937         status = smb2_lock(tree, &lck);
938         CHECK_STATUS(status, NT_STATUS_OK);
939
940         /* Disconnect/Reconnect. */
941         talloc_free(tree);
942         tree = NULL;
943
944         if (!torture_smb2_connection(tctx, &tree)) {
945                 torture_warning(tctx, "couldn't reconnect, bailing\n");
946                 ret = false;
947                 goto done;
948         }
949
950         ZERO_STRUCT(io);
951         io.in.fname = fname;
952         io.in.durable_handle = &h;
953         io.in.lease_request = &ls;
954
955         status = smb2_create(tree, mem_ctx, &io);
956         CHECK_STATUS(status, NT_STATUS_OK);
957         h = io.out.file.handle;
958
959         lck.in.file.handle      = h;
960         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
961         status = smb2_lock(tree, &lck);
962         CHECK_STATUS(status, NT_STATUS_OK);
963
964  done:
965         smb2_util_close(tree, h);
966         smb2_util_unlink(tree, fname);
967         talloc_free(tree);
968
969         return ret;
970 }
971
972 /*
973   Open, disconnect, open in another tree, reconnect.
974
975   This test actually demonstrates a minimum level of respect for the durable
976   open in the face of another open. As long as this test shows an inability to
977   reconnect after an open, the oplock/lease tests above will certainly
978   demonstrate an error on reconnect.
979 */
980 bool test_durable_open_open(struct torture_context *tctx,
981                             struct smb2_tree *tree1,
982                             struct smb2_tree *tree2)
983 {
984         TALLOC_CTX *mem_ctx = talloc_new(tctx);
985         struct smb2_create io1, io2;
986         struct smb2_lease ls;
987         struct smb2_handle h1, h2;
988         NTSTATUS status;
989         char fname[256];
990         bool ret = true;
991         uint64_t lease;
992
993         /*
994          * Choose a random name and random lease in case the state is left a
995          * little funky.
996          */
997         lease = random();
998         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
999
1000         /* Clean slate */
1001         smb2_util_unlink(tree1, fname);
1002
1003         /* Create with lease */
1004         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1005                                 smb2_util_share_access(""),
1006                                 lease,
1007                                 smb2_util_lease_state("RH"));
1008         io1.in.durable_open = true;
1009
1010         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1011
1012         status = smb2_create(tree1, mem_ctx, &io1);
1013         CHECK_STATUS(status, NT_STATUS_OK);
1014         h1 = io1.out.file.handle;
1015         CHECK_VAL(io1.out.durable_open, true);
1016         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1017
1018         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1019         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1020         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1021         CHECK_VAL(io1.out.lease_response.lease_state,
1022                   smb2_util_lease_state("RH"));
1023
1024         /* Disconnect */
1025         talloc_free(tree1);
1026         tree1 = NULL;
1027
1028         /* Open the file in tree2 */
1029         status = smb2_create(tree2, mem_ctx, &io2);
1030         CHECK_STATUS(status, NT_STATUS_OK);
1031         h2 = io2.out.file.handle;
1032         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1033
1034         /* Reconnect */
1035         if (!torture_smb2_connection(tctx, &tree1)) {
1036                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1037                 ret = false;
1038                 goto done;
1039         }
1040
1041         ZERO_STRUCT(io1);
1042         io1.in.fname = fname;
1043         io1.in.durable_handle = &h1;
1044         io1.in.lease_request = &ls;
1045
1046         /*
1047          * Windows7 (build 7000) will give away an open immediately if the
1048          * original client is gone. (ZML: This seems like a bug. It should give
1049          * some time for the client to reconnect!)
1050          */
1051         status = smb2_create(tree1, mem_ctx, &io1);
1052         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1053         h1 = io1.out.file.handle;
1054
1055  done:
1056         smb2_util_close(tree2, h2);
1057         smb2_util_unlink(tree2, fname);
1058         smb2_util_close(tree1, h1);
1059         smb2_util_unlink(tree1, fname);
1060
1061         talloc_free(tree1);
1062         talloc_free(tree2);
1063
1064         return ret;
1065 }
1066
1067 struct torture_suite *torture_smb2_durable_open_init(void)
1068 {
1069         struct torture_suite *suite =
1070             torture_suite_create(talloc_autofree_context(), "durable-open");
1071
1072         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1073         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1074         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1075         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1076         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1077         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1078         torture_suite_add_2smb2_test(suite, "file-position",
1079             test_durable_open_file_position);
1080         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1081         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1082         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1083         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
1084
1085         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1086
1087         return suite;
1088 }