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 io;
436 struct smb2_tree *tree2 = NULL;
437 struct smb2_tree *tree3 = NULL;
438 uint64_t previous_session_id;
439 struct smbcli_options options;
440 struct GUID orig_client_guid;
442 options = tree->session->transport->options;
443 orig_client_guid = options.client_guid;
445 /* Choose a random name in case the state is left a little funky. */
446 snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
447 generate_random_str(tctx, 8));
449 smb2_util_unlink(tree, fname);
451 smb2_oplock_create_share(&io, fname,
452 smb2_util_share_access(""),
453 smb2_util_oplock_level("b"));
454 io.in.durable_open = true;
456 status = smb2_create(tree, mem_ctx, &io);
457 CHECK_STATUS(status, NT_STATUS_OK);
458 _h = io.out.file.handle;
460 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
461 CHECK_VAL(io.out.durable_open, true);
462 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
465 * a session reconnect on a second tcp connection
468 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
470 /* for oplocks, the client guid can be different: */
471 options.client_guid = GUID_random();
473 ret = torture_smb2_connection_ext(tctx, previous_session_id,
475 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
478 * check that this has deleted the old session
483 io.in.durable_handle = h;
485 status = smb2_create(tree, mem_ctx, &io);
486 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
491 * but a durable reconnect on the new session succeeds:
496 io.in.durable_handle = h;
498 status = smb2_create(tree2, mem_ctx, &io);
499 CHECK_STATUS(status, NT_STATUS_OK);
500 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
501 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
502 _h = io.out.file.handle;
506 * a session reconnect on a second tcp connection
509 previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
511 /* the original client_guid works just the same */
512 options.client_guid = orig_client_guid;
514 ret = torture_smb2_connection_ext(tctx, previous_session_id,
516 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
519 * check that this has deleted the old session
524 io.in.durable_handle = h;
526 status = smb2_create(tree2, mem_ctx, &io);
527 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
532 * but a durable reconnect on the new session succeeds:
537 io.in.durable_handle = h;
539 status = smb2_create(tree3, mem_ctx, &io);
540 CHECK_STATUS(status, NT_STATUS_OK);
541 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
542 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
543 _h = io.out.file.handle;
557 smb2_util_close(tree, *h);
560 smb2_util_unlink(tree, fname);
565 talloc_free(mem_ctx);
571 * basic test for doing a durable open
572 * tcp disconnect, reconnect, do a durable reopen (succeeds)
574 static bool test_durable_open_reopen2(struct torture_context *tctx,
575 struct smb2_tree *tree)
578 TALLOC_CTX *mem_ctx = talloc_new(tctx);
580 struct smb2_handle _h;
581 struct smb2_handle *h = NULL;
582 struct smb2_create io;
585 /* Choose a random name in case the state is left a little funky. */
586 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
587 generate_random_str(tctx, 8));
589 smb2_util_unlink(tree, fname);
591 smb2_oplock_create_share(&io, fname,
592 smb2_util_share_access(""),
593 smb2_util_oplock_level("b"));
594 io.in.durable_open = true;
596 status = smb2_create(tree, mem_ctx, &io);
597 CHECK_STATUS(status, NT_STATUS_OK);
598 _h = io.out.file.handle;
600 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
601 CHECK_VAL(io.out.durable_open, true);
602 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
604 /* disconnect, leaving the durable in place */
607 if (!torture_smb2_connection(tctx, &tree)) {
608 torture_warning(tctx, "couldn't reconnect, bailing\n");
614 /* the path name is ignored by the server */
616 io.in.durable_handle = h; /* durable v1 reconnect request */
619 status = smb2_create(tree, mem_ctx, &io);
620 CHECK_STATUS(status, NT_STATUS_OK);
621 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
622 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
623 _h = io.out.file.handle;
626 /* disconnect again, leaving the durable in place */
629 if (!torture_smb2_connection(tctx, &tree)) {
630 torture_warning(tctx, "couldn't reconnect, bailing\n");
636 * show that the filename and many other fields
637 * are ignored. only the reconnect request blob
641 /* the path name is ignored by the server */
642 io.in.security_flags = 0x78;
643 io.in.oplock_level = 0x78;
644 io.in.impersonation_level = 0x12345678;
645 io.in.create_flags = 0x12345678;
646 io.in.reserved = 0x12345678;
647 io.in.desired_access = 0x12345678;
648 io.in.file_attributes = 0x12345678;
649 io.in.share_access = 0x12345678;
650 io.in.create_disposition = 0x12345678;
651 io.in.create_options = 0x12345678;
652 io.in.fname = "__non_existing_fname__";
653 io.in.durable_handle = h; /* durable v1 reconnect request */
656 status = smb2_create(tree, mem_ctx, &io);
657 CHECK_STATUS(status, NT_STATUS_OK);
658 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
659 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
660 _h = io.out.file.handle;
663 /* disconnect, leaving the durable in place */
666 if (!torture_smb2_connection(tctx, &tree)) {
667 torture_warning(tctx, "couldn't reconnect, bailing\n");
673 * show that an additionally specified durable v1 request
674 * is ignored by the server.
675 * See MS-SMB2, 3.3.5.9.7
676 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
679 /* the path name is ignored by the server */
681 io.in.durable_handle = h; /* durable v1 reconnect request */
682 io.in.durable_open = true; /* durable v1 handle request */
685 status = smb2_create(tree, mem_ctx, &io);
686 CHECK_STATUS(status, NT_STATUS_OK);
687 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
688 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
689 _h = io.out.file.handle;
695 smb2_util_close(tree, *h);
698 smb2_util_unlink(tree, fname);
703 talloc_free(mem_ctx);
709 * lease variant of reopen2
710 * basic test for doing a durable open
711 * tcp disconnect, reconnect, do a durable reopen (succeeds)
713 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
714 struct smb2_tree *tree)
717 TALLOC_CTX *mem_ctx = talloc_new(tctx);
719 struct smb2_handle _h;
720 struct smb2_handle *h = NULL;
721 struct smb2_create io;
722 struct smb2_lease ls;
725 struct smbcli_options options;
728 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
729 if (!(caps & SMB2_CAP_LEASING)) {
730 torture_skip(tctx, "leases are not supported");
733 options = tree->session->transport->options;
735 /* Choose a random name in case the state is left a little funky. */
736 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
737 generate_random_str(tctx, 8));
739 smb2_util_unlink(tree, fname);
741 lease_key = random();
742 smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
743 smb2_util_lease_state("RWH"));
744 io.in.durable_open = true;
746 status = smb2_create(tree, mem_ctx, &io);
747 CHECK_STATUS(status, NT_STATUS_OK);
748 _h = io.out.file.handle;
750 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
752 CHECK_VAL(io.out.durable_open, true);
753 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
754 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
755 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
756 CHECK_VAL(io.out.lease_response.lease_state,
757 smb2_util_lease_state("RWH"));
758 CHECK_VAL(io.out.lease_response.lease_flags, 0);
759 CHECK_VAL(io.out.lease_response.lease_duration, 0);
761 /* disconnect, reconnect and then do durable reopen */
764 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
765 torture_warning(tctx, "couldn't reconnect, bailing\n");
771 /* a few failure tests: */
774 * several attempts without lease attached:
775 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
776 * irrespective of file name provided
781 io.in.durable_handle = h;
782 status = smb2_create(tree, mem_ctx, &io);
783 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
786 io.in.fname = "__non_existing_fname__";
787 io.in.durable_handle = h;
788 status = smb2_create(tree, mem_ctx, &io);
789 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
793 io.in.durable_handle = h;
794 status = smb2_create(tree, mem_ctx, &io);
795 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
798 * attempt with lease provided, but
799 * with a changed lease key. => fails
803 io.in.durable_open = false;
804 io.in.durable_handle = h;
805 io.in.lease_request = &ls;
806 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
807 /* a wrong lease key lets the request fail */
808 ls.lease_key.data[0]++;
810 status = smb2_create(tree, mem_ctx, &io);
811 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
813 /* restore the correct lease key */
814 ls.lease_key.data[0]--;
817 * this last failing attempt is almost correct:
818 * only problem is: we use the wrong filename...
819 * Note that this gives INVALID_PARAMETER.
820 * This is different from oplocks!
823 io.in.fname = "__non_existing_fname__";
824 io.in.durable_open = false;
825 io.in.durable_handle = h;
826 io.in.lease_request = &ls;
827 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
829 status = smb2_create(tree, mem_ctx, &io);
830 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
833 * Now for a succeeding reconnect:
838 io.in.durable_open = false;
839 io.in.durable_handle = h;
840 io.in.lease_request = &ls;
841 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
843 /* the requested lease state is irrelevant */
844 ls.lease_state = smb2_util_lease_state("");
848 status = smb2_create(tree, mem_ctx, &io);
849 CHECK_STATUS(status, NT_STATUS_OK);
851 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
852 CHECK_VAL(io.out.durable_open, false);
853 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
854 CHECK_VAL(io.out.persistent_open, false);
855 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
856 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
857 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
858 CHECK_VAL(io.out.lease_response.lease_state,
859 smb2_util_lease_state("RWH"));
860 CHECK_VAL(io.out.lease_response.lease_flags, 0);
861 CHECK_VAL(io.out.lease_response.lease_duration, 0);
862 _h = io.out.file.handle;
865 /* disconnect one more time */
868 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
869 torture_warning(tctx, "couldn't reconnect, bailing\n");
875 * demonstrate that various parameters are ignored
881 * These are completely ignored by the server
883 io.in.security_flags = 0x78;
884 io.in.oplock_level = 0x78;
885 io.in.impersonation_level = 0x12345678;
886 io.in.create_flags = 0x12345678;
887 io.in.reserved = 0x12345678;
888 io.in.desired_access = 0x12345678;
889 io.in.file_attributes = 0x12345678;
890 io.in.share_access = 0x12345678;
891 io.in.create_disposition = 0x12345678;
892 io.in.create_options = 0x12345678;
895 * only these are checked:
897 * - io.in.durable_handle,
898 * - io.in.lease_request->lease_key
902 io.in.durable_open_v2 = false;
903 io.in.durable_handle_v2 = h;
904 io.in.lease_request = &ls;
906 /* the requested lease state is irrelevant */
907 ls.lease_state = smb2_util_lease_state("");
911 status = smb2_create(tree, mem_ctx, &io);
912 CHECK_STATUS(status, NT_STATUS_OK);
914 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
915 CHECK_VAL(io.out.durable_open, false);
916 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
917 CHECK_VAL(io.out.persistent_open, false);
918 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
919 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
920 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
921 CHECK_VAL(io.out.lease_response.lease_state,
922 smb2_util_lease_state("RWH"));
923 CHECK_VAL(io.out.lease_response.lease_flags, 0);
924 CHECK_VAL(io.out.lease_response.lease_duration, 0);
926 _h = io.out.file.handle;
932 smb2_util_close(tree, *h);
935 smb2_util_unlink(tree, fname);
940 talloc_free(mem_ctx);
946 * lease v2 variant of reopen2
947 * basic test for doing a durable open
948 * tcp disconnect, reconnect, do a durable reopen (succeeds)
950 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
951 struct smb2_tree *tree)
954 TALLOC_CTX *mem_ctx = talloc_new(tctx);
956 struct smb2_handle _h;
957 struct smb2_handle *h = NULL;
958 struct smb2_create io;
959 struct smb2_lease ls;
962 struct smbcli_options options;
965 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
966 if (!(caps & SMB2_CAP_LEASING)) {
967 torture_skip(tctx, "leases are not supported");
970 options = tree->session->transport->options;
972 /* Choose a random name in case the state is left a little funky. */
973 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
974 generate_random_str(tctx, 8));
976 smb2_util_unlink(tree, fname);
978 lease_key = random();
979 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
980 lease_key, 0, /* parent lease key */
981 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
982 io.in.durable_open = true;
984 status = smb2_create(tree, mem_ctx, &io);
985 CHECK_STATUS(status, NT_STATUS_OK);
986 _h = io.out.file.handle;
988 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
990 CHECK_VAL(io.out.durable_open, true);
991 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
992 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
993 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
994 CHECK_VAL(io.out.lease_response_v2.lease_state,
995 smb2_util_lease_state("RWH"));
996 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
997 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
999 /* disconnect, reconnect and then do durable reopen */
1002 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1003 torture_warning(tctx, "couldn't reconnect, bailing\n");
1008 /* a few failure tests: */
1011 * several attempts without lease attached:
1012 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1013 * irrespective of file name provided
1018 io.in.durable_handle = h;
1019 status = smb2_create(tree, mem_ctx, &io);
1020 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1023 io.in.fname = "__non_existing_fname__";
1024 io.in.durable_handle = h;
1025 status = smb2_create(tree, mem_ctx, &io);
1026 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1029 io.in.fname = fname;
1030 io.in.durable_handle = h;
1031 status = smb2_create(tree, mem_ctx, &io);
1032 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1035 * attempt with lease provided, but
1036 * with a changed lease key. => fails
1039 io.in.fname = fname;
1040 io.in.durable_open = false;
1041 io.in.durable_handle = h;
1042 io.in.lease_request_v2 = &ls;
1043 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1044 /* a wrong lease key lets the request fail */
1045 ls.lease_key.data[0]++;
1047 status = smb2_create(tree, mem_ctx, &io);
1048 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1050 /* restore the correct lease key */
1051 ls.lease_key.data[0]--;
1054 * this last failing attempt is almost correct:
1055 * only problem is: we use the wrong filename...
1056 * Note that this gives INVALID_PARAMETER.
1057 * This is different from oplocks!
1060 io.in.fname = "__non_existing_fname__";
1061 io.in.durable_open = false;
1062 io.in.durable_handle = h;
1063 io.in.lease_request_v2 = &ls;
1064 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1066 status = smb2_create(tree, mem_ctx, &io);
1067 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1070 * Now for a succeeding reconnect:
1074 io.in.fname = fname;
1075 io.in.durable_open = false;
1076 io.in.durable_handle = h;
1077 io.in.lease_request_v2 = &ls;
1078 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1080 /* the requested lease state is irrelevant */
1081 ls.lease_state = smb2_util_lease_state("");
1085 status = smb2_create(tree, mem_ctx, &io);
1086 CHECK_STATUS(status, NT_STATUS_OK);
1088 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1089 CHECK_VAL(io.out.durable_open, false);
1090 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1091 CHECK_VAL(io.out.persistent_open, false);
1092 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1093 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1094 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1095 CHECK_VAL(io.out.lease_response_v2.lease_state,
1096 smb2_util_lease_state("RWH"));
1097 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1098 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1099 _h = io.out.file.handle;
1102 /* disconnect one more time */
1105 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1106 torture_warning(tctx, "couldn't reconnect, bailing\n");
1112 * demonstrate that various parameters are ignored
1118 * These are completely ignored by the server
1120 io.in.security_flags = 0x78;
1121 io.in.oplock_level = 0x78;
1122 io.in.impersonation_level = 0x12345678;
1123 io.in.create_flags = 0x12345678;
1124 io.in.reserved = 0x12345678;
1125 io.in.desired_access = 0x12345678;
1126 io.in.file_attributes = 0x12345678;
1127 io.in.share_access = 0x12345678;
1128 io.in.create_disposition = 0x12345678;
1129 io.in.create_options = 0x12345678;
1132 * only these are checked:
1134 * - io.in.durable_handle,
1135 * - io.in.lease_request->lease_key
1138 io.in.fname = fname;
1139 io.in.durable_open_v2 = false;
1140 io.in.durable_handle_v2 = h;
1141 io.in.lease_request_v2 = &ls;
1143 /* the requested lease state is irrelevant */
1144 ls.lease_state = smb2_util_lease_state("");
1148 status = smb2_create(tree, mem_ctx, &io);
1149 CHECK_STATUS(status, NT_STATUS_OK);
1151 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1152 CHECK_VAL(io.out.durable_open, false);
1153 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1154 CHECK_VAL(io.out.persistent_open, false);
1155 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1156 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1157 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1158 CHECK_VAL(io.out.lease_response_v2.lease_state,
1159 smb2_util_lease_state("RWH"));
1160 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1161 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1163 _h = io.out.file.handle;
1169 smb2_util_close(tree, *h);
1172 smb2_util_unlink(tree, fname);
1177 talloc_free(mem_ctx);
1183 * basic test for doing a durable open
1184 * tcp disconnect, reconnect with a session reconnect and
1185 * do a durable reopen (succeeds)
1187 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1188 struct smb2_tree *tree)
1191 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1193 struct smb2_handle _h;
1194 struct smb2_handle *h = NULL;
1195 struct smb2_create io1, io2;
1196 uint64_t previous_session_id;
1198 struct smbcli_options options;
1200 options = tree->session->transport->options;
1202 /* Choose a random name in case the state is left a little funky. */
1203 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1204 generate_random_str(tctx, 8));
1206 smb2_util_unlink(tree, fname);
1208 smb2_oplock_create_share(&io1, fname,
1209 smb2_util_share_access(""),
1210 smb2_util_oplock_level("b"));
1211 io1.in.durable_open = true;
1213 status = smb2_create(tree, mem_ctx, &io1);
1214 CHECK_STATUS(status, NT_STATUS_OK);
1215 _h = io1.out.file.handle;
1217 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1218 CHECK_VAL(io1.out.durable_open, true);
1219 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1221 /* disconnect, reconnect and then do durable reopen */
1222 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1226 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1229 torture_warning(tctx, "couldn't reconnect, bailing\n");
1235 io2.in.fname = fname;
1236 io2.in.durable_handle = h;
1239 status = smb2_create(tree, mem_ctx, &io2);
1240 CHECK_STATUS(status, NT_STATUS_OK);
1241 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1242 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1243 _h = io2.out.file.handle;
1249 smb2_util_close(tree, *h);
1252 smb2_util_unlink(tree, fname);
1257 talloc_free(mem_ctx);
1264 * basic test for doing a durable open:
1265 * tdis, new tcon, try durable reopen (fails)
1267 static bool test_durable_open_reopen3(struct torture_context *tctx,
1268 struct smb2_tree *tree)
1271 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1273 struct smb2_handle _h;
1274 struct smb2_handle *h = NULL;
1275 struct smb2_create io1, io2;
1277 struct smb2_tree *tree2;
1279 /* Choose a random name in case the state is left a little funky. */
1280 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1281 generate_random_str(tctx, 8));
1283 smb2_util_unlink(tree, fname);
1285 smb2_oplock_create_share(&io1, fname,
1286 smb2_util_share_access(""),
1287 smb2_util_oplock_level("b"));
1288 io1.in.durable_open = true;
1290 status = smb2_create(tree, mem_ctx, &io1);
1291 CHECK_STATUS(status, NT_STATUS_OK);
1292 _h = io1.out.file.handle;
1294 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1295 CHECK_VAL(io1.out.durable_open, true);
1296 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1298 /* disconnect, reconnect and then do durable reopen */
1299 status = smb2_tdis(tree);
1300 CHECK_STATUS(status, NT_STATUS_OK);
1302 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1303 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1310 io2.in.fname = fname;
1311 io2.in.durable_handle = h;
1313 status = smb2_create(tree2, mem_ctx, &io2);
1314 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1319 smb2_util_close(tree, *h);
1322 smb2_util_unlink(tree2, fname);
1327 talloc_free(mem_ctx);
1333 * basic test for doing a durable open:
1334 * logoff, create a new session, do a durable reopen (succeeds)
1336 static bool test_durable_open_reopen4(struct torture_context *tctx,
1337 struct smb2_tree *tree)
1340 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1342 struct smb2_handle _h;
1343 struct smb2_handle *h = NULL;
1344 struct smb2_create io1, io2;
1346 struct smb2_transport *transport;
1347 struct smb2_session *session2;
1348 struct smb2_tree *tree2;
1350 /* Choose a random name in case the state is left a little funky. */
1351 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1352 generate_random_str(tctx, 8));
1354 smb2_util_unlink(tree, fname);
1356 smb2_oplock_create_share(&io1, fname,
1357 smb2_util_share_access(""),
1358 smb2_util_oplock_level("b"));
1359 io1.in.durable_open = true;
1361 status = smb2_create(tree, mem_ctx, &io1);
1362 CHECK_STATUS(status, NT_STATUS_OK);
1363 _h = io1.out.file.handle;
1365 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1366 CHECK_VAL(io1.out.durable_open, true);
1367 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1370 * do a session logoff, establish a new session and tree
1371 * connect on the same transport, and try a durable reopen
1373 transport = tree->session->transport;
1374 status = smb2_logoff(tree->session);
1375 CHECK_STATUS(status, NT_STATUS_OK);
1377 if (!torture_smb2_session_setup(tctx, transport,
1378 0, /* previous_session_id */
1379 mem_ctx, &session2))
1381 torture_warning(tctx, "session setup failed.\n");
1387 * the session setup has talloc-stolen the transport,
1388 * so we can safely free the old tree+session for clarity
1392 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1393 torture_warning(tctx, "tree connect failed.\n");
1399 io2.in.fname = fname;
1400 io2.in.durable_handle = h;
1403 status = smb2_create(tree2, mem_ctx, &io2);
1404 CHECK_STATUS(status, NT_STATUS_OK);
1406 _h = io2.out.file.handle;
1408 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1409 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1414 smb2_util_close(tree2, *h);
1417 smb2_util_unlink(tree2, fname);
1422 talloc_free(mem_ctx);
1427 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1428 struct smb2_tree *tree)
1431 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1433 struct smb2_handle _h;
1434 struct smb2_handle *h = NULL;
1435 struct smb2_create io1, io2;
1439 /* Choose a random name in case the state is left a little funky. */
1440 snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1441 generate_random_str(tctx, 8));
1443 smb2_util_unlink(tree, fname);
1445 smb2_oplock_create_share(&io1, fname,
1446 smb2_util_share_access(""),
1447 smb2_util_oplock_level("b"));
1448 io1.in.durable_open = true;
1449 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1451 status = smb2_create(tree, mem_ctx, &io1);
1452 CHECK_STATUS(status, NT_STATUS_OK);
1453 _h = io1.out.file.handle;
1455 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1456 CHECK_VAL(io1.out.durable_open, true);
1457 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1459 status = smb2_util_write(tree, *h, &b, 0, 1);
1460 CHECK_STATUS(status, NT_STATUS_OK);
1462 /* disconnect, leaving the durable handle in place */
1465 if (!torture_smb2_connection(tctx, &tree)) {
1466 torture_warning(tctx, "could not reconnect, bailing\n");
1472 * Open the file on the new connection again
1473 * and check that it has been newly created,
1474 * i.e. delete on close was effective on the disconnected handle.
1475 * Also check that the file is really empty,
1476 * the previously written byte gone.
1478 smb2_oplock_create_share(&io2, fname,
1479 smb2_util_share_access(""),
1480 smb2_util_oplock_level("b"));
1481 io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1483 status = smb2_create(tree, mem_ctx, &io2);
1484 CHECK_STATUS(status, NT_STATUS_OK);
1485 _h = io2.out.file.handle;
1487 CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1488 CHECK_VAL(io2.out.durable_open, false);
1489 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1494 smb2_util_close(tree, *h);
1497 smb2_util_unlink(tree, fname);
1502 talloc_free(mem_ctx);
1508 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1509 struct smb2_tree *tree)
1512 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1514 struct smb2_handle _h;
1515 struct smb2_handle *h = NULL;
1516 struct smb2_create io;
1519 uint64_t previous_session_id;
1520 uint64_t alloc_size_step;
1521 struct smbcli_options options;
1523 options = tree->session->transport->options;
1525 /* Choose a random name in case the state is left a little funky. */
1526 snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1527 generate_random_str(tctx, 8));
1529 smb2_util_unlink(tree, fname);
1531 smb2_oplock_create_share(&io, fname,
1532 smb2_util_share_access(""),
1533 smb2_util_oplock_level("b"));
1534 io.in.durable_open = true;
1535 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1537 status = smb2_create(tree, mem_ctx, &io);
1538 CHECK_STATUS(status, NT_STATUS_OK);
1539 _h = io.out.file.handle;
1541 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1542 CHECK_VAL(io.out.durable_open, true);
1543 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1545 status = smb2_util_write(tree, *h, &b, 0, 1);
1546 CHECK_STATUS(status, NT_STATUS_OK);
1548 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1550 /* disconnect, leaving the durable handle in place */
1553 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1556 torture_warning(tctx, "could not reconnect, bailing\n");
1562 io.in.fname = fname;
1563 io.in.durable_handle = h;
1565 status = smb2_create(tree, mem_ctx, &io);
1566 CHECK_STATUS(status, NT_STATUS_OK);
1567 _h = io.out.file.handle;
1569 alloc_size_step = io.out.alloc_size;
1570 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1571 CHECK_VAL(io.out.durable_open, false);
1572 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1574 /* close the file, thereby deleting it */
1575 smb2_util_close(tree, *h);
1576 status = smb2_logoff(tree->session);
1579 if (!torture_smb2_connection(tctx, &tree)) {
1580 torture_warning(tctx, "could not reconnect, bailing\n");
1586 * Open the file on the new connection again
1587 * and check that it has been newly created,
1588 * i.e. delete on close was effective on the reconnected handle.
1589 * Also check that the file is really empty,
1590 * the previously written byte gone.
1592 smb2_oplock_create_share(&io, fname,
1593 smb2_util_share_access(""),
1594 smb2_util_oplock_level("b"));
1595 io.in.durable_open = true;
1596 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1598 status = smb2_create(tree, mem_ctx, &io);
1599 CHECK_STATUS(status, NT_STATUS_OK);
1600 _h = io.out.file.handle;
1602 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1603 CHECK_VAL(io.out.durable_open, true);
1604 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1609 smb2_util_close(tree, *h);
1612 smb2_util_unlink(tree, fname);
1617 talloc_free(mem_ctx);
1623 basic testing of SMB2 durable opens
1624 regarding the position information on the handle
1626 static bool test_durable_open_file_position(struct torture_context *tctx,
1627 struct smb2_tree *tree)
1629 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1630 struct smb2_handle h;
1631 struct smb2_create io;
1633 const char *fname = "durable_open_position.dat";
1634 union smb_fileinfo qfinfo;
1635 union smb_setfileinfo sfinfo;
1638 uint64_t previous_session_id;
1639 struct smbcli_options options;
1641 options = tree->session->transport->options;
1643 smb2_util_unlink(tree, fname);
1645 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1646 io.in.durable_open = true;
1648 status = smb2_create(tree, mem_ctx, &io);
1649 CHECK_STATUS(status, NT_STATUS_OK);
1650 h = io.out.file.handle;
1651 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1652 CHECK_VAL(io.out.durable_open, true);
1653 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1655 /* TODO: check extra blob content */
1657 ZERO_STRUCT(qfinfo);
1658 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1659 qfinfo.generic.in.file.handle = h;
1660 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1661 CHECK_STATUS(status, NT_STATUS_OK);
1662 CHECK_VAL(qfinfo.position_information.out.position, 0);
1663 pos = qfinfo.position_information.out.position;
1664 torture_comment(tctx, "position: %llu\n",
1665 (unsigned long long)pos);
1667 ZERO_STRUCT(sfinfo);
1668 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1669 sfinfo.generic.in.file.handle = h;
1670 sfinfo.position_information.in.position = 0x1000;
1671 status = smb2_setinfo_file(tree, &sfinfo);
1672 CHECK_STATUS(status, NT_STATUS_OK);
1674 ZERO_STRUCT(qfinfo);
1675 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1676 qfinfo.generic.in.file.handle = h;
1677 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1678 CHECK_STATUS(status, NT_STATUS_OK);
1679 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1680 pos = qfinfo.position_information.out.position;
1681 torture_comment(tctx, "position: %llu\n",
1682 (unsigned long long)pos);
1684 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1686 /* tcp disconnect */
1690 /* do a session reconnect */
1691 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1694 torture_warning(tctx, "couldn't reconnect, bailing\n");
1699 ZERO_STRUCT(qfinfo);
1700 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1701 qfinfo.generic.in.file.handle = h;
1702 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1703 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1706 io.in.fname = fname;
1707 io.in.durable_handle = &h;
1709 status = smb2_create(tree, mem_ctx, &io);
1710 CHECK_STATUS(status, NT_STATUS_OK);
1711 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1712 CHECK_VAL(io.out.reserved, 0x00);
1713 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1714 CHECK_VAL(io.out.alloc_size, 0);
1715 CHECK_VAL(io.out.size, 0);
1716 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1717 CHECK_VAL(io.out.reserved2, 0);
1719 h = io.out.file.handle;
1721 ZERO_STRUCT(qfinfo);
1722 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1723 qfinfo.generic.in.file.handle = h;
1724 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1725 CHECK_STATUS(status, NT_STATUS_OK);
1726 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1727 pos = qfinfo.position_information.out.position;
1728 torture_comment(tctx, "position: %llu\n",
1729 (unsigned long long)pos);
1731 smb2_util_close(tree, h);
1733 talloc_free(mem_ctx);
1735 smb2_util_unlink(tree, fname);
1744 Open, disconnect, oplock break, reconnect.
1746 static bool test_durable_open_oplock(struct torture_context *tctx,
1747 struct smb2_tree *tree1,
1748 struct smb2_tree *tree2)
1750 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1751 struct smb2_create io1, io2;
1752 struct smb2_handle h1, h2;
1757 /* Choose a random name in case the state is left a little funky. */
1758 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1761 smb2_util_unlink(tree1, fname);
1763 /* Create with batch oplock */
1764 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1765 io1.in.durable_open = true;
1768 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1770 status = smb2_create(tree1, mem_ctx, &io1);
1771 CHECK_STATUS(status, NT_STATUS_OK);
1772 h1 = io1.out.file.handle;
1773 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1774 CHECK_VAL(io1.out.durable_open, true);
1775 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1777 /* Disconnect after getting the batch */
1782 * Windows7 (build 7000) will break a batch oplock immediately if the
1783 * original client is gone. (ZML: This seems like a bug. It should give
1784 * some time for the client to reconnect!)
1786 status = smb2_create(tree2, mem_ctx, &io2);
1787 CHECK_STATUS(status, NT_STATUS_OK);
1788 h2 = io2.out.file.handle;
1789 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1790 CHECK_VAL(io2.out.durable_open, true);
1791 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1793 /* What if tree1 tries to come back and reclaim? */
1794 if (!torture_smb2_connection(tctx, &tree1)) {
1795 torture_warning(tctx, "couldn't reconnect, bailing\n");
1801 io1.in.fname = fname;
1802 io1.in.durable_handle = &h1;
1804 status = smb2_create(tree1, mem_ctx, &io1);
1805 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1808 smb2_util_close(tree2, h2);
1809 smb2_util_unlink(tree2, fname);
1818 Open, disconnect, lease break, reconnect.
1820 static bool test_durable_open_lease(struct torture_context *tctx,
1821 struct smb2_tree *tree1,
1822 struct smb2_tree *tree2)
1824 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1825 struct smb2_create io1, io2;
1826 struct smb2_lease ls1, ls2;
1827 struct smb2_handle h1, h2;
1831 uint64_t lease1, lease2;
1834 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1835 if (!(caps & SMB2_CAP_LEASING)) {
1836 torture_skip(tctx, "leases are not supported");
1840 * Choose a random name and random lease in case the state is left a
1845 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1848 smb2_util_unlink(tree1, fname);
1850 /* Create with lease */
1851 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1852 lease1, smb2_util_lease_state("RHW"));
1853 io1.in.durable_open = true;
1855 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1856 lease2, smb2_util_lease_state("RHW"));
1857 io2.in.durable_open = true;
1858 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1860 status = smb2_create(tree1, mem_ctx, &io1);
1861 CHECK_STATUS(status, NT_STATUS_OK);
1862 h1 = io1.out.file.handle;
1863 CHECK_VAL(io1.out.durable_open, true);
1864 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1866 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1867 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1868 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1869 CHECK_VAL(io1.out.lease_response.lease_state,
1870 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1872 /* Disconnect after getting the lease */
1877 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1878 * even if the original client is gone. (ZML: This seems like a bug. It
1879 * should give some time for the client to reconnect! And why RH?)
1881 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1882 * Test is adapted accordingly.
1884 status = smb2_create(tree2, mem_ctx, &io2);
1885 CHECK_STATUS(status, NT_STATUS_OK);
1886 h2 = io2.out.file.handle;
1887 CHECK_VAL(io2.out.durable_open, true);
1888 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1890 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1891 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1892 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1893 CHECK_VAL(io2.out.lease_response.lease_state,
1894 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1896 /* What if tree1 tries to come back and reclaim? */
1897 if (!torture_smb2_connection(tctx, &tree1)) {
1898 torture_warning(tctx, "couldn't reconnect, bailing\n");
1904 io1.in.fname = fname;
1905 io1.in.durable_handle = &h1;
1906 io1.in.lease_request = &ls1;
1908 status = smb2_create(tree1, mem_ctx, &io1);
1909 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1912 smb2_util_close(tree2, h2);
1913 smb2_util_unlink(tree2, fname);
1921 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1922 struct smb2_tree *tree)
1924 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1925 struct smb2_create io;
1926 struct smb2_handle h;
1927 struct smb2_lock lck;
1928 struct smb2_lock_element el[2];
1935 snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1938 smb2_util_unlink(tree, fname);
1940 /* Create with oplock */
1942 smb2_oplock_create_share(&io, fname,
1943 smb2_util_share_access(""),
1944 smb2_util_oplock_level("b"));
1945 io.in.durable_open = true;
1947 status = smb2_create(tree, mem_ctx, &io);
1948 CHECK_STATUS(status, NT_STATUS_OK);
1949 h = io.out.file.handle;
1950 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1952 CHECK_VAL(io.out.durable_open, true);
1953 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1958 lck.in.lock_count = 0x0001;
1959 lck.in.lock_sequence = 0x00000000;
1960 lck.in.file.handle = h;
1963 el[0].reserved = 0x00000000;
1964 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1965 status = smb2_lock(tree, &lck);
1966 CHECK_STATUS(status, NT_STATUS_OK);
1968 /* Disconnect/Reconnect. */
1972 if (!torture_smb2_connection(tctx, &tree)) {
1973 torture_warning(tctx, "couldn't reconnect, bailing\n");
1979 io.in.fname = fname;
1980 io.in.durable_handle = &h;
1982 status = smb2_create(tree, mem_ctx, &io);
1983 CHECK_STATUS(status, NT_STATUS_OK);
1984 h = io.out.file.handle;
1986 lck.in.file.handle = h;
1987 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1988 status = smb2_lock(tree, &lck);
1989 CHECK_STATUS(status, NT_STATUS_OK);
1992 smb2_util_close(tree, h);
1993 smb2_util_unlink(tree, fname);
2000 Open, take BRL, disconnect, reconnect.
2002 static bool test_durable_open_lock_lease(struct torture_context *tctx,
2003 struct smb2_tree *tree)
2005 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2006 struct smb2_create io;
2007 struct smb2_lease ls;
2008 struct smb2_handle h;
2009 struct smb2_lock lck;
2010 struct smb2_lock_element el[2];
2016 struct smbcli_options options;
2018 options = tree->session->transport->options;
2020 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2021 if (!(caps & SMB2_CAP_LEASING)) {
2022 torture_skip(tctx, "leases are not supported");
2026 * Choose a random name and random lease in case the state is left a
2030 snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2033 smb2_util_unlink(tree, fname);
2035 /* Create with lease */
2037 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2038 smb2_util_lease_state("RWH"));
2039 io.in.durable_open = true;
2041 status = smb2_create(tree, mem_ctx, &io);
2042 CHECK_STATUS(status, NT_STATUS_OK);
2043 h = io.out.file.handle;
2044 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2046 CHECK_VAL(io.out.durable_open, true);
2047 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2048 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2049 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2050 CHECK_VAL(io.out.lease_response.lease_state,
2051 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2056 lck.in.lock_count = 0x0001;
2057 lck.in.lock_sequence = 0x00000000;
2058 lck.in.file.handle = h;
2061 el[0].reserved = 0x00000000;
2062 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2063 status = smb2_lock(tree, &lck);
2064 CHECK_STATUS(status, NT_STATUS_OK);
2066 /* Disconnect/Reconnect. */
2070 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2071 torture_warning(tctx, "couldn't reconnect, bailing\n");
2077 io.in.fname = fname;
2078 io.in.durable_handle = &h;
2079 io.in.lease_request = &ls;
2081 status = smb2_create(tree, mem_ctx, &io);
2082 CHECK_STATUS(status, NT_STATUS_OK);
2083 h = io.out.file.handle;
2085 lck.in.file.handle = h;
2086 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2087 status = smb2_lock(tree, &lck);
2088 CHECK_STATUS(status, NT_STATUS_OK);
2091 smb2_util_close(tree, h);
2092 smb2_util_unlink(tree, fname);
2099 * Open with a RH lease, disconnect, open in another tree, reconnect.
2101 * This test actually demonstrates a minimum level of respect for the durable
2102 * open in the face of another open. As long as this test shows an inability to
2103 * reconnect after an open, the oplock/lease tests above will certainly
2104 * demonstrate an error on reconnect.
2106 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2107 struct smb2_tree *tree1,
2108 struct smb2_tree *tree2)
2110 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2111 struct smb2_create io1, io2;
2112 struct smb2_lease ls;
2113 struct smb2_handle h1, h2;
2119 struct smbcli_options options;
2121 options = tree1->session->transport->options;
2123 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2124 if (!(caps & SMB2_CAP_LEASING)) {
2125 torture_skip(tctx, "leases are not supported");
2129 * Choose a random name and random lease in case the state is left a
2133 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2134 generate_random_str(tctx, 8));
2137 smb2_util_unlink(tree1, fname);
2139 /* Create with lease */
2140 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2141 smb2_util_share_access(""),
2143 smb2_util_lease_state("RH"));
2144 io1.in.durable_open = true;
2146 status = smb2_create(tree1, mem_ctx, &io1);
2147 CHECK_STATUS(status, NT_STATUS_OK);
2148 h1 = io1.out.file.handle;
2149 CHECK_VAL(io1.out.durable_open, true);
2150 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2152 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2153 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2154 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2155 CHECK_VAL(io1.out.lease_response.lease_state,
2156 smb2_util_lease_state("RH"));
2162 /* Open the file in tree2 */
2163 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2165 status = smb2_create(tree2, mem_ctx, &io2);
2166 CHECK_STATUS(status, NT_STATUS_OK);
2167 h2 = io2.out.file.handle;
2168 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2171 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2172 torture_warning(tctx, "couldn't reconnect, bailing\n");
2178 io1.in.fname = fname;
2179 io1.in.durable_handle = &h1;
2180 io1.in.lease_request = &ls;
2183 * Windows7 (build 7000) will give away an open immediately if the
2184 * original client is gone. (ZML: This seems like a bug. It should give
2185 * some time for the client to reconnect!)
2187 status = smb2_create(tree1, mem_ctx, &io1);
2188 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2189 h1 = io1.out.file.handle;
2193 smb2_util_close(tree1, h1);
2194 smb2_util_unlink(tree1, fname);
2198 smb2_util_close(tree2, h2);
2199 smb2_util_unlink(tree2, fname);
2206 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2208 * This test actually demonstrates a minimum level of respect for the durable
2209 * open in the face of another open. As long as this test shows an inability to
2210 * reconnect after an open, the oplock/lease tests above will certainly
2211 * demonstrate an error on reconnect.
2213 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2214 struct smb2_tree *tree1,
2215 struct smb2_tree *tree2)
2217 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2218 struct smb2_create io1, io2;
2219 struct smb2_handle h1, h2;
2225 * Choose a random name and random lease in case the state is left a
2228 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2229 generate_random_str(tctx, 8));
2232 smb2_util_unlink(tree1, fname);
2234 /* Create with batch oplock */
2235 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2236 io1.in.durable_open = true;
2238 status = smb2_create(tree1, mem_ctx, &io1);
2239 CHECK_STATUS(status, NT_STATUS_OK);
2240 h1 = io1.out.file.handle;
2241 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2242 CHECK_VAL(io1.out.durable_open, true);
2243 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2249 /* Open the file in tree2 */
2250 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2252 status = smb2_create(tree2, mem_ctx, &io2);
2253 CHECK_STATUS(status, NT_STATUS_OK);
2254 h2 = io2.out.file.handle;
2255 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2258 if (!torture_smb2_connection(tctx, &tree1)) {
2259 torture_warning(tctx, "couldn't reconnect, bailing\n");
2265 io1.in.fname = fname;
2266 io1.in.durable_handle = &h1;
2268 status = smb2_create(tree1, mem_ctx, &io1);
2269 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2270 h1 = io1.out.file.handle;
2273 smb2_util_close(tree2, h2);
2274 smb2_util_unlink(tree2, fname);
2275 if (tree1 != NULL) {
2276 smb2_util_close(tree1, h1);
2277 smb2_util_unlink(tree1, fname);
2287 * test behaviour with initial allocation size
2289 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2290 struct smb2_tree *tree)
2293 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2295 struct smb2_handle _h;
2296 struct smb2_handle *h = NULL;
2297 struct smb2_create io;
2299 uint64_t previous_session_id;
2300 uint64_t alloc_size_step;
2301 uint64_t initial_alloc_size = 0x100;
2302 const uint8_t *b = NULL;
2303 struct smbcli_options options;
2305 options = tree->session->transport->options;
2307 /* Choose a random name in case the state is left a little funky. */
2308 snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2309 generate_random_str(tctx, 8));
2311 smb2_util_unlink(tree, fname);
2313 smb2_oplock_create_share(&io, fname,
2314 smb2_util_share_access(""),
2315 smb2_util_oplock_level("b"));
2316 io.in.durable_open = true;
2317 io.in.alloc_size = initial_alloc_size;
2319 status = smb2_create(tree, mem_ctx, &io);
2320 CHECK_STATUS(status, NT_STATUS_OK);
2321 _h = io.out.file.handle;
2323 CHECK_NOT_VAL(io.out.alloc_size, 0);
2324 alloc_size_step = io.out.alloc_size;
2325 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2326 alloc_size_step, 0);
2327 CHECK_VAL(io.out.durable_open, true);
2328 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2330 /* prepare buffer */
2331 b = talloc_zero_size(mem_ctx, alloc_size_step);
2334 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2336 /* disconnect, reconnect and then do durable reopen */
2340 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2343 torture_warning(tctx, "couldn't reconnect, bailing\n");
2349 io.in.fname = fname;
2350 io.in.durable_handle = h;
2353 status = smb2_create(tree, mem_ctx, &io);
2354 CHECK_STATUS(status, NT_STATUS_OK);
2355 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2356 alloc_size_step, 0);
2357 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2358 _h = io.out.file.handle;
2361 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2363 /* write one byte */
2364 status = smb2_util_write(tree, *h, b, 0, 1);
2365 CHECK_STATUS(status, NT_STATUS_OK);
2367 /* disconnect, reconnect and then do durable reopen */
2371 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2374 torture_warning(tctx, "couldn't reconnect, bailing\n");
2380 io.in.fname = fname;
2381 io.in.durable_handle = h;
2384 status = smb2_create(tree, mem_ctx, &io);
2385 CHECK_STATUS(status, NT_STATUS_OK);
2386 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2387 alloc_size_step, 1);
2388 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2389 _h = io.out.file.handle;
2392 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2394 /* write more byte than initial allocation size */
2395 status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2397 /* disconnect, reconnect and then do durable reopen */
2401 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2404 torture_warning(tctx, "couldn't reconnect, bailing\n");
2410 io.in.fname = fname;
2411 io.in.durable_handle = h;
2414 status = smb2_create(tree, mem_ctx, &io);
2415 CHECK_STATUS(status, NT_STATUS_OK);
2416 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2417 alloc_size_step * 2, alloc_size_step + 1);
2418 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2419 _h = io.out.file.handle;
2424 smb2_util_close(tree, *h);
2427 smb2_util_unlink(tree, fname);
2431 talloc_free(mem_ctx);
2437 * test behaviour when a disconnect happens while creating a read-only file
2439 static bool test_durable_open_read_only(struct torture_context *tctx,
2440 struct smb2_tree *tree)
2443 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2445 struct smb2_handle _h;
2446 struct smb2_handle *h = NULL;
2447 struct smb2_create io;
2449 uint64_t previous_session_id;
2450 const uint8_t b = 0;
2451 uint64_t alloc_size = 0;
2452 struct smbcli_options options;
2454 options = tree->session->transport->options;
2456 /* Choose a random name in case the state is left a little funky. */
2457 snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2458 generate_random_str(tctx, 8));
2460 smb2_util_unlink(tree, fname);
2462 smb2_oplock_create_share(&io, fname,
2463 smb2_util_share_access(""),
2464 smb2_util_oplock_level("b"));
2465 io.in.durable_open = true;
2466 io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2468 status = smb2_create(tree, mem_ctx, &io);
2469 CHECK_STATUS(status, NT_STATUS_OK);
2470 _h = io.out.file.handle;
2472 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2473 CHECK_VAL(io.out.durable_open, true);
2474 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2476 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2478 /* write one byte */
2479 status = smb2_util_write(tree, *h, &b, 0, 1);
2480 CHECK_STATUS(status, NT_STATUS_OK);
2482 /* disconnect, reconnect and then do durable reopen */
2486 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2489 torture_warning(tctx, "couldn't reconnect, bailing\n");
2495 io.in.fname = fname;
2496 io.in.durable_handle = h;
2499 status = smb2_create(tree, mem_ctx, &io);
2500 CHECK_STATUS(status, NT_STATUS_OK);
2501 alloc_size = io.out.alloc_size;
2502 CHECK_CREATED_SIZE(&io, EXISTED,
2503 FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2505 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2506 _h = io.out.file.handle;
2509 /* write one byte */
2510 status = smb2_util_write(tree, *h, &b, 1, 1);
2511 CHECK_STATUS(status, NT_STATUS_OK);
2515 union smb_setfileinfo sfinfo;
2517 ZERO_STRUCT(sfinfo);
2518 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2519 sfinfo.basic_info.in.file.handle = *h;
2520 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2521 smb2_setinfo_file(tree, &sfinfo);
2523 smb2_util_close(tree, *h);
2526 smb2_util_unlink(tree, fname);
2530 talloc_free(mem_ctx);
2536 * durable open with oplock, disconnect, exit
2538 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2539 struct smb2_tree *tree)
2541 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2542 struct smb2_create io;
2543 struct smb2_handle _h;
2544 struct smb2_handle *h = NULL;
2549 snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2550 generate_random_str(mem_ctx, 8));
2552 smb2_util_unlink(tree, fname);
2554 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2555 io.in.durable_open = true;
2557 status = smb2_create(tree, mem_ctx, &io);
2558 CHECK_STATUS(status, NT_STATUS_OK);
2560 _h = io.out.file.handle;
2563 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2564 CHECK_VAL(io.out.durable_open, true);
2565 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2574 smb2_util_close(tree, *h);
2576 smb2_util_unlink(tree, fname);
2578 talloc_free(mem_ctx);
2583 struct torture_suite *torture_smb2_durable_open_init(void)
2585 struct torture_suite *suite =
2586 torture_suite_create(talloc_autofree_context(), "durable-open");
2588 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2589 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2590 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2591 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2592 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2593 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2594 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2595 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2596 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2597 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2598 torture_suite_add_1smb2_test(suite, "delete_on_close1",
2599 test_durable_open_delete_on_close1);
2600 torture_suite_add_1smb2_test(suite, "delete_on_close2",
2601 test_durable_open_delete_on_close2);
2602 torture_suite_add_1smb2_test(suite, "file-position",
2603 test_durable_open_file_position);
2604 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2605 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2606 torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2607 torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2608 torture_suite_add_2smb2_test(suite, "open2-lease",
2609 test_durable_open_open2_lease);
2610 torture_suite_add_2smb2_test(suite, "open2-oplock",
2611 test_durable_open_open2_oplock);
2612 torture_suite_add_1smb2_test(suite, "alloc-size",
2613 test_durable_open_alloc_size);
2614 torture_suite_add_1smb2_test(suite, "read-only",
2615 test_durable_open_read_only);
2617 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2622 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
2624 struct torture_suite *suite =
2625 torture_suite_create(talloc_autofree_context(),
2626 "durable-open-disconnect");
2628 torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2629 test_durable_open_oplock_disconnect);
2631 suite->description = talloc_strdup(suite,
2632 "SMB2-DURABLE-OPEN-DISCONNECT tests");