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, correct) do { \
39 if ((v) == (correct)) { \
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)correct); \
45 #define CHECK_STATUS(status, correct) do { \
46 if (!NT_STATUS_EQUAL(status, correct)) { \
47 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48 nt_errstr(status), nt_errstr(correct)); \
53 #define CHECK_CREATED(__io, __created, __attribute) \
55 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56 CHECK_VAL((__io)->out.alloc_size, 0); \
57 CHECK_VAL((__io)->out.size, 0); \
58 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
59 CHECK_VAL((__io)->out.reserved2, 0); \
62 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
64 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
65 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
66 CHECK_VAL((__io)->out.size, (__size)); \
67 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
68 CHECK_VAL((__io)->out.reserved2, 0); \
74 * basic durable_open test.
75 * durable state should only be granted when requested
76 * along with a batch oplock or a handle lease.
78 * This test tests durable open with all possible oplock types.
81 struct durable_open_vs_oplock
{
83 const char *share_mode
;
87 #define NUM_OPLOCK_TYPES 4
88 #define NUM_SHARE_MODES 8
89 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
90 static struct durable_open_vs_oplock durable_open_vs_oplock_table
[NUM_OPLOCK_OPEN_TESTS
] =
105 { "s", "RD", false },
106 { "s", "RW", false },
107 { "s", "WD", false },
108 { "s", "RWD", false },
114 { "x", "RD", false },
115 { "x", "RW", false },
116 { "x", "WD", false },
117 { "x", "RWD", false },
126 { "b", "RWD", true },
129 static bool test_one_durable_open_open_oplock(struct torture_context
*tctx
,
130 struct smb2_tree
*tree
,
132 struct durable_open_vs_oplock test
)
135 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
136 struct smb2_handle _h
;
137 struct smb2_handle
*h
= NULL
;
139 struct smb2_create io
;
141 smb2_util_unlink(tree
, fname
);
143 smb2_oplock_create_share(&io
, fname
,
144 smb2_util_share_access(test
.share_mode
),
145 smb2_util_oplock_level(test
.level
));
146 io
.in
.durable_open
= true;
148 status
= smb2_create(tree
, mem_ctx
, &io
);
149 CHECK_STATUS(status
, NT_STATUS_OK
);
150 _h
= io
.out
.file
.handle
;
152 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
153 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
154 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level(test
.level
));
158 smb2_util_close(tree
, *h
);
160 smb2_util_unlink(tree
, fname
);
161 talloc_free(mem_ctx
);
166 static bool test_durable_open_open_oplock(struct torture_context
*tctx
,
167 struct smb2_tree
*tree
)
169 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
174 /* Choose a random name in case the state is left a little funky. */
175 snprintf(fname
, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx
, 8));
177 smb2_util_unlink(tree
, fname
);
179 /* test various oplock levels with durable open */
181 for (i
= 0; i
< NUM_OPLOCK_OPEN_TESTS
; i
++) {
182 ret
= test_one_durable_open_open_oplock(tctx
,
185 durable_open_vs_oplock_table
[i
]);
192 smb2_util_unlink(tree
, fname
);
194 talloc_free(mem_ctx
);
200 * basic durable_open test.
201 * durable state should only be granted when requested
202 * along with a batch oplock or a handle lease.
204 * This test tests durable open with all valid lease types.
207 struct durable_open_vs_lease
{
209 const char *share_mode
;
213 #define NUM_LEASE_TYPES 5
214 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
215 static struct durable_open_vs_lease durable_open_vs_lease_table
[NUM_LEASE_OPEN_TESTS
] =
224 { "", "RWD", false },
230 { "R", "RW", false },
231 { "R", "RD", false },
232 { "R", "DW", false },
233 { "R", "RWD", false },
236 { "RW", "R", false },
237 { "RW", "W", false },
238 { "RW", "D", false },
239 { "RW", "RW", false },
240 { "RW", "RD", false },
241 { "RW", "WD", false },
242 { "RW", "RWD", false },
248 { "RH", "RW", true },
249 { "RH", "RD", true },
250 { "RH", "WD", true },
251 { "RH", "RWD", true },
254 { "RHW", "R", true },
255 { "RHW", "W", true },
256 { "RHW", "D", true },
257 { "RHW", "RW", true },
258 { "RHW", "RD", true },
259 { "RHW", "WD", true },
260 { "RHW", "RWD", true },
263 static bool test_one_durable_open_open_lease(struct torture_context
*tctx
,
264 struct smb2_tree
*tree
,
266 struct durable_open_vs_lease test
)
269 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
270 struct smb2_handle _h
;
271 struct smb2_handle
*h
= NULL
;
273 struct smb2_create io
;
274 struct smb2_lease ls
;
278 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
279 if (!(caps
& SMB2_CAP_LEASING
)) {
280 torture_skip(tctx
, "leases are not supported");
283 smb2_util_unlink(tree
, fname
);
287 smb2_lease_create_share(&io
, &ls
, false /* dir */, fname
,
288 smb2_util_share_access(test
.share_mode
),
290 smb2_util_lease_state(test
.type
));
291 io
.in
.durable_open
= true;
293 status
= smb2_create(tree
, mem_ctx
, &io
);
294 CHECK_STATUS(status
, NT_STATUS_OK
);
295 _h
= io
.out
.file
.handle
;
297 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
298 CHECK_VAL(io
.out
.durable_open
, test
.expected
);
299 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
300 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
301 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
302 CHECK_VAL(io
.out
.lease_response
.lease_state
,
303 smb2_util_lease_state(test
.type
));
306 smb2_util_close(tree
, *h
);
308 smb2_util_unlink(tree
, fname
);
309 talloc_free(mem_ctx
);
314 static bool test_durable_open_open_lease(struct torture_context
*tctx
,
315 struct smb2_tree
*tree
)
317 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
323 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
324 if (!(caps
& SMB2_CAP_LEASING
)) {
325 torture_skip(tctx
, "leases are not supported");
328 /* Choose a random name in case the state is left a little funky. */
329 snprintf(fname
, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx
, 8));
331 smb2_util_unlink(tree
, fname
);
334 /* test various oplock levels with durable open */
336 for (i
= 0; i
< NUM_LEASE_OPEN_TESTS
; i
++) {
337 ret
= test_one_durable_open_open_lease(tctx
,
340 durable_open_vs_lease_table
[i
]);
347 smb2_util_unlink(tree
, fname
);
349 talloc_free(mem_ctx
);
355 * basic test for doing a durable open
356 * and do a durable reopen on the same connection
357 * while the first open is still active (fails)
359 static bool test_durable_open_reopen1(struct torture_context
*tctx
,
360 struct smb2_tree
*tree
)
363 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
365 struct smb2_handle _h
;
366 struct smb2_handle
*h
= NULL
;
367 struct smb2_create io1
, io2
;
370 /* Choose a random name in case the state is left a little funky. */
371 snprintf(fname
, 256, "durable_open_reopen1_%s.dat",
372 generate_random_str(tctx
, 8));
374 smb2_util_unlink(tree
, fname
);
376 smb2_oplock_create_share(&io1
, fname
,
377 smb2_util_share_access(""),
378 smb2_util_oplock_level("b"));
379 io1
.in
.durable_open
= true;
381 status
= smb2_create(tree
, mem_ctx
, &io1
);
382 CHECK_STATUS(status
, NT_STATUS_OK
);
383 _h
= io1
.out
.file
.handle
;
385 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
386 CHECK_VAL(io1
.out
.durable_open
, true);
387 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
389 /* try a durable reconnect while the file is still open */
391 io2
.in
.fname
= fname
;
392 io2
.in
.durable_handle
= h
;
394 status
= smb2_create(tree
, mem_ctx
, &io2
);
395 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
399 smb2_util_close(tree
, *h
);
402 smb2_util_unlink(tree
, fname
);
406 talloc_free(mem_ctx
);
412 * Basic test for doing a durable open
413 * and do a session reconnect while the first
414 * session is still active and the handle is
415 * still open in the client.
416 * This closes the original session and a
417 * durable reconnect on the new session succeeds.
419 static bool test_durable_open_reopen1a(struct torture_context
*tctx
,
420 struct smb2_tree
*tree
)
423 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
425 struct smb2_handle _h
;
426 struct smb2_handle
*h
= NULL
;
427 struct smb2_create io1
, io2
;
429 struct smb2_tree
*tree2
= NULL
;
430 uint64_t previous_session_id
;
431 struct smbcli_options options
;
433 options
= tree
->session
->transport
->options
;
435 /* Choose a random name in case the state is left a little funky. */
436 snprintf(fname
, 256, "durable_open_reopen1a_%s.dat",
437 generate_random_str(tctx
, 8));
439 smb2_util_unlink(tree
, fname
);
441 smb2_oplock_create_share(&io1
, fname
,
442 smb2_util_share_access(""),
443 smb2_util_oplock_level("b"));
444 io1
.in
.durable_open
= true;
446 status
= smb2_create(tree
, mem_ctx
, &io1
);
447 CHECK_STATUS(status
, NT_STATUS_OK
);
448 _h
= io1
.out
.file
.handle
;
450 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
451 CHECK_VAL(io1
.out
.durable_open
, true);
452 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
455 * a session reconnect on a second tcp connection
458 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
460 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
463 torture_warning(tctx
, "couldn't reconnect, bailing\n");
469 * check that this has deleted the old session
473 io2
.in
.fname
= fname
;
474 io2
.in
.durable_handle
= h
;
476 status
= smb2_create(tree
, mem_ctx
, &io2
);
477 CHECK_STATUS(status
, NT_STATUS_USER_SESSION_DELETED
);
480 * but a durable reconnect on the new session succeeds:
484 io2
.in
.fname
= fname
;
485 io2
.in
.durable_handle
= h
;
487 status
= smb2_create(tree2
, mem_ctx
, &io2
);
488 CHECK_STATUS(status
, NT_STATUS_OK
);
489 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
490 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
491 _h
= io2
.out
.file
.handle
;
496 smb2_util_close(tree2
, *h
);
499 smb2_util_unlink(tree2
, fname
);
505 talloc_free(mem_ctx
);
511 * basic test for doing a durable open
512 * tcp disconnect, reconnect, do a durable reopen (succeeds)
514 static bool test_durable_open_reopen2(struct torture_context
*tctx
,
515 struct smb2_tree
*tree
)
518 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
520 struct smb2_handle _h
;
521 struct smb2_handle
*h
= NULL
;
522 struct smb2_create io
;
525 /* Choose a random name in case the state is left a little funky. */
526 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
527 generate_random_str(tctx
, 8));
529 smb2_util_unlink(tree
, fname
);
531 smb2_oplock_create_share(&io
, fname
,
532 smb2_util_share_access(""),
533 smb2_util_oplock_level("b"));
534 io
.in
.durable_open
= true;
536 status
= smb2_create(tree
, mem_ctx
, &io
);
537 CHECK_STATUS(status
, NT_STATUS_OK
);
538 _h
= io
.out
.file
.handle
;
540 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
541 CHECK_VAL(io
.out
.durable_open
, true);
542 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
544 /* disconnect, leaving the durable in place */
547 if (!torture_smb2_connection(tctx
, &tree
)) {
548 torture_warning(tctx
, "couldn't reconnect, bailing\n");
554 /* the path name is ignored by the server */
556 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
559 status
= smb2_create(tree
, mem_ctx
, &io
);
560 CHECK_STATUS(status
, NT_STATUS_OK
);
561 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
562 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
563 _h
= io
.out
.file
.handle
;
566 /* disconnect again, leaving the durable in place */
569 if (!torture_smb2_connection(tctx
, &tree
)) {
570 torture_warning(tctx
, "couldn't reconnect, bailing\n");
576 * show that the filename and many other fields
577 * are ignored. only the reconnect request blob
581 /* the path name is ignored by the server */
582 io
.in
.security_flags
= 0x78;
583 io
.in
.oplock_level
= 0x78;
584 io
.in
.impersonation_level
= 0x12345678;
585 io
.in
.create_flags
= 0x12345678;
586 io
.in
.reserved
= 0x12345678;
587 io
.in
.desired_access
= 0x12345678;
588 io
.in
.file_attributes
= 0x12345678;
589 io
.in
.share_access
= 0x12345678;
590 io
.in
.create_disposition
= 0x12345678;
591 io
.in
.create_options
= 0x12345678;
592 io
.in
.fname
= "__non_existing_fname__";
593 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
596 status
= smb2_create(tree
, mem_ctx
, &io
);
597 CHECK_STATUS(status
, NT_STATUS_OK
);
598 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
599 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
600 _h
= io
.out
.file
.handle
;
603 /* disconnect, leaving the durable in place */
606 if (!torture_smb2_connection(tctx
, &tree
)) {
607 torture_warning(tctx
, "couldn't reconnect, bailing\n");
613 * show that an additionally specified durable v1 request
614 * is ignored by the server.
615 * See MS-SMB2, 3.3.5.9.7
616 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
619 /* the path name is ignored by the server */
621 io
.in
.durable_handle
= h
; /* durable v1 reconnect request */
622 io
.in
.durable_open
= true; /* durable v1 handle request */
625 status
= smb2_create(tree
, mem_ctx
, &io
);
626 CHECK_STATUS(status
, NT_STATUS_OK
);
627 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
628 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
629 _h
= io
.out
.file
.handle
;
635 smb2_util_close(tree
, *h
);
638 smb2_util_unlink(tree
, fname
);
643 talloc_free(mem_ctx
);
649 * lease variant of reopen2
650 * basic test for doing a durable open
651 * tcp disconnect, reconnect, do a durable reopen (succeeds)
653 static bool test_durable_open_reopen2_lease(struct torture_context
*tctx
,
654 struct smb2_tree
*tree
)
657 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
659 struct smb2_handle _h
;
660 struct smb2_handle
*h
= NULL
;
661 struct smb2_create io
;
662 struct smb2_lease ls
;
665 struct smbcli_options options
;
668 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
669 if (!(caps
& SMB2_CAP_LEASING
)) {
670 torture_skip(tctx
, "leases are not supported");
673 options
= tree
->session
->transport
->options
;
675 /* Choose a random name in case the state is left a little funky. */
676 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
677 generate_random_str(tctx
, 8));
679 smb2_util_unlink(tree
, fname
);
681 lease_key
= random();
682 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease_key
,
683 smb2_util_lease_state("RWH"));
684 io
.in
.durable_open
= true;
686 status
= smb2_create(tree
, mem_ctx
, &io
);
687 CHECK_STATUS(status
, NT_STATUS_OK
);
688 _h
= io
.out
.file
.handle
;
690 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
692 CHECK_VAL(io
.out
.durable_open
, true);
693 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
694 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
695 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
696 CHECK_VAL(io
.out
.lease_response
.lease_state
,
697 smb2_util_lease_state("RWH"));
698 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
699 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
701 /* disconnect, reconnect and then do durable reopen */
704 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
705 torture_warning(tctx
, "couldn't reconnect, bailing\n");
711 /* a few failure tests: */
714 * several attempts without lease attached:
715 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
716 * irrespective of file name provided
721 io
.in
.durable_handle
= h
;
722 status
= smb2_create(tree
, mem_ctx
, &io
);
723 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
726 io
.in
.fname
= "__non_existing_fname__";
727 io
.in
.durable_handle
= h
;
728 status
= smb2_create(tree
, mem_ctx
, &io
);
729 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
733 io
.in
.durable_handle
= h
;
734 status
= smb2_create(tree
, mem_ctx
, &io
);
735 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
738 * attempt with lease provided, but
739 * with a changed lease key. => fails
743 io
.in
.durable_open
= false;
744 io
.in
.durable_handle
= h
;
745 io
.in
.lease_request
= &ls
;
746 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
747 /* a wrong lease key lets the request fail */
748 ls
.lease_key
.data
[0]++;
750 status
= smb2_create(tree
, mem_ctx
, &io
);
751 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
753 /* restore the correct lease key */
754 ls
.lease_key
.data
[0]--;
757 * this last failing attempt is almost correct:
758 * only problem is: we use the wrong filename...
759 * Note that this gives INVALID_PARAMETER.
760 * This is different from oplocks!
763 io
.in
.fname
= "__non_existing_fname__";
764 io
.in
.durable_open
= false;
765 io
.in
.durable_handle
= h
;
766 io
.in
.lease_request
= &ls
;
767 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
769 status
= smb2_create(tree
, mem_ctx
, &io
);
770 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
773 * Now for a succeeding reconnect:
778 io
.in
.durable_open
= false;
779 io
.in
.durable_handle
= h
;
780 io
.in
.lease_request
= &ls
;
781 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
783 /* the requested lease state is irrelevant */
784 ls
.lease_state
= smb2_util_lease_state("");
788 status
= smb2_create(tree
, mem_ctx
, &io
);
789 CHECK_STATUS(status
, NT_STATUS_OK
);
791 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
792 CHECK_VAL(io
.out
.durable_open
, false);
793 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
794 CHECK_VAL(io
.out
.persistent_open
, false);
795 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
796 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
797 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
798 CHECK_VAL(io
.out
.lease_response
.lease_state
,
799 smb2_util_lease_state("RWH"));
800 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
801 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
802 _h
= io
.out
.file
.handle
;
805 /* disconnect one more time */
808 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
809 torture_warning(tctx
, "couldn't reconnect, bailing\n");
815 * demonstrate that various parameters are ignored
821 * These are completely ignored by the server
823 io
.in
.security_flags
= 0x78;
824 io
.in
.oplock_level
= 0x78;
825 io
.in
.impersonation_level
= 0x12345678;
826 io
.in
.create_flags
= 0x12345678;
827 io
.in
.reserved
= 0x12345678;
828 io
.in
.desired_access
= 0x12345678;
829 io
.in
.file_attributes
= 0x12345678;
830 io
.in
.share_access
= 0x12345678;
831 io
.in
.create_disposition
= 0x12345678;
832 io
.in
.create_options
= 0x12345678;
835 * only these are checked:
837 * - io.in.durable_handle,
838 * - io.in.lease_request->lease_key
842 io
.in
.durable_open_v2
= false;
843 io
.in
.durable_handle_v2
= h
;
844 io
.in
.lease_request
= &ls
;
846 /* the requested lease state is irrelevant */
847 ls
.lease_state
= smb2_util_lease_state("");
851 status
= smb2_create(tree
, mem_ctx
, &io
);
852 CHECK_STATUS(status
, NT_STATUS_OK
);
854 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
855 CHECK_VAL(io
.out
.durable_open
, false);
856 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
857 CHECK_VAL(io
.out
.persistent_open
, false);
858 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
859 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease_key
);
860 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease_key
);
861 CHECK_VAL(io
.out
.lease_response
.lease_state
,
862 smb2_util_lease_state("RWH"));
863 CHECK_VAL(io
.out
.lease_response
.lease_flags
, 0);
864 CHECK_VAL(io
.out
.lease_response
.lease_duration
, 0);
866 _h
= io
.out
.file
.handle
;
872 smb2_util_close(tree
, *h
);
875 smb2_util_unlink(tree
, fname
);
880 talloc_free(mem_ctx
);
886 * lease v2 variant of reopen2
887 * basic test for doing a durable open
888 * tcp disconnect, reconnect, do a durable reopen (succeeds)
890 static bool test_durable_open_reopen2_lease_v2(struct torture_context
*tctx
,
891 struct smb2_tree
*tree
)
894 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
896 struct smb2_handle _h
;
897 struct smb2_handle
*h
= NULL
;
898 struct smb2_create io
;
899 struct smb2_lease ls
;
902 struct smbcli_options options
;
905 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
906 if (!(caps
& SMB2_CAP_LEASING
)) {
907 torture_skip(tctx
, "leases are not supported");
910 options
= tree
->session
->transport
->options
;
912 /* Choose a random name in case the state is left a little funky. */
913 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
914 generate_random_str(tctx
, 8));
916 smb2_util_unlink(tree
, fname
);
918 lease_key
= random();
919 smb2_lease_v2_create(&io
, &ls
, false /* dir */, fname
,
920 lease_key
, 0, /* parent lease key */
921 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
922 io
.in
.durable_open
= true;
924 status
= smb2_create(tree
, mem_ctx
, &io
);
925 CHECK_STATUS(status
, NT_STATUS_OK
);
926 _h
= io
.out
.file
.handle
;
928 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
930 CHECK_VAL(io
.out
.durable_open
, true);
931 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
932 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
933 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
934 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
935 smb2_util_lease_state("RWH"));
936 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
937 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
939 /* disconnect, reconnect and then do durable reopen */
942 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
943 torture_warning(tctx
, "couldn't reconnect, bailing\n");
948 /* a few failure tests: */
951 * several attempts without lease attached:
952 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
953 * irrespective of file name provided
958 io
.in
.durable_handle
= h
;
959 status
= smb2_create(tree
, mem_ctx
, &io
);
960 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
963 io
.in
.fname
= "__non_existing_fname__";
964 io
.in
.durable_handle
= h
;
965 status
= smb2_create(tree
, mem_ctx
, &io
);
966 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
970 io
.in
.durable_handle
= h
;
971 status
= smb2_create(tree
, mem_ctx
, &io
);
972 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
975 * attempt with lease provided, but
976 * with a changed lease key. => fails
980 io
.in
.durable_open
= false;
981 io
.in
.durable_handle
= h
;
982 io
.in
.lease_request_v2
= &ls
;
983 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
984 /* a wrong lease key lets the request fail */
985 ls
.lease_key
.data
[0]++;
987 status
= smb2_create(tree
, mem_ctx
, &io
);
988 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
990 /* restore the correct lease key */
991 ls
.lease_key
.data
[0]--;
994 * this last failing attempt is almost correct:
995 * only problem is: we use the wrong filename...
996 * Note that this gives INVALID_PARAMETER.
997 * This is different from oplocks!
1000 io
.in
.fname
= "__non_existing_fname__";
1001 io
.in
.durable_open
= false;
1002 io
.in
.durable_handle
= h
;
1003 io
.in
.lease_request_v2
= &ls
;
1004 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1006 status
= smb2_create(tree
, mem_ctx
, &io
);
1007 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
1010 * Now for a succeeding reconnect:
1014 io
.in
.fname
= fname
;
1015 io
.in
.durable_open
= false;
1016 io
.in
.durable_handle
= h
;
1017 io
.in
.lease_request_v2
= &ls
;
1018 io
.in
.oplock_level
= SMB2_OPLOCK_LEVEL_LEASE
;
1020 /* the requested lease state is irrelevant */
1021 ls
.lease_state
= smb2_util_lease_state("");
1025 status
= smb2_create(tree
, mem_ctx
, &io
);
1026 CHECK_STATUS(status
, NT_STATUS_OK
);
1028 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1029 CHECK_VAL(io
.out
.durable_open
, false);
1030 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1031 CHECK_VAL(io
.out
.persistent_open
, false);
1032 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1033 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1034 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1035 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1036 smb2_util_lease_state("RWH"));
1037 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1038 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1039 _h
= io
.out
.file
.handle
;
1042 /* disconnect one more time */
1045 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
1046 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1052 * demonstrate that various parameters are ignored
1058 * These are completely ignored by the server
1060 io
.in
.security_flags
= 0x78;
1061 io
.in
.oplock_level
= 0x78;
1062 io
.in
.impersonation_level
= 0x12345678;
1063 io
.in
.create_flags
= 0x12345678;
1064 io
.in
.reserved
= 0x12345678;
1065 io
.in
.desired_access
= 0x12345678;
1066 io
.in
.file_attributes
= 0x12345678;
1067 io
.in
.share_access
= 0x12345678;
1068 io
.in
.create_disposition
= 0x12345678;
1069 io
.in
.create_options
= 0x12345678;
1072 * only these are checked:
1074 * - io.in.durable_handle,
1075 * - io.in.lease_request->lease_key
1078 io
.in
.fname
= fname
;
1079 io
.in
.durable_open_v2
= false;
1080 io
.in
.durable_handle_v2
= h
;
1081 io
.in
.lease_request_v2
= &ls
;
1083 /* the requested lease state is irrelevant */
1084 ls
.lease_state
= smb2_util_lease_state("");
1088 status
= smb2_create(tree
, mem_ctx
, &io
);
1089 CHECK_STATUS(status
, NT_STATUS_OK
);
1091 CHECK_CREATED(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1092 CHECK_VAL(io
.out
.durable_open
, false);
1093 CHECK_VAL(io
.out
.durable_open_v2
, false); /* no dh2q response blob */
1094 CHECK_VAL(io
.out
.persistent_open
, false);
1095 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1096 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[0], lease_key
);
1097 CHECK_VAL(io
.out
.lease_response_v2
.lease_key
.data
[1], ~lease_key
);
1098 CHECK_VAL(io
.out
.lease_response_v2
.lease_state
,
1099 smb2_util_lease_state("RWH"));
1100 CHECK_VAL(io
.out
.lease_response_v2
.lease_flags
, 0);
1101 CHECK_VAL(io
.out
.lease_response_v2
.lease_duration
, 0);
1103 _h
= io
.out
.file
.handle
;
1109 smb2_util_close(tree
, *h
);
1112 smb2_util_unlink(tree
, fname
);
1117 talloc_free(mem_ctx
);
1123 * basic test for doing a durable open
1124 * tcp disconnect, reconnect with a session reconnect and
1125 * do a durable reopen (succeeds)
1127 static bool test_durable_open_reopen2a(struct torture_context
*tctx
,
1128 struct smb2_tree
*tree
)
1131 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1133 struct smb2_handle _h
;
1134 struct smb2_handle
*h
= NULL
;
1135 struct smb2_create io1
, io2
;
1136 uint64_t previous_session_id
;
1138 struct smbcli_options options
;
1140 options
= tree
->session
->transport
->options
;
1142 /* Choose a random name in case the state is left a little funky. */
1143 snprintf(fname
, 256, "durable_open_reopen2_%s.dat",
1144 generate_random_str(tctx
, 8));
1146 smb2_util_unlink(tree
, fname
);
1148 smb2_oplock_create_share(&io1
, fname
,
1149 smb2_util_share_access(""),
1150 smb2_util_oplock_level("b"));
1151 io1
.in
.durable_open
= true;
1153 status
= smb2_create(tree
, mem_ctx
, &io1
);
1154 CHECK_STATUS(status
, NT_STATUS_OK
);
1155 _h
= io1
.out
.file
.handle
;
1157 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1158 CHECK_VAL(io1
.out
.durable_open
, true);
1159 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1161 /* disconnect, reconnect and then do durable reopen */
1162 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1166 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1169 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1175 io2
.in
.fname
= fname
;
1176 io2
.in
.durable_handle
= h
;
1179 status
= smb2_create(tree
, mem_ctx
, &io2
);
1180 CHECK_STATUS(status
, NT_STATUS_OK
);
1181 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1182 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1183 _h
= io2
.out
.file
.handle
;
1189 smb2_util_close(tree
, *h
);
1192 smb2_util_unlink(tree
, fname
);
1197 talloc_free(mem_ctx
);
1204 * basic test for doing a durable open:
1205 * tdis, new tcon, try durable reopen (fails)
1207 static bool test_durable_open_reopen3(struct torture_context
*tctx
,
1208 struct smb2_tree
*tree
)
1211 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1213 struct smb2_handle _h
;
1214 struct smb2_handle
*h
= NULL
;
1215 struct smb2_create io1
, io2
;
1217 struct smb2_tree
*tree2
;
1219 /* Choose a random name in case the state is left a little funky. */
1220 snprintf(fname
, 256, "durable_open_reopen3_%s.dat",
1221 generate_random_str(tctx
, 8));
1223 smb2_util_unlink(tree
, fname
);
1225 smb2_oplock_create_share(&io1
, fname
,
1226 smb2_util_share_access(""),
1227 smb2_util_oplock_level("b"));
1228 io1
.in
.durable_open
= true;
1230 status
= smb2_create(tree
, mem_ctx
, &io1
);
1231 CHECK_STATUS(status
, NT_STATUS_OK
);
1232 _h
= io1
.out
.file
.handle
;
1234 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1235 CHECK_VAL(io1
.out
.durable_open
, true);
1236 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1238 /* disconnect, reconnect and then do durable reopen */
1239 status
= smb2_tdis(tree
);
1240 CHECK_STATUS(status
, NT_STATUS_OK
);
1242 if (!torture_smb2_tree_connect(tctx
, tree
->session
, mem_ctx
, &tree2
)) {
1243 torture_warning(tctx
, "couldn't reconnect to share, bailing\n");
1250 io2
.in
.fname
= fname
;
1251 io2
.in
.durable_handle
= h
;
1253 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1254 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1259 smb2_util_close(tree
, *h
);
1262 smb2_util_unlink(tree2
, fname
);
1267 talloc_free(mem_ctx
);
1273 * basic test for doing a durable open:
1274 * logoff, create a new session, do a durable reopen (succeeds)
1276 static bool test_durable_open_reopen4(struct torture_context
*tctx
,
1277 struct smb2_tree
*tree
)
1280 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1282 struct smb2_handle _h
;
1283 struct smb2_handle
*h
= NULL
;
1284 struct smb2_create io1
, io2
;
1286 struct smb2_transport
*transport
;
1287 struct smb2_session
*session2
;
1288 struct smb2_tree
*tree2
;
1290 /* Choose a random name in case the state is left a little funky. */
1291 snprintf(fname
, 256, "durable_open_reopen4_%s.dat",
1292 generate_random_str(tctx
, 8));
1294 smb2_util_unlink(tree
, fname
);
1296 smb2_oplock_create_share(&io1
, fname
,
1297 smb2_util_share_access(""),
1298 smb2_util_oplock_level("b"));
1299 io1
.in
.durable_open
= true;
1301 status
= smb2_create(tree
, mem_ctx
, &io1
);
1302 CHECK_STATUS(status
, NT_STATUS_OK
);
1303 _h
= io1
.out
.file
.handle
;
1305 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1306 CHECK_VAL(io1
.out
.durable_open
, true);
1307 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1310 * do a session logoff, establish a new session and tree
1311 * connect on the same transport, and try a durable reopen
1313 transport
= tree
->session
->transport
;
1314 status
= smb2_logoff(tree
->session
);
1315 CHECK_STATUS(status
, NT_STATUS_OK
);
1317 if (!torture_smb2_session_setup(tctx
, transport
,
1318 0, /* previous_session_id */
1319 mem_ctx
, &session2
))
1321 torture_warning(tctx
, "session setup failed.\n");
1327 * the session setup has talloc-stolen the transport,
1328 * so we can safely free the old tree+session for clarity
1332 if (!torture_smb2_tree_connect(tctx
, session2
, mem_ctx
, &tree2
)) {
1333 torture_warning(tctx
, "tree connect failed.\n");
1339 io2
.in
.fname
= fname
;
1340 io2
.in
.durable_handle
= h
;
1343 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1344 CHECK_STATUS(status
, NT_STATUS_OK
);
1346 _h
= io2
.out
.file
.handle
;
1348 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1349 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1354 smb2_util_close(tree2
, *h
);
1357 smb2_util_unlink(tree2
, fname
);
1362 talloc_free(mem_ctx
);
1367 static bool test_durable_open_delete_on_close1(struct torture_context
*tctx
,
1368 struct smb2_tree
*tree
)
1371 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1373 struct smb2_handle _h
;
1374 struct smb2_handle
*h
= NULL
;
1375 struct smb2_create io1
, io2
;
1379 /* Choose a random name in case the state is left a little funky. */
1380 snprintf(fname
, 256, "durable_open_delete_on_close1_%s.dat",
1381 generate_random_str(tctx
, 8));
1383 smb2_util_unlink(tree
, fname
);
1385 smb2_oplock_create_share(&io1
, fname
,
1386 smb2_util_share_access(""),
1387 smb2_util_oplock_level("b"));
1388 io1
.in
.durable_open
= true;
1389 io1
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1391 status
= smb2_create(tree
, mem_ctx
, &io1
);
1392 CHECK_STATUS(status
, NT_STATUS_OK
);
1393 _h
= io1
.out
.file
.handle
;
1395 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1396 CHECK_VAL(io1
.out
.durable_open
, true);
1397 CHECK_VAL(io1
.out
.oplock_level
, smb2_util_oplock_level("b"));
1399 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1400 CHECK_STATUS(status
, NT_STATUS_OK
);
1402 /* disconnect, leaving the durable handle in place */
1405 if (!torture_smb2_connection(tctx
, &tree
)) {
1406 torture_warning(tctx
, "could not reconnect, bailing\n");
1412 * Open the file on the new connection again
1413 * and check that it has been newly created,
1414 * i.e. delete on close was effective on the disconnected handle.
1415 * Also check that the file is really empty,
1416 * the previously written byte gone.
1418 smb2_oplock_create_share(&io2
, fname
,
1419 smb2_util_share_access(""),
1420 smb2_util_oplock_level("b"));
1421 io2
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1423 status
= smb2_create(tree
, mem_ctx
, &io2
);
1424 CHECK_STATUS(status
, NT_STATUS_OK
);
1425 _h
= io2
.out
.file
.handle
;
1427 CHECK_CREATED_SIZE(&io2
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1428 CHECK_VAL(io2
.out
.durable_open
, false);
1429 CHECK_VAL(io2
.out
.oplock_level
, smb2_util_oplock_level("b"));
1434 smb2_util_close(tree
, *h
);
1437 smb2_util_unlink(tree
, fname
);
1442 talloc_free(mem_ctx
);
1448 static bool test_durable_open_delete_on_close2(struct torture_context
*tctx
,
1449 struct smb2_tree
*tree
)
1452 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1454 struct smb2_handle _h
;
1455 struct smb2_handle
*h
= NULL
;
1456 struct smb2_create io
;
1459 uint64_t previous_session_id
;
1460 uint64_t alloc_size_step
;
1461 struct smbcli_options options
;
1463 options
= tree
->session
->transport
->options
;
1465 /* Choose a random name in case the state is left a little funky. */
1466 snprintf(fname
, 256, "durable_open_delete_on_close2_%s.dat",
1467 generate_random_str(tctx
, 8));
1469 smb2_util_unlink(tree
, fname
);
1471 smb2_oplock_create_share(&io
, fname
,
1472 smb2_util_share_access(""),
1473 smb2_util_oplock_level("b"));
1474 io
.in
.durable_open
= true;
1475 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1477 status
= smb2_create(tree
, mem_ctx
, &io
);
1478 CHECK_STATUS(status
, NT_STATUS_OK
);
1479 _h
= io
.out
.file
.handle
;
1481 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1482 CHECK_VAL(io
.out
.durable_open
, true);
1483 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1485 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
1486 CHECK_STATUS(status
, NT_STATUS_OK
);
1488 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1490 /* disconnect, leaving the durable handle in place */
1493 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1496 torture_warning(tctx
, "could not reconnect, bailing\n");
1502 io
.in
.fname
= fname
;
1503 io
.in
.durable_handle
= h
;
1505 status
= smb2_create(tree
, mem_ctx
, &io
);
1506 CHECK_STATUS(status
, NT_STATUS_OK
);
1507 _h
= io
.out
.file
.handle
;
1509 alloc_size_step
= io
.out
.alloc_size
;
1510 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
, alloc_size_step
, 1);
1511 CHECK_VAL(io
.out
.durable_open
, false);
1512 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1514 /* close the file, thereby deleting it */
1515 smb2_util_close(tree
, *h
);
1516 status
= smb2_logoff(tree
->session
);
1519 if (!torture_smb2_connection(tctx
, &tree
)) {
1520 torture_warning(tctx
, "could not reconnect, bailing\n");
1526 * Open the file on the new connection again
1527 * and check that it has been newly created,
1528 * i.e. delete on close was effective on the reconnected handle.
1529 * Also check that the file is really empty,
1530 * the previously written byte gone.
1532 smb2_oplock_create_share(&io
, fname
,
1533 smb2_util_share_access(""),
1534 smb2_util_oplock_level("b"));
1535 io
.in
.durable_open
= true;
1536 io
.in
.create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
1538 status
= smb2_create(tree
, mem_ctx
, &io
);
1539 CHECK_STATUS(status
, NT_STATUS_OK
);
1540 _h
= io
.out
.file
.handle
;
1542 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
, 0, 0);
1543 CHECK_VAL(io
.out
.durable_open
, true);
1544 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1549 smb2_util_close(tree
, *h
);
1552 smb2_util_unlink(tree
, fname
);
1557 talloc_free(mem_ctx
);
1563 basic testing of SMB2 durable opens
1564 regarding the position information on the handle
1566 static bool test_durable_open_file_position(struct torture_context
*tctx
,
1567 struct smb2_tree
*tree
)
1569 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1570 struct smb2_handle h
;
1571 struct smb2_create io
;
1573 const char *fname
= "durable_open_position.dat";
1574 union smb_fileinfo qfinfo
;
1575 union smb_setfileinfo sfinfo
;
1578 uint64_t previous_session_id
;
1579 struct smbcli_options options
;
1581 options
= tree
->session
->transport
->options
;
1583 smb2_util_unlink(tree
, fname
);
1585 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1586 io
.in
.durable_open
= true;
1588 status
= smb2_create(tree
, mem_ctx
, &io
);
1589 CHECK_STATUS(status
, NT_STATUS_OK
);
1590 h
= io
.out
.file
.handle
;
1591 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1592 CHECK_VAL(io
.out
.durable_open
, true);
1593 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1595 /* TODO: check extra blob content */
1597 ZERO_STRUCT(qfinfo
);
1598 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1599 qfinfo
.generic
.in
.file
.handle
= h
;
1600 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1601 CHECK_STATUS(status
, NT_STATUS_OK
);
1602 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0);
1603 pos
= qfinfo
.position_information
.out
.position
;
1604 torture_comment(tctx
, "position: %llu\n",
1605 (unsigned long long)pos
);
1607 ZERO_STRUCT(sfinfo
);
1608 sfinfo
.generic
.level
= RAW_SFILEINFO_POSITION_INFORMATION
;
1609 sfinfo
.generic
.in
.file
.handle
= h
;
1610 sfinfo
.position_information
.in
.position
= 0x1000;
1611 status
= smb2_setinfo_file(tree
, &sfinfo
);
1612 CHECK_STATUS(status
, NT_STATUS_OK
);
1614 ZERO_STRUCT(qfinfo
);
1615 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1616 qfinfo
.generic
.in
.file
.handle
= h
;
1617 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1618 CHECK_STATUS(status
, NT_STATUS_OK
);
1619 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1620 pos
= qfinfo
.position_information
.out
.position
;
1621 torture_comment(tctx
, "position: %llu\n",
1622 (unsigned long long)pos
);
1624 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
1626 /* tcp disconnect */
1630 /* do a session reconnect */
1631 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
1634 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1639 ZERO_STRUCT(qfinfo
);
1640 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1641 qfinfo
.generic
.in
.file
.handle
= h
;
1642 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1643 CHECK_STATUS(status
, NT_STATUS_FILE_CLOSED
);
1646 io
.in
.fname
= fname
;
1647 io
.in
.durable_handle
= &h
;
1649 status
= smb2_create(tree
, mem_ctx
, &io
);
1650 CHECK_STATUS(status
, NT_STATUS_OK
);
1651 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1652 CHECK_VAL(io
.out
.reserved
, 0x00);
1653 CHECK_VAL(io
.out
.create_action
, NTCREATEX_ACTION_EXISTED
);
1654 CHECK_VAL(io
.out
.alloc_size
, 0);
1655 CHECK_VAL(io
.out
.size
, 0);
1656 CHECK_VAL(io
.out
.file_attr
, FILE_ATTRIBUTE_ARCHIVE
);
1657 CHECK_VAL(io
.out
.reserved2
, 0);
1659 h
= io
.out
.file
.handle
;
1661 ZERO_STRUCT(qfinfo
);
1662 qfinfo
.generic
.level
= RAW_FILEINFO_POSITION_INFORMATION
;
1663 qfinfo
.generic
.in
.file
.handle
= h
;
1664 status
= smb2_getinfo_file(tree
, mem_ctx
, &qfinfo
);
1665 CHECK_STATUS(status
, NT_STATUS_OK
);
1666 CHECK_VAL(qfinfo
.position_information
.out
.position
, 0x1000);
1667 pos
= qfinfo
.position_information
.out
.position
;
1668 torture_comment(tctx
, "position: %llu\n",
1669 (unsigned long long)pos
);
1671 smb2_util_close(tree
, h
);
1673 talloc_free(mem_ctx
);
1675 smb2_util_unlink(tree
, fname
);
1684 Open, disconnect, oplock break, reconnect.
1686 static bool test_durable_open_oplock(struct torture_context
*tctx
,
1687 struct smb2_tree
*tree1
,
1688 struct smb2_tree
*tree2
)
1690 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1691 struct smb2_create io1
, io2
;
1692 struct smb2_handle h1
, h2
;
1697 /* Choose a random name in case the state is left a little funky. */
1698 snprintf(fname
, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx
, 8));
1701 smb2_util_unlink(tree1
, fname
);
1703 /* Create with batch oplock */
1704 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
1705 io1
.in
.durable_open
= true;
1708 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
1710 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1711 CHECK_STATUS(status
, NT_STATUS_OK
);
1712 h1
= io1
.out
.file
.handle
;
1713 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1714 CHECK_VAL(io1
.out
.durable_open
, true);
1715 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1717 /* Disconnect after getting the batch */
1722 * Windows7 (build 7000) will break a batch oplock immediately if the
1723 * original client is gone. (ZML: This seems like a bug. It should give
1724 * some time for the client to reconnect!)
1726 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1727 CHECK_STATUS(status
, NT_STATUS_OK
);
1728 h2
= io2
.out
.file
.handle
;
1729 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1730 CHECK_VAL(io2
.out
.durable_open
, true);
1731 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
1733 /* What if tree1 tries to come back and reclaim? */
1734 if (!torture_smb2_connection(tctx
, &tree1
)) {
1735 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1741 io1
.in
.fname
= fname
;
1742 io1
.in
.durable_handle
= &h1
;
1744 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1745 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1748 smb2_util_close(tree2
, h2
);
1749 smb2_util_unlink(tree2
, fname
);
1758 Open, disconnect, lease break, reconnect.
1760 static bool test_durable_open_lease(struct torture_context
*tctx
,
1761 struct smb2_tree
*tree1
,
1762 struct smb2_tree
*tree2
)
1764 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1765 struct smb2_create io1
, io2
;
1766 struct smb2_lease ls1
, ls2
;
1767 struct smb2_handle h1
, h2
;
1771 uint64_t lease1
, lease2
;
1774 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
1775 if (!(caps
& SMB2_CAP_LEASING
)) {
1776 torture_skip(tctx
, "leases are not supported");
1780 * Choose a random name and random lease in case the state is left a
1785 snprintf(fname
, 256, "durable_open_lease_%s.dat", generate_random_str(tctx
, 8));
1788 smb2_util_unlink(tree1
, fname
);
1790 /* Create with lease */
1791 smb2_lease_create(&io1
, &ls1
, false /* dir */, fname
,
1792 lease1
, smb2_util_lease_state("RHW"));
1793 io1
.in
.durable_open
= true;
1795 smb2_lease_create(&io2
, &ls2
, false /* dir */, fname
,
1796 lease2
, smb2_util_lease_state("RHW"));
1797 io2
.in
.durable_open
= true;
1798 io2
.in
.create_disposition
= NTCREATEX_DISP_OPEN
;
1800 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1801 CHECK_STATUS(status
, NT_STATUS_OK
);
1802 h1
= io1
.out
.file
.handle
;
1803 CHECK_VAL(io1
.out
.durable_open
, true);
1804 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1806 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1807 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease1
);
1808 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease1
);
1809 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
1810 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1812 /* Disconnect after getting the lease */
1817 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1818 * even if the original client is gone. (ZML: This seems like a bug. It
1819 * should give some time for the client to reconnect! And why RH?)
1821 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1822 * Test is adapted accordingly.
1824 status
= smb2_create(tree2
, mem_ctx
, &io2
);
1825 CHECK_STATUS(status
, NT_STATUS_OK
);
1826 h2
= io2
.out
.file
.handle
;
1827 CHECK_VAL(io2
.out
.durable_open
, true);
1828 CHECK_CREATED(&io2
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
);
1830 CHECK_VAL(io2
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1831 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[0], lease2
);
1832 CHECK_VAL(io2
.out
.lease_response
.lease_key
.data
[1], ~lease2
);
1833 CHECK_VAL(io2
.out
.lease_response
.lease_state
,
1834 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1836 /* What if tree1 tries to come back and reclaim? */
1837 if (!torture_smb2_connection(tctx
, &tree1
)) {
1838 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1844 io1
.in
.fname
= fname
;
1845 io1
.in
.durable_handle
= &h1
;
1846 io1
.in
.lease_request
= &ls1
;
1848 status
= smb2_create(tree1
, mem_ctx
, &io1
);
1849 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
1852 smb2_util_close(tree2
, h2
);
1853 smb2_util_unlink(tree2
, fname
);
1861 static bool test_durable_open_lock_oplock(struct torture_context
*tctx
,
1862 struct smb2_tree
*tree
)
1864 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1865 struct smb2_create io
;
1866 struct smb2_handle h
;
1867 struct smb2_lock lck
;
1868 struct smb2_lock_element el
[2];
1875 snprintf(fname
, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx
, 8));
1878 smb2_util_unlink(tree
, fname
);
1880 /* Create with oplock */
1882 smb2_oplock_create_share(&io
, fname
,
1883 smb2_util_share_access(""),
1884 smb2_util_oplock_level("b"));
1885 io
.in
.durable_open
= true;
1887 status
= smb2_create(tree
, mem_ctx
, &io
);
1888 CHECK_STATUS(status
, NT_STATUS_OK
);
1889 h
= io
.out
.file
.handle
;
1890 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1892 CHECK_VAL(io
.out
.durable_open
, true);
1893 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
1898 lck
.in
.lock_count
= 0x0001;
1899 lck
.in
.lock_sequence
= 0x00000000;
1900 lck
.in
.file
.handle
= h
;
1903 el
[0].reserved
= 0x00000000;
1904 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
1905 status
= smb2_lock(tree
, &lck
);
1906 CHECK_STATUS(status
, NT_STATUS_OK
);
1908 /* Disconnect/Reconnect. */
1912 if (!torture_smb2_connection(tctx
, &tree
)) {
1913 torture_warning(tctx
, "couldn't reconnect, bailing\n");
1919 io
.in
.fname
= fname
;
1920 io
.in
.durable_handle
= &h
;
1922 status
= smb2_create(tree
, mem_ctx
, &io
);
1923 CHECK_STATUS(status
, NT_STATUS_OK
);
1924 h
= io
.out
.file
.handle
;
1926 lck
.in
.file
.handle
= h
;
1927 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
1928 status
= smb2_lock(tree
, &lck
);
1929 CHECK_STATUS(status
, NT_STATUS_OK
);
1932 smb2_util_close(tree
, h
);
1933 smb2_util_unlink(tree
, fname
);
1940 Open, take BRL, disconnect, reconnect.
1942 static bool test_durable_open_lock_lease(struct torture_context
*tctx
,
1943 struct smb2_tree
*tree
)
1945 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
1946 struct smb2_create io
;
1947 struct smb2_lease ls
;
1948 struct smb2_handle h
;
1949 struct smb2_lock lck
;
1950 struct smb2_lock_element el
[2];
1956 struct smbcli_options options
;
1958 options
= tree
->session
->transport
->options
;
1960 caps
= smb2cli_conn_server_capabilities(tree
->session
->transport
->conn
);
1961 if (!(caps
& SMB2_CAP_LEASING
)) {
1962 torture_skip(tctx
, "leases are not supported");
1966 * Choose a random name and random lease in case the state is left a
1970 snprintf(fname
, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx
, 8));
1973 smb2_util_unlink(tree
, fname
);
1975 /* Create with lease */
1977 smb2_lease_create(&io
, &ls
, false /* dir */, fname
, lease
,
1978 smb2_util_lease_state("RWH"));
1979 io
.in
.durable_open
= true;
1981 status
= smb2_create(tree
, mem_ctx
, &io
);
1982 CHECK_STATUS(status
, NT_STATUS_OK
);
1983 h
= io
.out
.file
.handle
;
1984 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
1986 CHECK_VAL(io
.out
.durable_open
, true);
1987 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
1988 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[0], lease
);
1989 CHECK_VAL(io
.out
.lease_response
.lease_key
.data
[1], ~lease
);
1990 CHECK_VAL(io
.out
.lease_response
.lease_state
,
1991 SMB2_LEASE_READ
|SMB2_LEASE_HANDLE
|SMB2_LEASE_WRITE
);
1996 lck
.in
.lock_count
= 0x0001;
1997 lck
.in
.lock_sequence
= 0x00000000;
1998 lck
.in
.file
.handle
= h
;
2001 el
[0].reserved
= 0x00000000;
2002 el
[0].flags
= SMB2_LOCK_FLAG_EXCLUSIVE
;
2003 status
= smb2_lock(tree
, &lck
);
2004 CHECK_STATUS(status
, NT_STATUS_OK
);
2006 /* Disconnect/Reconnect. */
2010 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree
)) {
2011 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2017 io
.in
.fname
= fname
;
2018 io
.in
.durable_handle
= &h
;
2019 io
.in
.lease_request
= &ls
;
2021 status
= smb2_create(tree
, mem_ctx
, &io
);
2022 CHECK_STATUS(status
, NT_STATUS_OK
);
2023 h
= io
.out
.file
.handle
;
2025 lck
.in
.file
.handle
= h
;
2026 el
[0].flags
= SMB2_LOCK_FLAG_UNLOCK
;
2027 status
= smb2_lock(tree
, &lck
);
2028 CHECK_STATUS(status
, NT_STATUS_OK
);
2031 smb2_util_close(tree
, h
);
2032 smb2_util_unlink(tree
, fname
);
2039 * Open with a RH lease, disconnect, open in another tree, reconnect.
2041 * This test actually demonstrates a minimum level of respect for the durable
2042 * open in the face of another open. As long as this test shows an inability to
2043 * reconnect after an open, the oplock/lease tests above will certainly
2044 * demonstrate an error on reconnect.
2046 static bool test_durable_open_open2_lease(struct torture_context
*tctx
,
2047 struct smb2_tree
*tree1
,
2048 struct smb2_tree
*tree2
)
2050 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2051 struct smb2_create io1
, io2
;
2052 struct smb2_lease ls
;
2053 struct smb2_handle h1
, h2
;
2059 struct smbcli_options options
;
2061 options
= tree1
->session
->transport
->options
;
2063 caps
= smb2cli_conn_server_capabilities(tree1
->session
->transport
->conn
);
2064 if (!(caps
& SMB2_CAP_LEASING
)) {
2065 torture_skip(tctx
, "leases are not supported");
2069 * Choose a random name and random lease in case the state is left a
2073 snprintf(fname
, 256, "durable_open_open2_lease_%s.dat",
2074 generate_random_str(tctx
, 8));
2077 smb2_util_unlink(tree1
, fname
);
2079 /* Create with lease */
2080 smb2_lease_create_share(&io1
, &ls
, false /* dir */, fname
,
2081 smb2_util_share_access(""),
2083 smb2_util_lease_state("RH"));
2084 io1
.in
.durable_open
= true;
2086 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2087 CHECK_STATUS(status
, NT_STATUS_OK
);
2088 h1
= io1
.out
.file
.handle
;
2089 CHECK_VAL(io1
.out
.durable_open
, true);
2090 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2092 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_LEASE
);
2093 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[0], lease
);
2094 CHECK_VAL(io1
.out
.lease_response
.lease_key
.data
[1], ~lease
);
2095 CHECK_VAL(io1
.out
.lease_response
.lease_state
,
2096 smb2_util_lease_state("RH"));
2102 /* Open the file in tree2 */
2103 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2105 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2106 CHECK_STATUS(status
, NT_STATUS_OK
);
2107 h2
= io2
.out
.file
.handle
;
2108 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2111 if (!torture_smb2_connection_ext(tctx
, 0, &options
, &tree1
)) {
2112 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2118 io1
.in
.fname
= fname
;
2119 io1
.in
.durable_handle
= &h1
;
2120 io1
.in
.lease_request
= &ls
;
2123 * Windows7 (build 7000) will give away an open immediately if the
2124 * original client is gone. (ZML: This seems like a bug. It should give
2125 * some time for the client to reconnect!)
2127 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2128 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2129 h1
= io1
.out
.file
.handle
;
2132 smb2_util_close(tree2
, h2
);
2133 smb2_util_unlink(tree2
, fname
);
2134 smb2_util_close(tree1
, h1
);
2135 smb2_util_unlink(tree1
, fname
);
2144 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2146 * This test actually demonstrates a minimum level of respect for the durable
2147 * open in the face of another open. As long as this test shows an inability to
2148 * reconnect after an open, the oplock/lease tests above will certainly
2149 * demonstrate an error on reconnect.
2151 static bool test_durable_open_open2_oplock(struct torture_context
*tctx
,
2152 struct smb2_tree
*tree1
,
2153 struct smb2_tree
*tree2
)
2155 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2156 struct smb2_create io1
, io2
;
2157 struct smb2_handle h1
, h2
;
2163 * Choose a random name and random lease in case the state is left a
2166 snprintf(fname
, 256, "durable_open_open2_oplock_%s.dat",
2167 generate_random_str(tctx
, 8));
2170 smb2_util_unlink(tree1
, fname
);
2172 /* Create with batch oplock */
2173 smb2_oplock_create(&io1
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2174 io1
.in
.durable_open
= true;
2176 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2177 CHECK_STATUS(status
, NT_STATUS_OK
);
2178 h1
= io1
.out
.file
.handle
;
2179 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2180 CHECK_VAL(io1
.out
.durable_open
, true);
2181 CHECK_VAL(io1
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2187 /* Open the file in tree2 */
2188 smb2_oplock_create(&io2
, fname
, SMB2_OPLOCK_LEVEL_NONE
);
2190 status
= smb2_create(tree2
, mem_ctx
, &io2
);
2191 CHECK_STATUS(status
, NT_STATUS_OK
);
2192 h2
= io2
.out
.file
.handle
;
2193 CHECK_CREATED(&io1
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2196 if (!torture_smb2_connection(tctx
, &tree1
)) {
2197 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2203 io1
.in
.fname
= fname
;
2204 io1
.in
.durable_handle
= &h1
;
2206 status
= smb2_create(tree1
, mem_ctx
, &io1
);
2207 CHECK_STATUS(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
);
2208 h1
= io1
.out
.file
.handle
;
2211 smb2_util_close(tree2
, h2
);
2212 smb2_util_unlink(tree2
, fname
);
2213 if (tree1
!= NULL
) {
2214 smb2_util_close(tree1
, h1
);
2215 smb2_util_unlink(tree1
, fname
);
2225 * test behaviour with initial allocation size
2227 static bool test_durable_open_alloc_size(struct torture_context
*tctx
,
2228 struct smb2_tree
*tree
)
2231 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2233 struct smb2_handle _h
;
2234 struct smb2_handle
*h
= NULL
;
2235 struct smb2_create io
;
2237 uint64_t previous_session_id
;
2238 uint64_t alloc_size_step
;
2239 uint64_t initial_alloc_size
= 0x100;
2240 const uint8_t *b
= NULL
;
2241 struct smbcli_options options
;
2243 options
= tree
->session
->transport
->options
;
2245 /* Choose a random name in case the state is left a little funky. */
2246 snprintf(fname
, 256, "durable_open_alloc_size_%s.dat",
2247 generate_random_str(tctx
, 8));
2249 smb2_util_unlink(tree
, fname
);
2251 smb2_oplock_create_share(&io
, fname
,
2252 smb2_util_share_access(""),
2253 smb2_util_oplock_level("b"));
2254 io
.in
.durable_open
= true;
2255 io
.in
.alloc_size
= initial_alloc_size
;
2257 status
= smb2_create(tree
, mem_ctx
, &io
);
2258 CHECK_STATUS(status
, NT_STATUS_OK
);
2259 _h
= io
.out
.file
.handle
;
2261 CHECK_NOT_VAL(io
.out
.alloc_size
, 0);
2262 alloc_size_step
= io
.out
.alloc_size
;
2263 CHECK_CREATED_SIZE(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
,
2264 alloc_size_step
, 0);
2265 CHECK_VAL(io
.out
.durable_open
, true);
2266 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2268 /* prepare buffer */
2269 b
= talloc_zero_size(mem_ctx
, alloc_size_step
);
2270 CHECK_NOT_VAL(b
, NULL
);
2272 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2274 /* disconnect, reconnect and then do durable reopen */
2278 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2281 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2287 io
.in
.fname
= fname
;
2288 io
.in
.durable_handle
= h
;
2291 status
= smb2_create(tree
, mem_ctx
, &io
);
2292 CHECK_STATUS(status
, NT_STATUS_OK
);
2293 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2294 alloc_size_step
, 0);
2295 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2296 _h
= io
.out
.file
.handle
;
2299 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2301 /* write one byte */
2302 status
= smb2_util_write(tree
, *h
, b
, 0, 1);
2303 CHECK_STATUS(status
, NT_STATUS_OK
);
2305 /* disconnect, reconnect and then do durable reopen */
2309 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2312 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2318 io
.in
.fname
= fname
;
2319 io
.in
.durable_handle
= h
;
2322 status
= smb2_create(tree
, mem_ctx
, &io
);
2323 CHECK_STATUS(status
, NT_STATUS_OK
);
2324 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2325 alloc_size_step
, 1);
2326 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2327 _h
= io
.out
.file
.handle
;
2330 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2332 /* write more byte than initial allocation size */
2333 status
= smb2_util_write(tree
, *h
, b
, 1, alloc_size_step
);
2335 /* disconnect, reconnect and then do durable reopen */
2339 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2342 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2348 io
.in
.fname
= fname
;
2349 io
.in
.durable_handle
= h
;
2352 status
= smb2_create(tree
, mem_ctx
, &io
);
2353 CHECK_STATUS(status
, NT_STATUS_OK
);
2354 CHECK_CREATED_SIZE(&io
, EXISTED
, FILE_ATTRIBUTE_ARCHIVE
,
2355 alloc_size_step
* 2, alloc_size_step
+ 1);
2356 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2357 _h
= io
.out
.file
.handle
;
2362 smb2_util_close(tree
, *h
);
2365 smb2_util_unlink(tree
, fname
);
2369 talloc_free(mem_ctx
);
2375 * test behaviour when a disconnect happens while creating a read-only file
2377 static bool test_durable_open_read_only(struct torture_context
*tctx
,
2378 struct smb2_tree
*tree
)
2381 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2383 struct smb2_handle _h
;
2384 struct smb2_handle
*h
= NULL
;
2385 struct smb2_create io
;
2387 uint64_t previous_session_id
;
2388 const uint8_t b
= 0;
2389 uint64_t alloc_size
= 0;
2390 struct smbcli_options options
;
2392 options
= tree
->session
->transport
->options
;
2394 /* Choose a random name in case the state is left a little funky. */
2395 snprintf(fname
, 256, "durable_open_initial_alloc_%s.dat",
2396 generate_random_str(tctx
, 8));
2398 smb2_util_unlink(tree
, fname
);
2400 smb2_oplock_create_share(&io
, fname
,
2401 smb2_util_share_access(""),
2402 smb2_util_oplock_level("b"));
2403 io
.in
.durable_open
= true;
2404 io
.in
.file_attributes
= FILE_ATTRIBUTE_READONLY
;
2406 status
= smb2_create(tree
, mem_ctx
, &io
);
2407 CHECK_STATUS(status
, NT_STATUS_OK
);
2408 _h
= io
.out
.file
.handle
;
2410 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
);
2411 CHECK_VAL(io
.out
.durable_open
, true);
2412 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2414 previous_session_id
= smb2cli_session_current_id(tree
->session
->smbXcli
);
2416 /* write one byte */
2417 status
= smb2_util_write(tree
, *h
, &b
, 0, 1);
2418 CHECK_STATUS(status
, NT_STATUS_OK
);
2420 /* disconnect, reconnect and then do durable reopen */
2424 if (!torture_smb2_connection_ext(tctx
, previous_session_id
,
2427 torture_warning(tctx
, "couldn't reconnect, bailing\n");
2433 io
.in
.fname
= fname
;
2434 io
.in
.durable_handle
= h
;
2437 status
= smb2_create(tree
, mem_ctx
, &io
);
2438 CHECK_STATUS(status
, NT_STATUS_OK
);
2439 alloc_size
= io
.out
.alloc_size
;
2440 CHECK_CREATED_SIZE(&io
, EXISTED
,
2441 FILE_ATTRIBUTE_READONLY
|FILE_ATTRIBUTE_ARCHIVE
,
2443 CHECK_VAL(io
.out
.oplock_level
, smb2_util_oplock_level("b"));
2444 _h
= io
.out
.file
.handle
;
2447 /* write one byte */
2448 status
= smb2_util_write(tree
, *h
, &b
, 1, 1);
2449 CHECK_STATUS(status
, NT_STATUS_OK
);
2453 union smb_setfileinfo sfinfo
;
2455 ZERO_STRUCT(sfinfo
);
2456 sfinfo
.basic_info
.level
= RAW_SFILEINFO_BASIC_INFORMATION
;
2457 sfinfo
.basic_info
.in
.file
.handle
= *h
;
2458 sfinfo
.basic_info
.in
.attrib
= FILE_ATTRIBUTE_NORMAL
;
2459 smb2_setinfo_file(tree
, &sfinfo
);
2461 smb2_util_close(tree
, *h
);
2464 smb2_util_unlink(tree
, fname
);
2468 talloc_free(mem_ctx
);
2474 * durable open with oplock, disconnect, exit
2476 static bool test_durable_open_oplock_disconnect(struct torture_context
*tctx
,
2477 struct smb2_tree
*tree
)
2479 TALLOC_CTX
*mem_ctx
= talloc_new(tctx
);
2480 struct smb2_create io
;
2481 struct smb2_handle _h
;
2482 struct smb2_handle
*h
= NULL
;
2487 snprintf(fname
, 256, "durable_open_oplock_disconnect_%s.dat",
2488 generate_random_str(mem_ctx
, 8));
2490 smb2_util_unlink(tree
, fname
);
2492 smb2_oplock_create(&io
, fname
, SMB2_OPLOCK_LEVEL_BATCH
);
2493 io
.in
.durable_open
= true;
2495 status
= smb2_create(tree
, mem_ctx
, &io
);
2496 CHECK_STATUS(status
, NT_STATUS_OK
);
2498 _h
= io
.out
.file
.handle
;
2501 CHECK_CREATED(&io
, CREATED
, FILE_ATTRIBUTE_ARCHIVE
);
2502 CHECK_VAL(io
.out
.durable_open
, true);
2503 CHECK_VAL(io
.out
.oplock_level
, SMB2_OPLOCK_LEVEL_BATCH
);
2512 smb2_util_close(tree
, *h
);
2514 smb2_util_unlink(tree
, fname
);
2516 talloc_free(mem_ctx
);
2521 struct torture_suite
*torture_smb2_durable_open_init(void)
2523 struct torture_suite
*suite
=
2524 torture_suite_create(talloc_autofree_context(), "durable-open");
2526 torture_suite_add_1smb2_test(suite
, "open-oplock", test_durable_open_open_oplock
);
2527 torture_suite_add_1smb2_test(suite
, "open-lease", test_durable_open_open_lease
);
2528 torture_suite_add_1smb2_test(suite
, "reopen1", test_durable_open_reopen1
);
2529 torture_suite_add_1smb2_test(suite
, "reopen1a", test_durable_open_reopen1a
);
2530 torture_suite_add_1smb2_test(suite
, "reopen2", test_durable_open_reopen2
);
2531 torture_suite_add_1smb2_test(suite
, "reopen2-lease", test_durable_open_reopen2_lease
);
2532 torture_suite_add_1smb2_test(suite
, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2
);
2533 torture_suite_add_1smb2_test(suite
, "reopen2a", test_durable_open_reopen2a
);
2534 torture_suite_add_1smb2_test(suite
, "reopen3", test_durable_open_reopen3
);
2535 torture_suite_add_1smb2_test(suite
, "reopen4", test_durable_open_reopen4
);
2536 torture_suite_add_1smb2_test(suite
, "delete_on_close1",
2537 test_durable_open_delete_on_close1
);
2538 torture_suite_add_1smb2_test(suite
, "delete_on_close2",
2539 test_durable_open_delete_on_close2
);
2540 torture_suite_add_1smb2_test(suite
, "file-position",
2541 test_durable_open_file_position
);
2542 torture_suite_add_2smb2_test(suite
, "oplock", test_durable_open_oplock
);
2543 torture_suite_add_2smb2_test(suite
, "lease", test_durable_open_lease
);
2544 torture_suite_add_1smb2_test(suite
, "lock-oplock", test_durable_open_lock_oplock
);
2545 torture_suite_add_1smb2_test(suite
, "lock-lease", test_durable_open_lock_lease
);
2546 torture_suite_add_2smb2_test(suite
, "open2-lease",
2547 test_durable_open_open2_lease
);
2548 torture_suite_add_2smb2_test(suite
, "open2-oplock",
2549 test_durable_open_open2_oplock
);
2550 torture_suite_add_1smb2_test(suite
, "alloc-size",
2551 test_durable_open_alloc_size
);
2552 torture_suite_add_1smb2_test(suite
, "read-only",
2553 test_durable_open_read_only
);
2555 suite
->description
= talloc_strdup(suite
, "SMB2-DURABLE-OPEN tests");
2560 struct torture_suite
*torture_smb2_durable_open_disconnect_init(void)
2562 struct torture_suite
*suite
=
2563 torture_suite_create(talloc_autofree_context(),
2564 "durable-open-disconnect");
2566 torture_suite_add_1smb2_test(suite
, "open-oplock-disconnect",
2567 test_durable_open_oplock_disconnect
);
2569 suite
->description
= talloc_strdup(suite
,
2570 "SMB2-DURABLE-OPEN-DISCONNECT tests");