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/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)v, (int)correct); \
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
45 #define CHECK_CREATED(__io, __created, __attribute) \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
56 * basic durable_open test.
57 * durable state should only be granted when requested
58 * along with a batch oplock or a handle lease.
60 * This test tests durable open with all possible oplock types.
63 struct durable_open_vs_oplock {
65 const char *share_mode;
69 #define NUM_OPLOCK_TYPES 4
70 #define NUM_SHARE_MODES 8
71 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
72 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
90 { "s", "RWD", false },
99 { "x", "RWD", false },
108 { "b", "RWD", true },
111 static bool test_one_durable_open_open1(struct torture_context *tctx,
112 struct smb2_tree *tree,
114 struct durable_open_vs_oplock test)
117 TALLOC_CTX *mem_ctx = talloc_new(tctx);
118 struct smb2_handle _h;
119 struct smb2_handle *h = NULL;
121 struct smb2_create io;
123 smb2_util_unlink(tree, fname);
125 smb2_oplock_create_share(&io, fname,
126 smb2_util_share_access(test.share_mode),
127 smb2_util_oplock_level(test.level));
128 io.in.durable_open = true;
130 status = smb2_create(tree, mem_ctx, &io);
131 CHECK_STATUS(status, NT_STATUS_OK);
132 _h = io.out.file.handle;
134 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
135 CHECK_VAL(io.out.durable_open, test.expected);
136 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
140 smb2_util_close(tree, *h);
142 smb2_util_unlink(tree, fname);
143 talloc_free(mem_ctx);
148 bool test_durable_open_open1(struct torture_context *tctx,
149 struct smb2_tree *tree)
151 TALLOC_CTX *mem_ctx = talloc_new(tctx);
156 /* Choose a random name in case the state is left a little funky. */
157 snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
159 smb2_util_unlink(tree, fname);
161 /* test various oplock levels with durable open */
163 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
164 ret = test_one_durable_open_open1(tctx,
167 durable_open_vs_oplock_table[i]);
174 smb2_util_unlink(tree, fname);
176 talloc_free(mem_ctx);
182 * basic durable_open test.
183 * durable state should only be granted when requested
184 * along with a batch oplock or a handle lease.
186 * This test tests durable open with all valid lease types.
189 struct durable_open_vs_lease {
191 const char *share_mode;
195 #define NUM_LEASE_TYPES 5
196 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
197 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
206 { "", "RWD", false },
212 { "R", "RW", false },
213 { "R", "RD", false },
214 { "R", "DW", false },
215 { "R", "RWD", false },
218 { "RW", "R", false },
219 { "RW", "W", false },
220 { "RW", "D", false },
221 { "RW", "RW", false },
222 { "RW", "RD", false },
223 { "RW", "WD", false },
224 { "RW", "RWD", false },
230 { "RH", "RW", true },
231 { "RH", "RD", true },
232 { "RH", "WD", true },
233 { "RH", "RWD", true },
236 { "RHW", "R", true },
237 { "RHW", "W", true },
238 { "RHW", "D", true },
239 { "RHW", "RW", true },
240 { "RHW", "RD", true },
241 { "RHW", "WD", true },
242 { "RHW", "RWD", true },
245 static bool test_one_durable_open_open2(struct torture_context *tctx,
246 struct smb2_tree *tree,
248 struct durable_open_vs_lease test)
251 TALLOC_CTX *mem_ctx = talloc_new(tctx);
252 struct smb2_handle _h;
253 struct smb2_handle *h = NULL;
255 struct smb2_create io;
256 struct smb2_lease ls;
260 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
261 if (!(caps & SMB2_CAP_LEASING)) {
262 torture_skip(tctx, "leases are not supported");
265 smb2_util_unlink(tree, fname);
269 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
270 smb2_util_share_access(test.share_mode),
272 smb2_util_lease_state(test.type));
273 io.in.durable_open = true;
275 status = smb2_create(tree, mem_ctx, &io);
276 CHECK_STATUS(status, NT_STATUS_OK);
277 _h = io.out.file.handle;
279 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
280 CHECK_VAL(io.out.durable_open, test.expected);
281 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
282 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
283 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
284 CHECK_VAL(io.out.lease_response.lease_state,
285 smb2_util_lease_state(test.type));
288 smb2_util_close(tree, *h);
290 smb2_util_unlink(tree, fname);
291 talloc_free(mem_ctx);
296 bool test_durable_open_open2(struct torture_context *tctx,
297 struct smb2_tree *tree)
299 TALLOC_CTX *mem_ctx = talloc_new(tctx);
305 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
306 if (!(caps & SMB2_CAP_LEASING)) {
307 torture_skip(tctx, "leases are not supported");
310 /* Choose a random name in case the state is left a little funky. */
311 snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
313 smb2_util_unlink(tree, fname);
316 /* test various oplock levels with durable open */
318 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
319 ret = test_one_durable_open_open2(tctx,
322 durable_open_vs_lease_table[i]);
329 smb2_util_unlink(tree, fname);
331 talloc_free(mem_ctx);
337 * basic test for doing a durable open
338 * and do a durable reopen on the same connection
339 * while the first open is still active (fails)
341 bool test_durable_open_reopen1(struct torture_context *tctx,
342 struct smb2_tree *tree)
345 TALLOC_CTX *mem_ctx = talloc_new(tctx);
347 struct smb2_handle _h;
348 struct smb2_handle *h = NULL;
349 struct smb2_create io1, io2;
352 /* Choose a random name in case the state is left a little funky. */
353 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
354 generate_random_str(tctx, 8));
356 smb2_util_unlink(tree, fname);
358 smb2_oplock_create_share(&io1, fname,
359 smb2_util_share_access(""),
360 smb2_util_oplock_level("b"));
361 io1.in.durable_open = true;
363 status = smb2_create(tree, mem_ctx, &io1);
364 CHECK_STATUS(status, NT_STATUS_OK);
365 _h = io1.out.file.handle;
367 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
368 CHECK_VAL(io1.out.durable_open, true);
369 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
371 /* try a durable reconnect while the file is still open */
373 io2.in.fname = fname;
374 io2.in.durable_handle = h;
376 status = smb2_create(tree, mem_ctx, &io2);
377 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
381 smb2_util_close(tree, *h);
384 smb2_util_unlink(tree, fname);
388 talloc_free(mem_ctx);
394 * basic test for doing a durable open
395 * tcp disconnect, reconnect, do a durable reopen (succeeds)
397 bool test_durable_open_reopen2(struct torture_context *tctx,
398 struct smb2_tree *tree)
401 TALLOC_CTX *mem_ctx = talloc_new(tctx);
403 struct smb2_handle _h;
404 struct smb2_handle *h = NULL;
405 struct smb2_create io1, io2;
408 /* Choose a random name in case the state is left a little funky. */
409 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
410 generate_random_str(tctx, 8));
412 smb2_util_unlink(tree, fname);
414 smb2_oplock_create_share(&io1, fname,
415 smb2_util_share_access(""),
416 smb2_util_oplock_level("b"));
417 io1.in.durable_open = true;
419 status = smb2_create(tree, mem_ctx, &io1);
420 CHECK_STATUS(status, NT_STATUS_OK);
421 _h = io1.out.file.handle;
423 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
424 CHECK_VAL(io1.out.durable_open, true);
425 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
427 /* disconnect, reconnect and then do durable reopen */
431 if (!torture_smb2_connection(tctx, &tree)) {
432 torture_warning(tctx, "couldn't reconnect, bailing\n");
438 io2.in.fname = fname;
439 io2.in.durable_handle = h;
442 status = smb2_create(tree, mem_ctx, &io2);
443 CHECK_STATUS(status, NT_STATUS_OK);
444 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
445 CHECK_VAL(io2.out.durable_open, true);
446 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
447 _h = io2.out.file.handle;
452 smb2_util_close(tree, *h);
455 smb2_util_unlink(tree, fname);
459 talloc_free(mem_ctx);
465 * basic test for doing a durable open
466 * tcp disconnect, reconnect with a session reconnect and
467 * do a durable reopen (succeeds)
469 bool test_durable_open_reopen2a(struct torture_context *tctx,
470 struct smb2_tree *tree)
473 TALLOC_CTX *mem_ctx = talloc_new(tctx);
475 struct smb2_handle _h;
476 struct smb2_handle *h = NULL;
477 struct smb2_create io1, io2;
478 uint64_t previous_session_id;
481 /* Choose a random name in case the state is left a little funky. */
482 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
483 generate_random_str(tctx, 8));
485 smb2_util_unlink(tree, fname);
487 smb2_oplock_create_share(&io1, fname,
488 smb2_util_share_access(""),
489 smb2_util_oplock_level("b"));
490 io1.in.durable_open = true;
492 status = smb2_create(tree, mem_ctx, &io1);
493 CHECK_STATUS(status, NT_STATUS_OK);
494 _h = io1.out.file.handle;
496 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
497 CHECK_VAL(io1.out.durable_open, true);
498 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
500 /* disconnect, reconnect and then do durable reopen */
501 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
505 if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
506 torture_warning(tctx, "couldn't reconnect, bailing\n");
512 io2.in.fname = fname;
513 io2.in.durable_handle = h;
516 status = smb2_create(tree, mem_ctx, &io2);
517 CHECK_STATUS(status, NT_STATUS_OK);
518 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
519 CHECK_VAL(io2.out.durable_open, true);
520 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
521 _h = io2.out.file.handle;
526 smb2_util_close(tree, *h);
529 smb2_util_unlink(tree, fname);
533 talloc_free(mem_ctx);
540 * basic test for doing a durable open:
541 * tdis, new tcon, try durable reopen (fails)
543 bool test_durable_open_reopen3(struct torture_context *tctx,
544 struct smb2_tree *tree)
547 TALLOC_CTX *mem_ctx = talloc_new(tctx);
549 struct smb2_handle _h;
550 struct smb2_handle *h = NULL;
551 struct smb2_create io1, io2;
553 struct smb2_tree *tree2;
555 /* Choose a random name in case the state is left a little funky. */
556 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
557 generate_random_str(tctx, 8));
559 smb2_util_unlink(tree, fname);
561 smb2_oplock_create_share(&io1, fname,
562 smb2_util_share_access(""),
563 smb2_util_oplock_level("b"));
564 io1.in.durable_open = true;
566 status = smb2_create(tree, mem_ctx, &io1);
567 CHECK_STATUS(status, NT_STATUS_OK);
568 _h = io1.out.file.handle;
570 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
571 CHECK_VAL(io1.out.durable_open, true);
572 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
574 /* disconnect, reconnect and then do durable reopen */
575 status = smb2_tdis(tree);
576 CHECK_STATUS(status, NT_STATUS_OK);
578 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
579 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
586 io2.in.fname = fname;
587 io2.in.durable_handle = h;
589 status = smb2_create(tree2, mem_ctx, &io2);
590 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
594 smb2_util_close(tree, *h);
597 smb2_util_unlink(tree2, fname);
601 talloc_free(mem_ctx);
607 * basic test for doing a durable open:
608 * logoff, create a new session, do a durable reopen (succeeds)
610 bool test_durable_open_reopen4(struct torture_context *tctx,
611 struct smb2_tree *tree)
614 TALLOC_CTX *mem_ctx = talloc_new(tctx);
616 struct smb2_handle _h;
617 struct smb2_handle *h = NULL;
618 struct smb2_create io1, io2;
620 struct smb2_transport *transport;
621 struct smb2_session *session2;
622 struct smb2_tree *tree2;
624 /* Choose a random name in case the state is left a little funky. */
625 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
626 generate_random_str(tctx, 8));
628 smb2_util_unlink(tree, fname);
630 smb2_oplock_create_share(&io1, fname,
631 smb2_util_share_access(""),
632 smb2_util_oplock_level("b"));
633 io1.in.durable_open = true;
634 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
636 status = smb2_create(tree, mem_ctx, &io1);
637 CHECK_STATUS(status, NT_STATUS_OK);
638 _h = io1.out.file.handle;
640 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
641 CHECK_VAL(io1.out.durable_open, true);
642 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
645 * do a session logoff, establish a new session and tree
646 * connect on the same transport, and try a durable reopen
648 transport = tree->session->transport;
649 status = smb2_logoff(tree->session);
650 CHECK_STATUS(status, NT_STATUS_OK);
652 if (!torture_smb2_session_setup(tctx, transport,
653 0, /* previous_session_id */
656 torture_warning(tctx, "session setup failed.\n");
662 * the session setup has talloc-stolen the transport,
663 * so we can safely free the old tree+session for clarity
667 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
668 torture_warning(tctx, "tree connect failed.\n");
674 io2.in.fname = fname;
675 io2.in.durable_handle = h;
678 status = smb2_create(tree2, mem_ctx, &io2);
679 CHECK_STATUS(status, NT_STATUS_OK);
681 _h = io2.out.file.handle;
683 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
684 CHECK_VAL(io2.out.durable_open, true);
685 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
689 smb2_util_close(tree2, *h);
692 smb2_util_unlink(tree2, fname);
696 talloc_free(mem_ctx);
702 basic testing of SMB2 durable opens
703 regarding the position information on the handle
705 bool test_durable_open_file_position(struct torture_context *tctx,
706 struct smb2_tree *tree1,
707 struct smb2_tree *tree2)
709 TALLOC_CTX *mem_ctx = talloc_new(tctx);
710 struct smb2_handle h1, h2;
711 struct smb2_create io1, io2;
713 const char *fname = "durable_open_position.dat";
714 union smb_fileinfo qfinfo;
715 union smb_setfileinfo sfinfo;
719 smb2_util_unlink(tree1, fname);
721 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
722 io1.in.durable_open = true;
724 status = smb2_create(tree1, mem_ctx, &io1);
725 CHECK_STATUS(status, NT_STATUS_OK);
726 h1 = io1.out.file.handle;
727 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
728 CHECK_VAL(io1.out.durable_open, true);
729 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
731 /* TODO: check extra blob content */
734 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
735 qfinfo.generic.in.file.handle = h1;
736 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
737 CHECK_STATUS(status, NT_STATUS_OK);
738 CHECK_VAL(qfinfo.position_information.out.position, 0);
739 pos = qfinfo.position_information.out.position;
740 torture_comment(tctx, "position: %llu\n",
741 (unsigned long long)pos);
744 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
745 sfinfo.generic.in.file.handle = h1;
746 sfinfo.position_information.in.position = 0x1000;
747 status = smb2_setinfo_file(tree1, &sfinfo);
748 CHECK_STATUS(status, NT_STATUS_OK);
751 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
752 qfinfo.generic.in.file.handle = h1;
753 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
754 CHECK_STATUS(status, NT_STATUS_OK);
755 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
756 pos = qfinfo.position_information.out.position;
757 torture_comment(tctx, "position: %llu\n",
758 (unsigned long long)pos);
764 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
765 qfinfo.generic.in.file.handle = h1;
766 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
767 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
770 io2.in.fname = fname;
771 io2.in.durable_handle = &h1;
773 status = smb2_create(tree2, mem_ctx, &io2);
774 CHECK_STATUS(status, NT_STATUS_OK);
775 CHECK_VAL(io2.out.durable_open, true);
776 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
777 CHECK_VAL(io2.out.reserved, 0x00);
778 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
779 CHECK_VAL(io2.out.alloc_size, 0);
780 CHECK_VAL(io2.out.size, 0);
781 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
782 CHECK_VAL(io2.out.reserved2, 0);
784 h2 = io2.out.file.handle;
787 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
788 qfinfo.generic.in.file.handle = h2;
789 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
790 CHECK_STATUS(status, NT_STATUS_OK);
791 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
792 pos = qfinfo.position_information.out.position;
793 torture_comment(tctx, "position: %llu\n",
794 (unsigned long long)pos);
796 smb2_util_close(tree2, h2);
798 talloc_free(mem_ctx);
800 smb2_util_unlink(tree2, fname);
809 Open, disconnect, oplock break, reconnect.
811 bool test_durable_open_oplock(struct torture_context *tctx,
812 struct smb2_tree *tree1,
813 struct smb2_tree *tree2)
815 TALLOC_CTX *mem_ctx = talloc_new(tctx);
816 struct smb2_create io1, io2;
817 struct smb2_handle h1, h2;
822 /* Choose a random name in case the state is left a little funky. */
823 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
826 smb2_util_unlink(tree1, fname);
828 /* Create with batch oplock */
829 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
830 io1.in.durable_open = true;
833 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
835 status = smb2_create(tree1, mem_ctx, &io1);
836 CHECK_STATUS(status, NT_STATUS_OK);
837 h1 = io1.out.file.handle;
838 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
839 CHECK_VAL(io1.out.durable_open, true);
840 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
842 /* Disconnect after getting the batch */
847 * Windows7 (build 7000) will break a batch oplock immediately if the
848 * original client is gone. (ZML: This seems like a bug. It should give
849 * some time for the client to reconnect!)
851 status = smb2_create(tree2, mem_ctx, &io2);
852 CHECK_STATUS(status, NT_STATUS_OK);
853 h2 = io2.out.file.handle;
854 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
855 CHECK_VAL(io2.out.durable_open, true);
856 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
858 /* What if tree1 tries to come back and reclaim? */
859 if (!torture_smb2_connection(tctx, &tree1)) {
860 torture_warning(tctx, "couldn't reconnect, bailing\n");
866 io1.in.fname = fname;
867 io1.in.durable_handle = &h1;
869 status = smb2_create(tree1, mem_ctx, &io1);
870 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
873 smb2_util_close(tree2, h2);
874 smb2_util_unlink(tree2, fname);
883 Open, disconnect, lease break, reconnect.
885 bool test_durable_open_lease(struct torture_context *tctx,
886 struct smb2_tree *tree1,
887 struct smb2_tree *tree2)
889 TALLOC_CTX *mem_ctx = talloc_new(tctx);
890 struct smb2_create io1, io2;
891 struct smb2_lease ls1, ls2;
892 struct smb2_handle h1, h2;
896 uint64_t lease1, lease2;
899 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
900 if (!(caps & SMB2_CAP_LEASING)) {
901 torture_skip(tctx, "leases are not supported");
905 * Choose a random name and random lease in case the state is left a
910 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
913 smb2_util_unlink(tree1, fname);
915 /* Create with lease */
916 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
917 lease1, smb2_util_lease_state("RHW"));
918 io1.in.durable_open = true;
920 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
921 lease2, smb2_util_lease_state("RHW"));
922 io2.in.durable_open = true;
923 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
925 status = smb2_create(tree1, mem_ctx, &io1);
926 CHECK_STATUS(status, NT_STATUS_OK);
927 h1 = io1.out.file.handle;
928 CHECK_VAL(io1.out.durable_open, true);
929 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
931 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
932 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
933 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
934 CHECK_VAL(io1.out.lease_response.lease_state,
935 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
937 /* Disconnect after getting the lease */
942 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
943 * even if the original client is gone. (ZML: This seems like a bug. It
944 * should give some time for the client to reconnect! And why RH?)
946 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
947 * Test is adapted accordingly.
949 status = smb2_create(tree2, mem_ctx, &io2);
950 CHECK_STATUS(status, NT_STATUS_OK);
951 h2 = io2.out.file.handle;
952 CHECK_VAL(io2.out.durable_open, true);
953 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
955 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
956 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
957 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
958 CHECK_VAL(io2.out.lease_response.lease_state,
959 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
961 /* What if tree1 tries to come back and reclaim? */
962 if (!torture_smb2_connection(tctx, &tree1)) {
963 torture_warning(tctx, "couldn't reconnect, bailing\n");
969 io1.in.fname = fname;
970 io1.in.durable_handle = &h1;
971 io1.in.lease_request = &ls1;
973 status = smb2_create(tree1, mem_ctx, &io1);
974 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
977 smb2_util_close(tree2, h2);
978 smb2_util_unlink(tree2, fname);
987 Open, take BRL, disconnect, reconnect.
989 bool test_durable_open_lock(struct torture_context *tctx,
990 struct smb2_tree *tree)
992 TALLOC_CTX *mem_ctx = talloc_new(tctx);
993 struct smb2_create io;
994 struct smb2_lease ls;
995 struct smb2_handle h;
996 struct smb2_lock lck;
997 struct smb2_lock_element el[2];
1004 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1005 if (!(caps & SMB2_CAP_LEASING)) {
1006 torture_skip(tctx, "leases are not supported");
1010 * Choose a random name and random lease in case the state is left a
1014 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1017 smb2_util_unlink(tree, fname);
1019 /* Create with lease */
1021 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1022 smb2_util_lease_state("RWH"));
1023 io.in.durable_open = true;
1025 status = smb2_create(tree, mem_ctx, &io);
1026 CHECK_STATUS(status, NT_STATUS_OK);
1027 h = io.out.file.handle;
1028 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1030 CHECK_VAL(io.out.durable_open, true);
1031 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1032 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1033 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1034 CHECK_VAL(io.out.lease_response.lease_state,
1035 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1040 lck.in.lock_count = 0x0001;
1041 lck.in.lock_sequence = 0x00000000;
1042 lck.in.file.handle = h;
1045 el[0].reserved = 0x00000000;
1046 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1047 status = smb2_lock(tree, &lck);
1048 CHECK_STATUS(status, NT_STATUS_OK);
1050 /* Disconnect/Reconnect. */
1054 if (!torture_smb2_connection(tctx, &tree)) {
1055 torture_warning(tctx, "couldn't reconnect, bailing\n");
1061 io.in.fname = fname;
1062 io.in.durable_handle = &h;
1063 io.in.lease_request = &ls;
1065 status = smb2_create(tree, mem_ctx, &io);
1066 CHECK_STATUS(status, NT_STATUS_OK);
1067 h = io.out.file.handle;
1069 lck.in.file.handle = h;
1070 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1071 status = smb2_lock(tree, &lck);
1072 CHECK_STATUS(status, NT_STATUS_OK);
1075 smb2_util_close(tree, h);
1076 smb2_util_unlink(tree, fname);
1083 * Open with a RH lease, disconnect, open in another tree, reconnect.
1085 * This test actually demonstrates a minimum level of respect for the durable
1086 * open in the face of another open. As long as this test shows an inability to
1087 * reconnect after an open, the oplock/lease tests above will certainly
1088 * demonstrate an error on reconnect.
1090 bool test_durable_open_open_lease(struct torture_context *tctx,
1091 struct smb2_tree *tree1,
1092 struct smb2_tree *tree2)
1094 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1095 struct smb2_create io1, io2;
1096 struct smb2_lease ls;
1097 struct smb2_handle h1, h2;
1104 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1105 if (!(caps & SMB2_CAP_LEASING)) {
1106 torture_skip(tctx, "leases are not supported");
1110 * Choose a random name and random lease in case the state is left a
1114 snprintf(fname, 256, "durable_open_open_lease_%s.dat",
1115 generate_random_str(tctx, 8));
1118 smb2_util_unlink(tree1, fname);
1120 /* Create with lease */
1121 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1122 smb2_util_share_access(""),
1124 smb2_util_lease_state("RH"));
1125 io1.in.durable_open = true;
1127 status = smb2_create(tree1, mem_ctx, &io1);
1128 CHECK_STATUS(status, NT_STATUS_OK);
1129 h1 = io1.out.file.handle;
1130 CHECK_VAL(io1.out.durable_open, true);
1131 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1133 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1134 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1135 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1136 CHECK_VAL(io1.out.lease_response.lease_state,
1137 smb2_util_lease_state("RH"));
1143 /* Open the file in tree2 */
1144 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1146 status = smb2_create(tree2, mem_ctx, &io2);
1147 CHECK_STATUS(status, NT_STATUS_OK);
1148 h2 = io2.out.file.handle;
1149 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1152 if (!torture_smb2_connection(tctx, &tree1)) {
1153 torture_warning(tctx, "couldn't reconnect, bailing\n");
1159 io1.in.fname = fname;
1160 io1.in.durable_handle = &h1;
1161 io1.in.lease_request = &ls;
1164 * Windows7 (build 7000) will give away an open immediately if the
1165 * original client is gone. (ZML: This seems like a bug. It should give
1166 * some time for the client to reconnect!)
1168 status = smb2_create(tree1, mem_ctx, &io1);
1169 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1170 h1 = io1.out.file.handle;
1173 smb2_util_close(tree2, h2);
1174 smb2_util_unlink(tree2, fname);
1175 smb2_util_close(tree1, h1);
1176 smb2_util_unlink(tree1, fname);
1185 * Open with a batch oplock, disconnect, open in another tree, reconnect.
1187 * This test actually demonstrates a minimum level of respect for the durable
1188 * open in the face of another open. As long as this test shows an inability to
1189 * reconnect after an open, the oplock/lease tests above will certainly
1190 * demonstrate an error on reconnect.
1192 bool test_durable_open_open_oplock(struct torture_context *tctx,
1193 struct smb2_tree *tree1,
1194 struct smb2_tree *tree2)
1196 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1197 struct smb2_create io1, io2;
1198 struct smb2_handle h1, h2;
1204 * Choose a random name and random lease in case the state is left a
1207 snprintf(fname, 256, "durable_open_open_oplock_%s.dat",
1208 generate_random_str(tctx, 8));
1211 smb2_util_unlink(tree1, fname);
1213 /* Create with batch oplock */
1214 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1215 io1.in.durable_open = true;
1217 status = smb2_create(tree1, mem_ctx, &io1);
1218 CHECK_STATUS(status, NT_STATUS_OK);
1219 h1 = io1.out.file.handle;
1220 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1221 CHECK_VAL(io1.out.durable_open, true);
1222 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1228 /* Open the file in tree2 */
1229 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1231 status = smb2_create(tree2, mem_ctx, &io2);
1232 CHECK_STATUS(status, NT_STATUS_OK);
1233 h2 = io2.out.file.handle;
1234 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1237 if (!torture_smb2_connection(tctx, &tree1)) {
1238 torture_warning(tctx, "couldn't reconnect, bailing\n");
1244 io1.in.fname = fname;
1245 io1.in.durable_handle = &h1;
1247 status = smb2_create(tree1, mem_ctx, &io1);
1248 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1249 h1 = io1.out.file.handle;
1252 smb2_util_close(tree2, h2);
1253 smb2_util_unlink(tree2, fname);
1254 smb2_util_close(tree1, h1);
1255 smb2_util_unlink(tree1, fname);
1263 struct torture_suite *torture_smb2_durable_open_init(void)
1265 struct torture_suite *suite =
1266 torture_suite_create(talloc_autofree_context(), "durable-open");
1268 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1269 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1270 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1271 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1272 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1273 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1274 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1275 torture_suite_add_2smb2_test(suite, "file-position",
1276 test_durable_open_file_position);
1277 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1278 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1279 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1280 torture_suite_add_2smb2_test(suite, "open-lease",
1281 test_durable_open_open_lease);
1282 torture_suite_add_2smb2_test(suite, "open-oplock",
1283 test_durable_open_open_oplock);
1285 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");