s4:torture:smb2: add durable-v2-open.reopen2-lease
[Samba.git] / source4 / torture / smb2 / durable_v2_open.c
blob216097dcac9cd7aa9fdffe4df5fdace5d9f0647a
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 * basic durable_open test.
87 * durable state should only be granted when requested
88 * along with a batch oplock or a handle lease.
90 * This test tests durable open with all possible oplock types.
93 struct durable_open_vs_oplock {
94 const char *level;
95 const char *share_mode;
96 bool durable;
97 bool persistent;
100 #define NUM_OPLOCK_TYPES 4
101 #define NUM_SHARE_MODES 8
102 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
103 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
105 { "", "", false, false },
106 { "", "R", false, false },
107 { "", "W", false, false },
108 { "", "D", false, false },
109 { "", "RD", false, false },
110 { "", "RW", false, false },
111 { "", "WD", false, false },
112 { "", "RWD", false, false },
114 { "s", "", false, false },
115 { "s", "R", false, false },
116 { "s", "W", false, false },
117 { "s", "D", false, false },
118 { "s", "RD", false, false },
119 { "s", "RW", false, false },
120 { "s", "WD", false, false },
121 { "s", "RWD", false, false },
123 { "x", "", false, false },
124 { "x", "R", false, false },
125 { "x", "W", false, false },
126 { "x", "D", false, false },
127 { "x", "RD", false, false },
128 { "x", "RW", false, false },
129 { "x", "WD", false, false },
130 { "x", "RWD", false, false },
132 { "b", "", true, false },
133 { "b", "R", true, false },
134 { "b", "W", true, false },
135 { "b", "D", true, false },
136 { "b", "RD", true, false },
137 { "b", "RW", true, false },
138 { "b", "WD", true, false },
139 { "b", "RWD", true, false },
142 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
143 struct smb2_tree *tree,
144 const char *fname,
145 bool request_persistent,
146 struct durable_open_vs_oplock test)
148 NTSTATUS status;
149 TALLOC_CTX *mem_ctx = talloc_new(tctx);
150 struct smb2_handle _h;
151 struct smb2_handle *h = NULL;
152 bool ret = true;
153 struct smb2_create io;
155 smb2_util_unlink(tree, fname);
157 smb2_oplock_create_share(&io, fname,
158 smb2_util_share_access(test.share_mode),
159 smb2_util_oplock_level(test.level));
160 io.in.durable_open = false;
161 io.in.durable_open_v2 = true;
162 io.in.persistent_open = request_persistent;
163 io.in.create_guid = GUID_random();
165 status = smb2_create(tree, mem_ctx, &io);
166 CHECK_STATUS(status, NT_STATUS_OK);
167 _h = io.out.file.handle;
168 h = &_h;
169 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
170 CHECK_VAL(io.out.durable_open, false);
171 CHECK_VAL(io.out.durable_open_v2, test.durable);
172 CHECK_VAL(io.out.persistent_open, test.persistent);
173 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
175 done:
176 if (h != NULL) {
177 smb2_util_close(tree, *h);
179 smb2_util_unlink(tree, fname);
180 talloc_free(mem_ctx);
182 return ret;
185 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
186 struct smb2_tree *tree,
187 const char *fname,
188 bool request_persistent,
189 struct durable_open_vs_oplock *table,
190 uint8_t num_tests)
192 bool ret = true;
193 uint8_t i;
195 smb2_util_unlink(tree, fname);
197 for (i = 0; i < num_tests; i++) {
198 ret = test_one_durable_v2_open_oplock(tctx,
199 tree,
200 fname,
201 request_persistent,
202 table[i]);
203 if (ret == false) {
204 goto done;
208 done:
209 smb2_util_unlink(tree, fname);
211 return ret;
214 bool test_durable_v2_open_oplock(struct torture_context *tctx,
215 struct smb2_tree *tree)
217 bool ret;
218 char fname[256];
220 /* Choose a random name in case the state is left a little funky. */
221 snprintf(fname, 256, "durable_open_oplock_%s.dat",
222 generate_random_str(tctx, 8));
224 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
225 false, /* request_persistent */
226 durable_open_vs_oplock_table,
227 NUM_OPLOCK_OPEN_TESTS);
229 talloc_free(tree);
231 return ret;
235 * basic durable handle open test.
236 * persistent state should only be granted when requested
237 * along with a batch oplock or a handle lease.
239 * This test tests persistent open with all valid lease types.
242 struct durable_open_vs_lease {
243 const char *type;
244 const char *share_mode;
245 bool durable;
246 bool persistent;
249 #define NUM_LEASE_TYPES 5
250 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
251 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
253 { "", "", false, false },
254 { "", "R", false, false },
255 { "", "W", false, false },
256 { "", "D", false, false },
257 { "", "RW", false, false },
258 { "", "RD", false, false },
259 { "", "WD", false, false },
260 { "", "RWD", false, false },
262 { "R", "", false, false },
263 { "R", "R", false, false },
264 { "R", "W", false, false },
265 { "R", "D", false, false },
266 { "R", "RW", false, false },
267 { "R", "RD", false, false },
268 { "R", "DW", false, false },
269 { "R", "RWD", false, false },
271 { "RW", "", false, false },
272 { "RW", "R", false, false },
273 { "RW", "W", false, false },
274 { "RW", "D", false, false },
275 { "RW", "RW", false, false },
276 { "RW", "RD", false, false },
277 { "RW", "WD", false, false },
278 { "RW", "RWD", false, false },
280 { "RH", "", true, false },
281 { "RH", "R", true, false },
282 { "RH", "W", true, false },
283 { "RH", "D", true, false },
284 { "RH", "RW", true, false },
285 { "RH", "RD", true, false },
286 { "RH", "WD", true, false },
287 { "RH", "RWD", true, false },
289 { "RHW", "", true, false },
290 { "RHW", "R", true, false },
291 { "RHW", "W", true, false },
292 { "RHW", "D", true, false },
293 { "RHW", "RW", true, false },
294 { "RHW", "RD", true, false },
295 { "RHW", "WD", true, false },
296 { "RHW", "RWD", true, false },
299 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
300 struct smb2_tree *tree,
301 const char *fname,
302 bool request_persistent,
303 struct durable_open_vs_lease test)
305 NTSTATUS status;
306 TALLOC_CTX *mem_ctx = talloc_new(tctx);
307 struct smb2_handle _h;
308 struct smb2_handle *h = NULL;
309 bool ret = true;
310 struct smb2_create io;
311 struct smb2_lease ls;
312 uint64_t lease;
314 smb2_util_unlink(tree, fname);
316 lease = random();
318 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
319 smb2_util_share_access(test.share_mode),
320 lease,
321 smb2_util_lease_state(test.type));
322 io.in.durable_open = false;
323 io.in.durable_open_v2 = true;
324 io.in.persistent_open = request_persistent;
325 io.in.create_guid = GUID_random();
327 status = smb2_create(tree, mem_ctx, &io);
328 CHECK_STATUS(status, NT_STATUS_OK);
329 _h = io.out.file.handle;
330 h = &_h;
331 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
332 CHECK_VAL(io.out.durable_open, false);
333 CHECK_VAL(io.out.durable_open_v2, test.durable);
334 CHECK_VAL(io.out.persistent_open, test.persistent);
335 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
336 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
337 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
338 CHECK_VAL(io.out.lease_response.lease_state,
339 smb2_util_lease_state(test.type));
340 done:
341 if (h != NULL) {
342 smb2_util_close(tree, *h);
344 smb2_util_unlink(tree, fname);
345 talloc_free(mem_ctx);
347 return ret;
350 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
351 struct smb2_tree *tree,
352 const char *fname,
353 bool request_persistent,
354 struct durable_open_vs_lease *table,
355 uint8_t num_tests)
357 bool ret = true;
358 uint8_t i;
360 smb2_util_unlink(tree, fname);
362 for (i = 0; i < num_tests; i++) {
363 ret = test_one_durable_v2_open_lease(tctx,
364 tree,
365 fname,
366 request_persistent,
367 table[i]);
368 if (ret == false) {
369 goto done;
373 done:
374 smb2_util_unlink(tree, fname);
376 return ret;
379 bool test_durable_v2_open_lease(struct torture_context *tctx,
380 struct smb2_tree *tree)
382 char fname[256];
383 bool ret = true;
384 uint32_t caps;
386 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
387 if (!(caps & SMB2_CAP_LEASING)) {
388 torture_skip(tctx, "leases are not supported");
391 /* Choose a random name in case the state is left a little funky. */
392 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
394 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
395 false, /* request_persistent */
396 durable_open_vs_lease_table,
397 NUM_LEASE_OPEN_TESTS);
399 talloc_free(tree);
400 return ret;
404 * basic test for doing a durable open
405 * and do a durable reopen on the same connection
406 * while the first open is still active (fails)
408 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
409 struct smb2_tree *tree)
411 NTSTATUS status;
412 TALLOC_CTX *mem_ctx = talloc_new(tctx);
413 char fname[256];
414 struct smb2_handle _h;
415 struct smb2_handle *h = NULL;
416 struct smb2_create io;
417 struct GUID create_guid = GUID_random();
418 bool ret = true;
420 /* Choose a random name in case the state is left a little funky. */
421 snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
422 generate_random_str(tctx, 8));
424 smb2_util_unlink(tree, fname);
426 smb2_oplock_create_share(&io, fname,
427 smb2_util_share_access(""),
428 smb2_util_oplock_level("b"));
429 io.in.durable_open = false;
430 io.in.durable_open_v2 = true;
431 io.in.persistent_open = false;
432 io.in.create_guid = create_guid;
433 io.in.timeout = UINT32_MAX;
435 status = smb2_create(tree, mem_ctx, &io);
436 CHECK_STATUS(status, NT_STATUS_OK);
437 _h = io.out.file.handle;
438 h = &_h;
439 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
440 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
441 CHECK_VAL(io.out.durable_open, false);
442 CHECK_VAL(io.out.durable_open_v2, true);
443 CHECK_VAL(io.out.persistent_open, false);
444 CHECK_VAL(io.out.timeout, io.in.timeout);
446 /* try a durable reconnect while the file is still open */
447 ZERO_STRUCT(io);
448 io.in.fname = "";
449 io.in.durable_handle_v2 = h;
450 io.in.create_guid = create_guid;
451 status = smb2_create(tree, mem_ctx, &io);
452 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
454 done:
455 if (h != NULL) {
456 smb2_util_close(tree, *h);
459 smb2_util_unlink(tree, fname);
461 talloc_free(tree);
463 talloc_free(mem_ctx);
465 return ret;
469 * basic test for doing a durable open
470 * tcp disconnect, reconnect, do a durable reopen (succeeds)
472 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
473 struct smb2_tree *tree)
475 NTSTATUS status;
476 TALLOC_CTX *mem_ctx = talloc_new(tctx);
477 char fname[256];
478 struct smb2_handle _h;
479 struct smb2_handle *h = NULL;
480 struct smb2_create io;
481 struct GUID create_guid = GUID_random();
482 bool ret = true;
484 /* Choose a random name in case the state is left a little funky. */
485 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
486 generate_random_str(tctx, 8));
488 smb2_util_unlink(tree, fname);
490 smb2_oplock_create_share(&io, fname,
491 smb2_util_share_access(""),
492 smb2_util_oplock_level("b"));
493 io.in.durable_open = false;
494 io.in.durable_open_v2 = true;
495 io.in.persistent_open = false;
496 io.in.create_guid = create_guid;
497 io.in.timeout = UINT32_MAX;
499 status = smb2_create(tree, mem_ctx, &io);
500 CHECK_STATUS(status, NT_STATUS_OK);
501 _h = io.out.file.handle;
502 h = &_h;
503 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
504 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
505 CHECK_VAL(io.out.durable_open, false);
506 CHECK_VAL(io.out.durable_open_v2, true);
507 CHECK_VAL(io.out.persistent_open, false);
508 CHECK_VAL(io.out.timeout, io.in.timeout);
510 /* disconnect, reconnect and then do durable reopen */
511 talloc_free(tree);
512 tree = NULL;
514 if (!torture_smb2_connection(tctx, &tree)) {
515 torture_warning(tctx, "couldn't reconnect, bailing\n");
516 ret = false;
517 goto done;
520 ZERO_STRUCT(io);
521 io.in.fname = "";
522 io.in.durable_handle_v2 = h;
523 status = smb2_create(tree, mem_ctx, &io);
524 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
526 ZERO_STRUCT(io);
527 io.in.fname = "__non_existing_fname__";
528 io.in.durable_handle_v2 = h;
529 status = smb2_create(tree, mem_ctx, &io);
530 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
532 ZERO_STRUCT(io);
533 io.in.fname = fname;
534 io.in.durable_handle_v2 = h;
535 status = smb2_create(tree, mem_ctx, &io);
536 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
538 ZERO_STRUCT(io);
540 * These are completely ignored by the server
542 io.in.security_flags = 0x78;
543 io.in.oplock_level = 0x78;
544 io.in.impersonation_level = 0x12345678;
545 io.in.create_flags = 0x12345678;
546 io.in.reserved = 0x12345678;
547 io.in.desired_access = 0x12345678;
548 io.in.file_attributes = 0x12345678;
549 io.in.share_access = 0x12345678;
550 io.in.create_disposition = 0x12345678;
551 io.in.create_options = 0x12345678;
552 io.in.fname = "__non_existing_fname__";
555 * only io.in.durable_handle_v2 and
556 * io.in.create_guid are checked
558 io.in.durable_open_v2 = false;
559 io.in.durable_handle_v2 = h;
560 io.in.create_guid = create_guid;
561 h = NULL;
563 status = smb2_create(tree, mem_ctx, &io);
564 CHECK_STATUS(status, NT_STATUS_OK);
565 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
566 CHECK_VAL(io.out.durable_open, false);
567 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
568 CHECK_VAL(io.out.persistent_open, false);
569 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
570 _h = io.out.file.handle;
571 h = &_h;
573 done:
574 if (h != NULL) {
575 smb2_util_close(tree, *h);
578 smb2_util_unlink(tree, fname);
580 talloc_free(tree);
582 talloc_free(mem_ctx);
584 return ret;
588 * lease variant of reopen2
589 * basic test for doing a durable open
590 * tcp disconnect, reconnect, do a durable reopen (succeeds)
592 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
593 struct smb2_tree *tree)
595 NTSTATUS status;
596 TALLOC_CTX *mem_ctx = talloc_new(tctx);
597 char fname[256];
598 struct smb2_handle _h;
599 struct smb2_handle *h = NULL;
600 struct smb2_create io;
601 struct GUID create_guid = GUID_random();
602 struct smb2_lease ls;
603 uint64_t lease_key;
604 bool ret = true;
605 struct smbcli_options options;
606 uint32_t caps;
608 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
609 if (!(caps & SMB2_CAP_LEASING)) {
610 torture_skip(tctx, "leases are not supported");
613 options = tree->session->transport->options;
615 /* Choose a random name in case the state is left a little funky. */
616 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
617 generate_random_str(tctx, 8));
619 smb2_util_unlink(tree, fname);
621 lease_key = random();
622 smb2_lease_create(&io, &ls, false /* dir */, fname,
623 lease_key, smb2_util_lease_state("RWH"));
624 io.in.durable_open = false;
625 io.in.durable_open_v2 = true;
626 io.in.persistent_open = false;
627 io.in.create_guid = create_guid;
628 io.in.timeout = UINT32_MAX;
630 status = smb2_create(tree, mem_ctx, &io);
631 CHECK_STATUS(status, NT_STATUS_OK);
632 _h = io.out.file.handle;
633 h = &_h;
634 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
635 CHECK_VAL(io.out.durable_open, false);
636 CHECK_VAL(io.out.durable_open_v2, true);
637 CHECK_VAL(io.out.persistent_open, false);
638 CHECK_VAL(io.out.timeout, io.in.timeout);
639 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
640 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
641 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
642 CHECK_VAL(io.out.lease_response.lease_state,
643 smb2_util_lease_state("RWH"));
644 CHECK_VAL(io.out.lease_response.lease_flags, 0);
645 CHECK_VAL(io.out.lease_response.lease_duration, 0);
647 /* disconnect, reconnect and then do durable reopen */
648 TALLOC_FREE(tree);
650 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
651 torture_warning(tctx, "couldn't reconnect, bailing\n");
652 ret = false;
653 goto done;
656 /* a few failure tests: */
659 * several attempts without lease attached:
660 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
661 * irrespective of file name provided
664 ZERO_STRUCT(io);
665 io.in.fname = "";
666 io.in.durable_handle_v2 = h;
667 status = smb2_create(tree, mem_ctx, &io);
668 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
670 ZERO_STRUCT(io);
671 io.in.fname = "__non_existing_fname__";
672 io.in.durable_handle_v2 = h;
673 status = smb2_create(tree, mem_ctx, &io);
674 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
676 ZERO_STRUCT(io);
677 io.in.fname = fname;
678 io.in.durable_handle_v2 = h;
679 status = smb2_create(tree, mem_ctx, &io);
680 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
683 * attempt with lease provided, but
684 * with a changed lease key. => fails
686 ZERO_STRUCT(io);
687 io.in.fname = fname;
688 io.in.durable_open_v2 = false;
689 io.in.durable_handle_v2 = h;
690 io.in.create_guid = create_guid;
691 io.in.lease_request = &ls;
692 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
693 /* a wrong lease key lets the request fail */
694 ls.lease_key.data[0]++;
696 status = smb2_create(tree, mem_ctx, &io);
697 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
699 /* restore the correct lease key */
700 ls.lease_key.data[0]--;
703 * this last failing attempt is almost correct:
704 * only problem is: we use the wrong filename...
705 * Note that this gives INVALID_PARAMETER.
706 * This is different from oplocks!
708 ZERO_STRUCT(io);
709 io.in.fname = "__non_existing_fname__";
710 io.in.durable_open_v2 = false;
711 io.in.durable_handle_v2 = h;
712 io.in.create_guid = create_guid;
713 io.in.lease_request = &ls;
714 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
716 status = smb2_create(tree, mem_ctx, &io);
717 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
720 * Now for a succeeding reconnect:
723 ZERO_STRUCT(io);
724 io.in.fname = fname;
725 io.in.durable_open_v2 = false;
726 io.in.durable_handle_v2 = h;
727 io.in.create_guid = create_guid;
728 io.in.lease_request = &ls;
729 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
731 /* the requested lease state is irrelevant */
732 ls.lease_state = smb2_util_lease_state("");
734 h = NULL;
736 status = smb2_create(tree, mem_ctx, &io);
737 CHECK_STATUS(status, NT_STATUS_OK);
739 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
740 CHECK_VAL(io.out.durable_open, false);
741 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
742 CHECK_VAL(io.out.persistent_open, false);
743 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
744 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
745 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
746 CHECK_VAL(io.out.lease_response.lease_state,
747 smb2_util_lease_state("RWH"));
748 CHECK_VAL(io.out.lease_response.lease_flags, 0);
749 CHECK_VAL(io.out.lease_response.lease_duration, 0);
750 _h = io.out.file.handle;
751 h = &_h;
753 /* disconnect one more time */
754 TALLOC_FREE(tree);
756 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
757 torture_warning(tctx, "couldn't reconnect, bailing\n");
758 ret = false;
759 goto done;
763 * demonstrate that various parameters are ignored
764 * in the reconnect
767 ZERO_STRUCT(io);
769 * These are completely ignored by the server
771 io.in.security_flags = 0x78;
772 io.in.oplock_level = 0x78;
773 io.in.impersonation_level = 0x12345678;
774 io.in.create_flags = 0x12345678;
775 io.in.reserved = 0x12345678;
776 io.in.desired_access = 0x12345678;
777 io.in.file_attributes = 0x12345678;
778 io.in.share_access = 0x12345678;
779 io.in.create_disposition = 0x12345678;
780 io.in.create_options = 0x12345678;
783 * only these are checked:
784 * - io.in.fname
785 * - io.in.durable_handle_v2,
786 * - io.in.create_guid
787 * - io.in.lease_request->lease_key
790 io.in.fname = fname;
791 io.in.durable_open_v2 = false;
792 io.in.durable_handle_v2 = h;
793 io.in.create_guid = create_guid;
794 io.in.lease_request = &ls;
796 /* the requested lease state is irrelevant */
797 ls.lease_state = smb2_util_lease_state("");
799 h = NULL;
801 status = smb2_create(tree, mem_ctx, &io);
802 CHECK_STATUS(status, NT_STATUS_OK);
804 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
805 CHECK_VAL(io.out.durable_open, false);
806 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
807 CHECK_VAL(io.out.persistent_open, false);
808 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
809 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
810 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
811 CHECK_VAL(io.out.lease_response.lease_state,
812 smb2_util_lease_state("RWH"));
813 CHECK_VAL(io.out.lease_response.lease_flags, 0);
814 CHECK_VAL(io.out.lease_response.lease_duration, 0);
816 _h = io.out.file.handle;
817 h = &_h;
819 done:
820 if (h != NULL) {
821 smb2_util_close(tree, *h);
824 smb2_util_unlink(tree, fname);
826 talloc_free(tree);
828 talloc_free(mem_ctx);
830 return ret;
834 * Test durable request / reconnect with AppInstanceId
836 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
837 struct smb2_tree *tree1,
838 struct smb2_tree *tree2)
840 NTSTATUS status;
841 TALLOC_CTX *mem_ctx = talloc_new(tctx);
842 char fname[256];
843 struct smb2_handle _h1, _h2;
844 struct smb2_handle *h1 = NULL, *h2 = NULL;
845 struct smb2_create io1, io2;
846 bool ret = true;
847 struct GUID create_guid_1 = GUID_random();
848 struct GUID create_guid_2 = GUID_random();
849 struct GUID app_instance_id = GUID_random();
851 /* Choose a random name in case the state is left a little funky. */
852 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
853 generate_random_str(tctx, 8));
855 smb2_util_unlink(tree1, fname);
857 ZERO_STRUCT(break_info);
858 tree1->session->transport->oplock.handler = torture_oplock_handler;
859 tree1->session->transport->oplock.private_data = tree1;
861 smb2_oplock_create_share(&io1, fname,
862 smb2_util_share_access(""),
863 smb2_util_oplock_level("b"));
864 io1.in.durable_open = false;
865 io1.in.durable_open_v2 = true;
866 io1.in.persistent_open = false;
867 io1.in.create_guid = create_guid_1;
868 io1.in.app_instance_id = &app_instance_id;
869 io1.in.timeout = UINT32_MAX;
871 status = smb2_create(tree1, mem_ctx, &io1);
872 CHECK_STATUS(status, NT_STATUS_OK);
873 _h1 = io1.out.file.handle;
874 h1 = &_h1;
875 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
876 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
877 CHECK_VAL(io1.out.durable_open, false);
878 CHECK_VAL(io1.out.durable_open_v2, true);
879 CHECK_VAL(io1.out.persistent_open, false);
880 CHECK_VAL(io1.out.timeout, io1.in.timeout);
883 * try to open the file as durable from a second tree with
884 * a different create guid but the same app_instance_id
885 * while the first handle is still open.
888 smb2_oplock_create_share(&io2, fname,
889 smb2_util_share_access(""),
890 smb2_util_oplock_level("b"));
891 io2.in.durable_open = false;
892 io2.in.durable_open_v2 = true;
893 io2.in.persistent_open = false;
894 io2.in.create_guid = create_guid_2;
895 io2.in.app_instance_id = &app_instance_id;
896 io2.in.timeout = UINT32_MAX;
898 status = smb2_create(tree2, mem_ctx, &io2);
899 CHECK_STATUS(status, NT_STATUS_OK);
900 _h2 = io2.out.file.handle;
901 h2 = &_h2;
902 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
903 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
904 CHECK_VAL(io2.out.durable_open, false);
905 CHECK_VAL(io2.out.durable_open_v2, true);
906 CHECK_VAL(io2.out.persistent_open, false);
907 CHECK_VAL(io2.out.timeout, io2.in.timeout);
909 CHECK_VAL(break_info.count, 0);
911 status = smb2_util_close(tree1, *h1);
912 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
913 h1 = NULL;
915 done:
916 if (h1 != NULL) {
917 smb2_util_close(tree1, *h1);
919 if (h2 != NULL) {
920 smb2_util_close(tree2, *h2);
923 smb2_util_unlink(tree2, fname);
925 talloc_free(tree1);
926 talloc_free(tree2);
928 talloc_free(mem_ctx);
930 return ret;
935 * basic persistent open test.
937 * This test tests durable open with all possible oplock types.
940 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
942 { "", "", true, true },
943 { "", "R", true, true },
944 { "", "W", true, true },
945 { "", "D", true, true },
946 { "", "RD", true, true },
947 { "", "RW", true, true },
948 { "", "WD", true, true },
949 { "", "RWD", true, true },
951 { "s", "", true, true },
952 { "s", "R", true, true },
953 { "s", "W", true, true },
954 { "s", "D", true, true },
955 { "s", "RD", true, true },
956 { "s", "RW", true, true },
957 { "s", "WD", true, true },
958 { "s", "RWD", true, true },
960 { "x", "", true, true },
961 { "x", "R", true, true },
962 { "x", "W", true, true },
963 { "x", "D", true, true },
964 { "x", "RD", true, true },
965 { "x", "RW", true, true },
966 { "x", "WD", true, true },
967 { "x", "RWD", true, true },
969 { "b", "", true, true },
970 { "b", "R", true, true },
971 { "b", "W", true, true },
972 { "b", "D", true, true },
973 { "b", "RD", true, true },
974 { "b", "RW", true, true },
975 { "b", "WD", true, true },
976 { "b", "RWD", true, true },
979 bool test_persistent_open_oplock(struct torture_context *tctx,
980 struct smb2_tree *tree)
982 char fname[256];
983 bool ret = true;
984 uint32_t share_capabilities;
985 bool share_is_ca = false;
986 struct durable_open_vs_oplock *table;
988 /* Choose a random name in case the state is left a little funky. */
989 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
991 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
992 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
994 if (share_is_ca) {
995 table = persistent_open_oplock_ca_table;
996 } else {
997 table = durable_open_vs_oplock_table;
1000 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1001 true, /* request_persistent */
1002 table,
1003 NUM_OPLOCK_OPEN_TESTS);
1005 talloc_free(tree);
1007 return ret;
1011 * basic persistent handle open test.
1012 * persistent state should only be granted when requested
1013 * along with a batch oplock or a handle lease.
1015 * This test tests persistent open with all valid lease types.
1018 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1020 { "", "", true, true },
1021 { "", "R", true, true },
1022 { "", "W", true, true },
1023 { "", "D", true, true },
1024 { "", "RW", true, true },
1025 { "", "RD", true, true },
1026 { "", "WD", true, true },
1027 { "", "RWD", true, true },
1029 { "R", "", true, true },
1030 { "R", "R", true, true },
1031 { "R", "W", true, true },
1032 { "R", "D", true, true },
1033 { "R", "RW", true, true },
1034 { "R", "RD", true, true },
1035 { "R", "DW", true, true },
1036 { "R", "RWD", true, true },
1038 { "RW", "", true, true },
1039 { "RW", "R", true, true },
1040 { "RW", "W", true, true },
1041 { "RW", "D", true, true },
1042 { "RW", "RW", true, true },
1043 { "RW", "RD", true, true },
1044 { "RW", "WD", true, true },
1045 { "RW", "RWD", true, true },
1047 { "RH", "", true, true },
1048 { "RH", "R", true, true },
1049 { "RH", "W", true, true },
1050 { "RH", "D", true, true },
1051 { "RH", "RW", true, true },
1052 { "RH", "RD", true, true },
1053 { "RH", "WD", true, true },
1054 { "RH", "RWD", true, true },
1056 { "RHW", "", true, true },
1057 { "RHW", "R", true, true },
1058 { "RHW", "W", true, true },
1059 { "RHW", "D", true, true },
1060 { "RHW", "RW", true, true },
1061 { "RHW", "RD", true, true },
1062 { "RHW", "WD", true, true },
1063 { "RHW", "RWD", true, true },
1066 bool test_persistent_open_lease(struct torture_context *tctx,
1067 struct smb2_tree *tree)
1069 char fname[256];
1070 bool ret = true;
1071 uint32_t caps;
1072 uint32_t share_capabilities;
1073 bool share_is_ca;
1074 struct durable_open_vs_lease *table;
1076 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1077 if (!(caps & SMB2_CAP_LEASING)) {
1078 torture_skip(tctx, "leases are not supported");
1081 /* Choose a random name in case the state is left a little funky. */
1082 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1084 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1085 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1087 if (share_is_ca) {
1088 table = persistent_open_lease_ca_table;
1089 } else {
1090 table = durable_open_vs_lease_table;
1093 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1094 true, /* request_persistent */
1095 table,
1096 NUM_LEASE_OPEN_TESTS);
1098 talloc_free(tree);
1100 return ret;
1103 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1105 struct torture_suite *suite =
1106 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1108 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1109 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1110 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1111 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1112 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1113 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1114 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1115 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1117 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
1119 return suite;