s4:torture/smb2: Add torture tests for lease breaks, durable opens.
[ira/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "librpc/gen_ndr/security.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28
29 #define CHECK_VAL(v, correct) do { \
30         if ((v) != (correct)) { \
31                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
32                                 __location__, #v, (int)v, (int)correct); \
33                 ret = false; \
34         }} while (0)
35
36 #define CHECK_STATUS(status, correct) do { \
37         if (!NT_STATUS_EQUAL(status, correct)) { \
38                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
39                        nt_errstr(status), nt_errstr(correct)); \
40                 ret = false; \
41                 goto done; \
42         }} while (0)
43
44 #define CHECK_CREATED(__io, __created, __attribute)                     \
45         do {                                                            \
46                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
47                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
48                 CHECK_VAL((__io)->out.size, 0);                         \
49                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
50                 CHECK_VAL((__io)->out.reserved2, 0);                    \
51         } while(0)
52
53 /*
54    basic testing of SMB2 durable opens
55    regarding the position information on the handle
56 */
57 bool test_durable_open_file_position(struct torture_context *tctx,
58                                      struct smb2_tree *tree1,
59                                      struct smb2_tree *tree2)
60 {
61         TALLOC_CTX *mem_ctx = talloc_new(tctx);
62         struct smb2_handle h1, h2;
63         struct smb2_create io1, io2;
64         NTSTATUS status;
65         const char *fname = "durable_open_position.dat";
66         union smb_fileinfo qfinfo;
67         union smb_setfileinfo sfinfo;
68         bool ret = true;
69         uint64_t pos;
70
71         smb2_util_unlink(tree1, fname);
72
73         ZERO_STRUCT(io1);
74         io1.in.security_flags           = 0x00;
75         io1.in.oplock_level             = SMB2_OPLOCK_LEVEL_BATCH;
76         io1.in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
77         io1.in.create_flags             = 0x00000000;
78         io1.in.reserved                 = 0x00000000;
79         io1.in.desired_access           = SEC_RIGHTS_FILE_ALL;
80         io1.in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
81         io1.in.share_access             = NTCREATEX_SHARE_ACCESS_READ |
82                                           NTCREATEX_SHARE_ACCESS_WRITE |
83                                           NTCREATEX_SHARE_ACCESS_DELETE;
84         io1.in.create_disposition       = NTCREATEX_DISP_OPEN_IF;
85         io1.in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
86                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
87                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
88                                           0x00200000;
89         io1.in.durable_open             = true;
90         io1.in.fname                    = fname;
91
92         status = smb2_create(tree1, mem_ctx, &io1);
93         CHECK_STATUS(status, NT_STATUS_OK);
94         h1 = io1.out.file.handle;
95         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
96         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
97
98         /* TODO: check extra blob content */
99
100         ZERO_STRUCT(qfinfo);
101         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
102         qfinfo.generic.in.file.handle = h1;
103         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
104         CHECK_STATUS(status, NT_STATUS_OK);
105         CHECK_VAL(qfinfo.position_information.out.position, 0);
106         pos = qfinfo.position_information.out.position;
107         torture_comment(tctx, "position: %llu\n",
108                         (unsigned long long)pos);
109
110         ZERO_STRUCT(sfinfo);
111         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
112         sfinfo.generic.in.file.handle = h1;
113         sfinfo.position_information.in.position = 0x1000;
114         status = smb2_setinfo_file(tree1, &sfinfo);
115         CHECK_STATUS(status, NT_STATUS_OK);
116
117         ZERO_STRUCT(qfinfo);
118         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
119         qfinfo.generic.in.file.handle = h1;
120         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
121         CHECK_STATUS(status, NT_STATUS_OK);
122         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
123         pos = qfinfo.position_information.out.position;
124         torture_comment(tctx, "position: %llu\n",
125                         (unsigned long long)pos);
126
127         talloc_free(tree1);
128         tree1 = NULL;
129
130         ZERO_STRUCT(qfinfo);
131         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
132         qfinfo.generic.in.file.handle = h1;
133         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
134         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
135
136         ZERO_STRUCT(io2);
137         io2.in.fname = fname;
138         io2.in.durable_handle = &h1;
139
140         status = smb2_create(tree2, mem_ctx, &io2);
141         CHECK_STATUS(status, NT_STATUS_OK);
142         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
143         CHECK_VAL(io2.out.reserved, 0x00);
144         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
145         CHECK_VAL(io2.out.alloc_size, 0);
146         CHECK_VAL(io2.out.size, 0);
147         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
148         CHECK_VAL(io2.out.reserved2, 0);
149
150         h2 = io2.out.file.handle;
151
152         ZERO_STRUCT(qfinfo);
153         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
154         qfinfo.generic.in.file.handle = h2;
155         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
156         CHECK_STATUS(status, NT_STATUS_OK);
157         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
158         pos = qfinfo.position_information.out.position;
159         torture_comment(tctx, "position: %llu\n",
160                         (unsigned long long)pos);
161
162         smb2_util_close(tree2, h2);
163
164         talloc_free(mem_ctx);
165
166         smb2_util_unlink(tree2, fname);
167 done:
168         return ret;
169 }
170
171 /*
172   Open, disconnect, oplock break, reconnect.
173 */
174 bool test_durable_open_oplock(struct torture_context *tctx,
175                               struct smb2_tree *tree1,
176                               struct smb2_tree *tree2)
177 {
178         TALLOC_CTX *mem_ctx = talloc_new(tctx);
179         struct smb2_create io1, io2;
180         struct smb2_handle h1, h2;
181         NTSTATUS status;
182         char fname[256];
183         bool ret = true;
184
185         /* Choose a random name in case the state is left a little funky. */
186         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
187
188         /* Clean slate */
189         smb2_util_unlink(tree1, fname);
190
191         /* Create with batch oplock */
192         ZERO_STRUCT(io1);
193         io1.in.security_flags           = 0x00;
194         io1.in.oplock_level             = SMB2_OPLOCK_LEVEL_BATCH;
195         io1.in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
196         io1.in.create_flags             = 0x00000000;
197         io1.in.reserved                 = 0x00000000;
198         io1.in.desired_access           = SEC_RIGHTS_FILE_ALL;
199         io1.in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
200         io1.in.share_access             = NTCREATEX_SHARE_ACCESS_READ |
201                                           NTCREATEX_SHARE_ACCESS_WRITE |
202                                           NTCREATEX_SHARE_ACCESS_DELETE;
203         io1.in.create_disposition       = NTCREATEX_DISP_OPEN_IF;
204         io1.in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
205                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
206                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
207                                           0x00200000;
208         io1.in.fname                    = fname;
209         io1.in.durable_open             = true;
210
211         io2 = io1;
212         io2.in.create_disposition       = NTCREATEX_DISP_OPEN;
213
214         status = smb2_create(tree1, mem_ctx, &io1);
215         CHECK_STATUS(status, NT_STATUS_OK);
216         h1 = io1.out.file.handle;
217         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
218         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
219
220         /* Disconnect after getting the batch */
221         talloc_free(tree1);
222         tree1 = NULL;
223
224         /*
225          * Windows7 (build 7000) will break a batch oplock immediately if the
226          * original client is gone. (ZML: This seems like a bug. It should give
227          * some time for the client to reconnect!)
228          */
229         status = smb2_create(tree2, mem_ctx, &io2);
230         CHECK_STATUS(status, NT_STATUS_OK);
231         h2 = io2.out.file.handle;
232         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
233         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
234
235         /* What if tree1 tries to come back and reclaim? */
236         if (!torture_smb2_connection(tctx, &tree1)) {
237                 torture_warning(tctx, "couldn't reconnect, bailing\n");
238                 ret = false;
239                 goto done;
240         }
241
242         ZERO_STRUCT(io1);
243         io1.in.fname = fname;
244         io1.in.durable_handle = &h1;
245
246         status = smb2_create(tree1, mem_ctx, &io1);
247         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
248
249  done:
250         smb2_util_close(tree2, h2);
251         smb2_util_unlink(tree2, fname);
252
253         return ret;
254 }
255
256 /*
257   Open, disconnect, lease break, reconnect.
258 */
259 bool test_durable_open_lease(struct torture_context *tctx,
260                              struct smb2_tree *tree1,
261                              struct smb2_tree *tree2)
262 {
263         TALLOC_CTX *mem_ctx = talloc_new(tctx);
264         struct smb2_create io1, io2;
265         struct smb2_lease ls1, ls2;
266         struct smb2_handle h1, h2;
267         NTSTATUS status;
268         char fname[256];
269         bool ret = true;
270         uint64_t lease1, lease2;
271
272         /*
273          * Choose a random name and random lease in case the state is left a
274          * little funky.
275          */
276         lease1 = random();
277         lease2 = random();
278         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
279
280         /* Clean slate */
281         smb2_util_unlink(tree1, fname);
282
283         /* Create with lease */
284         ZERO_STRUCT(io1);
285         io1.in.security_flags           = 0x00;
286         io1.in.oplock_level             = SMB2_OPLOCK_LEVEL_LEASE;
287         io1.in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
288         io1.in.create_flags             = 0x00000000;
289         io1.in.reserved                 = 0x00000000;
290         io1.in.desired_access           = SEC_RIGHTS_FILE_ALL;
291         io1.in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
292         io1.in.share_access             = NTCREATEX_SHARE_ACCESS_READ |
293                                           NTCREATEX_SHARE_ACCESS_WRITE |
294                                           NTCREATEX_SHARE_ACCESS_DELETE;
295         io1.in.create_disposition       = NTCREATEX_DISP_OPEN_IF;
296         io1.in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
297                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
298                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
299                                           0x00200000;
300         io1.in.fname                    = fname;
301         io1.in.durable_open             = true;
302
303         ZERO_STRUCT(ls1);
304         ls1.lease_key.data[0] = lease1;
305         ls1.lease_key.data[1] = ~lease1;
306         ls1.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
307         io1.in.lease_request = &ls1;
308
309         io2 = io1;
310         ls2 = ls1;
311         ls2.lease_key.data[0] = lease2;
312         ls2.lease_key.data[1] = ~lease2;
313         io2.in.lease_request = &ls2;
314         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
315
316         status = smb2_create(tree1, mem_ctx, &io1);
317         CHECK_STATUS(status, NT_STATUS_OK);
318         h1 = io1.out.file.handle;
319         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
320
321         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
322         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
323         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
324         CHECK_VAL(io1.out.lease_response.lease_state,
325             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
326
327         /* Disconnect after getting the lease */
328         talloc_free(tree1);
329         tree1 = NULL;
330
331         /*
332          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
333          * even if the original client is gone. (ZML: This seems like a bug. It
334          * should give some time for the client to reconnect! And why RH?)
335          */
336         status = smb2_create(tree2, mem_ctx, &io2);
337         CHECK_STATUS(status, NT_STATUS_OK);
338         h2 = io2.out.file.handle;
339         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
340
341         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
342         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
343         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
344         CHECK_VAL(io2.out.lease_response.lease_state,
345             SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
346
347         /* What if tree1 tries to come back and reclaim? */
348         if (!torture_smb2_connection(tctx, &tree1)) {
349                 torture_warning(tctx, "couldn't reconnect, bailing\n");
350                 ret = false;
351                 goto done;
352         }
353
354         ZERO_STRUCT(io1);
355         io1.in.fname = fname;
356         io1.in.durable_handle = &h1;
357         io1.in.lease_request = &ls1;
358
359         status = smb2_create(tree1, mem_ctx, &io1);
360         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
361
362  done:
363         smb2_util_close(tree2, h2);
364         smb2_util_unlink(tree2, fname);
365
366         return ret;
367 }
368
369 /*
370   Open, take BRL, disconnect, reconnect.
371 */
372 bool test_durable_open_lock(struct torture_context *tctx,
373                             struct smb2_tree *tree)
374 {
375         TALLOC_CTX *mem_ctx = talloc_new(tctx);
376         struct smb2_create io;
377         struct smb2_lease ls;
378         struct smb2_handle h;
379         struct smb2_lock lck;
380         struct smb2_lock_element el[2];
381         NTSTATUS status;
382         char fname[256];
383         bool ret = true;
384         uint64_t lease;
385
386         /*
387          * Choose a random name and random lease in case the state is left a
388          * little funky.
389          */
390         lease = random();
391         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
392
393         /* Clean slate */
394         smb2_util_unlink(tree, fname);
395
396         /* Create with lease */
397         ZERO_STRUCT(io);
398         io.in.security_flags            = 0x00;
399         io.in.oplock_level              = SMB2_OPLOCK_LEVEL_LEASE;
400         io.in.impersonation_level       = NTCREATEX_IMPERSONATION_IMPERSONATION;
401         io.in.create_flags              = 0x00000000;
402         io.in.reserved                  = 0x00000000;
403         io.in.desired_access            = SEC_RIGHTS_FILE_ALL;
404         io.in.file_attributes           = FILE_ATTRIBUTE_NORMAL;
405         io.in.share_access              = NTCREATEX_SHARE_ACCESS_READ |
406                                           NTCREATEX_SHARE_ACCESS_WRITE |
407                                           NTCREATEX_SHARE_ACCESS_DELETE;
408         io.in.create_disposition        = NTCREATEX_DISP_OPEN_IF;
409         io.in.create_options            = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
410                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
411                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
412                                           0x00200000;
413         io.in.fname                     = fname;
414         io.in.durable_open              = true;
415
416         ZERO_STRUCT(ls);
417         ls.lease_key.data[0] = lease;
418         ls.lease_key.data[1] = ~lease;
419         ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
420         io.in.lease_request = &ls;
421
422         status = smb2_create(tree, mem_ctx, &io);
423         CHECK_STATUS(status, NT_STATUS_OK);
424         h = io.out.file.handle;
425         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
426
427         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
428         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
429         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
430         CHECK_VAL(io.out.lease_response.lease_state,
431             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
432
433         ZERO_STRUCT(lck);
434         ZERO_STRUCT(el);
435         lck.in.locks            = el;
436         lck.in.lock_count       = 0x0001;
437         lck.in.reserved         = 0x00000000;
438         lck.in.file.handle      = h;
439         el[0].offset            = 0;
440         el[0].length            = 1;
441         el[0].reserved          = 0x00000000;
442         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
443         status = smb2_lock(tree, &lck);
444         CHECK_STATUS(status, NT_STATUS_OK);
445
446         /* Disconnect/Reconnect. */
447         talloc_free(tree);
448         tree = NULL;
449
450         if (!torture_smb2_connection(tctx, &tree)) {
451                 torture_warning(tctx, "couldn't reconnect, bailing\n");
452                 ret = false;
453                 goto done;
454         }
455
456         ZERO_STRUCT(io);
457         io.in.fname = fname;
458         io.in.durable_handle = &h;
459         io.in.lease_request = &ls;
460
461         status = smb2_create(tree, mem_ctx, &io);
462         CHECK_STATUS(status, NT_STATUS_OK);
463         h = io.out.file.handle;
464
465         lck.in.file.handle      = h;
466         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
467         status = smb2_lock(tree, &lck);
468         CHECK_STATUS(status, NT_STATUS_OK);
469
470  done:
471         smb2_util_close(tree, h);
472         smb2_util_unlink(tree, fname);
473
474         return ret;
475 }
476
477 /*
478   Open, disconnect, open in another tree, reconnect.
479
480   This test actually demonstrates a minimum level of respect for the durable
481   open in the face of another open. As long as this test shows an inability to
482   reconnect after an open, the oplock/lease tests above will certainly
483   demonstrate an error on reconnect.
484 */
485 bool test_durable_open_open(struct torture_context *tctx,
486                             struct smb2_tree *tree1,
487                             struct smb2_tree *tree2)
488 {
489         TALLOC_CTX *mem_ctx = talloc_new(tctx);
490         struct smb2_create io1, io2;
491         struct smb2_lease ls;
492         struct smb2_handle h1, h2;
493         NTSTATUS status;
494         char fname[256];
495         bool ret = true;
496         uint64_t lease;
497
498         /*
499          * Choose a random name and random lease in case the state is left a
500          * little funky.
501          */
502         lease = random();
503         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
504
505         /* Clean slate */
506         smb2_util_unlink(tree1, fname);
507
508         /* Create with lease */
509         ZERO_STRUCT(io1);
510         io1.in.security_flags           = 0x00;
511         io1.in.oplock_level             = SMB2_OPLOCK_LEVEL_LEASE;
512         io1.in.impersonation_level      = NTCREATEX_IMPERSONATION_IMPERSONATION;
513         io1.in.create_flags             = 0x00000000;
514         io1.in.reserved                 = 0x00000000;
515         io1.in.desired_access           = SEC_RIGHTS_FILE_ALL;
516         io1.in.file_attributes          = FILE_ATTRIBUTE_NORMAL;
517         io1.in.share_access             = NTCREATEX_SHARE_ACCESS_NONE;
518         io1.in.create_disposition       = NTCREATEX_DISP_OPEN_IF;
519         io1.in.create_options           = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
520                                           NTCREATEX_OPTIONS_ASYNC_ALERT |
521                                           NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
522                                           0x00200000;
523         io1.in.fname                    = fname;
524         io1.in.durable_open             = true;
525
526         io2 = io1;
527         io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
528         io2.in.durable_open = false;
529
530         ZERO_STRUCT(ls);
531         ls.lease_key.data[0] = lease;
532         ls.lease_key.data[1] = ~lease;
533         ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
534         io1.in.lease_request = &ls;
535
536         status = smb2_create(tree1, mem_ctx, &io1);
537         CHECK_STATUS(status, NT_STATUS_OK);
538         h1 = io1.out.file.handle;
539         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
540
541         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
542         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
543         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
544         CHECK_VAL(io1.out.lease_response.lease_state,
545             SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
546
547         /* Disconnect */
548         talloc_free(tree1);
549         tree1 = NULL;
550
551         /* Open the file in tree2 */
552         status = smb2_create(tree2, mem_ctx, &io2);
553         CHECK_STATUS(status, NT_STATUS_OK);
554         h2 = io2.out.file.handle;
555         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
556
557         /* Reconnect */
558         if (!torture_smb2_connection(tctx, &tree1)) {
559                 torture_warning(tctx, "couldn't reconnect, bailing\n");
560                 ret = false;
561                 goto done;
562         }
563
564         ZERO_STRUCT(io1);
565         io1.in.fname = fname;
566         io1.in.durable_handle = &h1;
567         io1.in.lease_request = &ls;
568
569         /*
570          * Windows7 (build 7000) will give away an open immediately if the
571          * original client is gone. (ZML: This seems like a bug. It should give
572          * some time for the client to reconnect!)
573          */
574         status = smb2_create(tree1, mem_ctx, &io1);
575         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
576         h1 = io1.out.file.handle;
577
578  done:
579         smb2_util_close(tree2, h2);
580         smb2_util_unlink(tree2, fname);
581         smb2_util_close(tree1, h1);
582         smb2_util_unlink(tree1, fname);
583
584         return ret;
585 }
586
587 struct torture_suite *torture_smb2_durable_open_init(void)
588 {
589         struct torture_suite *suite =
590             torture_suite_create(talloc_autofree_context(), "DURABLE-OPEN");
591
592         torture_suite_add_2smb2_test(suite, "FILE-POSITION",
593             test_durable_open_file_position);
594         torture_suite_add_2smb2_test(suite, "OPLOCK", test_durable_open_oplock);
595         torture_suite_add_2smb2_test(suite, "LEASE", test_durable_open_lease);
596         torture_suite_add_1smb2_test(suite, "LOCK", test_durable_open_lock);
597         torture_suite_add_2smb2_test(suite, "OPEN", test_durable_open_open);
598
599         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
600
601         return suite;
602 }