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.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
59 static void torture_oplock_close_callback(struct smb2_request
*req
)
61 smb2_close_recv(req
, &break_info
.cl
);
64 /* A general oplock break notification handler. This should be used when a
65 * test expects to break from batch or exclusive to a lower level. */
66 static bool torture_oplock_handler(struct smb2_transport
*transport
,
67 const struct smb2_handle
*handle
,
71 struct smb2_tree
*tree
= private_data
;
72 struct smb2_request
*req
;
76 ZERO_STRUCT(break_info
.cl
);
77 break_info
.cl
.in
.file
.handle
= *handle
;
79 req
= smb2_close_send(tree
, &break_info
.cl
);
80 req
->async
.fn
= torture_oplock_close_callback
;
81 req
->async
.private_data
= NULL
;
86 * testing various create blob combinations.
88 bool test_durable_v2_open_create_blob(struct torture_context
*tctx
,
89 struct smb2_tree
*tree
)
92 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
94 struct smb2_handle _h
;
95 struct smb2_handle
*h
= NULL
;
96 struct smb2_create io
;
97 struct GUID create_guid
= GUID_random();
99 struct smbcli_options options
;
101 options
= tree
->session
->transport
->options
;
103 /* Choose a random name in case the state is left a little funky. */
104 snprintf(fname
, 256, "durable_v2_open_create_blob_%s.dat",
105 generate_random_str(tctx
, 8));
107 smb2_util_unlink(tree
, fname
);
109 smb2_oplock_create_share(&io
, fname
,
110 smb2_util_share_access(""),
111 smb2_util_oplock_level("b"));
112 io
.in
.durable_open
= false;
113 io
.in
.durable_open_v2
= true;
114 io
.in
.persistent_open
= false;
115 io
.in
.create_guid
= create_guid
;
116 io
.in
.timeout
= UINT32_MAX
;
118 status
= smb2_create(tree
, mem_ctx
, &io
);
119 CHECK_STATUS(status
, NT_STATUS_OK
);
120 _h
= io
.out
.file
.handle
;
122 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
123 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
124 CHECK_VAL(io
.out
.durable_open
, false);
125 CHECK_VAL(io
.out
.durable_open_v2
, true);
126 CHECK_VAL(io
.out
.persistent_open
, false);
127 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
132 /* create a new session (same client_guid) */
133 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
134 torture_warning(tctx
, "couldn't reconnect, bailing\n");
140 * check invalid combinations of durable handle
141 * request and reconnect blobs
142 * See MS-SMB2: 3.3.5.9.12
143 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
147 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
148 io
.in
.durable_open
= true; /* durable v1 handle request */
149 io
.in
.create_guid
= create_guid
;
150 status
= smb2_create(tree
, mem_ctx
, &io
);
151 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
155 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
156 io
.in
.durable_open_v2
= true; /* durable v2 handle request */
157 io
.in
.create_guid
= create_guid
;
158 status
= smb2_create(tree
, mem_ctx
, &io
);
159 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
163 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
164 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
165 io
.in
.create_guid
= create_guid
;
166 status
= smb2_create(tree
, mem_ctx
, &io
);
167 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
171 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect request */
172 io
.in
.durable_open_v2
= true; /* durable v2 handle request */
173 io
.in
.create_guid
= create_guid
;
174 status
= smb2_create(tree
, mem_ctx
, &io
);
175 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
179 smb2_util_close(tree
, *h
);
182 smb2_util_unlink(tree
, fname
);
186 talloc_free(mem_ctx
);
193 * basic durable_open test.
194 * durable state should only be granted when requested
195 * along with a batch oplock or a handle lease.
197 * This test tests durable open with all possible oplock types.
200 struct durable_open_vs_oplock
{
202 const char *share_mode
;
207 #define NUM_OPLOCK_TYPES 4
208 #define NUM_SHARE_MODES 8
209 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
210 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
212 { "", "", false, false },
213 { "", "R", false, false },
214 { "", "W", false, false },
215 { "", "D", false, false },
216 { "", "RD", false, false },
217 { "", "RW", false, false },
218 { "", "WD", false, false },
219 { "", "RWD", false, false },
221 { "s", "", false, false },
222 { "s", "R", false, false },
223 { "s", "W", false, false },
224 { "s", "D", false, false },
225 { "s", "RD", false, false },
226 { "s", "RW", false, false },
227 { "s", "WD", false, false },
228 { "s", "RWD", false, false },
230 { "x", "", false, false },
231 { "x", "R", false, false },
232 { "x", "W", false, false },
233 { "x", "D", false, false },
234 { "x", "RD", false, false },
235 { "x", "RW", false, false },
236 { "x", "WD", false, false },
237 { "x", "RWD", false, false },
239 { "b", "", true, false },
240 { "b", "R", true, false },
241 { "b", "W", true, false },
242 { "b", "D", true, false },
243 { "b", "RD", true, false },
244 { "b", "RW", true, false },
245 { "b", "WD", true, false },
246 { "b", "RWD", true, false },
249 static bool test_one_durable_v2_open_oplock(struct torture_context
*tctx
,
250 struct smb2_tree
*tree
,
252 bool request_persistent
,
253 struct durable_open_vs_oplock test
)
256 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
257 struct smb2_handle _h
;
258 struct smb2_handle
*h
= NULL
;
260 struct smb2_create io
;
262 smb2_util_unlink(tree
, fname
);
264 smb2_oplock_create_share(&io
, fname
,
265 smb2_util_share_access(test
.share_mode
),
266 smb2_util_oplock_level(test
.level
));
267 io
.in
.durable_open
= false;
268 io
.in
.durable_open_v2
= true;
269 io
.in
.persistent_open
= request_persistent
;
270 io
.in
.create_guid
= GUID_random();
272 status
= smb2_create(tree
, mem_ctx
, &io
);
273 CHECK_STATUS(status
, NT_STATUS_OK
);
274 _h
= io
.out
.file
.handle
;
276 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
277 CHECK_VAL(io
.out
.durable_open
, false);
278 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
279 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
280 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
284 smb2_util_close(tree
, *h
);
286 smb2_util_unlink(tree
, fname
);
287 talloc_free(mem_ctx
);
292 static bool test_durable_v2_open_oplock_table(struct torture_context
*tctx
,
293 struct smb2_tree
*tree
,
295 bool request_persistent
,
296 struct durable_open_vs_oplock
*table
,
302 smb2_util_unlink(tree
, fname
);
304 for (i
= 0; i
< num_tests
; i
++) {
305 ret
= test_one_durable_v2_open_oplock(tctx
,
316 smb2_util_unlink(tree
, fname
);
321 bool test_durable_v2_open_oplock(struct torture_context
*tctx
,
322 struct smb2_tree
*tree
)
327 /* Choose a random name in case the state is left a little funky. */
328 snprintf(fname
, 256, "durable_open_oplock_%s.dat",
329 generate_random_str(tctx
, 8));
331 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
332 false, /* request_persistent */
333 durable_open_vs_oplock_table
,
334 NUM_OPLOCK_OPEN_TESTS
);
342 * basic durable handle open test.
343 * persistent state should only be granted when requested
344 * along with a batch oplock or a handle lease.
346 * This test tests persistent open with all valid lease types.
349 struct durable_open_vs_lease
{
351 const char *share_mode
;
356 #define NUM_LEASE_TYPES 5
357 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
358 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
360 { "", "", false, false },
361 { "", "R", false, false },
362 { "", "W", false, false },
363 { "", "D", false, false },
364 { "", "RW", false, false },
365 { "", "RD", false, false },
366 { "", "WD", false, false },
367 { "", "RWD", false, false },
369 { "R", "", false, false },
370 { "R", "R", false, false },
371 { "R", "W", false, false },
372 { "R", "D", false, false },
373 { "R", "RW", false, false },
374 { "R", "RD", false, false },
375 { "R", "DW", false, false },
376 { "R", "RWD", false, false },
378 { "RW", "", false, false },
379 { "RW", "R", false, false },
380 { "RW", "W", false, false },
381 { "RW", "D", false, false },
382 { "RW", "RW", false, false },
383 { "RW", "RD", false, false },
384 { "RW", "WD", false, false },
385 { "RW", "RWD", false, false },
387 { "RH", "", true, false },
388 { "RH", "R", true, false },
389 { "RH", "W", true, false },
390 { "RH", "D", true, false },
391 { "RH", "RW", true, false },
392 { "RH", "RD", true, false },
393 { "RH", "WD", true, false },
394 { "RH", "RWD", true, false },
396 { "RHW", "", true, false },
397 { "RHW", "R", true, false },
398 { "RHW", "W", true, false },
399 { "RHW", "D", true, false },
400 { "RHW", "RW", true, false },
401 { "RHW", "RD", true, false },
402 { "RHW", "WD", true, false },
403 { "RHW", "RWD", true, false },
406 static bool test_one_durable_v2_open_lease(struct torture_context
*tctx
,
407 struct smb2_tree
*tree
,
409 bool request_persistent
,
410 struct durable_open_vs_lease test
)
413 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
414 struct smb2_handle _h
;
415 struct smb2_handle
*h
= NULL
;
417 struct smb2_create io
;
418 struct smb2_lease ls
;
421 smb2_util_unlink(tree
, fname
);
425 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
426 smb2_util_share_access(test
.share_mode
),
428 smb2_util_lease_state(test
.type
));
429 io
.in
.durable_open
= false;
430 io
.in
.durable_open_v2
= true;
431 io
.in
.persistent_open
= request_persistent
;
432 io
.in
.create_guid
= GUID_random();
434 status
= smb2_create(tree
, mem_ctx
, &io
);
435 CHECK_STATUS(status
, NT_STATUS_OK
);
436 _h
= io
.out
.file
.handle
;
438 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
439 CHECK_VAL(io
.out
.durable_open
, false);
440 CHECK_VAL(io
.out
.durable_open_v2
, test
.durable
);
441 CHECK_VAL(io
.out
.persistent_open
, test
.persistent
);
442 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
443 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
444 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
445 CHECK_VAL(io
.out
.lease_response
.lease_state
,
446 smb2_util_lease_state(test
.type
));
449 smb2_util_close(tree
, *h
);
451 smb2_util_unlink(tree
, fname
);
452 talloc_free(mem_ctx
);
457 static bool test_durable_v2_open_lease_table(struct torture_context
*tctx
,
458 struct smb2_tree
*tree
,
460 bool request_persistent
,
461 struct durable_open_vs_lease
*table
,
467 smb2_util_unlink(tree
, fname
);
469 for (i
= 0; i
< num_tests
; i
++) {
470 ret
= test_one_durable_v2_open_lease(tctx
,
481 smb2_util_unlink(tree
, fname
);
486 bool test_durable_v2_open_lease(struct torture_context
*tctx
,
487 struct smb2_tree
*tree
)
493 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
494 if (!(caps
& SMB2_CAP_LEASING
)) {
495 torture_skip(tctx
, "leases are not supported");
498 /* Choose a random name in case the state is left a little funky. */
499 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
501 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
502 false, /* request_persistent */
503 durable_open_vs_lease_table
,
504 NUM_LEASE_OPEN_TESTS
);
511 * basic test for doing a durable open
512 * and do a durable reopen on the same connection
513 * while the first open is still active (fails)
515 bool test_durable_v2_open_reopen1(struct torture_context
*tctx
,
516 struct smb2_tree
*tree
)
519 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
521 struct smb2_handle _h
;
522 struct smb2_handle
*h
= NULL
;
523 struct smb2_create io
;
524 struct GUID create_guid
= GUID_random();
527 /* Choose a random name in case the state is left a little funky. */
528 snprintf(fname
, 256, "durable_v2_open_reopen1_%s.dat",
529 generate_random_str(tctx
, 8));
531 smb2_util_unlink(tree
, fname
);
533 smb2_oplock_create_share(&io
, fname
,
534 smb2_util_share_access(""),
535 smb2_util_oplock_level("b"));
536 io
.in
.durable_open
= false;
537 io
.in
.durable_open_v2
= true;
538 io
.in
.persistent_open
= false;
539 io
.in
.create_guid
= create_guid
;
540 io
.in
.timeout
= UINT32_MAX
;
542 status
= smb2_create(tree
, mem_ctx
, &io
);
543 CHECK_STATUS(status
, NT_STATUS_OK
);
544 _h
= io
.out
.file
.handle
;
546 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
547 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
548 CHECK_VAL(io
.out
.durable_open
, false);
549 CHECK_VAL(io
.out
.durable_open_v2
, true);
550 CHECK_VAL(io
.out
.persistent_open
, false);
551 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
553 /* try a durable reconnect while the file is still open */
556 io
.in
.durable_handle_v2
= h
;
557 io
.in
.create_guid
= create_guid
;
558 status
= smb2_create(tree
, mem_ctx
, &io
);
559 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
563 smb2_util_close(tree
, *h
);
566 smb2_util_unlink(tree
, fname
);
570 talloc_free(mem_ctx
);
576 * Basic test for doing a durable open
577 * and do a session reconnect while the first
578 * session is still active and the handle is
579 * still open in the client.
580 * This closes the original session and a
581 * durable reconnect on the new session succeeds.
583 bool test_durable_v2_open_reopen1a(struct torture_context
*tctx
,
584 struct smb2_tree
*tree
)
587 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
589 struct smb2_handle _h
;
590 struct smb2_handle
*h
= NULL
;
591 struct smb2_create io
, io2
;
592 struct GUID create_guid
= GUID_random();
594 struct smb2_tree
*tree2
= NULL
;
595 uint64_t previous_session_id
;
596 struct smbcli_options options
;
598 options
= tree
->session
->transport
->options
;
600 /* Choose a random name in case the state is left a little funky. */
601 snprintf(fname
, 256, "durable_v2_open_reopen1a_%s.dat",
602 generate_random_str(tctx
, 8));
604 smb2_util_unlink(tree
, fname
);
606 smb2_oplock_create_share(&io
, fname
,
607 smb2_util_share_access(""),
608 smb2_util_oplock_level("b"));
609 io
.in
.durable_open
= false;
610 io
.in
.durable_open_v2
= true;
611 io
.in
.persistent_open
= false;
612 io
.in
.create_guid
= create_guid
;
613 io
.in
.timeout
= UINT32_MAX
;
615 status
= smb2_create(tree
, mem_ctx
, &io
);
616 CHECK_STATUS(status
, NT_STATUS_OK
);
617 _h
= io
.out
.file
.handle
;
619 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
620 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
621 CHECK_VAL(io
.out
.durable_open
, false);
622 CHECK_VAL(io
.out
.durable_open_v2
, true);
623 CHECK_VAL(io
.out
.persistent_open
, false);
624 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
627 * a session reconnect on a second tcp connection
630 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
632 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
635 torture_warning(tctx
, "couldn't reconnect, bailing\n");
641 * check that this has deleted the old session
646 io
.in
.durable_handle_v2
= h
;
647 io
.in
.create_guid
= create_guid
;
648 status
= smb2_create(tree
, mem_ctx
, &io
);
649 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
652 * but a durable reconnect on the new session succeeds:
657 io2
.in
.durable_handle_v2
= h
;
658 io2
.in
.create_guid
= create_guid
;
659 status
= smb2_create(tree2
, mem_ctx
, &io2
);
660 CHECK_STATUS(status
, NT_STATUS_OK
);
661 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
662 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
663 CHECK_VAL(io2
.out
.durable_open
, false);
664 CHECK_VAL(io2
.out
.durable_open_v2
, false); /* no dh2q response blob */
665 CHECK_VAL(io2
.out
.persistent_open
, false);
666 CHECK_VAL(io2
.out
.timeout
, io
.in
.timeout
);
667 _h
= io2
.out
.file
.handle
;
672 smb2_util_close(tree
, *h
);
675 smb2_util_unlink(tree
, fname
);
679 talloc_free(mem_ctx
);
685 * basic test for doing a durable open
686 * tcp disconnect, reconnect, do a durable reopen (succeeds)
688 bool test_durable_v2_open_reopen2(struct torture_context
*tctx
,
689 struct smb2_tree
*tree
)
692 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
694 struct smb2_handle _h
;
695 struct smb2_handle
*h
= NULL
;
696 struct smb2_create io
;
697 struct GUID create_guid
= GUID_random();
698 struct GUID create_guid_invalid
= GUID_random();
701 /* Choose a random name in case the state is left a little funky. */
702 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
703 generate_random_str(tctx
, 8));
705 smb2_util_unlink(tree
, fname
);
707 smb2_oplock_create_share(&io
, fname
,
708 smb2_util_share_access(""),
709 smb2_util_oplock_level("b"));
710 io
.in
.durable_open
= false;
711 io
.in
.durable_open_v2
= true;
712 io
.in
.persistent_open
= false;
713 io
.in
.create_guid
= create_guid
;
714 io
.in
.timeout
= UINT32_MAX
;
716 status
= smb2_create(tree
, mem_ctx
, &io
);
717 CHECK_STATUS(status
, NT_STATUS_OK
);
718 _h
= io
.out
.file
.handle
;
720 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
721 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
722 CHECK_VAL(io
.out
.durable_open
, false);
723 CHECK_VAL(io
.out
.durable_open_v2
, true);
724 CHECK_VAL(io
.out
.persistent_open
, false);
725 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
727 /* disconnect, leaving the durable open */
730 if (!torture_smb2_connection(tctx
, &tree
)) {
731 torture_warning(tctx
, "couldn't reconnect, bailing\n");
737 * first a few failure cases
742 io
.in
.durable_handle_v2
= h
;
743 status
= smb2_create(tree
, mem_ctx
, &io
);
744 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
747 io
.in
.fname
= "__non_existing_fname__";
748 io
.in
.durable_handle_v2
= h
;
749 status
= smb2_create(tree
, mem_ctx
, &io
);
750 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
754 io
.in
.durable_handle_v2
= h
;
755 status
= smb2_create(tree
, mem_ctx
, &io
);
756 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
758 /* a non-zero but non-matching create_guid does not change it: */
761 io
.in
.durable_handle_v2
= h
;
762 io
.in
.create_guid
= create_guid_invalid
;
763 status
= smb2_create(tree
, mem_ctx
, &io
);
764 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
768 * The important difference is that the create_guid is provided.
772 io
.in
.durable_open_v2
= false;
773 io
.in
.durable_handle_v2
= h
;
774 io
.in
.create_guid
= create_guid
;
777 status
= smb2_create(tree
, mem_ctx
, &io
);
778 CHECK_STATUS(status
, NT_STATUS_OK
);
779 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
780 CHECK_VAL(io
.out
.durable_open
, false);
781 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
782 CHECK_VAL(io
.out
.persistent_open
, false);
783 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
784 _h
= io
.out
.file
.handle
;
787 /* disconnect one more time */
790 if (!torture_smb2_connection(tctx
, &tree
)) {
791 torture_warning(tctx
, "couldn't reconnect, bailing\n");
797 /* These are completely ignored by the server */
798 io
.in
.security_flags
= 0x78;
799 io
.in
.oplock_level
= 0x78;
800 io
.in
.impersonation_level
= 0x12345678;
801 io
.in
.create_flags
= 0x12345678;
802 io
.in
.reserved
= 0x12345678;
803 io
.in
.desired_access
= 0x12345678;
804 io
.in
.file_attributes
= 0x12345678;
805 io
.in
.share_access
= 0x12345678;
806 io
.in
.create_disposition
= 0x12345678;
807 io
.in
.create_options
= 0x12345678;
808 io
.in
.fname
= "__non_existing_fname__";
811 * only io.in.durable_handle_v2 and
812 * io.in.create_guid are checked
814 io
.in
.durable_open_v2
= false;
815 io
.in
.durable_handle_v2
= h
;
816 io
.in
.create_guid
= create_guid
;
819 status
= smb2_create(tree
, mem_ctx
, &io
);
820 CHECK_STATUS(status
, NT_STATUS_OK
);
821 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
822 CHECK_VAL(io
.out
.durable_open
, false);
823 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
824 CHECK_VAL(io
.out
.persistent_open
, false);
825 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
826 _h
= io
.out
.file
.handle
;
831 smb2_util_close(tree
, *h
);
834 smb2_util_unlink(tree
, fname
);
838 talloc_free(mem_ctx
);
844 * durable reconnect test:
845 * connect with v2, reconnect with v1
847 bool test_durable_v2_open_reopen2b(struct torture_context
*tctx
,
848 struct smb2_tree
*tree
)
851 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
853 struct smb2_handle _h
;
854 struct smb2_handle
*h
= NULL
;
855 struct smb2_create io
;
856 struct GUID create_guid
= GUID_random();
858 struct smbcli_options options
;
860 options
= tree
->session
->transport
->options
;
862 /* Choose a random name in case the state is left a little funky. */
863 snprintf(fname
, 256, "durable_v2_open_reopen2b_%s.dat",
864 generate_random_str(tctx
, 8));
866 smb2_util_unlink(tree
, fname
);
868 smb2_oplock_create_share(&io
, fname
,
869 smb2_util_share_access(""),
870 smb2_util_oplock_level("b"));
871 io
.in
.durable_open
= false;
872 io
.in
.durable_open_v2
= true;
873 io
.in
.persistent_open
= false;
874 io
.in
.create_guid
= create_guid
;
875 io
.in
.timeout
= UINT32_MAX
;
877 status
= smb2_create(tree
, mem_ctx
, &io
);
878 CHECK_STATUS(status
, NT_STATUS_OK
);
879 _h
= io
.out
.file
.handle
;
881 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
882 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
883 CHECK_VAL(io
.out
.durable_open
, false);
884 CHECK_VAL(io
.out
.durable_open_v2
, true);
885 CHECK_VAL(io
.out
.persistent_open
, false);
886 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
888 /* disconnect, leaving the durable open */
891 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
892 torture_warning(tctx
, "couldn't reconnect, bailing\n");
899 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
900 io
.in
.create_guid
= GUID_zero(); /* but zero create GUID */
901 status
= smb2_create(tree
, mem_ctx
, &io
);
902 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
906 io
.in
.durable_handle
= h
; /* durable v1 (!) reconnect */
909 status
= smb2_create(tree
, mem_ctx
, &io
);
910 CHECK_STATUS(status
, NT_STATUS_OK
);
911 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
912 CHECK_VAL(io
.out
.durable_open
, false);
913 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
914 CHECK_VAL(io
.out
.persistent_open
, false);
915 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
916 _h
= io
.out
.file
.handle
;
921 smb2_util_close(tree
, *h
);
924 smb2_util_unlink(tree
, fname
);
928 talloc_free(mem_ctx
);
933 * durable reconnect test:
934 * connect with v1, reconnect with v2 : fails (no create_guid...)
936 bool test_durable_v2_open_reopen2c(struct torture_context
*tctx
,
937 struct smb2_tree
*tree
)
940 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
942 struct smb2_handle _h
;
943 struct smb2_handle
*h
= NULL
;
944 struct smb2_create io
;
945 struct GUID create_guid
= GUID_random();
947 struct smbcli_options options
;
949 options
= tree
->session
->transport
->options
;
951 /* Choose a random name in case the state is left a little funky. */
952 snprintf(fname
, 256, "durable_v2_open_reopen2c_%s.dat",
953 generate_random_str(tctx
, 8));
955 smb2_util_unlink(tree
, fname
);
957 smb2_oplock_create_share(&io
, fname
,
958 smb2_util_share_access(""),
959 smb2_util_oplock_level("b"));
960 io
.in
.durable_open
= true;
961 io
.in
.durable_open_v2
= false;
962 io
.in
.persistent_open
= false;
963 io
.in
.create_guid
= create_guid
;
964 io
.in
.timeout
= UINT32_MAX
;
966 status
= smb2_create(tree
, mem_ctx
, &io
);
967 CHECK_STATUS(status
, NT_STATUS_OK
);
968 _h
= io
.out
.file
.handle
;
970 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
971 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
972 CHECK_VAL(io
.out
.durable_open
, true);
973 CHECK_VAL(io
.out
.durable_open_v2
, false);
974 CHECK_VAL(io
.out
.persistent_open
, false);
975 CHECK_VAL(io
.out
.timeout
, 0);
977 /* disconnect, leaving the durable open */
980 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
981 torture_warning(tctx
, "couldn't reconnect, bailing\n");
988 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
989 io
.in
.create_guid
= create_guid
;
990 status
= smb2_create(tree
, mem_ctx
, &io
);
991 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
995 smb2_util_close(tree
, *h
);
998 smb2_util_unlink(tree
, fname
);
1002 talloc_free(mem_ctx
);
1008 * lease variant of reopen2
1009 * basic test for doing a durable open
1010 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1012 bool test_durable_v2_open_reopen2_lease(struct torture_context
*tctx
,
1013 struct smb2_tree
*tree
)
1016 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1018 struct smb2_handle _h
;
1019 struct smb2_handle
*h
= NULL
;
1020 struct smb2_create io
;
1021 struct GUID create_guid
= GUID_random();
1022 struct smb2_lease ls
;
1025 struct smbcli_options options
;
1028 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1029 if (!(caps
& SMB2_CAP_LEASING
)) {
1030 torture_skip(tctx
, "leases are not supported");
1033 options
= tree
->session
->transport
->options
;
1035 /* Choose a random name in case the state is left a little funky. */
1036 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
1037 generate_random_str(tctx
, 8));
1039 smb2_util_unlink(tree
, fname
);
1041 lease_key
= random();
1042 smb2_lease_create(&io
, &ls
, false /* dir */, fname
,
1043 lease_key
, smb2_util_lease_state("RWH"));
1044 io
.in
.durable_open
= false;
1045 io
.in
.durable_open_v2
= true;
1046 io
.in
.persistent_open
= false;
1047 io
.in
.create_guid
= create_guid
;
1048 io
.in
.timeout
= UINT32_MAX
;
1050 status
= smb2_create(tree
, mem_ctx
, &io
);
1051 CHECK_STATUS(status
, NT_STATUS_OK
);
1052 _h
= io
.out
.file
.handle
;
1054 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1055 CHECK_VAL(io
.out
.durable_open
, false);
1056 CHECK_VAL(io
.out
.durable_open_v2
, true);
1057 CHECK_VAL(io
.out
.persistent_open
, false);
1058 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1059 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1060 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1061 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1062 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1063 smb2_util_lease_state("RWH"));
1064 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1065 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1067 /* disconnect, reconnect and then do durable reopen */
1070 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1071 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1076 /* a few failure tests: */
1079 * several attempts without lease attached:
1080 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1081 * irrespective of file name provided
1086 io
.in
.durable_handle_v2
= h
;
1087 status
= smb2_create(tree
, mem_ctx
, &io
);
1088 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1091 io
.in
.fname
= "__non_existing_fname__";
1092 io
.in
.durable_handle_v2
= h
;
1093 status
= smb2_create(tree
, mem_ctx
, &io
);
1094 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1097 io
.in
.fname
= fname
;
1098 io
.in
.durable_handle_v2
= h
;
1099 status
= smb2_create(tree
, mem_ctx
, &io
);
1100 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1103 * attempt with lease provided, but
1104 * with a changed lease key. => fails
1107 io
.in
.fname
= fname
;
1108 io
.in
.durable_open_v2
= false;
1109 io
.in
.durable_handle_v2
= h
;
1110 io
.in
.create_guid
= create_guid
;
1111 io
.in
.lease_request
= &ls
;
1112 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1113 /* a wrong lease key lets the request fail */
1114 ls
.lease_key
.data
[0]++;
1116 status
= smb2_create(tree
, mem_ctx
, &io
);
1117 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1119 /* restore the correct lease key */
1120 ls
.lease_key
.data
[0]--;
1123 * this last failing attempt is almost correct:
1124 * only problem is: we use the wrong filename...
1125 * Note that this gives INVALID_PARAMETER.
1126 * This is different from oplocks!
1129 io
.in
.fname
= "__non_existing_fname__";
1130 io
.in
.durable_open_v2
= false;
1131 io
.in
.durable_handle_v2
= h
;
1132 io
.in
.create_guid
= create_guid
;
1133 io
.in
.lease_request
= &ls
;
1134 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1136 status
= smb2_create(tree
, mem_ctx
, &io
);
1137 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1140 * Now for a succeeding reconnect:
1144 io
.in
.fname
= fname
;
1145 io
.in
.durable_open_v2
= false;
1146 io
.in
.durable_handle_v2
= h
;
1147 io
.in
.create_guid
= create_guid
;
1148 io
.in
.lease_request
= &ls
;
1149 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1151 /* the requested lease state is irrelevant */
1152 ls
.lease_state
= smb2_util_lease_state("");
1156 status
= smb2_create(tree
, mem_ctx
, &io
);
1157 CHECK_STATUS(status
, NT_STATUS_OK
);
1159 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1160 CHECK_VAL(io
.out
.durable_open
, false);
1161 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1162 CHECK_VAL(io
.out
.persistent_open
, false);
1163 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1164 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1165 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1166 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1167 smb2_util_lease_state("RWH"));
1168 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1169 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1170 _h
= io
.out
.file
.handle
;
1173 /* disconnect one more time */
1176 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1177 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1183 * demonstrate that various parameters are ignored
1189 * These are completely ignored by the server
1191 io
.in
.security_flags
= 0x78;
1192 io
.in
.oplock_level
= 0x78;
1193 io
.in
.impersonation_level
= 0x12345678;
1194 io
.in
.create_flags
= 0x12345678;
1195 io
.in
.reserved
= 0x12345678;
1196 io
.in
.desired_access
= 0x12345678;
1197 io
.in
.file_attributes
= 0x12345678;
1198 io
.in
.share_access
= 0x12345678;
1199 io
.in
.create_disposition
= 0x12345678;
1200 io
.in
.create_options
= 0x12345678;
1203 * only these are checked:
1205 * - io.in.durable_handle_v2,
1206 * - io.in.create_guid
1207 * - io.in.lease_request->lease_key
1210 io
.in
.fname
= fname
;
1211 io
.in
.durable_open_v2
= false;
1212 io
.in
.durable_handle_v2
= h
;
1213 io
.in
.create_guid
= create_guid
;
1214 io
.in
.lease_request
= &ls
;
1216 /* the requested lease state is irrelevant */
1217 ls
.lease_state
= smb2_util_lease_state("");
1221 status
= smb2_create(tree
, mem_ctx
, &io
);
1222 CHECK_STATUS(status
, NT_STATUS_OK
);
1224 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1225 CHECK_VAL(io
.out
.durable_open
, false);
1226 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1227 CHECK_VAL(io
.out
.persistent_open
, false);
1228 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1229 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1230 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1231 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1232 smb2_util_lease_state("RWH"));
1233 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1234 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1236 _h
= io
.out
.file
.handle
;
1241 smb2_util_close(tree
, *h
);
1244 smb2_util_unlink(tree
, fname
);
1248 talloc_free(mem_ctx
);
1254 * lease_v2 variant of reopen2
1255 * basic test for doing a durable open
1256 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1258 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context
*tctx
,
1259 struct smb2_tree
*tree
)
1262 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1264 struct smb2_handle _h
;
1265 struct smb2_handle
*h
= NULL
;
1266 struct smb2_create io
;
1267 struct GUID create_guid
= GUID_random();
1268 struct smb2_lease ls
;
1271 struct smbcli_options options
;
1274 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1275 if (!(caps
& SMB2_CAP_LEASING
)) {
1276 torture_skip(tctx
, "leases are not supported");
1279 options
= tree
->session
->transport
->options
;
1281 /* Choose a random name in case the state is left a little funky. */
1282 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
1283 generate_random_str(tctx
, 8));
1285 smb2_util_unlink(tree
, fname
);
1287 lease_key
= random();
1288 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
1289 lease_key
, 0, /* parent lease key */
1290 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1291 io
.in
.durable_open
= false;
1292 io
.in
.durable_open_v2
= true;
1293 io
.in
.persistent_open
= false;
1294 io
.in
.create_guid
= create_guid
;
1295 io
.in
.timeout
= UINT32_MAX
;
1297 status
= smb2_create(tree
, mem_ctx
, &io
);
1298 CHECK_STATUS(status
, NT_STATUS_OK
);
1299 _h
= io
.out
.file
.handle
;
1301 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1302 CHECK_VAL(io
.out
.durable_open
, false);
1303 CHECK_VAL(io
.out
.durable_open_v2
, true);
1304 CHECK_VAL(io
.out
.persistent_open
, false);
1305 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1306 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1307 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1308 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1310 /* disconnect, reconnect and then do durable reopen */
1313 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1314 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1319 /* a few failure tests: */
1322 * several attempts without lease attached:
1323 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1324 * irrespective of file name provided
1329 io
.in
.durable_handle_v2
= h
;
1330 status
= smb2_create(tree
, mem_ctx
, &io
);
1331 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1334 io
.in
.fname
= "__non_existing_fname__";
1335 io
.in
.durable_handle_v2
= h
;
1336 status
= smb2_create(tree
, mem_ctx
, &io
);
1337 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1340 io
.in
.fname
= fname
;
1341 io
.in
.durable_handle_v2
= h
;
1342 status
= smb2_create(tree
, mem_ctx
, &io
);
1343 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1346 * attempt with lease provided, but
1347 * with a changed lease key. => fails
1350 io
.in
.fname
= fname
;
1351 io
.in
.durable_open_v2
= false;
1352 io
.in
.durable_handle_v2
= h
;
1353 io
.in
.create_guid
= create_guid
;
1354 io
.in
.lease_request_v2
= &ls
;
1355 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1356 /* a wrong lease key lets the request fail */
1357 ls
.lease_key
.data
[0]++;
1359 status
= smb2_create(tree
, mem_ctx
, &io
);
1360 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1362 /* restore the correct lease key */
1363 ls
.lease_key
.data
[0]--;
1367 * this last failing attempt is almost correct:
1368 * only problem is: we use the wrong filename...
1369 * Note that this gives INVALID_PARAMETER.
1370 * This is different from oplocks!
1373 io
.in
.fname
= "__non_existing_fname__";
1374 io
.in
.durable_open_v2
= false;
1375 io
.in
.durable_handle_v2
= h
;
1376 io
.in
.create_guid
= create_guid
;
1377 io
.in
.lease_request_v2
= &ls
;
1378 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1380 status
= smb2_create(tree
, mem_ctx
, &io
);
1381 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1384 * Now for a succeeding reconnect:
1388 io
.in
.fname
= fname
;
1389 io
.in
.durable_open_v2
= false;
1390 io
.in
.durable_handle_v2
= h
;
1391 io
.in
.create_guid
= create_guid
;
1392 io
.in
.lease_request_v2
= &ls
;
1393 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1395 /* the requested lease state is irrelevant */
1396 ls
.lease_state
= smb2_util_lease_state("");
1400 status
= smb2_create(tree
, mem_ctx
, &io
);
1401 CHECK_STATUS(status
, NT_STATUS_OK
);
1403 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1404 CHECK_VAL(io
.out
.durable_open
, false);
1405 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1406 CHECK_VAL(io
.out
.persistent_open
, false);
1407 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1408 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1409 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1410 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1411 smb2_util_lease_state("RWH"));
1412 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1413 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1414 _h
= io
.out
.file
.handle
;
1417 /* disconnect one more time */
1420 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1421 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1427 * demonstrate that various parameters are ignored
1433 * These are completely ignored by the server
1435 io
.in
.security_flags
= 0x78;
1436 io
.in
.oplock_level
= 0x78;
1437 io
.in
.impersonation_level
= 0x12345678;
1438 io
.in
.create_flags
= 0x12345678;
1439 io
.in
.reserved
= 0x12345678;
1440 io
.in
.desired_access
= 0x12345678;
1441 io
.in
.file_attributes
= 0x12345678;
1442 io
.in
.share_access
= 0x12345678;
1443 io
.in
.create_disposition
= 0x12345678;
1444 io
.in
.create_options
= 0x12345678;
1445 io
.in
.fname
= "__non_existing_fname__";
1448 * only these are checked:
1450 * - io.in.durable_handle_v2,
1451 * - io.in.create_guid
1452 * - io.in.lease_request_v2->lease_key
1455 io
.in
.fname
= fname
;
1456 io
.in
.durable_open_v2
= false;
1457 io
.in
.durable_handle_v2
= h
;
1458 io
.in
.create_guid
= create_guid
;
1459 io
.in
.lease_request_v2
= &ls
;
1461 /* the requested lease state is irrelevant */
1462 ls
.lease_state
= smb2_util_lease_state("");
1466 status
= smb2_create(tree
, mem_ctx
, &io
);
1467 CHECK_STATUS(status
, NT_STATUS_OK
);
1468 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1469 CHECK_VAL(io
.out
.durable_open
, false);
1470 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1471 CHECK_VAL(io
.out
.persistent_open
, false);
1472 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1473 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1474 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1475 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1476 smb2_util_lease_state("RWH"));
1477 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1478 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1480 _h
= io
.out
.file
.handle
;
1485 smb2_util_close(tree
, *h
);
1488 smb2_util_unlink(tree
, fname
);
1492 talloc_free(mem_ctx
);
1498 * Test durable request / reconnect with AppInstanceId
1500 bool test_durable_v2_open_app_instance(struct torture_context
*tctx
,
1501 struct smb2_tree
*tree1
,
1502 struct smb2_tree
*tree2
)
1505 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1507 struct smb2_handle _h1
, _h2
;
1508 struct smb2_handle
*h1
= NULL
, *h2
= NULL
;
1509 struct smb2_create io1
, io2
;
1511 struct GUID create_guid_1
= GUID_random();
1512 struct GUID create_guid_2
= GUID_random();
1513 struct GUID app_instance_id
= GUID_random();
1515 /* Choose a random name in case the state is left a little funky. */
1516 snprintf(fname
, 256, "durable_v2_open_app_instance_%s.dat",
1517 generate_random_str(tctx
, 8));
1519 smb2_util_unlink(tree1
, fname
);
1521 ZERO_STRUCT(break_info
);
1522 tree1
->session
->transport
->oplock
.handler
= torture_oplock_handler
;
1523 tree1
->session
->transport
->oplock
.private_data
= tree1
;
1525 smb2_oplock_create_share(&io1
, fname
,
1526 smb2_util_share_access(""),
1527 smb2_util_oplock_level("b"));
1528 io1
.in
.durable_open
= false;
1529 io1
.in
.durable_open_v2
= true;
1530 io1
.in
.persistent_open
= false;
1531 io1
.in
.create_guid
= create_guid_1
;
1532 io1
.in
.app_instance_id
= &app_instance_id
;
1533 io1
.in
.timeout
= UINT32_MAX
;
1535 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1536 CHECK_STATUS(status
, NT_STATUS_OK
);
1537 _h1
= io1
.out
.file
.handle
;
1539 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1540 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1541 CHECK_VAL(io1
.out
.durable_open
, false);
1542 CHECK_VAL(io1
.out
.durable_open_v2
, true);
1543 CHECK_VAL(io1
.out
.persistent_open
, false);
1544 CHECK_VAL(io1
.out
.timeout
, io1
.in
.timeout
);
1547 * try to open the file as durable from a second tree with
1548 * a different create guid but the same app_instance_id
1549 * while the first handle is still open.
1552 smb2_oplock_create_share(&io2
, fname
,
1553 smb2_util_share_access(""),
1554 smb2_util_oplock_level("b"));
1555 io2
.in
.durable_open
= false;
1556 io2
.in
.durable_open_v2
= true;
1557 io2
.in
.persistent_open
= false;
1558 io2
.in
.create_guid
= create_guid_2
;
1559 io2
.in
.app_instance_id
= &app_instance_id
;
1560 io2
.in
.timeout
= UINT32_MAX
;
1562 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1563 CHECK_STATUS(status
, NT_STATUS_OK
);
1564 _h2
= io2
.out
.file
.handle
;
1566 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1567 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1568 CHECK_VAL(io2
.out
.durable_open
, false);
1569 CHECK_VAL(io2
.out
.durable_open_v2
, true);
1570 CHECK_VAL(io2
.out
.persistent_open
, false);
1571 CHECK_VAL(io2
.out
.timeout
, io2
.in
.timeout
);
1573 CHECK_VAL(break_info
.count
, 0);
1575 status
= smb2_util_close(tree1
, *h1
);
1576 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1581 smb2_util_close(tree1
, *h1
);
1584 smb2_util_close(tree2
, *h2
);
1587 smb2_util_unlink(tree2
, fname
);
1592 talloc_free(mem_ctx
);
1599 * basic persistent open test.
1601 * This test tests durable open with all possible oplock types.
1604 struct durable_open_vs_oplock persistent_open_oplock_ca_table
[NUM_OPLOCK_OPEN_TESTS
] =
1606 { "", "", true, true },
1607 { "", "R", true, true },
1608 { "", "W", true, true },
1609 { "", "D", true, true },
1610 { "", "RD", true, true },
1611 { "", "RW", true, true },
1612 { "", "WD", true, true },
1613 { "", "RWD", true, true },
1615 { "s", "", true, true },
1616 { "s", "R", true, true },
1617 { "s", "W", true, true },
1618 { "s", "D", true, true },
1619 { "s", "RD", true, true },
1620 { "s", "RW", true, true },
1621 { "s", "WD", true, true },
1622 { "s", "RWD", true, true },
1624 { "x", "", true, true },
1625 { "x", "R", true, true },
1626 { "x", "W", true, true },
1627 { "x", "D", true, true },
1628 { "x", "RD", true, true },
1629 { "x", "RW", true, true },
1630 { "x", "WD", true, true },
1631 { "x", "RWD", true, true },
1633 { "b", "", true, true },
1634 { "b", "R", true, true },
1635 { "b", "W", true, true },
1636 { "b", "D", true, true },
1637 { "b", "RD", true, true },
1638 { "b", "RW", true, true },
1639 { "b", "WD", true, true },
1640 { "b", "RWD", true, true },
1643 bool test_persistent_open_oplock(struct torture_context
*tctx
,
1644 struct smb2_tree
*tree
)
1648 uint32_t share_capabilities
;
1649 bool share_is_ca
= false;
1650 struct durable_open_vs_oplock
*table
;
1652 /* Choose a random name in case the state is left a little funky. */
1653 snprintf(fname
, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1655 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1656 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1659 table
= persistent_open_oplock_ca_table
;
1661 table
= durable_open_vs_oplock_table
;
1664 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
1665 true, /* request_persistent */
1667 NUM_OPLOCK_OPEN_TESTS
);
1675 * basic persistent handle open test.
1676 * persistent state should only be granted when requested
1677 * along with a batch oplock or a handle lease.
1679 * This test tests persistent open with all valid lease types.
1682 struct durable_open_vs_lease persistent_open_lease_ca_table
[NUM_LEASE_OPEN_TESTS
] =
1684 { "", "", true, true },
1685 { "", "R", true, true },
1686 { "", "W", true, true },
1687 { "", "D", true, true },
1688 { "", "RW", true, true },
1689 { "", "RD", true, true },
1690 { "", "WD", true, true },
1691 { "", "RWD", true, true },
1693 { "R", "", true, true },
1694 { "R", "R", true, true },
1695 { "R", "W", true, true },
1696 { "R", "D", true, true },
1697 { "R", "RW", true, true },
1698 { "R", "RD", true, true },
1699 { "R", "DW", true, true },
1700 { "R", "RWD", true, true },
1702 { "RW", "", true, true },
1703 { "RW", "R", true, true },
1704 { "RW", "W", true, true },
1705 { "RW", "D", true, true },
1706 { "RW", "RW", true, true },
1707 { "RW", "RD", true, true },
1708 { "RW", "WD", true, true },
1709 { "RW", "RWD", true, true },
1711 { "RH", "", true, true },
1712 { "RH", "R", true, true },
1713 { "RH", "W", true, true },
1714 { "RH", "D", true, true },
1715 { "RH", "RW", true, true },
1716 { "RH", "RD", true, true },
1717 { "RH", "WD", true, true },
1718 { "RH", "RWD", true, true },
1720 { "RHW", "", true, true },
1721 { "RHW", "R", true, true },
1722 { "RHW", "W", true, true },
1723 { "RHW", "D", true, true },
1724 { "RHW", "RW", true, true },
1725 { "RHW", "RD", true, true },
1726 { "RHW", "WD", true, true },
1727 { "RHW", "RWD", true, true },
1730 bool test_persistent_open_lease(struct torture_context
*tctx
,
1731 struct smb2_tree
*tree
)
1736 uint32_t share_capabilities
;
1738 struct durable_open_vs_lease
*table
;
1740 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1741 if (!(caps
& SMB2_CAP_LEASING
)) {
1742 torture_skip(tctx
, "leases are not supported");
1745 /* Choose a random name in case the state is left a little funky. */
1746 snprintf(fname
, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx
, 8));
1748 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1749 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1752 table
= persistent_open_lease_ca_table
;
1754 table
= durable_open_vs_lease_table
;
1757 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
1758 true, /* request_persistent */
1760 NUM_LEASE_OPEN_TESTS
);
1767 struct torture_suite
*torture_smb2_durable_v2_open_init(void)
1769 struct torture_suite
*suite
=
1770 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1772 torture_suite_add_1smb2_test(suite
, "create-blob", test_durable_v2_open_create_blob
);
1773 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_v2_open_oplock
);
1774 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_v2_open_lease
);
1775 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_v2_open_reopen1
);
1776 torture_suite_add_1smb2_test(suite
, "reopen1a", test_durable_v2_open_reopen1a
);
1777 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_v2_open_reopen2
);
1778 torture_suite_add_1smb2_test(suite
, "reopen2b", test_durable_v2_open_reopen2b
);
1779 torture_suite_add_1smb2_test(suite
, "reopen2c", test_durable_v2_open_reopen2c
);
1780 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_v2_open_reopen2_lease
);
1781 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2
);
1782 torture_suite_add_2smb2_test(suite
, "app-instance", test_durable_v2_open_app_instance
);
1783 torture_suite_add_1smb2_test(suite
, "persistent-open-oplock", test_persistent_open_oplock
);
1784 torture_suite_add_1smb2_test(suite
, "persistent-open-lease", test_persistent_open_lease
);
1786 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-V2-OPEN tests");