2 Unix SMB/CIFS implementation.
4 test suite for SMB2 version two of durable opens
6 Copyright (C) Michael Adam 2012
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "librpc/ndr/libndr.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)v, (int)correct); \
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
45 #define CHECK_CREATED(__io, __created, __attribute) \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
59 static void torture_oplock_close_callback(struct smb2_request *req)
61 smb2_close_recv(req, &break_info.cl);
64 /* A general oplock break notification handler. This should be used when a
65 * test expects to break from batch or exclusive to a lower level. */
66 static bool torture_oplock_handler(struct smb2_transport *transport,
67 const struct smb2_handle *handle,
71 struct smb2_tree *tree = private_data;
72 struct smb2_request *req;
76 ZERO_STRUCT(break_info.cl);
77 break_info.cl.in.file.handle = *handle;
79 req = smb2_close_send(tree, &break_info.cl);
80 req->async.fn = torture_oplock_close_callback;
81 req->async.private_data = NULL;
86 * testing various create blob combinations.
88 bool test_durable_v2_open_create_blob(struct torture_context *tctx,
89 struct smb2_tree *tree)
92 TALLOC_CTX *mem_ctx = talloc_new(tctx);
94 struct smb2_handle _h;
95 struct smb2_handle *h = NULL;
96 struct smb2_create io;
97 struct GUID create_guid = GUID_random();
99 struct smbcli_options options;
101 options = tree->session->transport->options;
103 /* Choose a random name in case the state is left a little funky. */
104 snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
105 generate_random_str(tctx, 8));
107 smb2_util_unlink(tree, fname);
109 smb2_oplock_create_share(&io, fname,
110 smb2_util_share_access(""),
111 smb2_util_oplock_level("b"));
112 io.in.durable_open = false;
113 io.in.durable_open_v2 = true;
114 io.in.persistent_open = false;
115 io.in.create_guid = create_guid;
116 io.in.timeout = UINT32_MAX;
118 status = smb2_create(tree, mem_ctx, &io);
119 CHECK_STATUS(status, NT_STATUS_OK);
120 _h = io.out.file.handle;
122 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
123 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
124 CHECK_VAL(io.out.durable_open, false);
125 CHECK_VAL(io.out.durable_open_v2, true);
126 CHECK_VAL(io.out.persistent_open, false);
127 CHECK_VAL(io.out.timeout, io.in.timeout);
132 /* create a new session (same client_guid) */
133 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
134 torture_warning(tctx, "couldn't reconnect, bailing\n");
140 * check invalid combinations of durable handle
141 * request and reconnect blobs
142 * See MS-SMB2: 3.3.5.9.12
143 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
147 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
148 io.in.durable_open = true; /* durable v1 handle request */
149 io.in.create_guid = create_guid;
150 status = smb2_create(tree, mem_ctx, &io);
151 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
155 io.in.durable_handle = h; /* durable v1 reconnect request */
156 io.in.durable_open_v2 = true; /* durable v2 handle request */
157 io.in.create_guid = create_guid;
158 status = smb2_create(tree, mem_ctx, &io);
159 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
163 io.in.durable_handle = h; /* durable v1 reconnect request */
164 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
165 io.in.create_guid = create_guid;
166 status = smb2_create(tree, mem_ctx, &io);
167 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
171 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
172 io.in.durable_open_v2 = true; /* durable v2 handle request */
173 io.in.create_guid = create_guid;
174 status = smb2_create(tree, mem_ctx, &io);
175 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
179 smb2_util_close(tree, *h);
182 smb2_util_unlink(tree, fname);
186 talloc_free(mem_ctx);
193 * basic durable_open test.
194 * durable state should only be granted when requested
195 * along with a batch oplock or a handle lease.
197 * This test tests durable open with all possible oplock types.
200 struct durable_open_vs_oplock {
202 const char *share_mode;
207 #define NUM_OPLOCK_TYPES 4
208 #define NUM_SHARE_MODES 8
209 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
210 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
212 { "", "", false, false },
213 { "", "R", false, false },
214 { "", "W", false, false },
215 { "", "D", false, false },
216 { "", "RD", false, false },
217 { "", "RW", false, false },
218 { "", "WD", false, false },
219 { "", "RWD", false, false },
221 { "s", "", false, false },
222 { "s", "R", false, false },
223 { "s", "W", false, false },
224 { "s", "D", false, false },
225 { "s", "RD", false, false },
226 { "s", "RW", false, false },
227 { "s", "WD", false, false },
228 { "s", "RWD", false, false },
230 { "x", "", false, false },
231 { "x", "R", false, false },
232 { "x", "W", false, false },
233 { "x", "D", false, false },
234 { "x", "RD", false, false },
235 { "x", "RW", false, false },
236 { "x", "WD", false, false },
237 { "x", "RWD", false, false },
239 { "b", "", true, false },
240 { "b", "R", true, false },
241 { "b", "W", true, false },
242 { "b", "D", true, false },
243 { "b", "RD", true, false },
244 { "b", "RW", true, false },
245 { "b", "WD", true, false },
246 { "b", "RWD", true, false },
249 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
250 struct smb2_tree *tree,
252 bool request_persistent,
253 struct durable_open_vs_oplock test)
256 TALLOC_CTX *mem_ctx = talloc_new(tctx);
257 struct smb2_handle _h;
258 struct smb2_handle *h = NULL;
260 struct smb2_create io;
262 smb2_util_unlink(tree, fname);
264 smb2_oplock_create_share(&io, fname,
265 smb2_util_share_access(test.share_mode),
266 smb2_util_oplock_level(test.level));
267 io.in.durable_open = false;
268 io.in.durable_open_v2 = true;
269 io.in.persistent_open = request_persistent;
270 io.in.create_guid = GUID_random();
272 status = smb2_create(tree, mem_ctx, &io);
273 CHECK_STATUS(status, NT_STATUS_OK);
274 _h = io.out.file.handle;
276 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
277 CHECK_VAL(io.out.durable_open, false);
278 CHECK_VAL(io.out.durable_open_v2, test.durable);
279 CHECK_VAL(io.out.persistent_open, test.persistent);
280 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
284 smb2_util_close(tree, *h);
286 smb2_util_unlink(tree, fname);
287 talloc_free(mem_ctx);
292 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
293 struct smb2_tree *tree,
295 bool request_persistent,
296 struct durable_open_vs_oplock *table,
302 smb2_util_unlink(tree, fname);
304 for (i = 0; i < num_tests; i++) {
305 ret = test_one_durable_v2_open_oplock(tctx,
316 smb2_util_unlink(tree, fname);
321 bool test_durable_v2_open_oplock(struct torture_context *tctx,
322 struct smb2_tree *tree)
327 /* Choose a random name in case the state is left a little funky. */
328 snprintf(fname, 256, "durable_open_oplock_%s.dat",
329 generate_random_str(tctx, 8));
331 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
332 false, /* request_persistent */
333 durable_open_vs_oplock_table,
334 NUM_OPLOCK_OPEN_TESTS);
342 * basic durable handle open test.
343 * persistent state should only be granted when requested
344 * along with a batch oplock or a handle lease.
346 * This test tests persistent open with all valid lease types.
349 struct durable_open_vs_lease {
351 const char *share_mode;
356 #define NUM_LEASE_TYPES 5
357 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
358 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
360 { "", "", false, false },
361 { "", "R", false, false },
362 { "", "W", false, false },
363 { "", "D", false, false },
364 { "", "RW", false, false },
365 { "", "RD", false, false },
366 { "", "WD", false, false },
367 { "", "RWD", false, false },
369 { "R", "", false, false },
370 { "R", "R", false, false },
371 { "R", "W", false, false },
372 { "R", "D", false, false },
373 { "R", "RW", false, false },
374 { "R", "RD", false, false },
375 { "R", "DW", false, false },
376 { "R", "RWD", false, false },
378 { "RW", "", false, false },
379 { "RW", "R", false, false },
380 { "RW", "W", false, false },
381 { "RW", "D", false, false },
382 { "RW", "RW", false, false },
383 { "RW", "RD", false, false },
384 { "RW", "WD", false, false },
385 { "RW", "RWD", false, false },
387 { "RH", "", true, false },
388 { "RH", "R", true, false },
389 { "RH", "W", true, false },
390 { "RH", "D", true, false },
391 { "RH", "RW", true, false },
392 { "RH", "RD", true, false },
393 { "RH", "WD", true, false },
394 { "RH", "RWD", true, false },
396 { "RHW", "", true, false },
397 { "RHW", "R", true, false },
398 { "RHW", "W", true, false },
399 { "RHW", "D", true, false },
400 { "RHW", "RW", true, false },
401 { "RHW", "RD", true, false },
402 { "RHW", "WD", true, false },
403 { "RHW", "RWD", true, false },
406 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
407 struct smb2_tree *tree,
409 bool request_persistent,
410 struct durable_open_vs_lease test)
413 TALLOC_CTX *mem_ctx = talloc_new(tctx);
414 struct smb2_handle _h;
415 struct smb2_handle *h = NULL;
417 struct smb2_create io;
418 struct smb2_lease ls;
421 smb2_util_unlink(tree, fname);
425 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
426 smb2_util_share_access(test.share_mode),
428 smb2_util_lease_state(test.type));
429 io.in.durable_open = false;
430 io.in.durable_open_v2 = true;
431 io.in.persistent_open = request_persistent;
432 io.in.create_guid = GUID_random();
434 status = smb2_create(tree, mem_ctx, &io);
435 CHECK_STATUS(status, NT_STATUS_OK);
436 _h = io.out.file.handle;
438 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
439 CHECK_VAL(io.out.durable_open, false);
440 CHECK_VAL(io.out.durable_open_v2, test.durable);
441 CHECK_VAL(io.out.persistent_open, test.persistent);
442 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
443 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
444 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
445 CHECK_VAL(io.out.lease_response.lease_state,
446 smb2_util_lease_state(test.type));
449 smb2_util_close(tree, *h);
451 smb2_util_unlink(tree, fname);
452 talloc_free(mem_ctx);
457 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
458 struct smb2_tree *tree,
460 bool request_persistent,
461 struct durable_open_vs_lease *table,
467 smb2_util_unlink(tree, fname);
469 for (i = 0; i < num_tests; i++) {
470 ret = test_one_durable_v2_open_lease(tctx,
481 smb2_util_unlink(tree, fname);
486 bool test_durable_v2_open_lease(struct torture_context *tctx,
487 struct smb2_tree *tree)
493 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
494 if (!(caps & SMB2_CAP_LEASING)) {
495 torture_skip(tctx, "leases are not supported");
498 /* Choose a random name in case the state is left a little funky. */
499 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
501 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
502 false, /* request_persistent */
503 durable_open_vs_lease_table,
504 NUM_LEASE_OPEN_TESTS);
511 * basic test for doing a durable open
512 * and do a durable reopen on the same connection
513 * while the first open is still active (fails)
515 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
516 struct smb2_tree *tree)
519 TALLOC_CTX *mem_ctx = talloc_new(tctx);
521 struct smb2_handle _h;
522 struct smb2_handle *h = NULL;
523 struct smb2_create io;
524 struct GUID create_guid = GUID_random();
527 /* Choose a random name in case the state is left a little funky. */
528 snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
529 generate_random_str(tctx, 8));
531 smb2_util_unlink(tree, fname);
533 smb2_oplock_create_share(&io, fname,
534 smb2_util_share_access(""),
535 smb2_util_oplock_level("b"));
536 io.in.durable_open = false;
537 io.in.durable_open_v2 = true;
538 io.in.persistent_open = false;
539 io.in.create_guid = create_guid;
540 io.in.timeout = UINT32_MAX;
542 status = smb2_create(tree, mem_ctx, &io);
543 CHECK_STATUS(status, NT_STATUS_OK);
544 _h = io.out.file.handle;
546 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
547 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
548 CHECK_VAL(io.out.durable_open, false);
549 CHECK_VAL(io.out.durable_open_v2, true);
550 CHECK_VAL(io.out.persistent_open, false);
551 CHECK_VAL(io.out.timeout, io.in.timeout);
553 /* try a durable reconnect while the file is still open */
556 io.in.durable_handle_v2 = h;
557 io.in.create_guid = create_guid;
558 status = smb2_create(tree, mem_ctx, &io);
559 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
563 smb2_util_close(tree, *h);
566 smb2_util_unlink(tree, fname);
570 talloc_free(mem_ctx);
576 * Basic test for doing a durable open
577 * and do a session reconnect while the first
578 * session is still active and the handle is
579 * still open in the client.
580 * This closes the original session and a
581 * durable reconnect on the new session succeeds.
583 bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
584 struct smb2_tree *tree)
587 TALLOC_CTX *mem_ctx = talloc_new(tctx);
589 struct smb2_handle _h;
590 struct smb2_handle *h = NULL;
591 struct smb2_create io;
592 struct GUID create_guid = GUID_random();
594 struct smb2_tree *tree2 = NULL;
595 struct smb2_tree *tree3 = NULL;
596 uint64_t previous_session_id;
597 struct smbcli_options options;
598 struct GUID orig_client_guid;
600 options = tree->session->transport->options;
601 orig_client_guid = options.client_guid;
603 /* Choose a random name in case the state is left a little funky. */
604 snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
605 generate_random_str(tctx, 8));
607 smb2_util_unlink(tree, fname);
609 smb2_oplock_create_share(&io, fname,
610 smb2_util_share_access(""),
611 smb2_util_oplock_level("b"));
612 io.in.durable_open = false;
613 io.in.durable_open_v2 = true;
614 io.in.persistent_open = false;
615 io.in.create_guid = create_guid;
616 io.in.timeout = UINT32_MAX;
618 status = smb2_create(tree, mem_ctx, &io);
619 CHECK_STATUS(status, NT_STATUS_OK);
620 _h = io.out.file.handle;
622 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
623 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
624 CHECK_VAL(io.out.durable_open, false);
625 CHECK_VAL(io.out.durable_open_v2, true);
626 CHECK_VAL(io.out.persistent_open, false);
627 CHECK_VAL(io.out.timeout, io.in.timeout);
630 * a session reconnect on a second tcp connection
633 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
635 /* for oplocks, the client guid can be different: */
636 options.client_guid = GUID_random();
638 ret = torture_smb2_connection_ext(tctx, previous_session_id,
640 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
643 * check that this has deleted the old session
648 io.in.durable_handle_v2 = h;
649 io.in.create_guid = create_guid;
650 status = smb2_create(tree, mem_ctx, &io);
651 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
656 * but a durable reconnect on the new session succeeds:
661 io.in.durable_handle_v2 = h;
662 io.in.create_guid = create_guid;
663 status = smb2_create(tree2, mem_ctx, &io);
664 CHECK_STATUS(status, NT_STATUS_OK);
665 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
666 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
667 CHECK_VAL(io.out.durable_open, false);
668 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
669 CHECK_VAL(io.out.persistent_open, false);
670 CHECK_VAL(io.out.timeout, io.in.timeout);
671 _h = io.out.file.handle;
675 * a session reconnect on a second tcp connection
678 previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
680 /* it works the same with the original guid */
681 options.client_guid = orig_client_guid;
683 ret = torture_smb2_connection_ext(tctx, previous_session_id,
685 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
688 * check that this has deleted the old session
693 io.in.durable_handle_v2 = h;
694 io.in.create_guid = create_guid;
695 status = smb2_create(tree2, mem_ctx, &io);
696 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
700 * but a durable reconnect on the new session succeeds:
705 io.in.durable_handle_v2 = h;
706 io.in.create_guid = create_guid;
707 status = smb2_create(tree3, mem_ctx, &io);
708 CHECK_STATUS(status, NT_STATUS_OK);
709 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
710 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
711 CHECK_VAL(io.out.durable_open, false);
712 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
713 CHECK_VAL(io.out.persistent_open, false);
714 CHECK_VAL(io.out.timeout, io.in.timeout);
715 _h = io.out.file.handle;
729 smb2_util_close(tree, *h);
732 smb2_util_unlink(tree, fname);
737 talloc_free(mem_ctx);
743 * basic test for doing a durable open
744 * tcp disconnect, reconnect, do a durable reopen (succeeds)
746 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
747 struct smb2_tree *tree)
750 TALLOC_CTX *mem_ctx = talloc_new(tctx);
752 struct smb2_handle _h;
753 struct smb2_handle *h = NULL;
754 struct smb2_create io;
755 struct GUID create_guid = GUID_random();
756 struct GUID create_guid_invalid = GUID_random();
759 /* Choose a random name in case the state is left a little funky. */
760 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
761 generate_random_str(tctx, 8));
763 smb2_util_unlink(tree, fname);
765 smb2_oplock_create_share(&io, fname,
766 smb2_util_share_access(""),
767 smb2_util_oplock_level("b"));
768 io.in.durable_open = false;
769 io.in.durable_open_v2 = true;
770 io.in.persistent_open = false;
771 io.in.create_guid = create_guid;
772 io.in.timeout = UINT32_MAX;
774 status = smb2_create(tree, mem_ctx, &io);
775 CHECK_STATUS(status, NT_STATUS_OK);
776 _h = io.out.file.handle;
778 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
779 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
780 CHECK_VAL(io.out.durable_open, false);
781 CHECK_VAL(io.out.durable_open_v2, true);
782 CHECK_VAL(io.out.persistent_open, false);
783 CHECK_VAL(io.out.timeout, io.in.timeout);
785 /* disconnect, leaving the durable open */
788 if (!torture_smb2_connection(tctx, &tree)) {
789 torture_warning(tctx, "couldn't reconnect, bailing\n");
795 * first a few failure cases
800 io.in.durable_handle_v2 = h;
801 status = smb2_create(tree, mem_ctx, &io);
802 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
805 io.in.fname = "__non_existing_fname__";
806 io.in.durable_handle_v2 = h;
807 status = smb2_create(tree, mem_ctx, &io);
808 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
812 io.in.durable_handle_v2 = h;
813 status = smb2_create(tree, mem_ctx, &io);
814 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
816 /* a non-zero but non-matching create_guid does not change it: */
819 io.in.durable_handle_v2 = h;
820 io.in.create_guid = create_guid_invalid;
821 status = smb2_create(tree, mem_ctx, &io);
822 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
826 * The important difference is that the create_guid is provided.
830 io.in.durable_open_v2 = false;
831 io.in.durable_handle_v2 = h;
832 io.in.create_guid = create_guid;
835 status = smb2_create(tree, mem_ctx, &io);
836 CHECK_STATUS(status, NT_STATUS_OK);
837 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
838 CHECK_VAL(io.out.durable_open, false);
839 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
840 CHECK_VAL(io.out.persistent_open, false);
841 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
842 _h = io.out.file.handle;
845 /* disconnect one more time */
848 if (!torture_smb2_connection(tctx, &tree)) {
849 torture_warning(tctx, "couldn't reconnect, bailing\n");
855 /* These are completely ignored by the server */
856 io.in.security_flags = 0x78;
857 io.in.oplock_level = 0x78;
858 io.in.impersonation_level = 0x12345678;
859 io.in.create_flags = 0x12345678;
860 io.in.reserved = 0x12345678;
861 io.in.desired_access = 0x12345678;
862 io.in.file_attributes = 0x12345678;
863 io.in.share_access = 0x12345678;
864 io.in.create_disposition = 0x12345678;
865 io.in.create_options = 0x12345678;
866 io.in.fname = "__non_existing_fname__";
869 * only io.in.durable_handle_v2 and
870 * io.in.create_guid are checked
872 io.in.durable_open_v2 = false;
873 io.in.durable_handle_v2 = h;
874 io.in.create_guid = create_guid;
877 status = smb2_create(tree, mem_ctx, &io);
878 CHECK_STATUS(status, NT_STATUS_OK);
879 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
880 CHECK_VAL(io.out.durable_open, false);
881 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
882 CHECK_VAL(io.out.persistent_open, false);
883 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
884 _h = io.out.file.handle;
889 smb2_util_close(tree, *h);
892 smb2_util_unlink(tree, fname);
896 talloc_free(mem_ctx);
902 * durable reconnect test:
903 * connect with v2, reconnect with v1
905 bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
906 struct smb2_tree *tree)
909 TALLOC_CTX *mem_ctx = talloc_new(tctx);
911 struct smb2_handle _h;
912 struct smb2_handle *h = NULL;
913 struct smb2_create io;
914 struct GUID create_guid = GUID_random();
916 struct smbcli_options options;
918 options = tree->session->transport->options;
920 /* Choose a random name in case the state is left a little funky. */
921 snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
922 generate_random_str(tctx, 8));
924 smb2_util_unlink(tree, fname);
926 smb2_oplock_create_share(&io, fname,
927 smb2_util_share_access(""),
928 smb2_util_oplock_level("b"));
929 io.in.durable_open = false;
930 io.in.durable_open_v2 = true;
931 io.in.persistent_open = false;
932 io.in.create_guid = create_guid;
933 io.in.timeout = UINT32_MAX;
935 status = smb2_create(tree, mem_ctx, &io);
936 CHECK_STATUS(status, NT_STATUS_OK);
937 _h = io.out.file.handle;
939 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
940 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
941 CHECK_VAL(io.out.durable_open, false);
942 CHECK_VAL(io.out.durable_open_v2, true);
943 CHECK_VAL(io.out.persistent_open, false);
944 CHECK_VAL(io.out.timeout, io.in.timeout);
946 /* disconnect, leaving the durable open */
949 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
950 torture_warning(tctx, "couldn't reconnect, bailing\n");
957 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
958 io.in.create_guid = GUID_zero(); /* but zero create GUID */
959 status = smb2_create(tree, mem_ctx, &io);
960 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
964 io.in.durable_handle = h; /* durable v1 (!) reconnect */
967 status = smb2_create(tree, mem_ctx, &io);
968 CHECK_STATUS(status, NT_STATUS_OK);
969 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
970 CHECK_VAL(io.out.durable_open, false);
971 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
972 CHECK_VAL(io.out.persistent_open, false);
973 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
974 _h = io.out.file.handle;
979 smb2_util_close(tree, *h);
982 smb2_util_unlink(tree, fname);
986 talloc_free(mem_ctx);
991 * durable reconnect test:
992 * connect with v1, reconnect with v2 : fails (no create_guid...)
994 bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
995 struct smb2_tree *tree)
998 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1000 struct smb2_handle _h;
1001 struct smb2_handle *h = NULL;
1002 struct smb2_create io;
1003 struct GUID create_guid = GUID_random();
1005 struct smbcli_options options;
1007 options = tree->session->transport->options;
1009 /* Choose a random name in case the state is left a little funky. */
1010 snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
1011 generate_random_str(tctx, 8));
1013 smb2_util_unlink(tree, fname);
1015 smb2_oplock_create_share(&io, fname,
1016 smb2_util_share_access(""),
1017 smb2_util_oplock_level("b"));
1018 io.in.durable_open = true;
1019 io.in.durable_open_v2 = false;
1020 io.in.persistent_open = false;
1021 io.in.create_guid = create_guid;
1022 io.in.timeout = UINT32_MAX;
1024 status = smb2_create(tree, mem_ctx, &io);
1025 CHECK_STATUS(status, NT_STATUS_OK);
1026 _h = io.out.file.handle;
1028 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1029 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1030 CHECK_VAL(io.out.durable_open, true);
1031 CHECK_VAL(io.out.durable_open_v2, false);
1032 CHECK_VAL(io.out.persistent_open, false);
1033 CHECK_VAL(io.out.timeout, 0);
1035 /* disconnect, leaving the durable open */
1038 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1039 torture_warning(tctx, "couldn't reconnect, bailing\n");
1045 io.in.fname = fname;
1046 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1047 io.in.create_guid = create_guid;
1048 status = smb2_create(tree, mem_ctx, &io);
1049 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1053 smb2_util_close(tree, *h);
1056 smb2_util_unlink(tree, fname);
1060 talloc_free(mem_ctx);
1066 * lease variant of reopen2
1067 * basic test for doing a durable open
1068 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1070 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1071 struct smb2_tree *tree)
1074 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1076 struct smb2_handle _h;
1077 struct smb2_handle *h = NULL;
1078 struct smb2_create io;
1079 struct GUID create_guid = GUID_random();
1080 struct smb2_lease ls;
1083 struct smbcli_options options;
1086 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1087 if (!(caps & SMB2_CAP_LEASING)) {
1088 torture_skip(tctx, "leases are not supported");
1091 options = tree->session->transport->options;
1093 /* Choose a random name in case the state is left a little funky. */
1094 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1095 generate_random_str(tctx, 8));
1097 smb2_util_unlink(tree, fname);
1099 lease_key = random();
1100 smb2_lease_create(&io, &ls, false /* dir */, fname,
1101 lease_key, smb2_util_lease_state("RWH"));
1102 io.in.durable_open = false;
1103 io.in.durable_open_v2 = true;
1104 io.in.persistent_open = false;
1105 io.in.create_guid = create_guid;
1106 io.in.timeout = UINT32_MAX;
1108 status = smb2_create(tree, mem_ctx, &io);
1109 CHECK_STATUS(status, NT_STATUS_OK);
1110 _h = io.out.file.handle;
1112 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1113 CHECK_VAL(io.out.durable_open, false);
1114 CHECK_VAL(io.out.durable_open_v2, true);
1115 CHECK_VAL(io.out.persistent_open, false);
1116 CHECK_VAL(io.out.timeout, io.in.timeout);
1117 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1118 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1119 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1120 CHECK_VAL(io.out.lease_response.lease_state,
1121 smb2_util_lease_state("RWH"));
1122 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1123 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1125 /* disconnect, reconnect and then do durable reopen */
1128 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1129 torture_warning(tctx, "couldn't reconnect, bailing\n");
1134 /* a few failure tests: */
1137 * several attempts without lease attached:
1138 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1139 * irrespective of file name provided
1144 io.in.durable_handle_v2 = h;
1145 status = smb2_create(tree, mem_ctx, &io);
1146 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1149 io.in.fname = "__non_existing_fname__";
1150 io.in.durable_handle_v2 = h;
1151 status = smb2_create(tree, mem_ctx, &io);
1152 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1155 io.in.fname = fname;
1156 io.in.durable_handle_v2 = h;
1157 status = smb2_create(tree, mem_ctx, &io);
1158 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1161 * attempt with lease provided, but
1162 * with a changed lease key. => fails
1165 io.in.fname = fname;
1166 io.in.durable_open_v2 = false;
1167 io.in.durable_handle_v2 = h;
1168 io.in.create_guid = create_guid;
1169 io.in.lease_request = &ls;
1170 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1171 /* a wrong lease key lets the request fail */
1172 ls.lease_key.data[0]++;
1174 status = smb2_create(tree, mem_ctx, &io);
1175 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1177 /* restore the correct lease key */
1178 ls.lease_key.data[0]--;
1181 * this last failing attempt is almost correct:
1182 * only problem is: we use the wrong filename...
1183 * Note that this gives INVALID_PARAMETER.
1184 * This is different from oplocks!
1187 io.in.fname = "__non_existing_fname__";
1188 io.in.durable_open_v2 = false;
1189 io.in.durable_handle_v2 = h;
1190 io.in.create_guid = create_guid;
1191 io.in.lease_request = &ls;
1192 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1194 status = smb2_create(tree, mem_ctx, &io);
1195 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1198 * Now for a succeeding reconnect:
1202 io.in.fname = fname;
1203 io.in.durable_open_v2 = false;
1204 io.in.durable_handle_v2 = h;
1205 io.in.create_guid = create_guid;
1206 io.in.lease_request = &ls;
1207 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1209 /* the requested lease state is irrelevant */
1210 ls.lease_state = smb2_util_lease_state("");
1214 status = smb2_create(tree, mem_ctx, &io);
1215 CHECK_STATUS(status, NT_STATUS_OK);
1217 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1218 CHECK_VAL(io.out.durable_open, false);
1219 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1220 CHECK_VAL(io.out.persistent_open, false);
1221 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1222 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1223 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1224 CHECK_VAL(io.out.lease_response.lease_state,
1225 smb2_util_lease_state("RWH"));
1226 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1227 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1228 _h = io.out.file.handle;
1231 /* disconnect one more time */
1234 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1235 torture_warning(tctx, "couldn't reconnect, bailing\n");
1241 * demonstrate that various parameters are ignored
1247 * These are completely ignored by the server
1249 io.in.security_flags = 0x78;
1250 io.in.oplock_level = 0x78;
1251 io.in.impersonation_level = 0x12345678;
1252 io.in.create_flags = 0x12345678;
1253 io.in.reserved = 0x12345678;
1254 io.in.desired_access = 0x12345678;
1255 io.in.file_attributes = 0x12345678;
1256 io.in.share_access = 0x12345678;
1257 io.in.create_disposition = 0x12345678;
1258 io.in.create_options = 0x12345678;
1261 * only these are checked:
1263 * - io.in.durable_handle_v2,
1264 * - io.in.create_guid
1265 * - io.in.lease_request->lease_key
1268 io.in.fname = fname;
1269 io.in.durable_open_v2 = false;
1270 io.in.durable_handle_v2 = h;
1271 io.in.create_guid = create_guid;
1272 io.in.lease_request = &ls;
1274 /* the requested lease state is irrelevant */
1275 ls.lease_state = smb2_util_lease_state("");
1279 status = smb2_create(tree, mem_ctx, &io);
1280 CHECK_STATUS(status, NT_STATUS_OK);
1282 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1283 CHECK_VAL(io.out.durable_open, false);
1284 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1285 CHECK_VAL(io.out.persistent_open, false);
1286 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1287 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1288 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1289 CHECK_VAL(io.out.lease_response.lease_state,
1290 smb2_util_lease_state("RWH"));
1291 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1292 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1294 _h = io.out.file.handle;
1299 smb2_util_close(tree, *h);
1302 smb2_util_unlink(tree, fname);
1306 talloc_free(mem_ctx);
1312 * lease_v2 variant of reopen2
1313 * basic test for doing a durable open
1314 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1316 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1317 struct smb2_tree *tree)
1320 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1322 struct smb2_handle _h;
1323 struct smb2_handle *h = NULL;
1324 struct smb2_create io;
1325 struct GUID create_guid = GUID_random();
1326 struct smb2_lease ls;
1329 struct smbcli_options options;
1332 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1333 if (!(caps & SMB2_CAP_LEASING)) {
1334 torture_skip(tctx, "leases are not supported");
1337 options = tree->session->transport->options;
1339 /* Choose a random name in case the state is left a little funky. */
1340 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1341 generate_random_str(tctx, 8));
1343 smb2_util_unlink(tree, fname);
1345 lease_key = random();
1346 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1347 lease_key, 0, /* parent lease key */
1348 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1349 io.in.durable_open = false;
1350 io.in.durable_open_v2 = true;
1351 io.in.persistent_open = false;
1352 io.in.create_guid = create_guid;
1353 io.in.timeout = UINT32_MAX;
1355 status = smb2_create(tree, mem_ctx, &io);
1356 CHECK_STATUS(status, NT_STATUS_OK);
1357 _h = io.out.file.handle;
1359 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1360 CHECK_VAL(io.out.durable_open, false);
1361 CHECK_VAL(io.out.durable_open_v2, true);
1362 CHECK_VAL(io.out.persistent_open, false);
1363 CHECK_VAL(io.out.timeout, io.in.timeout);
1364 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1365 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1366 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1368 /* disconnect, reconnect and then do durable reopen */
1371 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1372 torture_warning(tctx, "couldn't reconnect, bailing\n");
1377 /* a few failure tests: */
1380 * several attempts without lease attached:
1381 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1382 * irrespective of file name provided
1387 io.in.durable_handle_v2 = h;
1388 status = smb2_create(tree, mem_ctx, &io);
1389 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1392 io.in.fname = "__non_existing_fname__";
1393 io.in.durable_handle_v2 = h;
1394 status = smb2_create(tree, mem_ctx, &io);
1395 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1398 io.in.fname = fname;
1399 io.in.durable_handle_v2 = h;
1400 status = smb2_create(tree, mem_ctx, &io);
1401 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1404 * attempt with lease provided, but
1405 * with a changed lease key. => fails
1408 io.in.fname = fname;
1409 io.in.durable_open_v2 = false;
1410 io.in.durable_handle_v2 = h;
1411 io.in.create_guid = create_guid;
1412 io.in.lease_request_v2 = &ls;
1413 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1414 /* a wrong lease key lets the request fail */
1415 ls.lease_key.data[0]++;
1417 status = smb2_create(tree, mem_ctx, &io);
1418 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1420 /* restore the correct lease key */
1421 ls.lease_key.data[0]--;
1425 * this last failing attempt is almost correct:
1426 * only problem is: we use the wrong filename...
1427 * Note that this gives INVALID_PARAMETER.
1428 * This is different from oplocks!
1431 io.in.fname = "__non_existing_fname__";
1432 io.in.durable_open_v2 = false;
1433 io.in.durable_handle_v2 = h;
1434 io.in.create_guid = create_guid;
1435 io.in.lease_request_v2 = &ls;
1436 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1438 status = smb2_create(tree, mem_ctx, &io);
1439 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1442 * Now for a succeeding reconnect:
1446 io.in.fname = fname;
1447 io.in.durable_open_v2 = false;
1448 io.in.durable_handle_v2 = h;
1449 io.in.create_guid = create_guid;
1450 io.in.lease_request_v2 = &ls;
1451 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1453 /* the requested lease state is irrelevant */
1454 ls.lease_state = smb2_util_lease_state("");
1458 status = smb2_create(tree, mem_ctx, &io);
1459 CHECK_STATUS(status, NT_STATUS_OK);
1461 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1462 CHECK_VAL(io.out.durable_open, false);
1463 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1464 CHECK_VAL(io.out.persistent_open, false);
1465 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1466 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1467 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1468 CHECK_VAL(io.out.lease_response_v2.lease_state,
1469 smb2_util_lease_state("RWH"));
1470 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1471 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1472 _h = io.out.file.handle;
1475 /* disconnect one more time */
1478 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1479 torture_warning(tctx, "couldn't reconnect, bailing\n");
1485 * demonstrate that various parameters are ignored
1491 * These are completely ignored by the server
1493 io.in.security_flags = 0x78;
1494 io.in.oplock_level = 0x78;
1495 io.in.impersonation_level = 0x12345678;
1496 io.in.create_flags = 0x12345678;
1497 io.in.reserved = 0x12345678;
1498 io.in.desired_access = 0x12345678;
1499 io.in.file_attributes = 0x12345678;
1500 io.in.share_access = 0x12345678;
1501 io.in.create_disposition = 0x12345678;
1502 io.in.create_options = 0x12345678;
1503 io.in.fname = "__non_existing_fname__";
1506 * only these are checked:
1508 * - io.in.durable_handle_v2,
1509 * - io.in.create_guid
1510 * - io.in.lease_request_v2->lease_key
1513 io.in.fname = fname;
1514 io.in.durable_open_v2 = false;
1515 io.in.durable_handle_v2 = h;
1516 io.in.create_guid = create_guid;
1517 io.in.lease_request_v2 = &ls;
1519 /* the requested lease state is irrelevant */
1520 ls.lease_state = smb2_util_lease_state("");
1524 status = smb2_create(tree, mem_ctx, &io);
1525 CHECK_STATUS(status, NT_STATUS_OK);
1526 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1527 CHECK_VAL(io.out.durable_open, false);
1528 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1529 CHECK_VAL(io.out.persistent_open, false);
1530 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1531 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1532 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1533 CHECK_VAL(io.out.lease_response_v2.lease_state,
1534 smb2_util_lease_state("RWH"));
1535 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1536 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1538 _h = io.out.file.handle;
1543 smb2_util_close(tree, *h);
1546 smb2_util_unlink(tree, fname);
1550 talloc_free(mem_ctx);
1556 * Test durable request / reconnect with AppInstanceId
1558 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1559 struct smb2_tree *tree1,
1560 struct smb2_tree *tree2)
1563 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1565 struct smb2_handle _h1, _h2;
1566 struct smb2_handle *h1 = NULL, *h2 = NULL;
1567 struct smb2_create io1, io2;
1569 struct GUID create_guid_1 = GUID_random();
1570 struct GUID create_guid_2 = GUID_random();
1571 struct GUID app_instance_id = GUID_random();
1573 /* Choose a random name in case the state is left a little funky. */
1574 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1575 generate_random_str(tctx, 8));
1577 smb2_util_unlink(tree1, fname);
1579 ZERO_STRUCT(break_info);
1580 tree1->session->transport->oplock.handler = torture_oplock_handler;
1581 tree1->session->transport->oplock.private_data = tree1;
1583 smb2_oplock_create_share(&io1, fname,
1584 smb2_util_share_access(""),
1585 smb2_util_oplock_level("b"));
1586 io1.in.durable_open = false;
1587 io1.in.durable_open_v2 = true;
1588 io1.in.persistent_open = false;
1589 io1.in.create_guid = create_guid_1;
1590 io1.in.app_instance_id = &app_instance_id;
1591 io1.in.timeout = UINT32_MAX;
1593 status = smb2_create(tree1, mem_ctx, &io1);
1594 CHECK_STATUS(status, NT_STATUS_OK);
1595 _h1 = io1.out.file.handle;
1597 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1598 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1599 CHECK_VAL(io1.out.durable_open, false);
1600 CHECK_VAL(io1.out.durable_open_v2, true);
1601 CHECK_VAL(io1.out.persistent_open, false);
1602 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1605 * try to open the file as durable from a second tree with
1606 * a different create guid but the same app_instance_id
1607 * while the first handle is still open.
1610 smb2_oplock_create_share(&io2, fname,
1611 smb2_util_share_access(""),
1612 smb2_util_oplock_level("b"));
1613 io2.in.durable_open = false;
1614 io2.in.durable_open_v2 = true;
1615 io2.in.persistent_open = false;
1616 io2.in.create_guid = create_guid_2;
1617 io2.in.app_instance_id = &app_instance_id;
1618 io2.in.timeout = UINT32_MAX;
1620 status = smb2_create(tree2, mem_ctx, &io2);
1621 CHECK_STATUS(status, NT_STATUS_OK);
1622 _h2 = io2.out.file.handle;
1624 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1625 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1626 CHECK_VAL(io2.out.durable_open, false);
1627 CHECK_VAL(io2.out.durable_open_v2, true);
1628 CHECK_VAL(io2.out.persistent_open, false);
1629 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1631 CHECK_VAL(break_info.count, 0);
1633 status = smb2_util_close(tree1, *h1);
1634 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1639 smb2_util_close(tree1, *h1);
1642 smb2_util_close(tree2, *h2);
1645 smb2_util_unlink(tree2, fname);
1650 talloc_free(mem_ctx);
1657 * basic persistent open test.
1659 * This test tests durable open with all possible oplock types.
1662 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1664 { "", "", true, true },
1665 { "", "R", true, true },
1666 { "", "W", true, true },
1667 { "", "D", true, true },
1668 { "", "RD", true, true },
1669 { "", "RW", true, true },
1670 { "", "WD", true, true },
1671 { "", "RWD", true, true },
1673 { "s", "", true, true },
1674 { "s", "R", true, true },
1675 { "s", "W", true, true },
1676 { "s", "D", true, true },
1677 { "s", "RD", true, true },
1678 { "s", "RW", true, true },
1679 { "s", "WD", true, true },
1680 { "s", "RWD", true, true },
1682 { "x", "", true, true },
1683 { "x", "R", true, true },
1684 { "x", "W", true, true },
1685 { "x", "D", true, true },
1686 { "x", "RD", true, true },
1687 { "x", "RW", true, true },
1688 { "x", "WD", true, true },
1689 { "x", "RWD", true, true },
1691 { "b", "", true, true },
1692 { "b", "R", true, true },
1693 { "b", "W", true, true },
1694 { "b", "D", true, true },
1695 { "b", "RD", true, true },
1696 { "b", "RW", true, true },
1697 { "b", "WD", true, true },
1698 { "b", "RWD", true, true },
1701 bool test_persistent_open_oplock(struct torture_context *tctx,
1702 struct smb2_tree *tree)
1706 uint32_t share_capabilities;
1707 bool share_is_ca = false;
1708 struct durable_open_vs_oplock *table;
1710 /* Choose a random name in case the state is left a little funky. */
1711 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1713 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1714 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1717 table = persistent_open_oplock_ca_table;
1719 table = durable_open_vs_oplock_table;
1722 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1723 true, /* request_persistent */
1725 NUM_OPLOCK_OPEN_TESTS);
1733 * basic persistent handle open test.
1734 * persistent state should only be granted when requested
1735 * along with a batch oplock or a handle lease.
1737 * This test tests persistent open with all valid lease types.
1740 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1742 { "", "", true, true },
1743 { "", "R", true, true },
1744 { "", "W", true, true },
1745 { "", "D", true, true },
1746 { "", "RW", true, true },
1747 { "", "RD", true, true },
1748 { "", "WD", true, true },
1749 { "", "RWD", true, true },
1751 { "R", "", true, true },
1752 { "R", "R", true, true },
1753 { "R", "W", true, true },
1754 { "R", "D", true, true },
1755 { "R", "RW", true, true },
1756 { "R", "RD", true, true },
1757 { "R", "DW", true, true },
1758 { "R", "RWD", true, true },
1760 { "RW", "", true, true },
1761 { "RW", "R", true, true },
1762 { "RW", "W", true, true },
1763 { "RW", "D", true, true },
1764 { "RW", "RW", true, true },
1765 { "RW", "RD", true, true },
1766 { "RW", "WD", true, true },
1767 { "RW", "RWD", true, true },
1769 { "RH", "", true, true },
1770 { "RH", "R", true, true },
1771 { "RH", "W", true, true },
1772 { "RH", "D", true, true },
1773 { "RH", "RW", true, true },
1774 { "RH", "RD", true, true },
1775 { "RH", "WD", true, true },
1776 { "RH", "RWD", true, true },
1778 { "RHW", "", true, true },
1779 { "RHW", "R", true, true },
1780 { "RHW", "W", true, true },
1781 { "RHW", "D", true, true },
1782 { "RHW", "RW", true, true },
1783 { "RHW", "RD", true, true },
1784 { "RHW", "WD", true, true },
1785 { "RHW", "RWD", true, true },
1788 bool test_persistent_open_lease(struct torture_context *tctx,
1789 struct smb2_tree *tree)
1794 uint32_t share_capabilities;
1796 struct durable_open_vs_lease *table;
1798 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1799 if (!(caps & SMB2_CAP_LEASING)) {
1800 torture_skip(tctx, "leases are not supported");
1803 /* Choose a random name in case the state is left a little funky. */
1804 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1806 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1807 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1810 table = persistent_open_lease_ca_table;
1812 table = durable_open_vs_lease_table;
1815 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1816 true, /* request_persistent */
1818 NUM_LEASE_OPEN_TESTS);
1825 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1827 struct torture_suite *suite =
1828 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1830 torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
1831 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1832 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1833 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1834 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
1835 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1836 torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
1837 torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
1838 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1839 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
1840 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1841 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1842 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1844 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");