s4:torture:smb2: extend the durable-open.reopen2 test
[mat/samba.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7    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
31 #define CHECK_VAL(v, correct) do { \
32         if ((v) != (correct)) { \
33                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35                 ret = false; \
36         }} while (0)
37
38 #define CHECK_NOT_VAL(v, correct) do { \
39         if ((v) == (correct)) { \
40                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41                                 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
42                 ret = false; \
43         }} while (0)
44
45 #define CHECK_STATUS(status, correct) do { \
46         if (!NT_STATUS_EQUAL(status, correct)) { \
47                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48                        nt_errstr(status), nt_errstr(correct)); \
49                 ret = false; \
50                 goto done; \
51         }} while (0)
52
53 #define CHECK_CREATED(__io, __created, __attribute)                     \
54         do {                                                            \
55                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
57                 CHECK_VAL((__io)->out.size, 0);                         \
58                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
59                 CHECK_VAL((__io)->out.reserved2, 0);                    \
60         } while(0)
61
62 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size)  \
63         do {                                                                    \
64                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
65                 CHECK_VAL((__io)->out.alloc_size, (__alloc_size));              \
66                 CHECK_VAL((__io)->out.size, (__size));                          \
67                 CHECK_VAL((__io)->out.file_attr, (__attribute));                \
68                 CHECK_VAL((__io)->out.reserved2, 0);                            \
69         } while(0)
70
71
72
73 /**
74  * basic durable_open test.
75  * durable state should only be granted when requested
76  * along with a batch oplock or a handle lease.
77  *
78  * This test tests durable open with all possible oplock types.
79  */
80
81 struct durable_open_vs_oplock {
82         const char *level;
83         const char *share_mode;
84         bool expected;
85 };
86
87 #define NUM_OPLOCK_TYPES 4
88 #define NUM_SHARE_MODES 8
89 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
90 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
91 {
92         { "", "", false },
93         { "", "R", false },
94         { "", "W", false },
95         { "", "D", false },
96         { "", "RD", false },
97         { "", "RW", false },
98         { "", "WD", false },
99         { "", "RWD", false },
100
101         { "s", "", false },
102         { "s", "R", false },
103         { "s", "W", false },
104         { "s", "D", false },
105         { "s", "RD", false },
106         { "s", "RW", false },
107         { "s", "WD", false },
108         { "s", "RWD", false },
109
110         { "x", "", false },
111         { "x", "R", false },
112         { "x", "W", false },
113         { "x", "D", false },
114         { "x", "RD", false },
115         { "x", "RW", false },
116         { "x", "WD", false },
117         { "x", "RWD", false },
118
119         { "b", "", true },
120         { "b", "R", true },
121         { "b", "W", true },
122         { "b", "D", true },
123         { "b", "RD", true },
124         { "b", "RW", true },
125         { "b", "WD", true },
126         { "b", "RWD", true },
127 };
128
129 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
130                                               struct smb2_tree *tree,
131                                               const char *fname,
132                                               struct durable_open_vs_oplock test)
133 {
134         NTSTATUS status;
135         TALLOC_CTX *mem_ctx = talloc_new(tctx);
136         struct smb2_handle _h;
137         struct smb2_handle *h = NULL;
138         bool ret = true;
139         struct smb2_create io;
140
141         smb2_util_unlink(tree, fname);
142
143         smb2_oplock_create_share(&io, fname,
144                                  smb2_util_share_access(test.share_mode),
145                                  smb2_util_oplock_level(test.level));
146         io.in.durable_open = true;
147
148         status = smb2_create(tree, mem_ctx, &io);
149         CHECK_STATUS(status, NT_STATUS_OK);
150         _h = io.out.file.handle;
151         h = &_h;
152         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
153         CHECK_VAL(io.out.durable_open, test.expected);
154         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
155
156 done:
157         if (h != NULL) {
158                 smb2_util_close(tree, *h);
159         }
160         smb2_util_unlink(tree, fname);
161         talloc_free(mem_ctx);
162
163         return ret;
164 }
165
166 static bool test_durable_open_open_oplock(struct torture_context *tctx,
167                                           struct smb2_tree *tree)
168 {
169         TALLOC_CTX *mem_ctx = talloc_new(tctx);
170         char fname[256];
171         bool ret = true;
172         int i;
173
174         /* Choose a random name in case the state is left a little funky. */
175         snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
176
177         smb2_util_unlink(tree, fname);
178
179         /* test various oplock levels with durable open */
180
181         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
182                 ret = test_one_durable_open_open_oplock(tctx,
183                                                         tree,
184                                                         fname,
185                                                         durable_open_vs_oplock_table[i]);
186                 if (ret == false) {
187                         goto done;
188                 }
189         }
190
191 done:
192         smb2_util_unlink(tree, fname);
193         talloc_free(tree);
194         talloc_free(mem_ctx);
195
196         return ret;
197 }
198
199 /**
200  * basic durable_open test.
201  * durable state should only be granted when requested
202  * along with a batch oplock or a handle lease.
203  *
204  * This test tests durable open with all valid lease types.
205  */
206
207 struct durable_open_vs_lease {
208         const char *type;
209         const char *share_mode;
210         bool expected;
211 };
212
213 #define NUM_LEASE_TYPES 5
214 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
215 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
216 {
217         { "", "", false },
218         { "", "R", false },
219         { "", "W", false },
220         { "", "D", false },
221         { "", "RW", false },
222         { "", "RD", false },
223         { "", "WD", false },
224         { "", "RWD", false },
225
226         { "R", "", false },
227         { "R", "R", false },
228         { "R", "W", false },
229         { "R", "D", false },
230         { "R", "RW", false },
231         { "R", "RD", false },
232         { "R", "DW", false },
233         { "R", "RWD", false },
234
235         { "RW", "", false },
236         { "RW", "R", false },
237         { "RW", "W", false },
238         { "RW", "D", false },
239         { "RW", "RW", false },
240         { "RW", "RD", false },
241         { "RW", "WD", false },
242         { "RW", "RWD", false },
243
244         { "RH", "", true },
245         { "RH", "R", true },
246         { "RH", "W", true },
247         { "RH", "D", true },
248         { "RH", "RW", true },
249         { "RH", "RD", true },
250         { "RH", "WD", true },
251         { "RH", "RWD", true },
252
253         { "RHW", "", true },
254         { "RHW", "R", true },
255         { "RHW", "W", true },
256         { "RHW", "D", true },
257         { "RHW", "RW", true },
258         { "RHW", "RD", true },
259         { "RHW", "WD", true },
260         { "RHW", "RWD", true },
261 };
262
263 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
264                                              struct smb2_tree *tree,
265                                              const char *fname,
266                                              struct durable_open_vs_lease test)
267 {
268         NTSTATUS status;
269         TALLOC_CTX *mem_ctx = talloc_new(tctx);
270         struct smb2_handle _h;
271         struct smb2_handle *h = NULL;
272         bool ret = true;
273         struct smb2_create io;
274         struct smb2_lease ls;
275         uint64_t lease;
276         uint32_t caps;
277
278         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
279         if (!(caps & SMB2_CAP_LEASING)) {
280                 torture_skip(tctx, "leases are not supported");
281         }
282
283         smb2_util_unlink(tree, fname);
284
285         lease = random();
286
287         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
288                                 smb2_util_share_access(test.share_mode),
289                                 lease,
290                                 smb2_util_lease_state(test.type));
291         io.in.durable_open = true;
292
293         status = smb2_create(tree, mem_ctx, &io);
294         CHECK_STATUS(status, NT_STATUS_OK);
295         _h = io.out.file.handle;
296         h = &_h;
297         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
298         CHECK_VAL(io.out.durable_open, test.expected);
299         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
300         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
301         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
302         CHECK_VAL(io.out.lease_response.lease_state,
303                   smb2_util_lease_state(test.type));
304 done:
305         if (h != NULL) {
306                 smb2_util_close(tree, *h);
307         }
308         smb2_util_unlink(tree, fname);
309         talloc_free(mem_ctx);
310
311         return ret;
312 }
313
314 static bool test_durable_open_open_lease(struct torture_context *tctx,
315                                          struct smb2_tree *tree)
316 {
317         TALLOC_CTX *mem_ctx = talloc_new(tctx);
318         char fname[256];
319         bool ret = true;
320         int i;
321         uint32_t caps;
322
323         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
324         if (!(caps & SMB2_CAP_LEASING)) {
325                 torture_skip(tctx, "leases are not supported");
326         }
327
328         /* Choose a random name in case the state is left a little funky. */
329         snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
330
331         smb2_util_unlink(tree, fname);
332
333
334         /* test various oplock levels with durable open */
335
336         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
337                 ret = test_one_durable_open_open_lease(tctx,
338                                                        tree,
339                                                        fname,
340                                                        durable_open_vs_lease_table[i]);
341                 if (ret == false) {
342                         goto done;
343                 }
344         }
345
346 done:
347         smb2_util_unlink(tree, fname);
348         talloc_free(tree);
349         talloc_free(mem_ctx);
350
351         return ret;
352 }
353
354 /**
355  * basic test for doing a durable open
356  * and do a durable reopen on the same connection
357  * while the first open is still active (fails)
358  */
359 static bool test_durable_open_reopen1(struct torture_context *tctx,
360                                       struct smb2_tree *tree)
361 {
362         NTSTATUS status;
363         TALLOC_CTX *mem_ctx = talloc_new(tctx);
364         char fname[256];
365         struct smb2_handle _h;
366         struct smb2_handle *h = NULL;
367         struct smb2_create io1, io2;
368         bool ret = true;
369
370         /* Choose a random name in case the state is left a little funky. */
371         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
372                  generate_random_str(tctx, 8));
373
374         smb2_util_unlink(tree, fname);
375
376         smb2_oplock_create_share(&io1, fname,
377                                  smb2_util_share_access(""),
378                                  smb2_util_oplock_level("b"));
379         io1.in.durable_open = true;
380
381         status = smb2_create(tree, mem_ctx, &io1);
382         CHECK_STATUS(status, NT_STATUS_OK);
383         _h = io1.out.file.handle;
384         h = &_h;
385         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
386         CHECK_VAL(io1.out.durable_open, true);
387         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
388
389         /* try a durable reconnect while the file is still open */
390         ZERO_STRUCT(io2);
391         io2.in.fname = fname;
392         io2.in.durable_handle = h;
393
394         status = smb2_create(tree, mem_ctx, &io2);
395         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
396
397 done:
398         if (h != NULL) {
399                 smb2_util_close(tree, *h);
400         }
401
402         smb2_util_unlink(tree, fname);
403
404         talloc_free(tree);
405
406         talloc_free(mem_ctx);
407
408         return ret;
409 }
410
411 /**
412  * basic test for doing a durable open
413  * tcp disconnect, reconnect, do a durable reopen (succeeds)
414  */
415 static bool test_durable_open_reopen2(struct torture_context *tctx,
416                                       struct smb2_tree *tree)
417 {
418         NTSTATUS status;
419         TALLOC_CTX *mem_ctx = talloc_new(tctx);
420         char fname[256];
421         struct smb2_handle _h;
422         struct smb2_handle *h = NULL;
423         struct smb2_create io;
424         bool ret = true;
425
426         /* Choose a random name in case the state is left a little funky. */
427         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
428                  generate_random_str(tctx, 8));
429
430         smb2_util_unlink(tree, fname);
431
432         smb2_oplock_create_share(&io, fname,
433                                  smb2_util_share_access(""),
434                                  smb2_util_oplock_level("b"));
435         io.in.durable_open = true;
436
437         status = smb2_create(tree, mem_ctx, &io);
438         CHECK_STATUS(status, NT_STATUS_OK);
439         _h = io.out.file.handle;
440         h = &_h;
441         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
442         CHECK_VAL(io.out.durable_open, true);
443         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
444
445         /* disconnect, leaving the durable in place */
446         TALLOC_FREE(tree);
447
448         if (!torture_smb2_connection(tctx, &tree)) {
449                 torture_warning(tctx, "couldn't reconnect, bailing\n");
450                 ret = false;
451                 goto done;
452         }
453
454         ZERO_STRUCT(io);
455         /* the path name is ignored by the server */
456         io.in.fname = fname;
457         io.in.durable_handle = h; /* durable v1 reconnect request */
458         h = NULL;
459
460         status = smb2_create(tree, mem_ctx, &io);
461         CHECK_STATUS(status, NT_STATUS_OK);
462         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
463         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
464         _h = io.out.file.handle;
465         h = &_h;
466
467         /* disconnect again, leaving the durable in place */
468         TALLOC_FREE(tree);
469
470         if (!torture_smb2_connection(tctx, &tree)) {
471                 torture_warning(tctx, "couldn't reconnect, bailing\n");
472                 ret = false;
473                 goto done;
474         }
475
476         /*
477          * show that the filename and many other fields
478          * are ignored. only the reconnect request blob
479          * is important.
480          */
481         ZERO_STRUCT(io);
482         /* the path name is ignored by the server */
483         io.in.security_flags = 0x78;
484         io.in.oplock_level = 0x78;
485         io.in.impersonation_level = 0x12345678;
486         io.in.create_flags = 0x12345678;
487         io.in.reserved = 0x12345678;
488         io.in.desired_access = 0x12345678;
489         io.in.file_attributes = 0x12345678;
490         io.in.share_access = 0x12345678;
491         io.in.create_disposition = 0x12345678;
492         io.in.create_options = 0x12345678;
493         io.in.fname = "__non_existing_fname__";
494         io.in.durable_handle = h; /* durable v1 reconnect request */
495         h = NULL;
496
497         status = smb2_create(tree, mem_ctx, &io);
498         CHECK_STATUS(status, NT_STATUS_OK);
499         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
500         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
501         _h = io.out.file.handle;
502         h = &_h;
503
504         /* disconnect, leaving the durable in place */
505         TALLOC_FREE(tree);
506
507         if (!torture_smb2_connection(tctx, &tree)) {
508                 torture_warning(tctx, "couldn't reconnect, bailing\n");
509                 ret = false;
510                 goto done;
511         }
512
513         /*
514          * show that an additionally specified durable v1 request
515          * is ignored by the server.
516          * See MS-SMB2, 3.3.5.9.7
517          * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
518          */
519         ZERO_STRUCT(io);
520         /* the path name is ignored by the server */
521         io.in.fname = fname;
522         io.in.durable_handle = h;  /* durable v1 reconnect request */
523         io.in.durable_open = true; /* durable v1 handle request */
524         h = NULL;
525
526         status = smb2_create(tree, mem_ctx, &io);
527         CHECK_STATUS(status, NT_STATUS_OK);
528         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
529         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
530         _h = io.out.file.handle;
531         h = &_h;
532
533 done:
534         if (tree != NULL) {
535                 if (h != NULL) {
536                         smb2_util_close(tree, *h);
537                 }
538
539                 smb2_util_unlink(tree, fname);
540
541                 talloc_free(tree);
542         }
543
544         talloc_free(mem_ctx);
545
546         return ret;
547 }
548
549 /**
550  * lease variant of reopen2
551  * basic test for doing a durable open
552  * tcp disconnect, reconnect, do a durable reopen (succeeds)
553  */
554 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
555                                             struct smb2_tree *tree)
556 {
557         NTSTATUS status;
558         TALLOC_CTX *mem_ctx = talloc_new(tctx);
559         char fname[256];
560         struct smb2_handle _h;
561         struct smb2_handle *h = NULL;
562         struct smb2_create io;
563         struct smb2_lease ls;
564         uint64_t lease_key;
565         bool ret = true;
566         struct smbcli_options options;
567         uint32_t caps;
568
569         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
570         if (!(caps & SMB2_CAP_LEASING)) {
571                 torture_skip(tctx, "leases are not supported");
572         }
573
574         options = tree->session->transport->options;
575
576         /* Choose a random name in case the state is left a little funky. */
577         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
578                  generate_random_str(tctx, 8));
579
580         smb2_util_unlink(tree, fname);
581
582         lease_key = random();
583         smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
584                           smb2_util_lease_state("RWH"));
585         io.in.durable_open = true;
586
587         status = smb2_create(tree, mem_ctx, &io);
588         CHECK_STATUS(status, NT_STATUS_OK);
589         _h = io.out.file.handle;
590         h = &_h;
591         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
592
593         CHECK_VAL(io.out.durable_open, true);
594         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
595         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
596         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
597         CHECK_VAL(io.out.lease_response.lease_state,
598                   smb2_util_lease_state("RWH"));
599         CHECK_VAL(io.out.lease_response.lease_flags, 0);
600         CHECK_VAL(io.out.lease_response.lease_duration, 0);
601
602         /* disconnect, reconnect and then do durable reopen */
603         TALLOC_FREE(tree);
604
605         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
606                 torture_warning(tctx, "couldn't reconnect, bailing\n");
607                 ret = false;
608                 goto done;
609         }
610
611
612         /* a few failure tests: */
613
614         /*
615          * several attempts without lease attached:
616          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
617          * irrespective of file name provided
618          */
619
620         ZERO_STRUCT(io);
621         io.in.fname = "";
622         io.in.durable_handle = h;
623         status = smb2_create(tree, mem_ctx, &io);
624         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
625
626         ZERO_STRUCT(io);
627         io.in.fname = "__non_existing_fname__";
628         io.in.durable_handle = h;
629         status = smb2_create(tree, mem_ctx, &io);
630         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
631
632         ZERO_STRUCT(io);
633         io.in.fname = fname;
634         io.in.durable_handle = h;
635         status = smb2_create(tree, mem_ctx, &io);
636         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
637
638         /*
639          * attempt with lease provided, but
640          * with a changed lease key. => fails
641          */
642         ZERO_STRUCT(io);
643         io.in.fname = fname;
644         io.in.durable_open = false;
645         io.in.durable_handle = h;
646         io.in.lease_request = &ls;
647         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
648         /* a wrong lease key lets the request fail */
649         ls.lease_key.data[0]++;
650
651         status = smb2_create(tree, mem_ctx, &io);
652         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
653
654         /* restore the correct lease key */
655         ls.lease_key.data[0]--;
656
657         /*
658          * this last failing attempt is almost correct:
659          * only problem is: we use the wrong filename...
660          * Note that this gives INVALID_PARAMETER.
661          * This is different from oplocks!
662          */
663         ZERO_STRUCT(io);
664         io.in.fname = "__non_existing_fname__";
665         io.in.durable_open = false;
666         io.in.durable_handle = h;
667         io.in.lease_request = &ls;
668         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
669
670         status = smb2_create(tree, mem_ctx, &io);
671         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
672
673         /*
674          * Now for a succeeding reconnect:
675          */
676
677         ZERO_STRUCT(io);
678         io.in.fname = fname;
679         io.in.durable_open = false;
680         io.in.durable_handle = h;
681         io.in.lease_request = &ls;
682         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
683
684         /* the requested lease state is irrelevant */
685         ls.lease_state = smb2_util_lease_state("");
686
687         h = NULL;
688
689         status = smb2_create(tree, mem_ctx, &io);
690         CHECK_STATUS(status, NT_STATUS_OK);
691
692         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
693         CHECK_VAL(io.out.durable_open, false);
694         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
695         CHECK_VAL(io.out.persistent_open, false);
696         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
697         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
698         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
699         CHECK_VAL(io.out.lease_response.lease_state,
700                   smb2_util_lease_state("RWH"));
701         CHECK_VAL(io.out.lease_response.lease_flags, 0);
702         CHECK_VAL(io.out.lease_response.lease_duration, 0);
703         _h = io.out.file.handle;
704         h = &_h;
705
706         /* disconnect one more time */
707         TALLOC_FREE(tree);
708
709         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
710                 torture_warning(tctx, "couldn't reconnect, bailing\n");
711                 ret = false;
712                 goto done;
713         }
714
715         /*
716          * demonstrate that various parameters are ignored
717          * in the reconnect
718          */
719
720         ZERO_STRUCT(io);
721         /*
722          * These are completely ignored by the server
723          */
724         io.in.security_flags = 0x78;
725         io.in.oplock_level = 0x78;
726         io.in.impersonation_level = 0x12345678;
727         io.in.create_flags = 0x12345678;
728         io.in.reserved = 0x12345678;
729         io.in.desired_access = 0x12345678;
730         io.in.file_attributes = 0x12345678;
731         io.in.share_access = 0x12345678;
732         io.in.create_disposition = 0x12345678;
733         io.in.create_options = 0x12345678;
734
735         /*
736          * only these are checked:
737          * - io.in.fname
738          * - io.in.durable_handle,
739          * - io.in.lease_request->lease_key
740          */
741
742         io.in.fname = fname;
743         io.in.durable_open_v2 = false;
744         io.in.durable_handle_v2 = h;
745         io.in.lease_request = &ls;
746
747         /* the requested lease state is irrelevant */
748         ls.lease_state = smb2_util_lease_state("");
749
750         h = NULL;
751
752         status = smb2_create(tree, mem_ctx, &io);
753         CHECK_STATUS(status, NT_STATUS_OK);
754
755         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
756         CHECK_VAL(io.out.durable_open, false);
757         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
758         CHECK_VAL(io.out.persistent_open, false);
759         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
760         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
761         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
762         CHECK_VAL(io.out.lease_response.lease_state,
763                   smb2_util_lease_state("RWH"));
764         CHECK_VAL(io.out.lease_response.lease_flags, 0);
765         CHECK_VAL(io.out.lease_response.lease_duration, 0);
766
767         _h = io.out.file.handle;
768         h = &_h;
769
770 done:
771         if (tree != NULL) {
772                 if (h != NULL) {
773                         smb2_util_close(tree, *h);
774                 }
775
776                 smb2_util_unlink(tree, fname);
777
778                 talloc_free(tree);
779         }
780
781         talloc_free(mem_ctx);
782
783         return ret;
784 }
785
786 /**
787  * lease v2 variant of reopen2
788  * basic test for doing a durable open
789  * tcp disconnect, reconnect, do a durable reopen (succeeds)
790  */
791 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
792                                                struct smb2_tree *tree)
793 {
794         NTSTATUS status;
795         TALLOC_CTX *mem_ctx = talloc_new(tctx);
796         char fname[256];
797         struct smb2_handle _h;
798         struct smb2_handle *h = NULL;
799         struct smb2_create io;
800         struct smb2_lease ls;
801         uint64_t lease_key;
802         bool ret = true;
803         struct smbcli_options options;
804         uint32_t caps;
805
806         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
807         if (!(caps & SMB2_CAP_LEASING)) {
808                 torture_skip(tctx, "leases are not supported");
809         }
810
811         options = tree->session->transport->options;
812
813         /* Choose a random name in case the state is left a little funky. */
814         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
815                  generate_random_str(tctx, 8));
816
817         smb2_util_unlink(tree, fname);
818
819         lease_key = random();
820         smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
821                              lease_key, 0, /* parent lease key */
822                              smb2_util_lease_state("RWH"), 0 /* lease epoch */);
823         io.in.durable_open = true;
824
825         status = smb2_create(tree, mem_ctx, &io);
826         CHECK_STATUS(status, NT_STATUS_OK);
827         _h = io.out.file.handle;
828         h = &_h;
829         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
830
831         CHECK_VAL(io.out.durable_open, true);
832         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
833         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
834         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
835         CHECK_VAL(io.out.lease_response_v2.lease_state,
836                   smb2_util_lease_state("RWH"));
837         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
838         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
839
840         /* disconnect, reconnect and then do durable reopen */
841         TALLOC_FREE(tree);
842
843         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
844                 torture_warning(tctx, "couldn't reconnect, bailing\n");
845                 ret = false;
846                 goto done;
847         }
848
849         /* a few failure tests: */
850
851         /*
852          * several attempts without lease attached:
853          * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
854          * irrespective of file name provided
855          */
856
857         ZERO_STRUCT(io);
858         io.in.fname = "";
859         io.in.durable_handle = h;
860         status = smb2_create(tree, mem_ctx, &io);
861         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
862
863         ZERO_STRUCT(io);
864         io.in.fname = "__non_existing_fname__";
865         io.in.durable_handle = h;
866         status = smb2_create(tree, mem_ctx, &io);
867         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
868
869         ZERO_STRUCT(io);
870         io.in.fname = fname;
871         io.in.durable_handle = h;
872         status = smb2_create(tree, mem_ctx, &io);
873         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
874
875         /*
876          * attempt with lease provided, but
877          * with a changed lease key. => fails
878          */
879         ZERO_STRUCT(io);
880         io.in.fname = fname;
881         io.in.durable_open = false;
882         io.in.durable_handle = h;
883         io.in.lease_request_v2 = &ls;
884         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
885         /* a wrong lease key lets the request fail */
886         ls.lease_key.data[0]++;
887
888         status = smb2_create(tree, mem_ctx, &io);
889         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
890
891         /* restore the correct lease key */
892         ls.lease_key.data[0]--;
893
894         /*
895          * this last failing attempt is almost correct:
896          * only problem is: we use the wrong filename...
897          * Note that this gives INVALID_PARAMETER.
898          * This is different from oplocks!
899          */
900         ZERO_STRUCT(io);
901         io.in.fname = "__non_existing_fname__";
902         io.in.durable_open = false;
903         io.in.durable_handle = h;
904         io.in.lease_request_v2 = &ls;
905         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
906
907         status = smb2_create(tree, mem_ctx, &io);
908         CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
909
910         /*
911          * Now for a succeeding reconnect:
912          */
913
914         ZERO_STRUCT(io);
915         io.in.fname = fname;
916         io.in.durable_open = false;
917         io.in.durable_handle = h;
918         io.in.lease_request_v2 = &ls;
919         io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
920
921         /* the requested lease state is irrelevant */
922         ls.lease_state = smb2_util_lease_state("");
923
924         h = NULL;
925
926         status = smb2_create(tree, mem_ctx, &io);
927         CHECK_STATUS(status, NT_STATUS_OK);
928
929         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
930         CHECK_VAL(io.out.durable_open, false);
931         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
932         CHECK_VAL(io.out.persistent_open, false);
933         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
934         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
935         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
936         CHECK_VAL(io.out.lease_response_v2.lease_state,
937                   smb2_util_lease_state("RWH"));
938         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
939         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
940         _h = io.out.file.handle;
941         h = &_h;
942
943         /* disconnect one more time */
944         TALLOC_FREE(tree);
945
946         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
947                 torture_warning(tctx, "couldn't reconnect, bailing\n");
948                 ret = false;
949                 goto done;
950         }
951
952         /*
953          * demonstrate that various parameters are ignored
954          * in the reconnect
955          */
956
957         ZERO_STRUCT(io);
958         /*
959          * These are completely ignored by the server
960          */
961         io.in.security_flags = 0x78;
962         io.in.oplock_level = 0x78;
963         io.in.impersonation_level = 0x12345678;
964         io.in.create_flags = 0x12345678;
965         io.in.reserved = 0x12345678;
966         io.in.desired_access = 0x12345678;
967         io.in.file_attributes = 0x12345678;
968         io.in.share_access = 0x12345678;
969         io.in.create_disposition = 0x12345678;
970         io.in.create_options = 0x12345678;
971
972         /*
973          * only these are checked:
974          * - io.in.fname
975          * - io.in.durable_handle,
976          * - io.in.lease_request->lease_key
977          */
978
979         io.in.fname = fname;
980         io.in.durable_open_v2 = false;
981         io.in.durable_handle_v2 = h;
982         io.in.lease_request_v2 = &ls;
983
984         /* the requested lease state is irrelevant */
985         ls.lease_state = smb2_util_lease_state("");
986
987         h = NULL;
988
989         status = smb2_create(tree, mem_ctx, &io);
990         CHECK_STATUS(status, NT_STATUS_OK);
991
992         CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
993         CHECK_VAL(io.out.durable_open, false);
994         CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
995         CHECK_VAL(io.out.persistent_open, false);
996         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
997         CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
998         CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
999         CHECK_VAL(io.out.lease_response_v2.lease_state,
1000                   smb2_util_lease_state("RWH"));
1001         CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1002         CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1003
1004         _h = io.out.file.handle;
1005         h = &_h;
1006
1007 done:
1008         if (tree != NULL) {
1009                 if (h != NULL) {
1010                         smb2_util_close(tree, *h);
1011                 }
1012
1013                 smb2_util_unlink(tree, fname);
1014
1015                 talloc_free(tree);
1016         }
1017
1018         talloc_free(mem_ctx);
1019
1020         return ret;
1021 }
1022
1023 /**
1024  * basic test for doing a durable open
1025  * tcp disconnect, reconnect with a session reconnect and
1026  * do a durable reopen (succeeds)
1027  */
1028 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1029                                        struct smb2_tree *tree)
1030 {
1031         NTSTATUS status;
1032         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1033         char fname[256];
1034         struct smb2_handle _h;
1035         struct smb2_handle *h = NULL;
1036         struct smb2_create io1, io2;
1037         uint64_t previous_session_id;
1038         bool ret = true;
1039         struct smbcli_options options;
1040
1041         options = tree->session->transport->options;
1042
1043         /* Choose a random name in case the state is left a little funky. */
1044         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1045                  generate_random_str(tctx, 8));
1046
1047         smb2_util_unlink(tree, fname);
1048
1049         smb2_oplock_create_share(&io1, fname,
1050                                  smb2_util_share_access(""),
1051                                  smb2_util_oplock_level("b"));
1052         io1.in.durable_open = true;
1053
1054         status = smb2_create(tree, mem_ctx, &io1);
1055         CHECK_STATUS(status, NT_STATUS_OK);
1056         _h = io1.out.file.handle;
1057         h = &_h;
1058         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1059         CHECK_VAL(io1.out.durable_open, true);
1060         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1061
1062         /* disconnect, reconnect and then do durable reopen */
1063         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1064         talloc_free(tree);
1065         tree = NULL;
1066
1067         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1068                                          &options, &tree))
1069         {
1070                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1071                 ret = false;
1072                 goto done;
1073         }
1074
1075         ZERO_STRUCT(io2);
1076         io2.in.fname = fname;
1077         io2.in.durable_handle = h;
1078         h = NULL;
1079
1080         status = smb2_create(tree, mem_ctx, &io2);
1081         CHECK_STATUS(status, NT_STATUS_OK);
1082         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1083         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1084         _h = io2.out.file.handle;
1085         h = &_h;
1086
1087 done:
1088         if (tree != NULL) {
1089                 if (h != NULL) {
1090                         smb2_util_close(tree, *h);
1091                 }
1092
1093                 smb2_util_unlink(tree, fname);
1094
1095                 talloc_free(tree);
1096         }
1097
1098         talloc_free(mem_ctx);
1099
1100         return ret;
1101 }
1102
1103
1104 /**
1105  * basic test for doing a durable open:
1106  * tdis, new tcon, try durable reopen (fails)
1107  */
1108 static bool test_durable_open_reopen3(struct torture_context *tctx,
1109                                       struct smb2_tree *tree)
1110 {
1111         NTSTATUS status;
1112         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1113         char fname[256];
1114         struct smb2_handle _h;
1115         struct smb2_handle *h = NULL;
1116         struct smb2_create io1, io2;
1117         bool ret = true;
1118         struct smb2_tree *tree2;
1119
1120         /* Choose a random name in case the state is left a little funky. */
1121         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1122                  generate_random_str(tctx, 8));
1123
1124         smb2_util_unlink(tree, fname);
1125
1126         smb2_oplock_create_share(&io1, fname,
1127                                  smb2_util_share_access(""),
1128                                  smb2_util_oplock_level("b"));
1129         io1.in.durable_open = true;
1130
1131         status = smb2_create(tree, mem_ctx, &io1);
1132         CHECK_STATUS(status, NT_STATUS_OK);
1133         _h = io1.out.file.handle;
1134         h = &_h;
1135         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1136         CHECK_VAL(io1.out.durable_open, true);
1137         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1138
1139         /* disconnect, reconnect and then do durable reopen */
1140         status = smb2_tdis(tree);
1141         CHECK_STATUS(status, NT_STATUS_OK);
1142
1143         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1144                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1145                 ret = false;
1146                 goto done;
1147         }
1148
1149
1150         ZERO_STRUCT(io2);
1151         io2.in.fname = fname;
1152         io2.in.durable_handle = h;
1153
1154         status = smb2_create(tree2, mem_ctx, &io2);
1155         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1156
1157 done:
1158         if (tree != NULL) {
1159                 if (h != NULL) {
1160                         smb2_util_close(tree, *h);
1161                 }
1162
1163                 smb2_util_unlink(tree2, fname);
1164
1165                 talloc_free(tree);
1166         }
1167
1168         talloc_free(mem_ctx);
1169
1170         return ret;
1171 }
1172
1173 /**
1174  * basic test for doing a durable open:
1175  * logoff, create a new session, do a durable reopen (succeeds)
1176  */
1177 static bool test_durable_open_reopen4(struct torture_context *tctx,
1178                                       struct smb2_tree *tree)
1179 {
1180         NTSTATUS status;
1181         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1182         char fname[256];
1183         struct smb2_handle _h;
1184         struct smb2_handle *h = NULL;
1185         struct smb2_create io1, io2;
1186         bool ret = true;
1187         struct smb2_transport *transport;
1188         struct smb2_session *session2;
1189         struct smb2_tree *tree2;
1190
1191         /* Choose a random name in case the state is left a little funky. */
1192         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1193                  generate_random_str(tctx, 8));
1194
1195         smb2_util_unlink(tree, fname);
1196
1197         smb2_oplock_create_share(&io1, fname,
1198                                  smb2_util_share_access(""),
1199                                  smb2_util_oplock_level("b"));
1200         io1.in.durable_open = true;
1201         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1202
1203         status = smb2_create(tree, mem_ctx, &io1);
1204         CHECK_STATUS(status, NT_STATUS_OK);
1205         _h = io1.out.file.handle;
1206         h = &_h;
1207         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1208         CHECK_VAL(io1.out.durable_open, true);
1209         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1210
1211         /*
1212          * do a session logoff, establish a new session and tree
1213          * connect on the same transport, and try a durable reopen
1214          */
1215         transport = tree->session->transport;
1216         status = smb2_logoff(tree->session);
1217         CHECK_STATUS(status, NT_STATUS_OK);
1218
1219         if (!torture_smb2_session_setup(tctx, transport,
1220                                         0, /* previous_session_id */
1221                                         mem_ctx, &session2))
1222         {
1223                 torture_warning(tctx, "session setup failed.\n");
1224                 ret = false;
1225                 goto done;
1226         }
1227
1228         /*
1229          * the session setup has talloc-stolen the transport,
1230          * so we can safely free the old tree+session for clarity
1231          */
1232         TALLOC_FREE(tree);
1233
1234         if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1235                 torture_warning(tctx, "tree connect failed.\n");
1236                 ret = false;
1237                 goto done;
1238         }
1239
1240         ZERO_STRUCT(io2);
1241         io2.in.fname = fname;
1242         io2.in.durable_handle = h;
1243         h = NULL;
1244
1245         status = smb2_create(tree2, mem_ctx, &io2);
1246         CHECK_STATUS(status, NT_STATUS_OK);
1247
1248         _h = io2.out.file.handle;
1249         h = &_h;
1250         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1251         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1252
1253 done:
1254         if (tree != NULL) {
1255                 if (h != NULL) {
1256                         smb2_util_close(tree2, *h);
1257                 }
1258
1259                 smb2_util_unlink(tree2, fname);
1260
1261                 talloc_free(tree);
1262         }
1263
1264         talloc_free(mem_ctx);
1265
1266         return ret;
1267 }
1268
1269 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1270                                                struct smb2_tree *tree)
1271 {
1272         NTSTATUS status;
1273         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1274         char fname[256];
1275         struct smb2_handle _h;
1276         struct smb2_handle *h = NULL;
1277         struct smb2_create io1, io2;
1278         bool ret = true;
1279         uint8_t b = 0;
1280
1281         /* Choose a random name in case the state is left a little funky. */
1282         snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1283                  generate_random_str(tctx, 8));
1284
1285         smb2_util_unlink(tree, fname);
1286
1287         smb2_oplock_create_share(&io1, fname,
1288                                  smb2_util_share_access(""),
1289                                  smb2_util_oplock_level("b"));
1290         io1.in.durable_open = true;
1291         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1292
1293         status = smb2_create(tree, mem_ctx, &io1);
1294         CHECK_STATUS(status, NT_STATUS_OK);
1295         _h = io1.out.file.handle;
1296         h = &_h;
1297         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1298         CHECK_VAL(io1.out.durable_open, true);
1299         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1300
1301         status = smb2_util_write(tree, *h, &b, 0, 1);
1302         CHECK_STATUS(status, NT_STATUS_OK);
1303
1304         /* disconnect, leaving the durable handle in place */
1305         TALLOC_FREE(tree);
1306
1307         if (!torture_smb2_connection(tctx, &tree)) {
1308                 torture_warning(tctx, "could not reconnect, bailing\n");
1309                 ret = false;
1310                 goto done;
1311         }
1312
1313         /*
1314          * Open the file on the new connection again
1315          * and check that it has been newly created,
1316          * i.e. delete on close was effective on the disconnected handle.
1317          * Also check that the file is really empty,
1318          * the previously written byte gone.
1319          */
1320         smb2_oplock_create_share(&io2, fname,
1321                                  smb2_util_share_access(""),
1322                                  smb2_util_oplock_level("b"));
1323         io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1324
1325         status = smb2_create(tree, mem_ctx, &io2);
1326         CHECK_STATUS(status, NT_STATUS_OK);
1327         _h = io2.out.file.handle;
1328         h = &_h;
1329         CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1330         CHECK_VAL(io2.out.durable_open, false);
1331         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1332
1333 done:
1334         if (tree != NULL) {
1335                 if (h != NULL) {
1336                         smb2_util_close(tree, *h);
1337                 }
1338
1339                 smb2_util_unlink(tree, fname);
1340
1341                 talloc_free(tree);
1342         }
1343
1344         talloc_free(mem_ctx);
1345
1346         return ret;
1347 }
1348
1349
1350 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1351                                                struct smb2_tree *tree)
1352 {
1353         NTSTATUS status;
1354         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1355         char fname[256];
1356         struct smb2_handle _h;
1357         struct smb2_handle *h = NULL;
1358         struct smb2_create io;
1359         bool ret = true;
1360         uint8_t b = 0;
1361         uint64_t previous_session_id;
1362         uint64_t alloc_size_step;
1363         struct smbcli_options options;
1364
1365         options = tree->session->transport->options;
1366
1367         /* Choose a random name in case the state is left a little funky. */
1368         snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1369                  generate_random_str(tctx, 8));
1370
1371         smb2_util_unlink(tree, fname);
1372
1373         smb2_oplock_create_share(&io, fname,
1374                                  smb2_util_share_access(""),
1375                                  smb2_util_oplock_level("b"));
1376         io.in.durable_open = true;
1377         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1378
1379         status = smb2_create(tree, mem_ctx, &io);
1380         CHECK_STATUS(status, NT_STATUS_OK);
1381         _h = io.out.file.handle;
1382         h = &_h;
1383         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1384         CHECK_VAL(io.out.durable_open, true);
1385         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1386
1387         status = smb2_util_write(tree, *h, &b, 0, 1);
1388         CHECK_STATUS(status, NT_STATUS_OK);
1389
1390         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1391
1392         /* disconnect, leaving the durable handle in place */
1393         TALLOC_FREE(tree);
1394
1395         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1396                                          &options, &tree))
1397         {
1398                 torture_warning(tctx, "could not reconnect, bailing\n");
1399                 ret = false;
1400                 goto done;
1401         }
1402
1403         ZERO_STRUCT(io);
1404         io.in.fname = fname;
1405         io.in.durable_handle = h;
1406
1407         status = smb2_create(tree, mem_ctx, &io);
1408         CHECK_STATUS(status, NT_STATUS_OK);
1409         _h = io.out.file.handle;
1410         h = &_h;
1411         alloc_size_step = io.out.alloc_size;
1412         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1413         CHECK_VAL(io.out.durable_open, false);
1414         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1415
1416         /* close the file, thereby deleting it */
1417         smb2_util_close(tree, *h);
1418         status = smb2_logoff(tree->session);
1419         TALLOC_FREE(tree);
1420
1421         if (!torture_smb2_connection(tctx, &tree)) {
1422                 torture_warning(tctx, "could not reconnect, bailing\n");
1423                 ret = false;
1424                 goto done;
1425         }
1426
1427         /*
1428          * Open the file on the new connection again
1429          * and check that it has been newly created,
1430          * i.e. delete on close was effective on the reconnected handle.
1431          * Also check that the file is really empty,
1432          * the previously written byte gone.
1433          */
1434         smb2_oplock_create_share(&io, fname,
1435                                  smb2_util_share_access(""),
1436                                  smb2_util_oplock_level("b"));
1437         io.in.durable_open = true;
1438         io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1439
1440         status = smb2_create(tree, mem_ctx, &io);
1441         CHECK_STATUS(status, NT_STATUS_OK);
1442         _h = io.out.file.handle;
1443         h = &_h;
1444         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1445         CHECK_VAL(io.out.durable_open, true);
1446         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1447
1448 done:
1449         if (tree != NULL) {
1450                 if (h != NULL) {
1451                         smb2_util_close(tree, *h);
1452                 }
1453
1454                 smb2_util_unlink(tree, fname);
1455
1456                 talloc_free(tree);
1457         }
1458
1459         talloc_free(mem_ctx);
1460
1461         return ret;
1462 }
1463
1464 /*
1465    basic testing of SMB2 durable opens
1466    regarding the position information on the handle
1467 */
1468 static bool test_durable_open_file_position(struct torture_context *tctx,
1469                                             struct smb2_tree *tree)
1470 {
1471         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1472         struct smb2_handle h;
1473         struct smb2_create io;
1474         NTSTATUS status;
1475         const char *fname = "durable_open_position.dat";
1476         union smb_fileinfo qfinfo;
1477         union smb_setfileinfo sfinfo;
1478         bool ret = true;
1479         uint64_t pos;
1480         uint64_t previous_session_id;
1481         struct smbcli_options options;
1482
1483         options = tree->session->transport->options;
1484
1485         smb2_util_unlink(tree, fname);
1486
1487         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1488         io.in.durable_open = true;
1489
1490         status = smb2_create(tree, mem_ctx, &io);
1491         CHECK_STATUS(status, NT_STATUS_OK);
1492         h = io.out.file.handle;
1493         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1494         CHECK_VAL(io.out.durable_open, true);
1495         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1496
1497         /* TODO: check extra blob content */
1498
1499         ZERO_STRUCT(qfinfo);
1500         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1501         qfinfo.generic.in.file.handle = h;
1502         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1503         CHECK_STATUS(status, NT_STATUS_OK);
1504         CHECK_VAL(qfinfo.position_information.out.position, 0);
1505         pos = qfinfo.position_information.out.position;
1506         torture_comment(tctx, "position: %llu\n",
1507                         (unsigned long long)pos);
1508
1509         ZERO_STRUCT(sfinfo);
1510         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1511         sfinfo.generic.in.file.handle = h;
1512         sfinfo.position_information.in.position = 0x1000;
1513         status = smb2_setinfo_file(tree, &sfinfo);
1514         CHECK_STATUS(status, NT_STATUS_OK);
1515
1516         ZERO_STRUCT(qfinfo);
1517         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1518         qfinfo.generic.in.file.handle = h;
1519         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1520         CHECK_STATUS(status, NT_STATUS_OK);
1521         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1522         pos = qfinfo.position_information.out.position;
1523         torture_comment(tctx, "position: %llu\n",
1524                         (unsigned long long)pos);
1525
1526         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1527
1528         /* tcp disconnect */
1529         talloc_free(tree);
1530         tree = NULL;
1531
1532         /* do a session reconnect */
1533         if (!torture_smb2_connection_ext(tctx, previous_session_id,
1534                                          &options, &tree))
1535         {
1536                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1537                 ret = false;
1538                 goto done;
1539         }
1540
1541         ZERO_STRUCT(qfinfo);
1542         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1543         qfinfo.generic.in.file.handle = h;
1544         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1545         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1546
1547         ZERO_STRUCT(io);
1548         io.in.fname = fname;
1549         io.in.durable_handle = &h;
1550
1551         status = smb2_create(tree, mem_ctx, &io);
1552         CHECK_STATUS(status, NT_STATUS_OK);
1553         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1554         CHECK_VAL(io.out.reserved, 0x00);
1555         CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1556         CHECK_VAL(io.out.alloc_size, 0);
1557         CHECK_VAL(io.out.size, 0);
1558         CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1559         CHECK_VAL(io.out.reserved2, 0);
1560
1561         h = io.out.file.handle;
1562
1563         ZERO_STRUCT(qfinfo);
1564         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1565         qfinfo.generic.in.file.handle = h;
1566         status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1567         CHECK_STATUS(status, NT_STATUS_OK);
1568         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1569         pos = qfinfo.position_information.out.position;
1570         torture_comment(tctx, "position: %llu\n",
1571                         (unsigned long long)pos);
1572
1573         smb2_util_close(tree, h);
1574
1575         talloc_free(mem_ctx);
1576
1577         smb2_util_unlink(tree, fname);
1578
1579 done:
1580         talloc_free(tree);
1581
1582         return ret;
1583 }
1584
1585 /*
1586   Open, disconnect, oplock break, reconnect.
1587 */
1588 static bool test_durable_open_oplock(struct torture_context *tctx,
1589                                      struct smb2_tree *tree1,
1590                                      struct smb2_tree *tree2)
1591 {
1592         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1593         struct smb2_create io1, io2;
1594         struct smb2_handle h1, h2;
1595         NTSTATUS status;
1596         char fname[256];
1597         bool ret = true;
1598
1599         /* Choose a random name in case the state is left a little funky. */
1600         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1601
1602         /* Clean slate */
1603         smb2_util_unlink(tree1, fname);
1604
1605         /* Create with batch oplock */
1606         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1607         io1.in.durable_open = true;
1608
1609         io2 = io1;
1610         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1611
1612         status = smb2_create(tree1, mem_ctx, &io1);
1613         CHECK_STATUS(status, NT_STATUS_OK);
1614         h1 = io1.out.file.handle;
1615         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1616         CHECK_VAL(io1.out.durable_open, true);
1617         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1618
1619         /* Disconnect after getting the batch */
1620         talloc_free(tree1);
1621         tree1 = NULL;
1622
1623         /*
1624          * Windows7 (build 7000) will break a batch oplock immediately if the
1625          * original client is gone. (ZML: This seems like a bug. It should give
1626          * some time for the client to reconnect!)
1627          */
1628         status = smb2_create(tree2, mem_ctx, &io2);
1629         CHECK_STATUS(status, NT_STATUS_OK);
1630         h2 = io2.out.file.handle;
1631         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1632         CHECK_VAL(io2.out.durable_open, true);
1633         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1634
1635         /* What if tree1 tries to come back and reclaim? */
1636         if (!torture_smb2_connection(tctx, &tree1)) {
1637                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1638                 ret = false;
1639                 goto done;
1640         }
1641
1642         ZERO_STRUCT(io1);
1643         io1.in.fname = fname;
1644         io1.in.durable_handle = &h1;
1645
1646         status = smb2_create(tree1, mem_ctx, &io1);
1647         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1648
1649  done:
1650         smb2_util_close(tree2, h2);
1651         smb2_util_unlink(tree2, fname);
1652
1653         talloc_free(tree1);
1654         talloc_free(tree2);
1655
1656         return ret;
1657 }
1658
1659 /*
1660   Open, disconnect, lease break, reconnect.
1661 */
1662 static bool test_durable_open_lease(struct torture_context *tctx,
1663                                     struct smb2_tree *tree1,
1664                                     struct smb2_tree *tree2)
1665 {
1666         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1667         struct smb2_create io1, io2;
1668         struct smb2_lease ls1, ls2;
1669         struct smb2_handle h1, h2;
1670         NTSTATUS status;
1671         char fname[256];
1672         bool ret = true;
1673         uint64_t lease1, lease2;
1674         uint32_t caps;
1675
1676         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1677         if (!(caps & SMB2_CAP_LEASING)) {
1678                 torture_skip(tctx, "leases are not supported");
1679         }
1680
1681         /*
1682          * Choose a random name and random lease in case the state is left a
1683          * little funky.
1684          */
1685         lease1 = random();
1686         lease2 = random();
1687         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1688
1689         /* Clean slate */
1690         smb2_util_unlink(tree1, fname);
1691
1692         /* Create with lease */
1693         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1694                           lease1, smb2_util_lease_state("RHW"));
1695         io1.in.durable_open = true;
1696
1697         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1698                           lease2, smb2_util_lease_state("RHW"));
1699         io2.in.durable_open = true;
1700         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1701
1702         status = smb2_create(tree1, mem_ctx, &io1);
1703         CHECK_STATUS(status, NT_STATUS_OK);
1704         h1 = io1.out.file.handle;
1705         CHECK_VAL(io1.out.durable_open, true);
1706         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1707
1708         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1709         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1710         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1711         CHECK_VAL(io1.out.lease_response.lease_state,
1712             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1713
1714         /* Disconnect after getting the lease */
1715         talloc_free(tree1);
1716         tree1 = NULL;
1717
1718         /*
1719          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1720          * even if the original client is gone. (ZML: This seems like a bug. It
1721          * should give some time for the client to reconnect! And why RH?)
1722          * 
1723          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1724          * Test is adapted accordingly.
1725          */
1726         status = smb2_create(tree2, mem_ctx, &io2);
1727         CHECK_STATUS(status, NT_STATUS_OK);
1728         h2 = io2.out.file.handle;
1729         CHECK_VAL(io2.out.durable_open, true);
1730         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1731
1732         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1733         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1734         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1735         CHECK_VAL(io2.out.lease_response.lease_state,
1736             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1737
1738         /* What if tree1 tries to come back and reclaim? */
1739         if (!torture_smb2_connection(tctx, &tree1)) {
1740                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1741                 ret = false;
1742                 goto done;
1743         }
1744
1745         ZERO_STRUCT(io1);
1746         io1.in.fname = fname;
1747         io1.in.durable_handle = &h1;
1748         io1.in.lease_request = &ls1;
1749
1750         status = smb2_create(tree1, mem_ctx, &io1);
1751         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1752
1753  done:
1754         smb2_util_close(tree2, h2);
1755         smb2_util_unlink(tree2, fname);
1756
1757         talloc_free(tree1);
1758         talloc_free(tree2);
1759
1760         return ret;
1761 }
1762
1763 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1764                                           struct smb2_tree *tree)
1765 {
1766         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1767         struct smb2_create io;
1768         struct smb2_handle h;
1769         struct smb2_lock lck;
1770         struct smb2_lock_element el[2];
1771         NTSTATUS status;
1772         char fname[256];
1773         bool ret = true;
1774
1775         /*
1776          */
1777         snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1778
1779         /* Clean slate */
1780         smb2_util_unlink(tree, fname);
1781
1782         /* Create with oplock */
1783
1784         smb2_oplock_create_share(&io, fname,
1785                                  smb2_util_share_access(""),
1786                                  smb2_util_oplock_level("b"));
1787         io.in.durable_open = true;
1788
1789         status = smb2_create(tree, mem_ctx, &io);
1790         CHECK_STATUS(status, NT_STATUS_OK);
1791         h = io.out.file.handle;
1792         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1793
1794         CHECK_VAL(io.out.durable_open, true);
1795         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1796
1797         ZERO_STRUCT(lck);
1798         ZERO_STRUCT(el);
1799         lck.in.locks            = el;
1800         lck.in.lock_count       = 0x0001;
1801         lck.in.lock_sequence    = 0x00000000;
1802         lck.in.file.handle      = h;
1803         el[0].offset            = 0;
1804         el[0].length            = 1;
1805         el[0].reserved          = 0x00000000;
1806         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1807         status = smb2_lock(tree, &lck);
1808         CHECK_STATUS(status, NT_STATUS_OK);
1809
1810         /* Disconnect/Reconnect. */
1811         talloc_free(tree);
1812         tree = NULL;
1813
1814         if (!torture_smb2_connection(tctx, &tree)) {
1815                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1816                 ret = false;
1817                 goto done;
1818         }
1819
1820         ZERO_STRUCT(io);
1821         io.in.fname = fname;
1822         io.in.durable_handle = &h;
1823
1824         status = smb2_create(tree, mem_ctx, &io);
1825         CHECK_STATUS(status, NT_STATUS_OK);
1826         h = io.out.file.handle;
1827
1828         lck.in.file.handle      = h;
1829         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1830         status = smb2_lock(tree, &lck);
1831         CHECK_STATUS(status, NT_STATUS_OK);
1832
1833  done:
1834         smb2_util_close(tree, h);
1835         smb2_util_unlink(tree, fname);
1836         talloc_free(tree);
1837
1838         return ret;
1839 }
1840
1841 /*
1842   Open, take BRL, disconnect, reconnect.
1843 */
1844 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1845                                          struct smb2_tree *tree)
1846 {
1847         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1848         struct smb2_create io;
1849         struct smb2_lease ls;
1850         struct smb2_handle h;
1851         struct smb2_lock lck;
1852         struct smb2_lock_element el[2];
1853         NTSTATUS status;
1854         char fname[256];
1855         bool ret = true;
1856         uint64_t lease;
1857         uint32_t caps;
1858         struct smbcli_options options;
1859
1860         options = tree->session->transport->options;
1861
1862         caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1863         if (!(caps & SMB2_CAP_LEASING)) {
1864                 torture_skip(tctx, "leases are not supported");
1865         }
1866
1867         /*
1868          * Choose a random name and random lease in case the state is left a
1869          * little funky.
1870          */
1871         lease = random();
1872         snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1873
1874         /* Clean slate */
1875         smb2_util_unlink(tree, fname);
1876
1877         /* Create with lease */
1878
1879         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1880                           smb2_util_lease_state("RWH"));
1881         io.in.durable_open              = true;
1882
1883         status = smb2_create(tree, mem_ctx, &io);
1884         CHECK_STATUS(status, NT_STATUS_OK);
1885         h = io.out.file.handle;
1886         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1887
1888         CHECK_VAL(io.out.durable_open, true);
1889         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1890         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1891         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1892         CHECK_VAL(io.out.lease_response.lease_state,
1893             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1894
1895         ZERO_STRUCT(lck);
1896         ZERO_STRUCT(el);
1897         lck.in.locks            = el;
1898         lck.in.lock_count       = 0x0001;
1899         lck.in.lock_sequence    = 0x00000000;
1900         lck.in.file.handle      = h;
1901         el[0].offset            = 0;
1902         el[0].length            = 1;
1903         el[0].reserved          = 0x00000000;
1904         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
1905         status = smb2_lock(tree, &lck);
1906         CHECK_STATUS(status, NT_STATUS_OK);
1907
1908         /* Disconnect/Reconnect. */
1909         talloc_free(tree);
1910         tree = NULL;
1911
1912         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1913                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1914                 ret = false;
1915                 goto done;
1916         }
1917
1918         ZERO_STRUCT(io);
1919         io.in.fname = fname;
1920         io.in.durable_handle = &h;
1921         io.in.lease_request = &ls;
1922
1923         status = smb2_create(tree, mem_ctx, &io);
1924         CHECK_STATUS(status, NT_STATUS_OK);
1925         h = io.out.file.handle;
1926
1927         lck.in.file.handle      = h;
1928         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
1929         status = smb2_lock(tree, &lck);
1930         CHECK_STATUS(status, NT_STATUS_OK);
1931
1932  done:
1933         smb2_util_close(tree, h);
1934         smb2_util_unlink(tree, fname);
1935         talloc_free(tree);
1936
1937         return ret;
1938 }
1939
1940 /**
1941  * Open with a RH lease, disconnect, open in another tree, reconnect.
1942  *
1943  * This test actually demonstrates a minimum level of respect for the durable
1944  * open in the face of another open. As long as this test shows an inability to
1945  * reconnect after an open, the oplock/lease tests above will certainly
1946  * demonstrate an error on reconnect.
1947  */
1948 static bool test_durable_open_open2_lease(struct torture_context *tctx,
1949                                           struct smb2_tree *tree1,
1950                                           struct smb2_tree *tree2)
1951 {
1952         TALLOC_CTX *mem_ctx = talloc_new(tctx);
1953         struct smb2_create io1, io2;
1954         struct smb2_lease ls;
1955         struct smb2_handle h1, h2;
1956         NTSTATUS status;
1957         char fname[256];
1958         bool ret = true;
1959         uint64_t lease;
1960         uint32_t caps;
1961         struct smbcli_options options;
1962
1963         options = tree1->session->transport->options;
1964
1965         caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1966         if (!(caps & SMB2_CAP_LEASING)) {
1967                 torture_skip(tctx, "leases are not supported");
1968         }
1969
1970         /*
1971          * Choose a random name and random lease in case the state is left a
1972          * little funky.
1973          */
1974         lease = random();
1975         snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1976                  generate_random_str(tctx, 8));
1977
1978         /* Clean slate */
1979         smb2_util_unlink(tree1, fname);
1980
1981         /* Create with lease */
1982         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1983                                 smb2_util_share_access(""),
1984                                 lease,
1985                                 smb2_util_lease_state("RH"));
1986         io1.in.durable_open = true;
1987
1988         status = smb2_create(tree1, mem_ctx, &io1);
1989         CHECK_STATUS(status, NT_STATUS_OK);
1990         h1 = io1.out.file.handle;
1991         CHECK_VAL(io1.out.durable_open, true);
1992         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1993
1994         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1995         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1996         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1997         CHECK_VAL(io1.out.lease_response.lease_state,
1998                   smb2_util_lease_state("RH"));
1999
2000         /* Disconnect */
2001         talloc_free(tree1);
2002         tree1 = NULL;
2003
2004         /* Open the file in tree2 */
2005         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2006
2007         status = smb2_create(tree2, mem_ctx, &io2);
2008         CHECK_STATUS(status, NT_STATUS_OK);
2009         h2 = io2.out.file.handle;
2010         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2011
2012         /* Reconnect */
2013         if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2014                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2015                 ret = false;
2016                 goto done;
2017         }
2018
2019         ZERO_STRUCT(io1);
2020         io1.in.fname = fname;
2021         io1.in.durable_handle = &h1;
2022         io1.in.lease_request = &ls;
2023
2024         /*
2025          * Windows7 (build 7000) will give away an open immediately if the
2026          * original client is gone. (ZML: This seems like a bug. It should give
2027          * some time for the client to reconnect!)
2028          */
2029         status = smb2_create(tree1, mem_ctx, &io1);
2030         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2031         h1 = io1.out.file.handle;
2032
2033  done:
2034         smb2_util_close(tree2, h2);
2035         smb2_util_unlink(tree2, fname);
2036         smb2_util_close(tree1, h1);
2037         smb2_util_unlink(tree1, fname);
2038
2039         talloc_free(tree1);
2040         talloc_free(tree2);
2041
2042         return ret;
2043 }
2044
2045 /**
2046  * Open with a batch oplock, disconnect, open in another tree, reconnect.
2047  *
2048  * This test actually demonstrates a minimum level of respect for the durable
2049  * open in the face of another open. As long as this test shows an inability to
2050  * reconnect after an open, the oplock/lease tests above will certainly
2051  * demonstrate an error on reconnect.
2052  */
2053 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2054                                            struct smb2_tree *tree1,
2055                                            struct smb2_tree *tree2)
2056 {
2057         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2058         struct smb2_create io1, io2;
2059         struct smb2_handle h1, h2;
2060         NTSTATUS status;
2061         char fname[256];
2062         bool ret = true;
2063
2064         /*
2065          * Choose a random name and random lease in case the state is left a
2066          * little funky.
2067          */
2068         snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2069                  generate_random_str(tctx, 8));
2070
2071         /* Clean slate */
2072         smb2_util_unlink(tree1, fname);
2073
2074         /* Create with batch oplock */
2075         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2076         io1.in.durable_open = true;
2077
2078         status = smb2_create(tree1, mem_ctx, &io1);
2079         CHECK_STATUS(status, NT_STATUS_OK);
2080         h1 = io1.out.file.handle;
2081         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2082         CHECK_VAL(io1.out.durable_open, true);
2083         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2084
2085         /* Disconnect */
2086         talloc_free(tree1);
2087         tree1 = NULL;
2088
2089         /* Open the file in tree2 */
2090         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2091
2092         status = smb2_create(tree2, mem_ctx, &io2);
2093         CHECK_STATUS(status, NT_STATUS_OK);
2094         h2 = io2.out.file.handle;
2095         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2096
2097         /* Reconnect */
2098         if (!torture_smb2_connection(tctx, &tree1)) {
2099                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2100                 ret = false;
2101                 goto done;
2102         }
2103
2104         ZERO_STRUCT(io1);
2105         io1.in.fname = fname;
2106         io1.in.durable_handle = &h1;
2107
2108         status = smb2_create(tree1, mem_ctx, &io1);
2109         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2110         h1 = io1.out.file.handle;
2111
2112  done:
2113         smb2_util_close(tree2, h2);
2114         smb2_util_unlink(tree2, fname);
2115         if (tree1 != NULL) {
2116                 smb2_util_close(tree1, h1);
2117                 smb2_util_unlink(tree1, fname);
2118         }
2119
2120         talloc_free(tree1);
2121         talloc_free(tree2);
2122
2123         return ret;
2124 }
2125
2126 /**
2127  * test behaviour with initial allocation size
2128  */
2129 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2130                                          struct smb2_tree *tree)
2131 {
2132         NTSTATUS status;
2133         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2134         char fname[256];
2135         struct smb2_handle _h;
2136         struct smb2_handle *h = NULL;
2137         struct smb2_create io;
2138         bool ret = true;
2139         uint64_t previous_session_id;
2140         uint64_t alloc_size_step;
2141         uint64_t initial_alloc_size = 0x100;
2142         const uint8_t *b = NULL;
2143         struct smbcli_options options;
2144
2145         options = tree->session->transport->options;
2146
2147         /* Choose a random name in case the state is left a little funky. */
2148         snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2149                  generate_random_str(tctx, 8));
2150
2151         smb2_util_unlink(tree, fname);
2152
2153         smb2_oplock_create_share(&io, fname,
2154                                  smb2_util_share_access(""),
2155                                  smb2_util_oplock_level("b"));
2156         io.in.durable_open = true;
2157         io.in.alloc_size = initial_alloc_size;
2158
2159         status = smb2_create(tree, mem_ctx, &io);
2160         CHECK_STATUS(status, NT_STATUS_OK);
2161         _h = io.out.file.handle;
2162         h = &_h;
2163         CHECK_NOT_VAL(io.out.alloc_size, 0);
2164         alloc_size_step = io.out.alloc_size;
2165         CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2166                            alloc_size_step, 0);
2167         CHECK_VAL(io.out.durable_open, true);
2168         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2169
2170         /* prepare buffer */
2171         b = talloc_zero_size(mem_ctx, alloc_size_step);
2172         CHECK_NOT_VAL(b, NULL);
2173
2174         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2175
2176         /* disconnect, reconnect and then do durable reopen */
2177         talloc_free(tree);
2178         tree = NULL;
2179
2180         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2181                                          &options, &tree))
2182         {
2183                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2184                 ret = false;
2185                 goto done;
2186         }
2187
2188         ZERO_STRUCT(io);
2189         io.in.fname = fname;
2190         io.in.durable_handle = h;
2191         h = NULL;
2192
2193         status = smb2_create(tree, mem_ctx, &io);
2194         CHECK_STATUS(status, NT_STATUS_OK);
2195         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2196                            alloc_size_step, 0);
2197         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2198         _h = io.out.file.handle;
2199         h = &_h;
2200
2201         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2202
2203         /* write one byte */
2204         status = smb2_util_write(tree, *h, b, 0, 1);
2205         CHECK_STATUS(status, NT_STATUS_OK);
2206
2207         /* disconnect, reconnect and then do durable reopen */
2208         talloc_free(tree);
2209         tree = NULL;
2210
2211         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2212                                          &options, &tree))
2213         {
2214                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2215                 ret = false;
2216                 goto done;
2217         }
2218
2219         ZERO_STRUCT(io);
2220         io.in.fname = fname;
2221         io.in.durable_handle = h;
2222         h = NULL;
2223
2224         status = smb2_create(tree, mem_ctx, &io);
2225         CHECK_STATUS(status, NT_STATUS_OK);
2226         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2227                            alloc_size_step, 1);
2228         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2229         _h = io.out.file.handle;
2230         h = &_h;
2231
2232         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2233
2234         /* write more byte than initial allocation size */
2235         status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2236
2237         /* disconnect, reconnect and then do durable reopen */
2238         talloc_free(tree);
2239         tree = NULL;
2240
2241         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2242                                          &options, &tree))
2243         {
2244                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2245                 ret = false;
2246                 goto done;
2247         }
2248
2249         ZERO_STRUCT(io);
2250         io.in.fname = fname;
2251         io.in.durable_handle = h;
2252         h = NULL;
2253
2254         status = smb2_create(tree, mem_ctx, &io);
2255         CHECK_STATUS(status, NT_STATUS_OK);
2256         CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2257                            alloc_size_step * 2, alloc_size_step + 1);
2258         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2259         _h = io.out.file.handle;
2260         h = &_h;
2261
2262 done:
2263         if (h != NULL) {
2264                 smb2_util_close(tree, *h);
2265         }
2266
2267         smb2_util_unlink(tree, fname);
2268
2269         talloc_free(tree);
2270
2271         talloc_free(mem_ctx);
2272
2273         return ret;
2274 }
2275
2276 /**
2277  * test behaviour when a disconnect happens while creating a read-only file
2278  */
2279 static bool test_durable_open_read_only(struct torture_context *tctx,
2280                                         struct smb2_tree *tree)
2281 {
2282         NTSTATUS status;
2283         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2284         char fname[256];
2285         struct smb2_handle _h;
2286         struct smb2_handle *h = NULL;
2287         struct smb2_create io;
2288         bool ret = true;
2289         uint64_t previous_session_id;
2290         const uint8_t b = 0;
2291         uint64_t alloc_size = 0;
2292         struct smbcli_options options;
2293
2294         options = tree->session->transport->options;
2295
2296         /* Choose a random name in case the state is left a little funky. */
2297         snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2298                  generate_random_str(tctx, 8));
2299
2300         smb2_util_unlink(tree, fname);
2301
2302         smb2_oplock_create_share(&io, fname,
2303                                  smb2_util_share_access(""),
2304                                  smb2_util_oplock_level("b"));
2305         io.in.durable_open = true;
2306         io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2307
2308         status = smb2_create(tree, mem_ctx, &io);
2309         CHECK_STATUS(status, NT_STATUS_OK);
2310         _h = io.out.file.handle;
2311         h = &_h;
2312         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2313         CHECK_VAL(io.out.durable_open, true);
2314         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2315
2316         previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2317
2318         /* write one byte */
2319         status = smb2_util_write(tree, *h, &b, 0, 1);
2320         CHECK_STATUS(status, NT_STATUS_OK);
2321
2322         /* disconnect, reconnect and then do durable reopen */
2323         talloc_free(tree);
2324         tree = NULL;
2325
2326         if (!torture_smb2_connection_ext(tctx, previous_session_id,
2327                                          &options, &tree))
2328         {
2329                 torture_warning(tctx, "couldn't reconnect, bailing\n");
2330                 ret = false;
2331                 goto done;
2332         }
2333
2334         ZERO_STRUCT(io);
2335         io.in.fname = fname;
2336         io.in.durable_handle = h;
2337         h = NULL;
2338
2339         status = smb2_create(tree, mem_ctx, &io);
2340         CHECK_STATUS(status, NT_STATUS_OK);
2341         alloc_size = io.out.alloc_size;
2342         CHECK_CREATED_SIZE(&io, EXISTED,
2343                            FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2344                            alloc_size, 1);
2345         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2346         _h = io.out.file.handle;
2347         h = &_h;
2348
2349         /* write one byte */
2350         status = smb2_util_write(tree, *h, &b, 1, 1);
2351         CHECK_STATUS(status, NT_STATUS_OK);
2352
2353 done:
2354         if (h != NULL) {
2355                 union smb_setfileinfo sfinfo;
2356
2357                 ZERO_STRUCT(sfinfo);
2358                 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2359                 sfinfo.basic_info.in.file.handle = *h;
2360                 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2361                 smb2_setinfo_file(tree, &sfinfo);
2362
2363                 smb2_util_close(tree, *h);
2364         }
2365
2366         smb2_util_unlink(tree, fname);
2367
2368         talloc_free(tree);
2369
2370         talloc_free(mem_ctx);
2371
2372         return ret;
2373 }
2374
2375 /**
2376  * durable open with oplock, disconnect, exit
2377  */
2378 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2379                                                 struct smb2_tree *tree)
2380 {
2381         TALLOC_CTX *mem_ctx = talloc_new(tctx);
2382         struct smb2_create io;
2383         struct smb2_handle _h;
2384         struct smb2_handle *h = NULL;
2385         NTSTATUS status;
2386         char fname[256];
2387         bool ret = true;
2388
2389         snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2390                  generate_random_str(mem_ctx, 8));
2391
2392         smb2_util_unlink(tree, fname);
2393
2394         smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2395         io.in.durable_open = true;
2396
2397         status = smb2_create(tree, mem_ctx, &io);
2398         CHECK_STATUS(status, NT_STATUS_OK);
2399
2400         _h = io.out.file.handle;
2401         h = &_h;
2402
2403         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2404         CHECK_VAL(io.out.durable_open, true);
2405         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2406
2407         /* disconnect */
2408         talloc_free(tree);
2409         tree = NULL;
2410
2411 done:
2412         if (tree != NULL) {
2413                 if (h != NULL) {
2414                         smb2_util_close(tree, *h);
2415                 }
2416                 smb2_util_unlink(tree, fname);
2417         }
2418         talloc_free(mem_ctx);
2419         return ret;
2420 }
2421
2422
2423 struct torture_suite *torture_smb2_durable_open_init(void)
2424 {
2425         struct torture_suite *suite =
2426             torture_suite_create(talloc_autofree_context(), "durable-open");
2427
2428         torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2429         torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2430         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2431         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2432         torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2433         torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2434         torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2435         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2436         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2437         torture_suite_add_1smb2_test(suite, "delete_on_close1",
2438                                      test_durable_open_delete_on_close1);
2439         torture_suite_add_1smb2_test(suite, "delete_on_close2",
2440                                      test_durable_open_delete_on_close2);
2441         torture_suite_add_1smb2_test(suite, "file-position",
2442             test_durable_open_file_position);
2443         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2444         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2445         torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2446         torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2447         torture_suite_add_2smb2_test(suite, "open2-lease",
2448                                      test_durable_open_open2_lease);
2449         torture_suite_add_2smb2_test(suite, "open2-oplock",
2450                                      test_durable_open_open2_oplock);
2451         torture_suite_add_1smb2_test(suite, "alloc-size",
2452                                      test_durable_open_alloc_size);
2453         torture_suite_add_1smb2_test(suite, "read-only",
2454                                      test_durable_open_read_only);
2455
2456         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2457
2458         return suite;
2459 }
2460
2461 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
2462 {
2463         struct torture_suite *suite =
2464             torture_suite_create(talloc_autofree_context(),
2465                                  "durable-open-disconnect");
2466
2467         torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2468                                      test_durable_open_oplock_disconnect);
2469
2470         suite->description = talloc_strdup(suite,
2471                                         "SMB2-DURABLE-OPEN-DISCONNECT tests");
2472
2473         return suite;
2474 }