2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
7 Copyright (C) Michael Adam 2011-2012
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
31 #define CHECK_VAL(v, correct) do { \
32 if ((v) != (correct)) { \
33 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
38 #define CHECK_NOT_VAL(v, incorrect) do { \
39 if ((v) == (incorrect)) { \
40 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41 __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
45 #define CHECK_NOT_NULL(p) do { \
47 torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
52 #define CHECK_STATUS(status, correct) do { \
53 if (!NT_STATUS_EQUAL(status, correct)) { \
54 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
55 nt_errstr(status), nt_errstr(correct)); \
60 #define CHECK_CREATED(__io, __created, __attribute) \
62 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
63 CHECK_VAL((__io)->out.alloc_size, 0); \
64 CHECK_VAL((__io)->out.size, 0); \
65 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
66 CHECK_VAL((__io)->out.reserved2, 0); \
69 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
71 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
72 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
73 CHECK_VAL((__io)->out.size, (__size)); \
74 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
75 CHECK_VAL((__io)->out.reserved2, 0); \
81 * basic durable_open test.
82 * durable state should only be granted when requested
83 * along with a batch oplock or a handle lease.
85 * This test tests durable open with all possible oplock types.
88 struct durable_open_vs_oplock
{
90 const char *share_mode
;
94 #define NUM_OPLOCK_TYPES 4
95 #define NUM_SHARE_MODES 8
96 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
97 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
106 { "", "RWD", false },
112 { "s", "RD", false },
113 { "s", "RW", false },
114 { "s", "WD", false },
115 { "s", "RWD", false },
121 { "x", "RD", false },
122 { "x", "RW", false },
123 { "x", "WD", false },
124 { "x", "RWD", false },
133 { "b", "RWD", true },
136 static bool test_one_durable_open_open_oplock(struct torture_context
*tctx
,
137 struct smb2_tree
*tree
,
139 struct durable_open_vs_oplock test
)
142 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
143 struct smb2_handle _h
;
144 struct smb2_handle
*h
= NULL
;
146 struct smb2_create io
;
148 smb2_util_unlink(tree
, fname
);
150 smb2_oplock_create_share(&io
, fname
,
151 smb2_util_share_access(test
.share_mode
),
152 smb2_util_oplock_level(test
.level
));
153 io
.in
.durable_open
= true;
155 status
= smb2_create(tree
, mem_ctx
, &io
);
156 CHECK_STATUS(status
, NT_STATUS_OK
);
157 _h
= io
.out
.file
.handle
;
159 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
160 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
161 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
165 smb2_util_close(tree
, *h
);
167 smb2_util_unlink(tree
, fname
);
168 talloc_free(mem_ctx
);
173 static bool test_durable_open_open_oplock(struct torture_context
*tctx
,
174 struct smb2_tree
*tree
)
176 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
181 /* Choose a random name in case the state is left a little funky. */
182 snprintf(fname
, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx
, 8));
184 smb2_util_unlink(tree
, fname
);
186 /* test various oplock levels with durable open */
188 for (i
= 0; i
< NUM_OPLOCK_OPEN_TESTS
; i
++) {
189 ret
= test_one_durable_open_open_oplock(tctx
,
192 durable_open_vs_oplock_table
[i
]);
199 smb2_util_unlink(tree
, fname
);
201 talloc_free(mem_ctx
);
207 * basic durable_open test.
208 * durable state should only be granted when requested
209 * along with a batch oplock or a handle lease.
211 * This test tests durable open with all valid lease types.
214 struct durable_open_vs_lease
{
216 const char *share_mode
;
220 #define NUM_LEASE_TYPES 5
221 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
222 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
231 { "", "RWD", false },
237 { "R", "RW", false },
238 { "R", "RD", false },
239 { "R", "DW", false },
240 { "R", "RWD", false },
243 { "RW", "R", false },
244 { "RW", "W", false },
245 { "RW", "D", false },
246 { "RW", "RW", false },
247 { "RW", "RD", false },
248 { "RW", "WD", false },
249 { "RW", "RWD", false },
255 { "RH", "RW", true },
256 { "RH", "RD", true },
257 { "RH", "WD", true },
258 { "RH", "RWD", true },
261 { "RHW", "R", true },
262 { "RHW", "W", true },
263 { "RHW", "D", true },
264 { "RHW", "RW", true },
265 { "RHW", "RD", true },
266 { "RHW", "WD", true },
267 { "RHW", "RWD", true },
270 static bool test_one_durable_open_open_lease(struct torture_context
*tctx
,
271 struct smb2_tree
*tree
,
273 struct durable_open_vs_lease test
)
276 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
277 struct smb2_handle _h
;
278 struct smb2_handle
*h
= NULL
;
280 struct smb2_create io
;
281 struct smb2_lease ls
;
285 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
286 if (!(caps
& SMB2_CAP_LEASING
)) {
287 torture_skip(tctx
, "leases are not supported");
290 smb2_util_unlink(tree
, fname
);
294 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
295 smb2_util_share_access(test
.share_mode
),
297 smb2_util_lease_state(test
.type
));
298 io
.in
.durable_open
= true;
300 status
= smb2_create(tree
, mem_ctx
, &io
);
301 CHECK_STATUS(status
, NT_STATUS_OK
);
302 _h
= io
.out
.file
.handle
;
304 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
305 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
306 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
307 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
308 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
309 CHECK_VAL(io
.out
.lease_response
.lease_state
,
310 smb2_util_lease_state(test
.type
));
313 smb2_util_close(tree
, *h
);
315 smb2_util_unlink(tree
, fname
);
316 talloc_free(mem_ctx
);
321 static bool test_durable_open_open_lease(struct torture_context
*tctx
,
322 struct smb2_tree
*tree
)
324 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
330 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
331 if (!(caps
& SMB2_CAP_LEASING
)) {
332 torture_skip(tctx
, "leases are not supported");
335 /* Choose a random name in case the state is left a little funky. */
336 snprintf(fname
, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx
, 8));
338 smb2_util_unlink(tree
, fname
);
341 /* test various oplock levels with durable open */
343 for (i
= 0; i
< NUM_LEASE_OPEN_TESTS
; i
++) {
344 ret
= test_one_durable_open_open_lease(tctx
,
347 durable_open_vs_lease_table
[i
]);
354 smb2_util_unlink(tree
, fname
);
356 talloc_free(mem_ctx
);
362 * basic test for doing a durable open
363 * and do a durable reopen on the same connection
364 * while the first open is still active (fails)
366 static bool test_durable_open_reopen1(struct torture_context
*tctx
,
367 struct smb2_tree
*tree
)
370 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
372 struct smb2_handle _h
;
373 struct smb2_handle
*h
= NULL
;
374 struct smb2_create io1
, io2
;
377 /* Choose a random name in case the state is left a little funky. */
378 snprintf(fname
, 256, "durable_open_reopen1_%s.dat",
379 generate_random_str(tctx
, 8));
381 smb2_util_unlink(tree
, fname
);
383 smb2_oplock_create_share(&io1
, fname
,
384 smb2_util_share_access(""),
385 smb2_util_oplock_level("b"));
386 io1
.in
.durable_open
= true;
388 status
= smb2_create(tree
, mem_ctx
, &io1
);
389 CHECK_STATUS(status
, NT_STATUS_OK
);
390 _h
= io1
.out
.file
.handle
;
392 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
393 CHECK_VAL(io1
.out
.durable_open
, true);
394 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
396 /* try a durable reconnect while the file is still open */
398 io2
.in
.fname
= fname
;
399 io2
.in
.durable_handle
= h
;
401 status
= smb2_create(tree
, mem_ctx
, &io2
);
402 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
406 smb2_util_close(tree
, *h
);
409 smb2_util_unlink(tree
, fname
);
413 talloc_free(mem_ctx
);
419 * Basic test for doing a durable open
420 * and do a session reconnect while the first
421 * session is still active and the handle is
422 * still open in the client.
423 * This closes the original session and a
424 * durable reconnect on the new session succeeds.
426 static bool test_durable_open_reopen1a(struct torture_context
*tctx
,
427 struct smb2_tree
*tree
)
430 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
432 struct smb2_handle _h
;
433 struct smb2_handle
*h
= NULL
;
434 struct smb2_create io1
, io2
;
436 struct smb2_tree
*tree2
= NULL
;
437 uint64_t previous_session_id
;
438 struct smbcli_options options
;
440 options
= tree
->session
->transport
->options
;
442 /* Choose a random name in case the state is left a little funky. */
443 snprintf(fname
, 256, "durable_open_reopen1a_%s.dat",
444 generate_random_str(tctx
, 8));
446 smb2_util_unlink(tree
, fname
);
448 smb2_oplock_create_share(&io1
, fname
,
449 smb2_util_share_access(""),
450 smb2_util_oplock_level("b"));
451 io1
.in
.durable_open
= true;
453 status
= smb2_create(tree
, mem_ctx
, &io1
);
454 CHECK_STATUS(status
, NT_STATUS_OK
);
455 _h
= io1
.out
.file
.handle
;
457 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
458 CHECK_VAL(io1
.out
.durable_open
, true);
459 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
462 * a session reconnect on a second tcp connection
465 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
467 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
470 torture_warning(tctx
, "couldn't reconnect, bailing\n");
476 * check that this has deleted the old session
480 io2
.in
.fname
= fname
;
481 io2
.in
.durable_handle
= h
;
483 status
= smb2_create(tree
, mem_ctx
, &io2
);
484 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
487 * but a durable reconnect on the new session succeeds:
491 io2
.in
.fname
= fname
;
492 io2
.in
.durable_handle
= h
;
494 status
= smb2_create(tree2
, mem_ctx
, &io2
);
495 CHECK_STATUS(status
, NT_STATUS_OK
);
496 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
497 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
498 _h
= io2
.out
.file
.handle
;
503 smb2_util_close(tree2
, *h
);
506 smb2_util_unlink(tree2
, fname
);
512 talloc_free(mem_ctx
);
518 * basic test for doing a durable open
519 * tcp disconnect, reconnect, do a durable reopen (succeeds)
521 static bool test_durable_open_reopen2(struct torture_context
*tctx
,
522 struct smb2_tree
*tree
)
525 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
527 struct smb2_handle _h
;
528 struct smb2_handle
*h
= NULL
;
529 struct smb2_create io
;
532 /* Choose a random name in case the state is left a little funky. */
533 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
534 generate_random_str(tctx
, 8));
536 smb2_util_unlink(tree
, fname
);
538 smb2_oplock_create_share(&io
, fname
,
539 smb2_util_share_access(""),
540 smb2_util_oplock_level("b"));
541 io
.in
.durable_open
= true;
543 status
= smb2_create(tree
, mem_ctx
, &io
);
544 CHECK_STATUS(status
, NT_STATUS_OK
);
545 _h
= io
.out
.file
.handle
;
547 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
548 CHECK_VAL(io
.out
.durable_open
, true);
549 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
551 /* disconnect, leaving the durable in place */
554 if (!torture_smb2_connection(tctx
, &tree
)) {
555 torture_warning(tctx
, "couldn't reconnect, bailing\n");
561 /* the path name is ignored by the server */
563 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
566 status
= smb2_create(tree
, mem_ctx
, &io
);
567 CHECK_STATUS(status
, NT_STATUS_OK
);
568 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
569 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
570 _h
= io
.out
.file
.handle
;
573 /* disconnect again, leaving the durable in place */
576 if (!torture_smb2_connection(tctx
, &tree
)) {
577 torture_warning(tctx
, "couldn't reconnect, bailing\n");
583 * show that the filename and many other fields
584 * are ignored. only the reconnect request blob
588 /* the path name is ignored by the server */
589 io
.in
.security_flags
= 0x78;
590 io
.in
.oplock_level
= 0x78;
591 io
.in
.impersonation_level
= 0x12345678;
592 io
.in
.create_flags
= 0x12345678;
593 io
.in
.reserved
= 0x12345678;
594 io
.in
.desired_access
= 0x12345678;
595 io
.in
.file_attributes
= 0x12345678;
596 io
.in
.share_access
= 0x12345678;
597 io
.in
.create_disposition
= 0x12345678;
598 io
.in
.create_options
= 0x12345678;
599 io
.in
.fname
= "__non_existing_fname__";
600 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
603 status
= smb2_create(tree
, mem_ctx
, &io
);
604 CHECK_STATUS(status
, NT_STATUS_OK
);
605 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
606 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
607 _h
= io
.out
.file
.handle
;
610 /* disconnect, leaving the durable in place */
613 if (!torture_smb2_connection(tctx
, &tree
)) {
614 torture_warning(tctx
, "couldn't reconnect, bailing\n");
620 * show that an additionally specified durable v1 request
621 * is ignored by the server.
622 * See MS-SMB2, 3.3.5.9.7
623 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
626 /* the path name is ignored by the server */
628 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
629 io
.in
.durable_open
= true; /* durable v1 handle request */
632 status
= smb2_create(tree
, mem_ctx
, &io
);
633 CHECK_STATUS(status
, NT_STATUS_OK
);
634 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
635 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
636 _h
= io
.out
.file
.handle
;
642 smb2_util_close(tree
, *h
);
645 smb2_util_unlink(tree
, fname
);
650 talloc_free(mem_ctx
);
656 * lease variant of reopen2
657 * basic test for doing a durable open
658 * tcp disconnect, reconnect, do a durable reopen (succeeds)
660 static bool test_durable_open_reopen2_lease(struct torture_context
*tctx
,
661 struct smb2_tree
*tree
)
664 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
666 struct smb2_handle _h
;
667 struct smb2_handle
*h
= NULL
;
668 struct smb2_create io
;
669 struct smb2_lease ls
;
672 struct smbcli_options options
;
675 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
676 if (!(caps
& SMB2_CAP_LEASING
)) {
677 torture_skip(tctx
, "leases are not supported");
680 options
= tree
->session
->transport
->options
;
682 /* Choose a random name in case the state is left a little funky. */
683 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
684 generate_random_str(tctx
, 8));
686 smb2_util_unlink(tree
, fname
);
688 lease_key
= random();
689 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease_key
,
690 smb2_util_lease_state("RWH"));
691 io
.in
.durable_open
= true;
693 status
= smb2_create(tree
, mem_ctx
, &io
);
694 CHECK_STATUS(status
, NT_STATUS_OK
);
695 _h
= io
.out
.file
.handle
;
697 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
699 CHECK_VAL(io
.out
.durable_open
, true);
700 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
701 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
702 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
703 CHECK_VAL(io
.out
.lease_response
.lease_state
,
704 smb2_util_lease_state("RWH"));
705 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
706 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
708 /* disconnect, reconnect and then do durable reopen */
711 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
712 torture_warning(tctx
, "couldn't reconnect, bailing\n");
718 /* a few failure tests: */
721 * several attempts without lease attached:
722 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
723 * irrespective of file name provided
728 io
.in
.durable_handle
= h
;
729 status
= smb2_create(tree
, mem_ctx
, &io
);
730 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
733 io
.in
.fname
= "__non_existing_fname__";
734 io
.in
.durable_handle
= h
;
735 status
= smb2_create(tree
, mem_ctx
, &io
);
736 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
740 io
.in
.durable_handle
= h
;
741 status
= smb2_create(tree
, mem_ctx
, &io
);
742 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
745 * attempt with lease provided, but
746 * with a changed lease key. => fails
750 io
.in
.durable_open
= false;
751 io
.in
.durable_handle
= h
;
752 io
.in
.lease_request
= &ls
;
753 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
754 /* a wrong lease key lets the request fail */
755 ls
.lease_key
.data
[0]++;
757 status
= smb2_create(tree
, mem_ctx
, &io
);
758 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
760 /* restore the correct lease key */
761 ls
.lease_key
.data
[0]--;
764 * this last failing attempt is almost correct:
765 * only problem is: we use the wrong filename...
766 * Note that this gives INVALID_PARAMETER.
767 * This is different from oplocks!
770 io
.in
.fname
= "__non_existing_fname__";
771 io
.in
.durable_open
= false;
772 io
.in
.durable_handle
= h
;
773 io
.in
.lease_request
= &ls
;
774 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
776 status
= smb2_create(tree
, mem_ctx
, &io
);
777 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
780 * Now for a succeeding reconnect:
785 io
.in
.durable_open
= false;
786 io
.in
.durable_handle
= h
;
787 io
.in
.lease_request
= &ls
;
788 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
790 /* the requested lease state is irrelevant */
791 ls
.lease_state
= smb2_util_lease_state("");
795 status
= smb2_create(tree
, mem_ctx
, &io
);
796 CHECK_STATUS(status
, NT_STATUS_OK
);
798 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
799 CHECK_VAL(io
.out
.durable_open
, false);
800 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
801 CHECK_VAL(io
.out
.persistent_open
, false);
802 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
803 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
804 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
805 CHECK_VAL(io
.out
.lease_response
.lease_state
,
806 smb2_util_lease_state("RWH"));
807 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
808 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
809 _h
= io
.out
.file
.handle
;
812 /* disconnect one more time */
815 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
816 torture_warning(tctx
, "couldn't reconnect, bailing\n");
822 * demonstrate that various parameters are ignored
828 * These are completely ignored by the server
830 io
.in
.security_flags
= 0x78;
831 io
.in
.oplock_level
= 0x78;
832 io
.in
.impersonation_level
= 0x12345678;
833 io
.in
.create_flags
= 0x12345678;
834 io
.in
.reserved
= 0x12345678;
835 io
.in
.desired_access
= 0x12345678;
836 io
.in
.file_attributes
= 0x12345678;
837 io
.in
.share_access
= 0x12345678;
838 io
.in
.create_disposition
= 0x12345678;
839 io
.in
.create_options
= 0x12345678;
842 * only these are checked:
844 * - io.in.durable_handle,
845 * - io.in.lease_request->lease_key
849 io
.in
.durable_open_v2
= false;
850 io
.in
.durable_handle_v2
= h
;
851 io
.in
.lease_request
= &ls
;
853 /* the requested lease state is irrelevant */
854 ls
.lease_state
= smb2_util_lease_state("");
858 status
= smb2_create(tree
, mem_ctx
, &io
);
859 CHECK_STATUS(status
, NT_STATUS_OK
);
861 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
862 CHECK_VAL(io
.out
.durable_open
, false);
863 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
864 CHECK_VAL(io
.out
.persistent_open
, false);
865 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
866 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
867 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
868 CHECK_VAL(io
.out
.lease_response
.lease_state
,
869 smb2_util_lease_state("RWH"));
870 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
871 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
873 _h
= io
.out
.file
.handle
;
879 smb2_util_close(tree
, *h
);
882 smb2_util_unlink(tree
, fname
);
887 talloc_free(mem_ctx
);
893 * lease v2 variant of reopen2
894 * basic test for doing a durable open
895 * tcp disconnect, reconnect, do a durable reopen (succeeds)
897 static bool test_durable_open_reopen2_lease_v2(struct torture_context
*tctx
,
898 struct smb2_tree
*tree
)
901 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
903 struct smb2_handle _h
;
904 struct smb2_handle
*h
= NULL
;
905 struct smb2_create io
;
906 struct smb2_lease ls
;
909 struct smbcli_options options
;
912 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
913 if (!(caps
& SMB2_CAP_LEASING
)) {
914 torture_skip(tctx
, "leases are not supported");
917 options
= tree
->session
->transport
->options
;
919 /* Choose a random name in case the state is left a little funky. */
920 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
921 generate_random_str(tctx
, 8));
923 smb2_util_unlink(tree
, fname
);
925 lease_key
= random();
926 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
927 lease_key
, 0, /* parent lease key */
928 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
929 io
.in
.durable_open
= true;
931 status
= smb2_create(tree
, mem_ctx
, &io
);
932 CHECK_STATUS(status
, NT_STATUS_OK
);
933 _h
= io
.out
.file
.handle
;
935 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
937 CHECK_VAL(io
.out
.durable_open
, true);
938 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
939 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
940 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
941 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
942 smb2_util_lease_state("RWH"));
943 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
944 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
946 /* disconnect, reconnect and then do durable reopen */
949 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
950 torture_warning(tctx
, "couldn't reconnect, bailing\n");
955 /* a few failure tests: */
958 * several attempts without lease attached:
959 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
960 * irrespective of file name provided
965 io
.in
.durable_handle
= h
;
966 status
= smb2_create(tree
, mem_ctx
, &io
);
967 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
970 io
.in
.fname
= "__non_existing_fname__";
971 io
.in
.durable_handle
= h
;
972 status
= smb2_create(tree
, mem_ctx
, &io
);
973 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
977 io
.in
.durable_handle
= h
;
978 status
= smb2_create(tree
, mem_ctx
, &io
);
979 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
982 * attempt with lease provided, but
983 * with a changed lease key. => fails
987 io
.in
.durable_open
= false;
988 io
.in
.durable_handle
= h
;
989 io
.in
.lease_request_v2
= &ls
;
990 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
991 /* a wrong lease key lets the request fail */
992 ls
.lease_key
.data
[0]++;
994 status
= smb2_create(tree
, mem_ctx
, &io
);
995 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
997 /* restore the correct lease key */
998 ls
.lease_key
.data
[0]--;
1001 * this last failing attempt is almost correct:
1002 * only problem is: we use the wrong filename...
1003 * Note that this gives INVALID_PARAMETER.
1004 * This is different from oplocks!
1007 io
.in
.fname
= "__non_existing_fname__";
1008 io
.in
.durable_open
= false;
1009 io
.in
.durable_handle
= h
;
1010 io
.in
.lease_request_v2
= &ls
;
1011 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1013 status
= smb2_create(tree
, mem_ctx
, &io
);
1014 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1017 * Now for a succeeding reconnect:
1021 io
.in
.fname
= fname
;
1022 io
.in
.durable_open
= false;
1023 io
.in
.durable_handle
= h
;
1024 io
.in
.lease_request_v2
= &ls
;
1025 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1027 /* the requested lease state is irrelevant */
1028 ls
.lease_state
= smb2_util_lease_state("");
1032 status
= smb2_create(tree
, mem_ctx
, &io
);
1033 CHECK_STATUS(status
, NT_STATUS_OK
);
1035 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1036 CHECK_VAL(io
.out
.durable_open
, false);
1037 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1038 CHECK_VAL(io
.out
.persistent_open
, false);
1039 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1040 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1041 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1042 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1043 smb2_util_lease_state("RWH"));
1044 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1045 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1046 _h
= io
.out
.file
.handle
;
1049 /* disconnect one more time */
1052 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1053 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1059 * demonstrate that various parameters are ignored
1065 * These are completely ignored by the server
1067 io
.in
.security_flags
= 0x78;
1068 io
.in
.oplock_level
= 0x78;
1069 io
.in
.impersonation_level
= 0x12345678;
1070 io
.in
.create_flags
= 0x12345678;
1071 io
.in
.reserved
= 0x12345678;
1072 io
.in
.desired_access
= 0x12345678;
1073 io
.in
.file_attributes
= 0x12345678;
1074 io
.in
.share_access
= 0x12345678;
1075 io
.in
.create_disposition
= 0x12345678;
1076 io
.in
.create_options
= 0x12345678;
1079 * only these are checked:
1081 * - io.in.durable_handle,
1082 * - io.in.lease_request->lease_key
1085 io
.in
.fname
= fname
;
1086 io
.in
.durable_open_v2
= false;
1087 io
.in
.durable_handle_v2
= h
;
1088 io
.in
.lease_request_v2
= &ls
;
1090 /* the requested lease state is irrelevant */
1091 ls
.lease_state
= smb2_util_lease_state("");
1095 status
= smb2_create(tree
, mem_ctx
, &io
);
1096 CHECK_STATUS(status
, NT_STATUS_OK
);
1098 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1099 CHECK_VAL(io
.out
.durable_open
, false);
1100 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1101 CHECK_VAL(io
.out
.persistent_open
, false);
1102 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1103 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1104 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1105 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1106 smb2_util_lease_state("RWH"));
1107 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1108 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1110 _h
= io
.out
.file
.handle
;
1116 smb2_util_close(tree
, *h
);
1119 smb2_util_unlink(tree
, fname
);
1124 talloc_free(mem_ctx
);
1130 * basic test for doing a durable open
1131 * tcp disconnect, reconnect with a session reconnect and
1132 * do a durable reopen (succeeds)
1134 static bool test_durable_open_reopen2a(struct torture_context
*tctx
,
1135 struct smb2_tree
*tree
)
1138 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1140 struct smb2_handle _h
;
1141 struct smb2_handle
*h
= NULL
;
1142 struct smb2_create io1
, io2
;
1143 uint64_t previous_session_id
;
1145 struct smbcli_options options
;
1147 options
= tree
->session
->transport
->options
;
1149 /* Choose a random name in case the state is left a little funky. */
1150 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
1151 generate_random_str(tctx
, 8));
1153 smb2_util_unlink(tree
, fname
);
1155 smb2_oplock_create_share(&io1
, fname
,
1156 smb2_util_share_access(""),
1157 smb2_util_oplock_level("b"));
1158 io1
.in
.durable_open
= true;
1160 status
= smb2_create(tree
, mem_ctx
, &io1
);
1161 CHECK_STATUS(status
, NT_STATUS_OK
);
1162 _h
= io1
.out
.file
.handle
;
1164 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1165 CHECK_VAL(io1
.out
.durable_open
, true);
1166 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1168 /* disconnect, reconnect and then do durable reopen */
1169 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1173 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1176 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1182 io2
.in
.fname
= fname
;
1183 io2
.in
.durable_handle
= h
;
1186 status
= smb2_create(tree
, mem_ctx
, &io2
);
1187 CHECK_STATUS(status
, NT_STATUS_OK
);
1188 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1189 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1190 _h
= io2
.out
.file
.handle
;
1196 smb2_util_close(tree
, *h
);
1199 smb2_util_unlink(tree
, fname
);
1204 talloc_free(mem_ctx
);
1211 * basic test for doing a durable open:
1212 * tdis, new tcon, try durable reopen (fails)
1214 static bool test_durable_open_reopen3(struct torture_context
*tctx
,
1215 struct smb2_tree
*tree
)
1218 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1220 struct smb2_handle _h
;
1221 struct smb2_handle
*h
= NULL
;
1222 struct smb2_create io1
, io2
;
1224 struct smb2_tree
*tree2
;
1226 /* Choose a random name in case the state is left a little funky. */
1227 snprintf(fname
, 256, "durable_open_reopen3_%s.dat",
1228 generate_random_str(tctx
, 8));
1230 smb2_util_unlink(tree
, fname
);
1232 smb2_oplock_create_share(&io1
, fname
,
1233 smb2_util_share_access(""),
1234 smb2_util_oplock_level("b"));
1235 io1
.in
.durable_open
= true;
1237 status
= smb2_create(tree
, mem_ctx
, &io1
);
1238 CHECK_STATUS(status
, NT_STATUS_OK
);
1239 _h
= io1
.out
.file
.handle
;
1241 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1242 CHECK_VAL(io1
.out
.durable_open
, true);
1243 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1245 /* disconnect, reconnect and then do durable reopen */
1246 status
= smb2_tdis(tree
);
1247 CHECK_STATUS(status
, NT_STATUS_OK
);
1249 if (!torture_smb2_tree_connect(tctx
, tree
->session
, mem_ctx
, &tree2
)) {
1250 torture_warning(tctx
, "couldn't reconnect to share, bailing\n");
1257 io2
.in
.fname
= fname
;
1258 io2
.in
.durable_handle
= h
;
1260 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1261 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1266 smb2_util_close(tree
, *h
);
1269 smb2_util_unlink(tree2
, fname
);
1274 talloc_free(mem_ctx
);
1280 * basic test for doing a durable open:
1281 * logoff, create a new session, do a durable reopen (succeeds)
1283 static bool test_durable_open_reopen4(struct torture_context
*tctx
,
1284 struct smb2_tree
*tree
)
1287 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1289 struct smb2_handle _h
;
1290 struct smb2_handle
*h
= NULL
;
1291 struct smb2_create io1
, io2
;
1293 struct smb2_transport
*transport
;
1294 struct smb2_session
*session2
;
1295 struct smb2_tree
*tree2
;
1297 /* Choose a random name in case the state is left a little funky. */
1298 snprintf(fname
, 256, "durable_open_reopen4_%s.dat",
1299 generate_random_str(tctx
, 8));
1301 smb2_util_unlink(tree
, fname
);
1303 smb2_oplock_create_share(&io1
, fname
,
1304 smb2_util_share_access(""),
1305 smb2_util_oplock_level("b"));
1306 io1
.in
.durable_open
= true;
1308 status
= smb2_create(tree
, mem_ctx
, &io1
);
1309 CHECK_STATUS(status
, NT_STATUS_OK
);
1310 _h
= io1
.out
.file
.handle
;
1312 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1313 CHECK_VAL(io1
.out
.durable_open
, true);
1314 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1317 * do a session logoff, establish a new session and tree
1318 * connect on the same transport, and try a durable reopen
1320 transport
= tree
->session
->transport
;
1321 status
= smb2_logoff(tree
->session
);
1322 CHECK_STATUS(status
, NT_STATUS_OK
);
1324 if (!torture_smb2_session_setup(tctx
, transport
,
1325 0, /* previous_session_id */
1326 mem_ctx
, &session2
))
1328 torture_warning(tctx
, "session setup failed.\n");
1334 * the session setup has talloc-stolen the transport,
1335 * so we can safely free the old tree+session for clarity
1339 if (!torture_smb2_tree_connect(tctx
, session2
, mem_ctx
, &tree2
)) {
1340 torture_warning(tctx
, "tree connect failed.\n");
1346 io2
.in
.fname
= fname
;
1347 io2
.in
.durable_handle
= h
;
1350 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1351 CHECK_STATUS(status
, NT_STATUS_OK
);
1353 _h
= io2
.out
.file
.handle
;
1355 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1356 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1361 smb2_util_close(tree2
, *h
);
1364 smb2_util_unlink(tree2
, fname
);
1369 talloc_free(mem_ctx
);
1374 static bool test_durable_open_delete_on_close1(struct torture_context
*tctx
,
1375 struct smb2_tree
*tree
)
1378 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1380 struct smb2_handle _h
;
1381 struct smb2_handle
*h
= NULL
;
1382 struct smb2_create io1
, io2
;
1386 /* Choose a random name in case the state is left a little funky. */
1387 snprintf(fname
, 256, "durable_open_delete_on_close1_%s.dat",
1388 generate_random_str(tctx
, 8));
1390 smb2_util_unlink(tree
, fname
);
1392 smb2_oplock_create_share(&io1
, fname
,
1393 smb2_util_share_access(""),
1394 smb2_util_oplock_level("b"));
1395 io1
.in
.durable_open
= true;
1396 io1
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1398 status
= smb2_create(tree
, mem_ctx
, &io1
);
1399 CHECK_STATUS(status
, NT_STATUS_OK
);
1400 _h
= io1
.out
.file
.handle
;
1402 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1403 CHECK_VAL(io1
.out
.durable_open
, true);
1404 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1406 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1407 CHECK_STATUS(status
, NT_STATUS_OK
);
1409 /* disconnect, leaving the durable handle in place */
1412 if (!torture_smb2_connection(tctx
, &tree
)) {
1413 torture_warning(tctx
, "could not reconnect, bailing\n");
1419 * Open the file on the new connection again
1420 * and check that it has been newly created,
1421 * i.e. delete on close was effective on the disconnected handle.
1422 * Also check that the file is really empty,
1423 * the previously written byte gone.
1425 smb2_oplock_create_share(&io2
, fname
,
1426 smb2_util_share_access(""),
1427 smb2_util_oplock_level("b"));
1428 io2
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1430 status
= smb2_create(tree
, mem_ctx
, &io2
);
1431 CHECK_STATUS(status
, NT_STATUS_OK
);
1432 _h
= io2
.out
.file
.handle
;
1434 CHECK_CREATED_SIZE(&io2
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1435 CHECK_VAL(io2
.out
.durable_open
, false);
1436 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1441 smb2_util_close(tree
, *h
);
1444 smb2_util_unlink(tree
, fname
);
1449 talloc_free(mem_ctx
);
1455 static bool test_durable_open_delete_on_close2(struct torture_context
*tctx
,
1456 struct smb2_tree
*tree
)
1459 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1461 struct smb2_handle _h
;
1462 struct smb2_handle
*h
= NULL
;
1463 struct smb2_create io
;
1466 uint64_t previous_session_id
;
1467 uint64_t alloc_size_step
;
1468 struct smbcli_options options
;
1470 options
= tree
->session
->transport
->options
;
1472 /* Choose a random name in case the state is left a little funky. */
1473 snprintf(fname
, 256, "durable_open_delete_on_close2_%s.dat",
1474 generate_random_str(tctx
, 8));
1476 smb2_util_unlink(tree
, fname
);
1478 smb2_oplock_create_share(&io
, fname
,
1479 smb2_util_share_access(""),
1480 smb2_util_oplock_level("b"));
1481 io
.in
.durable_open
= true;
1482 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1484 status
= smb2_create(tree
, mem_ctx
, &io
);
1485 CHECK_STATUS(status
, NT_STATUS_OK
);
1486 _h
= io
.out
.file
.handle
;
1488 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1489 CHECK_VAL(io
.out
.durable_open
, true);
1490 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1492 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1493 CHECK_STATUS(status
, NT_STATUS_OK
);
1495 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1497 /* disconnect, leaving the durable handle in place */
1500 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1503 torture_warning(tctx
, "could not reconnect, bailing\n");
1509 io
.in
.fname
= fname
;
1510 io
.in
.durable_handle
= h
;
1512 status
= smb2_create(tree
, mem_ctx
, &io
);
1513 CHECK_STATUS(status
, NT_STATUS_OK
);
1514 _h
= io
.out
.file
.handle
;
1516 alloc_size_step
= io
.out
.alloc_size
;
1517 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
, alloc_size_step
, 1);
1518 CHECK_VAL(io
.out
.durable_open
, false);
1519 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1521 /* close the file, thereby deleting it */
1522 smb2_util_close(tree
, *h
);
1523 status
= smb2_logoff(tree
->session
);
1526 if (!torture_smb2_connection(tctx
, &tree
)) {
1527 torture_warning(tctx
, "could not reconnect, bailing\n");
1533 * Open the file on the new connection again
1534 * and check that it has been newly created,
1535 * i.e. delete on close was effective on the reconnected handle.
1536 * Also check that the file is really empty,
1537 * the previously written byte gone.
1539 smb2_oplock_create_share(&io
, fname
,
1540 smb2_util_share_access(""),
1541 smb2_util_oplock_level("b"));
1542 io
.in
.durable_open
= true;
1543 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1545 status
= smb2_create(tree
, mem_ctx
, &io
);
1546 CHECK_STATUS(status
, NT_STATUS_OK
);
1547 _h
= io
.out
.file
.handle
;
1549 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1550 CHECK_VAL(io
.out
.durable_open
, true);
1551 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1556 smb2_util_close(tree
, *h
);
1559 smb2_util_unlink(tree
, fname
);
1564 talloc_free(mem_ctx
);
1570 basic testing of SMB2 durable opens
1571 regarding the position information on the handle
1573 static bool test_durable_open_file_position(struct torture_context
*tctx
,
1574 struct smb2_tree
*tree
)
1576 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1577 struct smb2_handle h
;
1578 struct smb2_create io
;
1580 const char *fname
= "durable_open_position.dat";
1581 union smb_fileinfo qfinfo
;
1582 union smb_setfileinfo sfinfo
;
1585 uint64_t previous_session_id
;
1586 struct smbcli_options options
;
1588 options
= tree
->session
->transport
->options
;
1590 smb2_util_unlink(tree
, fname
);
1592 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1593 io
.in
.durable_open
= true;
1595 status
= smb2_create(tree
, mem_ctx
, &io
);
1596 CHECK_STATUS(status
, NT_STATUS_OK
);
1597 h
= io
.out
.file
.handle
;
1598 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1599 CHECK_VAL(io
.out
.durable_open
, true);
1600 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1602 /* TODO: check extra blob content */
1604 ZERO_STRUCT(qfinfo
);
1605 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1606 qfinfo
.generic
.in
.file
.handle
= h
;
1607 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1608 CHECK_STATUS(status
, NT_STATUS_OK
);
1609 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0);
1610 pos
= qfinfo
.position_information
.out
.position
;
1611 torture_comment(tctx
, "position: %llu\n",
1612 (unsigned long long)pos
);
1614 ZERO_STRUCT(sfinfo
);
1615 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
1616 sfinfo
.generic
.in
.file
.handle
= h
;
1617 sfinfo
.position_information
.in
.position
= 0x1000;
1618 status
= smb2_setinfo_file(tree
, &sfinfo
);
1619 CHECK_STATUS(status
, NT_STATUS_OK
);
1621 ZERO_STRUCT(qfinfo
);
1622 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1623 qfinfo
.generic
.in
.file
.handle
= h
;
1624 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1625 CHECK_STATUS(status
, NT_STATUS_OK
);
1626 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1627 pos
= qfinfo
.position_information
.out
.position
;
1628 torture_comment(tctx
, "position: %llu\n",
1629 (unsigned long long)pos
);
1631 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1633 /* tcp disconnect */
1637 /* do a session reconnect */
1638 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1641 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1646 ZERO_STRUCT(qfinfo
);
1647 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1648 qfinfo
.generic
.in
.file
.handle
= h
;
1649 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1650 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1653 io
.in
.fname
= fname
;
1654 io
.in
.durable_handle
= &h
;
1656 status
= smb2_create(tree
, mem_ctx
, &io
);
1657 CHECK_STATUS(status
, NT_STATUS_OK
);
1658 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1659 CHECK_VAL(io
.out
.reserved
, 0x00);
1660 CHECK_VAL(io
.out
.create_action
, NTCREATEX_ACTION_EXISTED
);
1661 CHECK_VAL(io
.out
.alloc_size
, 0);
1662 CHECK_VAL(io
.out
.size
, 0);
1663 CHECK_VAL(io
.out
.file_attr
, FILE_ATTRIBUTE_ARCHIVE
);
1664 CHECK_VAL(io
.out
.reserved2
, 0);
1666 h
= io
.out
.file
.handle
;
1668 ZERO_STRUCT(qfinfo
);
1669 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1670 qfinfo
.generic
.in
.file
.handle
= h
;
1671 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1672 CHECK_STATUS(status
, NT_STATUS_OK
);
1673 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1674 pos
= qfinfo
.position_information
.out
.position
;
1675 torture_comment(tctx
, "position: %llu\n",
1676 (unsigned long long)pos
);
1678 smb2_util_close(tree
, h
);
1680 talloc_free(mem_ctx
);
1682 smb2_util_unlink(tree
, fname
);
1691 Open, disconnect, oplock break, reconnect.
1693 static bool test_durable_open_oplock(struct torture_context
*tctx
,
1694 struct smb2_tree
*tree1
,
1695 struct smb2_tree
*tree2
)
1697 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1698 struct smb2_create io1
, io2
;
1699 struct smb2_handle h1
, h2
;
1704 /* Choose a random name in case the state is left a little funky. */
1705 snprintf(fname
, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1708 smb2_util_unlink(tree1
, fname
);
1710 /* Create with batch oplock */
1711 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1712 io1
.in
.durable_open
= true;
1715 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
1717 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1718 CHECK_STATUS(status
, NT_STATUS_OK
);
1719 h1
= io1
.out
.file
.handle
;
1720 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1721 CHECK_VAL(io1
.out
.durable_open
, true);
1722 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1724 /* Disconnect after getting the batch */
1729 * Windows7 (build 7000) will break a batch oplock immediately if the
1730 * original client is gone. (ZML: This seems like a bug. It should give
1731 * some time for the client to reconnect!)
1733 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1734 CHECK_STATUS(status
, NT_STATUS_OK
);
1735 h2
= io2
.out
.file
.handle
;
1736 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1737 CHECK_VAL(io2
.out
.durable_open
, true);
1738 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1740 /* What if tree1 tries to come back and reclaim? */
1741 if (!torture_smb2_connection(tctx
, &tree1
)) {
1742 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1748 io1
.in
.fname
= fname
;
1749 io1
.in
.durable_handle
= &h1
;
1751 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1752 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1755 smb2_util_close(tree2
, h2
);
1756 smb2_util_unlink(tree2
, fname
);
1765 Open, disconnect, lease break, reconnect.
1767 static bool test_durable_open_lease(struct torture_context
*tctx
,
1768 struct smb2_tree
*tree1
,
1769 struct smb2_tree
*tree2
)
1771 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1772 struct smb2_create io1
, io2
;
1773 struct smb2_lease ls1
, ls2
;
1774 struct smb2_handle h1
, h2
;
1778 uint64_t lease1
, lease2
;
1781 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
1782 if (!(caps
& SMB2_CAP_LEASING
)) {
1783 torture_skip(tctx
, "leases are not supported");
1787 * Choose a random name and random lease in case the state is left a
1792 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
1795 smb2_util_unlink(tree1
, fname
);
1797 /* Create with lease */
1798 smb2_lease_create(&io1
, &ls1
, false /* dir */, fname
,
1799 lease1
, smb2_util_lease_state("RHW"));
1800 io1
.in
.durable_open
= true;
1802 smb2_lease_create(&io2
, &ls2
, false /* dir */, fname
,
1803 lease2
, smb2_util_lease_state("RHW"));
1804 io2
.in
.durable_open
= true;
1805 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
1807 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1808 CHECK_STATUS(status
, NT_STATUS_OK
);
1809 h1
= io1
.out
.file
.handle
;
1810 CHECK_VAL(io1
.out
.durable_open
, true);
1811 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1813 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1814 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease1
);
1815 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease1
);
1816 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
1817 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1819 /* Disconnect after getting the lease */
1824 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1825 * even if the original client is gone. (ZML: This seems like a bug. It
1826 * should give some time for the client to reconnect! And why RH?)
1828 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1829 * Test is adapted accordingly.
1831 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1832 CHECK_STATUS(status
, NT_STATUS_OK
);
1833 h2
= io2
.out
.file
.handle
;
1834 CHECK_VAL(io2
.out
.durable_open
, true);
1835 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1837 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1838 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[0], lease2
);
1839 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[1], ~lease2
);
1840 CHECK_VAL(io2
.out
.lease_response
.lease_state
,
1841 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1843 /* What if tree1 tries to come back and reclaim? */
1844 if (!torture_smb2_connection(tctx
, &tree1
)) {
1845 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1851 io1
.in
.fname
= fname
;
1852 io1
.in
.durable_handle
= &h1
;
1853 io1
.in
.lease_request
= &ls1
;
1855 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1856 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1859 smb2_util_close(tree2
, h2
);
1860 smb2_util_unlink(tree2
, fname
);
1868 static bool test_durable_open_lock_oplock(struct torture_context
*tctx
,
1869 struct smb2_tree
*tree
)
1871 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1872 struct smb2_create io
;
1873 struct smb2_handle h
;
1874 struct smb2_lock lck
;
1875 struct smb2_lock_element el
[2];
1882 snprintf(fname
, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx
, 8));
1885 smb2_util_unlink(tree
, fname
);
1887 /* Create with oplock */
1889 smb2_oplock_create_share(&io
, fname
,
1890 smb2_util_share_access(""),
1891 smb2_util_oplock_level("b"));
1892 io
.in
.durable_open
= true;
1894 status
= smb2_create(tree
, mem_ctx
, &io
);
1895 CHECK_STATUS(status
, NT_STATUS_OK
);
1896 h
= io
.out
.file
.handle
;
1897 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1899 CHECK_VAL(io
.out
.durable_open
, true);
1900 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1905 lck
.in
.lock_count
= 0x0001;
1906 lck
.in
.lock_sequence
= 0x00000000;
1907 lck
.in
.file
.handle
= h
;
1910 el
[0].reserved
= 0x00000000;
1911 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
1912 status
= smb2_lock(tree
, &lck
);
1913 CHECK_STATUS(status
, NT_STATUS_OK
);
1915 /* Disconnect/Reconnect. */
1919 if (!torture_smb2_connection(tctx
, &tree
)) {
1920 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1926 io
.in
.fname
= fname
;
1927 io
.in
.durable_handle
= &h
;
1929 status
= smb2_create(tree
, mem_ctx
, &io
);
1930 CHECK_STATUS(status
, NT_STATUS_OK
);
1931 h
= io
.out
.file
.handle
;
1933 lck
.in
.file
.handle
= h
;
1934 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
1935 status
= smb2_lock(tree
, &lck
);
1936 CHECK_STATUS(status
, NT_STATUS_OK
);
1939 smb2_util_close(tree
, h
);
1940 smb2_util_unlink(tree
, fname
);
1947 Open, take BRL, disconnect, reconnect.
1949 static bool test_durable_open_lock_lease(struct torture_context
*tctx
,
1950 struct smb2_tree
*tree
)
1952 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1953 struct smb2_create io
;
1954 struct smb2_lease ls
;
1955 struct smb2_handle h
;
1956 struct smb2_lock lck
;
1957 struct smb2_lock_element el
[2];
1963 struct smbcli_options options
;
1965 options
= tree
->session
->transport
->options
;
1967 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1968 if (!(caps
& SMB2_CAP_LEASING
)) {
1969 torture_skip(tctx
, "leases are not supported");
1973 * Choose a random name and random lease in case the state is left a
1977 snprintf(fname
, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx
, 8));
1980 smb2_util_unlink(tree
, fname
);
1982 /* Create with lease */
1984 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease
,
1985 smb2_util_lease_state("RWH"));
1986 io
.in
.durable_open
= true;
1988 status
= smb2_create(tree
, mem_ctx
, &io
);
1989 CHECK_STATUS(status
, NT_STATUS_OK
);
1990 h
= io
.out
.file
.handle
;
1991 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1993 CHECK_VAL(io
.out
.durable_open
, true);
1994 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1995 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
1996 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
1997 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1998 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
2003 lck
.in
.lock_count
= 0x0001;
2004 lck
.in
.lock_sequence
= 0x00000000;
2005 lck
.in
.file
.handle
= h
;
2008 el
[0].reserved
= 0x00000000;
2009 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
2010 status
= smb2_lock(tree
, &lck
);
2011 CHECK_STATUS(status
, NT_STATUS_OK
);
2013 /* Disconnect/Reconnect. */
2017 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
2018 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2024 io
.in
.fname
= fname
;
2025 io
.in
.durable_handle
= &h
;
2026 io
.in
.lease_request
= &ls
;
2028 status
= smb2_create(tree
, mem_ctx
, &io
);
2029 CHECK_STATUS(status
, NT_STATUS_OK
);
2030 h
= io
.out
.file
.handle
;
2032 lck
.in
.file
.handle
= h
;
2033 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
2034 status
= smb2_lock(tree
, &lck
);
2035 CHECK_STATUS(status
, NT_STATUS_OK
);
2038 smb2_util_close(tree
, h
);
2039 smb2_util_unlink(tree
, fname
);
2046 * Open with a RH lease, disconnect, open in another tree, reconnect.
2048 * This test actually demonstrates a minimum level of respect for the durable
2049 * open in the face of another open. As long as this test shows an inability to
2050 * reconnect after an open, the oplock/lease tests above will certainly
2051 * demonstrate an error on reconnect.
2053 static bool test_durable_open_open2_lease(struct torture_context
*tctx
,
2054 struct smb2_tree
*tree1
,
2055 struct smb2_tree
*tree2
)
2057 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2058 struct smb2_create io1
, io2
;
2059 struct smb2_lease ls
;
2060 struct smb2_handle h1
, h2
;
2066 struct smbcli_options options
;
2068 options
= tree1
->session
->transport
->options
;
2070 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
2071 if (!(caps
& SMB2_CAP_LEASING
)) {
2072 torture_skip(tctx
, "leases are not supported");
2076 * Choose a random name and random lease in case the state is left a
2080 snprintf(fname
, 256, "durable_open_open2_lease_%s.dat",
2081 generate_random_str(tctx
, 8));
2084 smb2_util_unlink(tree1
, fname
);
2086 /* Create with lease */
2087 smb2_lease_create_share(&io1
, &ls
, false /* dir */, fname
,
2088 smb2_util_share_access(""),
2090 smb2_util_lease_state("RH"));
2091 io1
.in
.durable_open
= true;
2093 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2094 CHECK_STATUS(status
, NT_STATUS_OK
);
2095 h1
= io1
.out
.file
.handle
;
2096 CHECK_VAL(io1
.out
.durable_open
, true);
2097 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2099 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2100 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease
);
2101 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease
);
2102 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
2103 smb2_util_lease_state("RH"));
2109 /* Open the file in tree2 */
2110 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2112 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2113 CHECK_STATUS(status
, NT_STATUS_OK
);
2114 h2
= io2
.out
.file
.handle
;
2115 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2118 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree1
)) {
2119 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2125 io1
.in
.fname
= fname
;
2126 io1
.in
.durable_handle
= &h1
;
2127 io1
.in
.lease_request
= &ls
;
2130 * Windows7 (build 7000) will give away an open immediately if the
2131 * original client is gone. (ZML: This seems like a bug. It should give
2132 * some time for the client to reconnect!)
2134 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2135 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2136 h1
= io1
.out
.file
.handle
;
2140 smb2_util_close(tree1
, h1
);
2141 smb2_util_unlink(tree1
, fname
);
2145 smb2_util_close(tree2
, h2
);
2146 smb2_util_unlink(tree2
, fname
);
2153 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2155 * This test actually demonstrates a minimum level of respect for the durable
2156 * open in the face of another open. As long as this test shows an inability to
2157 * reconnect after an open, the oplock/lease tests above will certainly
2158 * demonstrate an error on reconnect.
2160 static bool test_durable_open_open2_oplock(struct torture_context
*tctx
,
2161 struct smb2_tree
*tree1
,
2162 struct smb2_tree
*tree2
)
2164 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2165 struct smb2_create io1
, io2
;
2166 struct smb2_handle h1
, h2
;
2172 * Choose a random name and random lease in case the state is left a
2175 snprintf(fname
, 256, "durable_open_open2_oplock_%s.dat",
2176 generate_random_str(tctx
, 8));
2179 smb2_util_unlink(tree1
, fname
);
2181 /* Create with batch oplock */
2182 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2183 io1
.in
.durable_open
= true;
2185 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2186 CHECK_STATUS(status
, NT_STATUS_OK
);
2187 h1
= io1
.out
.file
.handle
;
2188 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2189 CHECK_VAL(io1
.out
.durable_open
, true);
2190 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2196 /* Open the file in tree2 */
2197 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2199 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2200 CHECK_STATUS(status
, NT_STATUS_OK
);
2201 h2
= io2
.out
.file
.handle
;
2202 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2205 if (!torture_smb2_connection(tctx
, &tree1
)) {
2206 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2212 io1
.in
.fname
= fname
;
2213 io1
.in
.durable_handle
= &h1
;
2215 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2216 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2217 h1
= io1
.out
.file
.handle
;
2220 smb2_util_close(tree2
, h2
);
2221 smb2_util_unlink(tree2
, fname
);
2222 if (tree1
!= NULL
) {
2223 smb2_util_close(tree1
, h1
);
2224 smb2_util_unlink(tree1
, fname
);
2234 * test behaviour with initial allocation size
2236 static bool test_durable_open_alloc_size(struct torture_context
*tctx
,
2237 struct smb2_tree
*tree
)
2240 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2242 struct smb2_handle _h
;
2243 struct smb2_handle
*h
= NULL
;
2244 struct smb2_create io
;
2246 uint64_t previous_session_id
;
2247 uint64_t alloc_size_step
;
2248 uint64_t initial_alloc_size
= 0x100;
2249 const uint8_t *b
= NULL
;
2250 struct smbcli_options options
;
2252 options
= tree
->session
->transport
->options
;
2254 /* Choose a random name in case the state is left a little funky. */
2255 snprintf(fname
, 256, "durable_open_alloc_size_%s.dat",
2256 generate_random_str(tctx
, 8));
2258 smb2_util_unlink(tree
, fname
);
2260 smb2_oplock_create_share(&io
, fname
,
2261 smb2_util_share_access(""),
2262 smb2_util_oplock_level("b"));
2263 io
.in
.durable_open
= true;
2264 io
.in
.alloc_size
= initial_alloc_size
;
2266 status
= smb2_create(tree
, mem_ctx
, &io
);
2267 CHECK_STATUS(status
, NT_STATUS_OK
);
2268 _h
= io
.out
.file
.handle
;
2270 CHECK_NOT_VAL(io
.out
.alloc_size
, 0);
2271 alloc_size_step
= io
.out
.alloc_size
;
2272 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
,
2273 alloc_size_step
, 0);
2274 CHECK_VAL(io
.out
.durable_open
, true);
2275 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2277 /* prepare buffer */
2278 b
= talloc_zero_size(mem_ctx
, alloc_size_step
);
2281 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2283 /* disconnect, reconnect and then do durable reopen */
2287 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2290 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2296 io
.in
.fname
= fname
;
2297 io
.in
.durable_handle
= h
;
2300 status
= smb2_create(tree
, mem_ctx
, &io
);
2301 CHECK_STATUS(status
, NT_STATUS_OK
);
2302 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2303 alloc_size_step
, 0);
2304 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2305 _h
= io
.out
.file
.handle
;
2308 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2310 /* write one byte */
2311 status
= smb2_util_write(tree
, *h
, b
, 0, 1);
2312 CHECK_STATUS(status
, NT_STATUS_OK
);
2314 /* disconnect, reconnect and then do durable reopen */
2318 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2321 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2327 io
.in
.fname
= fname
;
2328 io
.in
.durable_handle
= h
;
2331 status
= smb2_create(tree
, mem_ctx
, &io
);
2332 CHECK_STATUS(status
, NT_STATUS_OK
);
2333 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2334 alloc_size_step
, 1);
2335 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2336 _h
= io
.out
.file
.handle
;
2339 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2341 /* write more byte than initial allocation size */
2342 status
= smb2_util_write(tree
, *h
, b
, 1, alloc_size_step
);
2344 /* disconnect, reconnect and then do durable reopen */
2348 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2351 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2357 io
.in
.fname
= fname
;
2358 io
.in
.durable_handle
= h
;
2361 status
= smb2_create(tree
, mem_ctx
, &io
);
2362 CHECK_STATUS(status
, NT_STATUS_OK
);
2363 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2364 alloc_size_step
* 2, alloc_size_step
+ 1);
2365 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2366 _h
= io
.out
.file
.handle
;
2371 smb2_util_close(tree
, *h
);
2374 smb2_util_unlink(tree
, fname
);
2378 talloc_free(mem_ctx
);
2384 * test behaviour when a disconnect happens while creating a read-only file
2386 static bool test_durable_open_read_only(struct torture_context
*tctx
,
2387 struct smb2_tree
*tree
)
2390 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2392 struct smb2_handle _h
;
2393 struct smb2_handle
*h
= NULL
;
2394 struct smb2_create io
;
2396 uint64_t previous_session_id
;
2397 const uint8_t b
= 0;
2398 uint64_t alloc_size
= 0;
2399 struct smbcli_options options
;
2401 options
= tree
->session
->transport
->options
;
2403 /* Choose a random name in case the state is left a little funky. */
2404 snprintf(fname
, 256, "durable_open_initial_alloc_%s.dat",
2405 generate_random_str(tctx
, 8));
2407 smb2_util_unlink(tree
, fname
);
2409 smb2_oplock_create_share(&io
, fname
,
2410 smb2_util_share_access(""),
2411 smb2_util_oplock_level("b"));
2412 io
.in
.durable_open
= true;
2413 io
.in
.file_attributes
= FILE_ATTRIBUTE_READONLY
;
2415 status
= smb2_create(tree
, mem_ctx
, &io
);
2416 CHECK_STATUS(status
, NT_STATUS_OK
);
2417 _h
= io
.out
.file
.handle
;
2419 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
);
2420 CHECK_VAL(io
.out
.durable_open
, true);
2421 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2423 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2425 /* write one byte */
2426 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
2427 CHECK_STATUS(status
, NT_STATUS_OK
);
2429 /* disconnect, reconnect and then do durable reopen */
2433 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2436 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2442 io
.in
.fname
= fname
;
2443 io
.in
.durable_handle
= h
;
2446 status
= smb2_create(tree
, mem_ctx
, &io
);
2447 CHECK_STATUS(status
, NT_STATUS_OK
);
2448 alloc_size
= io
.out
.alloc_size
;
2449 CHECK_CREATED_SIZE(&io
, EXISTED
,
2450 FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
,
2452 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2453 _h
= io
.out
.file
.handle
;
2456 /* write one byte */
2457 status
= smb2_util_write(tree
, *h
, &b
, 1, 1);
2458 CHECK_STATUS(status
, NT_STATUS_OK
);
2462 union smb_setfileinfo sfinfo
;
2464 ZERO_STRUCT(sfinfo
);
2465 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
2466 sfinfo
.basic_info
.in
.file
.handle
= *h
;
2467 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_NORMAL
;
2468 smb2_setinfo_file(tree
, &sfinfo
);
2470 smb2_util_close(tree
, *h
);
2473 smb2_util_unlink(tree
, fname
);
2477 talloc_free(mem_ctx
);
2483 * durable open with oplock, disconnect, exit
2485 static bool test_durable_open_oplock_disconnect(struct torture_context
*tctx
,
2486 struct smb2_tree
*tree
)
2488 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2489 struct smb2_create io
;
2490 struct smb2_handle _h
;
2491 struct smb2_handle
*h
= NULL
;
2496 snprintf(fname
, 256, "durable_open_oplock_disconnect_%s.dat",
2497 generate_random_str(mem_ctx
, 8));
2499 smb2_util_unlink(tree
, fname
);
2501 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2502 io
.in
.durable_open
= true;
2504 status
= smb2_create(tree
, mem_ctx
, &io
);
2505 CHECK_STATUS(status
, NT_STATUS_OK
);
2507 _h
= io
.out
.file
.handle
;
2510 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2511 CHECK_VAL(io
.out
.durable_open
, true);
2512 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2521 smb2_util_close(tree
, *h
);
2523 smb2_util_unlink(tree
, fname
);
2525 talloc_free(mem_ctx
);
2530 struct torture_suite
*torture_smb2_durable_open_init(void)
2532 struct torture_suite
*suite
=
2533 torture_suite_create(talloc_autofree_context(), "durable-open");
2535 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_open_open_oplock
);
2536 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_open_open_lease
);
2537 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_open_reopen1
);
2538 torture_suite_add_1smb2_test(suite
, "reopen1a", test_durable_open_reopen1a
);
2539 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_open_reopen2
);
2540 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_open_reopen2_lease
);
2541 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2
);
2542 torture_suite_add_1smb2_test(suite
, "reopen2a", test_durable_open_reopen2a
);
2543 torture_suite_add_1smb2_test(suite
, "reopen3", test_durable_open_reopen3
);
2544 torture_suite_add_1smb2_test(suite
, "reopen4", test_durable_open_reopen4
);
2545 torture_suite_add_1smb2_test(suite
, "delete_on_close1",
2546 test_durable_open_delete_on_close1
);
2547 torture_suite_add_1smb2_test(suite
, "delete_on_close2",
2548 test_durable_open_delete_on_close2
);
2549 torture_suite_add_1smb2_test(suite
, "file-position",
2550 test_durable_open_file_position
);
2551 torture_suite_add_2smb2_test(suite
, "oplock", test_durable_open_oplock
);
2552 torture_suite_add_2smb2_test(suite
, "lease", test_durable_open_lease
);
2553 torture_suite_add_1smb2_test(suite
, "lock-oplock", test_durable_open_lock_oplock
);
2554 torture_suite_add_1smb2_test(suite
, "lock-lease", test_durable_open_lock_lease
);
2555 torture_suite_add_2smb2_test(suite
, "open2-lease",
2556 test_durable_open_open2_lease
);
2557 torture_suite_add_2smb2_test(suite
, "open2-oplock",
2558 test_durable_open_open2_oplock
);
2559 torture_suite_add_1smb2_test(suite
, "alloc-size",
2560 test_durable_open_alloc_size
);
2561 torture_suite_add_1smb2_test(suite
, "read-only",
2562 test_durable_open_read_only
);
2564 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-OPEN tests");
2569 struct torture_suite
*torture_smb2_durable_open_disconnect_init(void)
2571 struct torture_suite
*suite
=
2572 torture_suite_create(talloc_autofree_context(),
2573 "durable-open-disconnect");
2575 torture_suite_add_1smb2_test(suite
, "open-oplock-disconnect",
2576 test_durable_open_oplock_disconnect
);
2578 suite
->description
= talloc_strdup(suite
,
2579 "SMB2-DURABLE-OPEN-DISCONNECT tests");