s4:torture:smb2: rewrite durable-open.open test to use smb2_*_create functions
[metze/samba/wip.git] / source4 / torture / smb2 / durable_open.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    test suite for SMB2 durable opens
5
6    Copyright (C) Stefan Metzmacher 2008
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27
28 #define CHECK_VAL(v, correct) do { \
29         if ((v) != (correct)) { \
30                 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
31                                 __location__, #v, (int)v, (int)correct); \
32                 ret = false; \
33         }} while (0)
34
35 #define CHECK_STATUS(status, correct) do { \
36         if (!NT_STATUS_EQUAL(status, correct)) { \
37                 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
38                        nt_errstr(status), nt_errstr(correct)); \
39                 ret = false; \
40                 goto done; \
41         }} while (0)
42
43 #define CHECK_CREATED(__io, __created, __attribute)                     \
44         do {                                                            \
45                 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46                 CHECK_VAL((__io)->out.alloc_size, 0);                   \
47                 CHECK_VAL((__io)->out.size, 0);                         \
48                 CHECK_VAL((__io)->out.file_attr, (__attribute));        \
49                 CHECK_VAL((__io)->out.reserved2, 0);                    \
50         } while(0)
51
52
53 /**
54  * basic durable_open test.
55  * durable state should only be granted when requested
56  * along with a batch oplock or a handle lease.
57  *
58  * This test tests durable open with all possible oplock types.
59  */
60
61 struct durable_open_vs_oplock {
62         const char *level;
63         const char *share_mode;
64         bool expected;
65 };
66
67 #define NUM_OPLOCK_TYPES 4
68 #define NUM_SHARE_MODES 8
69 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
70 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
71 {
72         { "", "", false },
73         { "", "R", false },
74         { "", "W", false },
75         { "", "D", false },
76         { "", "RD", false },
77         { "", "RW", false },
78         { "", "WD", false },
79         { "", "RWD", false },
80
81         { "s", "", false },
82         { "s", "R", false },
83         { "s", "W", false },
84         { "s", "D", false },
85         { "s", "RD", false },
86         { "s", "RW", false },
87         { "s", "WD", false },
88         { "s", "RWD", false },
89
90         { "x", "", false },
91         { "x", "R", false },
92         { "x", "W", false },
93         { "x", "D", false },
94         { "x", "RD", false },
95         { "x", "RW", false },
96         { "x", "WD", false },
97         { "x", "RWD", false },
98
99         { "b", "", true },
100         { "b", "R", true },
101         { "b", "W", true },
102         { "b", "D", true },
103         { "b", "RD", true },
104         { "b", "RW", true },
105         { "b", "WD", true },
106         { "b", "RWD", true },
107 };
108
109 static bool test_one_durable_open_open1(struct torture_context *tctx,
110                                         struct smb2_tree *tree,
111                                         const char *fname,
112                                         struct durable_open_vs_oplock test)
113 {
114         NTSTATUS status;
115         TALLOC_CTX *mem_ctx = talloc_new(tctx);
116         struct smb2_handle _h;
117         struct smb2_handle *h = NULL;
118         bool ret = true;
119         struct smb2_create io;
120
121         smb2_util_unlink(tree, fname);
122
123         smb2_oplock_create_share(&io, fname,
124                                  smb2_util_share_access(test.share_mode),
125                                  smb2_util_oplock_level(test.level));
126         io.in.durable_open = true;
127
128         status = smb2_create(tree, mem_ctx, &io);
129         CHECK_STATUS(status, NT_STATUS_OK);
130         _h = io.out.file.handle;
131         h = &_h;
132         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
133         CHECK_VAL(io.out.durable_open, test.expected);
134         CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
135
136 done:
137         if (h != NULL) {
138                 smb2_util_close(tree, *h);
139         }
140         smb2_util_unlink(tree, fname);
141         talloc_free(mem_ctx);
142
143         return ret;
144 }
145
146 bool test_durable_open_open1(struct torture_context *tctx,
147                              struct smb2_tree *tree)
148 {
149         TALLOC_CTX *mem_ctx = talloc_new(tctx);
150         char fname[256];
151         bool ret = true;
152         int i;
153
154         /* Choose a random name in case the state is left a little funky. */
155         snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
156
157         smb2_util_unlink(tree, fname);
158
159         /* test various oplock levels with durable open */
160
161         for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
162                 ret = test_one_durable_open_open1(tctx,
163                                                   tree,
164                                                   fname,
165                                                   durable_open_vs_oplock_table[i]);
166                 if (ret == false) {
167                         goto done;
168                 }
169         }
170
171 done:
172         smb2_util_unlink(tree, fname);
173         talloc_free(tree);
174         talloc_free(mem_ctx);
175
176         return ret;
177 }
178
179 /**
180  * basic durable_open test.
181  * durable state should only be granted when requested
182  * along with a batch oplock or a handle lease.
183  *
184  * This test tests durable open with all valid lease types.
185  */
186
187 struct durable_open_vs_lease {
188         const char *type;
189         const char *share_mode;
190         bool expected;
191 };
192
193 #define NUM_LEASE_TYPES 5
194 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
195 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
196 {
197         { "", "", false },
198         { "", "R", false },
199         { "", "W", false },
200         { "", "D", false },
201         { "", "RW", false },
202         { "", "RD", false },
203         { "", "WD", false },
204         { "", "RWD", false },
205
206         { "R", "", false },
207         { "R", "R", false },
208         { "R", "W", false },
209         { "R", "D", false },
210         { "R", "RW", false },
211         { "R", "RD", false },
212         { "R", "DW", false },
213         { "R", "RWD", false },
214
215         { "RW", "", false },
216         { "RW", "R", false },
217         { "RW", "W", false },
218         { "RW", "D", false },
219         { "RW", "RW", false },
220         { "RW", "RD", false },
221         { "RW", "WD", false },
222         { "RW", "RWD", false },
223
224         { "RH", "", true },
225         { "RH", "R", true },
226         { "RH", "W", true },
227         { "RH", "D", true },
228         { "RH", "RW", true },
229         { "RH", "RD", true },
230         { "RH", "WD", true },
231         { "RH", "RWD", true },
232
233         { "RHW", "", true },
234         { "RHW", "R", true },
235         { "RHW", "W", true },
236         { "RHW", "D", true },
237         { "RHW", "RW", true },
238         { "RHW", "RD", true },
239         { "RHW", "WD", true },
240         { "RHW", "RWD", true },
241 };
242
243 static bool test_one_durable_open_open2(struct torture_context *tctx,
244                                         struct smb2_tree *tree,
245                                         const char *fname,
246                                         struct durable_open_vs_lease test)
247 {
248         NTSTATUS status;
249         TALLOC_CTX *mem_ctx = talloc_new(tctx);
250         struct smb2_handle _h;
251         struct smb2_handle *h = NULL;
252         bool ret = true;
253         struct smb2_create io;
254         struct smb2_lease ls;
255         uint64_t lease;
256
257         smb2_util_unlink(tree, fname);
258
259         lease = random();
260
261         smb2_lease_create_share(&io, &ls, false /* dir */, fname,
262                                 smb2_util_share_access(test.share_mode),
263                                 lease,
264                                 smb2_util_lease_state(test.type));
265         io.in.durable_open = true;
266
267         status = smb2_create(tree, mem_ctx, &io);
268         CHECK_STATUS(status, NT_STATUS_OK);
269         _h = io.out.file.handle;
270         h = &_h;
271         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
272         CHECK_VAL(io.out.durable_open, test.expected);
273         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
274         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
275         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
276         CHECK_VAL(io.out.lease_response.lease_state,
277                   smb2_util_lease_state(test.type));
278 done:
279         if (h != NULL) {
280                 smb2_util_close(tree, *h);
281         }
282         smb2_util_unlink(tree, fname);
283         talloc_free(mem_ctx);
284
285         return ret;
286 }
287
288 bool test_durable_open_open2(struct torture_context *tctx,
289                              struct smb2_tree *tree)
290 {
291         TALLOC_CTX *mem_ctx = talloc_new(tctx);
292         char fname[256];
293         bool ret = true;
294         int i;
295
296         /* Choose a random name in case the state is left a little funky. */
297         snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
298
299         smb2_util_unlink(tree, fname);
300
301
302         /* test various oplock levels with durable open */
303
304         for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
305                 ret = test_one_durable_open_open2(tctx,
306                                                   tree,
307                                                   fname,
308                                                   durable_open_vs_lease_table[i]);
309                 if (ret == false) {
310                         goto done;
311                 }
312         }
313
314 done:
315         smb2_util_unlink(tree, fname);
316         talloc_free(tree);
317         talloc_free(mem_ctx);
318
319         return ret;
320 }
321
322 /*
323    basic testing of SMB2 durable opens
324    regarding the position information on the handle
325 */
326 bool test_durable_open_file_position(struct torture_context *tctx,
327                                      struct smb2_tree *tree1,
328                                      struct smb2_tree *tree2)
329 {
330         TALLOC_CTX *mem_ctx = talloc_new(tctx);
331         struct smb2_handle h1, h2;
332         struct smb2_create io1, io2;
333         NTSTATUS status;
334         const char *fname = "durable_open_position.dat";
335         union smb_fileinfo qfinfo;
336         union smb_setfileinfo sfinfo;
337         bool ret = true;
338         uint64_t pos;
339
340         smb2_util_unlink(tree1, fname);
341
342         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
343         io1.in.durable_open = true;
344
345         status = smb2_create(tree1, mem_ctx, &io1);
346         CHECK_STATUS(status, NT_STATUS_OK);
347         h1 = io1.out.file.handle;
348         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
349         CHECK_VAL(io1.out.durable_open, true);
350         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
351
352         /* TODO: check extra blob content */
353
354         ZERO_STRUCT(qfinfo);
355         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
356         qfinfo.generic.in.file.handle = h1;
357         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
358         CHECK_STATUS(status, NT_STATUS_OK);
359         CHECK_VAL(qfinfo.position_information.out.position, 0);
360         pos = qfinfo.position_information.out.position;
361         torture_comment(tctx, "position: %llu\n",
362                         (unsigned long long)pos);
363
364         ZERO_STRUCT(sfinfo);
365         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
366         sfinfo.generic.in.file.handle = h1;
367         sfinfo.position_information.in.position = 0x1000;
368         status = smb2_setinfo_file(tree1, &sfinfo);
369         CHECK_STATUS(status, NT_STATUS_OK);
370
371         ZERO_STRUCT(qfinfo);
372         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
373         qfinfo.generic.in.file.handle = h1;
374         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
375         CHECK_STATUS(status, NT_STATUS_OK);
376         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
377         pos = qfinfo.position_information.out.position;
378         torture_comment(tctx, "position: %llu\n",
379                         (unsigned long long)pos);
380
381         talloc_free(tree1);
382         tree1 = NULL;
383
384         ZERO_STRUCT(qfinfo);
385         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
386         qfinfo.generic.in.file.handle = h1;
387         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
388         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
389
390         ZERO_STRUCT(io2);
391         io2.in.fname = fname;
392         io2.in.durable_handle = &h1;
393
394         status = smb2_create(tree2, mem_ctx, &io2);
395         CHECK_STATUS(status, NT_STATUS_OK);
396         CHECK_VAL(io2.out.durable_open, true);
397         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
398         CHECK_VAL(io2.out.reserved, 0x00);
399         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
400         CHECK_VAL(io2.out.alloc_size, 0);
401         CHECK_VAL(io2.out.size, 0);
402         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
403         CHECK_VAL(io2.out.reserved2, 0);
404
405         h2 = io2.out.file.handle;
406
407         ZERO_STRUCT(qfinfo);
408         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
409         qfinfo.generic.in.file.handle = h2;
410         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
411         CHECK_STATUS(status, NT_STATUS_OK);
412         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
413         pos = qfinfo.position_information.out.position;
414         torture_comment(tctx, "position: %llu\n",
415                         (unsigned long long)pos);
416
417         smb2_util_close(tree2, h2);
418
419         talloc_free(mem_ctx);
420
421         smb2_util_unlink(tree2, fname);
422 done:
423         talloc_free(tree1);
424         talloc_free(tree2);
425
426         return ret;
427 }
428
429 /*
430   Open, disconnect, oplock break, reconnect.
431 */
432 bool test_durable_open_oplock(struct torture_context *tctx,
433                               struct smb2_tree *tree1,
434                               struct smb2_tree *tree2)
435 {
436         TALLOC_CTX *mem_ctx = talloc_new(tctx);
437         struct smb2_create io1, io2;
438         struct smb2_handle h1, h2;
439         NTSTATUS status;
440         char fname[256];
441         bool ret = true;
442
443         /* Choose a random name in case the state is left a little funky. */
444         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
445
446         /* Clean slate */
447         smb2_util_unlink(tree1, fname);
448
449         /* Create with batch oplock */
450         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
451         io1.in.durable_open = true;
452
453         io2 = io1;
454         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
455
456         status = smb2_create(tree1, mem_ctx, &io1);
457         CHECK_STATUS(status, NT_STATUS_OK);
458         h1 = io1.out.file.handle;
459         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
460         CHECK_VAL(io1.out.durable_open, true);
461         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
462
463         /* Disconnect after getting the batch */
464         talloc_free(tree1);
465         tree1 = NULL;
466
467         /*
468          * Windows7 (build 7000) will break a batch oplock immediately if the
469          * original client is gone. (ZML: This seems like a bug. It should give
470          * some time for the client to reconnect!)
471          */
472         status = smb2_create(tree2, mem_ctx, &io2);
473         CHECK_STATUS(status, NT_STATUS_OK);
474         h2 = io2.out.file.handle;
475         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
476         CHECK_VAL(io2.out.durable_open, true);
477         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
478
479         /* What if tree1 tries to come back and reclaim? */
480         if (!torture_smb2_connection(tctx, &tree1)) {
481                 torture_warning(tctx, "couldn't reconnect, bailing\n");
482                 ret = false;
483                 goto done;
484         }
485
486         ZERO_STRUCT(io1);
487         io1.in.fname = fname;
488         io1.in.durable_handle = &h1;
489
490         status = smb2_create(tree1, mem_ctx, &io1);
491         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
492
493  done:
494         smb2_util_close(tree2, h2);
495         smb2_util_unlink(tree2, fname);
496
497         talloc_free(tree1);
498         talloc_free(tree2);
499
500         return ret;
501 }
502
503 /*
504   Open, disconnect, lease break, reconnect.
505 */
506 bool test_durable_open_lease(struct torture_context *tctx,
507                              struct smb2_tree *tree1,
508                              struct smb2_tree *tree2)
509 {
510         TALLOC_CTX *mem_ctx = talloc_new(tctx);
511         struct smb2_create io1, io2;
512         struct smb2_lease ls1, ls2;
513         struct smb2_handle h1, h2;
514         NTSTATUS status;
515         char fname[256];
516         bool ret = true;
517         uint64_t lease1, lease2;
518
519         /*
520          * Choose a random name and random lease in case the state is left a
521          * little funky.
522          */
523         lease1 = random();
524         lease2 = random();
525         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
526
527         /* Clean slate */
528         smb2_util_unlink(tree1, fname);
529
530         /* Create with lease */
531         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
532                           lease1, smb2_util_lease_state("RHW"));
533         io1.in.durable_open = true;
534
535         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
536                           lease2, smb2_util_lease_state("RHW"));
537         io2.in.durable_open = true;
538         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
539
540         status = smb2_create(tree1, mem_ctx, &io1);
541         CHECK_STATUS(status, NT_STATUS_OK);
542         h1 = io1.out.file.handle;
543         CHECK_VAL(io1.out.durable_open, true);
544         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
545
546         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
547         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
548         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
549         CHECK_VAL(io1.out.lease_response.lease_state,
550             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
551
552         /* Disconnect after getting the lease */
553         talloc_free(tree1);
554         tree1 = NULL;
555
556         /*
557          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
558          * even if the original client is gone. (ZML: This seems like a bug. It
559          * should give some time for the client to reconnect! And why RH?)
560          * 
561          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
562          * Test is adapted accordingly.
563          */
564         status = smb2_create(tree2, mem_ctx, &io2);
565         CHECK_STATUS(status, NT_STATUS_OK);
566         h2 = io2.out.file.handle;
567         CHECK_VAL(io2.out.durable_open, true);
568         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
569
570         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
571         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
572         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
573         CHECK_VAL(io2.out.lease_response.lease_state,
574             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
575
576         /* What if tree1 tries to come back and reclaim? */
577         if (!torture_smb2_connection(tctx, &tree1)) {
578                 torture_warning(tctx, "couldn't reconnect, bailing\n");
579                 ret = false;
580                 goto done;
581         }
582
583         ZERO_STRUCT(io1);
584         io1.in.fname = fname;
585         io1.in.durable_handle = &h1;
586         io1.in.lease_request = &ls1;
587
588         status = smb2_create(tree1, mem_ctx, &io1);
589         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
590
591  done:
592         smb2_util_close(tree2, h2);
593         smb2_util_unlink(tree2, fname);
594
595         talloc_free(tree1);
596         talloc_free(tree2);
597
598         return ret;
599 }
600
601 /*
602   Open, take BRL, disconnect, reconnect.
603 */
604 bool test_durable_open_lock(struct torture_context *tctx,
605                             struct smb2_tree *tree)
606 {
607         TALLOC_CTX *mem_ctx = talloc_new(tctx);
608         struct smb2_create io;
609         struct smb2_lease ls;
610         struct smb2_handle h;
611         struct smb2_lock lck;
612         struct smb2_lock_element el[2];
613         NTSTATUS status;
614         char fname[256];
615         bool ret = true;
616         uint64_t lease;
617
618         /*
619          * Choose a random name and random lease in case the state is left a
620          * little funky.
621          */
622         lease = random();
623         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
624
625         /* Clean slate */
626         smb2_util_unlink(tree, fname);
627
628         /* Create with lease */
629
630         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
631                           smb2_util_lease_state("RWH"));
632         io.in.durable_open              = true;
633
634         status = smb2_create(tree, mem_ctx, &io);
635         CHECK_STATUS(status, NT_STATUS_OK);
636         h = io.out.file.handle;
637         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
638
639         CHECK_VAL(io.out.durable_open, true);
640         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
641         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
642         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
643         CHECK_VAL(io.out.lease_response.lease_state,
644             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
645
646         ZERO_STRUCT(lck);
647         ZERO_STRUCT(el);
648         lck.in.locks            = el;
649         lck.in.lock_count       = 0x0001;
650         lck.in.lock_sequence    = 0x00000000;
651         lck.in.file.handle      = h;
652         el[0].offset            = 0;
653         el[0].length            = 1;
654         el[0].reserved          = 0x00000000;
655         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
656         status = smb2_lock(tree, &lck);
657         CHECK_STATUS(status, NT_STATUS_OK);
658
659         /* Disconnect/Reconnect. */
660         talloc_free(tree);
661         tree = NULL;
662
663         if (!torture_smb2_connection(tctx, &tree)) {
664                 torture_warning(tctx, "couldn't reconnect, bailing\n");
665                 ret = false;
666                 goto done;
667         }
668
669         ZERO_STRUCT(io);
670         io.in.fname = fname;
671         io.in.durable_handle = &h;
672         io.in.lease_request = &ls;
673
674         status = smb2_create(tree, mem_ctx, &io);
675         CHECK_STATUS(status, NT_STATUS_OK);
676         h = io.out.file.handle;
677
678         lck.in.file.handle      = h;
679         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
680         status = smb2_lock(tree, &lck);
681         CHECK_STATUS(status, NT_STATUS_OK);
682
683  done:
684         smb2_util_close(tree, h);
685         smb2_util_unlink(tree, fname);
686         talloc_free(tree);
687
688         return ret;
689 }
690
691 /*
692   Open, disconnect, open in another tree, reconnect.
693
694   This test actually demonstrates a minimum level of respect for the durable
695   open in the face of another open. As long as this test shows an inability to
696   reconnect after an open, the oplock/lease tests above will certainly
697   demonstrate an error on reconnect.
698 */
699 bool test_durable_open_open(struct torture_context *tctx,
700                             struct smb2_tree *tree1,
701                             struct smb2_tree *tree2)
702 {
703         TALLOC_CTX *mem_ctx = talloc_new(tctx);
704         struct smb2_create io1, io2;
705         struct smb2_lease ls;
706         struct smb2_handle h1, h2;
707         NTSTATUS status;
708         char fname[256];
709         bool ret = true;
710         uint64_t lease;
711
712         /*
713          * Choose a random name and random lease in case the state is left a
714          * little funky.
715          */
716         lease = random();
717         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
718
719         /* Clean slate */
720         smb2_util_unlink(tree1, fname);
721
722         /* Create with lease */
723         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
724                                 smb2_util_share_access(""),
725                                 lease,
726                                 smb2_util_lease_state("RH"));
727         io1.in.durable_open = true;
728
729         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
730
731         status = smb2_create(tree1, mem_ctx, &io1);
732         CHECK_STATUS(status, NT_STATUS_OK);
733         h1 = io1.out.file.handle;
734         CHECK_VAL(io1.out.durable_open, true);
735         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
736
737         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
738         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
739         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
740         CHECK_VAL(io1.out.lease_response.lease_state,
741                   smb2_util_lease_state("RH"));
742
743         /* Disconnect */
744         talloc_free(tree1);
745         tree1 = NULL;
746
747         /* Open the file in tree2 */
748         status = smb2_create(tree2, mem_ctx, &io2);
749         CHECK_STATUS(status, NT_STATUS_OK);
750         h2 = io2.out.file.handle;
751         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
752
753         /* Reconnect */
754         if (!torture_smb2_connection(tctx, &tree1)) {
755                 torture_warning(tctx, "couldn't reconnect, bailing\n");
756                 ret = false;
757                 goto done;
758         }
759
760         ZERO_STRUCT(io1);
761         io1.in.fname = fname;
762         io1.in.durable_handle = &h1;
763         io1.in.lease_request = &ls;
764
765         /*
766          * Windows7 (build 7000) will give away an open immediately if the
767          * original client is gone. (ZML: This seems like a bug. It should give
768          * some time for the client to reconnect!)
769          */
770         status = smb2_create(tree1, mem_ctx, &io1);
771         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
772         h1 = io1.out.file.handle;
773
774  done:
775         smb2_util_close(tree2, h2);
776         smb2_util_unlink(tree2, fname);
777         smb2_util_close(tree1, h1);
778         smb2_util_unlink(tree1, fname);
779
780         talloc_free(tree1);
781         talloc_free(tree2);
782
783         return ret;
784 }
785
786 struct torture_suite *torture_smb2_durable_open_init(void)
787 {
788         struct torture_suite *suite =
789             torture_suite_create(talloc_autofree_context(), "durable-open");
790
791         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
792         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
793         torture_suite_add_2smb2_test(suite, "file-position",
794             test_durable_open_file_position);
795         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
796         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
797         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
798         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
799
800         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
801
802         return suite;
803 }