s4:torture:smb2: fix a typo in the durable-open.open test file name
[Samba/gbeck.git] / source4 / torture / smb2 / durable_open.c
blob29825e0f84f52f67ace2f2736c6b76543364cde1
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
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.
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.
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/>.
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"
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)
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)
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)
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.
58 * This test tests durable open with all possible oplock types.
61 struct durable_open_vs_oplock {
62 const char *level;
63 const char *share_mode;
64 bool expected;
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] =
72 { "", "", false },
73 { "", "R", false },
74 { "", "W", false },
75 { "", "D", false },
76 { "", "RD", false },
77 { "", "RW", false },
78 { "", "WD", false },
79 { "", "RWD", false },
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 },
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 },
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 },
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)
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;
121 smb2_util_unlink(tree, fname);
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;
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));
136 done:
137 if (h != NULL) {
138 smb2_util_close(tree, *h);
140 smb2_util_unlink(tree, fname);
141 talloc_free(mem_ctx);
143 return ret;
146 bool test_durable_open_open1(struct torture_context *tctx,
147 struct smb2_tree *tree)
149 TALLOC_CTX *mem_ctx = talloc_new(tctx);
150 char fname[256];
151 bool ret = true;
152 int i;
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));
157 smb2_util_unlink(tree, fname);
159 /* test various oplock levels with durable open */
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;
171 done:
172 smb2_util_unlink(tree, fname);
173 talloc_free(tree);
174 talloc_free(mem_ctx);
176 return ret;
180 * basic durable_open test.
181 * durable state should only be granted when requested
182 * along with a batch oplock or a handle lease.
184 * This test tests durable open with all valid lease types.
187 struct durable_open_vs_lease {
188 const char *type;
189 const char *share_mode;
190 bool expected;
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] =
197 { "", "", false },
198 { "", "R", false },
199 { "", "W", false },
200 { "", "D", false },
201 { "", "RW", false },
202 { "", "RD", false },
203 { "", "WD", false },
204 { "", "RWD", false },
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 },
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 },
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 },
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 },
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)
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;
257 smb2_util_unlink(tree, fname);
259 lease = random();
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;
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);
282 smb2_util_unlink(tree, fname);
283 talloc_free(mem_ctx);
285 return ret;
288 bool test_durable_open_open2(struct torture_context *tctx,
289 struct smb2_tree *tree)
291 TALLOC_CTX *mem_ctx = talloc_new(tctx);
292 char fname[256];
293 bool ret = true;
294 int i;
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));
299 smb2_util_unlink(tree, fname);
302 /* test various oplock levels with durable open */
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;
314 done:
315 smb2_util_unlink(tree, fname);
316 talloc_free(tree);
317 talloc_free(mem_ctx);
319 return ret;
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)
327 bool test_durable_open_reopen1(struct torture_context *tctx,
328 struct smb2_tree *tree)
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;
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));
342 smb2_util_unlink(tree, fname);
344 smb2_oplock_create_share(&io1, fname,
345 smb2_util_share_access(""),
346 smb2_util_oplock_level("b"));
347 io1.in.durable_open = true;
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"));
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;
362 status = smb2_create(tree, mem_ctx, &io2);
363 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
365 done:
366 if (h != NULL) {
367 smb2_util_close(tree, *h);
370 smb2_util_unlink(tree, fname);
372 talloc_free(tree);
374 talloc_free(mem_ctx);
376 return ret;
380 * basic test for doing a durable open
381 * tcp disconnect, reconnect, do a durable reopen (succeeds)
383 bool test_durable_open_reopen2(struct torture_context *tctx,
384 struct smb2_tree *tree)
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;
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));
398 smb2_util_unlink(tree, fname);
400 smb2_oplock_create_share(&io1, fname,
401 smb2_util_share_access(""),
402 smb2_util_oplock_level("b"));
403 io1.in.durable_open = true;
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"));
413 /* disconnect, reconnect and then do durable reopen */
414 talloc_free(tree);
415 tree = NULL;
417 if (!torture_smb2_connection(tctx, &tree)) {
418 torture_warning(tctx, "couldn't reconnect, bailing\n");
419 ret = false;
420 goto done;
423 ZERO_STRUCT(io2);
424 io2.in.fname = fname;
425 io2.in.durable_handle = h;
426 h = NULL;
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;
436 done:
437 if (h != NULL) {
438 smb2_util_close(tree, *h);
441 smb2_util_unlink(tree, fname);
443 talloc_free(tree);
445 talloc_free(mem_ctx);
447 return ret;
451 * basic test for doing a durable open:
452 * tdis, new tcon, try durable reopen (fails)
454 bool test_durable_open_reopen3(struct torture_context *tctx,
455 struct smb2_tree *tree)
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;
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));
470 smb2_util_unlink(tree, fname);
472 smb2_oplock_create_share(&io1, fname,
473 smb2_util_share_access(""),
474 smb2_util_oplock_level("b"));
475 io1.in.durable_open = true;
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"));
485 /* disconnect, reconnect and then do durable reopen */
486 status = smb2_tdis(tree);
487 CHECK_STATUS(status, NT_STATUS_OK);
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;
496 ZERO_STRUCT(io2);
497 io2.in.fname = fname;
498 io2.in.durable_handle = h;
500 status = smb2_create(tree2, mem_ctx, &io2);
501 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
503 done:
504 if (h != NULL) {
505 smb2_util_close(tree, *h);
508 smb2_util_unlink(tree2, fname);
510 talloc_free(tree);
512 talloc_free(mem_ctx);
514 return ret;
518 * basic test for doing a durable open:
519 * logoff, create a new session, do a durable reopen (succeeds)
521 bool test_durable_open_reopen4(struct torture_context *tctx,
522 struct smb2_tree *tree)
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;
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));
538 smb2_util_unlink(tree, fname);
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;
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"));
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);
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;
565 ZERO_STRUCT(io2);
566 io2.in.fname = fname;
567 io2.in.durable_handle = h;
569 status = smb2_create(tree, mem_ctx, &io2);
570 CHECK_STATUS(status, NT_STATUS_NETWORK_NAME_DELETED);
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;
578 ZERO_STRUCT(io2);
579 io2.in.fname = fname;
580 io2.in.durable_handle = h;
582 status = smb2_create(tree2, mem_ctx, &io2);
583 CHECK_STATUS(status, NT_STATUS_OK);
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"));
591 done:
592 if (h != NULL) {
593 smb2_util_close(tree, *h);
596 smb2_util_unlink(tree2, fname);
598 talloc_free(tree);
600 talloc_free(mem_ctx);
602 return ret;
606 basic testing of SMB2 durable opens
607 regarding the position information on the handle
609 bool test_durable_open_file_position(struct torture_context *tctx,
610 struct smb2_tree *tree1,
611 struct smb2_tree *tree2)
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;
623 smb2_util_unlink(tree1, fname);
625 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
626 io1.in.durable_open = true;
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);
635 /* TODO: check extra blob content */
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);
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);
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);
664 talloc_free(tree1);
665 tree1 = NULL;
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);
673 ZERO_STRUCT(io2);
674 io2.in.fname = fname;
675 io2.in.durable_handle = &h1;
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);
688 h2 = io2.out.file.handle;
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);
700 smb2_util_close(tree2, h2);
702 talloc_free(mem_ctx);
704 smb2_util_unlink(tree2, fname);
705 done:
706 talloc_free(tree1);
707 talloc_free(tree2);
709 return ret;
713 Open, disconnect, oplock break, reconnect.
715 bool test_durable_open_oplock(struct torture_context *tctx,
716 struct smb2_tree *tree1,
717 struct smb2_tree *tree2)
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;
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));
729 /* Clean slate */
730 smb2_util_unlink(tree1, fname);
732 /* Create with batch oplock */
733 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
734 io1.in.durable_open = true;
736 io2 = io1;
737 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
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);
746 /* Disconnect after getting the batch */
747 talloc_free(tree1);
748 tree1 = NULL;
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!)
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);
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;
769 ZERO_STRUCT(io1);
770 io1.in.fname = fname;
771 io1.in.durable_handle = &h1;
773 status = smb2_create(tree1, mem_ctx, &io1);
774 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
776 done:
777 smb2_util_close(tree2, h2);
778 smb2_util_unlink(tree2, fname);
780 talloc_free(tree1);
781 talloc_free(tree2);
783 return ret;
787 Open, disconnect, lease break, reconnect.
789 bool test_durable_open_lease(struct torture_context *tctx,
790 struct smb2_tree *tree1,
791 struct smb2_tree *tree2)
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;
803 * Choose a random name and random lease in case the state is left a
804 * little funky.
806 lease1 = random();
807 lease2 = random();
808 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
810 /* Clean slate */
811 smb2_util_unlink(tree1, fname);
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;
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;
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);
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);
835 /* Disconnect after getting the lease */
836 talloc_free(tree1);
837 tree1 = NULL;
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?)
844 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
845 * Test is adapted accordingly.
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);
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);
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;
866 ZERO_STRUCT(io1);
867 io1.in.fname = fname;
868 io1.in.durable_handle = &h1;
869 io1.in.lease_request = &ls1;
871 status = smb2_create(tree1, mem_ctx, &io1);
872 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
874 done:
875 smb2_util_close(tree2, h2);
876 smb2_util_unlink(tree2, fname);
878 talloc_free(tree1);
879 talloc_free(tree2);
881 return ret;
885 Open, take BRL, disconnect, reconnect.
887 bool test_durable_open_lock(struct torture_context *tctx,
888 struct smb2_tree *tree)
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;
902 * Choose a random name and random lease in case the state is left a
903 * little funky.
905 lease = random();
906 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
908 /* Clean slate */
909 smb2_util_unlink(tree, fname);
911 /* Create with lease */
913 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
914 smb2_util_lease_state("RWH"));
915 io.in.durable_open = true;
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);
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);
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);
942 /* Disconnect/Reconnect. */
943 talloc_free(tree);
944 tree = NULL;
946 if (!torture_smb2_connection(tctx, &tree)) {
947 torture_warning(tctx, "couldn't reconnect, bailing\n");
948 ret = false;
949 goto done;
952 ZERO_STRUCT(io);
953 io.in.fname = fname;
954 io.in.durable_handle = &h;
955 io.in.lease_request = &ls;
957 status = smb2_create(tree, mem_ctx, &io);
958 CHECK_STATUS(status, NT_STATUS_OK);
959 h = io.out.file.handle;
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);
966 done:
967 smb2_util_close(tree, h);
968 smb2_util_unlink(tree, fname);
969 talloc_free(tree);
971 return ret;
975 Open, disconnect, open in another tree, reconnect.
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.
982 bool test_durable_open_open(struct torture_context *tctx,
983 struct smb2_tree *tree1,
984 struct smb2_tree *tree2)
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;
996 * Choose a random name and random lease in case the state is left a
997 * little funky.
999 lease = random();
1000 snprintf(fname, 256, "durable_open_open_%s.dat", generate_random_str(tctx, 8));
1002 /* Clean slate */
1003 smb2_util_unlink(tree1, fname);
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;
1012 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
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);
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"));
1026 /* Disconnect */
1027 talloc_free(tree1);
1028 tree1 = NULL;
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);
1036 /* Reconnect */
1037 if (!torture_smb2_connection(tctx, &tree1)) {
1038 torture_warning(tctx, "couldn't reconnect, bailing\n");
1039 ret = false;
1040 goto done;
1043 ZERO_STRUCT(io1);
1044 io1.in.fname = fname;
1045 io1.in.durable_handle = &h1;
1046 io1.in.lease_request = &ls;
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!)
1053 status = smb2_create(tree1, mem_ctx, &io1);
1054 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1055 h1 = io1.out.file.handle;
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);
1063 talloc_free(tree1);
1064 talloc_free(tree2);
1066 return ret;
1069 struct torture_suite *torture_smb2_durable_open_init(void)
1071 struct torture_suite *suite =
1072 torture_suite_create(talloc_autofree_context(), "durable-open");
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);
1087 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1089 return suite;