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 * tcp disconnect, reconnect, do a durable reopen (succeeds)
579 bool test_durable_v2_open_reopen2(struct torture_context
*tctx
,
580 struct smb2_tree
*tree
)
583 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
585 struct smb2_handle _h
;
586 struct smb2_handle
*h
= NULL
;
587 struct smb2_create io
;
588 struct GUID create_guid
= GUID_random();
589 struct GUID create_guid_invalid
= GUID_random();
592 /* Choose a random name in case the state is left a little funky. */
593 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
594 generate_random_str(tctx
, 8));
596 smb2_util_unlink(tree
, fname
);
598 smb2_oplock_create_share(&io
, fname
,
599 smb2_util_share_access(""),
600 smb2_util_oplock_level("b"));
601 io
.in
.durable_open
= false;
602 io
.in
.durable_open_v2
= true;
603 io
.in
.persistent_open
= false;
604 io
.in
.create_guid
= create_guid
;
605 io
.in
.timeout
= UINT32_MAX
;
607 status
= smb2_create(tree
, mem_ctx
, &io
);
608 CHECK_STATUS(status
, NT_STATUS_OK
);
609 _h
= io
.out
.file
.handle
;
611 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
612 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
613 CHECK_VAL(io
.out
.durable_open
, false);
614 CHECK_VAL(io
.out
.durable_open_v2
, true);
615 CHECK_VAL(io
.out
.persistent_open
, false);
616 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
618 /* disconnect, leaving the durable open */
621 if (!torture_smb2_connection(tctx
, &tree
)) {
622 torture_warning(tctx
, "couldn't reconnect, bailing\n");
628 * first a few failure cases
633 io
.in
.durable_handle_v2
= h
;
634 status
= smb2_create(tree
, mem_ctx
, &io
);
635 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
638 io
.in
.fname
= "__non_existing_fname__";
639 io
.in
.durable_handle_v2
= h
;
640 status
= smb2_create(tree
, mem_ctx
, &io
);
641 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
645 io
.in
.durable_handle_v2
= h
;
646 status
= smb2_create(tree
, mem_ctx
, &io
);
647 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
649 /* a non-zero but non-matching create_guid does not change it: */
652 io
.in
.durable_handle_v2
= h
;
653 io
.in
.create_guid
= create_guid_invalid
;
654 status
= smb2_create(tree
, mem_ctx
, &io
);
655 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
659 * The important difference is that the create_guid is provided.
663 io
.in
.durable_open_v2
= false;
664 io
.in
.durable_handle_v2
= h
;
665 io
.in
.create_guid
= create_guid
;
668 status
= smb2_create(tree
, mem_ctx
, &io
);
669 CHECK_STATUS(status
, NT_STATUS_OK
);
670 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
671 CHECK_VAL(io
.out
.durable_open
, false);
672 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
673 CHECK_VAL(io
.out
.persistent_open
, false);
674 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
675 _h
= io
.out
.file
.handle
;
678 /* disconnect one more time */
681 if (!torture_smb2_connection(tctx
, &tree
)) {
682 torture_warning(tctx
, "couldn't reconnect, bailing\n");
688 /* These are completely ignored by the server */
689 io
.in
.security_flags
= 0x78;
690 io
.in
.oplock_level
= 0x78;
691 io
.in
.impersonation_level
= 0x12345678;
692 io
.in
.create_flags
= 0x12345678;
693 io
.in
.reserved
= 0x12345678;
694 io
.in
.desired_access
= 0x12345678;
695 io
.in
.file_attributes
= 0x12345678;
696 io
.in
.share_access
= 0x12345678;
697 io
.in
.create_disposition
= 0x12345678;
698 io
.in
.create_options
= 0x12345678;
699 io
.in
.fname
= "__non_existing_fname__";
702 * only io.in.durable_handle_v2 and
703 * io.in.create_guid are checked
705 io
.in
.durable_open_v2
= false;
706 io
.in
.durable_handle_v2
= h
;
707 io
.in
.create_guid
= create_guid
;
710 status
= smb2_create(tree
, mem_ctx
, &io
);
711 CHECK_STATUS(status
, NT_STATUS_OK
);
712 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
713 CHECK_VAL(io
.out
.durable_open
, false);
714 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
715 CHECK_VAL(io
.out
.persistent_open
, false);
716 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
717 _h
= io
.out
.file
.handle
;
722 smb2_util_close(tree
, *h
);
725 smb2_util_unlink(tree
, fname
);
729 talloc_free(mem_ctx
);
735 * durable reconnect test:
736 * connect with v2, reconnect with v1
738 bool test_durable_v2_open_reopen2b(struct torture_context
*tctx
,
739 struct smb2_tree
*tree
)
742 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
744 struct smb2_handle _h
;
745 struct smb2_handle
*h
= NULL
;
746 struct smb2_create io
;
747 struct GUID create_guid
= GUID_random();
749 struct smbcli_options options
;
751 options
= tree
->session
->transport
->options
;
753 /* Choose a random name in case the state is left a little funky. */
754 snprintf(fname
, 256, "durable_v2_open_reopen2b_%s.dat",
755 generate_random_str(tctx
, 8));
757 smb2_util_unlink(tree
, fname
);
759 smb2_oplock_create_share(&io
, fname
,
760 smb2_util_share_access(""),
761 smb2_util_oplock_level("b"));
762 io
.in
.durable_open
= false;
763 io
.in
.durable_open_v2
= true;
764 io
.in
.persistent_open
= false;
765 io
.in
.create_guid
= create_guid
;
766 io
.in
.timeout
= UINT32_MAX
;
768 status
= smb2_create(tree
, mem_ctx
, &io
);
769 CHECK_STATUS(status
, NT_STATUS_OK
);
770 _h
= io
.out
.file
.handle
;
772 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
773 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
774 CHECK_VAL(io
.out
.durable_open
, false);
775 CHECK_VAL(io
.out
.durable_open_v2
, true);
776 CHECK_VAL(io
.out
.persistent_open
, false);
777 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
779 /* disconnect, leaving the durable open */
782 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
783 torture_warning(tctx
, "couldn't reconnect, bailing\n");
790 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
791 io
.in
.create_guid
= GUID_zero(); /* but zero create GUID */
792 status
= smb2_create(tree
, mem_ctx
, &io
);
793 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
797 io
.in
.durable_handle
= h
; /* durable v1 (!) reconnect */
800 status
= smb2_create(tree
, mem_ctx
, &io
);
801 CHECK_STATUS(status
, NT_STATUS_OK
);
802 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
803 CHECK_VAL(io
.out
.durable_open
, false);
804 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
805 CHECK_VAL(io
.out
.persistent_open
, false);
806 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
807 _h
= io
.out
.file
.handle
;
812 smb2_util_close(tree
, *h
);
815 smb2_util_unlink(tree
, fname
);
819 talloc_free(mem_ctx
);
824 * durable reconnect test:
825 * connect with v1, reconnect with v2 : fails (no create_guid...)
827 bool test_durable_v2_open_reopen2c(struct torture_context
*tctx
,
828 struct smb2_tree
*tree
)
831 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
833 struct smb2_handle _h
;
834 struct smb2_handle
*h
= NULL
;
835 struct smb2_create io
;
836 struct GUID create_guid
= GUID_random();
838 struct smbcli_options options
;
840 options
= tree
->session
->transport
->options
;
842 /* Choose a random name in case the state is left a little funky. */
843 snprintf(fname
, 256, "durable_v2_open_reopen2c_%s.dat",
844 generate_random_str(tctx
, 8));
846 smb2_util_unlink(tree
, fname
);
848 smb2_oplock_create_share(&io
, fname
,
849 smb2_util_share_access(""),
850 smb2_util_oplock_level("b"));
851 io
.in
.durable_open
= true;
852 io
.in
.durable_open_v2
= false;
853 io
.in
.persistent_open
= false;
854 io
.in
.create_guid
= create_guid
;
855 io
.in
.timeout
= UINT32_MAX
;
857 status
= smb2_create(tree
, mem_ctx
, &io
);
858 CHECK_STATUS(status
, NT_STATUS_OK
);
859 _h
= io
.out
.file
.handle
;
861 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
862 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
863 CHECK_VAL(io
.out
.durable_open
, true);
864 CHECK_VAL(io
.out
.durable_open_v2
, false);
865 CHECK_VAL(io
.out
.persistent_open
, false);
866 CHECK_VAL(io
.out
.timeout
, 0);
868 /* disconnect, leaving the durable open */
871 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
872 torture_warning(tctx
, "couldn't reconnect, bailing\n");
879 io
.in
.durable_handle_v2
= h
; /* durable v2 reconnect */
880 io
.in
.create_guid
= create_guid
;
881 status
= smb2_create(tree
, mem_ctx
, &io
);
882 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
886 smb2_util_close(tree
, *h
);
889 smb2_util_unlink(tree
, fname
);
893 talloc_free(mem_ctx
);
899 * lease variant of reopen2
900 * basic test for doing a durable open
901 * tcp disconnect, reconnect, do a durable reopen (succeeds)
903 bool test_durable_v2_open_reopen2_lease(struct torture_context
*tctx
,
904 struct smb2_tree
*tree
)
907 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
909 struct smb2_handle _h
;
910 struct smb2_handle
*h
= NULL
;
911 struct smb2_create io
;
912 struct GUID create_guid
= GUID_random();
913 struct smb2_lease ls
;
916 struct smbcli_options options
;
919 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
920 if (!(caps
& SMB2_CAP_LEASING
)) {
921 torture_skip(tctx
, "leases are not supported");
924 options
= tree
->session
->transport
->options
;
926 /* Choose a random name in case the state is left a little funky. */
927 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
928 generate_random_str(tctx
, 8));
930 smb2_util_unlink(tree
, fname
);
932 lease_key
= random();
933 smb2_lease_create(&io
, &ls
, false /* dir */, fname
,
934 lease_key
, smb2_util_lease_state("RWH"));
935 io
.in
.durable_open
= false;
936 io
.in
.durable_open_v2
= true;
937 io
.in
.persistent_open
= false;
938 io
.in
.create_guid
= create_guid
;
939 io
.in
.timeout
= UINT32_MAX
;
941 status
= smb2_create(tree
, mem_ctx
, &io
);
942 CHECK_STATUS(status
, NT_STATUS_OK
);
943 _h
= io
.out
.file
.handle
;
945 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
946 CHECK_VAL(io
.out
.durable_open
, false);
947 CHECK_VAL(io
.out
.durable_open_v2
, true);
948 CHECK_VAL(io
.out
.persistent_open
, false);
949 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
950 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
951 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
952 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
953 CHECK_VAL(io
.out
.lease_response
.lease_state
,
954 smb2_util_lease_state("RWH"));
955 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
956 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
958 /* disconnect, reconnect and then do durable reopen */
961 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
962 torture_warning(tctx
, "couldn't reconnect, bailing\n");
967 /* a few failure tests: */
970 * several attempts without lease attached:
971 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
972 * irrespective of file name provided
977 io
.in
.durable_handle_v2
= h
;
978 status
= smb2_create(tree
, mem_ctx
, &io
);
979 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
982 io
.in
.fname
= "__non_existing_fname__";
983 io
.in
.durable_handle_v2
= h
;
984 status
= smb2_create(tree
, mem_ctx
, &io
);
985 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
989 io
.in
.durable_handle_v2
= h
;
990 status
= smb2_create(tree
, mem_ctx
, &io
);
991 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
994 * attempt with lease provided, but
995 * with a changed lease key. => fails
999 io
.in
.durable_open_v2
= false;
1000 io
.in
.durable_handle_v2
= h
;
1001 io
.in
.create_guid
= create_guid
;
1002 io
.in
.lease_request
= &ls
;
1003 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1004 /* a wrong lease key lets the request fail */
1005 ls
.lease_key
.data
[0]++;
1007 status
= smb2_create(tree
, mem_ctx
, &io
);
1008 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1010 /* restore the correct lease key */
1011 ls
.lease_key
.data
[0]--;
1014 * this last failing attempt is almost correct:
1015 * only problem is: we use the wrong filename...
1016 * Note that this gives INVALID_PARAMETER.
1017 * This is different from oplocks!
1020 io
.in
.fname
= "__non_existing_fname__";
1021 io
.in
.durable_open_v2
= false;
1022 io
.in
.durable_handle_v2
= h
;
1023 io
.in
.create_guid
= create_guid
;
1024 io
.in
.lease_request
= &ls
;
1025 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1027 status
= smb2_create(tree
, mem_ctx
, &io
);
1028 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1031 * Now for a succeeding reconnect:
1035 io
.in
.fname
= fname
;
1036 io
.in
.durable_open_v2
= false;
1037 io
.in
.durable_handle_v2
= h
;
1038 io
.in
.create_guid
= create_guid
;
1039 io
.in
.lease_request
= &ls
;
1040 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1042 /* the requested lease state is irrelevant */
1043 ls
.lease_state
= smb2_util_lease_state("");
1047 status
= smb2_create(tree
, mem_ctx
, &io
);
1048 CHECK_STATUS(status
, NT_STATUS_OK
);
1050 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1051 CHECK_VAL(io
.out
.durable_open
, false);
1052 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1053 CHECK_VAL(io
.out
.persistent_open
, false);
1054 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1055 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1056 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1057 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1058 smb2_util_lease_state("RWH"));
1059 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1060 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1061 _h
= io
.out
.file
.handle
;
1064 /* disconnect one more time */
1067 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1068 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1074 * demonstrate that various parameters are ignored
1080 * These are completely ignored by the server
1082 io
.in
.security_flags
= 0x78;
1083 io
.in
.oplock_level
= 0x78;
1084 io
.in
.impersonation_level
= 0x12345678;
1085 io
.in
.create_flags
= 0x12345678;
1086 io
.in
.reserved
= 0x12345678;
1087 io
.in
.desired_access
= 0x12345678;
1088 io
.in
.file_attributes
= 0x12345678;
1089 io
.in
.share_access
= 0x12345678;
1090 io
.in
.create_disposition
= 0x12345678;
1091 io
.in
.create_options
= 0x12345678;
1094 * only these are checked:
1096 * - io.in.durable_handle_v2,
1097 * - io.in.create_guid
1098 * - io.in.lease_request->lease_key
1101 io
.in
.fname
= fname
;
1102 io
.in
.durable_open_v2
= false;
1103 io
.in
.durable_handle_v2
= h
;
1104 io
.in
.create_guid
= create_guid
;
1105 io
.in
.lease_request
= &ls
;
1107 /* the requested lease state is irrelevant */
1108 ls
.lease_state
= smb2_util_lease_state("");
1112 status
= smb2_create(tree
, mem_ctx
, &io
);
1113 CHECK_STATUS(status
, NT_STATUS_OK
);
1115 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1116 CHECK_VAL(io
.out
.durable_open
, false);
1117 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1118 CHECK_VAL(io
.out
.persistent_open
, false);
1119 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1120 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
1121 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
1122 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1123 smb2_util_lease_state("RWH"));
1124 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
1125 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
1127 _h
= io
.out
.file
.handle
;
1132 smb2_util_close(tree
, *h
);
1135 smb2_util_unlink(tree
, fname
);
1139 talloc_free(mem_ctx
);
1145 * lease_v2 variant of reopen2
1146 * basic test for doing a durable open
1147 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1149 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context
*tctx
,
1150 struct smb2_tree
*tree
)
1153 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1155 struct smb2_handle _h
;
1156 struct smb2_handle
*h
= NULL
;
1157 struct smb2_create io
;
1158 struct GUID create_guid
= GUID_random();
1159 struct smb2_lease ls
;
1162 struct smbcli_options options
;
1165 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1166 if (!(caps
& SMB2_CAP_LEASING
)) {
1167 torture_skip(tctx
, "leases are not supported");
1170 options
= tree
->session
->transport
->options
;
1172 /* Choose a random name in case the state is left a little funky. */
1173 snprintf(fname
, 256, "durable_v2_open_reopen2_%s.dat",
1174 generate_random_str(tctx
, 8));
1176 smb2_util_unlink(tree
, fname
);
1178 lease_key
= random();
1179 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
1180 lease_key
, 0, /* parent lease key */
1181 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1182 io
.in
.durable_open
= false;
1183 io
.in
.durable_open_v2
= true;
1184 io
.in
.persistent_open
= false;
1185 io
.in
.create_guid
= create_guid
;
1186 io
.in
.timeout
= UINT32_MAX
;
1188 status
= smb2_create(tree
, mem_ctx
, &io
);
1189 CHECK_STATUS(status
, NT_STATUS_OK
);
1190 _h
= io
.out
.file
.handle
;
1192 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1193 CHECK_VAL(io
.out
.durable_open
, false);
1194 CHECK_VAL(io
.out
.durable_open_v2
, true);
1195 CHECK_VAL(io
.out
.persistent_open
, false);
1196 CHECK_VAL(io
.out
.timeout
, io
.in
.timeout
);
1197 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1198 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1199 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1201 /* disconnect, reconnect and then do durable reopen */
1204 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1205 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1210 /* a few failure tests: */
1213 * several attempts without lease attached:
1214 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1215 * irrespective of file name provided
1220 io
.in
.durable_handle_v2
= h
;
1221 status
= smb2_create(tree
, mem_ctx
, &io
);
1222 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1225 io
.in
.fname
= "__non_existing_fname__";
1226 io
.in
.durable_handle_v2
= h
;
1227 status
= smb2_create(tree
, mem_ctx
, &io
);
1228 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1231 io
.in
.fname
= fname
;
1232 io
.in
.durable_handle_v2
= h
;
1233 status
= smb2_create(tree
, mem_ctx
, &io
);
1234 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1237 * attempt with lease provided, but
1238 * with a changed lease key. => fails
1241 io
.in
.fname
= fname
;
1242 io
.in
.durable_open_v2
= false;
1243 io
.in
.durable_handle_v2
= h
;
1244 io
.in
.create_guid
= create_guid
;
1245 io
.in
.lease_request_v2
= &ls
;
1246 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1247 /* a wrong lease key lets the request fail */
1248 ls
.lease_key
.data
[0]++;
1250 status
= smb2_create(tree
, mem_ctx
, &io
);
1251 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1253 /* restore the correct lease key */
1254 ls
.lease_key
.data
[0]--;
1258 * this last failing attempt is almost correct:
1259 * only problem is: we use the wrong filename...
1260 * Note that this gives INVALID_PARAMETER.
1261 * This is different from oplocks!
1264 io
.in
.fname
= "__non_existing_fname__";
1265 io
.in
.durable_open_v2
= false;
1266 io
.in
.durable_handle_v2
= h
;
1267 io
.in
.create_guid
= create_guid
;
1268 io
.in
.lease_request_v2
= &ls
;
1269 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1271 status
= smb2_create(tree
, mem_ctx
, &io
);
1272 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1275 * Now for a succeeding reconnect:
1279 io
.in
.fname
= fname
;
1280 io
.in
.durable_open_v2
= false;
1281 io
.in
.durable_handle_v2
= h
;
1282 io
.in
.create_guid
= create_guid
;
1283 io
.in
.lease_request_v2
= &ls
;
1284 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1286 /* the requested lease state is irrelevant */
1287 ls
.lease_state
= smb2_util_lease_state("");
1291 status
= smb2_create(tree
, mem_ctx
, &io
);
1292 CHECK_STATUS(status
, NT_STATUS_OK
);
1294 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1295 CHECK_VAL(io
.out
.durable_open
, false);
1296 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1297 CHECK_VAL(io
.out
.persistent_open
, false);
1298 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1299 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1300 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1301 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1302 smb2_util_lease_state("RWH"));
1303 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1304 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1305 _h
= io
.out
.file
.handle
;
1308 /* disconnect one more time */
1311 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1312 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1318 * demonstrate that various parameters are ignored
1324 * These are completely ignored by the server
1326 io
.in
.security_flags
= 0x78;
1327 io
.in
.oplock_level
= 0x78;
1328 io
.in
.impersonation_level
= 0x12345678;
1329 io
.in
.create_flags
= 0x12345678;
1330 io
.in
.reserved
= 0x12345678;
1331 io
.in
.desired_access
= 0x12345678;
1332 io
.in
.file_attributes
= 0x12345678;
1333 io
.in
.share_access
= 0x12345678;
1334 io
.in
.create_disposition
= 0x12345678;
1335 io
.in
.create_options
= 0x12345678;
1336 io
.in
.fname
= "__non_existing_fname__";
1339 * only these are checked:
1341 * - io.in.durable_handle_v2,
1342 * - io.in.create_guid
1343 * - io.in.lease_request_v2->lease_key
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_v2
= &ls
;
1352 /* the requested lease state is irrelevant */
1353 ls
.lease_state
= smb2_util_lease_state("");
1357 status
= smb2_create(tree
, mem_ctx
, &io
);
1358 CHECK_STATUS(status
, NT_STATUS_OK
);
1359 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1360 CHECK_VAL(io
.out
.durable_open
, false);
1361 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1362 CHECK_VAL(io
.out
.persistent_open
, false);
1363 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1364 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1365 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1366 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1367 smb2_util_lease_state("RWH"));
1368 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1369 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1371 _h
= io
.out
.file
.handle
;
1376 smb2_util_close(tree
, *h
);
1379 smb2_util_unlink(tree
, fname
);
1383 talloc_free(mem_ctx
);
1389 * Test durable request / reconnect with AppInstanceId
1391 bool test_durable_v2_open_app_instance(struct torture_context
*tctx
,
1392 struct smb2_tree
*tree1
,
1393 struct smb2_tree
*tree2
)
1396 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1398 struct smb2_handle _h1
, _h2
;
1399 struct smb2_handle
*h1
= NULL
, *h2
= NULL
;
1400 struct smb2_create io1
, io2
;
1402 struct GUID create_guid_1
= GUID_random();
1403 struct GUID create_guid_2
= GUID_random();
1404 struct GUID app_instance_id
= GUID_random();
1406 /* Choose a random name in case the state is left a little funky. */
1407 snprintf(fname
, 256, "durable_v2_open_app_instance_%s.dat",
1408 generate_random_str(tctx
, 8));
1410 smb2_util_unlink(tree1
, fname
);
1412 ZERO_STRUCT(break_info
);
1413 tree1
->session
->transport
->oplock
.handler
= torture_oplock_handler
;
1414 tree1
->session
->transport
->oplock
.private_data
= tree1
;
1416 smb2_oplock_create_share(&io1
, fname
,
1417 smb2_util_share_access(""),
1418 smb2_util_oplock_level("b"));
1419 io1
.in
.durable_open
= false;
1420 io1
.in
.durable_open_v2
= true;
1421 io1
.in
.persistent_open
= false;
1422 io1
.in
.create_guid
= create_guid_1
;
1423 io1
.in
.app_instance_id
= &app_instance_id
;
1424 io1
.in
.timeout
= UINT32_MAX
;
1426 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1427 CHECK_STATUS(status
, NT_STATUS_OK
);
1428 _h1
= io1
.out
.file
.handle
;
1430 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1431 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1432 CHECK_VAL(io1
.out
.durable_open
, false);
1433 CHECK_VAL(io1
.out
.durable_open_v2
, true);
1434 CHECK_VAL(io1
.out
.persistent_open
, false);
1435 CHECK_VAL(io1
.out
.timeout
, io1
.in
.timeout
);
1438 * try to open the file as durable from a second tree with
1439 * a different create guid but the same app_instance_id
1440 * while the first handle is still open.
1443 smb2_oplock_create_share(&io2
, fname
,
1444 smb2_util_share_access(""),
1445 smb2_util_oplock_level("b"));
1446 io2
.in
.durable_open
= false;
1447 io2
.in
.durable_open_v2
= true;
1448 io2
.in
.persistent_open
= false;
1449 io2
.in
.create_guid
= create_guid_2
;
1450 io2
.in
.app_instance_id
= &app_instance_id
;
1451 io2
.in
.timeout
= UINT32_MAX
;
1453 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1454 CHECK_STATUS(status
, NT_STATUS_OK
);
1455 _h2
= io2
.out
.file
.handle
;
1457 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1458 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1459 CHECK_VAL(io2
.out
.durable_open
, false);
1460 CHECK_VAL(io2
.out
.durable_open_v2
, true);
1461 CHECK_VAL(io2
.out
.persistent_open
, false);
1462 CHECK_VAL(io2
.out
.timeout
, io2
.in
.timeout
);
1464 CHECK_VAL(break_info
.count
, 0);
1466 status
= smb2_util_close(tree1
, *h1
);
1467 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1472 smb2_util_close(tree1
, *h1
);
1475 smb2_util_close(tree2
, *h2
);
1478 smb2_util_unlink(tree2
, fname
);
1483 talloc_free(mem_ctx
);
1490 * basic persistent open test.
1492 * This test tests durable open with all possible oplock types.
1495 struct durable_open_vs_oplock persistent_open_oplock_ca_table
[NUM_OPLOCK_OPEN_TESTS
] =
1497 { "", "", true, true },
1498 { "", "R", true, true },
1499 { "", "W", true, true },
1500 { "", "D", true, true },
1501 { "", "RD", true, true },
1502 { "", "RW", true, true },
1503 { "", "WD", true, true },
1504 { "", "RWD", true, true },
1506 { "s", "", true, true },
1507 { "s", "R", true, true },
1508 { "s", "W", true, true },
1509 { "s", "D", true, true },
1510 { "s", "RD", true, true },
1511 { "s", "RW", true, true },
1512 { "s", "WD", true, true },
1513 { "s", "RWD", true, true },
1515 { "x", "", true, true },
1516 { "x", "R", true, true },
1517 { "x", "W", true, true },
1518 { "x", "D", true, true },
1519 { "x", "RD", true, true },
1520 { "x", "RW", true, true },
1521 { "x", "WD", true, true },
1522 { "x", "RWD", true, true },
1524 { "b", "", true, true },
1525 { "b", "R", true, true },
1526 { "b", "W", true, true },
1527 { "b", "D", true, true },
1528 { "b", "RD", true, true },
1529 { "b", "RW", true, true },
1530 { "b", "WD", true, true },
1531 { "b", "RWD", true, true },
1534 bool test_persistent_open_oplock(struct torture_context
*tctx
,
1535 struct smb2_tree
*tree
)
1539 uint32_t share_capabilities
;
1540 bool share_is_ca
= false;
1541 struct durable_open_vs_oplock
*table
;
1543 /* Choose a random name in case the state is left a little funky. */
1544 snprintf(fname
, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1546 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1547 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1550 table
= persistent_open_oplock_ca_table
;
1552 table
= durable_open_vs_oplock_table
;
1555 ret
= test_durable_v2_open_oplock_table(tctx
, tree
, fname
,
1556 true, /* request_persistent */
1558 NUM_OPLOCK_OPEN_TESTS
);
1566 * basic persistent handle open test.
1567 * persistent state should only be granted when requested
1568 * along with a batch oplock or a handle lease.
1570 * This test tests persistent open with all valid lease types.
1573 struct durable_open_vs_lease persistent_open_lease_ca_table
[NUM_LEASE_OPEN_TESTS
] =
1575 { "", "", true, true },
1576 { "", "R", true, true },
1577 { "", "W", true, true },
1578 { "", "D", true, true },
1579 { "", "RW", true, true },
1580 { "", "RD", true, true },
1581 { "", "WD", true, true },
1582 { "", "RWD", true, true },
1584 { "R", "", true, true },
1585 { "R", "R", true, true },
1586 { "R", "W", true, true },
1587 { "R", "D", true, true },
1588 { "R", "RW", true, true },
1589 { "R", "RD", true, true },
1590 { "R", "DW", true, true },
1591 { "R", "RWD", true, true },
1593 { "RW", "", true, true },
1594 { "RW", "R", true, true },
1595 { "RW", "W", true, true },
1596 { "RW", "D", true, true },
1597 { "RW", "RW", true, true },
1598 { "RW", "RD", true, true },
1599 { "RW", "WD", true, true },
1600 { "RW", "RWD", true, true },
1602 { "RH", "", true, true },
1603 { "RH", "R", true, true },
1604 { "RH", "W", true, true },
1605 { "RH", "D", true, true },
1606 { "RH", "RW", true, true },
1607 { "RH", "RD", true, true },
1608 { "RH", "WD", true, true },
1609 { "RH", "RWD", true, true },
1611 { "RHW", "", true, true },
1612 { "RHW", "R", true, true },
1613 { "RHW", "W", true, true },
1614 { "RHW", "D", true, true },
1615 { "RHW", "RW", true, true },
1616 { "RHW", "RD", true, true },
1617 { "RHW", "WD", true, true },
1618 { "RHW", "RWD", true, true },
1621 bool test_persistent_open_lease(struct torture_context
*tctx
,
1622 struct smb2_tree
*tree
)
1627 uint32_t share_capabilities
;
1629 struct durable_open_vs_lease
*table
;
1631 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1632 if (!(caps
& SMB2_CAP_LEASING
)) {
1633 torture_skip(tctx
, "leases are not supported");
1636 /* Choose a random name in case the state is left a little funky. */
1637 snprintf(fname
, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx
, 8));
1639 share_capabilities
= smb2cli_tcon_capabilities(tree
->smbXcli
);
1640 share_is_ca
= share_capabilities
& SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
1643 table
= persistent_open_lease_ca_table
;
1645 table
= durable_open_vs_lease_table
;
1648 ret
= test_durable_v2_open_lease_table(tctx
, tree
, fname
,
1649 true, /* request_persistent */
1651 NUM_LEASE_OPEN_TESTS
);
1658 struct torture_suite
*torture_smb2_durable_v2_open_init(void)
1660 struct torture_suite
*suite
=
1661 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1663 torture_suite_add_1smb2_test(suite
, "create-blob", test_durable_v2_open_create_blob
);
1664 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_v2_open_oplock
);
1665 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_v2_open_lease
);
1666 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_v2_open_reopen1
);
1667 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_v2_open_reopen2
);
1668 torture_suite_add_1smb2_test(suite
, "reopen2b", test_durable_v2_open_reopen2b
);
1669 torture_suite_add_1smb2_test(suite
, "reopen2c", test_durable_v2_open_reopen2c
);
1670 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_v2_open_reopen2_lease
);
1671 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2
);
1672 torture_suite_add_2smb2_test(suite
, "app-instance", test_durable_v2_open_app_instance
);
1673 torture_suite_add_1smb2_test(suite
, "persistent-open-oplock", test_persistent_open_oplock
);
1674 torture_suite_add_1smb2_test(suite
, "persistent-open-lease", test_persistent_open_lease
);
1676 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-V2-OPEN tests");