2 Unix SMB/CIFS implementation.
4 test suite for SMB2 version two of durable opens
6 Copyright (C) Michael Adam 2012
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/ndr/libndr.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)v, (int)correct); \
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
45 #define CHECK_CREATED(__io, __created, __attribute) \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.size, 0); \
49 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
50 CHECK_VAL((__io)->out.reserved2, 0); \
58 static void torture_oplock_close_callback(struct smb2_request
*req
)
60 smb2_close_recv(req
, &break_info
.cl
);
63 /* A general oplock break notification handler. This should be used when a
64 * test expects to break from batch or exclusive to a lower level. */
65 static bool torture_oplock_handler(struct smb2_transport
*transport
,
66 const struct smb2_handle
*handle
,
70 struct smb2_tree
*tree
= private_data
;
71 struct smb2_request
*req
;
75 ZERO_STRUCT(break_info
.cl
);
76 break_info
.cl
.in
.file
.handle
= *handle
;
78 req
= smb2_close_send(tree
, &break_info
.cl
);
79 req
->async
.fn
= torture_oplock_close_callback
;
80 req
->async
.private_data
= NULL
;
85 * testing various create blob combinations.
87 bool test_durable_v2_open_create_blob(struct torture_context
*tctx
,
88 struct smb2_tree
*tree
)
91 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
93 struct smb2_handle _h
;
94 struct smb2_handle
*h
= NULL
;
95 struct smb2_create io
;
96 struct GUID create_guid
= GUID_random();
98 struct smbcli_options options
;
100 options
= tree
->session
->transport
->options
;
102 /* Choose a random name in case the state is left a little funky. */
103 snprintf(fname
, 256, "durable_v2_open_create_blob_%s.dat",
104 generate_random_str(tctx
, 8));
106 smb2_util_unlink(tree
, fname
);
108 smb2_oplock_create_share(&io
, fname
,
109 smb2_util_share_access(""),
110 smb2_util_oplock_level("b"));
111 io
.in
.durable_open
= false;
112 io
.in
.durable_open_v2
= true;
113 io
.in
.persistent_open
= false;
114 io
.in
.create_guid
= create_guid
;
115 io
.in
.timeout
= UINT32_MAX
;
117 status
= smb2_create(tree
, mem_ctx
, &io
);
118 CHECK_STATUS(status
, NT_STATUS_OK
);
119 _h
= io
.out
.file
.handle
;
121 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
122 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
123 CHECK_VAL(io
.out
.durable_open
, false);
124 CHECK_VAL(io
.out
.durable_open_v2
, true);
125 CHECK_VAL(io
.out
.persistent_open
, false);
126 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
131 /* create a new session (same client_guid) */
132 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
133 torture_warning(tctx
, "couldn't reconnect, bailing\n");
139 * check invalid combinations of durable handle
140 * request and reconnect blobs
141 * See MS-SMB2: 3.3.5.9.12
142 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
146 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
147 io
.in
.durable_open
= true; /* durable v1 handle request */
148 io
.in
.create_guid
= create_guid
;
149 status
= smb2_create(tree
, mem_ctx
, &io
);
150 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
154 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
155 io
.in
.durable_open_v2
= true; /* durable v2 handle request */
156 io
.in
.create_guid
= create_guid
;
157 status
= smb2_create(tree
, mem_ctx
, &io
);
158 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
162 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
163 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
164 io
.in
.create_guid
= create_guid
;
165 status
= smb2_create(tree
, mem_ctx
, &io
);
166 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
170 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
171 io
.in
.durable_open_v2
= true; /* durable v2 handle request */
172 io
.in
.create_guid
= create_guid
;
173 status
= smb2_create(tree
, mem_ctx
, &io
);
174 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
178 smb2_util_close(tree
, *h
);
181 smb2_util_unlink(tree
, fname
);
185 talloc_free(mem_ctx
);
192 * basic durable_open test.
193 * durable state should only be granted when requested
194 * along with a batch oplock or a handle lease.
196 * This test tests durable open with all possible oplock types.
199 struct durable_open_vs_oplock
{
201 const char *share_mode
;
206 #define NUM_OPLOCK_TYPES 4
207 #define NUM_SHARE_MODES 8
208 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
209 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
211 { "", "", false, false },
212 { "", "R", false, false },
213 { "", "W", false, false },
214 { "", "D", false, false },
215 { "", "RD", false, false },
216 { "", "RW", false, false },
217 { "", "WD", false, false },
218 { "", "RWD", false, false },
220 { "s", "", false, false },
221 { "s", "R", false, false },
222 { "s", "W", false, false },
223 { "s", "D", false, false },
224 { "s", "RD", false, false },
225 { "s", "RW", false, false },
226 { "s", "WD", false, false },
227 { "s", "RWD", false, false },
229 { "x", "", false, false },
230 { "x", "R", false, false },
231 { "x", "W", false, false },
232 { "x", "D", false, false },
233 { "x", "RD", false, false },
234 { "x", "RW", false, false },
235 { "x", "WD", false, false },
236 { "x", "RWD", false, false },
238 { "b", "", true, false },
239 { "b", "R", true, false },
240 { "b", "W", true, false },
241 { "b", "D", true, false },
242 { "b", "RD", true, false },
243 { "b", "RW", true, false },
244 { "b", "WD", true, false },
245 { "b", "RWD", true, false },
248 static bool test_one_durable_v2_open_oplock(struct torture_context
*tctx
,
249 struct smb2_tree
*tree
,
251 bool request_persistent
,
252 struct durable_open_vs_oplock test
)
255 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
256 struct smb2_handle _h
;
257 struct smb2_handle
*h
= NULL
;
259 struct smb2_create io
;
261 smb2_util_unlink(tree
, fname
);
263 smb2_oplock_create_share(&io
, fname
,
264 smb2_util_share_access(test
.share_mode
),
265 smb2_util_oplock_level(test
.level
));
266 io
.in
.durable_open
= false;
267 io
.in
.durable_open_v2
= true;
268 io
.in
.persistent_open
= request_persistent
;
269 io
.in
.create_guid
= GUID_random();
271 status
= smb2_create(tree
, mem_ctx
, &io
);
272 CHECK_STATUS(status
, NT_STATUS_OK
);
273 _h
= io
.out
.file
.handle
;
275 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
276 CHECK_VAL(io
.out
.durable_open
, false);
277 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
278 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
279 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
283 smb2_util_close(tree
, *h
);
285 smb2_util_unlink(tree
, fname
);
286 talloc_free(mem_ctx
);
291 static bool test_durable_v2_open_oplock_table(struct torture_context
*tctx
,
292 struct smb2_tree
*tree
,
294 bool request_persistent
,
295 struct durable_open_vs_oplock
*table
,
301 smb2_util_unlink(tree
, fname
);
303 for (i
= 0; i
< num_tests
; i
++) {
304 ret
= test_one_durable_v2_open_oplock(tctx
,
315 smb2_util_unlink(tree
, fname
);
320 bool test_durable_v2_open_oplock(struct torture_context
*tctx
,
321 struct smb2_tree
*tree
)
326 /* Choose a random name in case the state is left a little funky. */
327 snprintf(fname
, 256, "durable_open_oplock_%s.dat",
328 generate_random_str(tctx
, 8));
330 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
331 false, /* request_persistent */
332 durable_open_vs_oplock_table
,
333 NUM_OPLOCK_OPEN_TESTS
);
341 * basic durable handle open test.
342 * persistent state should only be granted when requested
343 * along with a batch oplock or a handle lease.
345 * This test tests persistent open with all valid lease types.
348 struct durable_open_vs_lease
{
350 const char *share_mode
;
355 #define NUM_LEASE_TYPES 5
356 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
357 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
359 { "", "", false, false },
360 { "", "R", false, false },
361 { "", "W", false, false },
362 { "", "D", false, false },
363 { "", "RW", false, false },
364 { "", "RD", false, false },
365 { "", "WD", false, false },
366 { "", "RWD", false, false },
368 { "R", "", false, false },
369 { "R", "R", false, false },
370 { "R", "W", false, false },
371 { "R", "D", false, false },
372 { "R", "RW", false, false },
373 { "R", "RD", false, false },
374 { "R", "DW", false, false },
375 { "R", "RWD", false, false },
377 { "RW", "", false, false },
378 { "RW", "R", false, false },
379 { "RW", "W", false, false },
380 { "RW", "D", false, false },
381 { "RW", "RW", false, false },
382 { "RW", "RD", false, false },
383 { "RW", "WD", false, false },
384 { "RW", "RWD", false, false },
386 { "RH", "", true, false },
387 { "RH", "R", true, false },
388 { "RH", "W", true, false },
389 { "RH", "D", true, false },
390 { "RH", "RW", true, false },
391 { "RH", "RD", true, false },
392 { "RH", "WD", true, false },
393 { "RH", "RWD", true, false },
395 { "RHW", "", true, false },
396 { "RHW", "R", true, false },
397 { "RHW", "W", true, false },
398 { "RHW", "D", true, false },
399 { "RHW", "RW", true, false },
400 { "RHW", "RD", true, false },
401 { "RHW", "WD", true, false },
402 { "RHW", "RWD", true, false },
405 static bool test_one_durable_v2_open_lease(struct torture_context
*tctx
,
406 struct smb2_tree
*tree
,
408 bool request_persistent
,
409 struct durable_open_vs_lease test
)
412 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
413 struct smb2_handle _h
;
414 struct smb2_handle
*h
= NULL
;
416 struct smb2_create io
;
417 struct smb2_lease ls
;
420 smb2_util_unlink(tree
, fname
);
424 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
425 smb2_util_share_access(test
.share_mode
),
427 smb2_util_lease_state(test
.type
));
428 io
.in
.durable_open
= false;
429 io
.in
.durable_open_v2
= true;
430 io
.in
.persistent_open
= request_persistent
;
431 io
.in
.create_guid
= GUID_random();
433 status
= smb2_create(tree
, mem_ctx
, &io
);
434 CHECK_STATUS(status
, NT_STATUS_OK
);
435 _h
= io
.out
.file
.handle
;
437 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
438 CHECK_VAL(io
.out
.durable_open
, false);
439 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
440 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
441 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
442 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
443 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
444 CHECK_VAL(io
.out
.lease_response
.lease_state
,
445 smb2_util_lease_state(test
.type
));
448 smb2_util_close(tree
, *h
);
450 smb2_util_unlink(tree
, fname
);
451 talloc_free(mem_ctx
);
456 static bool test_durable_v2_open_lease_table(struct torture_context
*tctx
,
457 struct smb2_tree
*tree
,
459 bool request_persistent
,
460 struct durable_open_vs_lease
*table
,
466 smb2_util_unlink(tree
, fname
);
468 for (i
= 0; i
< num_tests
; i
++) {
469 ret
= test_one_durable_v2_open_lease(tctx
,
480 smb2_util_unlink(tree
, fname
);
485 bool test_durable_v2_open_lease(struct torture_context
*tctx
,
486 struct smb2_tree
*tree
)
492 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
493 if (!(caps
& SMB2_CAP_LEASING
)) {
494 torture_skip(tctx
, "leases are not supported");
497 /* Choose a random name in case the state is left a little funky. */
498 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
500 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
501 false, /* request_persistent */
502 durable_open_vs_lease_table
,
503 NUM_LEASE_OPEN_TESTS
);
510 * basic test for doing a durable open
511 * and do a durable reopen on the same connection
512 * while the first open is still active (fails)
514 bool test_durable_v2_open_reopen1(struct torture_context
*tctx
,
515 struct smb2_tree
*tree
)
518 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
520 struct smb2_handle _h
;
521 struct smb2_handle
*h
= NULL
;
522 struct smb2_create io
;
523 struct GUID create_guid
= GUID_random();
526 /* Choose a random name in case the state is left a little funky. */
527 snprintf(fname
, 256, "durable_v2_open_reopen1_%s.dat",
528 generate_random_str(tctx
, 8));
530 smb2_util_unlink(tree
, fname
);
532 smb2_oplock_create_share(&io
, fname
,
533 smb2_util_share_access(""),
534 smb2_util_oplock_level("b"));
535 io
.in
.durable_open
= false;
536 io
.in
.durable_open_v2
= true;
537 io
.in
.persistent_open
= false;
538 io
.in
.create_guid
= create_guid
;
539 io
.in
.timeout
= UINT32_MAX
;
541 status
= smb2_create(tree
, mem_ctx
, &io
);
542 CHECK_STATUS(status
, NT_STATUS_OK
);
543 _h
= io
.out
.file
.handle
;
545 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
546 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
547 CHECK_VAL(io
.out
.durable_open
, false);
548 CHECK_VAL(io
.out
.durable_open_v2
, true);
549 CHECK_VAL(io
.out
.persistent_open
, false);
550 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
552 /* try a durable reconnect while the file is still open */
555 io
.in
.durable_handle_v2
= h
;
556 io
.in
.create_guid
= create_guid
;
557 status
= smb2_create(tree
, mem_ctx
, &io
);
558 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
562 smb2_util_close(tree
, *h
);
565 smb2_util_unlink(tree
, fname
);
569 talloc_free(mem_ctx
);
575 * Basic test for doing a durable open
576 * and do a session reconnect while the first
577 * session is still active and the handle is
578 * still open in the client.
579 * This closes the original session and a
580 * durable reconnect on the new session succeeds.
582 bool test_durable_v2_open_reopen1a(struct torture_context
*tctx
,
583 struct smb2_tree
*tree
)
586 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
588 struct smb2_handle _h
;
589 struct smb2_handle
*h
= NULL
;
590 struct smb2_create io
;
591 struct GUID create_guid
= GUID_random();
593 struct smb2_tree
*tree2
= NULL
;
594 struct smb2_tree
*tree3
= NULL
;
595 uint64_t previous_session_id
;
596 struct smbcli_options options
;
597 struct GUID orig_client_guid
;
599 options
= tree
->session
->transport
->options
;
600 orig_client_guid
= options
.client_guid
;
602 /* Choose a random name in case the state is left a little funky. */
603 snprintf(fname
, 256, "durable_v2_open_reopen1a_%s.dat",
604 generate_random_str(tctx
, 8));
606 smb2_util_unlink(tree
, fname
);
608 smb2_oplock_create_share(&io
, fname
,
609 smb2_util_share_access(""),
610 smb2_util_oplock_level("b"));
611 io
.in
.durable_open
= false;
612 io
.in
.durable_open_v2
= true;
613 io
.in
.persistent_open
= false;
614 io
.in
.create_guid
= create_guid
;
615 io
.in
.timeout
= UINT32_MAX
;
617 status
= smb2_create(tree
, mem_ctx
, &io
);
618 CHECK_STATUS(status
, NT_STATUS_OK
);
619 _h
= io
.out
.file
.handle
;
621 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
622 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
623 CHECK_VAL(io
.out
.durable_open
, false);
624 CHECK_VAL(io
.out
.durable_open_v2
, true);
625 CHECK_VAL(io
.out
.persistent_open
, false);
626 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
629 * a session reconnect on a second tcp connection
632 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
634 /* for oplocks, the client guid can be different: */
635 options
.client_guid
= GUID_random();
637 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
639 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
642 * check that this has deleted the old session
647 io
.in
.durable_handle_v2
= h
;
648 io
.in
.create_guid
= create_guid
;
649 status
= smb2_create(tree
, mem_ctx
, &io
);
650 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
655 * but a durable reconnect on the new session succeeds:
660 io
.in
.durable_handle_v2
= h
;
661 io
.in
.create_guid
= create_guid
;
662 status
= smb2_create(tree2
, mem_ctx
, &io
);
663 CHECK_STATUS(status
, NT_STATUS_OK
);
664 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
665 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
666 CHECK_VAL(io
.out
.durable_open
, false);
667 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
668 CHECK_VAL(io
.out
.persistent_open
, false);
669 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
670 _h
= io
.out
.file
.handle
;
674 * a session reconnect on a second tcp connection
677 previous_session_id
= smb2cli_session_current_id(tree2
->session
->smbXcli
);
679 /* it works the same with the original guid */
680 options
.client_guid
= orig_client_guid
;
682 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
684 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
687 * check that this has deleted the old session
692 io
.in
.durable_handle_v2
= h
;
693 io
.in
.create_guid
= create_guid
;
694 status
= smb2_create(tree2
, mem_ctx
, &io
);
695 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
699 * but a durable reconnect on the new session succeeds:
704 io
.in
.durable_handle_v2
= h
;
705 io
.in
.create_guid
= create_guid
;
706 status
= smb2_create(tree3
, mem_ctx
, &io
);
707 CHECK_STATUS(status
, NT_STATUS_OK
);
708 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
709 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
710 CHECK_VAL(io
.out
.durable_open
, false);
711 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
712 CHECK_VAL(io
.out
.persistent_open
, false);
713 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
714 _h
= io
.out
.file
.handle
;
728 smb2_util_close(tree
, *h
);
731 smb2_util_unlink(tree
, fname
);
736 talloc_free(mem_ctx
);
742 * lease variant of reopen1a
744 * Basic test for doing a durable open and doing a session
745 * reconnect while the first session is still active and the
746 * handle is still open in the client.
747 * This closes the original session and a durable reconnect on
748 * the new session succeeds depending on the client guid:
750 * Durable reconnect on a session with a different client guid fails.
751 * Durable reconnect on a session with the original client guid succeeds.
753 bool test_durable_v2_open_reopen1a_lease(struct torture_context
*tctx
,
754 struct smb2_tree
*tree
)
757 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
759 struct smb2_handle _h
;
760 struct smb2_handle
*h
= NULL
;
761 struct smb2_create io
;
762 struct GUID create_guid
= GUID_random();
763 struct smb2_lease ls
;
766 struct smb2_tree
*tree2
= NULL
;
767 struct smb2_tree
*tree3
= NULL
;
768 uint64_t previous_session_id
;
769 struct smbcli_options options
;
770 struct GUID orig_client_guid
;
772 options
= tree
->session
->transport
->options
;
773 orig_client_guid
= options
.client_guid
;
775 /* Choose a random name in case the state is left a little funky. */
776 snprintf(fname
, 256, "durable_v2_open_reopen1a_lease_%s.dat",
777 generate_random_str(tctx
, 8));
779 smb2_util_unlink(tree
, fname
);
781 lease_key
= random();
782 smb2_lease_create(&io
, &ls
, false /* dir */, fname
,
783 lease_key
, smb2_util_lease_state("RWH"));
784 io
.in
.durable_open
= false;
785 io
.in
.durable_open_v2
= true;
786 io
.in
.persistent_open
= false;
787 io
.in
.create_guid
= create_guid
;
788 io
.in
.timeout
= UINT32_MAX
;
790 status
= smb2_create(tree
, mem_ctx
, &io
);
791 CHECK_STATUS(status
, NT_STATUS_OK
);
792 _h
= io
.out
.file
.handle
;
794 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
795 CHECK_VAL(io
.out
.durable_open
, false);
796 CHECK_VAL(io
.out
.durable_open_v2
, true);
797 CHECK_VAL(io
.out
.persistent_open
, false);
798 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
799 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
800 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
801 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
802 CHECK_VAL(io
.out
.lease_response
.lease_state
,
803 smb2_util_lease_state("RWH"));
804 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
805 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
807 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
810 * a session reconnect on a second tcp connection
811 * with a different client_guid does not allow
812 * the durable reconnect.
815 options
.client_guid
= GUID_random();
817 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
819 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
822 * check that this has deleted the old session
827 io
.in
.durable_handle_v2
= h
;
828 io
.in
.create_guid
= create_guid
;
829 io
.in
.lease_request
= &ls
;
830 status
= smb2_create(tree
, mem_ctx
, &io
);
831 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
835 * but a durable reconnect on the new session with the wrong
841 io
.in
.durable_handle_v2
= h
;
842 io
.in
.create_guid
= create_guid
;
843 io
.in
.lease_request
= &ls
;
844 status
= smb2_create(tree2
, mem_ctx
, &io
);
845 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
849 * now a session reconnect on a second tcp connection
850 * with original client_guid allows the durable reconnect.
853 options
.client_guid
= orig_client_guid
;
854 //options.client_guid = GUID_random();
856 ret
= torture_smb2_connection_ext(tctx
, previous_session_id
,
858 torture_assert_goto(tctx
, ret
, ret
, done
, "couldn't reconnect");
861 * check that this has deleted the old session
866 io
.in
.durable_handle_v2
= h
;
867 io
.in
.create_guid
= create_guid
;
868 io
.in
.lease_request
= &ls
;
869 status
= smb2_create(tree2
, mem_ctx
, &io
);
870 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
874 * but a durable reconnect on the new session succeeds:
879 io
.in
.durable_handle_v2
= h
;
880 io
.in
.create_guid
= create_guid
;
881 io
.in
.lease_request
= &ls
;
882 status
= smb2_create(tree3
, mem_ctx
, &io
);
883 CHECK_STATUS(status
, NT_STATUS_OK
);
884 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
885 CHECK_VAL(io
.out
.durable_open
, false);
886 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
887 CHECK_VAL(io
.out
.persistent_open
, false);
888 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
889 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
890 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
891 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
892 CHECK_VAL(io
.out
.lease_response
.lease_state
,
893 smb2_util_lease_state("RWH"));
894 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
895 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
896 _h
= io
.out
.file
.handle
;
910 smb2_util_close(tree
, *h
);
913 smb2_util_unlink(tree
, fname
);
918 talloc_free(mem_ctx
);
924 * basic test for doing a durable open
925 * tcp disconnect, reconnect, do a durable reopen (succeeds)
927 bool test_durable_v2_open_reopen2(struct torture_context
*tctx
,
928 struct smb2_tree
*tree
)
931 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
933 struct smb2_handle _h
;
934 struct smb2_handle
*h
= NULL
;
935 struct smb2_create io
;
936 struct GUID create_guid
= GUID_random();
937 struct GUID create_guid_invalid
= GUID_random();
940 /* Choose a random name in case the state is left a little funky. */
941 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
942 generate_random_str(tctx
, 8));
944 smb2_util_unlink(tree
, fname
);
946 smb2_oplock_create_share(&io
, fname
,
947 smb2_util_share_access(""),
948 smb2_util_oplock_level("b"));
949 io
.in
.durable_open
= false;
950 io
.in
.durable_open_v2
= true;
951 io
.in
.persistent_open
= false;
952 io
.in
.create_guid
= create_guid
;
953 io
.in
.timeout
= UINT32_MAX
;
955 status
= smb2_create(tree
, mem_ctx
, &io
);
956 CHECK_STATUS(status
, NT_STATUS_OK
);
957 _h
= io
.out
.file
.handle
;
959 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
960 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
961 CHECK_VAL(io
.out
.durable_open
, false);
962 CHECK_VAL(io
.out
.durable_open_v2
, true);
963 CHECK_VAL(io
.out
.persistent_open
, false);
964 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
966 /* disconnect, leaving the durable open */
969 if (!torture_smb2_connection(tctx
, &tree
)) {
970 torture_warning(tctx
, "couldn't reconnect, bailing\n");
976 * first a few failure cases
981 io
.in
.durable_handle_v2
= h
;
982 status
= smb2_create(tree
, mem_ctx
, &io
);
983 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
986 io
.in
.fname
= "__non_existing_fname__";
987 io
.in
.durable_handle_v2
= h
;
988 status
= smb2_create(tree
, mem_ctx
, &io
);
989 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
993 io
.in
.durable_handle_v2
= h
;
994 status
= smb2_create(tree
, mem_ctx
, &io
);
995 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
997 /* a non-zero but non-matching create_guid does not change it: */
1000 io
.in
.durable_handle_v2
= h
;
1001 io
.in
.create_guid
= create_guid_invalid
;
1002 status
= smb2_create(tree
, mem_ctx
, &io
);
1003 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1007 * The important difference is that the create_guid is provided.
1010 io
.in
.fname
= fname
;
1011 io
.in
.durable_open_v2
= false;
1012 io
.in
.durable_handle_v2
= h
;
1013 io
.in
.create_guid
= create_guid
;
1016 status
= smb2_create(tree
, mem_ctx
, &io
);
1017 CHECK_STATUS(status
, NT_STATUS_OK
);
1018 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1019 CHECK_VAL(io
.out
.durable_open
, false);
1020 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1021 CHECK_VAL(io
.out
.persistent_open
, false);
1022 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1023 _h
= io
.out
.file
.handle
;
1026 /* disconnect one more time */
1029 if (!torture_smb2_connection(tctx
, &tree
)) {
1030 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1036 /* These are completely ignored by the server */
1037 io
.in
.security_flags
= 0x78;
1038 io
.in
.oplock_level
= 0x78;
1039 io
.in
.impersonation_level
= 0x12345678;
1040 io
.in
.create_flags
= 0x12345678;
1041 io
.in
.reserved
= 0x12345678;
1042 io
.in
.desired_access
= 0x12345678;
1043 io
.in
.file_attributes
= 0x12345678;
1044 io
.in
.share_access
= 0x12345678;
1045 io
.in
.create_disposition
= 0x12345678;
1046 io
.in
.create_options
= 0x12345678;
1047 io
.in
.fname
= "__non_existing_fname__";
1050 * only io.in.durable_handle_v2 and
1051 * io.in.create_guid are checked
1053 io
.in
.durable_open_v2
= false;
1054 io
.in
.durable_handle_v2
= h
;
1055 io
.in
.create_guid
= create_guid
;
1058 status
= smb2_create(tree
, mem_ctx
, &io
);
1059 CHECK_STATUS(status
, NT_STATUS_OK
);
1060 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1061 CHECK_VAL(io
.out
.durable_open
, false);
1062 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1063 CHECK_VAL(io
.out
.persistent_open
, false);
1064 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1065 _h
= io
.out
.file
.handle
;
1070 smb2_util_close(tree
, *h
);
1073 smb2_util_unlink(tree
, fname
);
1077 talloc_free(mem_ctx
);
1083 * durable reconnect test:
1084 * connect with v2, reconnect with v1
1086 bool test_durable_v2_open_reopen2b(struct torture_context
*tctx
,
1087 struct smb2_tree
*tree
)
1090 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1092 struct smb2_handle _h
;
1093 struct smb2_handle
*h
= NULL
;
1094 struct smb2_create io
;
1095 struct GUID create_guid
= GUID_random();
1097 struct smbcli_options options
;
1099 options
= tree
->session
->transport
->options
;
1101 /* Choose a random name in case the state is left a little funky. */
1102 snprintf(fname
, 256, "durable_v2_open_reopen2b_%s.dat",
1103 generate_random_str(tctx
, 8));
1105 smb2_util_unlink(tree
, fname
);
1107 smb2_oplock_create_share(&io
, fname
,
1108 smb2_util_share_access(""),
1109 smb2_util_oplock_level("b"));
1110 io
.in
.durable_open
= false;
1111 io
.in
.durable_open_v2
= true;
1112 io
.in
.persistent_open
= false;
1113 io
.in
.create_guid
= create_guid
;
1114 io
.in
.timeout
= UINT32_MAX
;
1116 status
= smb2_create(tree
, mem_ctx
, &io
);
1117 CHECK_STATUS(status
, NT_STATUS_OK
);
1118 _h
= io
.out
.file
.handle
;
1120 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1121 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1122 CHECK_VAL(io
.out
.durable_open
, false);
1123 CHECK_VAL(io
.out
.durable_open_v2
, true);
1124 CHECK_VAL(io
.out
.persistent_open
, false);
1125 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1127 /* disconnect, leaving the durable open */
1130 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1131 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1137 io
.in
.fname
= fname
;
1138 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
1139 io
.in
.create_guid
= GUID_zero(); /* but zero create GUID */
1140 status
= smb2_create(tree
, mem_ctx
, &io
);
1141 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1144 io
.in
.fname
= fname
;
1145 io
.in
.durable_handle
= h
; /* durable v1 (!) reconnect */
1148 status
= smb2_create(tree
, mem_ctx
, &io
);
1149 CHECK_STATUS(status
, NT_STATUS_OK
);
1150 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1151 CHECK_VAL(io
.out
.durable_open
, false);
1152 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1153 CHECK_VAL(io
.out
.persistent_open
, false);
1154 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1155 _h
= io
.out
.file
.handle
;
1160 smb2_util_close(tree
, *h
);
1163 smb2_util_unlink(tree
, fname
);
1167 talloc_free(mem_ctx
);
1172 * durable reconnect test:
1173 * connect with v1, reconnect with v2 : fails (no create_guid...)
1175 bool test_durable_v2_open_reopen2c(struct torture_context
*tctx
,
1176 struct smb2_tree
*tree
)
1179 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1181 struct smb2_handle _h
;
1182 struct smb2_handle
*h
= NULL
;
1183 struct smb2_create io
;
1184 struct GUID create_guid
= GUID_random();
1186 struct smbcli_options options
;
1188 options
= tree
->session
->transport
->options
;
1190 /* Choose a random name in case the state is left a little funky. */
1191 snprintf(fname
, 256, "durable_v2_open_reopen2c_%s.dat",
1192 generate_random_str(tctx
, 8));
1194 smb2_util_unlink(tree
, fname
);
1196 smb2_oplock_create_share(&io
, fname
,
1197 smb2_util_share_access(""),
1198 smb2_util_oplock_level("b"));
1199 io
.in
.durable_open
= true;
1200 io
.in
.durable_open_v2
= false;
1201 io
.in
.persistent_open
= false;
1202 io
.in
.create_guid
= create_guid
;
1203 io
.in
.timeout
= UINT32_MAX
;
1205 status
= smb2_create(tree
, mem_ctx
, &io
);
1206 CHECK_STATUS(status
, NT_STATUS_OK
);
1207 _h
= io
.out
.file
.handle
;
1209 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1210 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1211 CHECK_VAL(io
.out
.durable_open
, true);
1212 CHECK_VAL(io
.out
.durable_open_v2
, false);
1213 CHECK_VAL(io
.out
.persistent_open
, false);
1214 CHECK_VAL(io
.out
.timeout
, 0);
1216 /* disconnect, leaving the durable open */
1219 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1220 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1226 io
.in
.fname
= fname
;
1227 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
1228 io
.in
.create_guid
= create_guid
;
1229 status
= smb2_create(tree
, mem_ctx
, &io
);
1230 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1234 smb2_util_close(tree
, *h
);
1237 smb2_util_unlink(tree
, fname
);
1241 talloc_free(mem_ctx
);
1247 * lease variant of reopen2
1248 * basic test for doing a durable open
1249 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1251 bool test_durable_v2_open_reopen2_lease(struct torture_context
*tctx
,
1252 struct smb2_tree
*tree
)
1255 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1257 struct smb2_handle _h
;
1258 struct smb2_handle
*h
= NULL
;
1259 struct smb2_create io
;
1260 struct GUID create_guid
= GUID_random();
1261 struct smb2_lease ls
;
1264 struct smbcli_options options
;
1267 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1268 if (!(caps
& SMB2_CAP_LEASING
)) {
1269 torture_skip(tctx
, "leases are not supported");
1272 options
= tree
->session
->transport
->options
;
1274 /* Choose a random name in case the state is left a little funky. */
1275 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
1276 generate_random_str(tctx
, 8));
1278 smb2_util_unlink(tree
, fname
);
1280 lease_key
= random();
1281 smb2_lease_create(&io
, &ls
, false /* dir */, fname
,
1282 lease_key
, smb2_util_lease_state("RWH"));
1283 io
.in
.durable_open
= false;
1284 io
.in
.durable_open_v2
= true;
1285 io
.in
.persistent_open
= false;
1286 io
.in
.create_guid
= create_guid
;
1287 io
.in
.timeout
= UINT32_MAX
;
1289 status
= smb2_create(tree
, mem_ctx
, &io
);
1290 CHECK_STATUS(status
, NT_STATUS_OK
);
1291 _h
= io
.out
.file
.handle
;
1293 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1294 CHECK_VAL(io
.out
.durable_open
, false);
1295 CHECK_VAL(io
.out
.durable_open_v2
, true);
1296 CHECK_VAL(io
.out
.persistent_open
, false);
1297 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1298 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1299 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1300 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1301 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1302 smb2_util_lease_state("RWH"));
1303 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1304 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1306 /* disconnect, reconnect and then do durable reopen */
1309 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1310 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1315 /* a few failure tests: */
1318 * several attempts without lease attached:
1319 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1320 * irrespective of file name provided
1325 io
.in
.durable_handle_v2
= h
;
1326 status
= smb2_create(tree
, mem_ctx
, &io
);
1327 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1330 io
.in
.fname
= "__non_existing_fname__";
1331 io
.in
.durable_handle_v2
= h
;
1332 status
= smb2_create(tree
, mem_ctx
, &io
);
1333 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1336 io
.in
.fname
= fname
;
1337 io
.in
.durable_handle_v2
= h
;
1338 status
= smb2_create(tree
, mem_ctx
, &io
);
1339 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1342 * attempt with lease provided, but
1343 * with a changed lease key. => fails
1346 io
.in
.fname
= fname
;
1347 io
.in
.durable_open_v2
= false;
1348 io
.in
.durable_handle_v2
= h
;
1349 io
.in
.create_guid
= create_guid
;
1350 io
.in
.lease_request
= &ls
;
1351 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1352 /* a wrong lease key lets the request fail */
1353 ls
.lease_key
.data
[0]++;
1355 status
= smb2_create(tree
, mem_ctx
, &io
);
1356 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1358 /* restore the correct lease key */
1359 ls
.lease_key
.data
[0]--;
1362 * this last failing attempt is almost correct:
1363 * only problem is: we use the wrong filename...
1364 * Note that this gives INVALID_PARAMETER.
1365 * This is different from oplocks!
1368 io
.in
.fname
= "__non_existing_fname__";
1369 io
.in
.durable_open_v2
= false;
1370 io
.in
.durable_handle_v2
= h
;
1371 io
.in
.create_guid
= create_guid
;
1372 io
.in
.lease_request
= &ls
;
1373 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1375 status
= smb2_create(tree
, mem_ctx
, &io
);
1376 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1379 * Now for a succeeding reconnect:
1383 io
.in
.fname
= fname
;
1384 io
.in
.durable_open_v2
= false;
1385 io
.in
.durable_handle_v2
= h
;
1386 io
.in
.create_guid
= create_guid
;
1387 io
.in
.lease_request
= &ls
;
1388 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1390 /* the requested lease state is irrelevant */
1391 ls
.lease_state
= smb2_util_lease_state("");
1395 status
= smb2_create(tree
, mem_ctx
, &io
);
1396 CHECK_STATUS(status
, NT_STATUS_OK
);
1398 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1399 CHECK_VAL(io
.out
.durable_open
, false);
1400 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1401 CHECK_VAL(io
.out
.persistent_open
, false);
1402 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1403 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1404 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1405 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1406 smb2_util_lease_state("RWH"));
1407 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1408 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1409 _h
= io
.out
.file
.handle
;
1412 /* disconnect one more time */
1415 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1416 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1422 * demonstrate that various parameters are ignored
1428 * These are completely ignored by the server
1430 io
.in
.security_flags
= 0x78;
1431 io
.in
.oplock_level
= 0x78;
1432 io
.in
.impersonation_level
= 0x12345678;
1433 io
.in
.create_flags
= 0x12345678;
1434 io
.in
.reserved
= 0x12345678;
1435 io
.in
.desired_access
= 0x12345678;
1436 io
.in
.file_attributes
= 0x12345678;
1437 io
.in
.share_access
= 0x12345678;
1438 io
.in
.create_disposition
= 0x12345678;
1439 io
.in
.create_options
= 0x12345678;
1442 * only these are checked:
1444 * - io.in.durable_handle_v2,
1445 * - io.in.create_guid
1446 * - io.in.lease_request->lease_key
1449 io
.in
.fname
= fname
;
1450 io
.in
.durable_open_v2
= false;
1451 io
.in
.durable_handle_v2
= h
;
1452 io
.in
.create_guid
= create_guid
;
1453 io
.in
.lease_request
= &ls
;
1455 /* the requested lease state is irrelevant */
1456 ls
.lease_state
= smb2_util_lease_state("");
1460 status
= smb2_create(tree
, mem_ctx
, &io
);
1461 CHECK_STATUS(status
, NT_STATUS_OK
);
1463 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1464 CHECK_VAL(io
.out
.durable_open
, false);
1465 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1466 CHECK_VAL(io
.out
.persistent_open
, false);
1467 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1468 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1469 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1470 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1471 smb2_util_lease_state("RWH"));
1472 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1473 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1475 _h
= io
.out
.file
.handle
;
1480 smb2_util_close(tree
, *h
);
1483 smb2_util_unlink(tree
, fname
);
1487 talloc_free(mem_ctx
);
1493 * lease_v2 variant of reopen2
1494 * basic test for doing a durable open
1495 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1497 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context
*tctx
,
1498 struct smb2_tree
*tree
)
1501 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1503 struct smb2_handle _h
;
1504 struct smb2_handle
*h
= NULL
;
1505 struct smb2_create io
;
1506 struct GUID create_guid
= GUID_random();
1507 struct smb2_lease ls
;
1510 struct smbcli_options options
;
1513 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1514 if (!(caps
& SMB2_CAP_LEASING
)) {
1515 torture_skip(tctx
, "leases are not supported");
1518 options
= tree
->session
->transport
->options
;
1520 smb2_deltree(tree
, __func__
);
1521 status
= torture_smb2_testdir(tree
, __func__
, &_h
);
1522 torture_assert_ntstatus_ok_goto(tctx
, status
, ret
, done
,
1523 "torture_smb2_testdir failed\n");
1524 smb2_util_close(tree
, _h
);
1526 /* Choose a random name in case the state is left a little funky. */
1527 snprintf(fname
, 256, "%s\\durable_v2_open_reopen2_%s.dat",
1528 __func__
, generate_random_str(tctx
, 8));
1530 smb2_util_unlink(tree
, fname
);
1532 lease_key
= random();
1533 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
1534 lease_key
, 0, /* parent lease key */
1535 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1536 io
.in
.durable_open
= false;
1537 io
.in
.durable_open_v2
= true;
1538 io
.in
.persistent_open
= false;
1539 io
.in
.create_guid
= create_guid
;
1540 io
.in
.timeout
= UINT32_MAX
;
1542 status
= smb2_create(tree
, mem_ctx
, &io
);
1543 CHECK_STATUS(status
, NT_STATUS_OK
);
1544 _h
= io
.out
.file
.handle
;
1546 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1547 CHECK_VAL(io
.out
.durable_open
, false);
1548 CHECK_VAL(io
.out
.durable_open_v2
, true);
1549 CHECK_VAL(io
.out
.persistent_open
, false);
1550 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1551 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1552 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1553 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1555 /* disconnect, reconnect and then do durable reopen */
1558 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1559 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1564 /* a few failure tests: */
1567 * several attempts without lease attached:
1568 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1569 * irrespective of file name provided
1574 io
.in
.durable_handle_v2
= h
;
1575 status
= smb2_create(tree
, mem_ctx
, &io
);
1576 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1579 io
.in
.fname
= "__non_existing_fname__";
1580 io
.in
.durable_handle_v2
= h
;
1581 status
= smb2_create(tree
, mem_ctx
, &io
);
1582 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1585 io
.in
.fname
= fname
;
1586 io
.in
.durable_handle_v2
= h
;
1587 status
= smb2_create(tree
, mem_ctx
, &io
);
1588 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1591 * attempt with lease provided, but
1592 * with a changed lease key. => fails
1595 io
.in
.fname
= fname
;
1596 io
.in
.durable_open_v2
= false;
1597 io
.in
.durable_handle_v2
= h
;
1598 io
.in
.create_guid
= create_guid
;
1599 io
.in
.lease_request_v2
= &ls
;
1600 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1601 /* a wrong lease key lets the request fail */
1602 ls
.lease_key
.data
[0]++;
1604 status
= smb2_create(tree
, mem_ctx
, &io
);
1605 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1607 /* restore the correct lease key */
1608 ls
.lease_key
.data
[0]--;
1612 * this last failing attempt is almost correct:
1613 * only problem is: we use the wrong filename...
1614 * Note that this gives INVALID_PARAMETER.
1615 * This is different from oplocks!
1618 io
.in
.fname
= "__non_existing_fname__";
1619 io
.in
.durable_open_v2
= false;
1620 io
.in
.durable_handle_v2
= h
;
1621 io
.in
.create_guid
= create_guid
;
1622 io
.in
.lease_request_v2
= &ls
;
1623 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1625 status
= smb2_create(tree
, mem_ctx
, &io
);
1626 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1629 * Now for a succeeding reconnect:
1633 io
.in
.fname
= fname
;
1634 io
.in
.durable_open_v2
= false;
1635 io
.in
.durable_handle_v2
= h
;
1636 io
.in
.create_guid
= create_guid
;
1637 io
.in
.lease_request_v2
= &ls
;
1638 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1640 /* the requested lease state is irrelevant */
1641 ls
.lease_state
= smb2_util_lease_state("");
1645 status
= smb2_create(tree
, mem_ctx
, &io
);
1646 CHECK_STATUS(status
, NT_STATUS_OK
);
1648 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1649 CHECK_VAL(io
.out
.durable_open
, false);
1650 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1651 CHECK_VAL(io
.out
.persistent_open
, false);
1652 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1653 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1654 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1655 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1656 smb2_util_lease_state("RWH"));
1657 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1658 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1659 _h
= io
.out
.file
.handle
;
1662 /* disconnect one more time */
1665 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1666 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1672 * demonstrate that various parameters are ignored
1678 * These are completely ignored by the server
1680 io
.in
.security_flags
= 0x78;
1681 io
.in
.oplock_level
= 0x78;
1682 io
.in
.impersonation_level
= 0x12345678;
1683 io
.in
.create_flags
= 0x12345678;
1684 io
.in
.reserved
= 0x12345678;
1685 io
.in
.desired_access
= 0x12345678;
1686 io
.in
.file_attributes
= 0x12345678;
1687 io
.in
.share_access
= 0x12345678;
1688 io
.in
.create_disposition
= 0x12345678;
1689 io
.in
.create_options
= 0x12345678;
1690 io
.in
.fname
= "__non_existing_fname__";
1693 * only these are checked:
1695 * - io.in.durable_handle_v2,
1696 * - io.in.create_guid
1697 * - io.in.lease_request_v2->lease_key
1700 io
.in
.fname
= fname
;
1701 io
.in
.durable_open_v2
= false;
1702 io
.in
.durable_handle_v2
= h
;
1703 io
.in
.create_guid
= create_guid
;
1704 io
.in
.lease_request_v2
= &ls
;
1706 /* the requested lease state is irrelevant */
1707 ls
.lease_state
= smb2_util_lease_state("");
1711 status
= smb2_create(tree
, mem_ctx
, &io
);
1712 CHECK_STATUS(status
, NT_STATUS_OK
);
1713 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1714 CHECK_VAL(io
.out
.durable_open
, false);
1715 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1716 CHECK_VAL(io
.out
.persistent_open
, false);
1717 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1718 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1719 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1720 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1721 smb2_util_lease_state("RWH"));
1722 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1723 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1725 _h
= io
.out
.file
.handle
;
1730 smb2_util_close(tree
, *h
);
1733 smb2_util_unlink(tree
, fname
);
1734 smb2_deltree(tree
, __func__
);
1738 talloc_free(mem_ctx
);
1744 * Test durable request / reconnect with AppInstanceId
1746 bool test_durable_v2_open_app_instance(struct torture_context
*tctx
,
1747 struct smb2_tree
*tree1
,
1748 struct smb2_tree
*tree2
)
1751 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1753 struct smb2_handle _h1
, _h2
;
1754 struct smb2_handle
*h1
= NULL
, *h2
= NULL
;
1755 struct smb2_create io1
, io2
;
1757 struct GUID create_guid_1
= GUID_random();
1758 struct GUID create_guid_2
= GUID_random();
1759 struct GUID app_instance_id
= GUID_random();
1761 /* Choose a random name in case the state is left a little funky. */
1762 snprintf(fname
, 256, "durable_v2_open_app_instance_%s.dat",
1763 generate_random_str(tctx
, 8));
1765 smb2_util_unlink(tree1
, fname
);
1767 ZERO_STRUCT(break_info
);
1768 tree1
->session
->transport
->oplock
.handler
= torture_oplock_handler
;
1769 tree1
->session
->transport
->oplock
.private_data
= tree1
;
1771 smb2_oplock_create_share(&io1
, fname
,
1772 smb2_util_share_access(""),
1773 smb2_util_oplock_level("b"));
1774 io1
.in
.durable_open
= false;
1775 io1
.in
.durable_open_v2
= true;
1776 io1
.in
.persistent_open
= false;
1777 io1
.in
.create_guid
= create_guid_1
;
1778 io1
.in
.app_instance_id
= &app_instance_id
;
1779 io1
.in
.timeout
= UINT32_MAX
;
1781 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1782 CHECK_STATUS(status
, NT_STATUS_OK
);
1783 _h1
= io1
.out
.file
.handle
;
1785 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1786 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1787 CHECK_VAL(io1
.out
.durable_open
, false);
1788 CHECK_VAL(io1
.out
.durable_open_v2
, true);
1789 CHECK_VAL(io1
.out
.persistent_open
, false);
1790 CHECK_VAL(io1
.out
.timeout
, io1
.in
.timeout
);
1793 * try to open the file as durable from a second tree with
1794 * a different create guid but the same app_instance_id
1795 * while the first handle is still open.
1798 smb2_oplock_create_share(&io2
, fname
,
1799 smb2_util_share_access(""),
1800 smb2_util_oplock_level("b"));
1801 io2
.in
.durable_open
= false;
1802 io2
.in
.durable_open_v2
= true;
1803 io2
.in
.persistent_open
= false;
1804 io2
.in
.create_guid
= create_guid_2
;
1805 io2
.in
.app_instance_id
= &app_instance_id
;
1806 io2
.in
.timeout
= UINT32_MAX
;
1808 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1809 CHECK_STATUS(status
, NT_STATUS_OK
);
1810 _h2
= io2
.out
.file
.handle
;
1812 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1813 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1814 CHECK_VAL(io2
.out
.durable_open
, false);
1815 CHECK_VAL(io2
.out
.durable_open_v2
, true);
1816 CHECK_VAL(io2
.out
.persistent_open
, false);
1817 CHECK_VAL(io2
.out
.timeout
, io2
.in
.timeout
);
1819 CHECK_VAL(break_info
.count
, 0);
1821 status
= smb2_util_close(tree1
, *h1
);
1822 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1827 smb2_util_close(tree1
, *h1
);
1830 smb2_util_close(tree2
, *h2
);
1833 smb2_util_unlink(tree2
, fname
);
1838 talloc_free(mem_ctx
);
1845 * basic persistent open test.
1847 * This test tests durable open with all possible oplock types.
1850 struct durable_open_vs_oplock persistent_open_oplock_ca_table
[NUM_OPLOCK_OPEN_TESTS
] =
1852 { "", "", true, true },
1853 { "", "R", true, true },
1854 { "", "W", true, true },
1855 { "", "D", true, true },
1856 { "", "RD", true, true },
1857 { "", "RW", true, true },
1858 { "", "WD", true, true },
1859 { "", "RWD", true, true },
1861 { "s", "", true, true },
1862 { "s", "R", true, true },
1863 { "s", "W", true, true },
1864 { "s", "D", true, true },
1865 { "s", "RD", true, true },
1866 { "s", "RW", true, true },
1867 { "s", "WD", true, true },
1868 { "s", "RWD", true, true },
1870 { "x", "", true, true },
1871 { "x", "R", true, true },
1872 { "x", "W", true, true },
1873 { "x", "D", true, true },
1874 { "x", "RD", true, true },
1875 { "x", "RW", true, true },
1876 { "x", "WD", true, true },
1877 { "x", "RWD", true, true },
1879 { "b", "", true, true },
1880 { "b", "R", true, true },
1881 { "b", "W", true, true },
1882 { "b", "D", true, true },
1883 { "b", "RD", true, true },
1884 { "b", "RW", true, true },
1885 { "b", "WD", true, true },
1886 { "b", "RWD", true, true },
1889 bool test_persistent_open_oplock(struct torture_context
*tctx
,
1890 struct smb2_tree
*tree
)
1894 uint32_t share_capabilities
;
1895 bool share_is_ca
= false;
1896 struct durable_open_vs_oplock
*table
;
1898 /* Choose a random name in case the state is left a little funky. */
1899 snprintf(fname
, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1901 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1902 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1905 table
= persistent_open_oplock_ca_table
;
1907 table
= durable_open_vs_oplock_table
;
1910 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
1911 true, /* request_persistent */
1913 NUM_OPLOCK_OPEN_TESTS
);
1921 * basic persistent handle open test.
1922 * persistent state should only be granted when requested
1923 * along with a batch oplock or a handle lease.
1925 * This test tests persistent open with all valid lease types.
1928 struct durable_open_vs_lease persistent_open_lease_ca_table
[NUM_LEASE_OPEN_TESTS
] =
1930 { "", "", true, true },
1931 { "", "R", true, true },
1932 { "", "W", true, true },
1933 { "", "D", true, true },
1934 { "", "RW", true, true },
1935 { "", "RD", true, true },
1936 { "", "WD", true, true },
1937 { "", "RWD", true, true },
1939 { "R", "", true, true },
1940 { "R", "R", true, true },
1941 { "R", "W", true, true },
1942 { "R", "D", true, true },
1943 { "R", "RW", true, true },
1944 { "R", "RD", true, true },
1945 { "R", "DW", true, true },
1946 { "R", "RWD", true, true },
1948 { "RW", "", true, true },
1949 { "RW", "R", true, true },
1950 { "RW", "W", true, true },
1951 { "RW", "D", true, true },
1952 { "RW", "RW", true, true },
1953 { "RW", "RD", true, true },
1954 { "RW", "WD", true, true },
1955 { "RW", "RWD", true, true },
1957 { "RH", "", true, true },
1958 { "RH", "R", true, true },
1959 { "RH", "W", true, true },
1960 { "RH", "D", true, true },
1961 { "RH", "RW", true, true },
1962 { "RH", "RD", true, true },
1963 { "RH", "WD", true, true },
1964 { "RH", "RWD", true, true },
1966 { "RHW", "", true, true },
1967 { "RHW", "R", true, true },
1968 { "RHW", "W", true, true },
1969 { "RHW", "D", true, true },
1970 { "RHW", "RW", true, true },
1971 { "RHW", "RD", true, true },
1972 { "RHW", "WD", true, true },
1973 { "RHW", "RWD", true, true },
1976 bool test_persistent_open_lease(struct torture_context
*tctx
,
1977 struct smb2_tree
*tree
)
1982 uint32_t share_capabilities
;
1984 struct durable_open_vs_lease
*table
;
1986 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1987 if (!(caps
& SMB2_CAP_LEASING
)) {
1988 torture_skip(tctx
, "leases are not supported");
1991 /* Choose a random name in case the state is left a little funky. */
1992 snprintf(fname
, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx
, 8));
1994 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1995 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1998 table
= persistent_open_lease_ca_table
;
2000 table
= durable_open_vs_lease_table
;
2003 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
2004 true, /* request_persistent */
2006 NUM_LEASE_OPEN_TESTS
);
2013 struct torture_suite
*torture_smb2_durable_v2_open_init(TALLOC_CTX
*ctx
)
2015 struct torture_suite
*suite
=
2016 torture_suite_create(ctx
, "durable-v2-open");
2018 torture_suite_add_1smb2_test(suite
, "create-blob", test_durable_v2_open_create_blob
);
2019 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_v2_open_oplock
);
2020 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_v2_open_lease
);
2021 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_v2_open_reopen1
);
2022 torture_suite_add_1smb2_test(suite
, "reopen1a", test_durable_v2_open_reopen1a
);
2023 torture_suite_add_1smb2_test(suite
, "reopen1a-lease", test_durable_v2_open_reopen1a_lease
);
2024 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_v2_open_reopen2
);
2025 torture_suite_add_1smb2_test(suite
, "reopen2b", test_durable_v2_open_reopen2b
);
2026 torture_suite_add_1smb2_test(suite
, "reopen2c", test_durable_v2_open_reopen2c
);
2027 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_v2_open_reopen2_lease
);
2028 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2
);
2029 torture_suite_add_2smb2_test(suite
, "app-instance", test_durable_v2_open_app_instance
);
2030 torture_suite_add_1smb2_test(suite
, "persistent-open-oplock", test_persistent_open_oplock
);
2031 torture_suite_add_1smb2_test(suite
, "persistent-open-lease", test_persistent_open_lease
);
2033 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-V2-OPEN tests");
2039 * basic test for doing a durable open
2040 * tcp disconnect, reconnect, do a durable reopen (succeeds)
2042 static bool test_durable_v2_reconnect_delay(struct torture_context
*tctx
,
2043 struct smb2_tree
*tree
)
2046 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2048 struct smb2_handle _h
;
2049 struct smb2_handle
*h
= NULL
;
2050 struct smb2_create io
;
2051 struct GUID create_guid
= GUID_random();
2052 struct smbcli_options options
;
2053 uint64_t previous_session_id
;
2057 options
= tree
->session
->transport
->options
;
2058 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2060 /* Choose a random name in case the state is left a little funky. */
2061 snprintf(fname
, 256, "durable_v2_reconnect_delay_%s.dat",
2062 generate_random_str(tctx
, 8));
2064 smb2_util_unlink(tree
, fname
);
2066 smb2_oplock_create_share(&io
, fname
,
2067 smb2_util_share_access(""),
2068 smb2_util_oplock_level("b"));
2069 io
.in
.durable_open
= false;
2070 io
.in
.durable_open_v2
= true;
2071 io
.in
.persistent_open
= false;
2072 io
.in
.create_guid
= create_guid
;
2075 status
= smb2_create(tree
, mem_ctx
, &io
);
2076 CHECK_STATUS(status
, NT_STATUS_OK
);
2078 _h
= io
.out
.file
.handle
;
2080 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2081 CHECK_VAL(io
.out
.durable_open_v2
, true);
2083 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
2084 CHECK_STATUS(status
, NT_STATUS_OK
);
2086 /* disconnect, leaving the durable open */
2089 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2091 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2097 io
.in
.fname
= fname
;
2098 io
.in
.durable_open_v2
= false;
2099 io
.in
.durable_handle_v2
= h
;
2100 io
.in
.create_guid
= create_guid
;
2103 status
= smb2_create(tree
, mem_ctx
, &io
);
2104 CHECK_STATUS(status
, NT_STATUS_OK
);
2105 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2106 _h
= io
.out
.file
.handle
;
2111 smb2_util_close(tree
, *h
);
2114 smb2_util_unlink(tree
, fname
);
2118 talloc_free(mem_ctx
);
2123 struct torture_suite
*torture_smb2_durable_v2_delay_init(TALLOC_CTX
*ctx
)
2125 struct torture_suite
*suite
=
2126 torture_suite_create(ctx
, "durable-v2-delay");
2128 torture_suite_add_1smb2_test(suite
, "durable_v2_reconnect_delay", test_durable_v2_reconnect_delay
);