s3-utils: Fix scanf format in smbacls.
[Samba.git] / source4 / torture / smb2 / durable_v2_open.c
blob8f221ecf364079cde0f38a3a851227d6586e0bec
1 /*
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/>.
22 #include "includes.h"
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); \
34 ret = false; \
35 }} while (0)
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)); \
41 ret = false; \
42 goto done; \
43 }} while (0)
45 #define CHECK_CREATED(__io, __created, __attribute) \
46 do { \
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); \
52 } while(0)
54 static struct {
55 int count;
56 struct smb2_close cl;
57 } break_info;
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,
68 uint8_t level,
69 void *private_data)
71 struct smb2_tree *tree = private_data;
72 struct smb2_request *req;
74 break_info.count++;
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;
82 return true;
85 /**
86 * testing various create blob combinations.
88 bool test_durable_v2_open_create_blob(struct torture_context *tctx,
89 struct smb2_tree *tree)
91 NTSTATUS status;
92 TALLOC_CTX *mem_ctx = talloc_new(tctx);
93 char fname[256];
94 struct smb2_handle _h;
95 struct smb2_handle *h = NULL;
96 struct smb2_create io;
97 struct GUID create_guid = GUID_random();
98 bool ret = true;
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;
121 h = &_h;
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);
129 /* disconnect */
130 TALLOC_FREE(tree);
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");
135 ret = false;
136 goto done;
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
145 ZERO_STRUCT(io);
146 io.in.fname = fname;
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);
153 ZERO_STRUCT(io);
154 io.in.fname = fname;
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);
161 ZERO_STRUCT(io);
162 io.in.fname = fname;
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);
169 ZERO_STRUCT(io);
170 io.in.fname = fname;
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);
177 done:
178 if (h != NULL) {
179 smb2_util_close(tree, *h);
182 smb2_util_unlink(tree, fname);
184 talloc_free(tree);
186 talloc_free(mem_ctx);
188 return ret;
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 {
201 const char *level;
202 const char *share_mode;
203 bool durable;
204 bool persistent;
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,
251 const char *fname,
252 bool request_persistent,
253 struct durable_open_vs_oplock test)
255 NTSTATUS status;
256 TALLOC_CTX *mem_ctx = talloc_new(tctx);
257 struct smb2_handle _h;
258 struct smb2_handle *h = NULL;
259 bool ret = true;
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;
275 h = &_h;
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));
282 done:
283 if (h != NULL) {
284 smb2_util_close(tree, *h);
286 smb2_util_unlink(tree, fname);
287 talloc_free(mem_ctx);
289 return ret;
292 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
293 struct smb2_tree *tree,
294 const char *fname,
295 bool request_persistent,
296 struct durable_open_vs_oplock *table,
297 uint8_t num_tests)
299 bool ret = true;
300 uint8_t i;
302 smb2_util_unlink(tree, fname);
304 for (i = 0; i < num_tests; i++) {
305 ret = test_one_durable_v2_open_oplock(tctx,
306 tree,
307 fname,
308 request_persistent,
309 table[i]);
310 if (ret == false) {
311 goto done;
315 done:
316 smb2_util_unlink(tree, fname);
318 return ret;
321 bool test_durable_v2_open_oplock(struct torture_context *tctx,
322 struct smb2_tree *tree)
324 bool ret;
325 char fname[256];
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);
336 talloc_free(tree);
338 return ret;
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 {
350 const char *type;
351 const char *share_mode;
352 bool durable;
353 bool persistent;
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,
408 const char *fname,
409 bool request_persistent,
410 struct durable_open_vs_lease test)
412 NTSTATUS status;
413 TALLOC_CTX *mem_ctx = talloc_new(tctx);
414 struct smb2_handle _h;
415 struct smb2_handle *h = NULL;
416 bool ret = true;
417 struct smb2_create io;
418 struct smb2_lease ls;
419 uint64_t lease;
421 smb2_util_unlink(tree, fname);
423 lease = random();
425 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
426 smb2_util_share_access(test.share_mode),
427 lease,
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;
437 h = &_h;
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));
447 done:
448 if (h != NULL) {
449 smb2_util_close(tree, *h);
451 smb2_util_unlink(tree, fname);
452 talloc_free(mem_ctx);
454 return ret;
457 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
458 struct smb2_tree *tree,
459 const char *fname,
460 bool request_persistent,
461 struct durable_open_vs_lease *table,
462 uint8_t num_tests)
464 bool ret = true;
465 uint8_t i;
467 smb2_util_unlink(tree, fname);
469 for (i = 0; i < num_tests; i++) {
470 ret = test_one_durable_v2_open_lease(tctx,
471 tree,
472 fname,
473 request_persistent,
474 table[i]);
475 if (ret == false) {
476 goto done;
480 done:
481 smb2_util_unlink(tree, fname);
483 return ret;
486 bool test_durable_v2_open_lease(struct torture_context *tctx,
487 struct smb2_tree *tree)
489 char fname[256];
490 bool ret = true;
491 uint32_t caps;
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);
506 talloc_free(tree);
507 return ret;
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)
518 NTSTATUS status;
519 TALLOC_CTX *mem_ctx = talloc_new(tctx);
520 char fname[256];
521 struct smb2_handle _h;
522 struct smb2_handle *h = NULL;
523 struct smb2_create io;
524 struct GUID create_guid = GUID_random();
525 bool ret = true;
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;
545 h = &_h;
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 */
554 ZERO_STRUCT(io);
555 io.in.fname = "";
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);
561 done:
562 if (h != NULL) {
563 smb2_util_close(tree, *h);
566 smb2_util_unlink(tree, fname);
568 talloc_free(tree);
570 talloc_free(mem_ctx);
572 return ret;
576 * basic test for doing a durable open
577 * tcp disconnect, reconnect, do a durable reopen (succeeds)
579 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
580 struct smb2_tree *tree)
582 NTSTATUS status;
583 TALLOC_CTX *mem_ctx = talloc_new(tctx);
584 char fname[256];
585 struct smb2_handle _h;
586 struct smb2_handle *h = NULL;
587 struct smb2_create io;
588 struct GUID create_guid = GUID_random();
589 struct GUID create_guid_invalid = GUID_random();
590 bool ret = true;
592 /* Choose a random name in case the state is left a little funky. */
593 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
594 generate_random_str(tctx, 8));
596 smb2_util_unlink(tree, fname);
598 smb2_oplock_create_share(&io, fname,
599 smb2_util_share_access(""),
600 smb2_util_oplock_level("b"));
601 io.in.durable_open = false;
602 io.in.durable_open_v2 = true;
603 io.in.persistent_open = false;
604 io.in.create_guid = create_guid;
605 io.in.timeout = UINT32_MAX;
607 status = smb2_create(tree, mem_ctx, &io);
608 CHECK_STATUS(status, NT_STATUS_OK);
609 _h = io.out.file.handle;
610 h = &_h;
611 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
612 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
613 CHECK_VAL(io.out.durable_open, false);
614 CHECK_VAL(io.out.durable_open_v2, true);
615 CHECK_VAL(io.out.persistent_open, false);
616 CHECK_VAL(io.out.timeout, io.in.timeout);
618 /* disconnect, leaving the durable open */
619 TALLOC_FREE(tree);
621 if (!torture_smb2_connection(tctx, &tree)) {
622 torture_warning(tctx, "couldn't reconnect, bailing\n");
623 ret = false;
624 goto done;
628 * first a few failure cases
631 ZERO_STRUCT(io);
632 io.in.fname = "";
633 io.in.durable_handle_v2 = h;
634 status = smb2_create(tree, mem_ctx, &io);
635 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
637 ZERO_STRUCT(io);
638 io.in.fname = "__non_existing_fname__";
639 io.in.durable_handle_v2 = h;
640 status = smb2_create(tree, mem_ctx, &io);
641 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
643 ZERO_STRUCT(io);
644 io.in.fname = fname;
645 io.in.durable_handle_v2 = h;
646 status = smb2_create(tree, mem_ctx, &io);
647 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
649 /* a non-zero but non-matching create_guid does not change it: */
650 ZERO_STRUCT(io);
651 io.in.fname = fname;
652 io.in.durable_handle_v2 = h;
653 io.in.create_guid = create_guid_invalid;
654 status = smb2_create(tree, mem_ctx, &io);
655 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
658 * now success:
659 * The important difference is that the create_guid is provided.
661 ZERO_STRUCT(io);
662 io.in.fname = fname;
663 io.in.durable_open_v2 = false;
664 io.in.durable_handle_v2 = h;
665 io.in.create_guid = create_guid;
666 h = NULL;
668 status = smb2_create(tree, mem_ctx, &io);
669 CHECK_STATUS(status, NT_STATUS_OK);
670 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
671 CHECK_VAL(io.out.durable_open, false);
672 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
673 CHECK_VAL(io.out.persistent_open, false);
674 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
675 _h = io.out.file.handle;
676 h = &_h;
678 /* disconnect one more time */
679 TALLOC_FREE(tree);
681 if (!torture_smb2_connection(tctx, &tree)) {
682 torture_warning(tctx, "couldn't reconnect, bailing\n");
683 ret = false;
684 goto done;
687 ZERO_STRUCT(io);
688 /* These are completely ignored by the server */
689 io.in.security_flags = 0x78;
690 io.in.oplock_level = 0x78;
691 io.in.impersonation_level = 0x12345678;
692 io.in.create_flags = 0x12345678;
693 io.in.reserved = 0x12345678;
694 io.in.desired_access = 0x12345678;
695 io.in.file_attributes = 0x12345678;
696 io.in.share_access = 0x12345678;
697 io.in.create_disposition = 0x12345678;
698 io.in.create_options = 0x12345678;
699 io.in.fname = "__non_existing_fname__";
702 * only io.in.durable_handle_v2 and
703 * io.in.create_guid are checked
705 io.in.durable_open_v2 = false;
706 io.in.durable_handle_v2 = h;
707 io.in.create_guid = create_guid;
708 h = NULL;
710 status = smb2_create(tree, mem_ctx, &io);
711 CHECK_STATUS(status, NT_STATUS_OK);
712 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
713 CHECK_VAL(io.out.durable_open, false);
714 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
715 CHECK_VAL(io.out.persistent_open, false);
716 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
717 _h = io.out.file.handle;
718 h = &_h;
720 done:
721 if (h != NULL) {
722 smb2_util_close(tree, *h);
725 smb2_util_unlink(tree, fname);
727 talloc_free(tree);
729 talloc_free(mem_ctx);
731 return ret;
735 * durable reconnect test:
736 * connect with v2, reconnect with v1
738 bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
739 struct smb2_tree *tree)
741 NTSTATUS status;
742 TALLOC_CTX *mem_ctx = talloc_new(tctx);
743 char fname[256];
744 struct smb2_handle _h;
745 struct smb2_handle *h = NULL;
746 struct smb2_create io;
747 struct GUID create_guid = GUID_random();
748 bool ret = true;
749 struct smbcli_options options;
751 options = tree->session->transport->options;
753 /* Choose a random name in case the state is left a little funky. */
754 snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
755 generate_random_str(tctx, 8));
757 smb2_util_unlink(tree, fname);
759 smb2_oplock_create_share(&io, fname,
760 smb2_util_share_access(""),
761 smb2_util_oplock_level("b"));
762 io.in.durable_open = false;
763 io.in.durable_open_v2 = true;
764 io.in.persistent_open = false;
765 io.in.create_guid = create_guid;
766 io.in.timeout = UINT32_MAX;
768 status = smb2_create(tree, mem_ctx, &io);
769 CHECK_STATUS(status, NT_STATUS_OK);
770 _h = io.out.file.handle;
771 h = &_h;
772 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
773 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
774 CHECK_VAL(io.out.durable_open, false);
775 CHECK_VAL(io.out.durable_open_v2, true);
776 CHECK_VAL(io.out.persistent_open, false);
777 CHECK_VAL(io.out.timeout, io.in.timeout);
779 /* disconnect, leaving the durable open */
780 TALLOC_FREE(tree);
782 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
783 torture_warning(tctx, "couldn't reconnect, bailing\n");
784 ret = false;
785 goto done;
788 ZERO_STRUCT(io);
789 io.in.fname = fname;
790 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
791 io.in.create_guid = GUID_zero(); /* but zero create GUID */
792 status = smb2_create(tree, mem_ctx, &io);
793 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
795 ZERO_STRUCT(io);
796 io.in.fname = fname;
797 io.in.durable_handle = h; /* durable v1 (!) reconnect */
798 h = NULL;
800 status = smb2_create(tree, mem_ctx, &io);
801 CHECK_STATUS(status, NT_STATUS_OK);
802 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
803 CHECK_VAL(io.out.durable_open, false);
804 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
805 CHECK_VAL(io.out.persistent_open, false);
806 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
807 _h = io.out.file.handle;
808 h = &_h;
810 done:
811 if (h != NULL) {
812 smb2_util_close(tree, *h);
815 smb2_util_unlink(tree, fname);
817 talloc_free(tree);
819 talloc_free(mem_ctx);
821 return ret;
824 * durable reconnect test:
825 * connect with v1, reconnect with v2 : fails (no create_guid...)
827 bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
828 struct smb2_tree *tree)
830 NTSTATUS status;
831 TALLOC_CTX *mem_ctx = talloc_new(tctx);
832 char fname[256];
833 struct smb2_handle _h;
834 struct smb2_handle *h = NULL;
835 struct smb2_create io;
836 struct GUID create_guid = GUID_random();
837 bool ret = true;
838 struct smbcli_options options;
840 options = tree->session->transport->options;
842 /* Choose a random name in case the state is left a little funky. */
843 snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
844 generate_random_str(tctx, 8));
846 smb2_util_unlink(tree, fname);
848 smb2_oplock_create_share(&io, fname,
849 smb2_util_share_access(""),
850 smb2_util_oplock_level("b"));
851 io.in.durable_open = true;
852 io.in.durable_open_v2 = false;
853 io.in.persistent_open = false;
854 io.in.create_guid = create_guid;
855 io.in.timeout = UINT32_MAX;
857 status = smb2_create(tree, mem_ctx, &io);
858 CHECK_STATUS(status, NT_STATUS_OK);
859 _h = io.out.file.handle;
860 h = &_h;
861 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
862 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
863 CHECK_VAL(io.out.durable_open, true);
864 CHECK_VAL(io.out.durable_open_v2, false);
865 CHECK_VAL(io.out.persistent_open, false);
866 CHECK_VAL(io.out.timeout, 0);
868 /* disconnect, leaving the durable open */
869 TALLOC_FREE(tree);
871 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
872 torture_warning(tctx, "couldn't reconnect, bailing\n");
873 ret = false;
874 goto done;
877 ZERO_STRUCT(io);
878 io.in.fname = fname;
879 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
880 io.in.create_guid = create_guid;
881 status = smb2_create(tree, mem_ctx, &io);
882 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
884 done:
885 if (h != NULL) {
886 smb2_util_close(tree, *h);
889 smb2_util_unlink(tree, fname);
891 talloc_free(tree);
893 talloc_free(mem_ctx);
895 return ret;
899 * lease variant of reopen2
900 * basic test for doing a durable open
901 * tcp disconnect, reconnect, do a durable reopen (succeeds)
903 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
904 struct smb2_tree *tree)
906 NTSTATUS status;
907 TALLOC_CTX *mem_ctx = talloc_new(tctx);
908 char fname[256];
909 struct smb2_handle _h;
910 struct smb2_handle *h = NULL;
911 struct smb2_create io;
912 struct GUID create_guid = GUID_random();
913 struct smb2_lease ls;
914 uint64_t lease_key;
915 bool ret = true;
916 struct smbcli_options options;
917 uint32_t caps;
919 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
920 if (!(caps & SMB2_CAP_LEASING)) {
921 torture_skip(tctx, "leases are not supported");
924 options = tree->session->transport->options;
926 /* Choose a random name in case the state is left a little funky. */
927 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
928 generate_random_str(tctx, 8));
930 smb2_util_unlink(tree, fname);
932 lease_key = random();
933 smb2_lease_create(&io, &ls, false /* dir */, fname,
934 lease_key, smb2_util_lease_state("RWH"));
935 io.in.durable_open = false;
936 io.in.durable_open_v2 = true;
937 io.in.persistent_open = false;
938 io.in.create_guid = create_guid;
939 io.in.timeout = UINT32_MAX;
941 status = smb2_create(tree, mem_ctx, &io);
942 CHECK_STATUS(status, NT_STATUS_OK);
943 _h = io.out.file.handle;
944 h = &_h;
945 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
946 CHECK_VAL(io.out.durable_open, false);
947 CHECK_VAL(io.out.durable_open_v2, true);
948 CHECK_VAL(io.out.persistent_open, false);
949 CHECK_VAL(io.out.timeout, io.in.timeout);
950 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
951 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
952 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
953 CHECK_VAL(io.out.lease_response.lease_state,
954 smb2_util_lease_state("RWH"));
955 CHECK_VAL(io.out.lease_response.lease_flags, 0);
956 CHECK_VAL(io.out.lease_response.lease_duration, 0);
958 /* disconnect, reconnect and then do durable reopen */
959 TALLOC_FREE(tree);
961 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
962 torture_warning(tctx, "couldn't reconnect, bailing\n");
963 ret = false;
964 goto done;
967 /* a few failure tests: */
970 * several attempts without lease attached:
971 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
972 * irrespective of file name provided
975 ZERO_STRUCT(io);
976 io.in.fname = "";
977 io.in.durable_handle_v2 = h;
978 status = smb2_create(tree, mem_ctx, &io);
979 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
981 ZERO_STRUCT(io);
982 io.in.fname = "__non_existing_fname__";
983 io.in.durable_handle_v2 = h;
984 status = smb2_create(tree, mem_ctx, &io);
985 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
987 ZERO_STRUCT(io);
988 io.in.fname = fname;
989 io.in.durable_handle_v2 = h;
990 status = smb2_create(tree, mem_ctx, &io);
991 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
994 * attempt with lease provided, but
995 * with a changed lease key. => fails
997 ZERO_STRUCT(io);
998 io.in.fname = fname;
999 io.in.durable_open_v2 = false;
1000 io.in.durable_handle_v2 = h;
1001 io.in.create_guid = create_guid;
1002 io.in.lease_request = &ls;
1003 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1004 /* a wrong lease key lets the request fail */
1005 ls.lease_key.data[0]++;
1007 status = smb2_create(tree, mem_ctx, &io);
1008 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1010 /* restore the correct lease key */
1011 ls.lease_key.data[0]--;
1014 * this last failing attempt is almost correct:
1015 * only problem is: we use the wrong filename...
1016 * Note that this gives INVALID_PARAMETER.
1017 * This is different from oplocks!
1019 ZERO_STRUCT(io);
1020 io.in.fname = "__non_existing_fname__";
1021 io.in.durable_open_v2 = false;
1022 io.in.durable_handle_v2 = h;
1023 io.in.create_guid = create_guid;
1024 io.in.lease_request = &ls;
1025 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1027 status = smb2_create(tree, mem_ctx, &io);
1028 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1031 * Now for a succeeding reconnect:
1034 ZERO_STRUCT(io);
1035 io.in.fname = fname;
1036 io.in.durable_open_v2 = false;
1037 io.in.durable_handle_v2 = h;
1038 io.in.create_guid = create_guid;
1039 io.in.lease_request = &ls;
1040 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1042 /* the requested lease state is irrelevant */
1043 ls.lease_state = smb2_util_lease_state("");
1045 h = NULL;
1047 status = smb2_create(tree, mem_ctx, &io);
1048 CHECK_STATUS(status, NT_STATUS_OK);
1050 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1051 CHECK_VAL(io.out.durable_open, false);
1052 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1053 CHECK_VAL(io.out.persistent_open, false);
1054 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1055 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1056 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1057 CHECK_VAL(io.out.lease_response.lease_state,
1058 smb2_util_lease_state("RWH"));
1059 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1060 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1061 _h = io.out.file.handle;
1062 h = &_h;
1064 /* disconnect one more time */
1065 TALLOC_FREE(tree);
1067 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1068 torture_warning(tctx, "couldn't reconnect, bailing\n");
1069 ret = false;
1070 goto done;
1074 * demonstrate that various parameters are ignored
1075 * in the reconnect
1078 ZERO_STRUCT(io);
1080 * These are completely ignored by the server
1082 io.in.security_flags = 0x78;
1083 io.in.oplock_level = 0x78;
1084 io.in.impersonation_level = 0x12345678;
1085 io.in.create_flags = 0x12345678;
1086 io.in.reserved = 0x12345678;
1087 io.in.desired_access = 0x12345678;
1088 io.in.file_attributes = 0x12345678;
1089 io.in.share_access = 0x12345678;
1090 io.in.create_disposition = 0x12345678;
1091 io.in.create_options = 0x12345678;
1094 * only these are checked:
1095 * - io.in.fname
1096 * - io.in.durable_handle_v2,
1097 * - io.in.create_guid
1098 * - io.in.lease_request->lease_key
1101 io.in.fname = fname;
1102 io.in.durable_open_v2 = false;
1103 io.in.durable_handle_v2 = h;
1104 io.in.create_guid = create_guid;
1105 io.in.lease_request = &ls;
1107 /* the requested lease state is irrelevant */
1108 ls.lease_state = smb2_util_lease_state("");
1110 h = NULL;
1112 status = smb2_create(tree, mem_ctx, &io);
1113 CHECK_STATUS(status, NT_STATUS_OK);
1115 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1116 CHECK_VAL(io.out.durable_open, false);
1117 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1118 CHECK_VAL(io.out.persistent_open, false);
1119 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1120 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1121 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1122 CHECK_VAL(io.out.lease_response.lease_state,
1123 smb2_util_lease_state("RWH"));
1124 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1125 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1127 _h = io.out.file.handle;
1128 h = &_h;
1130 done:
1131 if (h != NULL) {
1132 smb2_util_close(tree, *h);
1135 smb2_util_unlink(tree, fname);
1137 talloc_free(tree);
1139 talloc_free(mem_ctx);
1141 return ret;
1145 * lease_v2 variant of reopen2
1146 * basic test for doing a durable open
1147 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1149 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1150 struct smb2_tree *tree)
1152 NTSTATUS status;
1153 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1154 char fname[256];
1155 struct smb2_handle _h;
1156 struct smb2_handle *h = NULL;
1157 struct smb2_create io;
1158 struct GUID create_guid = GUID_random();
1159 struct smb2_lease ls;
1160 uint64_t lease_key;
1161 bool ret = true;
1162 struct smbcli_options options;
1163 uint32_t caps;
1165 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1166 if (!(caps & SMB2_CAP_LEASING)) {
1167 torture_skip(tctx, "leases are not supported");
1170 options = tree->session->transport->options;
1172 /* Choose a random name in case the state is left a little funky. */
1173 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1174 generate_random_str(tctx, 8));
1176 smb2_util_unlink(tree, fname);
1178 lease_key = random();
1179 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1180 lease_key, 0, /* parent lease key */
1181 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1182 io.in.durable_open = false;
1183 io.in.durable_open_v2 = true;
1184 io.in.persistent_open = false;
1185 io.in.create_guid = create_guid;
1186 io.in.timeout = UINT32_MAX;
1188 status = smb2_create(tree, mem_ctx, &io);
1189 CHECK_STATUS(status, NT_STATUS_OK);
1190 _h = io.out.file.handle;
1191 h = &_h;
1192 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1193 CHECK_VAL(io.out.durable_open, false);
1194 CHECK_VAL(io.out.durable_open_v2, true);
1195 CHECK_VAL(io.out.persistent_open, false);
1196 CHECK_VAL(io.out.timeout, io.in.timeout);
1197 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1198 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1199 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1201 /* disconnect, reconnect and then do durable reopen */
1202 TALLOC_FREE(tree);
1204 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1205 torture_warning(tctx, "couldn't reconnect, bailing\n");
1206 ret = false;
1207 goto done;
1210 /* a few failure tests: */
1213 * several attempts without lease attached:
1214 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1215 * irrespective of file name provided
1218 ZERO_STRUCT(io);
1219 io.in.fname = "";
1220 io.in.durable_handle_v2 = h;
1221 status = smb2_create(tree, mem_ctx, &io);
1222 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1224 ZERO_STRUCT(io);
1225 io.in.fname = "__non_existing_fname__";
1226 io.in.durable_handle_v2 = h;
1227 status = smb2_create(tree, mem_ctx, &io);
1228 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1230 ZERO_STRUCT(io);
1231 io.in.fname = fname;
1232 io.in.durable_handle_v2 = h;
1233 status = smb2_create(tree, mem_ctx, &io);
1234 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1237 * attempt with lease provided, but
1238 * with a changed lease key. => fails
1240 ZERO_STRUCT(io);
1241 io.in.fname = fname;
1242 io.in.durable_open_v2 = false;
1243 io.in.durable_handle_v2 = h;
1244 io.in.create_guid = create_guid;
1245 io.in.lease_request_v2 = &ls;
1246 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1247 /* a wrong lease key lets the request fail */
1248 ls.lease_key.data[0]++;
1250 status = smb2_create(tree, mem_ctx, &io);
1251 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1253 /* restore the correct lease key */
1254 ls.lease_key.data[0]--;
1258 * this last failing attempt is almost correct:
1259 * only problem is: we use the wrong filename...
1260 * Note that this gives INVALID_PARAMETER.
1261 * This is different from oplocks!
1263 ZERO_STRUCT(io);
1264 io.in.fname = "__non_existing_fname__";
1265 io.in.durable_open_v2 = false;
1266 io.in.durable_handle_v2 = h;
1267 io.in.create_guid = create_guid;
1268 io.in.lease_request_v2 = &ls;
1269 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1271 status = smb2_create(tree, mem_ctx, &io);
1272 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1275 * Now for a succeeding reconnect:
1278 ZERO_STRUCT(io);
1279 io.in.fname = fname;
1280 io.in.durable_open_v2 = false;
1281 io.in.durable_handle_v2 = h;
1282 io.in.create_guid = create_guid;
1283 io.in.lease_request_v2 = &ls;
1284 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1286 /* the requested lease state is irrelevant */
1287 ls.lease_state = smb2_util_lease_state("");
1289 h = NULL;
1291 status = smb2_create(tree, mem_ctx, &io);
1292 CHECK_STATUS(status, NT_STATUS_OK);
1294 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1295 CHECK_VAL(io.out.durable_open, false);
1296 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1297 CHECK_VAL(io.out.persistent_open, false);
1298 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1299 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1300 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1301 CHECK_VAL(io.out.lease_response_v2.lease_state,
1302 smb2_util_lease_state("RWH"));
1303 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1304 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1305 _h = io.out.file.handle;
1306 h = &_h;
1308 /* disconnect one more time */
1309 TALLOC_FREE(tree);
1311 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1312 torture_warning(tctx, "couldn't reconnect, bailing\n");
1313 ret = false;
1314 goto done;
1318 * demonstrate that various parameters are ignored
1319 * in the reconnect
1322 ZERO_STRUCT(io);
1324 * These are completely ignored by the server
1326 io.in.security_flags = 0x78;
1327 io.in.oplock_level = 0x78;
1328 io.in.impersonation_level = 0x12345678;
1329 io.in.create_flags = 0x12345678;
1330 io.in.reserved = 0x12345678;
1331 io.in.desired_access = 0x12345678;
1332 io.in.file_attributes = 0x12345678;
1333 io.in.share_access = 0x12345678;
1334 io.in.create_disposition = 0x12345678;
1335 io.in.create_options = 0x12345678;
1336 io.in.fname = "__non_existing_fname__";
1339 * only these are checked:
1340 * - io.in.fname
1341 * - io.in.durable_handle_v2,
1342 * - io.in.create_guid
1343 * - io.in.lease_request_v2->lease_key
1346 io.in.fname = fname;
1347 io.in.durable_open_v2 = false;
1348 io.in.durable_handle_v2 = h;
1349 io.in.create_guid = create_guid;
1350 io.in.lease_request_v2 = &ls;
1352 /* the requested lease state is irrelevant */
1353 ls.lease_state = smb2_util_lease_state("");
1355 h = NULL;
1357 status = smb2_create(tree, mem_ctx, &io);
1358 CHECK_STATUS(status, NT_STATUS_OK);
1359 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1360 CHECK_VAL(io.out.durable_open, false);
1361 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1362 CHECK_VAL(io.out.persistent_open, false);
1363 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1364 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1365 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1366 CHECK_VAL(io.out.lease_response_v2.lease_state,
1367 smb2_util_lease_state("RWH"));
1368 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1369 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1371 _h = io.out.file.handle;
1372 h = &_h;
1374 done:
1375 if (h != NULL) {
1376 smb2_util_close(tree, *h);
1379 smb2_util_unlink(tree, fname);
1381 talloc_free(tree);
1383 talloc_free(mem_ctx);
1385 return ret;
1389 * Test durable request / reconnect with AppInstanceId
1391 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1392 struct smb2_tree *tree1,
1393 struct smb2_tree *tree2)
1395 NTSTATUS status;
1396 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1397 char fname[256];
1398 struct smb2_handle _h1, _h2;
1399 struct smb2_handle *h1 = NULL, *h2 = NULL;
1400 struct smb2_create io1, io2;
1401 bool ret = true;
1402 struct GUID create_guid_1 = GUID_random();
1403 struct GUID create_guid_2 = GUID_random();
1404 struct GUID app_instance_id = GUID_random();
1406 /* Choose a random name in case the state is left a little funky. */
1407 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1408 generate_random_str(tctx, 8));
1410 smb2_util_unlink(tree1, fname);
1412 ZERO_STRUCT(break_info);
1413 tree1->session->transport->oplock.handler = torture_oplock_handler;
1414 tree1->session->transport->oplock.private_data = tree1;
1416 smb2_oplock_create_share(&io1, fname,
1417 smb2_util_share_access(""),
1418 smb2_util_oplock_level("b"));
1419 io1.in.durable_open = false;
1420 io1.in.durable_open_v2 = true;
1421 io1.in.persistent_open = false;
1422 io1.in.create_guid = create_guid_1;
1423 io1.in.app_instance_id = &app_instance_id;
1424 io1.in.timeout = UINT32_MAX;
1426 status = smb2_create(tree1, mem_ctx, &io1);
1427 CHECK_STATUS(status, NT_STATUS_OK);
1428 _h1 = io1.out.file.handle;
1429 h1 = &_h1;
1430 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1431 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1432 CHECK_VAL(io1.out.durable_open, false);
1433 CHECK_VAL(io1.out.durable_open_v2, true);
1434 CHECK_VAL(io1.out.persistent_open, false);
1435 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1438 * try to open the file as durable from a second tree with
1439 * a different create guid but the same app_instance_id
1440 * while the first handle is still open.
1443 smb2_oplock_create_share(&io2, fname,
1444 smb2_util_share_access(""),
1445 smb2_util_oplock_level("b"));
1446 io2.in.durable_open = false;
1447 io2.in.durable_open_v2 = true;
1448 io2.in.persistent_open = false;
1449 io2.in.create_guid = create_guid_2;
1450 io2.in.app_instance_id = &app_instance_id;
1451 io2.in.timeout = UINT32_MAX;
1453 status = smb2_create(tree2, mem_ctx, &io2);
1454 CHECK_STATUS(status, NT_STATUS_OK);
1455 _h2 = io2.out.file.handle;
1456 h2 = &_h2;
1457 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1458 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1459 CHECK_VAL(io2.out.durable_open, false);
1460 CHECK_VAL(io2.out.durable_open_v2, true);
1461 CHECK_VAL(io2.out.persistent_open, false);
1462 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1464 CHECK_VAL(break_info.count, 0);
1466 status = smb2_util_close(tree1, *h1);
1467 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1468 h1 = NULL;
1470 done:
1471 if (h1 != NULL) {
1472 smb2_util_close(tree1, *h1);
1474 if (h2 != NULL) {
1475 smb2_util_close(tree2, *h2);
1478 smb2_util_unlink(tree2, fname);
1480 talloc_free(tree1);
1481 talloc_free(tree2);
1483 talloc_free(mem_ctx);
1485 return ret;
1490 * basic persistent open test.
1492 * This test tests durable open with all possible oplock types.
1495 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1497 { "", "", true, true },
1498 { "", "R", true, true },
1499 { "", "W", true, true },
1500 { "", "D", true, true },
1501 { "", "RD", true, true },
1502 { "", "RW", true, true },
1503 { "", "WD", true, true },
1504 { "", "RWD", true, true },
1506 { "s", "", true, true },
1507 { "s", "R", true, true },
1508 { "s", "W", true, true },
1509 { "s", "D", true, true },
1510 { "s", "RD", true, true },
1511 { "s", "RW", true, true },
1512 { "s", "WD", true, true },
1513 { "s", "RWD", true, true },
1515 { "x", "", true, true },
1516 { "x", "R", true, true },
1517 { "x", "W", true, true },
1518 { "x", "D", true, true },
1519 { "x", "RD", true, true },
1520 { "x", "RW", true, true },
1521 { "x", "WD", true, true },
1522 { "x", "RWD", true, true },
1524 { "b", "", true, true },
1525 { "b", "R", true, true },
1526 { "b", "W", true, true },
1527 { "b", "D", true, true },
1528 { "b", "RD", true, true },
1529 { "b", "RW", true, true },
1530 { "b", "WD", true, true },
1531 { "b", "RWD", true, true },
1534 bool test_persistent_open_oplock(struct torture_context *tctx,
1535 struct smb2_tree *tree)
1537 char fname[256];
1538 bool ret = true;
1539 uint32_t share_capabilities;
1540 bool share_is_ca = false;
1541 struct durable_open_vs_oplock *table;
1543 /* Choose a random name in case the state is left a little funky. */
1544 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1546 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1547 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1549 if (share_is_ca) {
1550 table = persistent_open_oplock_ca_table;
1551 } else {
1552 table = durable_open_vs_oplock_table;
1555 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1556 true, /* request_persistent */
1557 table,
1558 NUM_OPLOCK_OPEN_TESTS);
1560 talloc_free(tree);
1562 return ret;
1566 * basic persistent handle open test.
1567 * persistent state should only be granted when requested
1568 * along with a batch oplock or a handle lease.
1570 * This test tests persistent open with all valid lease types.
1573 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1575 { "", "", true, true },
1576 { "", "R", true, true },
1577 { "", "W", true, true },
1578 { "", "D", true, true },
1579 { "", "RW", true, true },
1580 { "", "RD", true, true },
1581 { "", "WD", true, true },
1582 { "", "RWD", true, true },
1584 { "R", "", true, true },
1585 { "R", "R", true, true },
1586 { "R", "W", true, true },
1587 { "R", "D", true, true },
1588 { "R", "RW", true, true },
1589 { "R", "RD", true, true },
1590 { "R", "DW", true, true },
1591 { "R", "RWD", true, true },
1593 { "RW", "", true, true },
1594 { "RW", "R", true, true },
1595 { "RW", "W", true, true },
1596 { "RW", "D", true, true },
1597 { "RW", "RW", true, true },
1598 { "RW", "RD", true, true },
1599 { "RW", "WD", true, true },
1600 { "RW", "RWD", true, true },
1602 { "RH", "", true, true },
1603 { "RH", "R", true, true },
1604 { "RH", "W", true, true },
1605 { "RH", "D", true, true },
1606 { "RH", "RW", true, true },
1607 { "RH", "RD", true, true },
1608 { "RH", "WD", true, true },
1609 { "RH", "RWD", true, true },
1611 { "RHW", "", true, true },
1612 { "RHW", "R", true, true },
1613 { "RHW", "W", true, true },
1614 { "RHW", "D", true, true },
1615 { "RHW", "RW", true, true },
1616 { "RHW", "RD", true, true },
1617 { "RHW", "WD", true, true },
1618 { "RHW", "RWD", true, true },
1621 bool test_persistent_open_lease(struct torture_context *tctx,
1622 struct smb2_tree *tree)
1624 char fname[256];
1625 bool ret = true;
1626 uint32_t caps;
1627 uint32_t share_capabilities;
1628 bool share_is_ca;
1629 struct durable_open_vs_lease *table;
1631 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1632 if (!(caps & SMB2_CAP_LEASING)) {
1633 torture_skip(tctx, "leases are not supported");
1636 /* Choose a random name in case the state is left a little funky. */
1637 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1639 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1640 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1642 if (share_is_ca) {
1643 table = persistent_open_lease_ca_table;
1644 } else {
1645 table = durable_open_vs_lease_table;
1648 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1649 true, /* request_persistent */
1650 table,
1651 NUM_LEASE_OPEN_TESTS);
1653 talloc_free(tree);
1655 return ret;
1658 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1660 struct torture_suite *suite =
1661 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1663 torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
1664 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1665 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1666 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1667 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1668 torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
1669 torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
1670 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1671 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
1672 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1673 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1674 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1676 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
1678 return suite;