libcli: Make cli_smb2_close_fnum async
[Samba.git] / source4 / torture / smb2 / durable_v2_open.c
blobf3ec344efcc66d77236596b35993bec57996f5d3
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 * and do a session reconnect while the first
578 * session is still active and the handle is
579 * still open in the client.
580 * This closes the original session and a
581 * durable reconnect on the new session succeeds.
583 bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
584 struct smb2_tree *tree)
586 NTSTATUS status;
587 TALLOC_CTX *mem_ctx = talloc_new(tctx);
588 char fname[256];
589 struct smb2_handle _h;
590 struct smb2_handle *h = NULL;
591 struct smb2_create io, io2;
592 struct GUID create_guid = GUID_random();
593 bool ret = true;
594 struct smb2_tree *tree2 = NULL;
595 uint64_t previous_session_id;
596 struct smbcli_options options;
598 options = tree->session->transport->options;
600 /* Choose a random name in case the state is left a little funky. */
601 snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
602 generate_random_str(tctx, 8));
604 smb2_util_unlink(tree, fname);
606 smb2_oplock_create_share(&io, fname,
607 smb2_util_share_access(""),
608 smb2_util_oplock_level("b"));
609 io.in.durable_open = false;
610 io.in.durable_open_v2 = true;
611 io.in.persistent_open = false;
612 io.in.create_guid = create_guid;
613 io.in.timeout = UINT32_MAX;
615 status = smb2_create(tree, mem_ctx, &io);
616 CHECK_STATUS(status, NT_STATUS_OK);
617 _h = io.out.file.handle;
618 h = &_h;
619 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
620 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
621 CHECK_VAL(io.out.durable_open, false);
622 CHECK_VAL(io.out.durable_open_v2, true);
623 CHECK_VAL(io.out.persistent_open, false);
624 CHECK_VAL(io.out.timeout, io.in.timeout);
627 * a session reconnect on a second tcp connection
630 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
632 if (!torture_smb2_connection_ext(tctx, previous_session_id,
633 &options, &tree2))
635 torture_warning(tctx, "couldn't reconnect, bailing\n");
636 ret = false;
637 goto done;
641 * check that this has deleted the old session
644 ZERO_STRUCT(io);
645 io.in.fname = "";
646 io.in.durable_handle_v2 = h;
647 io.in.create_guid = create_guid;
648 status = smb2_create(tree, mem_ctx, &io);
649 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
652 * but a durable reconnect on the new session succeeds:
655 ZERO_STRUCT(io2);
656 io2.in.fname = "";
657 io2.in.durable_handle_v2 = h;
658 io2.in.create_guid = create_guid;
659 status = smb2_create(tree2, mem_ctx, &io2);
660 CHECK_STATUS(status, NT_STATUS_OK);
661 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
662 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
663 CHECK_VAL(io2.out.durable_open, false);
664 CHECK_VAL(io2.out.durable_open_v2, false); /* no dh2q response blob */
665 CHECK_VAL(io2.out.persistent_open, false);
666 CHECK_VAL(io2.out.timeout, io.in.timeout);
667 _h = io2.out.file.handle;
668 h = &_h;
670 done:
671 if (h != NULL) {
672 smb2_util_close(tree, *h);
675 smb2_util_unlink(tree, fname);
677 talloc_free(tree);
679 talloc_free(mem_ctx);
681 return ret;
685 * basic test for doing a durable open
686 * tcp disconnect, reconnect, do a durable reopen (succeeds)
688 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
689 struct smb2_tree *tree)
691 NTSTATUS status;
692 TALLOC_CTX *mem_ctx = talloc_new(tctx);
693 char fname[256];
694 struct smb2_handle _h;
695 struct smb2_handle *h = NULL;
696 struct smb2_create io;
697 struct GUID create_guid = GUID_random();
698 struct GUID create_guid_invalid = GUID_random();
699 bool ret = true;
701 /* Choose a random name in case the state is left a little funky. */
702 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
703 generate_random_str(tctx, 8));
705 smb2_util_unlink(tree, fname);
707 smb2_oplock_create_share(&io, fname,
708 smb2_util_share_access(""),
709 smb2_util_oplock_level("b"));
710 io.in.durable_open = false;
711 io.in.durable_open_v2 = true;
712 io.in.persistent_open = false;
713 io.in.create_guid = create_guid;
714 io.in.timeout = UINT32_MAX;
716 status = smb2_create(tree, mem_ctx, &io);
717 CHECK_STATUS(status, NT_STATUS_OK);
718 _h = io.out.file.handle;
719 h = &_h;
720 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
721 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
722 CHECK_VAL(io.out.durable_open, false);
723 CHECK_VAL(io.out.durable_open_v2, true);
724 CHECK_VAL(io.out.persistent_open, false);
725 CHECK_VAL(io.out.timeout, io.in.timeout);
727 /* disconnect, leaving the durable open */
728 TALLOC_FREE(tree);
730 if (!torture_smb2_connection(tctx, &tree)) {
731 torture_warning(tctx, "couldn't reconnect, bailing\n");
732 ret = false;
733 goto done;
737 * first a few failure cases
740 ZERO_STRUCT(io);
741 io.in.fname = "";
742 io.in.durable_handle_v2 = h;
743 status = smb2_create(tree, mem_ctx, &io);
744 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
746 ZERO_STRUCT(io);
747 io.in.fname = "__non_existing_fname__";
748 io.in.durable_handle_v2 = h;
749 status = smb2_create(tree, mem_ctx, &io);
750 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
752 ZERO_STRUCT(io);
753 io.in.fname = fname;
754 io.in.durable_handle_v2 = h;
755 status = smb2_create(tree, mem_ctx, &io);
756 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
758 /* a non-zero but non-matching create_guid does not change it: */
759 ZERO_STRUCT(io);
760 io.in.fname = fname;
761 io.in.durable_handle_v2 = h;
762 io.in.create_guid = create_guid_invalid;
763 status = smb2_create(tree, mem_ctx, &io);
764 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
767 * now success:
768 * The important difference is that the create_guid is provided.
770 ZERO_STRUCT(io);
771 io.in.fname = fname;
772 io.in.durable_open_v2 = false;
773 io.in.durable_handle_v2 = h;
774 io.in.create_guid = create_guid;
775 h = NULL;
777 status = smb2_create(tree, mem_ctx, &io);
778 CHECK_STATUS(status, NT_STATUS_OK);
779 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
780 CHECK_VAL(io.out.durable_open, false);
781 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
782 CHECK_VAL(io.out.persistent_open, false);
783 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
784 _h = io.out.file.handle;
785 h = &_h;
787 /* disconnect one more time */
788 TALLOC_FREE(tree);
790 if (!torture_smb2_connection(tctx, &tree)) {
791 torture_warning(tctx, "couldn't reconnect, bailing\n");
792 ret = false;
793 goto done;
796 ZERO_STRUCT(io);
797 /* These are completely ignored by the server */
798 io.in.security_flags = 0x78;
799 io.in.oplock_level = 0x78;
800 io.in.impersonation_level = 0x12345678;
801 io.in.create_flags = 0x12345678;
802 io.in.reserved = 0x12345678;
803 io.in.desired_access = 0x12345678;
804 io.in.file_attributes = 0x12345678;
805 io.in.share_access = 0x12345678;
806 io.in.create_disposition = 0x12345678;
807 io.in.create_options = 0x12345678;
808 io.in.fname = "__non_existing_fname__";
811 * only io.in.durable_handle_v2 and
812 * io.in.create_guid are checked
814 io.in.durable_open_v2 = false;
815 io.in.durable_handle_v2 = h;
816 io.in.create_guid = create_guid;
817 h = NULL;
819 status = smb2_create(tree, mem_ctx, &io);
820 CHECK_STATUS(status, NT_STATUS_OK);
821 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
822 CHECK_VAL(io.out.durable_open, false);
823 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
824 CHECK_VAL(io.out.persistent_open, false);
825 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
826 _h = io.out.file.handle;
827 h = &_h;
829 done:
830 if (h != NULL) {
831 smb2_util_close(tree, *h);
834 smb2_util_unlink(tree, fname);
836 talloc_free(tree);
838 talloc_free(mem_ctx);
840 return ret;
844 * durable reconnect test:
845 * connect with v2, reconnect with v1
847 bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
848 struct smb2_tree *tree)
850 NTSTATUS status;
851 TALLOC_CTX *mem_ctx = talloc_new(tctx);
852 char fname[256];
853 struct smb2_handle _h;
854 struct smb2_handle *h = NULL;
855 struct smb2_create io;
856 struct GUID create_guid = GUID_random();
857 bool ret = true;
858 struct smbcli_options options;
860 options = tree->session->transport->options;
862 /* Choose a random name in case the state is left a little funky. */
863 snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
864 generate_random_str(tctx, 8));
866 smb2_util_unlink(tree, fname);
868 smb2_oplock_create_share(&io, fname,
869 smb2_util_share_access(""),
870 smb2_util_oplock_level("b"));
871 io.in.durable_open = false;
872 io.in.durable_open_v2 = true;
873 io.in.persistent_open = false;
874 io.in.create_guid = create_guid;
875 io.in.timeout = UINT32_MAX;
877 status = smb2_create(tree, mem_ctx, &io);
878 CHECK_STATUS(status, NT_STATUS_OK);
879 _h = io.out.file.handle;
880 h = &_h;
881 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
882 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
883 CHECK_VAL(io.out.durable_open, false);
884 CHECK_VAL(io.out.durable_open_v2, true);
885 CHECK_VAL(io.out.persistent_open, false);
886 CHECK_VAL(io.out.timeout, io.in.timeout);
888 /* disconnect, leaving the durable open */
889 TALLOC_FREE(tree);
891 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
892 torture_warning(tctx, "couldn't reconnect, bailing\n");
893 ret = false;
894 goto done;
897 ZERO_STRUCT(io);
898 io.in.fname = fname;
899 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
900 io.in.create_guid = GUID_zero(); /* but zero create GUID */
901 status = smb2_create(tree, mem_ctx, &io);
902 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
904 ZERO_STRUCT(io);
905 io.in.fname = fname;
906 io.in.durable_handle = h; /* durable v1 (!) reconnect */
907 h = NULL;
909 status = smb2_create(tree, mem_ctx, &io);
910 CHECK_STATUS(status, NT_STATUS_OK);
911 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
912 CHECK_VAL(io.out.durable_open, false);
913 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
914 CHECK_VAL(io.out.persistent_open, false);
915 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
916 _h = io.out.file.handle;
917 h = &_h;
919 done:
920 if (h != NULL) {
921 smb2_util_close(tree, *h);
924 smb2_util_unlink(tree, fname);
926 talloc_free(tree);
928 talloc_free(mem_ctx);
930 return ret;
933 * durable reconnect test:
934 * connect with v1, reconnect with v2 : fails (no create_guid...)
936 bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
937 struct smb2_tree *tree)
939 NTSTATUS status;
940 TALLOC_CTX *mem_ctx = talloc_new(tctx);
941 char fname[256];
942 struct smb2_handle _h;
943 struct smb2_handle *h = NULL;
944 struct smb2_create io;
945 struct GUID create_guid = GUID_random();
946 bool ret = true;
947 struct smbcli_options options;
949 options = tree->session->transport->options;
951 /* Choose a random name in case the state is left a little funky. */
952 snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
953 generate_random_str(tctx, 8));
955 smb2_util_unlink(tree, fname);
957 smb2_oplock_create_share(&io, fname,
958 smb2_util_share_access(""),
959 smb2_util_oplock_level("b"));
960 io.in.durable_open = true;
961 io.in.durable_open_v2 = false;
962 io.in.persistent_open = false;
963 io.in.create_guid = create_guid;
964 io.in.timeout = UINT32_MAX;
966 status = smb2_create(tree, mem_ctx, &io);
967 CHECK_STATUS(status, NT_STATUS_OK);
968 _h = io.out.file.handle;
969 h = &_h;
970 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
971 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
972 CHECK_VAL(io.out.durable_open, true);
973 CHECK_VAL(io.out.durable_open_v2, false);
974 CHECK_VAL(io.out.persistent_open, false);
975 CHECK_VAL(io.out.timeout, 0);
977 /* disconnect, leaving the durable open */
978 TALLOC_FREE(tree);
980 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
981 torture_warning(tctx, "couldn't reconnect, bailing\n");
982 ret = false;
983 goto done;
986 ZERO_STRUCT(io);
987 io.in.fname = fname;
988 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
989 io.in.create_guid = create_guid;
990 status = smb2_create(tree, mem_ctx, &io);
991 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
993 done:
994 if (h != NULL) {
995 smb2_util_close(tree, *h);
998 smb2_util_unlink(tree, fname);
1000 talloc_free(tree);
1002 talloc_free(mem_ctx);
1004 return ret;
1008 * lease variant of reopen2
1009 * basic test for doing a durable open
1010 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1012 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1013 struct smb2_tree *tree)
1015 NTSTATUS status;
1016 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1017 char fname[256];
1018 struct smb2_handle _h;
1019 struct smb2_handle *h = NULL;
1020 struct smb2_create io;
1021 struct GUID create_guid = GUID_random();
1022 struct smb2_lease ls;
1023 uint64_t lease_key;
1024 bool ret = true;
1025 struct smbcli_options options;
1026 uint32_t caps;
1028 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1029 if (!(caps & SMB2_CAP_LEASING)) {
1030 torture_skip(tctx, "leases are not supported");
1033 options = tree->session->transport->options;
1035 /* Choose a random name in case the state is left a little funky. */
1036 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1037 generate_random_str(tctx, 8));
1039 smb2_util_unlink(tree, fname);
1041 lease_key = random();
1042 smb2_lease_create(&io, &ls, false /* dir */, fname,
1043 lease_key, smb2_util_lease_state("RWH"));
1044 io.in.durable_open = false;
1045 io.in.durable_open_v2 = true;
1046 io.in.persistent_open = false;
1047 io.in.create_guid = create_guid;
1048 io.in.timeout = UINT32_MAX;
1050 status = smb2_create(tree, mem_ctx, &io);
1051 CHECK_STATUS(status, NT_STATUS_OK);
1052 _h = io.out.file.handle;
1053 h = &_h;
1054 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1055 CHECK_VAL(io.out.durable_open, false);
1056 CHECK_VAL(io.out.durable_open_v2, true);
1057 CHECK_VAL(io.out.persistent_open, false);
1058 CHECK_VAL(io.out.timeout, io.in.timeout);
1059 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1060 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1061 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1062 CHECK_VAL(io.out.lease_response.lease_state,
1063 smb2_util_lease_state("RWH"));
1064 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1065 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1067 /* disconnect, reconnect and then do durable reopen */
1068 TALLOC_FREE(tree);
1070 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1071 torture_warning(tctx, "couldn't reconnect, bailing\n");
1072 ret = false;
1073 goto done;
1076 /* a few failure tests: */
1079 * several attempts without lease attached:
1080 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1081 * irrespective of file name provided
1084 ZERO_STRUCT(io);
1085 io.in.fname = "";
1086 io.in.durable_handle_v2 = h;
1087 status = smb2_create(tree, mem_ctx, &io);
1088 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1090 ZERO_STRUCT(io);
1091 io.in.fname = "__non_existing_fname__";
1092 io.in.durable_handle_v2 = h;
1093 status = smb2_create(tree, mem_ctx, &io);
1094 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1096 ZERO_STRUCT(io);
1097 io.in.fname = fname;
1098 io.in.durable_handle_v2 = h;
1099 status = smb2_create(tree, mem_ctx, &io);
1100 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1103 * attempt with lease provided, but
1104 * with a changed lease key. => fails
1106 ZERO_STRUCT(io);
1107 io.in.fname = fname;
1108 io.in.durable_open_v2 = false;
1109 io.in.durable_handle_v2 = h;
1110 io.in.create_guid = create_guid;
1111 io.in.lease_request = &ls;
1112 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1113 /* a wrong lease key lets the request fail */
1114 ls.lease_key.data[0]++;
1116 status = smb2_create(tree, mem_ctx, &io);
1117 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1119 /* restore the correct lease key */
1120 ls.lease_key.data[0]--;
1123 * this last failing attempt is almost correct:
1124 * only problem is: we use the wrong filename...
1125 * Note that this gives INVALID_PARAMETER.
1126 * This is different from oplocks!
1128 ZERO_STRUCT(io);
1129 io.in.fname = "__non_existing_fname__";
1130 io.in.durable_open_v2 = false;
1131 io.in.durable_handle_v2 = h;
1132 io.in.create_guid = create_guid;
1133 io.in.lease_request = &ls;
1134 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1136 status = smb2_create(tree, mem_ctx, &io);
1137 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1140 * Now for a succeeding reconnect:
1143 ZERO_STRUCT(io);
1144 io.in.fname = fname;
1145 io.in.durable_open_v2 = false;
1146 io.in.durable_handle_v2 = h;
1147 io.in.create_guid = create_guid;
1148 io.in.lease_request = &ls;
1149 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1151 /* the requested lease state is irrelevant */
1152 ls.lease_state = smb2_util_lease_state("");
1154 h = NULL;
1156 status = smb2_create(tree, mem_ctx, &io);
1157 CHECK_STATUS(status, NT_STATUS_OK);
1159 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1160 CHECK_VAL(io.out.durable_open, false);
1161 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1162 CHECK_VAL(io.out.persistent_open, false);
1163 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1164 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1165 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1166 CHECK_VAL(io.out.lease_response.lease_state,
1167 smb2_util_lease_state("RWH"));
1168 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1169 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1170 _h = io.out.file.handle;
1171 h = &_h;
1173 /* disconnect one more time */
1174 TALLOC_FREE(tree);
1176 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1177 torture_warning(tctx, "couldn't reconnect, bailing\n");
1178 ret = false;
1179 goto done;
1183 * demonstrate that various parameters are ignored
1184 * in the reconnect
1187 ZERO_STRUCT(io);
1189 * These are completely ignored by the server
1191 io.in.security_flags = 0x78;
1192 io.in.oplock_level = 0x78;
1193 io.in.impersonation_level = 0x12345678;
1194 io.in.create_flags = 0x12345678;
1195 io.in.reserved = 0x12345678;
1196 io.in.desired_access = 0x12345678;
1197 io.in.file_attributes = 0x12345678;
1198 io.in.share_access = 0x12345678;
1199 io.in.create_disposition = 0x12345678;
1200 io.in.create_options = 0x12345678;
1203 * only these are checked:
1204 * - io.in.fname
1205 * - io.in.durable_handle_v2,
1206 * - io.in.create_guid
1207 * - io.in.lease_request->lease_key
1210 io.in.fname = fname;
1211 io.in.durable_open_v2 = false;
1212 io.in.durable_handle_v2 = h;
1213 io.in.create_guid = create_guid;
1214 io.in.lease_request = &ls;
1216 /* the requested lease state is irrelevant */
1217 ls.lease_state = smb2_util_lease_state("");
1219 h = NULL;
1221 status = smb2_create(tree, mem_ctx, &io);
1222 CHECK_STATUS(status, NT_STATUS_OK);
1224 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1225 CHECK_VAL(io.out.durable_open, false);
1226 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1227 CHECK_VAL(io.out.persistent_open, false);
1228 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1229 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1230 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1231 CHECK_VAL(io.out.lease_response.lease_state,
1232 smb2_util_lease_state("RWH"));
1233 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1234 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1236 _h = io.out.file.handle;
1237 h = &_h;
1239 done:
1240 if (h != NULL) {
1241 smb2_util_close(tree, *h);
1244 smb2_util_unlink(tree, fname);
1246 talloc_free(tree);
1248 talloc_free(mem_ctx);
1250 return ret;
1254 * lease_v2 variant of reopen2
1255 * basic test for doing a durable open
1256 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1258 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1259 struct smb2_tree *tree)
1261 NTSTATUS status;
1262 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1263 char fname[256];
1264 struct smb2_handle _h;
1265 struct smb2_handle *h = NULL;
1266 struct smb2_create io;
1267 struct GUID create_guid = GUID_random();
1268 struct smb2_lease ls;
1269 uint64_t lease_key;
1270 bool ret = true;
1271 struct smbcli_options options;
1272 uint32_t caps;
1274 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1275 if (!(caps & SMB2_CAP_LEASING)) {
1276 torture_skip(tctx, "leases are not supported");
1279 options = tree->session->transport->options;
1281 /* Choose a random name in case the state is left a little funky. */
1282 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1283 generate_random_str(tctx, 8));
1285 smb2_util_unlink(tree, fname);
1287 lease_key = random();
1288 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1289 lease_key, 0, /* parent lease key */
1290 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1291 io.in.durable_open = false;
1292 io.in.durable_open_v2 = true;
1293 io.in.persistent_open = false;
1294 io.in.create_guid = create_guid;
1295 io.in.timeout = UINT32_MAX;
1297 status = smb2_create(tree, mem_ctx, &io);
1298 CHECK_STATUS(status, NT_STATUS_OK);
1299 _h = io.out.file.handle;
1300 h = &_h;
1301 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1302 CHECK_VAL(io.out.durable_open, false);
1303 CHECK_VAL(io.out.durable_open_v2, true);
1304 CHECK_VAL(io.out.persistent_open, false);
1305 CHECK_VAL(io.out.timeout, io.in.timeout);
1306 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1307 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1308 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1310 /* disconnect, reconnect and then do durable reopen */
1311 TALLOC_FREE(tree);
1313 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1314 torture_warning(tctx, "couldn't reconnect, bailing\n");
1315 ret = false;
1316 goto done;
1319 /* a few failure tests: */
1322 * several attempts without lease attached:
1323 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1324 * irrespective of file name provided
1327 ZERO_STRUCT(io);
1328 io.in.fname = "";
1329 io.in.durable_handle_v2 = h;
1330 status = smb2_create(tree, mem_ctx, &io);
1331 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1333 ZERO_STRUCT(io);
1334 io.in.fname = "__non_existing_fname__";
1335 io.in.durable_handle_v2 = h;
1336 status = smb2_create(tree, mem_ctx, &io);
1337 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1339 ZERO_STRUCT(io);
1340 io.in.fname = fname;
1341 io.in.durable_handle_v2 = h;
1342 status = smb2_create(tree, mem_ctx, &io);
1343 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1346 * attempt with lease provided, but
1347 * with a changed lease key. => fails
1349 ZERO_STRUCT(io);
1350 io.in.fname = fname;
1351 io.in.durable_open_v2 = false;
1352 io.in.durable_handle_v2 = h;
1353 io.in.create_guid = create_guid;
1354 io.in.lease_request_v2 = &ls;
1355 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1356 /* a wrong lease key lets the request fail */
1357 ls.lease_key.data[0]++;
1359 status = smb2_create(tree, mem_ctx, &io);
1360 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1362 /* restore the correct lease key */
1363 ls.lease_key.data[0]--;
1367 * this last failing attempt is almost correct:
1368 * only problem is: we use the wrong filename...
1369 * Note that this gives INVALID_PARAMETER.
1370 * This is different from oplocks!
1372 ZERO_STRUCT(io);
1373 io.in.fname = "__non_existing_fname__";
1374 io.in.durable_open_v2 = false;
1375 io.in.durable_handle_v2 = h;
1376 io.in.create_guid = create_guid;
1377 io.in.lease_request_v2 = &ls;
1378 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1380 status = smb2_create(tree, mem_ctx, &io);
1381 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1384 * Now for a succeeding reconnect:
1387 ZERO_STRUCT(io);
1388 io.in.fname = fname;
1389 io.in.durable_open_v2 = false;
1390 io.in.durable_handle_v2 = h;
1391 io.in.create_guid = create_guid;
1392 io.in.lease_request_v2 = &ls;
1393 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1395 /* the requested lease state is irrelevant */
1396 ls.lease_state = smb2_util_lease_state("");
1398 h = NULL;
1400 status = smb2_create(tree, mem_ctx, &io);
1401 CHECK_STATUS(status, NT_STATUS_OK);
1403 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1404 CHECK_VAL(io.out.durable_open, false);
1405 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1406 CHECK_VAL(io.out.persistent_open, false);
1407 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1408 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1409 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1410 CHECK_VAL(io.out.lease_response_v2.lease_state,
1411 smb2_util_lease_state("RWH"));
1412 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1413 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1414 _h = io.out.file.handle;
1415 h = &_h;
1417 /* disconnect one more time */
1418 TALLOC_FREE(tree);
1420 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1421 torture_warning(tctx, "couldn't reconnect, bailing\n");
1422 ret = false;
1423 goto done;
1427 * demonstrate that various parameters are ignored
1428 * in the reconnect
1431 ZERO_STRUCT(io);
1433 * These are completely ignored by the server
1435 io.in.security_flags = 0x78;
1436 io.in.oplock_level = 0x78;
1437 io.in.impersonation_level = 0x12345678;
1438 io.in.create_flags = 0x12345678;
1439 io.in.reserved = 0x12345678;
1440 io.in.desired_access = 0x12345678;
1441 io.in.file_attributes = 0x12345678;
1442 io.in.share_access = 0x12345678;
1443 io.in.create_disposition = 0x12345678;
1444 io.in.create_options = 0x12345678;
1445 io.in.fname = "__non_existing_fname__";
1448 * only these are checked:
1449 * - io.in.fname
1450 * - io.in.durable_handle_v2,
1451 * - io.in.create_guid
1452 * - io.in.lease_request_v2->lease_key
1455 io.in.fname = fname;
1456 io.in.durable_open_v2 = false;
1457 io.in.durable_handle_v2 = h;
1458 io.in.create_guid = create_guid;
1459 io.in.lease_request_v2 = &ls;
1461 /* the requested lease state is irrelevant */
1462 ls.lease_state = smb2_util_lease_state("");
1464 h = NULL;
1466 status = smb2_create(tree, mem_ctx, &io);
1467 CHECK_STATUS(status, NT_STATUS_OK);
1468 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1469 CHECK_VAL(io.out.durable_open, false);
1470 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1471 CHECK_VAL(io.out.persistent_open, false);
1472 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1473 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1474 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1475 CHECK_VAL(io.out.lease_response_v2.lease_state,
1476 smb2_util_lease_state("RWH"));
1477 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1478 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1480 _h = io.out.file.handle;
1481 h = &_h;
1483 done:
1484 if (h != NULL) {
1485 smb2_util_close(tree, *h);
1488 smb2_util_unlink(tree, fname);
1490 talloc_free(tree);
1492 talloc_free(mem_ctx);
1494 return ret;
1498 * Test durable request / reconnect with AppInstanceId
1500 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1501 struct smb2_tree *tree1,
1502 struct smb2_tree *tree2)
1504 NTSTATUS status;
1505 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1506 char fname[256];
1507 struct smb2_handle _h1, _h2;
1508 struct smb2_handle *h1 = NULL, *h2 = NULL;
1509 struct smb2_create io1, io2;
1510 bool ret = true;
1511 struct GUID create_guid_1 = GUID_random();
1512 struct GUID create_guid_2 = GUID_random();
1513 struct GUID app_instance_id = GUID_random();
1515 /* Choose a random name in case the state is left a little funky. */
1516 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1517 generate_random_str(tctx, 8));
1519 smb2_util_unlink(tree1, fname);
1521 ZERO_STRUCT(break_info);
1522 tree1->session->transport->oplock.handler = torture_oplock_handler;
1523 tree1->session->transport->oplock.private_data = tree1;
1525 smb2_oplock_create_share(&io1, fname,
1526 smb2_util_share_access(""),
1527 smb2_util_oplock_level("b"));
1528 io1.in.durable_open = false;
1529 io1.in.durable_open_v2 = true;
1530 io1.in.persistent_open = false;
1531 io1.in.create_guid = create_guid_1;
1532 io1.in.app_instance_id = &app_instance_id;
1533 io1.in.timeout = UINT32_MAX;
1535 status = smb2_create(tree1, mem_ctx, &io1);
1536 CHECK_STATUS(status, NT_STATUS_OK);
1537 _h1 = io1.out.file.handle;
1538 h1 = &_h1;
1539 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1540 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1541 CHECK_VAL(io1.out.durable_open, false);
1542 CHECK_VAL(io1.out.durable_open_v2, true);
1543 CHECK_VAL(io1.out.persistent_open, false);
1544 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1547 * try to open the file as durable from a second tree with
1548 * a different create guid but the same app_instance_id
1549 * while the first handle is still open.
1552 smb2_oplock_create_share(&io2, fname,
1553 smb2_util_share_access(""),
1554 smb2_util_oplock_level("b"));
1555 io2.in.durable_open = false;
1556 io2.in.durable_open_v2 = true;
1557 io2.in.persistent_open = false;
1558 io2.in.create_guid = create_guid_2;
1559 io2.in.app_instance_id = &app_instance_id;
1560 io2.in.timeout = UINT32_MAX;
1562 status = smb2_create(tree2, mem_ctx, &io2);
1563 CHECK_STATUS(status, NT_STATUS_OK);
1564 _h2 = io2.out.file.handle;
1565 h2 = &_h2;
1566 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1567 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1568 CHECK_VAL(io2.out.durable_open, false);
1569 CHECK_VAL(io2.out.durable_open_v2, true);
1570 CHECK_VAL(io2.out.persistent_open, false);
1571 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1573 CHECK_VAL(break_info.count, 0);
1575 status = smb2_util_close(tree1, *h1);
1576 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1577 h1 = NULL;
1579 done:
1580 if (h1 != NULL) {
1581 smb2_util_close(tree1, *h1);
1583 if (h2 != NULL) {
1584 smb2_util_close(tree2, *h2);
1587 smb2_util_unlink(tree2, fname);
1589 talloc_free(tree1);
1590 talloc_free(tree2);
1592 talloc_free(mem_ctx);
1594 return ret;
1599 * basic persistent open test.
1601 * This test tests durable open with all possible oplock types.
1604 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1606 { "", "", true, true },
1607 { "", "R", true, true },
1608 { "", "W", true, true },
1609 { "", "D", true, true },
1610 { "", "RD", true, true },
1611 { "", "RW", true, true },
1612 { "", "WD", true, true },
1613 { "", "RWD", true, true },
1615 { "s", "", true, true },
1616 { "s", "R", true, true },
1617 { "s", "W", true, true },
1618 { "s", "D", true, true },
1619 { "s", "RD", true, true },
1620 { "s", "RW", true, true },
1621 { "s", "WD", true, true },
1622 { "s", "RWD", true, true },
1624 { "x", "", true, true },
1625 { "x", "R", true, true },
1626 { "x", "W", true, true },
1627 { "x", "D", true, true },
1628 { "x", "RD", true, true },
1629 { "x", "RW", true, true },
1630 { "x", "WD", true, true },
1631 { "x", "RWD", true, true },
1633 { "b", "", true, true },
1634 { "b", "R", true, true },
1635 { "b", "W", true, true },
1636 { "b", "D", true, true },
1637 { "b", "RD", true, true },
1638 { "b", "RW", true, true },
1639 { "b", "WD", true, true },
1640 { "b", "RWD", true, true },
1643 bool test_persistent_open_oplock(struct torture_context *tctx,
1644 struct smb2_tree *tree)
1646 char fname[256];
1647 bool ret = true;
1648 uint32_t share_capabilities;
1649 bool share_is_ca = false;
1650 struct durable_open_vs_oplock *table;
1652 /* Choose a random name in case the state is left a little funky. */
1653 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1655 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1656 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1658 if (share_is_ca) {
1659 table = persistent_open_oplock_ca_table;
1660 } else {
1661 table = durable_open_vs_oplock_table;
1664 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1665 true, /* request_persistent */
1666 table,
1667 NUM_OPLOCK_OPEN_TESTS);
1669 talloc_free(tree);
1671 return ret;
1675 * basic persistent handle open test.
1676 * persistent state should only be granted when requested
1677 * along with a batch oplock or a handle lease.
1679 * This test tests persistent open with all valid lease types.
1682 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1684 { "", "", true, true },
1685 { "", "R", true, true },
1686 { "", "W", true, true },
1687 { "", "D", true, true },
1688 { "", "RW", true, true },
1689 { "", "RD", true, true },
1690 { "", "WD", true, true },
1691 { "", "RWD", true, true },
1693 { "R", "", true, true },
1694 { "R", "R", true, true },
1695 { "R", "W", true, true },
1696 { "R", "D", true, true },
1697 { "R", "RW", true, true },
1698 { "R", "RD", true, true },
1699 { "R", "DW", true, true },
1700 { "R", "RWD", true, true },
1702 { "RW", "", true, true },
1703 { "RW", "R", true, true },
1704 { "RW", "W", true, true },
1705 { "RW", "D", true, true },
1706 { "RW", "RW", true, true },
1707 { "RW", "RD", true, true },
1708 { "RW", "WD", true, true },
1709 { "RW", "RWD", true, true },
1711 { "RH", "", true, true },
1712 { "RH", "R", true, true },
1713 { "RH", "W", true, true },
1714 { "RH", "D", true, true },
1715 { "RH", "RW", true, true },
1716 { "RH", "RD", true, true },
1717 { "RH", "WD", true, true },
1718 { "RH", "RWD", true, true },
1720 { "RHW", "", true, true },
1721 { "RHW", "R", true, true },
1722 { "RHW", "W", true, true },
1723 { "RHW", "D", true, true },
1724 { "RHW", "RW", true, true },
1725 { "RHW", "RD", true, true },
1726 { "RHW", "WD", true, true },
1727 { "RHW", "RWD", true, true },
1730 bool test_persistent_open_lease(struct torture_context *tctx,
1731 struct smb2_tree *tree)
1733 char fname[256];
1734 bool ret = true;
1735 uint32_t caps;
1736 uint32_t share_capabilities;
1737 bool share_is_ca;
1738 struct durable_open_vs_lease *table;
1740 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1741 if (!(caps & SMB2_CAP_LEASING)) {
1742 torture_skip(tctx, "leases are not supported");
1745 /* Choose a random name in case the state is left a little funky. */
1746 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1748 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1749 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1751 if (share_is_ca) {
1752 table = persistent_open_lease_ca_table;
1753 } else {
1754 table = durable_open_vs_lease_table;
1757 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
1758 true, /* request_persistent */
1759 table,
1760 NUM_LEASE_OPEN_TESTS);
1762 talloc_free(tree);
1764 return ret;
1767 struct torture_suite *torture_smb2_durable_v2_open_init(void)
1769 struct torture_suite *suite =
1770 torture_suite_create(talloc_autofree_context(), "durable-v2-open");
1772 torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
1773 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
1774 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
1775 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
1776 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
1777 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
1778 torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
1779 torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
1780 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
1781 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
1782 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
1783 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
1784 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
1786 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
1788 return suite;