docs: man smbtorture: Add missing meta data.
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / durable_v2_open.c
blobb9d0d8ce86c1c68b27cac08cd417e227c89644fd
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 const char *name;
73 struct smb2_request *req;
75 break_info.count++;
77 ZERO_STRUCT(break_info.cl);
78 break_info.cl.in.file.handle = *handle;
80 req = smb2_close_send(tree, &break_info.cl);
81 req->async.fn = torture_oplock_close_callback;
82 req->async.private_data = NULL;
83 return true;
86 /**
87 * basic durable_open test.
88 * durable state should only be granted when requested
89 * along with a batch oplock or a handle lease.
91 * This test tests durable open with all possible oplock types.
94 struct durable_open_vs_oplock {
95 const char *level;
96 const char *share_mode;
97 bool durable;
98 bool persistent;
101 #define NUM_OPLOCK_TYPES 4
102 #define NUM_SHARE_MODES 8
103 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
104 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
106 { "", "", false, false },
107 { "", "R", false, false },
108 { "", "W", false, false },
109 { "", "D", false, false },
110 { "", "RD", false, false },
111 { "", "RW", false, false },
112 { "", "WD", false, false },
113 { "", "RWD", false, false },
115 { "s", "", false, false },
116 { "s", "R", false, false },
117 { "s", "W", false, false },
118 { "s", "D", false, false },
119 { "s", "RD", false, false },
120 { "s", "RW", false, false },
121 { "s", "WD", false, false },
122 { "s", "RWD", false, false },
124 { "x", "", false, false },
125 { "x", "R", false, false },
126 { "x", "W", false, false },
127 { "x", "D", false, false },
128 { "x", "RD", false, false },
129 { "x", "RW", false, false },
130 { "x", "WD", false, false },
131 { "x", "RWD", false, false },
133 { "b", "", true, false },
134 { "b", "R", true, false },
135 { "b", "W", true, false },
136 { "b", "D", true, false },
137 { "b", "RD", true, false },
138 { "b", "RW", true, false },
139 { "b", "WD", true, false },
140 { "b", "RWD", true, false },
143 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
144 struct smb2_tree *tree,
145 const char *fname,
146 bool request_persistent,
147 struct durable_open_vs_oplock test)
149 NTSTATUS status;
150 TALLOC_CTX *mem_ctx = talloc_new(tctx);
151 struct smb2_handle _h;
152 struct smb2_handle *h = NULL;
153 bool ret = true;
154 struct smb2_create io;
156 smb2_util_unlink(tree, fname);
158 smb2_oplock_create_share(&io, fname,
159 smb2_util_share_access(test.share_mode),
160 smb2_util_oplock_level(test.level));
161 io.in.durable_open = false;
162 io.in.durable_open_v2 = true;
163 io.in.persistent_open = request_persistent;
164 io.in.create_guid = GUID_random();
166 status = smb2_create(tree, mem_ctx, &io);
167 CHECK_STATUS(status, NT_STATUS_OK);
168 _h = io.out.file.handle;
169 h = &_h;
170 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
171 CHECK_VAL(io.out.durable_open, false);
172 CHECK_VAL(io.out.durable_open_v2, test.durable);
173 CHECK_VAL(io.out.persistent_open, test.persistent);
174 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
176 done:
177 if (h != NULL) {
178 smb2_util_close(tree, *h);
180 smb2_util_unlink(tree, fname);
181 talloc_free(mem_ctx);
183 return ret;
186 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
187 struct smb2_tree *tree,
188 const char *fname,
189 bool request_persistent,
190 struct durable_open_vs_oplock *table,
191 uint8_t num_tests)
193 bool ret = true;
194 uint8_t i;
196 smb2_util_unlink(tree, fname);
198 for (i = 0; i < num_tests; i++) {
199 ret = test_one_durable_v2_open_oplock(tctx,
200 tree,
201 fname,
202 request_persistent,
203 table[i]);
204 if (ret == false) {
205 goto done;
209 done:
210 smb2_util_unlink(tree, fname);
212 return ret;
215 bool test_durable_v2_open_oplock(struct torture_context *tctx,
216 struct smb2_tree *tree)
218 bool ret;
219 char fname[256];
221 /* Choose a random name in case the state is left a little funky. */
222 snprintf(fname, 256, "durable_open_oplock_%s.dat",
223 generate_random_str(tctx, 8));
225 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
226 false, /* request_persistent */
227 durable_open_vs_oplock_table,
228 NUM_OPLOCK_OPEN_TESTS);
230 talloc_free(tree);
232 return ret;
236 * basic durable handle open test.
237 * persistent state should only be granted when requested
238 * along with a batch oplock or a handle lease.
240 * This test tests persistent open with all valid lease types.
243 struct durable_open_vs_lease {
244 const char *type;
245 const char *share_mode;
246 bool durable;
247 bool persistent;
250 #define NUM_LEASE_TYPES 5
251 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
252 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
254 { "", "", false, false },
255 { "", "R", false, false },
256 { "", "W", false, false },
257 { "", "D", false, false },
258 { "", "RW", false, false },
259 { "", "RD", false, false },
260 { "", "WD", false, false },
261 { "", "RWD", false, false },
263 { "R", "", false, false },
264 { "R", "R", false, false },
265 { "R", "W", false, false },
266 { "R", "D", false, false },
267 { "R", "RW", false, false },
268 { "R", "RD", false, false },
269 { "R", "DW", false, false },
270 { "R", "RWD", false, false },
272 { "RW", "", false, false },
273 { "RW", "R", false, false },
274 { "RW", "W", false, false },
275 { "RW", "D", false, false },
276 { "RW", "RW", false, false },
277 { "RW", "RD", false, false },
278 { "RW", "WD", false, false },
279 { "RW", "RWD", false, false },
281 { "RH", "", true, false },
282 { "RH", "R", true, false },
283 { "RH", "W", true, false },
284 { "RH", "D", true, false },
285 { "RH", "RW", true, false },
286 { "RH", "RD", true, false },
287 { "RH", "WD", true, false },
288 { "RH", "RWD", true, false },
290 { "RHW", "", true, false },
291 { "RHW", "R", true, false },
292 { "RHW", "W", true, false },
293 { "RHW", "D", true, false },
294 { "RHW", "RW", true, false },
295 { "RHW", "RD", true, false },
296 { "RHW", "WD", true, false },
297 { "RHW", "RWD", true, false },
300 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
301 struct smb2_tree *tree,
302 const char *fname,
303 bool request_persistent,
304 struct durable_open_vs_lease test)
306 NTSTATUS status;
307 TALLOC_CTX *mem_ctx = talloc_new(tctx);
308 struct smb2_handle _h;
309 struct smb2_handle *h = NULL;
310 bool ret = true;
311 struct smb2_create io;
312 struct smb2_lease ls;
313 uint64_t lease;
315 smb2_util_unlink(tree, fname);
317 lease = random();
319 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
320 smb2_util_share_access(test.share_mode),
321 lease,
322 smb2_util_lease_state(test.type));
323 io.in.durable_open = false;
324 io.in.durable_open_v2 = true;
325 io.in.persistent_open = request_persistent;
326 io.in.create_guid = GUID_random();
328 status = smb2_create(tree, mem_ctx, &io);
329 CHECK_STATUS(status, NT_STATUS_OK);
330 _h = io.out.file.handle;
331 h = &_h;
332 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
333 CHECK_VAL(io.out.durable_open, false);
334 CHECK_VAL(io.out.durable_open_v2, test.durable);
335 CHECK_VAL(io.out.persistent_open, test.persistent);
336 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
337 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
338 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
339 CHECK_VAL(io.out.lease_response.lease_state,
340 smb2_util_lease_state(test.type));
341 done:
342 if (h != NULL) {
343 smb2_util_close(tree, *h);
345 smb2_util_unlink(tree, fname);
346 talloc_free(mem_ctx);
348 return ret;
351 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
352 struct smb2_tree *tree,
353 const char *fname,
354 bool request_persistent,
355 struct durable_open_vs_lease *table,
356 uint8_t num_tests)
358 bool ret = true;
359 uint8_t i;
361 smb2_util_unlink(tree, fname);
363 for (i = 0; i < num_tests; i++) {
364 ret = test_one_durable_v2_open_lease(tctx,
365 tree,
366 fname,
367 request_persistent,
368 table[i]);
369 if (ret == false) {
370 goto done;
374 done:
375 smb2_util_unlink(tree, fname);
377 return ret;
380 bool test_durable_v2_open_lease(struct torture_context *tctx,
381 struct smb2_tree *tree)
383 char fname[256];
384 bool ret = true;
385 uint32_t caps;
387 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
388 if (!(caps & SMB2_CAP_LEASING)) {
389 torture_skip(tctx, "leases are not supported");
392 /* Choose a random name in case the state is left a little funky. */
393 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
395 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
396 false, /* request_persistent */
397 durable_open_vs_lease_table,
398 NUM_LEASE_OPEN_TESTS);
400 talloc_free(tree);
401 return ret;
405 * basic test for doing a durable open
406 * and do a durable reopen on the same connection
407 * while the first open is still active (fails)
409 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
410 struct smb2_tree *tree)
412 NTSTATUS status;
413 TALLOC_CTX *mem_ctx = talloc_new(tctx);
414 char fname[256];
415 struct smb2_handle _h;
416 struct smb2_handle *h = NULL;
417 struct smb2_create io;
418 struct GUID create_guid = GUID_random();
419 bool ret = true;
421 /* Choose a random name in case the state is left a little funky. */
422 snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
423 generate_random_str(tctx, 8));
425 smb2_util_unlink(tree, fname);
427 smb2_oplock_create_share(&io, fname,
428 smb2_util_share_access(""),
429 smb2_util_oplock_level("b"));
430 io.in.durable_open = false;
431 io.in.durable_open_v2 = true;
432 io.in.persistent_open = false;
433 io.in.create_guid = create_guid;
434 io.in.timeout = UINT32_MAX;
436 status = smb2_create(tree, mem_ctx, &io);
437 CHECK_STATUS(status, NT_STATUS_OK);
438 _h = io.out.file.handle;
439 h = &_h;
440 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
441 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
442 CHECK_VAL(io.out.durable_open, false);
443 CHECK_VAL(io.out.durable_open_v2, true);
444 CHECK_VAL(io.out.persistent_open, false);
445 CHECK_VAL(io.out.timeout, io.in.timeout);
447 /* try a durable reconnect while the file is still open */
448 ZERO_STRUCT(io);
449 io.in.fname = "";
450 io.in.durable_handle_v2 = h;
451 io.in.create_guid = create_guid;
452 status = smb2_create(tree, mem_ctx, &io);
453 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
455 done:
456 if (h != NULL) {
457 smb2_util_close(tree, *h);
460 smb2_util_unlink(tree, fname);
462 talloc_free(tree);
464 talloc_free(mem_ctx);
466 return ret;
470 * basic test for doing a durable open
471 * tcp disconnect, reconnect, do a durable reopen (succeeds)
473 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
474 struct smb2_tree *tree)
476 NTSTATUS status;
477 TALLOC_CTX *mem_ctx = talloc_new(tctx);
478 char fname[256];
479 struct smb2_handle _h;
480 struct smb2_handle *h = NULL;
481 struct smb2_create io;
482 struct GUID create_guid = GUID_random();
483 bool ret = true;
485 /* Choose a random name in case the state is left a little funky. */
486 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
487 generate_random_str(tctx, 8));
489 smb2_util_unlink(tree, fname);
491 smb2_oplock_create_share(&io, fname,
492 smb2_util_share_access(""),
493 smb2_util_oplock_level("b"));
494 io.in.durable_open = false;
495 io.in.durable_open_v2 = true;
496 io.in.persistent_open = false;
497 io.in.create_guid = create_guid;
498 io.in.timeout = UINT32_MAX;
500 status = smb2_create(tree, mem_ctx, &io);
501 CHECK_STATUS(status, NT_STATUS_OK);
502 _h = io.out.file.handle;
503 h = &_h;
504 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
505 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
506 CHECK_VAL(io.out.durable_open, false);
507 CHECK_VAL(io.out.durable_open_v2, true);
508 CHECK_VAL(io.out.persistent_open, false);
509 CHECK_VAL(io.out.timeout, io.in.timeout);
511 /* disconnect, reconnect and then do durable reopen */
512 talloc_free(tree);
513 tree = NULL;
515 if (!torture_smb2_connection(tctx, &tree)) {
516 torture_warning(tctx, "couldn't reconnect, bailing\n");
517 ret = false;
518 goto done;
521 ZERO_STRUCT(io);
522 io.in.fname = "";
523 io.in.durable_handle_v2 = h;
524 status = smb2_create(tree, mem_ctx, &io);
525 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
527 ZERO_STRUCT(io);
528 io.in.fname = "__non_existing_fname__";
529 io.in.durable_handle_v2 = h;
530 status = smb2_create(tree, mem_ctx, &io);
531 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
533 ZERO_STRUCT(io);
534 io.in.fname = fname;
535 io.in.durable_handle_v2 = h;
536 status = smb2_create(tree, mem_ctx, &io);
537 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
539 ZERO_STRUCT(io);
541 * These are completely ignored by the server
543 io.in.security_flags = 0x78;
544 io.in.oplock_level = 0x78;
545 io.in.impersonation_level = 0x12345678;
546 io.in.create_flags = 0x12345678;
547 io.in.reserved = 0x12345678;
548 io.in.desired_access = 0x12345678;
549 io.in.file_attributes = 0x12345678;
550 io.in.share_access = 0x12345678;
551 io.in.create_disposition = 0x12345678;
552 io.in.create_options = 0x12345678;
553 io.in.fname = "__non_existing_fname__";
556 * only io.in.durable_handle_v2 and
557 * io.in.create_guid are checked
559 io.in.durable_open_v2 = false;
560 io.in.durable_handle_v2 = h;
561 io.in.create_guid = create_guid;
562 h = NULL;
564 status = smb2_create(tree, mem_ctx, &io);
565 CHECK_STATUS(status, NT_STATUS_OK);
566 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
567 CHECK_VAL(io.out.durable_open, false);
568 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
569 CHECK_VAL(io.out.persistent_open, false);
570 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
571 _h = io.out.file.handle;
572 h = &_h;
574 done:
575 if (h != NULL) {
576 smb2_util_close(tree, *h);
579 smb2_util_unlink(tree, fname);
581 talloc_free(tree);
583 talloc_free(mem_ctx);
585 return ret;
589 * Test durable request / reconnect with AppInstanceId
591 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
592 struct smb2_tree *tree1,
593 struct smb2_tree *tree2)
595 NTSTATUS status;
596 TALLOC_CTX *mem_ctx = talloc_new(tctx);
597 char fname[256];
598 struct smb2_handle _h1, _h2;
599 struct smb2_handle *h1 = NULL, *h2 = NULL;
600 struct smb2_create io1, io2;
601 bool ret = true;
602 struct GUID create_guid_1 = GUID_random();
603 struct GUID create_guid_2 = GUID_random();
604 struct GUID app_instance_id = GUID_random();
606 /* Choose a random name in case the state is left a little funky. */
607 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
608 generate_random_str(tctx, 8));
610 smb2_util_unlink(tree1, fname);
612 ZERO_STRUCT(break_info);
613 tree1->session->transport->oplock.handler = torture_oplock_handler;
614 tree1->session->transport->oplock.private_data = tree1;
616 smb2_oplock_create_share(&io1, fname,
617 smb2_util_share_access(""),
618 smb2_util_oplock_level("b"));
619 io1.in.durable_open = false;
620 io1.in.durable_open_v2 = true;
621 io1.in.persistent_open = false;
622 io1.in.create_guid = create_guid_1;
623 io1.in.app_instance_id = &app_instance_id;
624 io1.in.timeout = UINT32_MAX;
626 status = smb2_create(tree1, mem_ctx, &io1);
627 CHECK_STATUS(status, NT_STATUS_OK);
628 _h1 = io1.out.file.handle;
629 h1 = &_h1;
630 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
631 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
632 CHECK_VAL(io1.out.durable_open, false);
633 CHECK_VAL(io1.out.durable_open_v2, true);
634 CHECK_VAL(io1.out.persistent_open, false);
635 CHECK_VAL(io1.out.timeout, io1.in.timeout);
638 * try to open the file as durable from a second tree with
639 * a different create guid but the same app_instance_id
640 * while the first handle is still open.
643 smb2_oplock_create_share(&io2, fname,
644 smb2_util_share_access(""),
645 smb2_util_oplock_level("b"));
646 io2.in.durable_open = false;
647 io2.in.durable_open_v2 = true;
648 io2.in.persistent_open = false;
649 io2.in.create_guid = create_guid_2;
650 io2.in.app_instance_id = &app_instance_id;
651 io2.in.timeout = UINT32_MAX;
653 status = smb2_create(tree2, mem_ctx, &io2);
654 CHECK_STATUS(status, NT_STATUS_OK);
655 _h2 = io2.out.file.handle;
656 h2 = &_h2;
657 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
658 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
659 CHECK_VAL(io2.out.durable_open, false);
660 CHECK_VAL(io2.out.durable_open_v2, true);
661 CHECK_VAL(io2.out.persistent_open, false);
662 CHECK_VAL(io2.out.timeout, io2.in.timeout);
664 CHECK_VAL(break_info.count, 0);
666 status = smb2_util_close(tree1, *h1);
667 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
668 h1 = NULL;
670 done:
671 if (h1 != NULL) {
672 smb2_util_close(tree1, *h1);
674 if (h2 != NULL) {
675 smb2_util_close(tree2, *h2);
678 smb2_util_unlink(tree2, fname);
680 talloc_free(tree1);
681 talloc_free(tree2);
683 talloc_free(mem_ctx);
685 return ret;
690 * basic persistent open test.
692 * This test tests durable open with all possible oplock types.
695 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
697 { "", "", true, true },
698 { "", "R", true, true },
699 { "", "W", true, true },
700 { "", "D", true, true },
701 { "", "RD", true, true },
702 { "", "RW", true, true },
703 { "", "WD", true, true },
704 { "", "RWD", true, true },
706 { "s", "", true, true },
707 { "s", "R", true, true },
708 { "s", "W", true, true },
709 { "s", "D", true, true },
710 { "s", "RD", true, true },
711 { "s", "RW", true, true },
712 { "s", "WD", true, true },
713 { "s", "RWD", true, true },
715 { "x", "", true, true },
716 { "x", "R", true, true },
717 { "x", "W", true, true },
718 { "x", "D", true, true },
719 { "x", "RD", true, true },
720 { "x", "RW", true, true },
721 { "x", "WD", true, true },
722 { "x", "RWD", true, true },
724 { "b", "", true, true },
725 { "b", "R", true, true },
726 { "b", "W", true, true },
727 { "b", "D", true, true },
728 { "b", "RD", true, true },
729 { "b", "RW", true, true },
730 { "b", "WD", true, true },
731 { "b", "RWD", true, true },
734 bool test_persistent_open_oplock(struct torture_context *tctx,
735 struct smb2_tree *tree)
737 char fname[256];
738 bool ret = true;
739 uint32_t share_capabilities;
740 bool share_is_ca = false;
741 struct durable_open_vs_oplock *table;
743 /* Choose a random name in case the state is left a little funky. */
744 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
746 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
747 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
749 if (share_is_ca) {
750 table = persistent_open_oplock_ca_table;
751 } else {
752 table = durable_open_vs_oplock_table;
755 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
756 true, /* request_persistent */
757 table,
758 NUM_OPLOCK_OPEN_TESTS);
760 talloc_free(tree);
762 return ret;
766 * basic persistent handle open test.
767 * persistent state should only be granted when requested
768 * along with a batch oplock or a handle lease.
770 * This test tests persistent open with all valid lease types.
773 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
775 { "", "", true, true },
776 { "", "R", true, true },
777 { "", "W", true, true },
778 { "", "D", true, true },
779 { "", "RW", true, true },
780 { "", "RD", true, true },
781 { "", "WD", true, true },
782 { "", "RWD", true, true },
784 { "R", "", true, true },
785 { "R", "R", true, true },
786 { "R", "W", true, true },
787 { "R", "D", true, true },
788 { "R", "RW", true, true },
789 { "R", "RD", true, true },
790 { "R", "DW", true, true },
791 { "R", "RWD", true, true },
793 { "RW", "", true, true },
794 { "RW", "R", true, true },
795 { "RW", "W", true, true },
796 { "RW", "D", true, true },
797 { "RW", "RW", true, true },
798 { "RW", "RD", true, true },
799 { "RW", "WD", true, true },
800 { "RW", "RWD", true, true },
802 { "RH", "", true, true },
803 { "RH", "R", true, true },
804 { "RH", "W", true, true },
805 { "RH", "D", true, true },
806 { "RH", "RW", true, true },
807 { "RH", "RD", true, true },
808 { "RH", "WD", true, true },
809 { "RH", "RWD", true, true },
811 { "RHW", "", true, true },
812 { "RHW", "R", true, true },
813 { "RHW", "W", true, true },
814 { "RHW", "D", true, true },
815 { "RHW", "RW", true, true },
816 { "RHW", "RD", true, true },
817 { "RHW", "WD", true, true },
818 { "RHW", "RWD", true, true },
821 bool test_persistent_open_lease(struct torture_context *tctx,
822 struct smb2_tree *tree)
824 char fname[256];
825 bool ret = true;
826 uint32_t caps;
827 uint32_t share_capabilities;
828 bool share_is_ca;
829 struct durable_open_vs_lease *table;
831 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
832 if (!(caps & SMB2_CAP_LEASING)) {
833 torture_skip(tctx, "leases are not supported");
836 /* Choose a random name in case the state is left a little funky. */
837 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
839 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
840 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
842 if (share_is_ca) {
843 table = persistent_open_lease_ca_table;
844 } else {
845 table = durable_open_vs_lease_table;
848 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
849 true, /* request_persistent */
850 table,
851 NUM_LEASE_OPEN_TESTS);
853 talloc_free(tree);
855 return ret;
858 struct torture_suite *torture_smb2_durable_v2_open_init(void)
860 struct torture_suite *suite =
861 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
863 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
864 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
865 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
866 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
867 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
868 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
869 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
871 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
873 return suite;