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"
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); \
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)); \
43 #define CHECK_CREATED(__io, __created, __attribute) \
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); \
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
{
63 const char *share_mode
;
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
] =
88 { "s", "RWD", false },
97 { "x", "RWD", false },
106 { "b", "RWD", true },
109 static bool test_one_durable_open_open1(struct torture_context
*tctx
,
110 struct smb2_tree
*tree
,
112 struct durable_open_vs_oplock test
)
115 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
116 struct smb2_handle _h
;
117 struct smb2_handle
*h
= NULL
;
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
;
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
));
138 smb2_util_close(tree
, *h
);
140 smb2_util_unlink(tree
, fname
);
141 talloc_free(mem_ctx
);
146 bool test_durable_open_open1(struct torture_context
*tctx
,
147 struct smb2_tree
*tree
)
149 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
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
,
165 durable_open_vs_oplock_table
[i
]);
172 smb2_util_unlink(tree
, fname
);
174 talloc_free(mem_ctx
);
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
{
189 const char *share_mode
;
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
] =
204 { "", "RWD", false },
210 { "R", "RW", false },
211 { "R", "RD", false },
212 { "R", "DW", false },
213 { "R", "RWD", 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 },
228 { "RH", "RW", true },
229 { "RH", "RD", true },
230 { "RH", "WD", true },
231 { "RH", "RWD", 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
,
246 struct durable_open_vs_lease test
)
249 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
250 struct smb2_handle _h
;
251 struct smb2_handle
*h
= NULL
;
253 struct smb2_create io
;
254 struct smb2_lease ls
;
257 smb2_util_unlink(tree
, fname
);
261 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
262 smb2_util_share_access(test
.share_mode
),
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
;
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
));
280 smb2_util_close(tree
, *h
);
282 smb2_util_unlink(tree
, fname
);
283 talloc_free(mem_ctx
);
288 bool test_durable_open_open2(struct torture_context
*tctx
,
289 struct smb2_tree
*tree
)
291 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
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
,
308 durable_open_vs_lease_table
[i
]);
315 smb2_util_unlink(tree
, fname
);
317 talloc_free(mem_ctx
);
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
)
331 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
333 struct smb2_handle _h
;
334 struct smb2_handle
*h
= NULL
;
335 struct smb2_create io1
, io2
;
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
;
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 */
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
);
367 smb2_util_close(tree
, *h
);
370 smb2_util_unlink(tree
, fname
);
374 talloc_free(mem_ctx
);
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
)
387 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
389 struct smb2_handle _h
;
390 struct smb2_handle
*h
= NULL
;
391 struct smb2_create io1
, io2
;
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
;
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 */
417 if (!torture_smb2_connection(tctx
, &tree
)) {
418 torture_warning(tctx
, "couldn't reconnect, bailing\n");
424 io2
.in
.fname
= fname
;
425 io2
.in
.durable_handle
= h
;
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
;
438 smb2_util_close(tree
, *h
);
441 smb2_util_unlink(tree
, fname
);
445 talloc_free(mem_ctx
);
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
)
458 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
460 struct smb2_handle _h
;
461 struct smb2_handle
*h
= NULL
;
462 struct smb2_create io1
, io2
;
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
;
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");
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
);
505 smb2_util_close(tree
, *h
);
508 smb2_util_unlink(tree2
, fname
);
512 talloc_free(mem_ctx
);
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
)
525 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
527 struct smb2_handle _h
;
528 struct smb2_handle
*h
= NULL
;
529 struct smb2_create io1
, io2
;
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
;
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");
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");
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
;
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"));
593 smb2_util_close(tree
, *h
);
596 smb2_util_unlink(tree2
, fname
);
600 talloc_free(mem_ctx
);
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
;
617 const char *fname
= "durable_open_position.dat";
618 union smb_fileinfo qfinfo
;
619 union smb_setfileinfo sfinfo
;
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 */
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
);
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
);
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
);
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
);
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
;
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
);
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
;
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));
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;
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 */
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");
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
);
777 smb2_util_close(tree2
, h2
);
778 smb2_util_unlink(tree2
, fname
);
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
;
800 uint64_t lease1
, lease2
;
803 * Choose a random name and random lease in case the state is left a
808 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
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 */
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");
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
);
875 smb2_util_close(tree2
, h2
);
876 smb2_util_unlink(tree2
, fname
);
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];
902 * Choose a random name and random lease in case the state is left a
906 snprintf(fname
, 256, "durable_open_lock_%s.dat", generate_random_str(tctx
, 8));
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
);
932 lck
.in
.lock_count
= 0x0001;
933 lck
.in
.lock_sequence
= 0x00000000;
934 lck
.in
.file
.handle
= h
;
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. */
946 if (!torture_smb2_connection(tctx
, &tree
)) {
947 torture_warning(tctx
, "couldn't reconnect, bailing\n");
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
);
967 smb2_util_close(tree
, h
);
968 smb2_util_unlink(tree
, fname
);
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
;
996 * Choose a random name and random lease in case the state is left a
1000 snprintf(fname
, 256, "durable_open_open_%s.dat", generate_random_str(tctx
, 8));
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(""),
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"));
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
);
1037 if (!torture_smb2_connection(tctx
, &tree1
)) {
1038 torture_warning(tctx
, "couldn't reconnect, bailing\n");
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
;
1058 smb2_util_close(tree2
, h2
);
1059 smb2_util_unlink(tree2
, fname
);
1060 smb2_util_close(tree1
, h1
);
1061 smb2_util_unlink(tree1
, fname
);
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");