2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
7 Copyright (C) Michael Adam 2011-2012
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
31 #define CHECK_VAL(v, correct) \
32 torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__)
34 #define CHECK_NOT_VAL(v, incorrect) \
35 torture_assert_u64_not_equal_goto(tctx, v, incorrect, ret, done, __location__)
37 #define CHECK_NOT_NULL(p) \
38 torture_assert_not_null_goto(tctx, p, ret, done, __location__)
40 #define CHECK_STATUS(status, correct) \
41 torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__)
43 #define CHECK_CREATED(__io, __created, __attribute) \
45 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46 CHECK_VAL((__io)->out.size, 0); \
47 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
48 CHECK_VAL((__io)->out.reserved2, 0); \
51 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
53 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
54 if (__alloc_size != 0) { \
55 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
57 CHECK_VAL((__io)->out.size, (__size)); \
58 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
59 CHECK_VAL((__io)->out.reserved2, 0); \
65 * basic durable_open test.
66 * durable state should only be granted when requested
67 * along with a batch oplock or a handle lease.
69 * This test tests durable open with all possible oplock types.
72 struct durable_open_vs_oplock
{
74 const char *share_mode
;
78 #define NUM_OPLOCK_TYPES 4
79 #define NUM_SHARE_MODES 8
80 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
81 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
99 { "s", "RWD", false },
105 { "x", "RD", false },
106 { "x", "RW", false },
107 { "x", "WD", false },
108 { "x", "RWD", false },
117 { "b", "RWD", true },
120 static bool test_one_durable_open_open_oplock(struct torture_context
*tctx
,
121 struct smb2_tree
*tree
,
123 struct durable_open_vs_oplock test
)
126 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
127 struct smb2_handle _h
;
128 struct smb2_handle
*h
= NULL
;
130 struct smb2_create io
;
132 smb2_util_unlink(tree
, fname
);
134 smb2_oplock_create_share(&io
, fname
,
135 smb2_util_share_access(test
.share_mode
),
136 smb2_util_oplock_level(test
.level
));
137 io
.in
.durable_open
= true;
139 status
= smb2_create(tree
, mem_ctx
, &io
);
140 CHECK_STATUS(status
, NT_STATUS_OK
);
141 _h
= io
.out
.file
.handle
;
143 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
144 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
145 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
149 smb2_util_close(tree
, *h
);
151 smb2_util_unlink(tree
, fname
);
152 talloc_free(mem_ctx
);
157 static bool test_durable_open_open_oplock(struct torture_context
*tctx
,
158 struct smb2_tree
*tree
)
160 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
165 /* Choose a random name in case the state is left a little funky. */
166 snprintf(fname
, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx
, 8));
168 smb2_util_unlink(tree
, fname
);
170 /* test various oplock levels with durable open */
172 for (i
= 0; i
< NUM_OPLOCK_OPEN_TESTS
; i
++) {
173 ret
= test_one_durable_open_open_oplock(tctx
,
176 durable_open_vs_oplock_table
[i
]);
183 smb2_util_unlink(tree
, fname
);
185 talloc_free(mem_ctx
);
191 * basic durable_open test.
192 * durable state should only be granted when requested
193 * along with a batch oplock or a handle lease.
195 * This test tests durable open with all valid lease types.
198 struct durable_open_vs_lease
{
200 const char *share_mode
;
204 #define NUM_LEASE_TYPES 5
205 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
206 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
215 { "", "RWD", false },
221 { "R", "RW", false },
222 { "R", "RD", false },
223 { "R", "DW", false },
224 { "R", "RWD", false },
227 { "RW", "R", false },
228 { "RW", "W", false },
229 { "RW", "D", false },
230 { "RW", "RW", false },
231 { "RW", "RD", false },
232 { "RW", "WD", false },
233 { "RW", "RWD", false },
239 { "RH", "RW", true },
240 { "RH", "RD", true },
241 { "RH", "WD", true },
242 { "RH", "RWD", true },
245 { "RHW", "R", true },
246 { "RHW", "W", true },
247 { "RHW", "D", true },
248 { "RHW", "RW", true },
249 { "RHW", "RD", true },
250 { "RHW", "WD", true },
251 { "RHW", "RWD", true },
254 static bool test_one_durable_open_open_lease(struct torture_context
*tctx
,
255 struct smb2_tree
*tree
,
257 struct durable_open_vs_lease test
)
260 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
261 struct smb2_handle _h
;
262 struct smb2_handle
*h
= NULL
;
264 struct smb2_create io
;
265 struct smb2_lease ls
;
269 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
270 if (!(caps
& SMB2_CAP_LEASING
)) {
271 torture_skip(tctx
, "leases are not supported");
274 smb2_util_unlink(tree
, fname
);
278 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
279 smb2_util_share_access(test
.share_mode
),
281 smb2_util_lease_state(test
.type
));
282 io
.in
.durable_open
= true;
284 status
= smb2_create(tree
, mem_ctx
, &io
);
285 CHECK_STATUS(status
, NT_STATUS_OK
);
286 _h
= io
.out
.file
.handle
;
288 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
289 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
290 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
291 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
292 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
293 CHECK_VAL(io
.out
.lease_response
.lease_state
,
294 smb2_util_lease_state(test
.type
));
297 smb2_util_close(tree
, *h
);
299 smb2_util_unlink(tree
, fname
);
300 talloc_free(mem_ctx
);
305 static bool test_durable_open_open_lease(struct torture_context
*tctx
,
306 struct smb2_tree
*tree
)
308 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
314 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
315 if (!(caps
& SMB2_CAP_LEASING
)) {
316 torture_skip(tctx
, "leases are not supported");
319 /* Choose a random name in case the state is left a little funky. */
320 snprintf(fname
, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx
, 8));
322 smb2_util_unlink(tree
, fname
);
325 /* test various oplock levels with durable open */
327 for (i
= 0; i
< NUM_LEASE_OPEN_TESTS
; i
++) {
328 ret
= test_one_durable_open_open_lease(tctx
,
331 durable_open_vs_lease_table
[i
]);
338 smb2_util_unlink(tree
, fname
);
340 talloc_free(mem_ctx
);
346 * basic test for doing a durable open
347 * and do a durable reopen on the same connection
348 * while the first open is still active (fails)
350 static bool test_durable_open_reopen1(struct torture_context
*tctx
,
351 struct smb2_tree
*tree
)
354 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
356 struct smb2_handle _h
;
357 struct smb2_handle
*h
= NULL
;
358 struct smb2_create io1
, io2
;
361 /* Choose a random name in case the state is left a little funky. */
362 snprintf(fname
, 256, "durable_open_reopen1_%s.dat",
363 generate_random_str(tctx
, 8));
365 smb2_util_unlink(tree
, fname
);
367 smb2_oplock_create_share(&io1
, fname
,
368 smb2_util_share_access(""),
369 smb2_util_oplock_level("b"));
370 io1
.in
.durable_open
= true;
372 status
= smb2_create(tree
, mem_ctx
, &io1
);
373 CHECK_STATUS(status
, NT_STATUS_OK
);
374 _h
= io1
.out
.file
.handle
;
376 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
377 CHECK_VAL(io1
.out
.durable_open
, true);
378 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
380 /* try a durable reconnect while the file is still open */
382 io2
.in
.fname
= fname
;
383 io2
.in
.durable_handle
= h
;
385 status
= smb2_create(tree
, mem_ctx
, &io2
);
386 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
390 smb2_util_close(tree
, *h
);
393 smb2_util_unlink(tree
, fname
);
397 talloc_free(mem_ctx
);
403 * Basic test for doing a durable open
404 * and do a session reconnect while the first
405 * session is still active and the handle is
406 * still open in the client.
407 * This closes the original session and a
408 * durable reconnect on the new session succeeds.
410 static bool test_durable_open_reopen1a(struct torture_context
*tctx
,
411 struct smb2_tree
*tree
)
414 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
416 struct smb2_handle _h
;
417 struct smb2_handle
*h
= NULL
;
418 struct smb2_create io
;
420 struct smb2_tree
*tree2
= NULL
;
421 struct smb2_tree
*tree3
= NULL
;
422 uint64_t previous_session_id
;
423 struct smbcli_options options
;
424 struct GUID orig_client_guid
;
426 options
= tree
->session
->transport
->options
;
427 orig_client_guid
= options
.client_guid
;
429 /* Choose a random name in case the state is left a little funky. */
430 snprintf(fname
, 256, "durable_open_reopen1a_%s.dat",
431 generate_random_str(tctx
, 8));
433 smb2_util_unlink(tree
, fname
);
435 smb2_oplock_create_share(&io
, fname
,
436 smb2_util_share_access(""),
437 smb2_util_oplock_level("b"));
438 io
.in
.durable_open
= true;
440 status
= smb2_create(tree
, mem_ctx
, &io
);
441 CHECK_STATUS(status
, NT_STATUS_OK
);
442 _h
= io
.out
.file
.handle
;
444 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
445 CHECK_VAL(io
.out
.durable_open
, true);
446 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
449 * a session reconnect on a second tcp connection
452 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
454 /* for oplocks, the client guid can be different: */
455 options
.client_guid
= GUID_random();
457 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
459 torture_assert_goto(tctx
, ret
, ret
, done
, "could not reconnect");
462 * check that this has deleted the old session
467 io
.in
.durable_handle
= h
;
469 status
= smb2_create(tree
, mem_ctx
, &io
);
470 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
475 * but a durable reconnect on the new session succeeds:
480 io
.in
.durable_handle
= h
;
482 status
= smb2_create(tree2
, mem_ctx
, &io
);
483 CHECK_STATUS(status
, NT_STATUS_OK
);
484 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
485 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
486 _h
= io
.out
.file
.handle
;
490 * a session reconnect on a second tcp connection
493 previous_session_id
= smb2cli_session_current_id(tree2
->session
->smbXcli
);
495 /* the original client_guid works just the same */
496 options
.client_guid
= orig_client_guid
;
498 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
500 torture_assert_goto(tctx
, ret
, ret
, done
, "could not reconnect");
503 * check that this has deleted the old session
508 io
.in
.durable_handle
= h
;
510 status
= smb2_create(tree2
, mem_ctx
, &io
);
511 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
516 * but a durable reconnect on the new session succeeds:
521 io
.in
.durable_handle
= h
;
523 status
= smb2_create(tree3
, mem_ctx
, &io
);
524 CHECK_STATUS(status
, NT_STATUS_OK
);
525 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
526 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
527 _h
= io
.out
.file
.handle
;
541 smb2_util_close(tree
, *h
);
544 smb2_util_unlink(tree
, fname
);
549 talloc_free(mem_ctx
);
555 * lease variant of reopen1a
557 * Basic test for doing a durable open and doing a session
558 * reconnect while the first session is still active and the
559 * handle is still open in the client.
560 * This closes the original session and a durable reconnect on
561 * the new session succeeds depending on the client guid:
563 * Durable reconnect on a session with a different client guid fails.
564 * Durable reconnect on a session with the original client guid succeeds.
566 bool test_durable_open_reopen1a_lease(struct torture_context
*tctx
,
567 struct smb2_tree
*tree
)
570 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
572 struct smb2_handle _h
;
573 struct smb2_handle
*h
= NULL
;
574 struct smb2_create io
;
575 struct smb2_lease ls
;
578 struct smb2_tree
*tree2
= NULL
;
579 struct smb2_tree
*tree3
= NULL
;
580 uint64_t previous_session_id
;
581 struct smbcli_options options
;
582 struct GUID orig_client_guid
;
584 options
= tree
->session
->transport
->options
;
585 orig_client_guid
= options
.client_guid
;
587 /* Choose a random name in case the state is left a little funky. */
588 snprintf(fname
, 256, "durable_v2_open_reopen1a_lease_%s.dat",
589 generate_random_str(tctx
, 8));
591 smb2_util_unlink(tree
, fname
);
593 lease_key
= random();
594 smb2_lease_create(&io
, &ls
, false /* dir */, fname
,
595 lease_key
, smb2_util_lease_state("RWH"));
596 io
.in
.durable_open
= true;
598 status
= smb2_create(tree
, mem_ctx
, &io
);
599 CHECK_STATUS(status
, NT_STATUS_OK
);
600 _h
= io
.out
.file
.handle
;
602 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
603 CHECK_VAL(io
.out
.durable_open
, true);
604 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
605 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
606 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
607 CHECK_VAL(io
.out
.lease_response
.lease_state
,
608 smb2_util_lease_state("RWH"));
609 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
610 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
612 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
615 * a session reconnect on a second tcp connection
616 * with a different client_guid does not allow
617 * the durable reconnect.
620 options
.client_guid
= GUID_random();
622 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
624 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
627 * check that this has deleted the old session
632 io
.in
.durable_handle
= h
;
633 io
.in
.lease_request
= &ls
;
634 status
= smb2_create(tree
, mem_ctx
, &io
);
635 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
640 * but a durable reconnect on the new session with the wrong
646 io
.in
.durable_handle
= h
;
647 io
.in
.lease_request
= &ls
;
648 status
= smb2_create(tree2
, mem_ctx
, &io
);
649 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
652 * now a session reconnect on a second tcp connection
653 * with original client_guid allows the durable reconnect.
656 options
.client_guid
= orig_client_guid
;
658 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
660 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
663 * check that this has deleted the old session
664 * In this case, a durable reconnect attempt with the
665 * correct client_guid yields a different error code.
670 io
.in
.durable_handle
= h
;
671 io
.in
.lease_request
= &ls
;
672 status
= smb2_create(tree2
, mem_ctx
, &io
);
673 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
677 * but a durable reconnect on the new session succeeds:
682 io
.in
.durable_handle
= h
;
683 io
.in
.lease_request
= &ls
;
684 status
= smb2_create(tree3
, mem_ctx
, &io
);
685 CHECK_STATUS(status
, NT_STATUS_OK
);
686 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
687 CHECK_VAL(io
.out
.durable_open
, false); /* no dh response context... */
688 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
689 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
690 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
691 CHECK_VAL(io
.out
.lease_response
.lease_state
,
692 smb2_util_lease_state("RWH"));
693 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
694 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
695 _h
= io
.out
.file
.handle
;
709 smb2_util_close(tree
, *h
);
712 smb2_util_unlink(tree
, fname
);
717 talloc_free(mem_ctx
);
724 * basic test for doing a durable open
725 * tcp disconnect, reconnect, do a durable reopen (succeeds)
727 static bool test_durable_open_reopen2(struct torture_context
*tctx
,
728 struct smb2_tree
*tree
)
731 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
733 struct smb2_handle _h
;
734 struct smb2_handle
*h
= NULL
;
735 struct smb2_create io
;
738 /* Choose a random name in case the state is left a little funky. */
739 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
740 generate_random_str(tctx
, 8));
742 smb2_util_unlink(tree
, fname
);
744 smb2_oplock_create_share(&io
, fname
,
745 smb2_util_share_access(""),
746 smb2_util_oplock_level("b"));
747 io
.in
.durable_open
= true;
749 status
= smb2_create(tree
, mem_ctx
, &io
);
750 CHECK_STATUS(status
, NT_STATUS_OK
);
751 _h
= io
.out
.file
.handle
;
753 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
754 CHECK_VAL(io
.out
.durable_open
, true);
755 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
757 /* disconnect, leaving the durable in place */
760 if (!torture_smb2_connection(tctx
, &tree
)) {
761 torture_warning(tctx
, "couldn't reconnect, bailing\n");
767 /* the path name is ignored by the server */
769 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
772 status
= smb2_create(tree
, mem_ctx
, &io
);
773 CHECK_STATUS(status
, NT_STATUS_OK
);
774 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
775 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
776 _h
= io
.out
.file
.handle
;
779 /* disconnect again, leaving the durable in place */
782 if (!torture_smb2_connection(tctx
, &tree
)) {
783 torture_warning(tctx
, "couldn't reconnect, bailing\n");
789 * show that the filename and many other fields
790 * are ignored. only the reconnect request blob
794 /* the path name is ignored by the server */
795 io
.in
.security_flags
= 0x78;
796 io
.in
.oplock_level
= 0x78;
797 io
.in
.impersonation_level
= 0x12345678;
798 io
.in
.create_flags
= 0x12345678;
799 io
.in
.reserved
= 0x12345678;
800 io
.in
.desired_access
= 0x12345678;
801 io
.in
.file_attributes
= 0x12345678;
802 io
.in
.share_access
= 0x12345678;
803 io
.in
.create_disposition
= 0x12345678;
804 io
.in
.create_options
= 0x12345678;
805 io
.in
.fname
= "__non_existing_fname__";
806 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
809 status
= smb2_create(tree
, mem_ctx
, &io
);
810 CHECK_STATUS(status
, NT_STATUS_OK
);
811 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
812 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
813 _h
= io
.out
.file
.handle
;
816 /* disconnect, leaving the durable in place */
819 if (!torture_smb2_connection(tctx
, &tree
)) {
820 torture_warning(tctx
, "couldn't reconnect, bailing\n");
826 * show that an additionally specified durable v1 request
827 * is ignored by the server.
828 * See MS-SMB2, 3.3.5.9.7
829 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
832 /* the path name is ignored by the server */
834 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
835 io
.in
.durable_open
= true; /* durable v1 handle request */
838 status
= smb2_create(tree
, mem_ctx
, &io
);
839 CHECK_STATUS(status
, NT_STATUS_OK
);
840 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
841 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
842 _h
= io
.out
.file
.handle
;
848 smb2_util_close(tree
, *h
);
851 smb2_util_unlink(tree
, fname
);
856 talloc_free(mem_ctx
);
862 * lease variant of reopen2
863 * basic test for doing a durable open
864 * tcp disconnect, reconnect, do a durable reopen (succeeds)
866 static bool test_durable_open_reopen2_lease(struct torture_context
*tctx
,
867 struct smb2_tree
*tree
)
870 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
872 struct smb2_handle _h
;
873 struct smb2_handle
*h
= NULL
;
874 struct smb2_create io
;
875 struct smb2_lease ls
;
878 struct smbcli_options options
;
881 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
882 if (!(caps
& SMB2_CAP_LEASING
)) {
883 torture_skip(tctx
, "leases are not supported");
886 options
= tree
->session
->transport
->options
;
888 /* Choose a random name in case the state is left a little funky. */
889 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
890 generate_random_str(tctx
, 8));
892 smb2_util_unlink(tree
, fname
);
894 lease_key
= random();
895 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease_key
,
896 smb2_util_lease_state("RWH"));
897 io
.in
.durable_open
= true;
899 status
= smb2_create(tree
, mem_ctx
, &io
);
900 CHECK_STATUS(status
, NT_STATUS_OK
);
901 _h
= io
.out
.file
.handle
;
903 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
905 CHECK_VAL(io
.out
.durable_open
, true);
906 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
907 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
908 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
909 CHECK_VAL(io
.out
.lease_response
.lease_state
,
910 smb2_util_lease_state("RWH"));
911 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
912 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
914 /* disconnect, reconnect and then do durable reopen */
917 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
918 torture_warning(tctx
, "couldn't reconnect, bailing\n");
924 /* a few failure tests: */
927 * several attempts without lease attached:
928 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
929 * irrespective of file name provided
934 io
.in
.durable_handle
= h
;
935 status
= smb2_create(tree
, mem_ctx
, &io
);
936 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
939 io
.in
.fname
= "__non_existing_fname__";
940 io
.in
.durable_handle
= h
;
941 status
= smb2_create(tree
, mem_ctx
, &io
);
942 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
946 io
.in
.durable_handle
= h
;
947 status
= smb2_create(tree
, mem_ctx
, &io
);
948 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
951 * attempt with lease provided, but
952 * with a changed lease key. => fails
956 io
.in
.durable_open
= false;
957 io
.in
.durable_handle
= h
;
958 io
.in
.lease_request
= &ls
;
959 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
960 /* a wrong lease key lets the request fail */
961 ls
.lease_key
.data
[0]++;
963 status
= smb2_create(tree
, mem_ctx
, &io
);
964 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
966 /* restore the correct lease key */
967 ls
.lease_key
.data
[0]--;
970 * this last failing attempt is almost correct:
971 * only problem is: we use the wrong filename...
972 * Note that this gives INVALID_PARAMETER.
973 * This is different from oplocks!
976 io
.in
.fname
= "__non_existing_fname__";
977 io
.in
.durable_open
= false;
978 io
.in
.durable_handle
= h
;
979 io
.in
.lease_request
= &ls
;
980 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
982 status
= smb2_create(tree
, mem_ctx
, &io
);
983 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
986 * Now for a succeeding reconnect:
991 io
.in
.durable_open
= false;
992 io
.in
.durable_handle
= h
;
993 io
.in
.lease_request
= &ls
;
994 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
996 /* the requested lease state is irrelevant */
997 ls
.lease_state
= smb2_util_lease_state("");
1001 status
= smb2_create(tree
, mem_ctx
, &io
);
1002 CHECK_STATUS(status
, NT_STATUS_OK
);
1004 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1005 CHECK_VAL(io
.out
.durable_open
, false);
1006 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1007 CHECK_VAL(io
.out
.persistent_open
, false);
1008 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1009 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1010 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1011 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1012 smb2_util_lease_state("RWH"));
1013 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1014 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1015 _h
= io
.out
.file
.handle
;
1018 /* disconnect one more time */
1021 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1022 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1028 * demonstrate that various parameters are ignored
1034 * These are completely ignored by the server
1036 io
.in
.security_flags
= 0x78;
1037 io
.in
.oplock_level
= 0x78;
1038 io
.in
.impersonation_level
= 0x12345678;
1039 io
.in
.create_flags
= 0x12345678;
1040 io
.in
.reserved
= 0x12345678;
1041 io
.in
.desired_access
= 0x12345678;
1042 io
.in
.file_attributes
= 0x12345678;
1043 io
.in
.share_access
= 0x12345678;
1044 io
.in
.create_disposition
= 0x12345678;
1045 io
.in
.create_options
= 0x12345678;
1048 * only these are checked:
1050 * - io.in.durable_handle,
1051 * - io.in.lease_request->lease_key
1054 io
.in
.fname
= fname
;
1055 io
.in
.durable_open_v2
= false;
1056 io
.in
.durable_handle_v2
= h
;
1057 io
.in
.lease_request
= &ls
;
1059 /* the requested lease state is irrelevant */
1060 ls
.lease_state
= smb2_util_lease_state("");
1064 status
= smb2_create(tree
, mem_ctx
, &io
);
1065 CHECK_STATUS(status
, NT_STATUS_OK
);
1067 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1068 CHECK_VAL(io
.out
.durable_open
, false);
1069 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1070 CHECK_VAL(io
.out
.persistent_open
, false);
1071 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1072 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1073 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1074 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1075 smb2_util_lease_state("RWH"));
1076 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1077 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1079 _h
= io
.out
.file
.handle
;
1085 smb2_util_close(tree
, *h
);
1088 smb2_util_unlink(tree
, fname
);
1093 talloc_free(mem_ctx
);
1099 * lease v2 variant of reopen2
1100 * basic test for doing a durable open
1101 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1103 static bool test_durable_open_reopen2_lease_v2(struct torture_context
*tctx
,
1104 struct smb2_tree
*tree
)
1107 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1109 struct smb2_handle _h
;
1110 struct smb2_handle
*h
= NULL
;
1111 struct smb2_create io
;
1112 struct smb2_lease ls
;
1115 struct smbcli_options options
;
1118 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1119 if (!(caps
& SMB2_CAP_LEASING
)) {
1120 torture_skip(tctx
, "leases are not supported");
1123 options
= tree
->session
->transport
->options
;
1125 /* Choose a random name in case the state is left a little funky. */
1126 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
1127 generate_random_str(tctx
, 8));
1129 smb2_util_unlink(tree
, fname
);
1131 lease_key
= random();
1132 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
1133 lease_key
, 0, /* parent lease key */
1134 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1135 io
.in
.durable_open
= true;
1137 status
= smb2_create(tree
, mem_ctx
, &io
);
1138 CHECK_STATUS(status
, NT_STATUS_OK
);
1139 _h
= io
.out
.file
.handle
;
1141 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1143 CHECK_VAL(io
.out
.durable_open
, true);
1144 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1145 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1146 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1147 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1148 smb2_util_lease_state("RWH"));
1149 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1150 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1152 /* disconnect, reconnect and then do durable reopen */
1155 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1156 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1161 /* a few failure tests: */
1164 * several attempts without lease attached:
1165 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1166 * irrespective of file name provided
1171 io
.in
.durable_handle
= h
;
1172 status
= smb2_create(tree
, mem_ctx
, &io
);
1173 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1176 io
.in
.fname
= "__non_existing_fname__";
1177 io
.in
.durable_handle
= h
;
1178 status
= smb2_create(tree
, mem_ctx
, &io
);
1179 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1182 io
.in
.fname
= fname
;
1183 io
.in
.durable_handle
= h
;
1184 status
= smb2_create(tree
, mem_ctx
, &io
);
1185 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1188 * attempt with lease provided, but
1189 * with a changed lease key. => fails
1192 io
.in
.fname
= fname
;
1193 io
.in
.durable_open
= false;
1194 io
.in
.durable_handle
= h
;
1195 io
.in
.lease_request_v2
= &ls
;
1196 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1197 /* a wrong lease key lets the request fail */
1198 ls
.lease_key
.data
[0]++;
1200 status
= smb2_create(tree
, mem_ctx
, &io
);
1201 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1203 /* restore the correct lease key */
1204 ls
.lease_key
.data
[0]--;
1207 * this last failing attempt is almost correct:
1208 * only problem is: we use the wrong filename...
1209 * Note that this gives INVALID_PARAMETER.
1210 * This is different from oplocks!
1213 io
.in
.fname
= "__non_existing_fname__";
1214 io
.in
.durable_open
= false;
1215 io
.in
.durable_handle
= h
;
1216 io
.in
.lease_request_v2
= &ls
;
1217 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1219 status
= smb2_create(tree
, mem_ctx
, &io
);
1220 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1223 * Now for a succeeding reconnect:
1227 io
.in
.fname
= fname
;
1228 io
.in
.durable_open
= false;
1229 io
.in
.durable_handle
= h
;
1230 io
.in
.lease_request_v2
= &ls
;
1231 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1233 /* the requested lease state is irrelevant */
1234 ls
.lease_state
= smb2_util_lease_state("");
1238 status
= smb2_create(tree
, mem_ctx
, &io
);
1239 CHECK_STATUS(status
, NT_STATUS_OK
);
1241 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1242 CHECK_VAL(io
.out
.durable_open
, false);
1243 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1244 CHECK_VAL(io
.out
.persistent_open
, false);
1245 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1246 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1247 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1248 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1249 smb2_util_lease_state("RWH"));
1250 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1251 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1252 _h
= io
.out
.file
.handle
;
1255 /* disconnect one more time */
1258 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1259 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1265 * demonstrate that various parameters are ignored
1271 * These are completely ignored by the server
1273 io
.in
.security_flags
= 0x78;
1274 io
.in
.oplock_level
= 0x78;
1275 io
.in
.impersonation_level
= 0x12345678;
1276 io
.in
.create_flags
= 0x12345678;
1277 io
.in
.reserved
= 0x12345678;
1278 io
.in
.desired_access
= 0x12345678;
1279 io
.in
.file_attributes
= 0x12345678;
1280 io
.in
.share_access
= 0x12345678;
1281 io
.in
.create_disposition
= 0x12345678;
1282 io
.in
.create_options
= 0x12345678;
1285 * only these are checked:
1287 * - io.in.durable_handle,
1288 * - io.in.lease_request->lease_key
1291 io
.in
.fname
= fname
;
1292 io
.in
.durable_open_v2
= false;
1293 io
.in
.durable_handle_v2
= h
;
1294 io
.in
.lease_request_v2
= &ls
;
1296 /* the requested lease state is irrelevant */
1297 ls
.lease_state
= smb2_util_lease_state("");
1301 status
= smb2_create(tree
, mem_ctx
, &io
);
1302 CHECK_STATUS(status
, NT_STATUS_OK
);
1304 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1305 CHECK_VAL(io
.out
.durable_open
, false);
1306 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1307 CHECK_VAL(io
.out
.persistent_open
, false);
1308 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1309 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1310 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1311 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1312 smb2_util_lease_state("RWH"));
1313 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1314 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1316 _h
= io
.out
.file
.handle
;
1322 smb2_util_close(tree
, *h
);
1325 smb2_util_unlink(tree
, fname
);
1330 talloc_free(mem_ctx
);
1336 * basic test for doing a durable open
1337 * tcp disconnect, reconnect with a session reconnect and
1338 * do a durable reopen (succeeds)
1340 static bool test_durable_open_reopen2a(struct torture_context
*tctx
,
1341 struct smb2_tree
*tree
)
1344 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1346 struct smb2_handle _h
;
1347 struct smb2_handle
*h
= NULL
;
1348 struct smb2_create io1
, io2
;
1349 uint64_t previous_session_id
;
1351 struct smbcli_options options
;
1353 options
= tree
->session
->transport
->options
;
1355 /* Choose a random name in case the state is left a little funky. */
1356 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
1357 generate_random_str(tctx
, 8));
1359 smb2_util_unlink(tree
, fname
);
1361 smb2_oplock_create_share(&io1
, fname
,
1362 smb2_util_share_access(""),
1363 smb2_util_oplock_level("b"));
1364 io1
.in
.durable_open
= true;
1366 status
= smb2_create(tree
, mem_ctx
, &io1
);
1367 CHECK_STATUS(status
, NT_STATUS_OK
);
1368 _h
= io1
.out
.file
.handle
;
1370 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1371 CHECK_VAL(io1
.out
.durable_open
, true);
1372 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1374 /* disconnect, reconnect and then do durable reopen */
1375 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1379 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1382 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1388 io2
.in
.fname
= fname
;
1389 io2
.in
.durable_handle
= h
;
1392 status
= smb2_create(tree
, mem_ctx
, &io2
);
1393 CHECK_STATUS(status
, NT_STATUS_OK
);
1394 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1395 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1396 _h
= io2
.out
.file
.handle
;
1402 smb2_util_close(tree
, *h
);
1405 smb2_util_unlink(tree
, fname
);
1410 talloc_free(mem_ctx
);
1417 * basic test for doing a durable open:
1418 * tdis, new tcon, try durable reopen (fails)
1420 static bool test_durable_open_reopen3(struct torture_context
*tctx
,
1421 struct smb2_tree
*tree
)
1424 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1426 struct smb2_handle _h
;
1427 struct smb2_handle
*h
= NULL
;
1428 struct smb2_create io1
, io2
;
1430 struct smb2_tree
*tree2
= NULL
;
1432 /* Choose a random name in case the state is left a little funky. */
1433 snprintf(fname
, 256, "durable_open_reopen3_%s.dat",
1434 generate_random_str(tctx
, 8));
1436 smb2_util_unlink(tree
, fname
);
1438 smb2_oplock_create_share(&io1
, fname
,
1439 smb2_util_share_access(""),
1440 smb2_util_oplock_level("b"));
1441 io1
.in
.durable_open
= true;
1443 status
= smb2_create(tree
, mem_ctx
, &io1
);
1444 CHECK_STATUS(status
, NT_STATUS_OK
);
1445 _h
= io1
.out
.file
.handle
;
1447 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1448 CHECK_VAL(io1
.out
.durable_open
, true);
1449 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1451 /* disconnect, reconnect and then do durable reopen */
1452 status
= smb2_tdis(tree
);
1453 CHECK_STATUS(status
, NT_STATUS_OK
);
1455 if (!torture_smb2_tree_connect(tctx
, tree
->session
, mem_ctx
, &tree2
)) {
1456 torture_warning(tctx
, "couldn't reconnect to share, bailing\n");
1463 io2
.in
.fname
= fname
;
1464 io2
.in
.durable_handle
= h
;
1466 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1467 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1472 smb2_util_close(tree
, *h
);
1475 smb2_util_unlink(tree2
, fname
);
1480 talloc_free(mem_ctx
);
1486 * basic test for doing a durable open:
1487 * logoff, create a new session, do a durable reopen (succeeds)
1489 static bool test_durable_open_reopen4(struct torture_context
*tctx
,
1490 struct smb2_tree
*tree
)
1493 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1495 struct smb2_handle _h
;
1496 struct smb2_handle
*h
= NULL
;
1497 struct smb2_create io1
, io2
;
1499 struct smb2_transport
*transport
= NULL
;
1500 struct smb2_session
*session2
= NULL
;
1501 struct smb2_tree
*tree2
= NULL
;
1503 /* Choose a random name in case the state is left a little funky. */
1504 snprintf(fname
, 256, "durable_open_reopen4_%s.dat",
1505 generate_random_str(tctx
, 8));
1507 smb2_util_unlink(tree
, fname
);
1509 smb2_oplock_create_share(&io1
, fname
,
1510 smb2_util_share_access(""),
1511 smb2_util_oplock_level("b"));
1512 io1
.in
.durable_open
= true;
1514 status
= smb2_create(tree
, mem_ctx
, &io1
);
1515 CHECK_STATUS(status
, NT_STATUS_OK
);
1516 _h
= io1
.out
.file
.handle
;
1518 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1519 CHECK_VAL(io1
.out
.durable_open
, true);
1520 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1523 * do a session logoff, establish a new session and tree
1524 * connect on the same transport, and try a durable reopen
1526 transport
= tree
->session
->transport
;
1527 status
= smb2_logoff(tree
->session
);
1528 CHECK_STATUS(status
, NT_STATUS_OK
);
1530 if (!torture_smb2_session_setup(tctx
, transport
,
1531 0, /* previous_session_id */
1532 mem_ctx
, &session2
))
1534 torture_warning(tctx
, "session setup failed.\n");
1540 * the session setup has talloc-stolen the transport,
1541 * so we can safely free the old tree+session for clarity
1545 if (!torture_smb2_tree_connect(tctx
, session2
, mem_ctx
, &tree2
)) {
1546 torture_warning(tctx
, "tree connect failed.\n");
1552 io2
.in
.fname
= fname
;
1553 io2
.in
.durable_handle
= h
;
1556 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1557 CHECK_STATUS(status
, NT_STATUS_OK
);
1559 _h
= io2
.out
.file
.handle
;
1561 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1562 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1567 smb2_util_close(tree2
, *h
);
1570 smb2_util_unlink(tree2
, fname
);
1575 talloc_free(mem_ctx
);
1580 static bool test_durable_open_delete_on_close1(struct torture_context
*tctx
,
1581 struct smb2_tree
*tree
)
1584 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1586 struct smb2_handle _h
;
1587 struct smb2_handle
*h
= NULL
;
1588 struct smb2_create io1
, io2
;
1592 /* Choose a random name in case the state is left a little funky. */
1593 snprintf(fname
, 256, "durable_open_delete_on_close1_%s.dat",
1594 generate_random_str(tctx
, 8));
1596 smb2_util_unlink(tree
, fname
);
1598 smb2_oplock_create_share(&io1
, fname
,
1599 smb2_util_share_access(""),
1600 smb2_util_oplock_level("b"));
1601 io1
.in
.durable_open
= true;
1602 io1
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1604 status
= smb2_create(tree
, mem_ctx
, &io1
);
1605 CHECK_STATUS(status
, NT_STATUS_OK
);
1606 _h
= io1
.out
.file
.handle
;
1608 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1609 CHECK_VAL(io1
.out
.durable_open
, true);
1610 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1612 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1613 CHECK_STATUS(status
, NT_STATUS_OK
);
1615 /* disconnect, leaving the durable handle in place */
1618 if (!torture_smb2_connection(tctx
, &tree
)) {
1619 torture_warning(tctx
, "could not reconnect, bailing\n");
1625 * Open the file on the new connection again
1626 * and check that it has been newly created,
1627 * i.e. delete on close was effective on the disconnected handle.
1628 * Also check that the file is really empty,
1629 * the previously written byte gone.
1631 smb2_oplock_create_share(&io2
, fname
,
1632 smb2_util_share_access(""),
1633 smb2_util_oplock_level("b"));
1634 io2
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1636 status
= smb2_create(tree
, mem_ctx
, &io2
);
1637 CHECK_STATUS(status
, NT_STATUS_OK
);
1638 _h
= io2
.out
.file
.handle
;
1640 CHECK_CREATED_SIZE(&io2
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1641 CHECK_VAL(io2
.out
.durable_open
, false);
1642 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1647 smb2_util_close(tree
, *h
);
1650 smb2_util_unlink(tree
, fname
);
1655 talloc_free(mem_ctx
);
1661 static bool test_durable_open_delete_on_close2(struct torture_context
*tctx
,
1662 struct smb2_tree
*tree
)
1665 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1667 struct smb2_handle _h
;
1668 struct smb2_handle
*h
= NULL
;
1669 struct smb2_create io
;
1672 uint64_t previous_session_id
;
1673 uint64_t alloc_size_step
;
1674 struct smbcli_options options
;
1676 options
= tree
->session
->transport
->options
;
1678 /* Choose a random name in case the state is left a little funky. */
1679 snprintf(fname
, 256, "durable_open_delete_on_close2_%s.dat",
1680 generate_random_str(tctx
, 8));
1682 smb2_util_unlink(tree
, fname
);
1684 smb2_oplock_create_share(&io
, fname
,
1685 smb2_util_share_access(""),
1686 smb2_util_oplock_level("b"));
1687 io
.in
.durable_open
= true;
1688 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1690 status
= smb2_create(tree
, mem_ctx
, &io
);
1691 CHECK_STATUS(status
, NT_STATUS_OK
);
1692 _h
= io
.out
.file
.handle
;
1694 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1695 CHECK_VAL(io
.out
.durable_open
, true);
1696 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1698 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1699 CHECK_STATUS(status
, NT_STATUS_OK
);
1701 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1703 /* disconnect, leaving the durable handle in place */
1706 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1709 torture_warning(tctx
, "could not reconnect, bailing\n");
1715 io
.in
.fname
= fname
;
1716 io
.in
.durable_handle
= h
;
1718 status
= smb2_create(tree
, mem_ctx
, &io
);
1719 CHECK_STATUS(status
, NT_STATUS_OK
);
1720 _h
= io
.out
.file
.handle
;
1722 alloc_size_step
= io
.out
.alloc_size
;
1723 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
, alloc_size_step
, 1);
1724 CHECK_VAL(io
.out
.durable_open
, false);
1725 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1727 /* close the file, thereby deleting it */
1728 smb2_util_close(tree
, *h
);
1729 status
= smb2_logoff(tree
->session
);
1732 if (!torture_smb2_connection(tctx
, &tree
)) {
1733 torture_warning(tctx
, "could not reconnect, bailing\n");
1739 * Open the file on the new connection again
1740 * and check that it has been newly created,
1741 * i.e. delete on close was effective on the reconnected handle.
1742 * Also check that the file is really empty,
1743 * the previously written byte gone.
1745 smb2_oplock_create_share(&io
, fname
,
1746 smb2_util_share_access(""),
1747 smb2_util_oplock_level("b"));
1748 io
.in
.durable_open
= true;
1749 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1751 status
= smb2_create(tree
, mem_ctx
, &io
);
1752 CHECK_STATUS(status
, NT_STATUS_OK
);
1753 _h
= io
.out
.file
.handle
;
1755 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1756 CHECK_VAL(io
.out
.durable_open
, true);
1757 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1762 smb2_util_close(tree
, *h
);
1765 smb2_util_unlink(tree
, fname
);
1770 talloc_free(mem_ctx
);
1776 basic testing of SMB2 durable opens
1777 regarding the position information on the handle
1779 static bool test_durable_open_file_position(struct torture_context
*tctx
,
1780 struct smb2_tree
*tree
)
1782 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1783 struct smb2_handle h
;
1784 struct smb2_create io
;
1786 const char *fname
= "durable_open_position.dat";
1787 union smb_fileinfo qfinfo
;
1788 union smb_setfileinfo sfinfo
;
1791 uint64_t previous_session_id
;
1792 struct smbcli_options options
;
1794 options
= tree
->session
->transport
->options
;
1796 smb2_util_unlink(tree
, fname
);
1798 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1799 io
.in
.durable_open
= true;
1801 status
= smb2_create(tree
, mem_ctx
, &io
);
1802 CHECK_STATUS(status
, NT_STATUS_OK
);
1803 h
= io
.out
.file
.handle
;
1804 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1805 CHECK_VAL(io
.out
.durable_open
, true);
1806 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1808 /* TODO: check extra blob content */
1810 ZERO_STRUCT(qfinfo
);
1811 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1812 qfinfo
.generic
.in
.file
.handle
= h
;
1813 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1814 CHECK_STATUS(status
, NT_STATUS_OK
);
1815 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0);
1816 pos
= qfinfo
.position_information
.out
.position
;
1817 torture_comment(tctx
, "position: %llu\n",
1818 (unsigned long long)pos
);
1820 ZERO_STRUCT(sfinfo
);
1821 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
1822 sfinfo
.generic
.in
.file
.handle
= h
;
1823 sfinfo
.position_information
.in
.position
= 0x1000;
1824 status
= smb2_setinfo_file(tree
, &sfinfo
);
1825 CHECK_STATUS(status
, NT_STATUS_OK
);
1827 ZERO_STRUCT(qfinfo
);
1828 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1829 qfinfo
.generic
.in
.file
.handle
= h
;
1830 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1831 CHECK_STATUS(status
, NT_STATUS_OK
);
1832 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1833 pos
= qfinfo
.position_information
.out
.position
;
1834 torture_comment(tctx
, "position: %llu\n",
1835 (unsigned long long)pos
);
1837 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1839 /* tcp disconnect */
1843 /* do a session reconnect */
1844 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1847 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1852 ZERO_STRUCT(qfinfo
);
1853 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1854 qfinfo
.generic
.in
.file
.handle
= h
;
1855 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1856 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1859 io
.in
.fname
= fname
;
1860 io
.in
.durable_handle
= &h
;
1862 status
= smb2_create(tree
, mem_ctx
, &io
);
1863 CHECK_STATUS(status
, NT_STATUS_OK
);
1864 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1865 CHECK_VAL(io
.out
.reserved
, 0x00);
1866 CHECK_VAL(io
.out
.create_action
, NTCREATEX_ACTION_EXISTED
);
1867 CHECK_VAL(io
.out
.size
, 0);
1868 CHECK_VAL(io
.out
.file_attr
, FILE_ATTRIBUTE_ARCHIVE
);
1869 CHECK_VAL(io
.out
.reserved2
, 0);
1871 h
= io
.out
.file
.handle
;
1873 ZERO_STRUCT(qfinfo
);
1874 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1875 qfinfo
.generic
.in
.file
.handle
= h
;
1876 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1877 CHECK_STATUS(status
, NT_STATUS_OK
);
1878 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1879 pos
= qfinfo
.position_information
.out
.position
;
1880 torture_comment(tctx
, "position: %llu\n",
1881 (unsigned long long)pos
);
1883 smb2_util_close(tree
, h
);
1885 talloc_free(mem_ctx
);
1887 smb2_util_unlink(tree
, fname
);
1896 Open, disconnect, oplock break, reconnect.
1898 static bool test_durable_open_oplock(struct torture_context
*tctx
,
1899 struct smb2_tree
*tree1
,
1900 struct smb2_tree
*tree2
)
1902 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1903 struct smb2_create io1
, io2
;
1904 struct smb2_handle h1
= {{0}};
1905 struct smb2_handle h2
= {{0}};
1910 /* Choose a random name in case the state is left a little funky. */
1911 snprintf(fname
, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1914 smb2_util_unlink(tree1
, fname
);
1916 /* Create with batch oplock */
1917 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1918 io1
.in
.durable_open
= true;
1921 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
1923 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1924 CHECK_STATUS(status
, NT_STATUS_OK
);
1925 h1
= io1
.out
.file
.handle
;
1926 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1927 CHECK_VAL(io1
.out
.durable_open
, true);
1928 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1930 /* Disconnect after getting the batch */
1935 * Windows7 (build 7000) will break a batch oplock immediately if the
1936 * original client is gone. (ZML: This seems like a bug. It should give
1937 * some time for the client to reconnect!)
1939 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1940 CHECK_STATUS(status
, NT_STATUS_OK
);
1941 h2
= io2
.out
.file
.handle
;
1942 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1943 CHECK_VAL(io2
.out
.durable_open
, true);
1944 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1946 /* What if tree1 tries to come back and reclaim? */
1947 if (!torture_smb2_connection(tctx
, &tree1
)) {
1948 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1954 io1
.in
.fname
= fname
;
1955 io1
.in
.durable_handle
= &h1
;
1957 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1958 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1961 smb2_util_close(tree2
, h2
);
1962 smb2_util_unlink(tree2
, fname
);
1971 Open, disconnect, lease break, reconnect.
1973 static bool test_durable_open_lease(struct torture_context
*tctx
,
1974 struct smb2_tree
*tree1
,
1975 struct smb2_tree
*tree2
)
1977 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1978 struct smb2_create io1
, io2
;
1979 struct smb2_lease ls1
, ls2
;
1980 struct smb2_handle h1
= {{0}};
1981 struct smb2_handle h2
= {{0}};
1985 uint64_t lease1
, lease2
;
1988 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
1989 if (!(caps
& SMB2_CAP_LEASING
)) {
1990 torture_skip(tctx
, "leases are not supported");
1994 * Choose a random name and random lease in case the state is left a
1999 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
2002 smb2_util_unlink(tree1
, fname
);
2004 /* Create with lease */
2005 smb2_lease_create(&io1
, &ls1
, false /* dir */, fname
,
2006 lease1
, smb2_util_lease_state("RHW"));
2007 io1
.in
.durable_open
= true;
2009 smb2_lease_create(&io2
, &ls2
, false /* dir */, fname
,
2010 lease2
, smb2_util_lease_state("RHW"));
2011 io2
.in
.durable_open
= true;
2012 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
2014 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2015 CHECK_STATUS(status
, NT_STATUS_OK
);
2016 h1
= io1
.out
.file
.handle
;
2017 CHECK_VAL(io1
.out
.durable_open
, true);
2018 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2020 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2021 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease1
);
2022 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease1
);
2023 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
2024 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
2026 /* Disconnect after getting the lease */
2031 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
2032 * even if the original client is gone. (ZML: This seems like a bug. It
2033 * should give some time for the client to reconnect! And why RH?)
2035 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
2036 * Test is adapted accordingly.
2038 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2039 CHECK_STATUS(status
, NT_STATUS_OK
);
2040 h2
= io2
.out
.file
.handle
;
2041 CHECK_VAL(io2
.out
.durable_open
, true);
2042 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
2044 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2045 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[0], lease2
);
2046 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[1], ~lease2
);
2047 CHECK_VAL(io2
.out
.lease_response
.lease_state
,
2048 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
2050 /* What if tree1 tries to come back and reclaim? */
2051 if (!torture_smb2_connection(tctx
, &tree1
)) {
2052 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2058 io1
.in
.fname
= fname
;
2059 io1
.in
.durable_handle
= &h1
;
2060 io1
.in
.lease_request
= &ls1
;
2062 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2063 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2066 smb2_util_close(tree2
, h2
);
2067 smb2_util_unlink(tree2
, fname
);
2075 static bool test_durable_open_lock_oplock(struct torture_context
*tctx
,
2076 struct smb2_tree
*tree
)
2078 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2079 struct smb2_create io
;
2080 struct smb2_handle h
= {{0}};
2081 struct smb2_lock lck
;
2082 struct smb2_lock_element el
[2];
2089 snprintf(fname
, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx
, 8));
2092 smb2_util_unlink(tree
, fname
);
2094 /* Create with oplock */
2096 smb2_oplock_create_share(&io
, fname
,
2097 smb2_util_share_access(""),
2098 smb2_util_oplock_level("b"));
2099 io
.in
.durable_open
= true;
2101 status
= smb2_create(tree
, mem_ctx
, &io
);
2102 CHECK_STATUS(status
, NT_STATUS_OK
);
2103 h
= io
.out
.file
.handle
;
2104 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2106 CHECK_VAL(io
.out
.durable_open
, true);
2107 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2112 lck
.in
.lock_count
= 0x0001;
2113 lck
.in
.lock_sequence
= 0x00000000;
2114 lck
.in
.file
.handle
= h
;
2117 el
[0].reserved
= 0x00000000;
2118 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
2119 status
= smb2_lock(tree
, &lck
);
2120 CHECK_STATUS(status
, NT_STATUS_OK
);
2122 /* Disconnect/Reconnect. */
2126 if (!torture_smb2_connection(tctx
, &tree
)) {
2127 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2133 io
.in
.fname
= fname
;
2134 io
.in
.durable_handle
= &h
;
2136 status
= smb2_create(tree
, mem_ctx
, &io
);
2137 CHECK_STATUS(status
, NT_STATUS_OK
);
2138 h
= io
.out
.file
.handle
;
2140 lck
.in
.file
.handle
= h
;
2141 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
2142 status
= smb2_lock(tree
, &lck
);
2143 CHECK_STATUS(status
, NT_STATUS_OK
);
2146 smb2_util_close(tree
, h
);
2147 smb2_util_unlink(tree
, fname
);
2154 Open(RWH), take BRL, disconnect, reconnect.
2156 static bool test_durable_open_lock_lease(struct torture_context
*tctx
,
2157 struct smb2_tree
*tree
)
2159 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2160 struct smb2_create io
;
2161 struct smb2_lease ls
;
2162 struct smb2_handle h
= {{0}};
2163 struct smb2_lock lck
;
2164 struct smb2_lock_element el
[2];
2170 struct smbcli_options options
;
2172 options
= tree
->session
->transport
->options
;
2174 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
2175 if (!(caps
& SMB2_CAP_LEASING
)) {
2176 torture_skip(tctx
, "leases are not supported");
2180 * Choose a random name and random lease in case the state is left a
2184 snprintf(fname
, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx
, 8));
2187 smb2_util_unlink(tree
, fname
);
2189 /* Create with lease */
2191 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease
,
2192 smb2_util_lease_state("RWH"));
2193 io
.in
.durable_open
= true;
2195 status
= smb2_create(tree
, mem_ctx
, &io
);
2196 CHECK_STATUS(status
, NT_STATUS_OK
);
2197 h
= io
.out
.file
.handle
;
2198 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2200 CHECK_VAL(io
.out
.durable_open
, true);
2201 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2202 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
2203 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
2204 CHECK_VAL(io
.out
.lease_response
.lease_state
,
2205 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
2210 lck
.in
.lock_count
= 0x0001;
2211 lck
.in
.lock_sequence
= 0x00000000;
2212 lck
.in
.file
.handle
= h
;
2215 el
[0].reserved
= 0x00000000;
2216 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
2217 status
= smb2_lock(tree
, &lck
);
2218 CHECK_STATUS(status
, NT_STATUS_OK
);
2220 /* Disconnect/Reconnect. */
2224 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
2225 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2231 io
.in
.fname
= fname
;
2232 io
.in
.durable_handle
= &h
;
2233 io
.in
.lease_request
= &ls
;
2235 status
= smb2_create(tree
, mem_ctx
, &io
);
2236 CHECK_STATUS(status
, NT_STATUS_OK
);
2237 h
= io
.out
.file
.handle
;
2239 lck
.in
.file
.handle
= h
;
2240 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
2241 status
= smb2_lock(tree
, &lck
);
2242 CHECK_STATUS(status
, NT_STATUS_OK
);
2245 smb2_util_close(tree
, h
);
2246 smb2_util_unlink(tree
, fname
);
2253 Open(RH), take BRL, disconnect, fails reconnect without W LEASE
2255 static bool test_durable_open_lock_noW_lease(struct torture_context
*tctx
,
2256 struct smb2_tree
*tree
)
2258 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2259 struct smb2_create io
;
2260 struct smb2_lease ls
;
2261 struct smb2_handle h
= {{0}};
2262 struct smb2_lock lck
;
2263 struct smb2_lock_element el
[2];
2269 struct smbcli_options options
;
2271 options
= tree
->session
->transport
->options
;
2273 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
2274 if (!(caps
& SMB2_CAP_LEASING
)) {
2275 torture_skip(tctx
, "leases are not supported");
2279 * Choose a random name and random lease in case the state is left a
2283 snprintf(fname
, 256, "durable_open_lease_noW_lock_%s.dat", generate_random_str(tctx
, 8));
2286 smb2_util_unlink(tree
, fname
);
2288 /* Create with lease */
2290 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease
,
2291 smb2_util_lease_state("RH"));
2292 io
.in
.durable_open
= true;
2294 status
= smb2_create(tree
, mem_ctx
, &io
);
2295 CHECK_STATUS(status
, NT_STATUS_OK
);
2296 h
= io
.out
.file
.handle
;
2297 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2299 CHECK_VAL(io
.out
.durable_open
, true);
2300 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2301 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
2302 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
2303 CHECK_VAL(io
.out
.lease_response
.lease_state
,
2304 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
);
2309 lck
.in
.lock_count
= 0x0001;
2310 lck
.in
.lock_sequence
= 0x00000000;
2311 lck
.in
.file
.handle
= h
;
2314 el
[0].reserved
= 0x00000000;
2315 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
2316 status
= smb2_lock(tree
, &lck
);
2317 CHECK_STATUS(status
, NT_STATUS_OK
);
2319 /* Disconnect/Reconnect. */
2323 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
2324 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2330 io
.in
.fname
= fname
;
2331 io
.in
.durable_handle
= &h
;
2332 io
.in
.lease_request
= &ls
;
2334 status
= smb2_create(tree
, mem_ctx
, &io
);
2335 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2336 h
= io
.out
.file
.handle
;
2339 smb2_util_close(tree
, h
);
2340 smb2_util_unlink(tree
, fname
);
2347 * Open with a RH lease, disconnect, open in another tree, reconnect.
2349 * This test actually demonstrates a minimum level of respect for the durable
2350 * open in the face of another open. As long as this test shows an inability to
2351 * reconnect after an open, the oplock/lease tests above will certainly
2352 * demonstrate an error on reconnect.
2354 static bool test_durable_open_open2_lease(struct torture_context
*tctx
,
2355 struct smb2_tree
*tree1
,
2356 struct smb2_tree
*tree2
)
2358 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2359 struct smb2_create io1
, io2
;
2360 struct smb2_lease ls
;
2361 struct smb2_handle h1
= {{0}};
2362 struct smb2_handle h2
= {{0}};
2368 struct smbcli_options options
;
2370 options
= tree1
->session
->transport
->options
;
2372 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
2373 if (!(caps
& SMB2_CAP_LEASING
)) {
2374 torture_skip(tctx
, "leases are not supported");
2378 * Choose a random name and random lease in case the state is left a
2382 snprintf(fname
, 256, "durable_open_open2_lease_%s.dat",
2383 generate_random_str(tctx
, 8));
2386 smb2_util_unlink(tree1
, fname
);
2388 /* Create with lease */
2389 smb2_lease_create_share(&io1
, &ls
, false /* dir */, fname
,
2390 smb2_util_share_access(""),
2392 smb2_util_lease_state("RH"));
2393 io1
.in
.durable_open
= true;
2395 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2396 CHECK_STATUS(status
, NT_STATUS_OK
);
2397 h1
= io1
.out
.file
.handle
;
2398 CHECK_VAL(io1
.out
.durable_open
, true);
2399 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2401 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2402 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease
);
2403 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease
);
2404 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
2405 smb2_util_lease_state("RH"));
2411 /* Open the file in tree2 */
2412 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2414 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2415 CHECK_STATUS(status
, NT_STATUS_OK
);
2416 h2
= io2
.out
.file
.handle
;
2417 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
2420 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree1
)) {
2421 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2427 io1
.in
.fname
= fname
;
2428 io1
.in
.durable_handle
= &h1
;
2429 io1
.in
.lease_request
= &ls
;
2432 * Windows7 (build 7000) will give away an open immediately if the
2433 * original client is gone. (ZML: This seems like a bug. It should give
2434 * some time for the client to reconnect!)
2436 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2437 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2438 h1
= io1
.out
.file
.handle
;
2442 smb2_util_close(tree1
, h1
);
2443 smb2_util_unlink(tree1
, fname
);
2447 smb2_util_close(tree2
, h2
);
2448 smb2_util_unlink(tree2
, fname
);
2455 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2457 * This test actually demonstrates a minimum level of respect for the durable
2458 * open in the face of another open. As long as this test shows an inability to
2459 * reconnect after an open, the oplock/lease tests above will certainly
2460 * demonstrate an error on reconnect.
2462 static bool test_durable_open_open2_oplock(struct torture_context
*tctx
,
2463 struct smb2_tree
*tree1
,
2464 struct smb2_tree
*tree2
)
2466 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2467 struct smb2_create io1
, io2
;
2468 struct smb2_handle h1
= {{0}};
2469 struct smb2_handle h2
= {{0}};
2475 * Choose a random name and random lease in case the state is left a
2478 snprintf(fname
, 256, "durable_open_open2_oplock_%s.dat",
2479 generate_random_str(tctx
, 8));
2482 smb2_util_unlink(tree1
, fname
);
2484 /* Create with batch oplock */
2485 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2486 io1
.in
.durable_open
= true;
2488 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2489 CHECK_STATUS(status
, NT_STATUS_OK
);
2490 h1
= io1
.out
.file
.handle
;
2491 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2492 CHECK_VAL(io1
.out
.durable_open
, true);
2493 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2499 /* Open the file in tree2 */
2500 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2502 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2503 CHECK_STATUS(status
, NT_STATUS_OK
);
2504 h2
= io2
.out
.file
.handle
;
2505 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2508 if (!torture_smb2_connection(tctx
, &tree1
)) {
2509 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2515 io1
.in
.fname
= fname
;
2516 io1
.in
.durable_handle
= &h1
;
2518 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2519 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2520 h1
= io1
.out
.file
.handle
;
2523 smb2_util_close(tree2
, h2
);
2524 smb2_util_unlink(tree2
, fname
);
2525 if (tree1
!= NULL
) {
2526 smb2_util_close(tree1
, h1
);
2527 smb2_util_unlink(tree1
, fname
);
2537 * test behaviour with initial allocation size
2539 static bool test_durable_open_alloc_size(struct torture_context
*tctx
,
2540 struct smb2_tree
*tree
)
2543 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2545 struct smb2_handle _h
;
2546 struct smb2_handle
*h
= NULL
;
2547 struct smb2_create io
;
2549 uint64_t previous_session_id
;
2550 uint64_t alloc_size_step
;
2551 uint64_t initial_alloc_size
= 0x1000;
2552 const uint8_t *b
= NULL
;
2553 struct smbcli_options options
;
2555 options
= tree
->session
->transport
->options
;
2557 /* Choose a random name in case the state is left a little funky. */
2558 snprintf(fname
, 256, "durable_open_alloc_size_%s.dat",
2559 generate_random_str(tctx
, 8));
2561 smb2_util_unlink(tree
, fname
);
2563 smb2_oplock_create_share(&io
, fname
,
2564 smb2_util_share_access(""),
2565 smb2_util_oplock_level("b"));
2566 io
.in
.durable_open
= true;
2567 io
.in
.alloc_size
= initial_alloc_size
;
2569 status
= smb2_create(tree
, mem_ctx
, &io
);
2570 CHECK_STATUS(status
, NT_STATUS_OK
);
2571 _h
= io
.out
.file
.handle
;
2573 CHECK_NOT_VAL(io
.out
.alloc_size
, 0);
2574 alloc_size_step
= io
.out
.alloc_size
;
2575 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
,
2576 alloc_size_step
, 0);
2577 CHECK_VAL(io
.out
.durable_open
, true);
2578 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2580 /* prepare buffer */
2581 b
= talloc_zero_size(mem_ctx
, alloc_size_step
);
2584 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2586 /* disconnect, reconnect and then do durable reopen */
2590 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2593 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2599 io
.in
.fname
= fname
;
2600 io
.in
.durable_handle
= h
;
2603 status
= smb2_create(tree
, mem_ctx
, &io
);
2604 CHECK_STATUS(status
, NT_STATUS_OK
);
2605 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2606 alloc_size_step
, 0);
2607 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2608 _h
= io
.out
.file
.handle
;
2611 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2613 /* write one byte */
2614 status
= smb2_util_write(tree
, *h
, b
, 0, 1);
2615 CHECK_STATUS(status
, NT_STATUS_OK
);
2617 /* disconnect, reconnect and then do durable reopen */
2621 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2624 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2630 io
.in
.fname
= fname
;
2631 io
.in
.durable_handle
= h
;
2634 status
= smb2_create(tree
, mem_ctx
, &io
);
2635 CHECK_STATUS(status
, NT_STATUS_OK
);
2636 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2637 alloc_size_step
, 1);
2638 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2639 _h
= io
.out
.file
.handle
;
2642 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2644 /* write more byte than initial allocation size */
2645 status
= smb2_util_write(tree
, *h
, b
, 1, alloc_size_step
);
2647 /* disconnect, reconnect and then do durable reopen */
2651 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2654 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2660 io
.in
.fname
= fname
;
2661 io
.in
.durable_handle
= h
;
2664 status
= smb2_create(tree
, mem_ctx
, &io
);
2665 CHECK_STATUS(status
, NT_STATUS_OK
);
2666 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2667 alloc_size_step
* 2, alloc_size_step
+ 1);
2668 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2669 _h
= io
.out
.file
.handle
;
2674 smb2_util_close(tree
, *h
);
2677 smb2_util_unlink(tree
, fname
);
2681 talloc_free(mem_ctx
);
2687 * test behaviour when a disconnect happens while creating a read-only file
2689 static bool test_durable_open_read_only(struct torture_context
*tctx
,
2690 struct smb2_tree
*tree
)
2693 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2695 struct smb2_handle _h
;
2696 struct smb2_handle
*h
= NULL
;
2697 struct smb2_create io
;
2699 uint64_t previous_session_id
;
2700 const uint8_t b
= 0;
2701 uint64_t alloc_size
= 0;
2702 struct smbcli_options options
;
2704 options
= tree
->session
->transport
->options
;
2706 /* Choose a random name in case the state is left a little funky. */
2707 snprintf(fname
, 256, "durable_open_initial_alloc_%s.dat",
2708 generate_random_str(tctx
, 8));
2710 smb2_util_unlink(tree
, fname
);
2712 smb2_oplock_create_share(&io
, fname
,
2713 smb2_util_share_access(""),
2714 smb2_util_oplock_level("b"));
2715 io
.in
.durable_open
= true;
2716 io
.in
.file_attributes
= FILE_ATTRIBUTE_READONLY
;
2718 status
= smb2_create(tree
, mem_ctx
, &io
);
2719 CHECK_STATUS(status
, NT_STATUS_OK
);
2720 _h
= io
.out
.file
.handle
;
2722 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
);
2723 CHECK_VAL(io
.out
.durable_open
, true);
2724 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2726 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2728 /* write one byte */
2729 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
2730 CHECK_STATUS(status
, NT_STATUS_OK
);
2732 /* disconnect, reconnect and then do durable reopen */
2736 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2739 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2745 io
.in
.fname
= fname
;
2746 io
.in
.durable_handle
= h
;
2749 status
= smb2_create(tree
, mem_ctx
, &io
);
2750 CHECK_STATUS(status
, NT_STATUS_OK
);
2751 alloc_size
= io
.out
.alloc_size
;
2752 CHECK_CREATED_SIZE(&io
, EXISTED
,
2753 FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
,
2755 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2756 _h
= io
.out
.file
.handle
;
2759 /* write one byte */
2760 status
= smb2_util_write(tree
, *h
, &b
, 1, 1);
2761 CHECK_STATUS(status
, NT_STATUS_OK
);
2765 union smb_setfileinfo sfinfo
;
2767 ZERO_STRUCT(sfinfo
);
2768 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
2769 sfinfo
.basic_info
.in
.file
.handle
= *h
;
2770 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_NORMAL
;
2771 smb2_setinfo_file(tree
, &sfinfo
);
2773 smb2_util_close(tree
, *h
);
2776 smb2_util_unlink(tree
, fname
);
2780 talloc_free(mem_ctx
);
2786 * durable open with oplock, disconnect, exit
2788 static bool test_durable_open_oplock_disconnect(struct torture_context
*tctx
,
2789 struct smb2_tree
*tree
)
2791 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2792 struct smb2_create io
;
2793 struct smb2_handle _h
;
2794 struct smb2_handle
*h
= NULL
;
2799 snprintf(fname
, 256, "durable_open_oplock_disconnect_%s.dat",
2800 generate_random_str(mem_ctx
, 8));
2802 smb2_util_unlink(tree
, fname
);
2804 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2805 io
.in
.durable_open
= true;
2807 status
= smb2_create(tree
, mem_ctx
, &io
);
2808 CHECK_STATUS(status
, NT_STATUS_OK
);
2810 _h
= io
.out
.file
.handle
;
2813 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2814 CHECK_VAL(io
.out
.durable_open
, true);
2815 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2824 smb2_util_close(tree
, *h
);
2826 smb2_util_unlink(tree
, fname
);
2828 talloc_free(mem_ctx
);
2833 * durable stat open with lease.
2835 static bool test_durable_open_stat_open(struct torture_context
*tctx
,
2836 struct smb2_tree
*tree
)
2838 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2839 struct smb2_create io
;
2840 struct smb2_handle _h
;
2841 struct smb2_handle
*h
= NULL
;
2842 struct smb2_lease ls
;
2848 snprintf(fname
, 256, "durable_open_stat_open_%s.dat",
2849 generate_random_str(mem_ctx
, 8));
2851 /* Ensure file doesn't exist. */
2852 smb2_util_unlink(tree
, fname
);
2854 /* Create a normal file. */
2855 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2856 status
= smb2_create(tree
, mem_ctx
, &io
);
2857 CHECK_STATUS(status
, NT_STATUS_OK
);
2858 _h
= io
.out
.file
.handle
;
2860 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2862 smb2_util_close(tree
, *h
);
2865 /* Now try a leased, durable handle stat open. */
2867 /* Create with lease */
2868 smb2_lease_create(&io
,
2873 smb2_util_lease_state("RH"));
2874 io
.in
.durable_open
= true;
2875 io
.in
.desired_access
= SEC_FILE_READ_ATTRIBUTE
;
2876 io
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
2878 status
= smb2_create(tree
, mem_ctx
, &io
);
2879 CHECK_STATUS(status
, NT_STATUS_OK
);
2880 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
2881 CHECK_VAL(io
.out
.durable_open
, true);
2882 _h
= io
.out
.file
.handle
;
2887 smb2_util_close(tree
, *h
);
2889 smb2_util_unlink(tree
, fname
);
2890 talloc_free(mem_ctx
);
2894 struct torture_suite
*torture_smb2_durable_open_init(TALLOC_CTX
*ctx
)
2896 struct torture_suite
*suite
=
2897 torture_suite_create(ctx
, "durable-open");
2899 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_open_open_oplock
);
2900 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_open_open_lease
);
2901 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_open_reopen1
);
2902 torture_suite_add_1smb2_test(suite
, "reopen1a", test_durable_open_reopen1a
);
2903 torture_suite_add_1smb2_test(suite
, "reopen1a-lease", test_durable_open_reopen1a_lease
);
2904 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_open_reopen2
);
2905 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_open_reopen2_lease
);
2906 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2
);
2907 torture_suite_add_1smb2_test(suite
, "reopen2a", test_durable_open_reopen2a
);
2908 torture_suite_add_1smb2_test(suite
, "reopen3", test_durable_open_reopen3
);
2909 torture_suite_add_1smb2_test(suite
, "reopen4", test_durable_open_reopen4
);
2910 torture_suite_add_1smb2_test(suite
, "delete_on_close1",
2911 test_durable_open_delete_on_close1
);
2912 torture_suite_add_1smb2_test(suite
, "delete_on_close2",
2913 test_durable_open_delete_on_close2
);
2914 torture_suite_add_1smb2_test(suite
, "file-position",
2915 test_durable_open_file_position
);
2916 torture_suite_add_2smb2_test(suite
, "oplock", test_durable_open_oplock
);
2917 torture_suite_add_2smb2_test(suite
, "lease", test_durable_open_lease
);
2918 torture_suite_add_1smb2_test(suite
, "lock-oplock", test_durable_open_lock_oplock
);
2919 torture_suite_add_1smb2_test(suite
, "lock-lease", test_durable_open_lock_lease
);
2920 torture_suite_add_1smb2_test(suite
, "lock-noW-lease", test_durable_open_lock_noW_lease
);
2921 torture_suite_add_2smb2_test(suite
, "open2-lease",
2922 test_durable_open_open2_lease
);
2923 torture_suite_add_2smb2_test(suite
, "open2-oplock",
2924 test_durable_open_open2_oplock
);
2925 torture_suite_add_1smb2_test(suite
, "alloc-size",
2926 test_durable_open_alloc_size
);
2927 torture_suite_add_1smb2_test(suite
, "read-only",
2928 test_durable_open_read_only
);
2929 torture_suite_add_1smb2_test(suite
, "stat-open",
2930 test_durable_open_stat_open
);
2932 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-OPEN tests");
2937 struct torture_suite
*torture_smb2_durable_open_disconnect_init(TALLOC_CTX
*ctx
)
2939 struct torture_suite
*suite
=
2940 torture_suite_create(ctx
,
2941 "durable-open-disconnect");
2943 torture_suite_add_1smb2_test(suite
, "open-oplock-disconnect",
2944 test_durable_open_oplock_disconnect
);
2946 suite
->description
= talloc_strdup(suite
,
2947 "SMB2-DURABLE-OPEN-DISCONNECT tests");