s4:torture:smb2:durable-open: skip the open-with-lease test on servers without lease...
[ddiss/samba.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
29
30 #define CHECK_VAL(v, correct) do { \
31         if ((v) != (correct)) { \
32                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33                                 __location__, #v, (int)v, (int)correct); \
34                 ret = false; \
35         }} while (0)
36
37 #define CHECK_STATUS(status, correct) do { \
38         if (!NT_STATUS_EQUAL(status, correct)) { \
39                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40                        nt_errstr(status), nt_errstr(correct)); \
41                 ret = false; \
42                 goto done; \
43         }} while (0)
44
45 #define CHECK_CREATED(__io, __created, __attribute)                     \
46         do {                                                            \
47                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
49                 CHECK_VAL((__io)->out.size, 0);                         \
50                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
51                 CHECK_VAL((__io)->out.reserved2, 0);                    \
52         } while(0)
53
54
55 /**
56  * basic durable_open test.
57  * durable state should only be granted when requested
58  * along with a batch oplock or a handle lease.
59  *
60  * This test tests durable open with all possible oplock types.
61  */
62
63 struct durable_open_vs_oplock {
64         const char *level;
65         const char *share_mode;
66         bool expected;
67 };
68
69 #define NUM_OPLOCK_TYPES 4
70 #define NUM_SHARE_MODES 8
71 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
72 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
73 {
74         { "", "", false },
75         { "", "R", false },
76         { "", "W", false },
77         { "", "D", false },
78         { "", "RD", false },
79         { "", "RW", false },
80         { "", "WD", false },
81         { "", "RWD", false },
82
83         { "s", "", false },
84         { "s", "R", false },
85         { "s", "W", false },
86         { "s", "D", false },
87         { "s", "RD", false },
88         { "s", "RW", false },
89         { "s", "WD", false },
90         { "s", "RWD", false },
91
92         { "x", "", false },
93         { "x", "R", false },
94         { "x", "W", false },
95         { "x", "D", false },
96         { "x", "RD", false },
97         { "x", "RW", false },
98         { "x", "WD", false },
99         { "x", "RWD", false },
100
101         { "b", "", true },
102         { "b", "R", true },
103         { "b", "W", true },
104         { "b", "D", true },
105         { "b", "RD", true },
106         { "b", "RW", true },
107         { "b", "WD", true },
108         { "b", "RWD", true },
109 };
110
111 static bool test_one_durable_open_open1(struct torture_context *tctx,
112                                         struct smb2_tree *tree,
113                                         const char *fname,
114                                         struct durable_open_vs_oplock test)
115 {
116         NTSTATUS status;
117         TALLOC_CTX *mem_ctx = talloc_new(tctx);
118         struct smb2_handle _h;
119         struct smb2_handle *h = NULL;
120         bool ret = true;
121         struct smb2_create io;
122
123         smb2_util_unlink(tree, fname);
124
125         smb2_oplock_create_share(&io, fname,
126                                  smb2_util_share_access(test.share_mode),
127                                  smb2_util_oplock_level(test.level));
128         io.in.durable_open = true;
129
130         status = smb2_create(tree, mem_ctx, &io);
131         CHECK_STATUS(status, NT_STATUS_OK);
132         _h = io.out.file.handle;
133         h = &_h;
134         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
135         CHECK_VAL(io.out.durable_open, test.expected);
136         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
137
138 done:
139         if (h != NULL) {
140                 smb2_util_close(tree, *h);
141         }
142         smb2_util_unlink(tree, fname);
143         talloc_free(mem_ctx);
144
145         return ret;
146 }
147
148 bool test_durable_open_open1(struct torture_context *tctx,
149                              struct smb2_tree *tree)
150 {
151         TALLOC_CTX *mem_ctx = talloc_new(tctx);
152         char fname[256];
153         bool ret = true;
154         int i;
155
156         /* Choose a random name in case the state is left a little funky. */
157         snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
158
159         smb2_util_unlink(tree, fname);
160
161         /* test various oplock levels with durable open */
162
163         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
164                 ret = test_one_durable_open_open1(tctx,
165                                                   tree,
166                                                   fname,
167                                                   durable_open_vs_oplock_table[i]);
168                 if (ret == false) {
169                         goto done;
170                 }
171         }
172
173 done:
174         smb2_util_unlink(tree, fname);
175         talloc_free(tree);
176         talloc_free(mem_ctx);
177
178         return ret;
179 }
180
181 /**
182  * basic durable_open test.
183  * durable state should only be granted when requested
184  * along with a batch oplock or a handle lease.
185  *
186  * This test tests durable open with all valid lease types.
187  */
188
189 struct durable_open_vs_lease {
190         const char *type;
191         const char *share_mode;
192         bool expected;
193 };
194
195 #define NUM_LEASE_TYPES 5
196 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
197 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
198 {
199         { "", "", false },
200         { "", "R", false },
201         { "", "W", false },
202         { "", "D", false },
203         { "", "RW", false },
204         { "", "RD", false },
205         { "", "WD", false },
206         { "", "RWD", false },
207
208         { "R", "", false },
209         { "R", "R", false },
210         { "R", "W", false },
211         { "R", "D", false },
212         { "R", "RW", false },
213         { "R", "RD", false },
214         { "R", "DW", false },
215         { "R", "RWD", false },
216
217         { "RW", "", false },
218         { "RW", "R", false },
219         { "RW", "W", false },
220         { "RW", "D", false },
221         { "RW", "RW", false },
222         { "RW", "RD", false },
223         { "RW", "WD", false },
224         { "RW", "RWD", false },
225
226         { "RH", "", true },
227         { "RH", "R", true },
228         { "RH", "W", true },
229         { "RH", "D", true },
230         { "RH", "RW", true },
231         { "RH", "RD", true },
232         { "RH", "WD", true },
233         { "RH", "RWD", true },
234
235         { "RHW", "", true },
236         { "RHW", "R", true },
237         { "RHW", "W", true },
238         { "RHW", "D", true },
239         { "RHW", "RW", true },
240         { "RHW", "RD", true },
241         { "RHW", "WD", true },
242         { "RHW", "RWD", true },
243 };
244
245 static bool test_one_durable_open_open2(struct torture_context *tctx,
246                                         struct smb2_tree *tree,
247                                         const char *fname,
248                                         struct durable_open_vs_lease test)
249 {
250         NTSTATUS status;
251         TALLOC_CTX *mem_ctx = talloc_new(tctx);
252         struct smb2_handle _h;
253         struct smb2_handle *h = NULL;
254         bool ret = true;
255         struct smb2_create io;
256         struct smb2_lease ls;
257         uint64_t lease;
258         uint32_t caps;
259
260         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
261         if (!(caps & SMB2_CAP_LEASING)) {
262                 torture_skip(tctx, "leases are not supported");
263         }
264
265         smb2_util_unlink(tree, fname);
266
267         lease = random();
268
269         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
270                                 smb2_util_share_access(test.share_mode),
271                                 lease,
272                                 smb2_util_lease_state(test.type));
273         io.in.durable_open = true;
274
275         status = smb2_create(tree, mem_ctx, &io);
276         CHECK_STATUS(status, NT_STATUS_OK);
277         _h = io.out.file.handle;
278         h = &_h;
279         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
280         CHECK_VAL(io.out.durable_open, test.expected);
281         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
282         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
283         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
284         CHECK_VAL(io.out.lease_response.lease_state,
285                   smb2_util_lease_state(test.type));
286 done:
287         if (h != NULL) {
288                 smb2_util_close(tree, *h);
289         }
290         smb2_util_unlink(tree, fname);
291         talloc_free(mem_ctx);
292
293         return ret;
294 }
295
296 bool test_durable_open_open2(struct torture_context *tctx,
297                              struct smb2_tree *tree)
298 {
299         TALLOC_CTX *mem_ctx = talloc_new(tctx);
300         char fname[256];
301         bool ret = true;
302         int i;
303         uint32_t caps;
304
305         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
306         if (!(caps & SMB2_CAP_LEASING)) {
307                 torture_skip(tctx, "leases are not supported");
308         }
309
310         /* Choose a random name in case the state is left a little funky. */
311         snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
312
313         smb2_util_unlink(tree, fname);
314
315
316         /* test various oplock levels with durable open */
317
318         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
319                 ret = test_one_durable_open_open2(tctx,
320                                                   tree,
321                                                   fname,
322                                                   durable_open_vs_lease_table[i]);
323                 if (ret == false) {
324                         goto done;
325                 }
326         }
327
328 done:
329         smb2_util_unlink(tree, fname);
330         talloc_free(tree);
331         talloc_free(mem_ctx);
332
333         return ret;
334 }
335
336 /**
337  * basic test for doing a durable open
338  * and do a durable reopen on the same connection
339  * while the first open is still active (fails)
340  */
341 bool test_durable_open_reopen1(struct torture_context *tctx,
342                                struct smb2_tree *tree)
343 {
344         NTSTATUS status;
345         TALLOC_CTX *mem_ctx = talloc_new(tctx);
346         char fname[256];
347         struct smb2_handle _h;
348         struct smb2_handle *h = NULL;
349         struct smb2_create io1, io2;
350         bool ret = true;
351
352         /* Choose a random name in case the state is left a little funky. */
353         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
354                  generate_random_str(tctx, 8));
355
356         smb2_util_unlink(tree, fname);
357
358         smb2_oplock_create_share(&io1, fname,
359                                  smb2_util_share_access(""),
360                                  smb2_util_oplock_level("b"));
361         io1.in.durable_open = true;
362
363         status = smb2_create(tree, mem_ctx, &io1);
364         CHECK_STATUS(status, NT_STATUS_OK);
365         _h = io1.out.file.handle;
366         h = &_h;
367         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
368         CHECK_VAL(io1.out.durable_open, true);
369         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
370
371         /* try a durable reconnect while the file is still open */
372         ZERO_STRUCT(io2);
373         io2.in.fname = fname;
374         io2.in.durable_handle = h;
375
376         status = smb2_create(tree, mem_ctx, &io2);
377         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
378
379 done:
380         if (h != NULL) {
381                 smb2_util_close(tree, *h);
382         }
383
384         smb2_util_unlink(tree, fname);
385
386         talloc_free(tree);
387
388         talloc_free(mem_ctx);
389
390         return ret;
391 }
392
393 /**
394  * basic test for doing a durable open
395  * tcp disconnect, reconnect, do a durable reopen (succeeds)
396  */
397 bool test_durable_open_reopen2(struct torture_context *tctx,
398                                struct smb2_tree *tree)
399 {
400         NTSTATUS status;
401         TALLOC_CTX *mem_ctx = talloc_new(tctx);
402         char fname[256];
403         struct smb2_handle _h;
404         struct smb2_handle *h = NULL;
405         struct smb2_create io1, io2;
406         bool ret = true;
407
408         /* Choose a random name in case the state is left a little funky. */
409         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
410                  generate_random_str(tctx, 8));
411
412         smb2_util_unlink(tree, fname);
413
414         smb2_oplock_create_share(&io1, fname,
415                                  smb2_util_share_access(""),
416                                  smb2_util_oplock_level("b"));
417         io1.in.durable_open = true;
418
419         status = smb2_create(tree, mem_ctx, &io1);
420         CHECK_STATUS(status, NT_STATUS_OK);
421         _h = io1.out.file.handle;
422         h = &_h;
423         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
424         CHECK_VAL(io1.out.durable_open, true);
425         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
426
427         /* disconnect, reconnect and then do durable reopen */
428         talloc_free(tree);
429         tree = NULL;
430
431         if (!torture_smb2_connection(tctx, &tree)) {
432                 torture_warning(tctx, "couldn't reconnect, bailing\n");
433                 ret = false;
434                 goto done;
435         }
436
437         ZERO_STRUCT(io2);
438         io2.in.fname = fname;
439         io2.in.durable_handle = h;
440         h = NULL;
441
442         status = smb2_create(tree, mem_ctx, &io2);
443         CHECK_STATUS(status, NT_STATUS_OK);
444         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
445         CHECK_VAL(io2.out.durable_open, true);
446         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
447         _h = io2.out.file.handle;
448         h = &_h;
449
450 done:
451         if (h != NULL) {
452                 smb2_util_close(tree, *h);
453         }
454
455         smb2_util_unlink(tree, fname);
456
457         talloc_free(tree);
458
459         talloc_free(mem_ctx);
460
461         return ret;
462 }
463
464 /**
465  * basic test for doing a durable open
466  * tcp disconnect, reconnect with a session reconnect and
467  * do a durable reopen (succeeds)
468  */
469 bool test_durable_open_reopen2a(struct torture_context *tctx,
470                                 struct smb2_tree *tree)
471 {
472         NTSTATUS status;
473         TALLOC_CTX *mem_ctx = talloc_new(tctx);
474         char fname[256];
475         struct smb2_handle _h;
476         struct smb2_handle *h = NULL;
477         struct smb2_create io1, io2;
478         uint64_t previous_session_id;
479         bool ret = true;
480
481         /* Choose a random name in case the state is left a little funky. */
482         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
483                  generate_random_str(tctx, 8));
484
485         smb2_util_unlink(tree, fname);
486
487         smb2_oplock_create_share(&io1, fname,
488                                  smb2_util_share_access(""),
489                                  smb2_util_oplock_level("b"));
490         io1.in.durable_open = true;
491
492         status = smb2_create(tree, mem_ctx, &io1);
493         CHECK_STATUS(status, NT_STATUS_OK);
494         _h = io1.out.file.handle;
495         h = &_h;
496         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
497         CHECK_VAL(io1.out.durable_open, true);
498         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
499
500         /* disconnect, reconnect and then do durable reopen */
501         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
502         talloc_free(tree);
503         tree = NULL;
504
505         if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
506                 torture_warning(tctx, "couldn't reconnect, bailing\n");
507                 ret = false;
508                 goto done;
509         }
510
511         ZERO_STRUCT(io2);
512         io2.in.fname = fname;
513         io2.in.durable_handle = h;
514         h = NULL;
515
516         status = smb2_create(tree, mem_ctx, &io2);
517         CHECK_STATUS(status, NT_STATUS_OK);
518         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
519         CHECK_VAL(io2.out.durable_open, true);
520         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
521         _h = io2.out.file.handle;
522         h = &_h;
523
524 done:
525         if (h != NULL) {
526                 smb2_util_close(tree, *h);
527         }
528
529         smb2_util_unlink(tree, fname);
530
531         talloc_free(tree);
532
533         talloc_free(mem_ctx);
534
535         return ret;
536 }
537
538
539 /**
540  * basic test for doing a durable open:
541  * tdis, new tcon, try durable reopen (fails)
542  */
543 bool test_durable_open_reopen3(struct torture_context *tctx,
544                                struct smb2_tree *tree)
545 {
546         NTSTATUS status;
547         TALLOC_CTX *mem_ctx = talloc_new(tctx);
548         char fname[256];
549         struct smb2_handle _h;
550         struct smb2_handle *h = NULL;
551         struct smb2_create io1, io2;
552         bool ret = true;
553         struct smb2_tree *tree2;
554
555         /* Choose a random name in case the state is left a little funky. */
556         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
557                  generate_random_str(tctx, 8));
558
559         smb2_util_unlink(tree, fname);
560
561         smb2_oplock_create_share(&io1, fname,
562                                  smb2_util_share_access(""),
563                                  smb2_util_oplock_level("b"));
564         io1.in.durable_open = true;
565
566         status = smb2_create(tree, mem_ctx, &io1);
567         CHECK_STATUS(status, NT_STATUS_OK);
568         _h = io1.out.file.handle;
569         h = &_h;
570         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
571         CHECK_VAL(io1.out.durable_open, true);
572         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
573
574         /* disconnect, reconnect and then do durable reopen */
575         status = smb2_tdis(tree);
576         CHECK_STATUS(status, NT_STATUS_OK);
577
578         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
579                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
580                 ret = false;
581                 goto done;
582         }
583
584
585         ZERO_STRUCT(io2);
586         io2.in.fname = fname;
587         io2.in.durable_handle = h;
588
589         status = smb2_create(tree2, mem_ctx, &io2);
590         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
591
592 done:
593         if (h != NULL) {
594                 smb2_util_close(tree, *h);
595         }
596
597         smb2_util_unlink(tree2, fname);
598
599         talloc_free(tree);
600
601         talloc_free(mem_ctx);
602
603         return ret;
604 }
605
606 /**
607  * basic test for doing a durable open:
608  * logoff, create a new session, do a durable reopen (succeeds)
609  */
610 bool test_durable_open_reopen4(struct torture_context *tctx,
611                                struct smb2_tree *tree)
612 {
613         NTSTATUS status;
614         TALLOC_CTX *mem_ctx = talloc_new(tctx);
615         char fname[256];
616         struct smb2_handle _h;
617         struct smb2_handle *h = NULL;
618         struct smb2_create io1, io2;
619         bool ret = true;
620         struct smb2_transport *transport;
621         struct smb2_session *session2;
622         struct smb2_tree *tree2;
623
624         /* Choose a random name in case the state is left a little funky. */
625         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
626                  generate_random_str(tctx, 8));
627
628         smb2_util_unlink(tree, fname);
629
630         smb2_oplock_create_share(&io1, fname,
631                                  smb2_util_share_access(""),
632                                  smb2_util_oplock_level("b"));
633         io1.in.durable_open = true;
634         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
635
636         status = smb2_create(tree, mem_ctx, &io1);
637         CHECK_STATUS(status, NT_STATUS_OK);
638         _h = io1.out.file.handle;
639         h = &_h;
640         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
641         CHECK_VAL(io1.out.durable_open, true);
642         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
643
644         /*
645          * do a session logoff, establish a new session and tree
646          * connect on the same transport, and try a durable reopen
647          */
648         transport = tree->session->transport;
649         status = smb2_logoff(tree->session);
650         CHECK_STATUS(status, NT_STATUS_OK);
651
652         if (!torture_smb2_session_setup(tctx, transport,
653                                         0, /* previous_session_id */
654                                         mem_ctx, &session2))
655         {
656                 torture_warning(tctx, "session setup failed.\n");
657                 ret = false;
658                 goto done;
659         }
660
661         /*
662          * the session setup has talloc-stolen the transport,
663          * so we can safely free the old tree+session for clarity
664          */
665         TALLOC_FREE(tree);
666
667         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
668                 torture_warning(tctx, "tree connect failed.\n");
669                 ret = false;
670                 goto done;
671         }
672
673         ZERO_STRUCT(io2);
674         io2.in.fname = fname;
675         io2.in.durable_handle = h;
676         h = NULL;
677
678         status = smb2_create(tree2, mem_ctx, &io2);
679         CHECK_STATUS(status, NT_STATUS_OK);
680
681         _h = io2.out.file.handle;
682         h = &_h;
683         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
684         CHECK_VAL(io2.out.durable_open, true);
685         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
686
687 done:
688         if (h != NULL) {
689                 smb2_util_close(tree2, *h);
690         }
691
692         smb2_util_unlink(tree2, fname);
693
694         talloc_free(tree);
695
696         talloc_free(mem_ctx);
697
698         return ret;
699 }
700
701 /*
702    basic testing of SMB2 durable opens
703    regarding the position information on the handle
704 */
705 bool test_durable_open_file_position(struct torture_context *tctx,
706                                      struct smb2_tree *tree1,
707                                      struct smb2_tree *tree2)
708 {
709         TALLOC_CTX *mem_ctx = talloc_new(tctx);
710         struct smb2_handle h1, h2;
711         struct smb2_create io1, io2;
712         NTSTATUS status;
713         const char *fname = "durable_open_position.dat";
714         union smb_fileinfo qfinfo;
715         union smb_setfileinfo sfinfo;
716         bool ret = true;
717         uint64_t pos;
718
719         smb2_util_unlink(tree1, fname);
720
721         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
722         io1.in.durable_open = true;
723
724         status = smb2_create(tree1, mem_ctx, &io1);
725         CHECK_STATUS(status, NT_STATUS_OK);
726         h1 = io1.out.file.handle;
727         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
728         CHECK_VAL(io1.out.durable_open, true);
729         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
730
731         /* TODO: check extra blob content */
732
733         ZERO_STRUCT(qfinfo);
734         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
735         qfinfo.generic.in.file.handle = h1;
736         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
737         CHECK_STATUS(status, NT_STATUS_OK);
738         CHECK_VAL(qfinfo.position_information.out.position, 0);
739         pos = qfinfo.position_information.out.position;
740         torture_comment(tctx, "position: %llu\n",
741                         (unsigned long long)pos);
742
743         ZERO_STRUCT(sfinfo);
744         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
745         sfinfo.generic.in.file.handle = h1;
746         sfinfo.position_information.in.position = 0x1000;
747         status = smb2_setinfo_file(tree1, &sfinfo);
748         CHECK_STATUS(status, NT_STATUS_OK);
749
750         ZERO_STRUCT(qfinfo);
751         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
752         qfinfo.generic.in.file.handle = h1;
753         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
754         CHECK_STATUS(status, NT_STATUS_OK);
755         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
756         pos = qfinfo.position_information.out.position;
757         torture_comment(tctx, "position: %llu\n",
758                         (unsigned long long)pos);
759
760         talloc_free(tree1);
761         tree1 = NULL;
762
763         ZERO_STRUCT(qfinfo);
764         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
765         qfinfo.generic.in.file.handle = h1;
766         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
767         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
768
769         ZERO_STRUCT(io2);
770         io2.in.fname = fname;
771         io2.in.durable_handle = &h1;
772
773         status = smb2_create(tree2, mem_ctx, &io2);
774         CHECK_STATUS(status, NT_STATUS_OK);
775         CHECK_VAL(io2.out.durable_open, true);
776         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
777         CHECK_VAL(io2.out.reserved, 0x00);
778         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
779         CHECK_VAL(io2.out.alloc_size, 0);
780         CHECK_VAL(io2.out.size, 0);
781         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
782         CHECK_VAL(io2.out.reserved2, 0);
783
784         h2 = io2.out.file.handle;
785
786         ZERO_STRUCT(qfinfo);
787         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
788         qfinfo.generic.in.file.handle = h2;
789         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
790         CHECK_STATUS(status, NT_STATUS_OK);
791         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
792         pos = qfinfo.position_information.out.position;
793         torture_comment(tctx, "position: %llu\n",
794                         (unsigned long long)pos);
795
796         smb2_util_close(tree2, h2);
797
798         talloc_free(mem_ctx);
799
800         smb2_util_unlink(tree2, fname);
801 done:
802         talloc_free(tree1);
803         talloc_free(tree2);
804
805         return ret;
806 }
807
808 /*
809   Open, disconnect, oplock break, reconnect.
810 */
811 bool test_durable_open_oplock(struct torture_context *tctx,
812                               struct smb2_tree *tree1,
813                               struct smb2_tree *tree2)
814 {
815         TALLOC_CTX *mem_ctx = talloc_new(tctx);
816         struct smb2_create io1, io2;
817         struct smb2_handle h1, h2;
818         NTSTATUS status;
819         char fname[256];
820         bool ret = true;
821
822         /* Choose a random name in case the state is left a little funky. */
823         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
824
825         /* Clean slate */
826         smb2_util_unlink(tree1, fname);
827
828         /* Create with batch oplock */
829         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
830         io1.in.durable_open = true;
831
832         io2 = io1;
833         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
834
835         status = smb2_create(tree1, mem_ctx, &io1);
836         CHECK_STATUS(status, NT_STATUS_OK);
837         h1 = io1.out.file.handle;
838         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
839         CHECK_VAL(io1.out.durable_open, true);
840         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
841
842         /* Disconnect after getting the batch */
843         talloc_free(tree1);
844         tree1 = NULL;
845
846         /*
847          * Windows7 (build 7000) will break a batch oplock immediately if the
848          * original client is gone. (ZML: This seems like a bug. It should give
849          * some time for the client to reconnect!)
850          */
851         status = smb2_create(tree2, mem_ctx, &io2);
852         CHECK_STATUS(status, NT_STATUS_OK);
853         h2 = io2.out.file.handle;
854         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
855         CHECK_VAL(io2.out.durable_open, true);
856         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
857
858         /* What if tree1 tries to come back and reclaim? */
859         if (!torture_smb2_connection(tctx, &tree1)) {
860                 torture_warning(tctx, "couldn't reconnect, bailing\n");
861                 ret = false;
862                 goto done;
863         }
864
865         ZERO_STRUCT(io1);
866         io1.in.fname = fname;
867         io1.in.durable_handle = &h1;
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, disconnect, lease break, reconnect.
884 */
885 bool test_durable_open_lease(struct torture_context *tctx,
886                              struct smb2_tree *tree1,
887                              struct smb2_tree *tree2)
888 {
889         TALLOC_CTX *mem_ctx = talloc_new(tctx);
890         struct smb2_create io1, io2;
891         struct smb2_lease ls1, ls2;
892         struct smb2_handle h1, h2;
893         NTSTATUS status;
894         char fname[256];
895         bool ret = true;
896         uint64_t lease1, lease2;
897         uint32_t caps;
898
899         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
900         if (!(caps & SMB2_CAP_LEASING)) {
901                 torture_skip(tctx, "leases are not supported");
902         }
903
904         /*
905          * Choose a random name and random lease in case the state is left a
906          * little funky.
907          */
908         lease1 = random();
909         lease2 = random();
910         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
911
912         /* Clean slate */
913         smb2_util_unlink(tree1, fname);
914
915         /* Create with lease */
916         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
917                           lease1, smb2_util_lease_state("RHW"));
918         io1.in.durable_open = true;
919
920         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
921                           lease2, smb2_util_lease_state("RHW"));
922         io2.in.durable_open = true;
923         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
924
925         status = smb2_create(tree1, mem_ctx, &io1);
926         CHECK_STATUS(status, NT_STATUS_OK);
927         h1 = io1.out.file.handle;
928         CHECK_VAL(io1.out.durable_open, true);
929         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
930
931         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
932         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
933         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
934         CHECK_VAL(io1.out.lease_response.lease_state,
935             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
936
937         /* Disconnect after getting the lease */
938         talloc_free(tree1);
939         tree1 = NULL;
940
941         /*
942          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
943          * even if the original client is gone. (ZML: This seems like a bug. It
944          * should give some time for the client to reconnect! And why RH?)
945          * 
946          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
947          * Test is adapted accordingly.
948          */
949         status = smb2_create(tree2, mem_ctx, &io2);
950         CHECK_STATUS(status, NT_STATUS_OK);
951         h2 = io2.out.file.handle;
952         CHECK_VAL(io2.out.durable_open, true);
953         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
954
955         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
956         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
957         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
958         CHECK_VAL(io2.out.lease_response.lease_state,
959             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
960
961         /* What if tree1 tries to come back and reclaim? */
962         if (!torture_smb2_connection(tctx, &tree1)) {
963                 torture_warning(tctx, "couldn't reconnect, bailing\n");
964                 ret = false;
965                 goto done;
966         }
967
968         ZERO_STRUCT(io1);
969         io1.in.fname = fname;
970         io1.in.durable_handle = &h1;
971         io1.in.lease_request = &ls1;
972
973         status = smb2_create(tree1, mem_ctx, &io1);
974         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
975
976  done:
977         smb2_util_close(tree2, h2);
978         smb2_util_unlink(tree2, fname);
979
980         talloc_free(tree1);
981         talloc_free(tree2);
982
983         return ret;
984 }
985
986 /*
987   Open, take BRL, disconnect, reconnect.
988 */
989 bool test_durable_open_lock(struct torture_context *tctx,
990                             struct smb2_tree *tree)
991 {
992         TALLOC_CTX *mem_ctx = talloc_new(tctx);
993         struct smb2_create io;
994         struct smb2_lease ls;
995         struct smb2_handle h;
996         struct smb2_lock lck;
997         struct smb2_lock_element el[2];
998         NTSTATUS status;
999         char fname[256];
1000         bool ret = true;
1001         uint64_t lease;
1002         uint32_t caps;
1003
1004         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1005         if (!(caps & SMB2_CAP_LEASING)) {
1006                 torture_skip(tctx, "leases are not supported");
1007         }
1008
1009         /*
1010          * Choose a random name and random lease in case the state is left a
1011          * little funky.
1012          */
1013         lease = random();
1014         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1015
1016         /* Clean slate */
1017         smb2_util_unlink(tree, fname);
1018
1019         /* Create with lease */
1020
1021         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1022                           smb2_util_lease_state("RWH"));
1023         io.in.durable_open              = true;
1024
1025         status = smb2_create(tree, mem_ctx, &io);
1026         CHECK_STATUS(status, NT_STATUS_OK);
1027         h = io.out.file.handle;
1028         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1029
1030         CHECK_VAL(io.out.durable_open, true);
1031         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1032         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1033         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1034         CHECK_VAL(io.out.lease_response.lease_state,
1035             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1036
1037         ZERO_STRUCT(lck);
1038         ZERO_STRUCT(el);
1039         lck.in.locks            = el;
1040         lck.in.lock_count       = 0x0001;
1041         lck.in.lock_sequence    = 0x00000000;
1042         lck.in.file.handle      = h;
1043         el[0].offset            = 0;
1044         el[0].length            = 1;
1045         el[0].reserved          = 0x00000000;
1046         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1047         status = smb2_lock(tree, &lck);
1048         CHECK_STATUS(status, NT_STATUS_OK);
1049
1050         /* Disconnect/Reconnect. */
1051         talloc_free(tree);
1052         tree = NULL;
1053
1054         if (!torture_smb2_connection(tctx, &tree)) {
1055                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1056                 ret = false;
1057                 goto done;
1058         }
1059
1060         ZERO_STRUCT(io);
1061         io.in.fname = fname;
1062         io.in.durable_handle = &h;
1063         io.in.lease_request = &ls;
1064
1065         status = smb2_create(tree, mem_ctx, &io);
1066         CHECK_STATUS(status, NT_STATUS_OK);
1067         h = io.out.file.handle;
1068
1069         lck.in.file.handle      = h;
1070         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1071         status = smb2_lock(tree, &lck);
1072         CHECK_STATUS(status, NT_STATUS_OK);
1073
1074  done:
1075         smb2_util_close(tree, h);
1076         smb2_util_unlink(tree, fname);
1077         talloc_free(tree);
1078
1079         return ret;
1080 }
1081
1082 /**
1083  * Open with a RH lease, disconnect, open in another tree, reconnect.
1084  *
1085  * This test actually demonstrates a minimum level of respect for the durable
1086  * open in the face of another open. As long as this test shows an inability to
1087  * reconnect after an open, the oplock/lease tests above will certainly
1088  * demonstrate an error on reconnect.
1089  */
1090 bool test_durable_open_open_lease(struct torture_context *tctx,
1091                                   struct smb2_tree *tree1,
1092                                   struct smb2_tree *tree2)
1093 {
1094         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1095         struct smb2_create io1, io2;
1096         struct smb2_lease ls;
1097         struct smb2_handle h1, h2;
1098         NTSTATUS status;
1099         char fname[256];
1100         bool ret = true;
1101         uint64_t lease;
1102         uint32_t caps;
1103
1104         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1105         if (!(caps & SMB2_CAP_LEASING)) {
1106                 torture_skip(tctx, "leases are not supported");
1107         }
1108
1109         /*
1110          * Choose a random name and random lease in case the state is left a
1111          * little funky.
1112          */
1113         lease = random();
1114         snprintf(fname, 256, "durable_open_open_lease_%s.dat",
1115                  generate_random_str(tctx, 8));
1116
1117         /* Clean slate */
1118         smb2_util_unlink(tree1, fname);
1119
1120         /* Create with lease */
1121         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1122                                 smb2_util_share_access(""),
1123                                 lease,
1124                                 smb2_util_lease_state("RH"));
1125         io1.in.durable_open = true;
1126
1127         status = smb2_create(tree1, mem_ctx, &io1);
1128         CHECK_STATUS(status, NT_STATUS_OK);
1129         h1 = io1.out.file.handle;
1130         CHECK_VAL(io1.out.durable_open, true);
1131         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1132
1133         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1134         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1135         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1136         CHECK_VAL(io1.out.lease_response.lease_state,
1137                   smb2_util_lease_state("RH"));
1138
1139         /* Disconnect */
1140         talloc_free(tree1);
1141         tree1 = NULL;
1142
1143         /* Open the file in tree2 */
1144         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1145
1146         status = smb2_create(tree2, mem_ctx, &io2);
1147         CHECK_STATUS(status, NT_STATUS_OK);
1148         h2 = io2.out.file.handle;
1149         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1150
1151         /* Reconnect */
1152         if (!torture_smb2_connection(tctx, &tree1)) {
1153                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1154                 ret = false;
1155                 goto done;
1156         }
1157
1158         ZERO_STRUCT(io1);
1159         io1.in.fname = fname;
1160         io1.in.durable_handle = &h1;
1161         io1.in.lease_request = &ls;
1162
1163         /*
1164          * Windows7 (build 7000) will give away an open immediately if the
1165          * original client is gone. (ZML: This seems like a bug. It should give
1166          * some time for the client to reconnect!)
1167          */
1168         status = smb2_create(tree1, mem_ctx, &io1);
1169         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1170         h1 = io1.out.file.handle;
1171
1172  done:
1173         smb2_util_close(tree2, h2);
1174         smb2_util_unlink(tree2, fname);
1175         smb2_util_close(tree1, h1);
1176         smb2_util_unlink(tree1, fname);
1177
1178         talloc_free(tree1);
1179         talloc_free(tree2);
1180
1181         return ret;
1182 }
1183
1184 /**
1185  * Open with a batch oplock, disconnect, open in another tree, reconnect.
1186  *
1187  * This test actually demonstrates a minimum level of respect for the durable
1188  * open in the face of another open. As long as this test shows an inability to
1189  * reconnect after an open, the oplock/lease tests above will certainly
1190  * demonstrate an error on reconnect.
1191  */
1192 bool test_durable_open_open_oplock(struct torture_context *tctx,
1193                                    struct smb2_tree *tree1,
1194                                    struct smb2_tree *tree2)
1195 {
1196         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1197         struct smb2_create io1, io2;
1198         struct smb2_handle h1, h2;
1199         NTSTATUS status;
1200         char fname[256];
1201         bool ret = true;
1202
1203         /*
1204          * Choose a random name and random lease in case the state is left a
1205          * little funky.
1206          */
1207         snprintf(fname, 256, "durable_open_open_oplock_%s.dat",
1208                  generate_random_str(tctx, 8));
1209
1210         /* Clean slate */
1211         smb2_util_unlink(tree1, fname);
1212
1213         /* Create with batch oplock */
1214         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1215         io1.in.durable_open = true;
1216
1217         status = smb2_create(tree1, mem_ctx, &io1);
1218         CHECK_STATUS(status, NT_STATUS_OK);
1219         h1 = io1.out.file.handle;
1220         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1221         CHECK_VAL(io1.out.durable_open, true);
1222         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1223
1224         /* Disconnect */
1225         talloc_free(tree1);
1226         tree1 = NULL;
1227
1228         /* Open the file in tree2 */
1229         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1230
1231         status = smb2_create(tree2, mem_ctx, &io2);
1232         CHECK_STATUS(status, NT_STATUS_OK);
1233         h2 = io2.out.file.handle;
1234         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1235
1236         /* Reconnect */
1237         if (!torture_smb2_connection(tctx, &tree1)) {
1238                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1239                 ret = false;
1240                 goto done;
1241         }
1242
1243         ZERO_STRUCT(io1);
1244         io1.in.fname = fname;
1245         io1.in.durable_handle = &h1;
1246
1247         status = smb2_create(tree1, mem_ctx, &io1);
1248         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1249         h1 = io1.out.file.handle;
1250
1251  done:
1252         smb2_util_close(tree2, h2);
1253         smb2_util_unlink(tree2, fname);
1254         smb2_util_close(tree1, h1);
1255         smb2_util_unlink(tree1, fname);
1256
1257         talloc_free(tree1);
1258         talloc_free(tree2);
1259
1260         return ret;
1261 }
1262
1263 struct torture_suite *torture_smb2_durable_open_init(void)
1264 {
1265         struct torture_suite *suite =
1266             torture_suite_create(talloc_autofree_context(), "durable-open");
1267
1268         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1269         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1270         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1271         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1272         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1273         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1274         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1275         torture_suite_add_2smb2_test(suite, "file-position",
1276             test_durable_open_file_position);
1277         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1278         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1279         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1280         torture_suite_add_2smb2_test(suite, "open-lease",
1281                                      test_durable_open_open_lease);
1282         torture_suite_add_2smb2_test(suite, "open-oplock",
1283                                      test_durable_open_open_oplock);
1284
1285         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1286
1287         return suite;
1288 }