s4:torture:smb2: invalidate the handle after the connection has been killed
[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 test for doing a durable open
324  * and do a durable reopen on the same connection
325  * while the first open is still active (fails)
326  */
327 bool test_durable_open_reopen1(struct torture_context *tctx,
328                                struct smb2_tree *tree)
329 {
330         NTSTATUS status;
331         TALLOC_CTX *mem_ctx = talloc_new(tctx);
332         char fname[256];
333         struct smb2_handle _h;
334         struct smb2_handle *h = NULL;
335         struct smb2_create io1, io2;
336         bool ret = true;
337
338         /* Choose a random name in case the state is left a little funky. */
339         snprintf(fname, 256, "durable_open_reopen1_%s.dat",
340                  generate_random_str(tctx, 8));
341
342         smb2_util_unlink(tree, fname);
343
344         smb2_oplock_create_share(&io1, fname,
345                                  smb2_util_share_access(""),
346                                  smb2_util_oplock_level("b"));
347         io1.in.durable_open = true;
348
349         status = smb2_create(tree, mem_ctx, &io1);
350         CHECK_STATUS(status, NT_STATUS_OK);
351         _h = io1.out.file.handle;
352         h = &_h;
353         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
354         CHECK_VAL(io1.out.durable_open, true);
355         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
356
357         /* try a durable reconnect while the file is still open */
358         ZERO_STRUCT(io2);
359         io2.in.fname = fname;
360         io2.in.durable_handle = h;
361
362         status = smb2_create(tree, mem_ctx, &io2);
363         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
364
365 done:
366         if (h != NULL) {
367                 smb2_util_close(tree, *h);
368         }
369
370         smb2_util_unlink(tree, fname);
371
372         talloc_free(tree);
373
374         talloc_free(mem_ctx);
375
376         return ret;
377 }
378
379 /**
380  * basic test for doing a durable open
381  * tcp disconnect, reconnect, do a durable reopen (succeeds)
382  */
383 bool test_durable_open_reopen2(struct torture_context *tctx,
384                                struct smb2_tree *tree)
385 {
386         NTSTATUS status;
387         TALLOC_CTX *mem_ctx = talloc_new(tctx);
388         char fname[256];
389         struct smb2_handle _h;
390         struct smb2_handle *h = NULL;
391         struct smb2_create io1, io2;
392         bool ret = true;
393
394         /* Choose a random name in case the state is left a little funky. */
395         snprintf(fname, 256, "durable_open_reopen2_%s.dat",
396                  generate_random_str(tctx, 8));
397
398         smb2_util_unlink(tree, fname);
399
400         smb2_oplock_create_share(&io1, fname,
401                                  smb2_util_share_access(""),
402                                  smb2_util_oplock_level("b"));
403         io1.in.durable_open = true;
404
405         status = smb2_create(tree, mem_ctx, &io1);
406         CHECK_STATUS(status, NT_STATUS_OK);
407         _h = io1.out.file.handle;
408         h = &_h;
409         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
410         CHECK_VAL(io1.out.durable_open, true);
411         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
412
413         /* disconnect, reconnect and then do durable reopen */
414         talloc_free(tree);
415         tree = NULL;
416
417         if (!torture_smb2_connection(tctx, &tree)) {
418                 torture_warning(tctx, "couldn't reconnect, bailing\n");
419                 ret = false;
420                 goto done;
421         }
422
423         ZERO_STRUCT(io2);
424         io2.in.fname = fname;
425         io2.in.durable_handle = h;
426         h = NULL;
427
428         status = smb2_create(tree, mem_ctx, &io2);
429         CHECK_STATUS(status, NT_STATUS_OK);
430         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
431         CHECK_VAL(io2.out.durable_open, true);
432         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
433         _h = io2.out.file.handle;
434         h = &_h;
435
436 done:
437         if (h != NULL) {
438                 smb2_util_close(tree, *h);
439         }
440
441         smb2_util_unlink(tree, fname);
442
443         talloc_free(tree);
444
445         talloc_free(mem_ctx);
446
447         return ret;
448 }
449
450 /**
451  * basic test for doing a durable open:
452  * tdis, new tcon, try durable reopen (fails)
453  */
454 bool test_durable_open_reopen3(struct torture_context *tctx,
455                                struct smb2_tree *tree)
456 {
457         NTSTATUS status;
458         TALLOC_CTX *mem_ctx = talloc_new(tctx);
459         char fname[256];
460         struct smb2_handle _h;
461         struct smb2_handle *h = NULL;
462         struct smb2_create io1, io2;
463         bool ret = true;
464         struct smb2_tree *tree2;
465
466         /* Choose a random name in case the state is left a little funky. */
467         snprintf(fname, 256, "durable_open_reopen3_%s.dat",
468                  generate_random_str(tctx, 8));
469
470         smb2_util_unlink(tree, fname);
471
472         smb2_oplock_create_share(&io1, fname,
473                                  smb2_util_share_access(""),
474                                  smb2_util_oplock_level("b"));
475         io1.in.durable_open = true;
476
477         status = smb2_create(tree, mem_ctx, &io1);
478         CHECK_STATUS(status, NT_STATUS_OK);
479         _h = io1.out.file.handle;
480         h = &_h;
481         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
482         CHECK_VAL(io1.out.durable_open, true);
483         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
484
485         /* disconnect, reconnect and then do durable reopen */
486         status = smb2_tdis(tree);
487         CHECK_STATUS(status, NT_STATUS_OK);
488
489         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
490                 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
491                 ret = false;
492                 goto done;
493         }
494
495
496         ZERO_STRUCT(io2);
497         io2.in.fname = fname;
498         io2.in.durable_handle = h;
499
500         status = smb2_create(tree2, mem_ctx, &io2);
501         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
502
503 done:
504         if (h != NULL) {
505                 smb2_util_close(tree, *h);
506         }
507
508         smb2_util_unlink(tree2, fname);
509
510         talloc_free(tree);
511
512         talloc_free(mem_ctx);
513
514         return ret;
515 }
516
517 /**
518  * basic test for doing a durable open:
519  * logoff, create a new session, do a durable reopen (succeeds)
520  */
521 bool test_durable_open_reopen4(struct torture_context *tctx,
522                                struct smb2_tree *tree)
523 {
524         NTSTATUS status;
525         TALLOC_CTX *mem_ctx = talloc_new(tctx);
526         char fname[256];
527         struct smb2_handle _h;
528         struct smb2_handle *h = NULL;
529         struct smb2_create io1, io2;
530         bool ret = true;
531         struct smb2_transport *transport;
532         struct smb2_tree *tree2;
533
534         /* Choose a random name in case the state is left a little funky. */
535         snprintf(fname, 256, "durable_open_reopen4_%s.dat",
536                  generate_random_str(tctx, 8));
537
538         smb2_util_unlink(tree, fname);
539
540         smb2_oplock_create_share(&io1, fname,
541                                  smb2_util_share_access(""),
542                                  smb2_util_oplock_level("b"));
543         io1.in.durable_open = true;
544         io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
545
546         status = smb2_create(tree, mem_ctx, &io1);
547         CHECK_STATUS(status, NT_STATUS_OK);
548         _h = io1.out.file.handle;
549         h = &_h;
550         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
551         CHECK_VAL(io1.out.durable_open, true);
552         CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
553
554         /* disconnect, reconnect and then do durable reopen */
555         transport = tree->session->transport;
556         status = smb2_logoff(tree->session);
557         CHECK_STATUS(status, NT_STATUS_OK);
558
559         if (!torture_smb2_session_setup(tctx, transport, mem_ctx, &tree->session)) {
560                 torture_warning(tctx, "session setup failed.\n");
561                 ret = false;
562                 goto done;
563         }
564
565         ZERO_STRUCT(io2);
566         io2.in.fname = fname;
567         io2.in.durable_handle = h;
568
569         status = smb2_create(tree, mem_ctx, &io2);
570         CHECK_STATUS(status, NT_STATUS_NETWORK_NAME_DELETED);
571
572         if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
573                 torture_warning(tctx, "tree connect failed.\n");
574                 ret = false;
575                 goto done;
576         }
577
578         ZERO_STRUCT(io2);
579         io2.in.fname = fname;
580         io2.in.durable_handle = h;
581
582         status = smb2_create(tree2, mem_ctx, &io2);
583         CHECK_STATUS(status, NT_STATUS_OK);
584
585         _h = io2.out.file.handle;
586         h = &_h;
587         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
588         CHECK_VAL(io2.out.durable_open, true);
589         CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
590
591 done:
592         if (h != NULL) {
593                 smb2_util_close(tree, *h);
594         }
595
596         smb2_util_unlink(tree2, fname);
597
598         talloc_free(tree);
599
600         talloc_free(mem_ctx);
601
602         return ret;
603 }
604
605 /*
606    basic testing of SMB2 durable opens
607    regarding the position information on the handle
608 */
609 bool test_durable_open_file_position(struct torture_context *tctx,
610                                      struct smb2_tree *tree1,
611                                      struct smb2_tree *tree2)
612 {
613         TALLOC_CTX *mem_ctx = talloc_new(tctx);
614         struct smb2_handle h1, h2;
615         struct smb2_create io1, io2;
616         NTSTATUS status;
617         const char *fname = "durable_open_position.dat";
618         union smb_fileinfo qfinfo;
619         union smb_setfileinfo sfinfo;
620         bool ret = true;
621         uint64_t pos;
622
623         smb2_util_unlink(tree1, fname);
624
625         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
626         io1.in.durable_open = true;
627
628         status = smb2_create(tree1, mem_ctx, &io1);
629         CHECK_STATUS(status, NT_STATUS_OK);
630         h1 = io1.out.file.handle;
631         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
632         CHECK_VAL(io1.out.durable_open, true);
633         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
634
635         /* TODO: check extra blob content */
636
637         ZERO_STRUCT(qfinfo);
638         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
639         qfinfo.generic.in.file.handle = h1;
640         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
641         CHECK_STATUS(status, NT_STATUS_OK);
642         CHECK_VAL(qfinfo.position_information.out.position, 0);
643         pos = qfinfo.position_information.out.position;
644         torture_comment(tctx, "position: %llu\n",
645                         (unsigned long long)pos);
646
647         ZERO_STRUCT(sfinfo);
648         sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
649         sfinfo.generic.in.file.handle = h1;
650         sfinfo.position_information.in.position = 0x1000;
651         status = smb2_setinfo_file(tree1, &sfinfo);
652         CHECK_STATUS(status, NT_STATUS_OK);
653
654         ZERO_STRUCT(qfinfo);
655         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
656         qfinfo.generic.in.file.handle = h1;
657         status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
658         CHECK_STATUS(status, NT_STATUS_OK);
659         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
660         pos = qfinfo.position_information.out.position;
661         torture_comment(tctx, "position: %llu\n",
662                         (unsigned long long)pos);
663
664         talloc_free(tree1);
665         tree1 = NULL;
666
667         ZERO_STRUCT(qfinfo);
668         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
669         qfinfo.generic.in.file.handle = h1;
670         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
671         CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
672
673         ZERO_STRUCT(io2);
674         io2.in.fname = fname;
675         io2.in.durable_handle = &h1;
676
677         status = smb2_create(tree2, mem_ctx, &io2);
678         CHECK_STATUS(status, NT_STATUS_OK);
679         CHECK_VAL(io2.out.durable_open, true);
680         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
681         CHECK_VAL(io2.out.reserved, 0x00);
682         CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
683         CHECK_VAL(io2.out.alloc_size, 0);
684         CHECK_VAL(io2.out.size, 0);
685         CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
686         CHECK_VAL(io2.out.reserved2, 0);
687
688         h2 = io2.out.file.handle;
689
690         ZERO_STRUCT(qfinfo);
691         qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
692         qfinfo.generic.in.file.handle = h2;
693         status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
694         CHECK_STATUS(status, NT_STATUS_OK);
695         CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
696         pos = qfinfo.position_information.out.position;
697         torture_comment(tctx, "position: %llu\n",
698                         (unsigned long long)pos);
699
700         smb2_util_close(tree2, h2);
701
702         talloc_free(mem_ctx);
703
704         smb2_util_unlink(tree2, fname);
705 done:
706         talloc_free(tree1);
707         talloc_free(tree2);
708
709         return ret;
710 }
711
712 /*
713   Open, disconnect, oplock break, reconnect.
714 */
715 bool test_durable_open_oplock(struct torture_context *tctx,
716                               struct smb2_tree *tree1,
717                               struct smb2_tree *tree2)
718 {
719         TALLOC_CTX *mem_ctx = talloc_new(tctx);
720         struct smb2_create io1, io2;
721         struct smb2_handle h1, h2;
722         NTSTATUS status;
723         char fname[256];
724         bool ret = true;
725
726         /* Choose a random name in case the state is left a little funky. */
727         snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
728
729         /* Clean slate */
730         smb2_util_unlink(tree1, fname);
731
732         /* Create with batch oplock */
733         smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
734         io1.in.durable_open = true;
735
736         io2 = io1;
737         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
738
739         status = smb2_create(tree1, mem_ctx, &io1);
740         CHECK_STATUS(status, NT_STATUS_OK);
741         h1 = io1.out.file.handle;
742         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
743         CHECK_VAL(io1.out.durable_open, true);
744         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
745
746         /* Disconnect after getting the batch */
747         talloc_free(tree1);
748         tree1 = NULL;
749
750         /*
751          * Windows7 (build 7000) will break a batch oplock immediately if the
752          * original client is gone. (ZML: This seems like a bug. It should give
753          * some time for the client to reconnect!)
754          */
755         status = smb2_create(tree2, mem_ctx, &io2);
756         CHECK_STATUS(status, NT_STATUS_OK);
757         h2 = io2.out.file.handle;
758         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
759         CHECK_VAL(io2.out.durable_open, true);
760         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
761
762         /* What if tree1 tries to come back and reclaim? */
763         if (!torture_smb2_connection(tctx, &tree1)) {
764                 torture_warning(tctx, "couldn't reconnect, bailing\n");
765                 ret = false;
766                 goto done;
767         }
768
769         ZERO_STRUCT(io1);
770         io1.in.fname = fname;
771         io1.in.durable_handle = &h1;
772
773         status = smb2_create(tree1, mem_ctx, &io1);
774         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
775
776  done:
777         smb2_util_close(tree2, h2);
778         smb2_util_unlink(tree2, fname);
779
780         talloc_free(tree1);
781         talloc_free(tree2);
782
783         return ret;
784 }
785
786 /*
787   Open, disconnect, lease break, reconnect.
788 */
789 bool test_durable_open_lease(struct torture_context *tctx,
790                              struct smb2_tree *tree1,
791                              struct smb2_tree *tree2)
792 {
793         TALLOC_CTX *mem_ctx = talloc_new(tctx);
794         struct smb2_create io1, io2;
795         struct smb2_lease ls1, ls2;
796         struct smb2_handle h1, h2;
797         NTSTATUS status;
798         char fname[256];
799         bool ret = true;
800         uint64_t lease1, lease2;
801
802         /*
803          * Choose a random name and random lease in case the state is left a
804          * little funky.
805          */
806         lease1 = random();
807         lease2 = random();
808         snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
809
810         /* Clean slate */
811         smb2_util_unlink(tree1, fname);
812
813         /* Create with lease */
814         smb2_lease_create(&io1, &ls1, false /* dir */, fname,
815                           lease1, smb2_util_lease_state("RHW"));
816         io1.in.durable_open = true;
817
818         smb2_lease_create(&io2, &ls2, false /* dir */, fname,
819                           lease2, smb2_util_lease_state("RHW"));
820         io2.in.durable_open = true;
821         io2.in.create_disposition = NTCREATEX_DISP_OPEN;
822
823         status = smb2_create(tree1, mem_ctx, &io1);
824         CHECK_STATUS(status, NT_STATUS_OK);
825         h1 = io1.out.file.handle;
826         CHECK_VAL(io1.out.durable_open, true);
827         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
828
829         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
830         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
831         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
832         CHECK_VAL(io1.out.lease_response.lease_state,
833             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
834
835         /* Disconnect after getting the lease */
836         talloc_free(tree1);
837         tree1 = NULL;
838
839         /*
840          * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
841          * even if the original client is gone. (ZML: This seems like a bug. It
842          * should give some time for the client to reconnect! And why RH?)
843          * 
844          * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
845          * Test is adapted accordingly.
846          */
847         status = smb2_create(tree2, mem_ctx, &io2);
848         CHECK_STATUS(status, NT_STATUS_OK);
849         h2 = io2.out.file.handle;
850         CHECK_VAL(io2.out.durable_open, true);
851         CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
852
853         CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
854         CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
855         CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
856         CHECK_VAL(io2.out.lease_response.lease_state,
857             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
858
859         /* What if tree1 tries to come back and reclaim? */
860         if (!torture_smb2_connection(tctx, &tree1)) {
861                 torture_warning(tctx, "couldn't reconnect, bailing\n");
862                 ret = false;
863                 goto done;
864         }
865
866         ZERO_STRUCT(io1);
867         io1.in.fname = fname;
868         io1.in.durable_handle = &h1;
869         io1.in.lease_request = &ls1;
870
871         status = smb2_create(tree1, mem_ctx, &io1);
872         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
873
874  done:
875         smb2_util_close(tree2, h2);
876         smb2_util_unlink(tree2, fname);
877
878         talloc_free(tree1);
879         talloc_free(tree2);
880
881         return ret;
882 }
883
884 /*
885   Open, take BRL, disconnect, reconnect.
886 */
887 bool test_durable_open_lock(struct torture_context *tctx,
888                             struct smb2_tree *tree)
889 {
890         TALLOC_CTX *mem_ctx = talloc_new(tctx);
891         struct smb2_create io;
892         struct smb2_lease ls;
893         struct smb2_handle h;
894         struct smb2_lock lck;
895         struct smb2_lock_element el[2];
896         NTSTATUS status;
897         char fname[256];
898         bool ret = true;
899         uint64_t lease;
900
901         /*
902          * Choose a random name and random lease in case the state is left a
903          * little funky.
904          */
905         lease = random();
906         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
907
908         /* Clean slate */
909         smb2_util_unlink(tree, fname);
910
911         /* Create with lease */
912
913         smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
914                           smb2_util_lease_state("RWH"));
915         io.in.durable_open              = true;
916
917         status = smb2_create(tree, mem_ctx, &io);
918         CHECK_STATUS(status, NT_STATUS_OK);
919         h = io.out.file.handle;
920         CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
921
922         CHECK_VAL(io.out.durable_open, true);
923         CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
924         CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
925         CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
926         CHECK_VAL(io.out.lease_response.lease_state,
927             SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
928
929         ZERO_STRUCT(lck);
930         ZERO_STRUCT(el);
931         lck.in.locks            = el;
932         lck.in.lock_count       = 0x0001;
933         lck.in.lock_sequence    = 0x00000000;
934         lck.in.file.handle      = h;
935         el[0].offset            = 0;
936         el[0].length            = 1;
937         el[0].reserved          = 0x00000000;
938         el[0].flags             = SMB2_LOCK_FLAG_EXCLUSIVE;
939         status = smb2_lock(tree, &lck);
940         CHECK_STATUS(status, NT_STATUS_OK);
941
942         /* Disconnect/Reconnect. */
943         talloc_free(tree);
944         tree = NULL;
945
946         if (!torture_smb2_connection(tctx, &tree)) {
947                 torture_warning(tctx, "couldn't reconnect, bailing\n");
948                 ret = false;
949                 goto done;
950         }
951
952         ZERO_STRUCT(io);
953         io.in.fname = fname;
954         io.in.durable_handle = &h;
955         io.in.lease_request = &ls;
956
957         status = smb2_create(tree, mem_ctx, &io);
958         CHECK_STATUS(status, NT_STATUS_OK);
959         h = io.out.file.handle;
960
961         lck.in.file.handle      = h;
962         el[0].flags             = SMB2_LOCK_FLAG_UNLOCK;
963         status = smb2_lock(tree, &lck);
964         CHECK_STATUS(status, NT_STATUS_OK);
965
966  done:
967         smb2_util_close(tree, h);
968         smb2_util_unlink(tree, fname);
969         talloc_free(tree);
970
971         return ret;
972 }
973
974 /*
975   Open, disconnect, open in another tree, reconnect.
976
977   This test actually demonstrates a minimum level of respect for the durable
978   open in the face of another open. As long as this test shows an inability to
979   reconnect after an open, the oplock/lease tests above will certainly
980   demonstrate an error on reconnect.
981 */
982 bool test_durable_open_open(struct torture_context *tctx,
983                             struct smb2_tree *tree1,
984                             struct smb2_tree *tree2)
985 {
986         TALLOC_CTX *mem_ctx = talloc_new(tctx);
987         struct smb2_create io1, io2;
988         struct smb2_lease ls;
989         struct smb2_handle h1, h2;
990         NTSTATUS status;
991         char fname[256];
992         bool ret = true;
993         uint64_t lease;
994
995         /*
996          * Choose a random name and random lease in case the state is left a
997          * little funky.
998          */
999         lease = random();
1000         snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1001
1002         /* Clean slate */
1003         smb2_util_unlink(tree1, fname);
1004
1005         /* Create with lease */
1006         smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1007                                 smb2_util_share_access(""),
1008                                 lease,
1009                                 smb2_util_lease_state("RH"));
1010         io1.in.durable_open = true;
1011
1012         smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1013
1014         status = smb2_create(tree1, mem_ctx, &io1);
1015         CHECK_STATUS(status, NT_STATUS_OK);
1016         h1 = io1.out.file.handle;
1017         CHECK_VAL(io1.out.durable_open, true);
1018         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1019
1020         CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1021         CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1022         CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1023         CHECK_VAL(io1.out.lease_response.lease_state,
1024                   smb2_util_lease_state("RH"));
1025
1026         /* Disconnect */
1027         talloc_free(tree1);
1028         tree1 = NULL;
1029
1030         /* Open the file in tree2 */
1031         status = smb2_create(tree2, mem_ctx, &io2);
1032         CHECK_STATUS(status, NT_STATUS_OK);
1033         h2 = io2.out.file.handle;
1034         CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1035
1036         /* Reconnect */
1037         if (!torture_smb2_connection(tctx, &tree1)) {
1038                 torture_warning(tctx, "couldn't reconnect, bailing\n");
1039                 ret = false;
1040                 goto done;
1041         }
1042
1043         ZERO_STRUCT(io1);
1044         io1.in.fname = fname;
1045         io1.in.durable_handle = &h1;
1046         io1.in.lease_request = &ls;
1047
1048         /*
1049          * Windows7 (build 7000) will give away an open immediately if the
1050          * original client is gone. (ZML: This seems like a bug. It should give
1051          * some time for the client to reconnect!)
1052          */
1053         status = smb2_create(tree1, mem_ctx, &io1);
1054         CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1055         h1 = io1.out.file.handle;
1056
1057  done:
1058         smb2_util_close(tree2, h2);
1059         smb2_util_unlink(tree2, fname);
1060         smb2_util_close(tree1, h1);
1061         smb2_util_unlink(tree1, fname);
1062
1063         talloc_free(tree1);
1064         talloc_free(tree2);
1065
1066         return ret;
1067 }
1068
1069 struct torture_suite *torture_smb2_durable_open_init(void)
1070 {
1071         struct torture_suite *suite =
1072             torture_suite_create(talloc_autofree_context(), "durable-open");
1073
1074         torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1075         torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1076         torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1077         torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1078         torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1079         torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1080         torture_suite_add_2smb2_test(suite, "file-position",
1081             test_durable_open_file_position);
1082         torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1083         torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1084         torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1085         torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
1086
1087         suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1088
1089         return suite;
1090 }