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