TODO source4/torture/smb2/durable_open.c
[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    Copyright (C) Michael Adam 2011-2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
30 #include "lib/util/time_basic.h"
31
32 #define CHECK_VAL(v, correct) do { \
33         if ((v) != (correct)) { \
34                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
35                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
36                 ret = false; \
37         }} while (0)
38
39 #define CHECK_NOT_VAL(v, incorrect) do { \
40         if ((v) == (incorrect)) { \
41                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
42                                 __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
43                 ret = false; \
44         }} while (0)
45
46 #define CHECK_NOT_NULL(p) do { \
47         if ((p) == NULL) { \
48                 torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
49                                 __location__, #p); \
50                 ret = false; \
51         }} while (0)
52
53 #define CHECK_STATUS(status, correct) do { \
54         if (!NT_STATUS_EQUAL(status, correct)) { \
55                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
56                        nt_errstr(status), nt_errstr(correct)); \
57                 ret = false; \
58                 goto done; \
59         }} while (0)
60
61 #define CHECK_CREATED(__io, __created, __attribute)                     \
62         do {                                                            \
63                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
64                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
65                 CHECK_VAL((__io)->out.size, 0);                         \
66                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
67                 CHECK_VAL((__io)->out.reserved2, 0);                    \
68         } while(0)
69
70 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size)  \
71         do {                                                                    \
72                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
73                 CHECK_VAL((__io)->out.alloc_size, (__alloc_size));              \
74                 CHECK_VAL((__io)->out.size, (__size));                          \
75                 CHECK_VAL((__io)->out.file_attr, (__attribute));                \
76                 CHECK_VAL((__io)->out.reserved2, 0);                            \
77         } while(0)
78
79
80
81 /**
82  * basic durable_open test.
83  * durable state should only be granted when requested
84  * along with a batch oplock or a handle lease.
85  *
86  * This test tests durable open with all possible oplock types.
87  */
88
89 struct durable_open_vs_oplock {
90         const char *level;
91         const char *share_mode;
92         bool expected;
93 };
94
95 #define NUM_OPLOCK_TYPES 4
96 #define NUM_SHARE_MODES 8
97 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
98 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
99 {
100         { "", "", false },
101         { "", "R", false },
102         { "", "W", false },
103         { "", "D", false },
104         { "", "RD", false },
105         { "", "RW", false },
106         { "", "WD", false },
107         { "", "RWD", false },
108
109         { "s", "", false },
110         { "s", "R", false },
111         { "s", "W", false },
112         { "s", "D", false },
113         { "s", "RD", false },
114         { "s", "RW", false },
115         { "s", "WD", false },
116         { "s", "RWD", false },
117
118         { "x", "", false },
119         { "x", "R", false },
120         { "x", "W", false },
121         { "x", "D", false },
122         { "x", "RD", false },
123         { "x", "RW", false },
124         { "x", "WD", false },
125         { "x", "RWD", false },
126
127         { "b", "", true },
128         { "b", "R", true },
129         { "b", "W", true },
130         { "b", "D", true },
131         { "b", "RD", true },
132         { "b", "RW", true },
133         { "b", "WD", true },
134         { "b", "RWD", true },
135 };
136
137 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
138                                               struct smb2_tree *tree,
139                                               const char *fname,
140                                               struct durable_open_vs_oplock test)
141 {
142         NTSTATUS status;
143         TALLOC_CTX *mem_ctx = talloc_new(tctx);
144         struct smb2_handle _h;
145         struct smb2_handle *h = NULL;
146         bool ret = true;
147         struct smb2_create io;
148
149         smb2_util_unlink(tree, fname);
150
151         smb2_oplock_create_share(&io, fname,
152                                  smb2_util_share_access(test.share_mode),
153                                  smb2_util_oplock_level(test.level));
154         io.in.durable_open = true;
155
156         status = smb2_create(tree, mem_ctx, &io);
157         CHECK_STATUS(status, NT_STATUS_OK);
158         _h = io.out.file.handle;
159         h = &_h;
160         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
161         CHECK_VAL(io.out.durable_open, test.expected);
162         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
163
164 done:
165         if (h != NULL) {
166                 smb2_util_close(tree, *h);
167         }
168         smb2_util_unlink(tree, fname);
169         talloc_free(mem_ctx);
170
171         return ret;
172 }
173
174 static bool test_durable_open_open_oplock(struct torture_context *tctx,
175                                           struct smb2_tree *tree)
176 {
177         TALLOC_CTX *mem_ctx = talloc_new(tctx);
178         char fname[256];
179         bool ret = true;
180         int i;
181
182         /* Choose a random name in case the state is left a little funky. */
183         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
184
185         smb2_util_unlink(tree, fname);
186
187         /* test various oplock levels with durable open */
188
189         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
190                 ret = test_one_durable_open_open_oplock(tctx,
191                                                         tree,
192                                                         fname,
193                                                         durable_open_vs_oplock_table[i]);
194                 if (ret == false) {
195                         goto done;
196                 }
197         }
198
199 done:
200         smb2_util_unlink(tree, fname);
201         talloc_free(tree);
202         talloc_free(mem_ctx);
203
204         return ret;
205 }
206
207 /**
208  * basic durable_open test.
209  * durable state should only be granted when requested
210  * along with a batch oplock or a handle lease.
211  *
212  * This test tests durable open with all valid lease types.
213  */
214
215 struct durable_open_vs_lease {
216         const char *type;
217         const char *share_mode;
218         bool expected;
219 };
220
221 #define NUM_LEASE_TYPES 5
222 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
223 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
224 {
225         { "", "", false },
226         { "", "R", false },
227         { "", "W", false },
228         { "", "D", false },
229         { "", "RW", false },
230         { "", "RD", false },
231         { "", "WD", false },
232         { "", "RWD", false },
233
234         { "R", "", false },
235         { "R", "R", false },
236         { "R", "W", false },
237         { "R", "D", false },
238         { "R", "RW", false },
239         { "R", "RD", false },
240         { "R", "DW", false },
241         { "R", "RWD", false },
242
243         { "RW", "", false },
244         { "RW", "R", false },
245         { "RW", "W", false },
246         { "RW", "D", false },
247         { "RW", "RW", false },
248         { "RW", "RD", false },
249         { "RW", "WD", false },
250         { "RW", "RWD", false },
251
252         { "RH", "", true },
253         { "RH", "R", true },
254         { "RH", "W", true },
255         { "RH", "D", true },
256         { "RH", "RW", true },
257         { "RH", "RD", true },
258         { "RH", "WD", true },
259         { "RH", "RWD", true },
260
261         { "RHW", "", true },
262         { "RHW", "R", true },
263         { "RHW", "W", true },
264         { "RHW", "D", true },
265         { "RHW", "RW", true },
266         { "RHW", "RD", true },
267         { "RHW", "WD", true },
268         { "RHW", "RWD", true },
269 };
270
271 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
272                                              struct smb2_tree *tree,
273                                              const char *fname,
274                                              struct durable_open_vs_lease test)
275 {
276         NTSTATUS status;
277         TALLOC_CTX *mem_ctx = talloc_new(tctx);
278         struct smb2_handle _h;
279         struct smb2_handle *h = NULL;
280         bool ret = true;
281         struct smb2_create io;
282         struct smb2_lease ls;
283         uint64_t lease;
284         uint32_t caps;
285
286         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
287         if (!(caps & SMB2_CAP_LEASING)) {
288                 torture_skip(tctx, "leases are not supported");
289         }
290
291         smb2_util_unlink(tree, fname);
292
293         lease = random();
294
295         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
296                                 smb2_util_share_access(test.share_mode),
297                                 lease,
298                                 smb2_util_lease_state(test.type));
299         io.in.durable_open = true;
300
301         status = smb2_create(tree, mem_ctx, &io);
302         CHECK_STATUS(status, NT_STATUS_OK);
303         _h = io.out.file.handle;
304         h = &_h;
305         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
306         CHECK_VAL(io.out.durable_open, test.expected);
307         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
308         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
309         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
310         CHECK_VAL(io.out.lease_response.lease_state,
311                   smb2_util_lease_state(test.type));
312 done:
313         if (h != NULL) {
314                 smb2_util_close(tree, *h);
315         }
316         smb2_util_unlink(tree, fname);
317         talloc_free(mem_ctx);
318
319         return ret;
320 }
321
322 static bool test_durable_open_open_lease(struct torture_context *tctx,
323                                          struct smb2_tree *tree)
324 {
325         TALLOC_CTX *mem_ctx = talloc_new(tctx);
326         char fname[256];
327         bool ret = true;
328         int i;
329         uint32_t caps;
330
331         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
332         if (!(caps & SMB2_CAP_LEASING)) {
333                 torture_skip(tctx, "leases are not supported");
334         }
335
336         /* Choose a random name in case the state is left a little funky. */
337         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
338
339         smb2_util_unlink(tree, fname);
340
341
342         /* test various oplock levels with durable open */
343
344         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
345                 ret = test_one_durable_open_open_lease(tctx,
346                                                        tree,
347                                                        fname,
348                                                        durable_open_vs_lease_table[i]);
349                 if (ret == false) {
350                         goto done;
351                 }
352         }
353
354 done:
355         smb2_util_unlink(tree, fname);
356         talloc_free(tree);
357         talloc_free(mem_ctx);
358
359         return ret;
360 }
361
362 /**
363  * basic test for doing a durable open
364  * and do a durable reopen on the same connection
365  * while the first open is still active (fails)
366  */
367 static bool test_durable_open_reopen1(struct torture_context *tctx,
368                                       struct smb2_tree *tree)
369 {
370         NTSTATUS status;
371         TALLOC_CTX *mem_ctx = talloc_new(tctx);
372         char fname[256];
373         struct smb2_handle _h;
374         struct smb2_handle *h = NULL;
375         struct smb2_create io1, io2;
376         bool ret = true;
377
378         /* Choose a random name in case the state is left a little funky. */
379         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
380                  generate_random_str(tctx, 8));
381
382         smb2_util_unlink(tree, fname);
383
384         smb2_oplock_create_share(&io1, fname,
385                                  smb2_util_share_access(""),
386                                  smb2_util_oplock_level("b"));
387         io1.in.durable_open = true;
388
389         status = smb2_create(tree, mem_ctx, &io1);
390         CHECK_STATUS(status, NT_STATUS_OK);
391         _h = io1.out.file.handle;
392         h = &_h;
393         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
394         CHECK_VAL(io1.out.durable_open, true);
395         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
396
397         /* try a durable reconnect while the file is still open */
398         ZERO_STRUCT(io2);
399         io2.in.fname = fname;
400         io2.in.durable_handle = h;
401
402         status = smb2_create(tree, mem_ctx, &io2);
403         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
404
405 done:
406         if (h != NULL) {
407                 smb2_util_close(tree, *h);
408         }
409
410         smb2_util_unlink(tree, fname);
411
412         talloc_free(tree);
413
414         talloc_free(mem_ctx);
415
416         return ret;
417 }
418
419 /**
420  * Basic test for doing a durable open
421  * and do a session reconnect while the first
422  * session is still active and the handle is
423  * still open in the client.
424  * This closes the original session and  a
425  * durable reconnect on the new session succeeds.
426  */
427 static bool test_durable_open_reopen1a(struct torture_context *tctx,
428                                        struct smb2_tree *tree)
429 {
430         NTSTATUS status;
431         TALLOC_CTX *mem_ctx = talloc_new(tctx);
432         char fname[256];
433         struct smb2_handle _h;
434         struct smb2_handle *h = NULL;
435         struct smb2_create io;
436         bool ret = true;
437         struct smb2_tree *tree2 = NULL;
438         struct smb2_tree *tree3 = NULL;
439         uint64_t previous_session_id;
440         struct smbcli_options options;
441         struct GUID orig_client_guid;
442
443         options = tree->session->transport->options;
444         orig_client_guid = options.client_guid;
445
446         /* Choose a random name in case the state is left a little funky. */
447         snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
448                  generate_random_str(tctx, 8));
449
450         smb2_util_unlink(tree, fname);
451
452         smb2_oplock_create_share(&io, fname,
453                                  smb2_util_share_access(""),
454                                  smb2_util_oplock_level("b"));
455         io.in.durable_open = true;
456
457         status = smb2_create(tree, mem_ctx, &io);
458         CHECK_STATUS(status, NT_STATUS_OK);
459         _h = io.out.file.handle;
460         h = &_h;
461         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
462         CHECK_VAL(io.out.durable_open, true);
463         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
464
465         /*
466          * a session reconnect on a second tcp connection
467          */
468
469         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
470
471         /* for oplocks, the client guid can be different: */
472         options.client_guid = GUID_random();
473
474         ret = torture_smb2_connection_ext(tctx, previous_session_id,
475                                           &options, &tree2);
476         torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
477
478         /*
479          * check that this has deleted the old session
480          */
481
482         ZERO_STRUCT(io);
483         io.in.fname = fname;
484         io.in.durable_handle = h;
485
486         status = smb2_create(tree, mem_ctx, &io);
487         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
488
489         TALLOC_FREE(tree);
490
491         /*
492          * but a durable reconnect on the new session succeeds:
493          */
494
495         ZERO_STRUCT(io);
496         io.in.fname = fname;
497         io.in.durable_handle = h;
498
499         status = smb2_create(tree2, mem_ctx, &io);
500         CHECK_STATUS(status, NT_STATUS_OK);
501         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
502         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
503         _h = io.out.file.handle;
504         h = &_h;
505
506         /*
507          * a session reconnect on a second tcp connection
508          */
509
510         previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
511
512         /* the original client_guid works just the same */
513         options.client_guid = orig_client_guid;
514
515         ret = torture_smb2_connection_ext(tctx, previous_session_id,
516                                           &options, &tree3);
517         torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
518
519         /*
520          * check that this has deleted the old session
521          */
522
523         ZERO_STRUCT(io);
524         io.in.fname = fname;
525         io.in.durable_handle = h;
526
527         status = smb2_create(tree2, mem_ctx, &io);
528         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
529
530         TALLOC_FREE(tree2);
531
532         /*
533          * but a durable reconnect on the new session succeeds:
534          */
535
536         ZERO_STRUCT(io);
537         io.in.fname = fname;
538         io.in.durable_handle = h;
539
540         status = smb2_create(tree3, mem_ctx, &io);
541         CHECK_STATUS(status, NT_STATUS_OK);
542         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
543         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
544         _h = io.out.file.handle;
545         h = &_h;
546
547 done:
548         if (tree == NULL) {
549                 tree = tree2;
550         }
551
552         if (tree == NULL) {
553                 tree = tree3;
554         }
555
556         if (tree != NULL) {
557                 if (h != NULL) {
558                         smb2_util_close(tree, *h);
559                         h = NULL;
560                 }
561                 smb2_util_unlink(tree, fname);
562
563                 talloc_free(tree);
564         }
565
566         talloc_free(mem_ctx);
567
568         return ret;
569 }
570
571 /**
572  * lease variant of reopen1a
573  *
574  * Basic test for doing a durable open and doing a session
575  * reconnect while the first session is still active and the
576  * handle is still open in the client.
577  * This closes the original session and  a durable reconnect on
578  * the new session succeeds depending on the client guid:
579  *
580  * Durable reconnect on a session with a different client guid fails.
581  * Durable reconnect on a session with the original client guid succeeds.
582  */
583 bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
584                                       struct smb2_tree *tree)
585 {
586         NTSTATUS status;
587         TALLOC_CTX *mem_ctx = talloc_new(tctx);
588         char fname[256];
589         struct smb2_handle _h;
590         struct smb2_handle *h = NULL;
591         struct smb2_create io;
592         struct smb2_lease ls;
593         uint64_t lease_key;
594         bool ret = true;
595         struct smb2_tree *tree2 = NULL;
596         struct smb2_tree *tree3 = NULL;
597         uint64_t previous_session_id;
598         struct smbcli_options options;
599         struct GUID orig_client_guid;
600
601         options = tree->session->transport->options;
602         orig_client_guid = options.client_guid;
603
604         /* Choose a random name in case the state is left a little funky. */
605         snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
606                  generate_random_str(tctx, 8));
607
608         smb2_util_unlink(tree, fname);
609
610         lease_key = random();
611         smb2_lease_create(&io, &ls, false /* dir */, fname,
612                           lease_key, smb2_util_lease_state("RWH"));
613         io.in.durable_open = true;
614
615         status = smb2_create(tree, mem_ctx, &io);
616         CHECK_STATUS(status, NT_STATUS_OK);
617         _h = io.out.file.handle;
618         h = &_h;
619         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
620         CHECK_VAL(io.out.durable_open, true);
621         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
622         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
623         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
624         CHECK_VAL(io.out.lease_response.lease_state,
625                   smb2_util_lease_state("RWH"));
626         CHECK_VAL(io.out.lease_response.lease_flags, 0);
627         CHECK_VAL(io.out.lease_response.lease_duration, 0);
628
629         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
630
631         /*
632          * a session reconnect on a second tcp connection
633          * with a different client_guid does not allow
634          * the durable reconnect.
635          */
636
637         options.client_guid = GUID_random();
638
639         ret = torture_smb2_connection_ext(tctx, previous_session_id,
640                                           &options, &tree2);
641         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
642
643         /*
644          * check that this has deleted the old session
645          */
646
647         ZERO_STRUCT(io);
648         io.in.fname = fname;
649         io.in.durable_handle = h;
650         io.in.lease_request = &ls;
651         status = smb2_create(tree, mem_ctx, &io);
652         CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
653         TALLOC_FREE(tree);
654
655
656         /*
657          * but a durable reconnect on the new session with the wrong
658          * client guid fails
659          */
660
661         ZERO_STRUCT(io);
662         io.in.fname = fname;
663         io.in.durable_handle = h;
664         io.in.lease_request = &ls;
665         status = smb2_create(tree2, mem_ctx, &io);
666         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
667
668         /*
669          * now a session reconnect on a second tcp connection
670          * with original client_guid allows the durable reconnect.
671          */
672
673         options.client_guid = orig_client_guid;
674
675         ret = torture_smb2_connection_ext(tctx, previous_session_id,
676                                           &options, &tree3);
677         torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
678
679         /*
680          * check that this has deleted the old session
681          * In this case, a durable reconnect attempt with the
682          * correct client_guid yields a different error code.
683          */
684
685         ZERO_STRUCT(io);
686         io.in.fname = fname;
687         io.in.durable_handle = h;
688         io.in.lease_request = &ls;
689         status = smb2_create(tree2, mem_ctx, &io);
690         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
691         TALLOC_FREE(tree2);
692
693         /*
694          * but a durable reconnect on the new session succeeds:
695          */
696
697         ZERO_STRUCT(io);
698         io.in.fname = fname;
699         io.in.durable_handle = h;
700         io.in.lease_request = &ls;
701         status = smb2_create(tree3, mem_ctx, &io);
702         CHECK_STATUS(status, NT_STATUS_OK);
703         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
704         CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
705         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
706         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
707         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
708         CHECK_VAL(io.out.lease_response.lease_state,
709                   smb2_util_lease_state("RWH"));
710         CHECK_VAL(io.out.lease_response.lease_flags, 0);
711         CHECK_VAL(io.out.lease_response.lease_duration, 0);
712         _h = io.out.file.handle;
713         h = &_h;
714
715 done:
716         if (tree == NULL) {
717                 tree = tree2;
718         }
719
720         if (tree == NULL) {
721                 tree = tree3;
722         }
723
724         if (tree != NULL) {
725                 if (h != NULL) {
726                         smb2_util_close(tree, *h);
727                 }
728
729                 smb2_util_unlink(tree, fname);
730
731                 talloc_free(tree);
732         }
733
734         talloc_free(mem_ctx);
735
736         return ret;
737 }
738
739
740 /**
741  * basic test for doing a durable open
742  * tcp disconnect, reconnect, do a durable reopen (succeeds)
743  */
744 static bool test_durable_open_reopen2(struct torture_context *tctx,
745                                       struct smb2_tree *tree)
746 {
747         NTSTATUS status;
748         TALLOC_CTX *mem_ctx = talloc_new(tctx);
749         char fname[256];
750         struct smb2_handle _h;
751         struct smb2_handle *h = NULL;
752         struct smb2_create io;
753         bool ret = true;
754
755         /* Choose a random name in case the state is left a little funky. */
756         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
757                  generate_random_str(tctx, 8));
758
759         smb2_util_unlink(tree, fname);
760
761         smb2_oplock_create_share(&io, fname,
762                                  smb2_util_share_access(""),
763                                  smb2_util_oplock_level("b"));
764         io.in.durable_open = true;
765
766         status = smb2_create(tree, mem_ctx, &io);
767         CHECK_STATUS(status, NT_STATUS_OK);
768         _h = io.out.file.handle;
769         h = &_h;
770         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
771         CHECK_VAL(io.out.durable_open, true);
772         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
773
774         /* disconnect, leaving the durable in place */
775         TALLOC_FREE(tree);
776
777         if (!torture_smb2_connection(tctx, &tree)) {
778                 torture_warning(tctx, "couldn't reconnect, bailing\n");
779                 ret = false;
780                 goto done;
781         }
782
783         ZERO_STRUCT(io);
784         /* the path name is ignored by the server */
785         io.in.fname = fname;
786         io.in.durable_handle = h; /* durable v1 reconnect request */
787         h = NULL;
788
789         status = smb2_create(tree, mem_ctx, &io);
790         CHECK_STATUS(status, NT_STATUS_OK);
791         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
792         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
793         _h = io.out.file.handle;
794         h = &_h;
795
796         /* disconnect again, leaving the durable in place */
797         TALLOC_FREE(tree);
798
799         if (!torture_smb2_connection(tctx, &tree)) {
800                 torture_warning(tctx, "couldn't reconnect, bailing\n");
801                 ret = false;
802                 goto done;
803         }
804
805         /*
806          * show that the filename and many other fields
807          * are ignored. only the reconnect request blob
808          * is important.
809          */
810         ZERO_STRUCT(io);
811         /* the path name is ignored by the server */
812         io.in.security_flags = 0x78;
813         io.in.oplock_level = 0x78;
814         io.in.impersonation_level = 0x12345678;
815         io.in.create_flags = 0x12345678;
816         io.in.reserved = 0x12345678;
817         io.in.desired_access = 0x12345678;
818         io.in.file_attributes = 0x12345678;
819         io.in.share_access = 0x12345678;
820         io.in.create_disposition = 0x12345678;
821         io.in.create_options = 0x12345678;
822         io.in.fname = "__non_existing_fname__";
823         io.in.durable_handle = h; /* durable v1 reconnect request */
824         h = NULL;
825
826         status = smb2_create(tree, mem_ctx, &io);
827         CHECK_STATUS(status, NT_STATUS_OK);
828         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
829         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
830         _h = io.out.file.handle;
831         h = &_h;
832
833         /* disconnect, leaving the durable in place */
834         TALLOC_FREE(tree);
835
836         if (!torture_smb2_connection(tctx, &tree)) {
837                 torture_warning(tctx, "couldn't reconnect, bailing\n");
838                 ret = false;
839                 goto done;
840         }
841
842         /*
843          * show that an additionally specified durable v1 request
844          * is ignored by the server.
845          * See MS-SMB2, 3.3.5.9.7
846          * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
847          */
848         ZERO_STRUCT(io);
849         /* the path name is ignored by the server */
850         io.in.fname = fname;
851         io.in.durable_handle = h;  /* durable v1 reconnect request */
852         io.in.durable_open = true; /* durable v1 handle request */
853         h = NULL;
854
855         status = smb2_create(tree, mem_ctx, &io);
856         CHECK_STATUS(status, NT_STATUS_OK);
857         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
858         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
859         _h = io.out.file.handle;
860         h = &_h;
861
862 done:
863         if (tree != NULL) {
864                 if (h != NULL) {
865                         smb2_util_close(tree, *h);
866                 }
867
868                 smb2_util_unlink(tree, fname);
869
870                 talloc_free(tree);
871         }
872
873         talloc_free(mem_ctx);
874
875         return ret;
876 }
877
878 /**
879  * lease variant of reopen2
880  * basic test for doing a durable open
881  * tcp disconnect, reconnect, do a durable reopen (succeeds)
882  */
883 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
884                                             struct smb2_tree *tree)
885 {
886         NTSTATUS status;
887         TALLOC_CTX *mem_ctx = talloc_new(tctx);
888         char fname[256];
889         struct smb2_handle _h;
890         struct smb2_handle *h = NULL;
891         struct smb2_create io;
892         struct smb2_lease ls;
893         uint64_t lease_key;
894         bool ret = true;
895         struct smbcli_options options;
896         uint32_t caps;
897
898         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
899         if (!(caps & SMB2_CAP_LEASING)) {
900                 torture_skip(tctx, "leases are not supported");
901         }
902
903         options = tree->session->transport->options;
904
905         /* Choose a random name in case the state is left a little funky. */
906         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
907                  generate_random_str(tctx, 8));
908
909         smb2_util_unlink(tree, fname);
910
911         lease_key = random();
912         smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
913                           smb2_util_lease_state("RWH"));
914         io.in.durable_open = true;
915
916         status = smb2_create(tree, mem_ctx, &io);
917         CHECK_STATUS(status, NT_STATUS_OK);
918         _h = io.out.file.handle;
919         h = &_h;
920         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
921
922         CHECK_VAL(io.out.durable_open, true);
923         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
924         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
925         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
926         CHECK_VAL(io.out.lease_response.lease_state,
927                   smb2_util_lease_state("RWH"));
928         CHECK_VAL(io.out.lease_response.lease_flags, 0);
929         CHECK_VAL(io.out.lease_response.lease_duration, 0);
930
931         /* disconnect, reconnect and then do durable reopen */
932         TALLOC_FREE(tree);
933
934         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
935                 torture_warning(tctx, "couldn't reconnect, bailing\n");
936                 ret = false;
937                 goto done;
938         }
939
940
941         /* a few failure tests: */
942
943         /*
944          * several attempts without lease attached:
945          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
946          * irrespective of file name provided
947          */
948
949         ZERO_STRUCT(io);
950         io.in.fname = "";
951         io.in.durable_handle = h;
952         status = smb2_create(tree, mem_ctx, &io);
953         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
954
955         ZERO_STRUCT(io);
956         io.in.fname = "__non_existing_fname__";
957         io.in.durable_handle = h;
958         status = smb2_create(tree, mem_ctx, &io);
959         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
960
961         ZERO_STRUCT(io);
962         io.in.fname = fname;
963         io.in.durable_handle = h;
964         status = smb2_create(tree, mem_ctx, &io);
965         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
966
967         /*
968          * attempt with lease provided, but
969          * with a changed lease key. => fails
970          */
971         ZERO_STRUCT(io);
972         io.in.fname = fname;
973         io.in.durable_open = false;
974         io.in.durable_handle = h;
975         io.in.lease_request = &ls;
976         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
977         /* a wrong lease key lets the request fail */
978         ls.lease_key.data[0]++;
979
980         status = smb2_create(tree, mem_ctx, &io);
981         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
982
983         /* restore the correct lease key */
984         ls.lease_key.data[0]--;
985
986         /*
987          * this last failing attempt is almost correct:
988          * only problem is: we use the wrong filename...
989          * Note that this gives INVALID_PARAMETER.
990          * This is different from oplocks!
991          */
992         ZERO_STRUCT(io);
993         io.in.fname = "__non_existing_fname__";
994         io.in.durable_open = false;
995         io.in.durable_handle = h;
996         io.in.lease_request = &ls;
997         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
998
999         status = smb2_create(tree, mem_ctx, &io);
1000         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1001
1002         /*
1003          * Now for a succeeding reconnect:
1004          */
1005
1006         ZERO_STRUCT(io);
1007         io.in.fname = fname;
1008         io.in.durable_open = false;
1009         io.in.durable_handle = h;
1010         io.in.lease_request = &ls;
1011         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1012
1013         /* the requested lease state is irrelevant */
1014         ls.lease_state = smb2_util_lease_state("");
1015
1016         h = NULL;
1017
1018         status = smb2_create(tree, mem_ctx, &io);
1019         CHECK_STATUS(status, NT_STATUS_OK);
1020
1021         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1022         CHECK_VAL(io.out.durable_open, false);
1023         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1024         CHECK_VAL(io.out.persistent_open, false);
1025         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1026         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1027         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1028         CHECK_VAL(io.out.lease_response.lease_state,
1029                   smb2_util_lease_state("RWH"));
1030         CHECK_VAL(io.out.lease_response.lease_flags, 0);
1031         CHECK_VAL(io.out.lease_response.lease_duration, 0);
1032         _h = io.out.file.handle;
1033         h = &_h;
1034
1035         /* disconnect one more time */
1036         TALLOC_FREE(tree);
1037
1038         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1039                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1040                 ret = false;
1041                 goto done;
1042         }
1043
1044         /*
1045          * demonstrate that various parameters are ignored
1046          * in the reconnect
1047          */
1048
1049         ZERO_STRUCT(io);
1050         /*
1051          * These are completely ignored by the server
1052          */
1053         io.in.security_flags = 0x78;
1054         io.in.oplock_level = 0x78;
1055         io.in.impersonation_level = 0x12345678;
1056         io.in.create_flags = 0x12345678;
1057         io.in.reserved = 0x12345678;
1058         io.in.desired_access = 0x12345678;
1059         io.in.file_attributes = 0x12345678;
1060         io.in.share_access = 0x12345678;
1061         io.in.create_disposition = 0x12345678;
1062         io.in.create_options = 0x12345678;
1063
1064         /*
1065          * only these are checked:
1066          * - io.in.fname
1067          * - io.in.durable_handle,
1068          * - io.in.lease_request->lease_key
1069          */
1070
1071         io.in.fname = fname;
1072         io.in.durable_open_v2 = false;
1073         io.in.durable_handle_v2 = h;
1074         io.in.lease_request = &ls;
1075
1076         /* the requested lease state is irrelevant */
1077         ls.lease_state = smb2_util_lease_state("");
1078
1079         h = NULL;
1080
1081         status = smb2_create(tree, mem_ctx, &io);
1082         CHECK_STATUS(status, NT_STATUS_OK);
1083
1084         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1085         CHECK_VAL(io.out.durable_open, false);
1086         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1087         CHECK_VAL(io.out.persistent_open, false);
1088         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1089         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1090         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1091         CHECK_VAL(io.out.lease_response.lease_state,
1092                   smb2_util_lease_state("RWH"));
1093         CHECK_VAL(io.out.lease_response.lease_flags, 0);
1094         CHECK_VAL(io.out.lease_response.lease_duration, 0);
1095
1096         _h = io.out.file.handle;
1097         h = &_h;
1098
1099 done:
1100         if (tree != NULL) {
1101                 if (h != NULL) {
1102                         smb2_util_close(tree, *h);
1103                 }
1104
1105                 smb2_util_unlink(tree, fname);
1106
1107                 talloc_free(tree);
1108         }
1109
1110         talloc_free(mem_ctx);
1111
1112         return ret;
1113 }
1114
1115 /**
1116  * lease v2 variant of reopen2
1117  * basic test for doing a durable open
1118  * tcp disconnect, reconnect, do a durable reopen (succeeds)
1119  */
1120 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
1121                                                struct smb2_tree *tree)
1122 {
1123         NTSTATUS status;
1124         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1125         char fname[256];
1126         struct smb2_handle _h;
1127         struct smb2_handle *h = NULL;
1128         struct smb2_create io;
1129         struct smb2_lease ls;
1130         uint64_t lease_key;
1131         bool ret = true;
1132         struct smbcli_options options;
1133         uint32_t caps;
1134
1135         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1136         if (!(caps & SMB2_CAP_LEASING)) {
1137                 torture_skip(tctx, "leases are not supported");
1138         }
1139
1140         options = tree->session->transport->options;
1141
1142         /* Choose a random name in case the state is left a little funky. */
1143         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1144                  generate_random_str(tctx, 8));
1145
1146         smb2_util_unlink(tree, fname);
1147
1148         lease_key = random();
1149         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1150                              lease_key, 0, /* parent lease key */
1151                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1152         io.in.durable_open = true;
1153
1154         status = smb2_create(tree, mem_ctx, &io);
1155         CHECK_STATUS(status, NT_STATUS_OK);
1156         _h = io.out.file.handle;
1157         h = &_h;
1158         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1159
1160         CHECK_VAL(io.out.durable_open, true);
1161         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1162         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1163         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1164         CHECK_VAL(io.out.lease_response_v2.lease_state,
1165                   smb2_util_lease_state("RWH"));
1166         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1167         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1168
1169         /* disconnect, reconnect and then do durable reopen */
1170         TALLOC_FREE(tree);
1171
1172         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1173                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1174                 ret = false;
1175                 goto done;
1176         }
1177
1178         /* a few failure tests: */
1179
1180         /*
1181          * several attempts without lease attached:
1182          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1183          * irrespective of file name provided
1184          */
1185
1186         ZERO_STRUCT(io);
1187         io.in.fname = "";
1188         io.in.durable_handle = h;
1189         status = smb2_create(tree, mem_ctx, &io);
1190         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1191
1192         ZERO_STRUCT(io);
1193         io.in.fname = "__non_existing_fname__";
1194         io.in.durable_handle = h;
1195         status = smb2_create(tree, mem_ctx, &io);
1196         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1197
1198         ZERO_STRUCT(io);
1199         io.in.fname = fname;
1200         io.in.durable_handle = h;
1201         status = smb2_create(tree, mem_ctx, &io);
1202         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1203
1204         /*
1205          * attempt with lease provided, but
1206          * with a changed lease key. => fails
1207          */
1208         ZERO_STRUCT(io);
1209         io.in.fname = fname;
1210         io.in.durable_open = false;
1211         io.in.durable_handle = h;
1212         io.in.lease_request_v2 = &ls;
1213         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1214         /* a wrong lease key lets the request fail */
1215         ls.lease_key.data[0]++;
1216
1217         status = smb2_create(tree, mem_ctx, &io);
1218         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1219
1220         /* restore the correct lease key */
1221         ls.lease_key.data[0]--;
1222
1223         /*
1224          * this last failing attempt is almost correct:
1225          * only problem is: we use the wrong filename...
1226          * Note that this gives INVALID_PARAMETER.
1227          * This is different from oplocks!
1228          */
1229         ZERO_STRUCT(io);
1230         io.in.fname = "__non_existing_fname__";
1231         io.in.durable_open = false;
1232         io.in.durable_handle = h;
1233         io.in.lease_request_v2 = &ls;
1234         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1235
1236         status = smb2_create(tree, mem_ctx, &io);
1237         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1238
1239         /*
1240          * Now for a succeeding reconnect:
1241          */
1242
1243         ZERO_STRUCT(io);
1244         io.in.fname = fname;
1245         io.in.durable_open = false;
1246         io.in.durable_handle = h;
1247         io.in.lease_request_v2 = &ls;
1248         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1249
1250         /* the requested lease state is irrelevant */
1251         ls.lease_state = smb2_util_lease_state("");
1252
1253         h = NULL;
1254
1255         status = smb2_create(tree, mem_ctx, &io);
1256         CHECK_STATUS(status, NT_STATUS_OK);
1257
1258         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1259         CHECK_VAL(io.out.durable_open, false);
1260         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1261         CHECK_VAL(io.out.persistent_open, false);
1262         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1263         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1264         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1265         CHECK_VAL(io.out.lease_response_v2.lease_state,
1266                   smb2_util_lease_state("RWH"));
1267         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1268         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1269         _h = io.out.file.handle;
1270         h = &_h;
1271
1272         /* disconnect one more time */
1273         TALLOC_FREE(tree);
1274
1275         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1276                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1277                 ret = false;
1278                 goto done;
1279         }
1280
1281         /*
1282          * demonstrate that various parameters are ignored
1283          * in the reconnect
1284          */
1285
1286         ZERO_STRUCT(io);
1287         /*
1288          * These are completely ignored by the server
1289          */
1290         io.in.security_flags = 0x78;
1291         io.in.oplock_level = 0x78;
1292         io.in.impersonation_level = 0x12345678;
1293         io.in.create_flags = 0x12345678;
1294         io.in.reserved = 0x12345678;
1295         io.in.desired_access = 0x12345678;
1296         io.in.file_attributes = 0x12345678;
1297         io.in.share_access = 0x12345678;
1298         io.in.create_disposition = 0x12345678;
1299         io.in.create_options = 0x12345678;
1300
1301         /*
1302          * only these are checked:
1303          * - io.in.fname
1304          * - io.in.durable_handle,
1305          * - io.in.lease_request->lease_key
1306          */
1307
1308         io.in.fname = fname;
1309         io.in.durable_open_v2 = false;
1310         io.in.durable_handle_v2 = h;
1311         io.in.lease_request_v2 = &ls;
1312
1313         /* the requested lease state is irrelevant */
1314         ls.lease_state = smb2_util_lease_state("");
1315
1316         h = NULL;
1317
1318         status = smb2_create(tree, mem_ctx, &io);
1319         CHECK_STATUS(status, NT_STATUS_OK);
1320
1321         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1322         CHECK_VAL(io.out.durable_open, false);
1323         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1324         CHECK_VAL(io.out.persistent_open, false);
1325         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1326         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1327         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1328         CHECK_VAL(io.out.lease_response_v2.lease_state,
1329                   smb2_util_lease_state("RWH"));
1330         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1331         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1332
1333         _h = io.out.file.handle;
1334         h = &_h;
1335
1336 done:
1337         if (tree != NULL) {
1338                 if (h != NULL) {
1339                         smb2_util_close(tree, *h);
1340                 }
1341
1342                 smb2_util_unlink(tree, fname);
1343
1344                 talloc_free(tree);
1345         }
1346
1347         talloc_free(mem_ctx);
1348
1349         return ret;
1350 }
1351
1352 /**
1353  * basic test for doing a durable open
1354  * tcp disconnect, reconnect with a session reconnect and
1355  * do a durable reopen (succeeds)
1356  */
1357 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1358                                        struct smb2_tree *tree)
1359 {
1360         NTSTATUS status;
1361         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1362         char fname[256];
1363         struct smb2_handle _h;
1364         struct smb2_handle *h = NULL;
1365         struct smb2_create io1, io2;
1366         uint64_t previous_session_id;
1367         bool ret = true;
1368         struct smbcli_options options;
1369
1370         options = tree->session->transport->options;
1371
1372         /* Choose a random name in case the state is left a little funky. */
1373         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1374                  generate_random_str(tctx, 8));
1375
1376         smb2_util_unlink(tree, fname);
1377
1378         smb2_oplock_create_share(&io1, fname,
1379                                  smb2_util_share_access(""),
1380                                  smb2_util_oplock_level("b"));
1381         io1.in.durable_open = true;
1382
1383         status = smb2_create(tree, mem_ctx, &io1);
1384         CHECK_STATUS(status, NT_STATUS_OK);
1385         _h = io1.out.file.handle;
1386         h = &_h;
1387         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1388         CHECK_VAL(io1.out.durable_open, true);
1389         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1390
1391         /* disconnect, reconnect and then do durable reopen */
1392         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1393         talloc_free(tree);
1394         tree = NULL;
1395
1396         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1397                                          &options, &tree))
1398         {
1399                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1400                 ret = false;
1401                 goto done;
1402         }
1403
1404         ZERO_STRUCT(io2);
1405         io2.in.fname = fname;
1406         io2.in.durable_handle = h;
1407         h = NULL;
1408
1409         status = smb2_create(tree, mem_ctx, &io2);
1410         CHECK_STATUS(status, NT_STATUS_OK);
1411         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1412         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1413         _h = io2.out.file.handle;
1414         h = &_h;
1415
1416 done:
1417         if (tree != NULL) {
1418                 if (h != NULL) {
1419                         smb2_util_close(tree, *h);
1420                 }
1421
1422                 smb2_util_unlink(tree, fname);
1423
1424                 talloc_free(tree);
1425         }
1426
1427         talloc_free(mem_ctx);
1428
1429         return ret;
1430 }
1431
1432
1433 /**
1434  * basic test for doing a durable open:
1435  * tdis, new tcon, try durable reopen (fails)
1436  */
1437 static bool test_durable_open_reopen3(struct torture_context *tctx,
1438                                       struct smb2_tree *tree)
1439 {
1440         NTSTATUS status;
1441         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1442         char fname[256];
1443         struct smb2_handle _h;
1444         struct smb2_handle *h = NULL;
1445         struct smb2_create io1, io2;
1446         bool ret = true;
1447         struct smb2_tree *tree2;
1448
1449         /* Choose a random name in case the state is left a little funky. */
1450         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1451                  generate_random_str(tctx, 8));
1452
1453         smb2_util_unlink(tree, fname);
1454
1455         smb2_oplock_create_share(&io1, fname,
1456                                  smb2_util_share_access(""),
1457                                  smb2_util_oplock_level("b"));
1458         io1.in.durable_open = true;
1459
1460         status = smb2_create(tree, mem_ctx, &io1);
1461         CHECK_STATUS(status, NT_STATUS_OK);
1462         _h = io1.out.file.handle;
1463         h = &_h;
1464         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1465         CHECK_VAL(io1.out.durable_open, true);
1466         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1467
1468         /* disconnect, reconnect and then do durable reopen */
1469         status = smb2_tdis(tree);
1470         CHECK_STATUS(status, NT_STATUS_OK);
1471
1472         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1473                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1474                 ret = false;
1475                 goto done;
1476         }
1477
1478
1479         ZERO_STRUCT(io2);
1480         io2.in.fname = fname;
1481         io2.in.durable_handle = h;
1482
1483         status = smb2_create(tree2, mem_ctx, &io2);
1484         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1485
1486 done:
1487         if (tree != NULL) {
1488                 if (h != NULL) {
1489                         smb2_util_close(tree, *h);
1490                 }
1491
1492                 smb2_util_unlink(tree2, fname);
1493
1494                 talloc_free(tree);
1495         }
1496
1497         talloc_free(mem_ctx);
1498
1499         return ret;
1500 }
1501
1502 /**
1503  * basic test for doing a durable open:
1504  * logoff, create a new session, do a durable reopen (succeeds)
1505  */
1506 static bool test_durable_open_reopen4(struct torture_context *tctx,
1507                                       struct smb2_tree *tree)
1508 {
1509         NTSTATUS status;
1510         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1511         char fname[256];
1512         struct smb2_handle _h;
1513         struct smb2_handle *h = NULL;
1514         struct smb2_create io1, io2;
1515         bool ret = true;
1516         struct smb2_transport *transport;
1517         struct smb2_session *session2;
1518         struct smb2_tree *tree2;
1519
1520         /* Choose a random name in case the state is left a little funky. */
1521         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1522                  generate_random_str(tctx, 8));
1523
1524         smb2_util_unlink(tree, fname);
1525
1526         smb2_oplock_create_share(&io1, fname,
1527                                  smb2_util_share_access(""),
1528                                  smb2_util_oplock_level("b"));
1529         io1.in.durable_open = true;
1530
1531         status = smb2_create(tree, mem_ctx, &io1);
1532         CHECK_STATUS(status, NT_STATUS_OK);
1533         _h = io1.out.file.handle;
1534         h = &_h;
1535         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1536         CHECK_VAL(io1.out.durable_open, true);
1537         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1538
1539         /*
1540          * do a session logoff, establish a new session and tree
1541          * connect on the same transport, and try a durable reopen
1542          */
1543         transport = tree->session->transport;
1544         status = smb2_logoff(tree->session);
1545         CHECK_STATUS(status, NT_STATUS_OK);
1546
1547         if (!torture_smb2_session_setup(tctx, transport,
1548                                         0, /* previous_session_id */
1549                                         mem_ctx, &session2))
1550         {
1551                 torture_warning(tctx, "session setup failed.\n");
1552                 ret = false;
1553                 goto done;
1554         }
1555
1556         /*
1557          * the session setup has talloc-stolen the transport,
1558          * so we can safely free the old tree+session for clarity
1559          */
1560         TALLOC_FREE(tree);
1561
1562         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1563                 torture_warning(tctx, "tree connect failed.\n");
1564                 ret = false;
1565                 goto done;
1566         }
1567
1568         ZERO_STRUCT(io2);
1569         io2.in.fname = fname;
1570         io2.in.durable_handle = h;
1571         h = NULL;
1572
1573         status = smb2_create(tree2, mem_ctx, &io2);
1574         CHECK_STATUS(status, NT_STATUS_OK);
1575
1576         _h = io2.out.file.handle;
1577         h = &_h;
1578         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1579         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1580
1581 done:
1582         if (tree != NULL) {
1583                 if (h != NULL) {
1584                         smb2_util_close(tree2, *h);
1585                 }
1586
1587                 smb2_util_unlink(tree2, fname);
1588
1589                 talloc_free(tree);
1590         }
1591
1592         talloc_free(mem_ctx);
1593
1594         return ret;
1595 }
1596
1597 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1598                                                struct smb2_tree *tree)
1599 {
1600         NTSTATUS status;
1601         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1602         char fname[256];
1603         struct smb2_handle _h;
1604         struct smb2_handle *h = NULL;
1605         struct smb2_create io1, io2;
1606         bool ret = true;
1607         uint8_t b = 0;
1608
1609         /* Choose a random name in case the state is left a little funky. */
1610         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1611                  generate_random_str(tctx, 8));
1612
1613         smb2_util_unlink(tree, fname);
1614
1615         smb2_oplock_create_share(&io1, fname,
1616                                  smb2_util_share_access(""),
1617                                  smb2_util_oplock_level("b"));
1618         io1.in.durable_open = true;
1619         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1620
1621         status = smb2_create(tree, mem_ctx, &io1);
1622         CHECK_STATUS(status, NT_STATUS_OK);
1623         _h = io1.out.file.handle;
1624         h = &_h;
1625         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1626         CHECK_VAL(io1.out.durable_open, true);
1627         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1628
1629         status = smb2_util_write(tree, *h, &b, 0, 1);
1630         CHECK_STATUS(status, NT_STATUS_OK);
1631
1632         /* disconnect, leaving the durable handle in place */
1633         TALLOC_FREE(tree);
1634
1635         if (!torture_smb2_connection(tctx, &tree)) {
1636                 torture_warning(tctx, "could not reconnect, bailing\n");
1637                 ret = false;
1638                 goto done;
1639         }
1640
1641         /*
1642          * Open the file on the new connection again
1643          * and check that it has been newly created,
1644          * i.e. delete on close was effective on the disconnected handle.
1645          * Also check that the file is really empty,
1646          * the previously written byte gone.
1647          */
1648         smb2_oplock_create_share(&io2, fname,
1649                                  smb2_util_share_access(""),
1650                                  smb2_util_oplock_level("b"));
1651         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1652
1653         status = smb2_create(tree, mem_ctx, &io2);
1654         CHECK_STATUS(status, NT_STATUS_OK);
1655         _h = io2.out.file.handle;
1656         h = &_h;
1657         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1658         CHECK_VAL(io2.out.durable_open, false);
1659         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1660
1661 done:
1662         if (tree != NULL) {
1663                 if (h != NULL) {
1664                         smb2_util_close(tree, *h);
1665                 }
1666
1667                 smb2_util_unlink(tree, fname);
1668
1669                 talloc_free(tree);
1670         }
1671
1672         talloc_free(mem_ctx);
1673
1674         return ret;
1675 }
1676
1677
1678 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1679                                                struct smb2_tree *tree)
1680 {
1681         NTSTATUS status;
1682         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1683         char fname[256];
1684         struct smb2_handle _h;
1685         struct smb2_handle *h = NULL;
1686         struct smb2_create io;
1687         bool ret = true;
1688         uint8_t b = 0;
1689         uint64_t previous_session_id;
1690         uint64_t alloc_size_step;
1691         struct smbcli_options options;
1692
1693         options = tree->session->transport->options;
1694
1695         /* Choose a random name in case the state is left a little funky. */
1696         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1697                  generate_random_str(tctx, 8));
1698
1699         smb2_util_unlink(tree, fname);
1700
1701         smb2_oplock_create_share(&io, fname,
1702                                  smb2_util_share_access(""),
1703                                  smb2_util_oplock_level("b"));
1704         io.in.durable_open = true;
1705         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1706
1707         status = smb2_create(tree, mem_ctx, &io);
1708         CHECK_STATUS(status, NT_STATUS_OK);
1709         _h = io.out.file.handle;
1710         h = &_h;
1711         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1712         CHECK_VAL(io.out.durable_open, true);
1713         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1714
1715         status = smb2_util_write(tree, *h, &b, 0, 1);
1716         CHECK_STATUS(status, NT_STATUS_OK);
1717
1718         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1719
1720         /* disconnect, leaving the durable handle in place */
1721         TALLOC_FREE(tree);
1722
1723         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1724                                          &options, &tree))
1725         {
1726                 torture_warning(tctx, "could not reconnect, bailing\n");
1727                 ret = false;
1728                 goto done;
1729         }
1730
1731         ZERO_STRUCT(io);
1732         io.in.fname = fname;
1733         io.in.durable_handle = h;
1734
1735         status = smb2_create(tree, mem_ctx, &io);
1736         CHECK_STATUS(status, NT_STATUS_OK);
1737         _h = io.out.file.handle;
1738         h = &_h;
1739         alloc_size_step = io.out.alloc_size;
1740         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1741         CHECK_VAL(io.out.durable_open, false);
1742         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1743
1744         /* close the file, thereby deleting it */
1745         smb2_util_close(tree, *h);
1746         status = smb2_logoff(tree->session);
1747         TALLOC_FREE(tree);
1748
1749         if (!torture_smb2_connection(tctx, &tree)) {
1750                 torture_warning(tctx, "could not reconnect, bailing\n");
1751                 ret = false;
1752                 goto done;
1753         }
1754
1755         /*
1756          * Open the file on the new connection again
1757          * and check that it has been newly created,
1758          * i.e. delete on close was effective on the reconnected handle.
1759          * Also check that the file is really empty,
1760          * the previously written byte gone.
1761          */
1762         smb2_oplock_create_share(&io, fname,
1763                                  smb2_util_share_access(""),
1764                                  smb2_util_oplock_level("b"));
1765         io.in.durable_open = true;
1766         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1767
1768         status = smb2_create(tree, mem_ctx, &io);
1769         CHECK_STATUS(status, NT_STATUS_OK);
1770         _h = io.out.file.handle;
1771         h = &_h;
1772         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1773         CHECK_VAL(io.out.durable_open, true);
1774         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1775
1776 done:
1777         if (tree != NULL) {
1778                 if (h != NULL) {
1779                         smb2_util_close(tree, *h);
1780                 }
1781
1782                 smb2_util_unlink(tree, fname);
1783
1784                 talloc_free(tree);
1785         }
1786
1787         talloc_free(mem_ctx);
1788
1789         return ret;
1790 }
1791
1792 /*
1793    basic testing of SMB2 durable opens
1794    regarding the position information on the handle
1795 */
1796 static bool test_durable_open_file_position(struct torture_context *tctx,
1797                                             struct smb2_tree *tree)
1798 {
1799         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1800         struct smb2_handle h;
1801         struct smb2_create io;
1802         NTSTATUS status;
1803         const char *fname = "durable_open_position.dat";
1804         union smb_fileinfo qfinfo;
1805         union smb_setfileinfo sfinfo;
1806         bool ret = true;
1807         uint64_t pos;
1808         uint64_t previous_session_id;
1809         struct smbcli_options options;
1810
1811         options = tree->session->transport->options;
1812
1813         smb2_util_unlink(tree, fname);
1814
1815         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1816         io.in.durable_open = true;
1817
1818         status = smb2_create(tree, mem_ctx, &io);
1819         CHECK_STATUS(status, NT_STATUS_OK);
1820         h = io.out.file.handle;
1821         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1822         CHECK_VAL(io.out.durable_open, true);
1823         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1824
1825         /* TODO: check extra blob content */
1826
1827         ZERO_STRUCT(qfinfo);
1828         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1829         qfinfo.generic.in.file.handle = h;
1830         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1831         CHECK_STATUS(status, NT_STATUS_OK);
1832         CHECK_VAL(qfinfo.position_information.out.position, 0);
1833         pos = qfinfo.position_information.out.position;
1834         torture_comment(tctx, "position: %llu\n",
1835                         (unsigned long long)pos);
1836
1837         ZERO_STRUCT(sfinfo);
1838         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1839         sfinfo.generic.in.file.handle = h;
1840         sfinfo.position_information.in.position = 0x1000;
1841         status = smb2_setinfo_file(tree, &sfinfo);
1842         CHECK_STATUS(status, NT_STATUS_OK);
1843
1844         ZERO_STRUCT(qfinfo);
1845         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1846         qfinfo.generic.in.file.handle = h;
1847         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1848         CHECK_STATUS(status, NT_STATUS_OK);
1849         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1850         pos = qfinfo.position_information.out.position;
1851         torture_comment(tctx, "position: %llu\n",
1852                         (unsigned long long)pos);
1853
1854         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1855
1856         /* tcp disconnect */
1857         talloc_free(tree);
1858         tree = NULL;
1859
1860         /* do a session reconnect */
1861         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1862                                          &options, &tree))
1863         {
1864                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1865                 ret = false;
1866                 goto done;
1867         }
1868
1869         ZERO_STRUCT(qfinfo);
1870         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1871         qfinfo.generic.in.file.handle = h;
1872         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1873         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1874
1875         ZERO_STRUCT(io);
1876         io.in.fname = fname;
1877         io.in.durable_handle = &h;
1878
1879         status = smb2_create(tree, mem_ctx, &io);
1880         CHECK_STATUS(status, NT_STATUS_OK);
1881         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1882         CHECK_VAL(io.out.reserved, 0x00);
1883         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1884         CHECK_VAL(io.out.alloc_size, 0);
1885         CHECK_VAL(io.out.size, 0);
1886         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1887         CHECK_VAL(io.out.reserved2, 0);
1888
1889         h = io.out.file.handle;
1890
1891         ZERO_STRUCT(qfinfo);
1892         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1893         qfinfo.generic.in.file.handle = h;
1894         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1895         CHECK_STATUS(status, NT_STATUS_OK);
1896         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1897         pos = qfinfo.position_information.out.position;
1898         torture_comment(tctx, "position: %llu\n",
1899                         (unsigned long long)pos);
1900
1901         smb2_util_close(tree, h);
1902
1903         talloc_free(mem_ctx);
1904
1905         smb2_util_unlink(tree, fname);
1906
1907 done:
1908         talloc_free(tree);
1909
1910         return ret;
1911 }
1912
1913 /*
1914   Open, disconnect, oplock break, reconnect.
1915 */
1916 static bool test_durable_open_oplock(struct torture_context *tctx,
1917                                      struct smb2_tree *tree1,
1918                                      struct smb2_tree *tree2)
1919 {
1920         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1921         struct smb2_create io1, io2;
1922         struct smb2_handle h1 = {{0}};
1923         struct smb2_handle h2 = {{0}};
1924         NTSTATUS status;
1925         char fname[256];
1926         bool ret = true;
1927
1928         /* Choose a random name in case the state is left a little funky. */
1929         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1930
1931         /* Clean slate */
1932         smb2_util_unlink(tree1, fname);
1933
1934         /* Create with batch oplock */
1935         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1936         io1.in.durable_open = true;
1937
1938         io2 = io1;
1939         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1940
1941         status = smb2_create(tree1, mem_ctx, &io1);
1942         CHECK_STATUS(status, NT_STATUS_OK);
1943         h1 = io1.out.file.handle;
1944         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1945         CHECK_VAL(io1.out.durable_open, true);
1946         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1947
1948         /* Disconnect after getting the batch */
1949         talloc_free(tree1);
1950         tree1 = NULL;
1951
1952         /*
1953          * Windows7 (build 7000) will break a batch oplock immediately if the
1954          * original client is gone. (ZML: This seems like a bug. It should give
1955          * some time for the client to reconnect!)
1956          */
1957         status = smb2_create(tree2, mem_ctx, &io2);
1958         CHECK_STATUS(status, NT_STATUS_OK);
1959         h2 = io2.out.file.handle;
1960         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1961         CHECK_VAL(io2.out.durable_open, true);
1962         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1963
1964         /* What if tree1 tries to come back and reclaim? */
1965         if (!torture_smb2_connection(tctx, &tree1)) {
1966                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1967                 ret = false;
1968                 goto done;
1969         }
1970
1971         ZERO_STRUCT(io1);
1972         io1.in.fname = fname;
1973         io1.in.durable_handle = &h1;
1974
1975         status = smb2_create(tree1, mem_ctx, &io1);
1976         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1977
1978  done:
1979         smb2_util_close(tree2, h2);
1980         smb2_util_unlink(tree2, fname);
1981
1982         talloc_free(tree1);
1983         talloc_free(tree2);
1984
1985         return ret;
1986 }
1987
1988 /*
1989   Open, disconnect, lease break, reconnect.
1990 */
1991 static bool test_durable_open_lease(struct torture_context *tctx,
1992                                     struct smb2_tree *tree1,
1993                                     struct smb2_tree *tree2)
1994 {
1995         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1996         struct smb2_create io1, io2;
1997         struct smb2_lease ls1, ls2;
1998         struct smb2_handle h1 = {{0}};
1999         struct smb2_handle h2 = {{0}};
2000         NTSTATUS status;
2001         char fname[256];
2002         bool ret = true;
2003         uint64_t lease1, lease2;
2004         uint32_t caps;
2005
2006         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2007         if (!(caps & SMB2_CAP_LEASING)) {
2008                 torture_skip(tctx, "leases are not supported");
2009         }
2010
2011         /*
2012          * Choose a random name and random lease in case the state is left a
2013          * little funky.
2014          */
2015         lease1 = random();
2016         lease2 = random();
2017         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
2018
2019         /* Clean slate */
2020         smb2_util_unlink(tree1, fname);
2021
2022         /* Create with lease */
2023         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
2024                           lease1, smb2_util_lease_state("RHW"));
2025         io1.in.durable_open = true;
2026
2027         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
2028                           lease2, smb2_util_lease_state("RHW"));
2029         io2.in.durable_open = true;
2030         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
2031
2032         status = smb2_create(tree1, mem_ctx, &io1);
2033         CHECK_STATUS(status, NT_STATUS_OK);
2034         h1 = io1.out.file.handle;
2035         CHECK_VAL(io1.out.durable_open, true);
2036         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2037
2038         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2039         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
2040         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
2041         CHECK_VAL(io1.out.lease_response.lease_state,
2042             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2043
2044         /* Disconnect after getting the lease */
2045         talloc_free(tree1);
2046         tree1 = NULL;
2047
2048         /*
2049          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
2050          * even if the original client is gone. (ZML: This seems like a bug. It
2051          * should give some time for the client to reconnect! And why RH?)
2052          * 
2053          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
2054          * Test is adapted accordingly.
2055          */
2056         status = smb2_create(tree2, mem_ctx, &io2);
2057         CHECK_STATUS(status, NT_STATUS_OK);
2058         h2 = io2.out.file.handle;
2059         CHECK_VAL(io2.out.durable_open, true);
2060         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2061
2062         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2063         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
2064         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
2065         CHECK_VAL(io2.out.lease_response.lease_state,
2066             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2067
2068         /* What if tree1 tries to come back and reclaim? */
2069         if (!torture_smb2_connection(tctx, &tree1)) {
2070                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2071                 ret = false;
2072                 goto done;
2073         }
2074
2075         ZERO_STRUCT(io1);
2076         io1.in.fname = fname;
2077         io1.in.durable_handle = &h1;
2078         io1.in.lease_request = &ls1;
2079
2080         status = smb2_create(tree1, mem_ctx, &io1);
2081         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2082
2083  done:
2084         smb2_util_close(tree2, h2);
2085         smb2_util_unlink(tree2, fname);
2086
2087         talloc_free(tree1);
2088         talloc_free(tree2);
2089
2090         return ret;
2091 }
2092
2093 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
2094                                           struct smb2_tree *tree)
2095 {
2096         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2097         struct smb2_create io;
2098         struct smb2_handle h = {{0}};
2099         struct smb2_lock lck;
2100         struct smb2_lock_element el[2];
2101         NTSTATUS status;
2102         char fname[256];
2103         bool ret = true;
2104
2105         /*
2106          */
2107         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
2108
2109         /* Clean slate */
2110         smb2_util_unlink(tree, fname);
2111
2112         /* Create with oplock */
2113
2114         smb2_oplock_create_share(&io, fname,
2115                                  smb2_util_share_access(""),
2116                                  smb2_util_oplock_level("b"));
2117         io.in.durable_open = true;
2118
2119         status = smb2_create(tree, mem_ctx, &io);
2120         CHECK_STATUS(status, NT_STATUS_OK);
2121         h = io.out.file.handle;
2122         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2123
2124         CHECK_VAL(io.out.durable_open, true);
2125         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2126
2127         ZERO_STRUCT(lck);
2128         ZERO_STRUCT(el);
2129         lck.in.locks            = el;
2130         lck.in.lock_count       = 0x0001;
2131         lck.in.lock_sequence    = 0x00000000;
2132         lck.in.file.handle      = h;
2133         el[0].offset            = 0;
2134         el[0].length            = 1;
2135         el[0].reserved          = 0x00000000;
2136         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
2137         status = smb2_lock(tree, &lck);
2138         CHECK_STATUS(status, NT_STATUS_OK);
2139
2140         /* Disconnect/Reconnect. */
2141         talloc_free(tree);
2142         tree = NULL;
2143
2144         if (!torture_smb2_connection(tctx, &tree)) {
2145                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2146                 ret = false;
2147                 goto done;
2148         }
2149
2150         ZERO_STRUCT(io);
2151         io.in.fname = fname;
2152         io.in.durable_handle = &h;
2153
2154         status = smb2_create(tree, mem_ctx, &io);
2155         CHECK_STATUS(status, NT_STATUS_OK);
2156         h = io.out.file.handle;
2157
2158         lck.in.file.handle      = h;
2159         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
2160         status = smb2_lock(tree, &lck);
2161         CHECK_STATUS(status, NT_STATUS_OK);
2162
2163  done:
2164         smb2_util_close(tree, h);
2165         smb2_util_unlink(tree, fname);
2166         talloc_free(tree);
2167
2168         return ret;
2169 }
2170
2171 /*
2172   Open, take BRL, disconnect, reconnect.
2173 */
2174 static bool test_durable_open_lock_lease(struct torture_context *tctx,
2175                                          struct smb2_tree *tree)
2176 {
2177         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2178         struct smb2_create io;
2179         struct smb2_lease ls;
2180         struct smb2_handle h = {{0}};
2181         struct smb2_lock lck;
2182         struct smb2_lock_element el[2];
2183         NTSTATUS status;
2184         char fname[256];
2185         bool ret = true;
2186         uint64_t lease;
2187         uint32_t caps;
2188         struct smbcli_options options;
2189
2190         options = tree->session->transport->options;
2191
2192         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2193         if (!(caps & SMB2_CAP_LEASING)) {
2194                 torture_skip(tctx, "leases are not supported");
2195         }
2196
2197         /*
2198          * Choose a random name and random lease in case the state is left a
2199          * little funky.
2200          */
2201         lease = random();
2202         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2203
2204         /* Clean slate */
2205         smb2_util_unlink(tree, fname);
2206
2207         /* Create with lease */
2208
2209         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2210                           smb2_util_lease_state("RWH"));
2211         io.in.durable_open              = true;
2212
2213         status = smb2_create(tree, mem_ctx, &io);
2214         CHECK_STATUS(status, NT_STATUS_OK);
2215         h = io.out.file.handle;
2216         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2217
2218         CHECK_VAL(io.out.durable_open, true);
2219         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2220         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2221         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2222         CHECK_VAL(io.out.lease_response.lease_state,
2223             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2224
2225         ZERO_STRUCT(lck);
2226         ZERO_STRUCT(el);
2227         lck.in.locks            = el;
2228         lck.in.lock_count       = 0x0001;
2229         lck.in.lock_sequence    = 0x00000000;
2230         lck.in.file.handle      = h;
2231         el[0].offset            = 0;
2232         el[0].length            = 1;
2233         el[0].reserved          = 0x00000000;
2234         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
2235         status = smb2_lock(tree, &lck);
2236         CHECK_STATUS(status, NT_STATUS_OK);
2237
2238         /* Disconnect/Reconnect. */
2239         talloc_free(tree);
2240         tree = NULL;
2241
2242         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2243                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2244                 ret = false;
2245                 goto done;
2246         }
2247
2248         ZERO_STRUCT(io);
2249         io.in.fname = fname;
2250         io.in.durable_handle = &h;
2251         io.in.lease_request = &ls;
2252
2253         status = smb2_create(tree, mem_ctx, &io);
2254         CHECK_STATUS(status, NT_STATUS_OK);
2255         h = io.out.file.handle;
2256
2257         lck.in.file.handle      = h;
2258         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
2259         status = smb2_lock(tree, &lck);
2260         CHECK_STATUS(status, NT_STATUS_OK);
2261
2262  done:
2263         smb2_util_close(tree, h);
2264         smb2_util_unlink(tree, fname);
2265         talloc_free(tree);
2266
2267         return ret;
2268 }
2269
2270 /**
2271  * Open with a RH lease, disconnect, open in another tree, reconnect.
2272  *
2273  * This test actually demonstrates a minimum level of respect for the durable
2274  * open in the face of another open. As long as this test shows an inability to
2275  * reconnect after an open, the oplock/lease tests above will certainly
2276  * demonstrate an error on reconnect.
2277  */
2278 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2279                                           struct smb2_tree *tree1,
2280                                           struct smb2_tree *tree2)
2281 {
2282         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2283         struct smb2_create io1, io2;
2284         struct smb2_lease ls;
2285         struct smb2_handle h1 = {{0}};
2286         struct smb2_handle h2 = {{0}};
2287         NTSTATUS status;
2288         char fname[256];
2289         bool ret = true;
2290         uint64_t lease;
2291         uint32_t caps;
2292         struct smbcli_options options;
2293
2294         options = tree1->session->transport->options;
2295
2296         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2297         if (!(caps & SMB2_CAP_LEASING)) {
2298                 torture_skip(tctx, "leases are not supported");
2299         }
2300
2301         /*
2302          * Choose a random name and random lease in case the state is left a
2303          * little funky.
2304          */
2305         lease = random();
2306         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2307                  generate_random_str(tctx, 8));
2308
2309         /* Clean slate */
2310         smb2_util_unlink(tree1, fname);
2311
2312         /* Create with lease */
2313         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2314                                 smb2_util_share_access(""),
2315                                 lease,
2316                                 smb2_util_lease_state("RH"));
2317         io1.in.durable_open = true;
2318
2319         status = smb2_create(tree1, mem_ctx, &io1);
2320         CHECK_STATUS(status, NT_STATUS_OK);
2321         h1 = io1.out.file.handle;
2322         CHECK_VAL(io1.out.durable_open, true);
2323         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2324
2325         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2326         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2327         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2328         CHECK_VAL(io1.out.lease_response.lease_state,
2329                   smb2_util_lease_state("RH"));
2330
2331         /* Disconnect */
2332         talloc_free(tree1);
2333         tree1 = NULL;
2334
2335         /* Open the file in tree2 */
2336         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2337
2338         status = smb2_create(tree2, mem_ctx, &io2);
2339         CHECK_STATUS(status, NT_STATUS_OK);
2340         h2 = io2.out.file.handle;
2341         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2342
2343         /* Reconnect */
2344         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2345                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2346                 ret = false;
2347                 goto done;
2348         }
2349
2350         ZERO_STRUCT(io1);
2351         io1.in.fname = fname;
2352         io1.in.durable_handle = &h1;
2353         io1.in.lease_request = &ls;
2354
2355         /*
2356          * Windows7 (build 7000) will give away an open immediately if the
2357          * original client is gone. (ZML: This seems like a bug. It should give
2358          * some time for the client to reconnect!)
2359          */
2360         status = smb2_create(tree1, mem_ctx, &io1);
2361         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2362         h1 = io1.out.file.handle;
2363
2364  done:
2365         if (tree1 != NULL){
2366                 smb2_util_close(tree1, h1);
2367                 smb2_util_unlink(tree1, fname);
2368                 talloc_free(tree1);
2369         }
2370
2371         smb2_util_close(tree2, h2);
2372         smb2_util_unlink(tree2, fname);
2373         talloc_free(tree2);
2374
2375         return ret;
2376 }
2377
2378 /**
2379  * Open with a batch oplock, disconnect, open in another tree, reconnect.
2380  *
2381  * This test actually demonstrates a minimum level of respect for the durable
2382  * open in the face of another open. As long as this test shows an inability to
2383  * reconnect after an open, the oplock/lease tests above will certainly
2384  * demonstrate an error on reconnect.
2385  */
2386 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2387                                            struct smb2_tree *tree1,
2388                                            struct smb2_tree *tree2)
2389 {
2390         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2391         struct smb2_create io1, io2;
2392         struct smb2_handle h1 = {{0}};
2393         struct smb2_handle h2 = {{0}};
2394         NTSTATUS status;
2395         char fname[256];
2396         bool ret = true;
2397
2398         /*
2399          * Choose a random name and random lease in case the state is left a
2400          * little funky.
2401          */
2402         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2403                  generate_random_str(tctx, 8));
2404
2405         /* Clean slate */
2406         smb2_util_unlink(tree1, fname);
2407
2408         /* Create with batch oplock */
2409         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2410         io1.in.durable_open = true;
2411
2412         status = smb2_create(tree1, mem_ctx, &io1);
2413         CHECK_STATUS(status, NT_STATUS_OK);
2414         h1 = io1.out.file.handle;
2415         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2416         CHECK_VAL(io1.out.durable_open, true);
2417         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2418
2419         /* Disconnect */
2420         talloc_free(tree1);
2421         tree1 = NULL;
2422
2423         /* Open the file in tree2 */
2424         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2425
2426         status = smb2_create(tree2, mem_ctx, &io2);
2427         CHECK_STATUS(status, NT_STATUS_OK);
2428         h2 = io2.out.file.handle;
2429         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2430
2431         /* Reconnect */
2432         if (!torture_smb2_connection(tctx, &tree1)) {
2433                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2434                 ret = false;
2435                 goto done;
2436         }
2437
2438         ZERO_STRUCT(io1);
2439         io1.in.fname = fname;
2440         io1.in.durable_handle = &h1;
2441
2442         status = smb2_create(tree1, mem_ctx, &io1);
2443         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2444         h1 = io1.out.file.handle;
2445
2446  done:
2447         smb2_util_close(tree2, h2);
2448         smb2_util_unlink(tree2, fname);
2449         if (tree1 != NULL) {
2450                 smb2_util_close(tree1, h1);
2451                 smb2_util_unlink(tree1, fname);
2452         }
2453
2454         talloc_free(tree1);
2455         talloc_free(tree2);
2456
2457         return ret;
2458 }
2459
2460 /**
2461  * test behaviour with initial allocation size
2462  */
2463 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2464                                          struct smb2_tree *tree)
2465 {
2466         NTSTATUS status;
2467         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2468         char fname[256];
2469         struct smb2_handle _h;
2470         struct smb2_handle *h = NULL;
2471         struct smb2_create io;
2472         bool ret = true;
2473         uint64_t previous_session_id;
2474         uint64_t alloc_size_step;
2475         uint64_t initial_alloc_size = 0x100;
2476         const uint8_t *b = NULL;
2477         struct smbcli_options options;
2478
2479         options = tree->session->transport->options;
2480
2481         /* Choose a random name in case the state is left a little funky. */
2482         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2483                  generate_random_str(tctx, 8));
2484
2485         smb2_util_unlink(tree, fname);
2486
2487         smb2_oplock_create_share(&io, fname,
2488                                  smb2_util_share_access(""),
2489                                  smb2_util_oplock_level("b"));
2490         io.in.durable_open = true;
2491         io.in.alloc_size = initial_alloc_size;
2492
2493         status = smb2_create(tree, mem_ctx, &io);
2494         CHECK_STATUS(status, NT_STATUS_OK);
2495         _h = io.out.file.handle;
2496         h = &_h;
2497         CHECK_NOT_VAL(io.out.alloc_size, 0);
2498         alloc_size_step = io.out.alloc_size;
2499         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2500                            alloc_size_step, 0);
2501         CHECK_VAL(io.out.durable_open, true);
2502         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2503
2504         /* prepare buffer */
2505         b = talloc_zero_size(mem_ctx, alloc_size_step);
2506         CHECK_NOT_NULL(b);
2507
2508         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2509
2510         /* disconnect, reconnect and then do durable reopen */
2511         talloc_free(tree);
2512         tree = NULL;
2513
2514         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2515                                          &options, &tree))
2516         {
2517                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2518                 ret = false;
2519                 goto done;
2520         }
2521
2522         ZERO_STRUCT(io);
2523         io.in.fname = fname;
2524         io.in.durable_handle = h;
2525         h = NULL;
2526
2527         status = smb2_create(tree, mem_ctx, &io);
2528         CHECK_STATUS(status, NT_STATUS_OK);
2529         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2530                            alloc_size_step, 0);
2531         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2532         _h = io.out.file.handle;
2533         h = &_h;
2534
2535         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2536
2537         /* write one byte */
2538         status = smb2_util_write(tree, *h, b, 0, 1);
2539         CHECK_STATUS(status, NT_STATUS_OK);
2540
2541         /* disconnect, reconnect and then do durable reopen */
2542         talloc_free(tree);
2543         tree = NULL;
2544
2545         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2546                                          &options, &tree))
2547         {
2548                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2549                 ret = false;
2550                 goto done;
2551         }
2552
2553         ZERO_STRUCT(io);
2554         io.in.fname = fname;
2555         io.in.durable_handle = h;
2556         h = NULL;
2557
2558         status = smb2_create(tree, mem_ctx, &io);
2559         CHECK_STATUS(status, NT_STATUS_OK);
2560         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2561                            alloc_size_step, 1);
2562         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2563         _h = io.out.file.handle;
2564         h = &_h;
2565
2566         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2567
2568         /* write more byte than initial allocation size */
2569         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2570
2571         /* disconnect, reconnect and then do durable reopen */
2572         talloc_free(tree);
2573         tree = NULL;
2574
2575         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2576                                          &options, &tree))
2577         {
2578                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2579                 ret = false;
2580                 goto done;
2581         }
2582
2583         ZERO_STRUCT(io);
2584         io.in.fname = fname;
2585         io.in.durable_handle = h;
2586         h = NULL;
2587
2588         status = smb2_create(tree, mem_ctx, &io);
2589         CHECK_STATUS(status, NT_STATUS_OK);
2590         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2591                            alloc_size_step * 2, alloc_size_step + 1);
2592         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2593         _h = io.out.file.handle;
2594         h = &_h;
2595
2596 done:
2597         if (h != NULL) {
2598                 smb2_util_close(tree, *h);
2599         }
2600
2601         smb2_util_unlink(tree, fname);
2602
2603         talloc_free(tree);
2604
2605         talloc_free(mem_ctx);
2606
2607         return ret;
2608 }
2609
2610 /**
2611  * test behaviour when a disconnect happens while creating a read-only file
2612  */
2613 static bool test_durable_open_read_only(struct torture_context *tctx,
2614                                         struct smb2_tree *tree)
2615 {
2616         NTSTATUS status;
2617         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2618         char fname[256];
2619         struct smb2_handle _h;
2620         struct smb2_handle *h = NULL;
2621         struct smb2_create io;
2622         bool ret = true;
2623         uint64_t previous_session_id;
2624         const uint8_t b = 0;
2625         uint64_t alloc_size = 0;
2626         struct smbcli_options options;
2627
2628         options = tree->session->transport->options;
2629
2630         /* Choose a random name in case the state is left a little funky. */
2631         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2632                  generate_random_str(tctx, 8));
2633
2634         smb2_util_unlink(tree, fname);
2635
2636         smb2_oplock_create_share(&io, fname,
2637                                  smb2_util_share_access(""),
2638                                  smb2_util_oplock_level("b"));
2639         io.in.durable_open = true;
2640         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2641
2642         status = smb2_create(tree, mem_ctx, &io);
2643         CHECK_STATUS(status, NT_STATUS_OK);
2644         _h = io.out.file.handle;
2645         h = &_h;
2646         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2647         CHECK_VAL(io.out.durable_open, true);
2648         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2649
2650         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2651
2652         /* write one byte */
2653         status = smb2_util_write(tree, *h, &b, 0, 1);
2654         CHECK_STATUS(status, NT_STATUS_OK);
2655
2656         /* disconnect, reconnect and then do durable reopen */
2657         talloc_free(tree);
2658         tree = NULL;
2659
2660         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2661                                          &options, &tree))
2662         {
2663                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2664                 ret = false;
2665                 goto done;
2666         }
2667
2668         ZERO_STRUCT(io);
2669         io.in.fname = fname;
2670         io.in.durable_handle = h;
2671         h = NULL;
2672
2673         status = smb2_create(tree, mem_ctx, &io);
2674         CHECK_STATUS(status, NT_STATUS_OK);
2675         alloc_size = io.out.alloc_size;
2676         CHECK_CREATED_SIZE(&io, EXISTED,
2677                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2678                            alloc_size, 1);
2679         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2680         _h = io.out.file.handle;
2681         h = &_h;
2682
2683         /* write one byte */
2684         status = smb2_util_write(tree, *h, &b, 1, 1);
2685         CHECK_STATUS(status, NT_STATUS_OK);
2686
2687 done:
2688         if (h != NULL) {
2689                 union smb_setfileinfo sfinfo;
2690
2691                 ZERO_STRUCT(sfinfo);
2692                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2693                 sfinfo.basic_info.in.file.handle = *h;
2694                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2695                 smb2_setinfo_file(tree, &sfinfo);
2696
2697                 smb2_util_close(tree, *h);
2698         }
2699
2700         smb2_util_unlink(tree, fname);
2701
2702         talloc_free(tree);
2703
2704         talloc_free(mem_ctx);
2705
2706         return ret;
2707 }
2708
2709 /**
2710  * durable open with oplock, disconnect, exit
2711  */
2712 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2713                                                 struct smb2_tree *tree)
2714 {
2715         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2716         struct smb2_create io;
2717         struct smb2_handle _h;
2718         struct smb2_handle *h = NULL;
2719         NTSTATUS status;
2720         char fname[256];
2721         bool ret = true;
2722
2723         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2724                  generate_random_str(mem_ctx, 8));
2725
2726         smb2_util_unlink(tree, fname);
2727
2728         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2729         io.in.durable_open = true;
2730
2731         status = smb2_create(tree, mem_ctx, &io);
2732         CHECK_STATUS(status, NT_STATUS_OK);
2733
2734         _h = io.out.file.handle;
2735         h = &_h;
2736
2737         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2738         CHECK_VAL(io.out.durable_open, true);
2739         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2740
2741         /* disconnect */
2742         talloc_free(tree);
2743         tree = NULL;
2744
2745 done:
2746         if (tree != NULL) {
2747                 if (h != NULL) {
2748                         smb2_util_close(tree, *h);
2749                 }
2750                 smb2_util_unlink(tree, fname);
2751         }
2752         talloc_free(mem_ctx);
2753         return ret;
2754 }
2755
2756 #define COMPARE_TIME_CMP(given, gelem, correct, celem, cmp) do { \
2757         const uint64_t _r = 10*1000*1000; \
2758         NTTIME _g = (given).basic_info.out.gelem; \
2759         NTTIME _gr = (_g / _r) * _r; \
2760         NTTIME _c = (correct).basic_info.out.celem; \
2761         NTTIME _cr = (_c / _r) * _r; \
2762         bool _strict = torture_setting_bool(tctx, "strict mode", false); \
2763         const char *_err = NULL; \
2764         if (_strict && (_g cmp _c)) { \
2765                 _err = "strict"; \
2766         } else if ((_g cmp _c) && (_gr cmp _cr)) { \
2767                 /* handle filesystem without high resolution timestamps */ \
2768                 _err = "rounded"; \
2769         } \
2770         if (_err != NULL) { \
2771                 struct timeval _gtv; \
2772                 struct timeval _ctv; \
2773                 struct timeval_buf _gtvb; \
2774                 struct timeval_buf _ctvb; \
2775                 nttime_to_timeval(&_gtv, _g); \
2776                 nttime_to_timeval(&_ctv, _c); \
2777                 torture_result(tctx, TORTURE_FAIL, \
2778                                __location__": %s wrong (%s.%s)%s %s (%s.%s)%s", \
2779                                _err, \
2780                                #given, #gelem, \
2781                                timeval_str_buf(&_gtv, false, true, &_gtvb), \
2782                                #cmp, \
2783                                #correct, #celem, \
2784                                timeval_str_buf(&_ctv, false, true, &_ctvb)); \
2785                 ret = false; \
2786                 goto done; \
2787         } \
2788 } while (0)
2789 #define COMPARE_WRITE_TIME_CMP(given, correct, cmp) do { \
2790         COMPARE_TIME_CMP(given, write_time, correct, write_time, cmp); \
2791 } while (0)
2792 #define COMPARE_WRITE_TIME_EQUAL(given,correct) \
2793         COMPARE_WRITE_TIME_CMP(given,correct,!=)
2794 #define COMPARE_WRITE_TIME_GREATER(given,correct) \
2795         COMPARE_WRITE_TIME_CMP(given,correct,<=)
2796
2797 #define COMPARE_ACCESS_TIME_CMP(given, correct, cmp) do { \
2798         COMPARE_TIME_CMP(given, access_time, correct, access_time, cmp); \
2799 } while (0)
2800 #define COMPARE_ACCESS_TIME_EQUAL(given,correct) \
2801         COMPARE_ACCESS_TIME_CMP(given,correct,!=)
2802 #define COMPARE_ACCESS_TIME_GREATER(given,correct) \
2803         COMPARE_ACCESS_TIME_CMP(given,correct,<=)
2804
2805 #define COMPARE_CHANGE_TIME_CMP(given, correct, cmp) do { \
2806         COMPARE_TIME_CMP(given, change_time, correct, change_time, cmp); \
2807 } while (0)
2808 #define COMPARE_CHANGE_TIME_EQUAL(given,correct) \
2809         COMPARE_CHANGE_TIME_CMP(given,correct,!=)
2810 #define COMPARE_CHANGE_TIME_GREATER(given,correct) \
2811         COMPARE_CHANGE_TIME_CMP(given,correct,<=)
2812
2813 #define COMPARE_CREATE_TIME_CMP(given, correct, cmp) do { \
2814         COMPARE_TIME_CMP(given, create_time, correct, create_time, cmp); \
2815 } while (0)
2816 #define COMPARE_CREATE_TIME_EQUAL(given,correct) \
2817         COMPARE_CREATE_TIME_CMP(given,correct,!=)
2818
2819 #define COMPARE_ALL_TIMES_EQUAL(given,correct) do { \
2820         COMPARE_WRITE_TIME_EQUAL(given,correct); \
2821         COMPARE_CHANGE_TIME_EQUAL(given,correct); \
2822         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
2823         COMPARE_CREATE_TIME_EQUAL(given,correct); \
2824 } while (0)
2825
2826 #define COMPARE_TIMES_AFTER_WRITE(given,correct) do { \
2827         COMPARE_WRITE_TIME_GREATER(given,correct); \
2828         COMPARE_CHANGE_TIME_GREATER(given,correct); \
2829         COMPARE_ACCESS_TIME_EQUAL(given,correct); \
2830         COMPARE_CREATE_TIME_EQUAL(given,correct); \
2831         COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
2832 } while (0)
2833
2834 #define COMPARE_TIMES_AFTER_CLOSE(given,correct) do { \
2835         COMPARE_WRITE_TIME_GREATER(given,correct); \
2836         COMPARE_CHANGE_TIME_GREATER(given,correct); \
2837         COMPARE_ACCESS_TIME_GREATER(given,correct); \
2838         COMPARE_CREATE_TIME_EQUAL(given,correct); \
2839         COMPARE_TIME_CMP(given, change_time, given, write_time, !=); \
2840         COMPARE_TIME_CMP(given, access_time, given, write_time, !=); \
2841 } while (0)
2842
2843 #define GET_INFO_FILE(tree, finfo) do { \
2844         struct timeval _atv; \
2845         struct timeval _wtv; \
2846         struct timeval_buf _atvb; \
2847         struct timeval_buf _wtvb; \
2848         NTSTATUS _status; \
2849         _status = smb2_getinfo_file(tree, tctx, &finfo); \
2850         if (!NT_STATUS_IS_OK(_status)) { \
2851                 ret = false; \
2852                 torture_result(tctx, TORTURE_FAIL, __location__": fileinfo failed: %s", \
2853                                nt_errstr(_status)); \
2854                 goto done; \
2855         } \
2856         nttime_to_timeval(&_atv, finfo.basic_info.out.access_time); \
2857         nttime_to_timeval(&_wtv, finfo.basic_info.out.write_time); \
2858         torture_comment(tctx, "fileinfo(%s,%s): Access(%s) Write(%s)\n", \
2859                         #tree, #finfo, \
2860                         timeval_str_buf(&_atv, false, true, &_atvb), \
2861                         timeval_str_buf(&_wtv, false, true, &_wtvb)); \
2862 } while (0)
2863
2864 #define GET_INFO_BOTH(finfo1, finfo2) do { \
2865         GET_INFO_FILE(tree2, finfo2); \
2866         GET_INFO_FILE(tree1, finfo1); \
2867         COMPARE_ALL_TIMES_EQUAL(finfo1, finfo2); \
2868 } while (0)
2869
2870 #define SET_INFO_FILE_EX(finfo, wrtime, tree, tfnum) do { \
2871         NTSTATUS _status; \
2872         union smb_setfileinfo sfinfo; \
2873         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
2874         sfinfo.basic_info.in.file.fnum = tfnum; \
2875         sfinfo.basic_info.in.create_time = 0; \
2876         sfinfo.basic_info.in.access_time = 0; \
2877         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
2878         sfinfo.basic_info.in.change_time = 0; \
2879         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
2880         _status = smb_raw_setfileinfo(tree, &sfinfo); \
2881         if (!NT_STATUS_IS_OK(_status)) { \
2882                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
2883                                nt_errstr(_status)); \
2884                 ret = false; \
2885                 goto done; \
2886         } \
2887 } while (0)
2888 #define SET_INFO_FILE(finfo, wrtime) \
2889         SET_INFO_FILE_EX(finfo, wrtime, cli->tree, fnum1)
2890
2891 #define SET_INFO_FILE_NS(finfo, wrtime, ns, tree, tfnum) do { \
2892         NTSTATUS _status; \
2893         union smb_setfileinfo sfinfo; \
2894         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFO; \
2895         sfinfo.basic_info.in.file.fnum = tfnum; \
2896         sfinfo.basic_info.in.create_time = 0; \
2897         sfinfo.basic_info.in.access_time = 0; \
2898         unix_to_nt_time(&sfinfo.basic_info.in.write_time, (wrtime)); \
2899         sfinfo.basic_info.in.write_time += (ns); \
2900         sfinfo.basic_info.in.change_time = 0; \
2901         sfinfo.basic_info.in.attrib = finfo1.basic_info.out.attrib; \
2902         _status = smb_raw_setfileinfo(tree, &sfinfo); \
2903         if (!NT_STATUS_IS_OK(_status)) { \
2904                 torture_result(tctx, TORTURE_FAIL, __location__": setfileinfo failed: %s", \
2905                                nt_errstr(_status)); \
2906                 ret = false; \
2907                 goto done; \
2908         } \
2909 } while (0)
2910
2911 static bool test_delay_writetime(struct torture_context *tctx,
2912                                  double used_delay,
2913                                  double normal_delay,
2914                                  const char *description,
2915                                  bool (*get_basic_info_cb)(void *private_data,
2916                                                            union smb_fileinfo *finfo),
2917                                  bool (*write_data_cb)(void *private_data),
2918                                  void *private_data)
2919 {
2920         union smb_fileinfo finfo0, finfo1, finfo2, finfo3, finfoT;
2921         struct timeval before_write;
2922         struct timeval after_write;
2923         struct timeval before_last_write;
2924         struct timeval after_last_write;
2925         struct timeval start;
2926         struct timeval end;
2927         double sec = used_delay / normal_delay;
2928         double msec = 1000 * sec;
2929         double nsec = 1000 * msec;
2930         double expected_delay = used_delay / nsec;
2931         double min_delay = expected_delay * 0.01;
2932         double max_delay = 5 * expected_delay;
2933         bool ret = true;
2934         bool ok;
2935
2936         torture_comment(tctx, "START: %s\n", description);
2937
2938         /* get the initial times */
2939         ok = get_basic_info_cb(private_data, &finfo0);
2940         torture_assert(tctx, ok, "get_basic_info_cb: finfo0");
2941
2942         /*
2943          * Make sure the time doesn't change during the next 5 seconds
2944          */
2945         start = timeval_current();
2946         end = timeval_add(&start, max_delay * 1.25, 0);
2947         while (!timeval_expired(&end)) {
2948                 smb_msleep(1 * msec);
2949                 torture_comment(tctx, "Check for no change\n");
2950                 ok = get_basic_info_cb(private_data, &finfoT);
2951                 torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
2952                 COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
2953         }
2954
2955         ok = get_basic_info_cb(private_data, &finfoT);
2956         torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
2957         COMPARE_WRITE_TIME_EQUAL(finfoT, finfo0);
2958
2959         torture_comment(tctx, "Do a write on the file handle\n");
2960         before_write = timeval_current();
2961         ok = write_data_cb(private_data);
2962         after_write = timeval_current();
2963         torture_assert(tctx, ok, "write_data_cb");
2964
2965         start = timeval_current();
2966         end = timeval_add(&start, max_delay * 2, 0);
2967         while (!timeval_expired(&end)) {
2968                 struct timeval before_get;
2969                 struct timeval after_get;
2970
2971                 torture_comment(tctx, "Wait for change\n");
2972                 before_get = timeval_current();
2973                 ok = get_basic_info_cb(private_data, &finfoT);
2974                 after_get = timeval_current();
2975                 torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
2976
2977                 if (finfoT.basic_info.out.write_time > finfo0.basic_info.out.write_time) {
2978                         double delayS = timeval_elapsed2(&after_write, &before_get);
2979                         double delayL = timeval_elapsed2(&before_write, &after_get);
2980
2981                         torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
2982                                         "(min delay == %.2f, max delay == %.2f)\n",
2983                                         delayS, delayL, min_delay, max_delay);
2984                         torture_assert(tctx, (delayL >= min_delay),
2985                                        "Server updated write_time to early!");
2986                         torture_assert(tctx, (delayS <= max_delay),
2987                                        "Server didn't update write_time!");
2988
2989                         COMPARE_TIMES_AFTER_WRITE(finfoT, finfo0);
2990                         break;
2991                 }
2992
2993                 COMPARE_ALL_TIMES_EQUAL(finfoT, finfo0);
2994                 smb_msleep(0.01 * msec);
2995         }
2996
2997         ok = get_basic_info_cb(private_data, &finfo1);
2998         torture_assert(tctx, ok, "get_basic_info_cb: finfo1");
2999         COMPARE_TIMES_AFTER_WRITE(finfo1, finfo0);
3000
3001         /*
3002          * Make sure the time doesn't change during the next 5 seconds
3003          */
3004         start = timeval_current();
3005         end = timeval_add(&start, max_delay * 1.25, 0);
3006         while (!timeval_expired(&end)) {
3007                 smb_msleep(1 * msec);
3008                 torture_comment(tctx, "Check for no additional change\n");
3009                 ok = get_basic_info_cb(private_data, &finfoT);
3010                 torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
3011                 COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
3012         }
3013
3014         ok = get_basic_info_cb(private_data, &finfoT);
3015         torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
3016         COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
3017
3018         torture_comment(tctx, "Do a write on the file handle\n");
3019         before_write = timeval_current();
3020         ok = write_data_cb(private_data);
3021         after_write = timeval_current();
3022         torture_assert(tctx, ok, "write_data_cb");
3023
3024         ZERO_STRUCT(finfo2);
3025         before_last_write = before_write;
3026         after_last_write = after_write;
3027         start = timeval_current();
3028         end = timeval_add(&start, max_delay * 2, 0);
3029         while (!timeval_expired(&end)) {
3030                 struct timeval before_get;
3031                 struct timeval after_get;
3032
3033                 smb_msleep(0.01 * msec);
3034
3035                 torture_comment(tctx, "Wait for change\n");
3036                 before_get = timeval_current();
3037                 ok = get_basic_info_cb(private_data, &finfoT);
3038                 after_get = timeval_current();
3039                 torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
3040
3041                 if (finfoT.basic_info.out.write_time > finfo1.basic_info.out.write_time) {
3042                         double delayS = timeval_elapsed2(&after_write, &before_get);
3043                         double delayL = timeval_elapsed2(&before_write, &after_get);
3044
3045                         torture_comment(tctx, "Server updated write_time after %.2f/%.2f seconds "
3046                                         "(min delay == %.2f, max delay == %.2f)\n",
3047                                         delayS, delayL, min_delay, max_delay);
3048                         torture_assert(tctx, (delayL >= min_delay),
3049                                        "Server updated write_time to early!");
3050                         torture_assert(tctx, (delayS <= max_delay),
3051                                        "Server didn't update write_time!");
3052
3053                         COMPARE_TIMES_AFTER_WRITE(finfoT, finfo1);
3054                         before_write = before_last_write;
3055                         after_write = after_last_write;
3056                         finfo2 = finfoT;
3057                         break;
3058                 }
3059
3060                 COMPARE_ALL_TIMES_EQUAL(finfoT, finfo1);
3061
3062                 torture_comment(tctx, "Write while waiting\n");
3063                 before_last_write = timeval_current();
3064                 ok = write_data_cb(private_data);
3065                 after_last_write = timeval_current();
3066                 torture_assert(tctx, ok, "write_data_cb");
3067         }
3068
3069         // We may get one additional change...
3070
3071         ok = get_basic_info_cb(private_data, &finfo3);
3072         torture_assert(tctx, ok, "get_basic_info_cb: finfo3");
3073         COMPARE_ALL_TIMES_EQUAL(finfo3, finfo2);
3074
3075         /*
3076          * Make sure the time doesn't change during the next 5 seconds
3077          */
3078         start = timeval_current();
3079         end = timeval_add(&start, max_delay * 1.25, 0);
3080         while (!timeval_expired(&end)) {
3081                 smb_msleep(1 * msec);
3082                 torture_comment(tctx, "Check for no additional change\n");
3083                 ok = get_basic_info_cb(private_data, &finfoT);
3084                 torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
3085                 COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
3086         }
3087
3088         ok = get_basic_info_cb(private_data, &finfoT);
3089         torture_assert(tctx, ok, "get_basic_info_cb: finfoT");
3090         COMPARE_ALL_TIMES_EQUAL(finfoT, finfo3);
3091
3092 done:
3093         return ret;
3094 }
3095
3096 struct test_durable_open_delaywrite1_state {
3097         struct torture_context *tctx;
3098         struct smb2_tree *tree1;
3099         struct smb2_tree *tree2;
3100         struct smb2_handle *h1;
3101         struct smb2_handle *h2;
3102 };
3103
3104 static bool test_durable_open_delaywrite1_get_info(void *private_data,
3105                                                    union smb_fileinfo *finfo)
3106 {
3107         struct test_durable_open_delaywrite1_state *state =
3108                 (struct test_durable_open_delaywrite1_state *)private_data;
3109         struct torture_context *tctx = state->tctx;
3110         union smb_fileinfo t1finfo;
3111         union smb_fileinfo t2finfo;
3112         bool ret = true;
3113
3114         ZERO_STRUCTP(finfo);
3115
3116         ZERO_STRUCT(t1finfo);
3117         t1finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3118         t1finfo.basic_info.in.file.handle = *state->h1;
3119
3120         ZERO_STRUCT(t2finfo);
3121         t2finfo.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3122         t2finfo.basic_info.in.file.handle = *state->h2;
3123
3124         GET_INFO_FILE(state->tree2, t2finfo);
3125         GET_INFO_FILE(state->tree1, t1finfo);
3126         if (t1finfo.basic_info.out.write_time != t2finfo.basic_info.out.write_time) {
3127                 /*
3128                  * There was a race, get it again on handle 2,
3129                  * but then they have to match.
3130                  */
3131                 GET_INFO_FILE(state->tree2, t2finfo);
3132         }
3133         COMPARE_ALL_TIMES_EQUAL(t1finfo, t2finfo);
3134
3135         finfo->basic_info.out = t1finfo.basic_info.out;
3136 done:
3137         return ret;
3138 }
3139
3140 static bool test_durable_open_delaywrite1_write_data(void *private_data)
3141 {
3142         struct test_durable_open_delaywrite1_state *state =
3143                 (struct test_durable_open_delaywrite1_state *)private_data;
3144         struct torture_context *tctx = state->tctx;
3145         struct smb2_write wr;
3146         NTSTATUS status;
3147         bool ret = true;
3148
3149         ZERO_STRUCT(wr);
3150         wr.in.file.handle = *state->h1;
3151         wr.in.offset      = 0;
3152         wr.in.data        = data_blob_const("x", 1);
3153         status = smb2_write(state->tree1, &wr);
3154         CHECK_STATUS(status, NT_STATUS_OK);
3155         torture_assert_int_equal_goto(tctx, wr.out.nwritten, 1,
3156                                       ret, done, "smb2_write");
3157
3158 done:
3159         return ret;
3160 }
3161
3162 static bool test_durable_open_delaywrite1(struct torture_context *tctx,
3163                                           struct smb2_tree *tree1,
3164                                           struct smb2_tree *tree2)
3165 {
3166         struct test_durable_open_delaywrite1_state state = {
3167                 .tctx = tctx,
3168                 .tree1 = tree1,
3169                 .tree2 = tree2,
3170         };
3171         NTSTATUS status;
3172         TALLOC_CTX *mem_ctx = talloc_new(tctx);
3173         char fname[256];
3174         struct smb2_handle _h1;
3175         struct smb2_handle *h1 = NULL;
3176         struct smb2_handle _h2;
3177         struct smb2_handle *h2 = NULL;
3178         struct smb2_create cr1;
3179         struct smb2_create cr2;
3180         union smb_fileinfo c1finfoCR, c1finfo0, c1finfo1, c1finfo2, c1finfoCL;
3181         union smb_fileinfo c2finfoCR, c2finfo0, c2finfo1, c2finfo2, c2finfo3, c2finfoCL;
3182         struct smb2_close cl1;
3183         struct smb2_close cl2;
3184         //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 2000000);
3185         //double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", 1000000);
3186         double normal_delay = 1000000;
3187         double used_delay = torture_setting_int(tctx, "writetimeupdatedelay", normal_delay);
3188         //double normal_delay = 1000000;
3189         //int normal_delay = 2000000;
3190         double sec = ((double)used_delay) / ((double)normal_delay);
3191         int msec = 1000 * sec;
3192         bool ret = true;
3193         bool ok;
3194
3195         /* Choose a random name in case the state is left a little funky. */
3196         snprintf(fname, 256, "durable_open_delaywrite1_%s.dat",
3197                  generate_random_str(tctx, 8));
3198
3199         smb2_util_unlink(tree1, fname);
3200
3201         smb2_oplock_create_share(&cr1, fname,
3202                                  smb2_util_share_access(""),
3203                                  smb2_util_oplock_level("b"));
3204         cr1.in.durable_open = true;
3205
3206         status = smb2_create(tree1, mem_ctx, &cr1);
3207         CHECK_STATUS(status, NT_STATUS_OK);
3208         _h1 = cr1.out.file.handle;
3209         h1 = &_h1;
3210         CHECK_CREATED(&cr1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3211         CHECK_VAL(cr1.out.oplock_level, smb2_util_oplock_level("b"));
3212         CHECK_VAL(cr1.out.durable_open, true);
3213         CHECK_VAL(cr1.out.durable_open_v2, false);
3214         CHECK_VAL(cr1.out.persistent_open, false);
3215
3216         cr2 = cr1;
3217         cr2.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
3218         cr2.in.durable_open = false;
3219         cr2.in.oplock_level = 0;
3220         cr2.in.create_disposition = NTCREATEX_DISP_OPEN;
3221         status = smb2_create(tree2, mem_ctx, &cr2);
3222         CHECK_STATUS(status, NT_STATUS_OK);
3223         _h2 = cr2.out.file.handle;
3224         h2 = &_h2;
3225         CHECK_CREATED(&cr2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3226         CHECK_VAL(cr2.out.oplock_level, 0);
3227         CHECK_VAL(cr2.out.durable_open, false);
3228         CHECK_VAL(cr2.out.durable_open_v2, false);
3229         CHECK_VAL(cr2.out.persistent_open, false);
3230
3231         ZERO_STRUCT(c1finfoCR);
3232         c1finfoCR.basic_info.out.create_time = cr1.out.create_time;
3233         c1finfoCR.basic_info.out.access_time = cr1.out.access_time;
3234         c1finfoCR.basic_info.out.write_time = cr1.out.write_time;
3235         c1finfoCR.basic_info.out.change_time = cr1.out.change_time;
3236         c1finfoCR.basic_info.out.attrib = cr1.out.file_attr;
3237
3238         ZERO_STRUCT(c2finfoCR);
3239         c2finfoCR.basic_info.out.create_time = cr2.out.create_time;
3240         c2finfoCR.basic_info.out.access_time = cr2.out.access_time;
3241         c2finfoCR.basic_info.out.write_time = cr2.out.write_time;
3242         c2finfoCR.basic_info.out.change_time = cr2.out.change_time;
3243         c2finfoCR.basic_info.out.attrib = cr2.out.file_attr;
3244
3245         COMPARE_ALL_TIMES_EQUAL(c1finfoCR, c2finfoCR);
3246
3247         ZERO_STRUCT(c1finfo0);
3248         c1finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3249         c1finfo0.basic_info.in.file.handle = *h1;
3250         c1finfo1 = c1finfo0;
3251         c1finfo2 = c1finfo0;
3252
3253         ZERO_STRUCT(c2finfo0);
3254         c2finfo0.basic_info.level = RAW_FILEINFO_BASIC_INFORMATION;
3255         c2finfo0.basic_info.in.file.handle = *h2;
3256         c2finfo1 = c2finfo0;
3257         c2finfo2 = c2finfo0;
3258         c2finfo3 = c2finfo0;
3259
3260         GET_INFO_BOTH(c1finfo0, c2finfo0);
3261         COMPARE_ALL_TIMES_EQUAL(c1finfo0, c1finfoCR);
3262
3263         state.h1 = h1;
3264         state.h2 = h2;
3265
3266         ok = test_delay_writetime(tctx, used_delay, normal_delay,
3267                         "run1",
3268                         test_durable_open_delaywrite1_get_info,
3269                         test_durable_open_delaywrite1_write_data,
3270                         &state);
3271         torture_assert(tctx, ok, "test_delay_writetime(1)");
3272         ok = test_delay_writetime(tctx, used_delay, normal_delay,
3273                         "run2",
3274                         test_durable_open_delaywrite1_get_info,
3275                         test_durable_open_delaywrite1_write_data,
3276                         &state);
3277         torture_assert(tctx, ok, "test_delay_writetime(2)");
3278
3279         GET_INFO_BOTH(c1finfo1, c2finfo1);
3280         COMPARE_TIMES_AFTER_WRITE(c1finfo1, c1finfo0);
3281
3282         /* sleep */
3283         torture_comment(tctx, "Sleep ...\n");
3284         smb_msleep(5 * msec);
3285         torture_comment(tctx, "... continue\n");
3286
3287         GET_INFO_BOTH(c1finfo2, c2finfo2);
3288         COMPARE_ALL_TIMES_EQUAL(c1finfo2, c1finfo1);
3289
3290         /*
3291          * the close updates the write time to the time of the close
3292          * and not to the time of the last write!
3293          */
3294         torture_comment(tctx, "Close the file handle\n");
3295
3296         ZERO_STRUCT(cl1);
3297         cl1.in.file.handle = *h1;
3298         cl1.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
3299         status = smb2_close(tree1, &cl1);
3300         CHECK_STATUS(status, NT_STATUS_OK);
3301         h1 = NULL;
3302         ZERO_STRUCT(c1finfoCL);
3303         c1finfoCL.basic_info.out.create_time = cl1.out.create_time;
3304         c1finfoCL.basic_info.out.access_time = cl1.out.access_time;
3305         c1finfoCL.basic_info.out.write_time = cl1.out.write_time;
3306         c1finfoCL.basic_info.out.change_time = cl1.out.change_time;
3307         c1finfoCL.basic_info.out.attrib = cl1.out.file_attr;
3308         COMPARE_ALL_TIMES_EQUAL(c1finfoCL, c1finfo2);
3309
3310         GET_INFO_FILE(tree2, c2finfo3);
3311         COMPARE_ALL_TIMES_EQUAL(c2finfo3, c1finfoCL);
3312
3313         ZERO_STRUCT(cl2);
3314         cl2.in.file.handle = *h2;
3315         cl2.in.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
3316         status = smb2_close(tree2, &cl2);
3317         CHECK_STATUS(status, NT_STATUS_OK);
3318         h2 = NULL;
3319         ZERO_STRUCT(c2finfoCL);
3320         c2finfoCL.basic_info.out.create_time = cl2.out.create_time;
3321         c2finfoCL.basic_info.out.access_time = cl2.out.access_time;
3322         c2finfoCL.basic_info.out.write_time = cl2.out.write_time;
3323         c2finfoCL.basic_info.out.change_time = cl2.out.change_time;
3324         c2finfoCL.basic_info.out.attrib = cl2.out.file_attr;
3325         COMPARE_ALL_TIMES_EQUAL(c2finfoCL, c1finfoCL);
3326
3327 done:
3328         if (h1 != NULL) {
3329                 smb2_util_close(tree1, *h1);
3330         }
3331         if (h2 != NULL) {
3332                 smb2_util_close(tree2, *h2);
3333         }
3334
3335         smb2_util_unlink(tree1, fname);
3336
3337         talloc_free(tree1);
3338         talloc_free(tree2);
3339
3340         talloc_free(mem_ctx);
3341
3342         return ret;
3343 }
3344
3345
3346 struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
3347 {
3348         struct torture_suite *suite =
3349             torture_suite_create(ctx, "durable-open");
3350
3351         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
3352         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
3353         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
3354         torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
3355         torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
3356         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
3357         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
3358         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
3359         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
3360         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
3361         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
3362         torture_suite_add_1smb2_test(suite, "delete_on_close1",
3363                                      test_durable_open_delete_on_close1);
3364         torture_suite_add_1smb2_test(suite, "delete_on_close2",
3365                                      test_durable_open_delete_on_close2);
3366         torture_suite_add_1smb2_test(suite, "file-position",
3367             test_durable_open_file_position);
3368         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
3369         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
3370         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
3371         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
3372         torture_suite_add_2smb2_test(suite, "open2-lease",
3373                                      test_durable_open_open2_lease);
3374         torture_suite_add_2smb2_test(suite, "open2-oplock",
3375                                      test_durable_open_open2_oplock);
3376         torture_suite_add_1smb2_test(suite, "alloc-size",
3377                                      test_durable_open_alloc_size);
3378         torture_suite_add_1smb2_test(suite, "read-only",
3379                                      test_durable_open_read_only);
3380         torture_suite_add_2smb2_test(suite, "delaywrite1",
3381                                      test_durable_open_delaywrite1);
3382
3383         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
3384
3385         return suite;
3386 }
3387
3388 struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
3389 {
3390         struct torture_suite *suite =
3391             torture_suite_create(ctx,
3392                                  "durable-open-disconnect");
3393
3394         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
3395                                      test_durable_open_oplock_disconnect);
3396
3397         suite->description = talloc_strdup(suite,
3398                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
3399
3400         return suite;
3401 }