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 "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "../libcli/smb/smbXcli_base.h"
29 #define CHECK_VAL(v, correct) do { \
30 if ((v) != (correct)) { \
31 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
32 __location__, #v, (int)v, (int)correct); \
36 #define CHECK_STATUS(status, correct) do { \
37 if (!NT_STATUS_EQUAL(status, correct)) { \
38 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
39 nt_errstr(status), nt_errstr(correct)); \
44 #define CHECK_CREATED(__io, __created, __attribute) \
46 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
47 CHECK_VAL((__io)->out.alloc_size, 0); \
48 CHECK_VAL((__io)->out.size, 0); \
49 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
50 CHECK_VAL((__io)->out.reserved2, 0); \
55 * basic durable_open test.
56 * durable state should only be granted when requested
57 * along with a batch oplock or a handle lease.
59 * This test tests durable open with all possible oplock types.
62 struct durable_open_vs_oplock
{
64 const char *share_mode
;
68 #define NUM_OPLOCK_TYPES 4
69 #define NUM_SHARE_MODES 8
70 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
71 struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
89 { "s", "RWD", false },
98 { "x", "RWD", false },
107 { "b", "RWD", true },
110 static bool test_one_durable_open_open1(struct torture_context
*tctx
,
111 struct smb2_tree
*tree
,
113 struct durable_open_vs_oplock test
)
116 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
117 struct smb2_handle _h
;
118 struct smb2_handle
*h
= NULL
;
120 struct smb2_create io
;
122 smb2_util_unlink(tree
, fname
);
124 smb2_oplock_create_share(&io
, fname
,
125 smb2_util_share_access(test
.share_mode
),
126 smb2_util_oplock_level(test
.level
));
127 io
.in
.durable_open
= true;
129 status
= smb2_create(tree
, mem_ctx
, &io
);
130 CHECK_STATUS(status
, NT_STATUS_OK
);
131 _h
= io
.out
.file
.handle
;
133 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
134 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
135 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
139 smb2_util_close(tree
, *h
);
141 smb2_util_unlink(tree
, fname
);
142 talloc_free(mem_ctx
);
147 bool test_durable_open_open1(struct torture_context
*tctx
,
148 struct smb2_tree
*tree
)
150 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
155 /* Choose a random name in case the state is left a little funky. */
156 snprintf(fname
, 256, "durable_open_open1_%s.dat", generate_random_str(tctx
, 8));
158 smb2_util_unlink(tree
, fname
);
160 /* test various oplock levels with durable open */
162 for (i
= 0; i
< NUM_OPLOCK_OPEN_TESTS
; i
++) {
163 ret
= test_one_durable_open_open1(tctx
,
166 durable_open_vs_oplock_table
[i
]);
173 smb2_util_unlink(tree
, fname
);
175 talloc_free(mem_ctx
);
181 * basic durable_open test.
182 * durable state should only be granted when requested
183 * along with a batch oplock or a handle lease.
185 * This test tests durable open with all valid lease types.
188 struct durable_open_vs_lease
{
190 const char *share_mode
;
194 #define NUM_LEASE_TYPES 5
195 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
196 struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
205 { "", "RWD", false },
211 { "R", "RW", false },
212 { "R", "RD", false },
213 { "R", "DW", false },
214 { "R", "RWD", false },
217 { "RW", "R", false },
218 { "RW", "W", false },
219 { "RW", "D", false },
220 { "RW", "RW", false },
221 { "RW", "RD", false },
222 { "RW", "WD", false },
223 { "RW", "RWD", false },
229 { "RH", "RW", true },
230 { "RH", "RD", true },
231 { "RH", "WD", true },
232 { "RH", "RWD", true },
235 { "RHW", "R", true },
236 { "RHW", "W", true },
237 { "RHW", "D", true },
238 { "RHW", "RW", true },
239 { "RHW", "RD", true },
240 { "RHW", "WD", true },
241 { "RHW", "RWD", true },
244 static bool test_one_durable_open_open2(struct torture_context
*tctx
,
245 struct smb2_tree
*tree
,
247 struct durable_open_vs_lease test
)
250 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
251 struct smb2_handle _h
;
252 struct smb2_handle
*h
= NULL
;
254 struct smb2_create io
;
255 struct smb2_lease ls
;
258 smb2_util_unlink(tree
, fname
);
262 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
263 smb2_util_share_access(test
.share_mode
),
265 smb2_util_lease_state(test
.type
));
266 io
.in
.durable_open
= true;
268 status
= smb2_create(tree
, mem_ctx
, &io
);
269 CHECK_STATUS(status
, NT_STATUS_OK
);
270 _h
= io
.out
.file
.handle
;
272 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
273 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
274 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
275 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
276 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
277 CHECK_VAL(io
.out
.lease_response
.lease_state
,
278 smb2_util_lease_state(test
.type
));
281 smb2_util_close(tree
, *h
);
283 smb2_util_unlink(tree
, fname
);
284 talloc_free(mem_ctx
);
289 bool test_durable_open_open2(struct torture_context
*tctx
,
290 struct smb2_tree
*tree
)
292 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
297 /* Choose a random name in case the state is left a little funky. */
298 snprintf(fname
, 256, "durable_open_open2_%s.dat", generate_random_str(tctx
, 8));
300 smb2_util_unlink(tree
, fname
);
303 /* test various oplock levels with durable open */
305 for (i
= 0; i
< NUM_LEASE_OPEN_TESTS
; i
++) {
306 ret
= test_one_durable_open_open2(tctx
,
309 durable_open_vs_lease_table
[i
]);
316 smb2_util_unlink(tree
, fname
);
318 talloc_free(mem_ctx
);
324 * basic test for doing a durable open
325 * and do a durable reopen on the same connection
326 * while the first open is still active (fails)
328 bool test_durable_open_reopen1(struct torture_context
*tctx
,
329 struct smb2_tree
*tree
)
332 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
334 struct smb2_handle _h
;
335 struct smb2_handle
*h
= NULL
;
336 struct smb2_create io1
, io2
;
339 /* Choose a random name in case the state is left a little funky. */
340 snprintf(fname
, 256, "durable_open_reopen1_%s.dat",
341 generate_random_str(tctx
, 8));
343 smb2_util_unlink(tree
, fname
);
345 smb2_oplock_create_share(&io1
, fname
,
346 smb2_util_share_access(""),
347 smb2_util_oplock_level("b"));
348 io1
.in
.durable_open
= true;
350 status
= smb2_create(tree
, mem_ctx
, &io1
);
351 CHECK_STATUS(status
, NT_STATUS_OK
);
352 _h
= io1
.out
.file
.handle
;
354 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
355 CHECK_VAL(io1
.out
.durable_open
, true);
356 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
358 /* try a durable reconnect while the file is still open */
360 io2
.in
.fname
= fname
;
361 io2
.in
.durable_handle
= h
;
363 status
= smb2_create(tree
, mem_ctx
, &io2
);
364 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
368 smb2_util_close(tree
, *h
);
371 smb2_util_unlink(tree
, fname
);
375 talloc_free(mem_ctx
);
381 * basic test for doing a durable open
382 * tcp disconnect, reconnect, do a durable reopen (succeeds)
384 bool test_durable_open_reopen2(struct torture_context
*tctx
,
385 struct smb2_tree
*tree
)
388 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
390 struct smb2_handle _h
;
391 struct smb2_handle
*h
= NULL
;
392 struct smb2_create io1
, io2
;
395 /* Choose a random name in case the state is left a little funky. */
396 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
397 generate_random_str(tctx
, 8));
399 smb2_util_unlink(tree
, fname
);
401 smb2_oplock_create_share(&io1
, fname
,
402 smb2_util_share_access(""),
403 smb2_util_oplock_level("b"));
404 io1
.in
.durable_open
= true;
406 status
= smb2_create(tree
, mem_ctx
, &io1
);
407 CHECK_STATUS(status
, NT_STATUS_OK
);
408 _h
= io1
.out
.file
.handle
;
410 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
411 CHECK_VAL(io1
.out
.durable_open
, true);
412 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
414 /* disconnect, reconnect and then do durable reopen */
418 if (!torture_smb2_connection(tctx
, &tree
)) {
419 torture_warning(tctx
, "couldn't reconnect, bailing\n");
425 io2
.in
.fname
= fname
;
426 io2
.in
.durable_handle
= h
;
429 status
= smb2_create(tree
, mem_ctx
, &io2
);
430 CHECK_STATUS(status
, NT_STATUS_OK
);
431 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
432 CHECK_VAL(io2
.out
.durable_open
, true);
433 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
434 _h
= io2
.out
.file
.handle
;
439 smb2_util_close(tree
, *h
);
442 smb2_util_unlink(tree
, fname
);
446 talloc_free(mem_ctx
);
452 * basic test for doing a durable open
453 * tcp disconnect, reconnect with a session reconnect and
454 * do a durable reopen (succeeds)
456 bool test_durable_open_reopen2a(struct torture_context
*tctx
,
457 struct smb2_tree
*tree
)
460 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
462 struct smb2_handle _h
;
463 struct smb2_handle
*h
= NULL
;
464 struct smb2_create io1
, io2
;
465 uint64_t previous_session_id
;
468 /* Choose a random name in case the state is left a little funky. */
469 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
470 generate_random_str(tctx
, 8));
472 smb2_util_unlink(tree
, fname
);
474 smb2_oplock_create_share(&io1
, fname
,
475 smb2_util_share_access(""),
476 smb2_util_oplock_level("b"));
477 io1
.in
.durable_open
= true;
479 status
= smb2_create(tree
, mem_ctx
, &io1
);
480 CHECK_STATUS(status
, NT_STATUS_OK
);
481 _h
= io1
.out
.file
.handle
;
483 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
484 CHECK_VAL(io1
.out
.durable_open
, true);
485 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
487 /* disconnect, reconnect and then do durable reopen */
488 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
492 if (!torture_smb2_connection_ext(tctx
, previous_session_id
, &tree
)) {
493 torture_warning(tctx
, "couldn't reconnect, bailing\n");
499 io2
.in
.fname
= fname
;
500 io2
.in
.durable_handle
= h
;
503 status
= smb2_create(tree
, mem_ctx
, &io2
);
504 CHECK_STATUS(status
, NT_STATUS_OK
);
505 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
506 CHECK_VAL(io2
.out
.durable_open
, true);
507 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
508 _h
= io2
.out
.file
.handle
;
513 smb2_util_close(tree
, *h
);
516 smb2_util_unlink(tree
, fname
);
520 talloc_free(mem_ctx
);
527 * basic test for doing a durable open:
528 * tdis, new tcon, try durable reopen (fails)
530 bool test_durable_open_reopen3(struct torture_context
*tctx
,
531 struct smb2_tree
*tree
)
534 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
536 struct smb2_handle _h
;
537 struct smb2_handle
*h
= NULL
;
538 struct smb2_create io1
, io2
;
540 struct smb2_tree
*tree2
;
542 /* Choose a random name in case the state is left a little funky. */
543 snprintf(fname
, 256, "durable_open_reopen3_%s.dat",
544 generate_random_str(tctx
, 8));
546 smb2_util_unlink(tree
, fname
);
548 smb2_oplock_create_share(&io1
, fname
,
549 smb2_util_share_access(""),
550 smb2_util_oplock_level("b"));
551 io1
.in
.durable_open
= true;
553 status
= smb2_create(tree
, mem_ctx
, &io1
);
554 CHECK_STATUS(status
, NT_STATUS_OK
);
555 _h
= io1
.out
.file
.handle
;
557 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
558 CHECK_VAL(io1
.out
.durable_open
, true);
559 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
561 /* disconnect, reconnect and then do durable reopen */
562 status
= smb2_tdis(tree
);
563 CHECK_STATUS(status
, NT_STATUS_OK
);
565 if (!torture_smb2_tree_connect(tctx
, tree
->session
, mem_ctx
, &tree2
)) {
566 torture_warning(tctx
, "couldn't reconnect to share, bailing\n");
573 io2
.in
.fname
= fname
;
574 io2
.in
.durable_handle
= h
;
576 status
= smb2_create(tree2
, mem_ctx
, &io2
);
577 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
581 smb2_util_close(tree
, *h
);
584 smb2_util_unlink(tree2
, fname
);
588 talloc_free(mem_ctx
);
594 * basic test for doing a durable open:
595 * logoff, create a new session, do a durable reopen (succeeds)
597 bool test_durable_open_reopen4(struct torture_context
*tctx
,
598 struct smb2_tree
*tree
)
601 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
603 struct smb2_handle _h
;
604 struct smb2_handle
*h
= NULL
;
605 struct smb2_create io1
, io2
;
607 struct smb2_transport
*transport
;
608 struct smb2_session
*session2
;
609 struct smb2_tree
*tree2
;
611 /* Choose a random name in case the state is left a little funky. */
612 snprintf(fname
, 256, "durable_open_reopen4_%s.dat",
613 generate_random_str(tctx
, 8));
615 smb2_util_unlink(tree
, fname
);
617 smb2_oplock_create_share(&io1
, fname
,
618 smb2_util_share_access(""),
619 smb2_util_oplock_level("b"));
620 io1
.in
.durable_open
= true;
621 io1
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
623 status
= smb2_create(tree
, mem_ctx
, &io1
);
624 CHECK_STATUS(status
, NT_STATUS_OK
);
625 _h
= io1
.out
.file
.handle
;
627 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
628 CHECK_VAL(io1
.out
.durable_open
, true);
629 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
632 * do a session logoff, establish a new session and tree
633 * connect on the same transport, and try a durable reopen
635 transport
= tree
->session
->transport
;
636 status
= smb2_logoff(tree
->session
);
637 CHECK_STATUS(status
, NT_STATUS_OK
);
639 if (!torture_smb2_session_setup(tctx
, transport
,
640 0, /* previous_session_id */
643 torture_warning(tctx
, "session setup failed.\n");
649 * the session setup has talloc-stolen the transport,
650 * so we can safely free the old tree+session for clarity
655 io2
.in
.fname
= fname
;
656 io2
.in
.durable_handle
= h
;
658 status
= smb2_create(tree
, mem_ctx
, &io2
);
659 CHECK_STATUS(status
, NT_STATUS_NETWORK_NAME_DELETED
);
661 if (!torture_smb2_tree_connect(tctx
, session2
, mem_ctx
, &tree2
)) {
662 torture_warning(tctx
, "tree connect failed.\n");
668 io2
.in
.fname
= fname
;
669 io2
.in
.durable_handle
= h
;
672 status
= smb2_create(tree2
, mem_ctx
, &io2
);
673 CHECK_STATUS(status
, NT_STATUS_OK
);
675 _h
= io2
.out
.file
.handle
;
677 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
678 CHECK_VAL(io2
.out
.durable_open
, true);
679 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
683 smb2_util_close(tree2
, *h
);
686 smb2_util_unlink(tree2
, fname
);
690 talloc_free(mem_ctx
);
696 basic testing of SMB2 durable opens
697 regarding the position information on the handle
699 bool test_durable_open_file_position(struct torture_context
*tctx
,
700 struct smb2_tree
*tree1
,
701 struct smb2_tree
*tree2
)
703 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
704 struct smb2_handle h1
, h2
;
705 struct smb2_create io1
, io2
;
707 const char *fname
= "durable_open_position.dat";
708 union smb_fileinfo qfinfo
;
709 union smb_setfileinfo sfinfo
;
713 smb2_util_unlink(tree1
, fname
);
715 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
716 io1
.in
.durable_open
= true;
718 status
= smb2_create(tree1
, mem_ctx
, &io1
);
719 CHECK_STATUS(status
, NT_STATUS_OK
);
720 h1
= io1
.out
.file
.handle
;
721 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
722 CHECK_VAL(io1
.out
.durable_open
, true);
723 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
725 /* TODO: check extra blob content */
728 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
729 qfinfo
.generic
.in
.file
.handle
= h1
;
730 status
= smb2_getinfo_file(tree1
, mem_ctx
, &qfinfo
);
731 CHECK_STATUS(status
, NT_STATUS_OK
);
732 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0);
733 pos
= qfinfo
.position_information
.out
.position
;
734 torture_comment(tctx
, "position: %llu\n",
735 (unsigned long long)pos
);
738 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
739 sfinfo
.generic
.in
.file
.handle
= h1
;
740 sfinfo
.position_information
.in
.position
= 0x1000;
741 status
= smb2_setinfo_file(tree1
, &sfinfo
);
742 CHECK_STATUS(status
, NT_STATUS_OK
);
745 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
746 qfinfo
.generic
.in
.file
.handle
= h1
;
747 status
= smb2_getinfo_file(tree1
, mem_ctx
, &qfinfo
);
748 CHECK_STATUS(status
, NT_STATUS_OK
);
749 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
750 pos
= qfinfo
.position_information
.out
.position
;
751 torture_comment(tctx
, "position: %llu\n",
752 (unsigned long long)pos
);
758 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
759 qfinfo
.generic
.in
.file
.handle
= h1
;
760 status
= smb2_getinfo_file(tree2
, mem_ctx
, &qfinfo
);
761 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
764 io2
.in
.fname
= fname
;
765 io2
.in
.durable_handle
= &h1
;
767 status
= smb2_create(tree2
, mem_ctx
, &io2
);
768 CHECK_STATUS(status
, NT_STATUS_OK
);
769 CHECK_VAL(io2
.out
.durable_open
, true);
770 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
771 CHECK_VAL(io2
.out
.reserved
, 0x00);
772 CHECK_VAL(io2
.out
.create_action
, NTCREATEX_ACTION_EXISTED
);
773 CHECK_VAL(io2
.out
.alloc_size
, 0);
774 CHECK_VAL(io2
.out
.size
, 0);
775 CHECK_VAL(io2
.out
.file_attr
, FILE_ATTRIBUTE_ARCHIVE
);
776 CHECK_VAL(io2
.out
.reserved2
, 0);
778 h2
= io2
.out
.file
.handle
;
781 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
782 qfinfo
.generic
.in
.file
.handle
= h2
;
783 status
= smb2_getinfo_file(tree2
, mem_ctx
, &qfinfo
);
784 CHECK_STATUS(status
, NT_STATUS_OK
);
785 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
786 pos
= qfinfo
.position_information
.out
.position
;
787 torture_comment(tctx
, "position: %llu\n",
788 (unsigned long long)pos
);
790 smb2_util_close(tree2
, h2
);
792 talloc_free(mem_ctx
);
794 smb2_util_unlink(tree2
, fname
);
803 Open, disconnect, oplock break, reconnect.
805 bool test_durable_open_oplock(struct torture_context
*tctx
,
806 struct smb2_tree
*tree1
,
807 struct smb2_tree
*tree2
)
809 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
810 struct smb2_create io1
, io2
;
811 struct smb2_handle h1
, h2
;
816 /* Choose a random name in case the state is left a little funky. */
817 snprintf(fname
, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx
, 8));
820 smb2_util_unlink(tree1
, fname
);
822 /* Create with batch oplock */
823 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
824 io1
.in
.durable_open
= true;
827 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
829 status
= smb2_create(tree1
, mem_ctx
, &io1
);
830 CHECK_STATUS(status
, NT_STATUS_OK
);
831 h1
= io1
.out
.file
.handle
;
832 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
833 CHECK_VAL(io1
.out
.durable_open
, true);
834 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
836 /* Disconnect after getting the batch */
841 * Windows7 (build 7000) will break a batch oplock immediately if the
842 * original client is gone. (ZML: This seems like a bug. It should give
843 * some time for the client to reconnect!)
845 status
= smb2_create(tree2
, mem_ctx
, &io2
);
846 CHECK_STATUS(status
, NT_STATUS_OK
);
847 h2
= io2
.out
.file
.handle
;
848 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
849 CHECK_VAL(io2
.out
.durable_open
, true);
850 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
852 /* What if tree1 tries to come back and reclaim? */
853 if (!torture_smb2_connection(tctx
, &tree1
)) {
854 torture_warning(tctx
, "couldn't reconnect, bailing\n");
860 io1
.in
.fname
= fname
;
861 io1
.in
.durable_handle
= &h1
;
863 status
= smb2_create(tree1
, mem_ctx
, &io1
);
864 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
867 smb2_util_close(tree2
, h2
);
868 smb2_util_unlink(tree2
, fname
);
877 Open, disconnect, lease break, reconnect.
879 bool test_durable_open_lease(struct torture_context
*tctx
,
880 struct smb2_tree
*tree1
,
881 struct smb2_tree
*tree2
)
883 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
884 struct smb2_create io1
, io2
;
885 struct smb2_lease ls1
, ls2
;
886 struct smb2_handle h1
, h2
;
890 uint64_t lease1
, lease2
;
893 * Choose a random name and random lease in case the state is left a
898 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
901 smb2_util_unlink(tree1
, fname
);
903 /* Create with lease */
904 smb2_lease_create(&io1
, &ls1
, false /* dir */, fname
,
905 lease1
, smb2_util_lease_state("RHW"));
906 io1
.in
.durable_open
= true;
908 smb2_lease_create(&io2
, &ls2
, false /* dir */, fname
,
909 lease2
, smb2_util_lease_state("RHW"));
910 io2
.in
.durable_open
= true;
911 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
913 status
= smb2_create(tree1
, mem_ctx
, &io1
);
914 CHECK_STATUS(status
, NT_STATUS_OK
);
915 h1
= io1
.out
.file
.handle
;
916 CHECK_VAL(io1
.out
.durable_open
, true);
917 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
919 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
920 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease1
);
921 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease1
);
922 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
923 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
925 /* Disconnect after getting the lease */
930 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
931 * even if the original client is gone. (ZML: This seems like a bug. It
932 * should give some time for the client to reconnect! And why RH?)
934 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
935 * Test is adapted accordingly.
937 status
= smb2_create(tree2
, mem_ctx
, &io2
);
938 CHECK_STATUS(status
, NT_STATUS_OK
);
939 h2
= io2
.out
.file
.handle
;
940 CHECK_VAL(io2
.out
.durable_open
, true);
941 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
943 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
944 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[0], lease2
);
945 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[1], ~lease2
);
946 CHECK_VAL(io2
.out
.lease_response
.lease_state
,
947 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
949 /* What if tree1 tries to come back and reclaim? */
950 if (!torture_smb2_connection(tctx
, &tree1
)) {
951 torture_warning(tctx
, "couldn't reconnect, bailing\n");
957 io1
.in
.fname
= fname
;
958 io1
.in
.durable_handle
= &h1
;
959 io1
.in
.lease_request
= &ls1
;
961 status
= smb2_create(tree1
, mem_ctx
, &io1
);
962 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
965 smb2_util_close(tree2
, h2
);
966 smb2_util_unlink(tree2
, fname
);
975 Open, take BRL, disconnect, reconnect.
977 bool test_durable_open_lock(struct torture_context
*tctx
,
978 struct smb2_tree
*tree
)
980 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
981 struct smb2_create io
;
982 struct smb2_lease ls
;
983 struct smb2_handle h
;
984 struct smb2_lock lck
;
985 struct smb2_lock_element el
[2];
992 * Choose a random name and random lease in case the state is left a
996 snprintf(fname
, 256, "durable_open_lock_%s.dat", generate_random_str(tctx
, 8));
999 smb2_util_unlink(tree
, fname
);
1001 /* Create with lease */
1003 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease
,
1004 smb2_util_lease_state("RWH"));
1005 io
.in
.durable_open
= true;
1007 status
= smb2_create(tree
, mem_ctx
, &io
);
1008 CHECK_STATUS(status
, NT_STATUS_OK
);
1009 h
= io
.out
.file
.handle
;
1010 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1012 CHECK_VAL(io
.out
.durable_open
, true);
1013 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1014 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
1015 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
1016 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1017 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1022 lck
.in
.lock_count
= 0x0001;
1023 lck
.in
.lock_sequence
= 0x00000000;
1024 lck
.in
.file
.handle
= h
;
1027 el
[0].reserved
= 0x00000000;
1028 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
1029 status
= smb2_lock(tree
, &lck
);
1030 CHECK_STATUS(status
, NT_STATUS_OK
);
1032 /* Disconnect/Reconnect. */
1036 if (!torture_smb2_connection(tctx
, &tree
)) {
1037 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1043 io
.in
.fname
= fname
;
1044 io
.in
.durable_handle
= &h
;
1045 io
.in
.lease_request
= &ls
;
1047 status
= smb2_create(tree
, mem_ctx
, &io
);
1048 CHECK_STATUS(status
, NT_STATUS_OK
);
1049 h
= io
.out
.file
.handle
;
1051 lck
.in
.file
.handle
= h
;
1052 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
1053 status
= smb2_lock(tree
, &lck
);
1054 CHECK_STATUS(status
, NT_STATUS_OK
);
1057 smb2_util_close(tree
, h
);
1058 smb2_util_unlink(tree
, fname
);
1065 * Open with a RH lease, disconnect, open in another tree, reconnect.
1067 * This test actually demonstrates a minimum level of respect for the durable
1068 * open in the face of another open. As long as this test shows an inability to
1069 * reconnect after an open, the oplock/lease tests above will certainly
1070 * demonstrate an error on reconnect.
1072 bool test_durable_open_open_lease(struct torture_context
*tctx
,
1073 struct smb2_tree
*tree1
,
1074 struct smb2_tree
*tree2
)
1076 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1077 struct smb2_create io1
, io2
;
1078 struct smb2_lease ls
;
1079 struct smb2_handle h1
, h2
;
1086 * Choose a random name and random lease in case the state is left a
1090 snprintf(fname
, 256, "durable_open_open_lease_%s.dat",
1091 generate_random_str(tctx
, 8));
1094 smb2_util_unlink(tree1
, fname
);
1096 /* Create with lease */
1097 smb2_lease_create_share(&io1
, &ls
, false /* dir */, fname
,
1098 smb2_util_share_access(""),
1100 smb2_util_lease_state("RH"));
1101 io1
.in
.durable_open
= true;
1103 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1104 CHECK_STATUS(status
, NT_STATUS_OK
);
1105 h1
= io1
.out
.file
.handle
;
1106 CHECK_VAL(io1
.out
.durable_open
, true);
1107 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1109 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1110 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease
);
1111 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease
);
1112 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
1113 smb2_util_lease_state("RH"));
1119 /* Open the file in tree2 */
1120 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
1122 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1123 CHECK_STATUS(status
, NT_STATUS_OK
);
1124 h2
= io2
.out
.file
.handle
;
1125 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1128 if (!torture_smb2_connection(tctx
, &tree1
)) {
1129 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1135 io1
.in
.fname
= fname
;
1136 io1
.in
.durable_handle
= &h1
;
1137 io1
.in
.lease_request
= &ls
;
1140 * Windows7 (build 7000) will give away an open immediately if the
1141 * original client is gone. (ZML: This seems like a bug. It should give
1142 * some time for the client to reconnect!)
1144 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1145 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1146 h1
= io1
.out
.file
.handle
;
1149 smb2_util_close(tree2
, h2
);
1150 smb2_util_unlink(tree2
, fname
);
1151 smb2_util_close(tree1
, h1
);
1152 smb2_util_unlink(tree1
, fname
);
1161 * Open with a batch oplock, disconnect, open in another tree, reconnect.
1163 * This test actually demonstrates a minimum level of respect for the durable
1164 * open in the face of another open. As long as this test shows an inability to
1165 * reconnect after an open, the oplock/lease tests above will certainly
1166 * demonstrate an error on reconnect.
1168 bool test_durable_open_open_oplock(struct torture_context
*tctx
,
1169 struct smb2_tree
*tree1
,
1170 struct smb2_tree
*tree2
)
1172 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1173 struct smb2_create io1
, io2
;
1174 struct smb2_lease ls
;
1175 struct smb2_handle h1
, h2
;
1182 * Choose a random name and random lease in case the state is left a
1186 snprintf(fname
, 256, "durable_open_open_oplock_%s.dat",
1187 generate_random_str(tctx
, 8));
1190 smb2_util_unlink(tree1
, fname
);
1192 /* Create with batch oplock */
1193 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1194 io1
.in
.durable_open
= true;
1196 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1197 CHECK_STATUS(status
, NT_STATUS_OK
);
1198 h1
= io1
.out
.file
.handle
;
1199 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1200 CHECK_VAL(io1
.out
.durable_open
, true);
1201 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1207 /* Open the file in tree2 */
1208 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
1210 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1211 CHECK_STATUS(status
, NT_STATUS_OK
);
1212 h2
= io2
.out
.file
.handle
;
1213 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1216 if (!torture_smb2_connection(tctx
, &tree1
)) {
1217 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1223 io1
.in
.fname
= fname
;
1224 io1
.in
.durable_handle
= &h1
;
1225 io1
.in
.lease_request
= &ls
;
1227 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1228 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1229 h1
= io1
.out
.file
.handle
;
1232 smb2_util_close(tree2
, h2
);
1233 smb2_util_unlink(tree2
, fname
);
1234 smb2_util_close(tree1
, h1
);
1235 smb2_util_unlink(tree1
, fname
);
1243 struct torture_suite
*torture_smb2_durable_open_init(void)
1245 struct torture_suite
*suite
=
1246 torture_suite_create(talloc_autofree_context(), "durable-open");
1248 torture_suite_add_1smb2_test(suite
, "open1", test_durable_open_open1
);
1249 torture_suite_add_1smb2_test(suite
, "open2", test_durable_open_open2
);
1250 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_open_reopen1
);
1251 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_open_reopen2
);
1252 torture_suite_add_1smb2_test(suite
, "reopen2a", test_durable_open_reopen2a
);
1253 torture_suite_add_1smb2_test(suite
, "reopen3", test_durable_open_reopen3
);
1254 torture_suite_add_1smb2_test(suite
, "reopen4", test_durable_open_reopen4
);
1255 torture_suite_add_2smb2_test(suite
, "file-position",
1256 test_durable_open_file_position
);
1257 torture_suite_add_2smb2_test(suite
, "oplock", test_durable_open_oplock
);
1258 torture_suite_add_2smb2_test(suite
, "lease", test_durable_open_lease
);
1259 torture_suite_add_1smb2_test(suite
, "lock", test_durable_open_lock
);
1260 torture_suite_add_2smb2_test(suite
, "open-lease",
1261 test_durable_open_open_lease
);
1262 torture_suite_add_2smb2_test(suite
, "open-oplock",
1263 test_durable_open_open_oplock
);
1265 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-OPEN tests");