s4:torture: Do not check if the alloc_size is 0 on empty files
[Samba.git] / source4 / torture / smb2 / durable_v2_open.c
blob7609987e31ffb7d60e8d2237632f91fa41fef6b5
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.size, 0); \
49 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
50 CHECK_VAL((__io)->out.reserved2, 0); \
51 } while(0)
53 static struct {
54 int count;
55 struct smb2_close cl;
56 } break_info;
58 static void torture_oplock_close_callback(struct smb2_request *req)
60 smb2_close_recv(req, &break_info.cl);
63 /* A general oplock break notification handler. This should be used when a
64 * test expects to break from batch or exclusive to a lower level. */
65 static bool torture_oplock_handler(struct smb2_transport *transport,
66 const struct smb2_handle *handle,
67 uint8_t level,
68 void *private_data)
70 struct smb2_tree *tree = private_data;
71 struct smb2_request *req;
73 break_info.count++;
75 ZERO_STRUCT(break_info.cl);
76 break_info.cl.in.file.handle = *handle;
78 req = smb2_close_send(tree, &break_info.cl);
79 req->async.fn = torture_oplock_close_callback;
80 req->async.private_data = NULL;
81 return true;
84 /**
85 * testing various create blob combinations.
87 bool test_durable_v2_open_create_blob(struct torture_context *tctx,
88 struct smb2_tree *tree)
90 NTSTATUS status;
91 TALLOC_CTX *mem_ctx = talloc_new(tctx);
92 char fname[256];
93 struct smb2_handle _h;
94 struct smb2_handle *h = NULL;
95 struct smb2_create io;
96 struct GUID create_guid = GUID_random();
97 bool ret = true;
98 struct smbcli_options options;
100 options = tree->session->transport->options;
102 /* Choose a random name in case the state is left a little funky. */
103 snprintf(fname, 256, "durable_v2_open_create_blob_%s.dat",
104 generate_random_str(tctx, 8));
106 smb2_util_unlink(tree, fname);
108 smb2_oplock_create_share(&io, fname,
109 smb2_util_share_access(""),
110 smb2_util_oplock_level("b"));
111 io.in.durable_open = false;
112 io.in.durable_open_v2 = true;
113 io.in.persistent_open = false;
114 io.in.create_guid = create_guid;
115 io.in.timeout = UINT32_MAX;
117 status = smb2_create(tree, mem_ctx, &io);
118 CHECK_STATUS(status, NT_STATUS_OK);
119 _h = io.out.file.handle;
120 h = &_h;
121 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
122 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
123 CHECK_VAL(io.out.durable_open, false);
124 CHECK_VAL(io.out.durable_open_v2, true);
125 CHECK_VAL(io.out.persistent_open, false);
126 CHECK_VAL(io.out.timeout, io.in.timeout);
128 /* disconnect */
129 TALLOC_FREE(tree);
131 /* create a new session (same client_guid) */
132 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
133 torture_warning(tctx, "couldn't reconnect, bailing\n");
134 ret = false;
135 goto done;
139 * check invalid combinations of durable handle
140 * request and reconnect blobs
141 * See MS-SMB2: 3.3.5.9.12
142 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 Create Context
144 ZERO_STRUCT(io);
145 io.in.fname = fname;
146 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
147 io.in.durable_open = true; /* durable v1 handle request */
148 io.in.create_guid = create_guid;
149 status = smb2_create(tree, mem_ctx, &io);
150 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
152 ZERO_STRUCT(io);
153 io.in.fname = fname;
154 io.in.durable_handle = h; /* durable v1 reconnect request */
155 io.in.durable_open_v2 = true; /* durable v2 handle request */
156 io.in.create_guid = create_guid;
157 status = smb2_create(tree, mem_ctx, &io);
158 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
160 ZERO_STRUCT(io);
161 io.in.fname = fname;
162 io.in.durable_handle = h; /* durable v1 reconnect request */
163 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
164 io.in.create_guid = create_guid;
165 status = smb2_create(tree, mem_ctx, &io);
166 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
168 ZERO_STRUCT(io);
169 io.in.fname = fname;
170 io.in.durable_handle_v2 = h; /* durable v2 reconnect request */
171 io.in.durable_open_v2 = true; /* durable v2 handle request */
172 io.in.create_guid = create_guid;
173 status = smb2_create(tree, mem_ctx, &io);
174 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
176 done:
177 if (h != NULL) {
178 smb2_util_close(tree, *h);
181 smb2_util_unlink(tree, fname);
183 talloc_free(tree);
185 talloc_free(mem_ctx);
187 return ret;
192 * basic durable_open test.
193 * durable state should only be granted when requested
194 * along with a batch oplock or a handle lease.
196 * This test tests durable open with all possible oplock types.
199 struct durable_open_vs_oplock {
200 const char *level;
201 const char *share_mode;
202 bool durable;
203 bool persistent;
206 #define NUM_OPLOCK_TYPES 4
207 #define NUM_SHARE_MODES 8
208 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
209 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
211 { "", "", false, false },
212 { "", "R", false, false },
213 { "", "W", false, false },
214 { "", "D", false, false },
215 { "", "RD", false, false },
216 { "", "RW", false, false },
217 { "", "WD", false, false },
218 { "", "RWD", false, false },
220 { "s", "", false, false },
221 { "s", "R", false, false },
222 { "s", "W", false, false },
223 { "s", "D", false, false },
224 { "s", "RD", false, false },
225 { "s", "RW", false, false },
226 { "s", "WD", false, false },
227 { "s", "RWD", false, false },
229 { "x", "", false, false },
230 { "x", "R", false, false },
231 { "x", "W", false, false },
232 { "x", "D", false, false },
233 { "x", "RD", false, false },
234 { "x", "RW", false, false },
235 { "x", "WD", false, false },
236 { "x", "RWD", false, false },
238 { "b", "", true, false },
239 { "b", "R", true, false },
240 { "b", "W", true, false },
241 { "b", "D", true, false },
242 { "b", "RD", true, false },
243 { "b", "RW", true, false },
244 { "b", "WD", true, false },
245 { "b", "RWD", true, false },
248 static bool test_one_durable_v2_open_oplock(struct torture_context *tctx,
249 struct smb2_tree *tree,
250 const char *fname,
251 bool request_persistent,
252 struct durable_open_vs_oplock test)
254 NTSTATUS status;
255 TALLOC_CTX *mem_ctx = talloc_new(tctx);
256 struct smb2_handle _h;
257 struct smb2_handle *h = NULL;
258 bool ret = true;
259 struct smb2_create io;
261 smb2_util_unlink(tree, fname);
263 smb2_oplock_create_share(&io, fname,
264 smb2_util_share_access(test.share_mode),
265 smb2_util_oplock_level(test.level));
266 io.in.durable_open = false;
267 io.in.durable_open_v2 = true;
268 io.in.persistent_open = request_persistent;
269 io.in.create_guid = GUID_random();
271 status = smb2_create(tree, mem_ctx, &io);
272 CHECK_STATUS(status, NT_STATUS_OK);
273 _h = io.out.file.handle;
274 h = &_h;
275 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
276 CHECK_VAL(io.out.durable_open, false);
277 CHECK_VAL(io.out.durable_open_v2, test.durable);
278 CHECK_VAL(io.out.persistent_open, test.persistent);
279 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
281 done:
282 if (h != NULL) {
283 smb2_util_close(tree, *h);
285 smb2_util_unlink(tree, fname);
286 talloc_free(mem_ctx);
288 return ret;
291 static bool test_durable_v2_open_oplock_table(struct torture_context *tctx,
292 struct smb2_tree *tree,
293 const char *fname,
294 bool request_persistent,
295 struct durable_open_vs_oplock *table,
296 uint8_t num_tests)
298 bool ret = true;
299 uint8_t i;
301 smb2_util_unlink(tree, fname);
303 for (i = 0; i < num_tests; i++) {
304 ret = test_one_durable_v2_open_oplock(tctx,
305 tree,
306 fname,
307 request_persistent,
308 table[i]);
309 if (ret == false) {
310 goto done;
314 done:
315 smb2_util_unlink(tree, fname);
317 return ret;
320 bool test_durable_v2_open_oplock(struct torture_context *tctx,
321 struct smb2_tree *tree)
323 bool ret;
324 char fname[256];
326 /* Choose a random name in case the state is left a little funky. */
327 snprintf(fname, 256, "durable_open_oplock_%s.dat",
328 generate_random_str(tctx, 8));
330 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
331 false, /* request_persistent */
332 durable_open_vs_oplock_table,
333 NUM_OPLOCK_OPEN_TESTS);
335 talloc_free(tree);
337 return ret;
341 * basic durable handle open test.
342 * persistent state should only be granted when requested
343 * along with a batch oplock or a handle lease.
345 * This test tests persistent open with all valid lease types.
348 struct durable_open_vs_lease {
349 const char *type;
350 const char *share_mode;
351 bool durable;
352 bool persistent;
355 #define NUM_LEASE_TYPES 5
356 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
357 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
359 { "", "", false, false },
360 { "", "R", false, false },
361 { "", "W", false, false },
362 { "", "D", false, false },
363 { "", "RW", false, false },
364 { "", "RD", false, false },
365 { "", "WD", false, false },
366 { "", "RWD", false, false },
368 { "R", "", false, false },
369 { "R", "R", false, false },
370 { "R", "W", false, false },
371 { "R", "D", false, false },
372 { "R", "RW", false, false },
373 { "R", "RD", false, false },
374 { "R", "DW", false, false },
375 { "R", "RWD", false, false },
377 { "RW", "", false, false },
378 { "RW", "R", false, false },
379 { "RW", "W", false, false },
380 { "RW", "D", false, false },
381 { "RW", "RW", false, false },
382 { "RW", "RD", false, false },
383 { "RW", "WD", false, false },
384 { "RW", "RWD", false, false },
386 { "RH", "", true, false },
387 { "RH", "R", true, false },
388 { "RH", "W", true, false },
389 { "RH", "D", true, false },
390 { "RH", "RW", true, false },
391 { "RH", "RD", true, false },
392 { "RH", "WD", true, false },
393 { "RH", "RWD", true, false },
395 { "RHW", "", true, false },
396 { "RHW", "R", true, false },
397 { "RHW", "W", true, false },
398 { "RHW", "D", true, false },
399 { "RHW", "RW", true, false },
400 { "RHW", "RD", true, false },
401 { "RHW", "WD", true, false },
402 { "RHW", "RWD", true, false },
405 static bool test_one_durable_v2_open_lease(struct torture_context *tctx,
406 struct smb2_tree *tree,
407 const char *fname,
408 bool request_persistent,
409 struct durable_open_vs_lease test)
411 NTSTATUS status;
412 TALLOC_CTX *mem_ctx = talloc_new(tctx);
413 struct smb2_handle _h;
414 struct smb2_handle *h = NULL;
415 bool ret = true;
416 struct smb2_create io;
417 struct smb2_lease ls;
418 uint64_t lease;
420 smb2_util_unlink(tree, fname);
422 lease = random();
424 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
425 smb2_util_share_access(test.share_mode),
426 lease,
427 smb2_util_lease_state(test.type));
428 io.in.durable_open = false;
429 io.in.durable_open_v2 = true;
430 io.in.persistent_open = request_persistent;
431 io.in.create_guid = GUID_random();
433 status = smb2_create(tree, mem_ctx, &io);
434 CHECK_STATUS(status, NT_STATUS_OK);
435 _h = io.out.file.handle;
436 h = &_h;
437 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
438 CHECK_VAL(io.out.durable_open, false);
439 CHECK_VAL(io.out.durable_open_v2, test.durable);
440 CHECK_VAL(io.out.persistent_open, test.persistent);
441 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
442 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
443 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
444 CHECK_VAL(io.out.lease_response.lease_state,
445 smb2_util_lease_state(test.type));
446 done:
447 if (h != NULL) {
448 smb2_util_close(tree, *h);
450 smb2_util_unlink(tree, fname);
451 talloc_free(mem_ctx);
453 return ret;
456 static bool test_durable_v2_open_lease_table(struct torture_context *tctx,
457 struct smb2_tree *tree,
458 const char *fname,
459 bool request_persistent,
460 struct durable_open_vs_lease *table,
461 uint8_t num_tests)
463 bool ret = true;
464 uint8_t i;
466 smb2_util_unlink(tree, fname);
468 for (i = 0; i < num_tests; i++) {
469 ret = test_one_durable_v2_open_lease(tctx,
470 tree,
471 fname,
472 request_persistent,
473 table[i]);
474 if (ret == false) {
475 goto done;
479 done:
480 smb2_util_unlink(tree, fname);
482 return ret;
485 bool test_durable_v2_open_lease(struct torture_context *tctx,
486 struct smb2_tree *tree)
488 char fname[256];
489 bool ret = true;
490 uint32_t caps;
492 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
493 if (!(caps & SMB2_CAP_LEASING)) {
494 torture_skip(tctx, "leases are not supported");
497 /* Choose a random name in case the state is left a little funky. */
498 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
500 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
501 false, /* request_persistent */
502 durable_open_vs_lease_table,
503 NUM_LEASE_OPEN_TESTS);
505 talloc_free(tree);
506 return ret;
510 * basic test for doing a durable open
511 * and do a durable reopen on the same connection
512 * while the first open is still active (fails)
514 bool test_durable_v2_open_reopen1(struct torture_context *tctx,
515 struct smb2_tree *tree)
517 NTSTATUS status;
518 TALLOC_CTX *mem_ctx = talloc_new(tctx);
519 char fname[256];
520 struct smb2_handle _h;
521 struct smb2_handle *h = NULL;
522 struct smb2_create io;
523 struct GUID create_guid = GUID_random();
524 bool ret = true;
526 /* Choose a random name in case the state is left a little funky. */
527 snprintf(fname, 256, "durable_v2_open_reopen1_%s.dat",
528 generate_random_str(tctx, 8));
530 smb2_util_unlink(tree, fname);
532 smb2_oplock_create_share(&io, fname,
533 smb2_util_share_access(""),
534 smb2_util_oplock_level("b"));
535 io.in.durable_open = false;
536 io.in.durable_open_v2 = true;
537 io.in.persistent_open = false;
538 io.in.create_guid = create_guid;
539 io.in.timeout = UINT32_MAX;
541 status = smb2_create(tree, mem_ctx, &io);
542 CHECK_STATUS(status, NT_STATUS_OK);
543 _h = io.out.file.handle;
544 h = &_h;
545 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
546 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
547 CHECK_VAL(io.out.durable_open, false);
548 CHECK_VAL(io.out.durable_open_v2, true);
549 CHECK_VAL(io.out.persistent_open, false);
550 CHECK_VAL(io.out.timeout, io.in.timeout);
552 /* try a durable reconnect while the file is still open */
553 ZERO_STRUCT(io);
554 io.in.fname = "";
555 io.in.durable_handle_v2 = h;
556 io.in.create_guid = create_guid;
557 status = smb2_create(tree, mem_ctx, &io);
558 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
560 done:
561 if (h != NULL) {
562 smb2_util_close(tree, *h);
565 smb2_util_unlink(tree, fname);
567 talloc_free(tree);
569 talloc_free(mem_ctx);
571 return ret;
575 * Basic test for doing a durable open
576 * and do a session reconnect while the first
577 * session is still active and the handle is
578 * still open in the client.
579 * This closes the original session and a
580 * durable reconnect on the new session succeeds.
582 bool test_durable_v2_open_reopen1a(struct torture_context *tctx,
583 struct smb2_tree *tree)
585 NTSTATUS status;
586 TALLOC_CTX *mem_ctx = talloc_new(tctx);
587 char fname[256];
588 struct smb2_handle _h;
589 struct smb2_handle *h = NULL;
590 struct smb2_create io;
591 struct GUID create_guid = GUID_random();
592 bool ret = true;
593 struct smb2_tree *tree2 = NULL;
594 struct smb2_tree *tree3 = NULL;
595 uint64_t previous_session_id;
596 struct smbcli_options options;
597 struct GUID orig_client_guid;
599 options = tree->session->transport->options;
600 orig_client_guid = options.client_guid;
602 /* Choose a random name in case the state is left a little funky. */
603 snprintf(fname, 256, "durable_v2_open_reopen1a_%s.dat",
604 generate_random_str(tctx, 8));
606 smb2_util_unlink(tree, fname);
608 smb2_oplock_create_share(&io, fname,
609 smb2_util_share_access(""),
610 smb2_util_oplock_level("b"));
611 io.in.durable_open = false;
612 io.in.durable_open_v2 = true;
613 io.in.persistent_open = false;
614 io.in.create_guid = create_guid;
615 io.in.timeout = UINT32_MAX;
617 status = smb2_create(tree, mem_ctx, &io);
618 CHECK_STATUS(status, NT_STATUS_OK);
619 _h = io.out.file.handle;
620 h = &_h;
621 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
622 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
623 CHECK_VAL(io.out.durable_open, false);
624 CHECK_VAL(io.out.durable_open_v2, true);
625 CHECK_VAL(io.out.persistent_open, false);
626 CHECK_VAL(io.out.timeout, io.in.timeout);
629 * a session reconnect on a second tcp connection
632 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
634 /* for oplocks, the client guid can be different: */
635 options.client_guid = GUID_random();
637 ret = torture_smb2_connection_ext(tctx, previous_session_id,
638 &options, &tree2);
639 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
642 * check that this has deleted the old session
645 ZERO_STRUCT(io);
646 io.in.fname = "";
647 io.in.durable_handle_v2 = h;
648 io.in.create_guid = create_guid;
649 status = smb2_create(tree, mem_ctx, &io);
650 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
652 TALLOC_FREE(tree);
655 * but a durable reconnect on the new session succeeds:
658 ZERO_STRUCT(io);
659 io.in.fname = "";
660 io.in.durable_handle_v2 = h;
661 io.in.create_guid = create_guid;
662 status = smb2_create(tree2, mem_ctx, &io);
663 CHECK_STATUS(status, NT_STATUS_OK);
664 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
665 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
666 CHECK_VAL(io.out.durable_open, false);
667 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
668 CHECK_VAL(io.out.persistent_open, false);
669 CHECK_VAL(io.out.timeout, io.in.timeout);
670 _h = io.out.file.handle;
671 h = &_h;
674 * a session reconnect on a second tcp connection
677 previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
679 /* it works the same with the original guid */
680 options.client_guid = orig_client_guid;
682 ret = torture_smb2_connection_ext(tctx, previous_session_id,
683 &options, &tree3);
684 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
687 * check that this has deleted the old session
690 ZERO_STRUCT(io);
691 io.in.fname = "";
692 io.in.durable_handle_v2 = h;
693 io.in.create_guid = create_guid;
694 status = smb2_create(tree2, mem_ctx, &io);
695 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
696 TALLOC_FREE(tree2);
699 * but a durable reconnect on the new session succeeds:
702 ZERO_STRUCT(io);
703 io.in.fname = "";
704 io.in.durable_handle_v2 = h;
705 io.in.create_guid = create_guid;
706 status = smb2_create(tree3, mem_ctx, &io);
707 CHECK_STATUS(status, NT_STATUS_OK);
708 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
709 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
710 CHECK_VAL(io.out.durable_open, false);
711 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
712 CHECK_VAL(io.out.persistent_open, false);
713 CHECK_VAL(io.out.timeout, io.in.timeout);
714 _h = io.out.file.handle;
715 h = &_h;
717 done:
718 if (tree == NULL) {
719 tree = tree2;
722 if (tree == NULL) {
723 tree = tree3;
726 if (tree != NULL) {
727 if (h != NULL) {
728 smb2_util_close(tree, *h);
731 smb2_util_unlink(tree, fname);
733 talloc_free(tree);
736 talloc_free(mem_ctx);
738 return ret;
742 * lease variant of reopen1a
744 * Basic test for doing a durable open and doing a session
745 * reconnect while the first session is still active and the
746 * handle is still open in the client.
747 * This closes the original session and a durable reconnect on
748 * the new session succeeds depending on the client guid:
750 * Durable reconnect on a session with a different client guid fails.
751 * Durable reconnect on a session with the original client guid succeeds.
753 bool test_durable_v2_open_reopen1a_lease(struct torture_context *tctx,
754 struct smb2_tree *tree)
756 NTSTATUS status;
757 TALLOC_CTX *mem_ctx = talloc_new(tctx);
758 char fname[256];
759 struct smb2_handle _h;
760 struct smb2_handle *h = NULL;
761 struct smb2_create io;
762 struct GUID create_guid = GUID_random();
763 struct smb2_lease ls;
764 uint64_t lease_key;
765 bool ret = true;
766 struct smb2_tree *tree2 = NULL;
767 struct smb2_tree *tree3 = NULL;
768 uint64_t previous_session_id;
769 struct smbcli_options options;
770 struct GUID orig_client_guid;
772 options = tree->session->transport->options;
773 orig_client_guid = options.client_guid;
775 /* Choose a random name in case the state is left a little funky. */
776 snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
777 generate_random_str(tctx, 8));
779 smb2_util_unlink(tree, fname);
781 lease_key = random();
782 smb2_lease_create(&io, &ls, false /* dir */, fname,
783 lease_key, smb2_util_lease_state("RWH"));
784 io.in.durable_open = false;
785 io.in.durable_open_v2 = true;
786 io.in.persistent_open = false;
787 io.in.create_guid = create_guid;
788 io.in.timeout = UINT32_MAX;
790 status = smb2_create(tree, mem_ctx, &io);
791 CHECK_STATUS(status, NT_STATUS_OK);
792 _h = io.out.file.handle;
793 h = &_h;
794 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
795 CHECK_VAL(io.out.durable_open, false);
796 CHECK_VAL(io.out.durable_open_v2, true);
797 CHECK_VAL(io.out.persistent_open, false);
798 CHECK_VAL(io.out.timeout, io.in.timeout);
799 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
800 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
801 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
802 CHECK_VAL(io.out.lease_response.lease_state,
803 smb2_util_lease_state("RWH"));
804 CHECK_VAL(io.out.lease_response.lease_flags, 0);
805 CHECK_VAL(io.out.lease_response.lease_duration, 0);
807 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
810 * a session reconnect on a second tcp connection
811 * with a different client_guid does not allow
812 * the durable reconnect.
815 options.client_guid = GUID_random();
817 ret = torture_smb2_connection_ext(tctx, previous_session_id,
818 &options, &tree2);
819 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
822 * check that this has deleted the old session
825 ZERO_STRUCT(io);
826 io.in.fname = fname;
827 io.in.durable_handle_v2 = h;
828 io.in.create_guid = create_guid;
829 io.in.lease_request = &ls;
830 status = smb2_create(tree, mem_ctx, &io);
831 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
832 TALLOC_FREE(tree);
835 * but a durable reconnect on the new session with the wrong
836 * client guid fails
839 ZERO_STRUCT(io);
840 io.in.fname = fname;
841 io.in.durable_handle_v2 = h;
842 io.in.create_guid = create_guid;
843 io.in.lease_request = &ls;
844 status = smb2_create(tree2, mem_ctx, &io);
845 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
849 * now a session reconnect on a second tcp connection
850 * with original client_guid allows the durable reconnect.
853 options.client_guid = orig_client_guid;
854 //options.client_guid = GUID_random();
856 ret = torture_smb2_connection_ext(tctx, previous_session_id,
857 &options, &tree3);
858 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
861 * check that this has deleted the old session
864 ZERO_STRUCT(io);
865 io.in.fname = fname;
866 io.in.durable_handle_v2 = h;
867 io.in.create_guid = create_guid;
868 io.in.lease_request = &ls;
869 status = smb2_create(tree2, mem_ctx, &io);
870 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
871 TALLOC_FREE(tree2);
874 * but a durable reconnect on the new session succeeds:
877 ZERO_STRUCT(io);
878 io.in.fname = fname;
879 io.in.durable_handle_v2 = h;
880 io.in.create_guid = create_guid;
881 io.in.lease_request = &ls;
882 status = smb2_create(tree3, mem_ctx, &io);
883 CHECK_STATUS(status, NT_STATUS_OK);
884 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
885 CHECK_VAL(io.out.durable_open, false);
886 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
887 CHECK_VAL(io.out.persistent_open, false);
888 CHECK_VAL(io.out.timeout, io.in.timeout);
889 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
890 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
891 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
892 CHECK_VAL(io.out.lease_response.lease_state,
893 smb2_util_lease_state("RWH"));
894 CHECK_VAL(io.out.lease_response.lease_flags, 0);
895 CHECK_VAL(io.out.lease_response.lease_duration, 0);
896 _h = io.out.file.handle;
897 h = &_h;
899 done:
900 if (tree == NULL) {
901 tree = tree2;
904 if (tree == NULL) {
905 tree = tree3;
908 if (tree != NULL) {
909 if (h != NULL) {
910 smb2_util_close(tree, *h);
913 smb2_util_unlink(tree, fname);
915 talloc_free(tree);
918 talloc_free(mem_ctx);
920 return ret;
924 * basic test for doing a durable open
925 * tcp disconnect, reconnect, do a durable reopen (succeeds)
927 bool test_durable_v2_open_reopen2(struct torture_context *tctx,
928 struct smb2_tree *tree)
930 NTSTATUS status;
931 TALLOC_CTX *mem_ctx = talloc_new(tctx);
932 char fname[256];
933 struct smb2_handle _h;
934 struct smb2_handle *h = NULL;
935 struct smb2_create io;
936 struct GUID create_guid = GUID_random();
937 struct GUID create_guid_invalid = GUID_random();
938 bool ret = true;
940 /* Choose a random name in case the state is left a little funky. */
941 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
942 generate_random_str(tctx, 8));
944 smb2_util_unlink(tree, fname);
946 smb2_oplock_create_share(&io, fname,
947 smb2_util_share_access(""),
948 smb2_util_oplock_level("b"));
949 io.in.durable_open = false;
950 io.in.durable_open_v2 = true;
951 io.in.persistent_open = false;
952 io.in.create_guid = create_guid;
953 io.in.timeout = UINT32_MAX;
955 status = smb2_create(tree, mem_ctx, &io);
956 CHECK_STATUS(status, NT_STATUS_OK);
957 _h = io.out.file.handle;
958 h = &_h;
959 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
960 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
961 CHECK_VAL(io.out.durable_open, false);
962 CHECK_VAL(io.out.durable_open_v2, true);
963 CHECK_VAL(io.out.persistent_open, false);
964 CHECK_VAL(io.out.timeout, io.in.timeout);
966 /* disconnect, leaving the durable open */
967 TALLOC_FREE(tree);
969 if (!torture_smb2_connection(tctx, &tree)) {
970 torture_warning(tctx, "couldn't reconnect, bailing\n");
971 ret = false;
972 goto done;
976 * first a few failure cases
979 ZERO_STRUCT(io);
980 io.in.fname = "";
981 io.in.durable_handle_v2 = h;
982 status = smb2_create(tree, mem_ctx, &io);
983 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
985 ZERO_STRUCT(io);
986 io.in.fname = "__non_existing_fname__";
987 io.in.durable_handle_v2 = h;
988 status = smb2_create(tree, mem_ctx, &io);
989 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
991 ZERO_STRUCT(io);
992 io.in.fname = fname;
993 io.in.durable_handle_v2 = h;
994 status = smb2_create(tree, mem_ctx, &io);
995 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
997 /* a non-zero but non-matching create_guid does not change it: */
998 ZERO_STRUCT(io);
999 io.in.fname = fname;
1000 io.in.durable_handle_v2 = h;
1001 io.in.create_guid = create_guid_invalid;
1002 status = smb2_create(tree, mem_ctx, &io);
1003 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1006 * now success:
1007 * The important difference is that the create_guid is provided.
1009 ZERO_STRUCT(io);
1010 io.in.fname = fname;
1011 io.in.durable_open_v2 = false;
1012 io.in.durable_handle_v2 = h;
1013 io.in.create_guid = create_guid;
1014 h = NULL;
1016 status = smb2_create(tree, mem_ctx, &io);
1017 CHECK_STATUS(status, NT_STATUS_OK);
1018 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1019 CHECK_VAL(io.out.durable_open, false);
1020 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1021 CHECK_VAL(io.out.persistent_open, false);
1022 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1023 _h = io.out.file.handle;
1024 h = &_h;
1026 /* disconnect one more time */
1027 TALLOC_FREE(tree);
1029 if (!torture_smb2_connection(tctx, &tree)) {
1030 torture_warning(tctx, "couldn't reconnect, bailing\n");
1031 ret = false;
1032 goto done;
1035 ZERO_STRUCT(io);
1036 /* These are completely ignored by the server */
1037 io.in.security_flags = 0x78;
1038 io.in.oplock_level = 0x78;
1039 io.in.impersonation_level = 0x12345678;
1040 io.in.create_flags = 0x12345678;
1041 io.in.reserved = 0x12345678;
1042 io.in.desired_access = 0x12345678;
1043 io.in.file_attributes = 0x12345678;
1044 io.in.share_access = 0x12345678;
1045 io.in.create_disposition = 0x12345678;
1046 io.in.create_options = 0x12345678;
1047 io.in.fname = "__non_existing_fname__";
1050 * only io.in.durable_handle_v2 and
1051 * io.in.create_guid are checked
1053 io.in.durable_open_v2 = false;
1054 io.in.durable_handle_v2 = h;
1055 io.in.create_guid = create_guid;
1056 h = NULL;
1058 status = smb2_create(tree, mem_ctx, &io);
1059 CHECK_STATUS(status, NT_STATUS_OK);
1060 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1061 CHECK_VAL(io.out.durable_open, false);
1062 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1063 CHECK_VAL(io.out.persistent_open, false);
1064 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1065 _h = io.out.file.handle;
1066 h = &_h;
1068 done:
1069 if (h != NULL) {
1070 smb2_util_close(tree, *h);
1073 smb2_util_unlink(tree, fname);
1075 talloc_free(tree);
1077 talloc_free(mem_ctx);
1079 return ret;
1083 * durable reconnect test:
1084 * connect with v2, reconnect with v1
1086 bool test_durable_v2_open_reopen2b(struct torture_context *tctx,
1087 struct smb2_tree *tree)
1089 NTSTATUS status;
1090 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1091 char fname[256];
1092 struct smb2_handle _h;
1093 struct smb2_handle *h = NULL;
1094 struct smb2_create io;
1095 struct GUID create_guid = GUID_random();
1096 bool ret = true;
1097 struct smbcli_options options;
1099 options = tree->session->transport->options;
1101 /* Choose a random name in case the state is left a little funky. */
1102 snprintf(fname, 256, "durable_v2_open_reopen2b_%s.dat",
1103 generate_random_str(tctx, 8));
1105 smb2_util_unlink(tree, fname);
1107 smb2_oplock_create_share(&io, fname,
1108 smb2_util_share_access(""),
1109 smb2_util_oplock_level("b"));
1110 io.in.durable_open = false;
1111 io.in.durable_open_v2 = true;
1112 io.in.persistent_open = false;
1113 io.in.create_guid = create_guid;
1114 io.in.timeout = UINT32_MAX;
1116 status = smb2_create(tree, mem_ctx, &io);
1117 CHECK_STATUS(status, NT_STATUS_OK);
1118 _h = io.out.file.handle;
1119 h = &_h;
1120 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1121 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1122 CHECK_VAL(io.out.durable_open, false);
1123 CHECK_VAL(io.out.durable_open_v2, true);
1124 CHECK_VAL(io.out.persistent_open, false);
1125 CHECK_VAL(io.out.timeout, io.in.timeout);
1127 /* disconnect, leaving the durable open */
1128 TALLOC_FREE(tree);
1130 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1131 torture_warning(tctx, "couldn't reconnect, bailing\n");
1132 ret = false;
1133 goto done;
1136 ZERO_STRUCT(io);
1137 io.in.fname = fname;
1138 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1139 io.in.create_guid = GUID_zero(); /* but zero create GUID */
1140 status = smb2_create(tree, mem_ctx, &io);
1141 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1143 ZERO_STRUCT(io);
1144 io.in.fname = fname;
1145 io.in.durable_handle = h; /* durable v1 (!) reconnect */
1146 h = NULL;
1148 status = smb2_create(tree, mem_ctx, &io);
1149 CHECK_STATUS(status, NT_STATUS_OK);
1150 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1151 CHECK_VAL(io.out.durable_open, false);
1152 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1153 CHECK_VAL(io.out.persistent_open, false);
1154 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1155 _h = io.out.file.handle;
1156 h = &_h;
1158 done:
1159 if (h != NULL) {
1160 smb2_util_close(tree, *h);
1163 smb2_util_unlink(tree, fname);
1165 talloc_free(tree);
1167 talloc_free(mem_ctx);
1169 return ret;
1172 * durable reconnect test:
1173 * connect with v1, reconnect with v2 : fails (no create_guid...)
1175 bool test_durable_v2_open_reopen2c(struct torture_context *tctx,
1176 struct smb2_tree *tree)
1178 NTSTATUS status;
1179 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1180 char fname[256];
1181 struct smb2_handle _h;
1182 struct smb2_handle *h = NULL;
1183 struct smb2_create io;
1184 struct GUID create_guid = GUID_random();
1185 bool ret = true;
1186 struct smbcli_options options;
1188 options = tree->session->transport->options;
1190 /* Choose a random name in case the state is left a little funky. */
1191 snprintf(fname, 256, "durable_v2_open_reopen2c_%s.dat",
1192 generate_random_str(tctx, 8));
1194 smb2_util_unlink(tree, fname);
1196 smb2_oplock_create_share(&io, fname,
1197 smb2_util_share_access(""),
1198 smb2_util_oplock_level("b"));
1199 io.in.durable_open = true;
1200 io.in.durable_open_v2 = false;
1201 io.in.persistent_open = false;
1202 io.in.create_guid = create_guid;
1203 io.in.timeout = UINT32_MAX;
1205 status = smb2_create(tree, mem_ctx, &io);
1206 CHECK_STATUS(status, NT_STATUS_OK);
1207 _h = io.out.file.handle;
1208 h = &_h;
1209 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1210 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1211 CHECK_VAL(io.out.durable_open, true);
1212 CHECK_VAL(io.out.durable_open_v2, false);
1213 CHECK_VAL(io.out.persistent_open, false);
1214 CHECK_VAL(io.out.timeout, 0);
1216 /* disconnect, leaving the durable open */
1217 TALLOC_FREE(tree);
1219 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1220 torture_warning(tctx, "couldn't reconnect, bailing\n");
1221 ret = false;
1222 goto done;
1225 ZERO_STRUCT(io);
1226 io.in.fname = fname;
1227 io.in.durable_handle_v2 = h; /* durable v2 reconnect */
1228 io.in.create_guid = create_guid;
1229 status = smb2_create(tree, mem_ctx, &io);
1230 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1232 done:
1233 if (h != NULL) {
1234 smb2_util_close(tree, *h);
1237 smb2_util_unlink(tree, fname);
1239 talloc_free(tree);
1241 talloc_free(mem_ctx);
1243 return ret;
1247 * lease variant of reopen2
1248 * basic test for doing a durable open
1249 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1251 bool test_durable_v2_open_reopen2_lease(struct torture_context *tctx,
1252 struct smb2_tree *tree)
1254 NTSTATUS status;
1255 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1256 char fname[256];
1257 struct smb2_handle _h;
1258 struct smb2_handle *h = NULL;
1259 struct smb2_create io;
1260 struct GUID create_guid = GUID_random();
1261 struct smb2_lease ls;
1262 uint64_t lease_key;
1263 bool ret = true;
1264 struct smbcli_options options;
1265 uint32_t caps;
1267 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1268 if (!(caps & SMB2_CAP_LEASING)) {
1269 torture_skip(tctx, "leases are not supported");
1272 options = tree->session->transport->options;
1274 /* Choose a random name in case the state is left a little funky. */
1275 snprintf(fname, 256, "durable_v2_open_reopen2_%s.dat",
1276 generate_random_str(tctx, 8));
1278 smb2_util_unlink(tree, fname);
1280 lease_key = random();
1281 smb2_lease_create(&io, &ls, false /* dir */, fname,
1282 lease_key, smb2_util_lease_state("RWH"));
1283 io.in.durable_open = false;
1284 io.in.durable_open_v2 = true;
1285 io.in.persistent_open = false;
1286 io.in.create_guid = create_guid;
1287 io.in.timeout = UINT32_MAX;
1289 status = smb2_create(tree, mem_ctx, &io);
1290 CHECK_STATUS(status, NT_STATUS_OK);
1291 _h = io.out.file.handle;
1292 h = &_h;
1293 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1294 CHECK_VAL(io.out.durable_open, false);
1295 CHECK_VAL(io.out.durable_open_v2, true);
1296 CHECK_VAL(io.out.persistent_open, false);
1297 CHECK_VAL(io.out.timeout, io.in.timeout);
1298 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1299 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1300 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1301 CHECK_VAL(io.out.lease_response.lease_state,
1302 smb2_util_lease_state("RWH"));
1303 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1304 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1306 /* disconnect, reconnect and then do durable reopen */
1307 TALLOC_FREE(tree);
1309 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1310 torture_warning(tctx, "couldn't reconnect, bailing\n");
1311 ret = false;
1312 goto done;
1315 /* a few failure tests: */
1318 * several attempts without lease attached:
1319 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1320 * irrespective of file name provided
1323 ZERO_STRUCT(io);
1324 io.in.fname = "";
1325 io.in.durable_handle_v2 = h;
1326 status = smb2_create(tree, mem_ctx, &io);
1327 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1329 ZERO_STRUCT(io);
1330 io.in.fname = "__non_existing_fname__";
1331 io.in.durable_handle_v2 = h;
1332 status = smb2_create(tree, mem_ctx, &io);
1333 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1335 ZERO_STRUCT(io);
1336 io.in.fname = fname;
1337 io.in.durable_handle_v2 = h;
1338 status = smb2_create(tree, mem_ctx, &io);
1339 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1342 * attempt with lease provided, but
1343 * with a changed lease key. => fails
1345 ZERO_STRUCT(io);
1346 io.in.fname = fname;
1347 io.in.durable_open_v2 = false;
1348 io.in.durable_handle_v2 = h;
1349 io.in.create_guid = create_guid;
1350 io.in.lease_request = &ls;
1351 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1352 /* a wrong lease key lets the request fail */
1353 ls.lease_key.data[0]++;
1355 status = smb2_create(tree, mem_ctx, &io);
1356 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1358 /* restore the correct lease key */
1359 ls.lease_key.data[0]--;
1362 * this last failing attempt is almost correct:
1363 * only problem is: we use the wrong filename...
1364 * Note that this gives INVALID_PARAMETER.
1365 * This is different from oplocks!
1367 ZERO_STRUCT(io);
1368 io.in.fname = "__non_existing_fname__";
1369 io.in.durable_open_v2 = false;
1370 io.in.durable_handle_v2 = h;
1371 io.in.create_guid = create_guid;
1372 io.in.lease_request = &ls;
1373 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1375 status = smb2_create(tree, mem_ctx, &io);
1376 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1379 * Now for a succeeding reconnect:
1382 ZERO_STRUCT(io);
1383 io.in.fname = fname;
1384 io.in.durable_open_v2 = false;
1385 io.in.durable_handle_v2 = h;
1386 io.in.create_guid = create_guid;
1387 io.in.lease_request = &ls;
1388 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1390 /* the requested lease state is irrelevant */
1391 ls.lease_state = smb2_util_lease_state("");
1393 h = NULL;
1395 status = smb2_create(tree, mem_ctx, &io);
1396 CHECK_STATUS(status, NT_STATUS_OK);
1398 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1399 CHECK_VAL(io.out.durable_open, false);
1400 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1401 CHECK_VAL(io.out.persistent_open, false);
1402 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1403 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1404 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1405 CHECK_VAL(io.out.lease_response.lease_state,
1406 smb2_util_lease_state("RWH"));
1407 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1408 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1409 _h = io.out.file.handle;
1410 h = &_h;
1412 /* disconnect one more time */
1413 TALLOC_FREE(tree);
1415 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1416 torture_warning(tctx, "couldn't reconnect, bailing\n");
1417 ret = false;
1418 goto done;
1422 * demonstrate that various parameters are ignored
1423 * in the reconnect
1426 ZERO_STRUCT(io);
1428 * These are completely ignored by the server
1430 io.in.security_flags = 0x78;
1431 io.in.oplock_level = 0x78;
1432 io.in.impersonation_level = 0x12345678;
1433 io.in.create_flags = 0x12345678;
1434 io.in.reserved = 0x12345678;
1435 io.in.desired_access = 0x12345678;
1436 io.in.file_attributes = 0x12345678;
1437 io.in.share_access = 0x12345678;
1438 io.in.create_disposition = 0x12345678;
1439 io.in.create_options = 0x12345678;
1442 * only these are checked:
1443 * - io.in.fname
1444 * - io.in.durable_handle_v2,
1445 * - io.in.create_guid
1446 * - io.in.lease_request->lease_key
1449 io.in.fname = fname;
1450 io.in.durable_open_v2 = false;
1451 io.in.durable_handle_v2 = h;
1452 io.in.create_guid = create_guid;
1453 io.in.lease_request = &ls;
1455 /* the requested lease state is irrelevant */
1456 ls.lease_state = smb2_util_lease_state("");
1458 h = NULL;
1460 status = smb2_create(tree, mem_ctx, &io);
1461 CHECK_STATUS(status, NT_STATUS_OK);
1463 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1464 CHECK_VAL(io.out.durable_open, false);
1465 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1466 CHECK_VAL(io.out.persistent_open, false);
1467 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1468 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1469 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1470 CHECK_VAL(io.out.lease_response.lease_state,
1471 smb2_util_lease_state("RWH"));
1472 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1473 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1475 _h = io.out.file.handle;
1476 h = &_h;
1478 done:
1479 if (h != NULL) {
1480 smb2_util_close(tree, *h);
1483 smb2_util_unlink(tree, fname);
1485 talloc_free(tree);
1487 talloc_free(mem_ctx);
1489 return ret;
1493 * lease_v2 variant of reopen2
1494 * basic test for doing a durable open
1495 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1497 bool test_durable_v2_open_reopen2_lease_v2(struct torture_context *tctx,
1498 struct smb2_tree *tree)
1500 NTSTATUS status;
1501 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1502 char fname[256];
1503 struct smb2_handle _h;
1504 struct smb2_handle *h = NULL;
1505 struct smb2_create io;
1506 struct GUID create_guid = GUID_random();
1507 struct smb2_lease ls;
1508 uint64_t lease_key;
1509 bool ret = true;
1510 struct smbcli_options options;
1511 uint32_t caps;
1513 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1514 if (!(caps & SMB2_CAP_LEASING)) {
1515 torture_skip(tctx, "leases are not supported");
1518 options = tree->session->transport->options;
1520 smb2_deltree(tree, __func__);
1521 status = torture_smb2_testdir(tree, __func__, &_h);
1522 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1523 "torture_smb2_testdir failed\n");
1524 smb2_util_close(tree, _h);
1526 /* Choose a random name in case the state is left a little funky. */
1527 snprintf(fname, 256, "%s\\durable_v2_open_reopen2_%s.dat",
1528 __func__, generate_random_str(tctx, 8));
1530 smb2_util_unlink(tree, fname);
1532 lease_key = random();
1533 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1534 lease_key, 0, /* parent lease key */
1535 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1536 io.in.durable_open = false;
1537 io.in.durable_open_v2 = true;
1538 io.in.persistent_open = false;
1539 io.in.create_guid = create_guid;
1540 io.in.timeout = UINT32_MAX;
1542 status = smb2_create(tree, mem_ctx, &io);
1543 CHECK_STATUS(status, NT_STATUS_OK);
1544 _h = io.out.file.handle;
1545 h = &_h;
1546 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1547 CHECK_VAL(io.out.durable_open, false);
1548 CHECK_VAL(io.out.durable_open_v2, true);
1549 CHECK_VAL(io.out.persistent_open, false);
1550 CHECK_VAL(io.out.timeout, io.in.timeout);
1551 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1552 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1553 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1555 /* disconnect, reconnect and then do durable reopen */
1556 TALLOC_FREE(tree);
1558 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1559 torture_warning(tctx, "couldn't reconnect, bailing\n");
1560 ret = false;
1561 goto done;
1564 /* a few failure tests: */
1567 * several attempts without lease attached:
1568 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1569 * irrespective of file name provided
1572 ZERO_STRUCT(io);
1573 io.in.fname = "";
1574 io.in.durable_handle_v2 = h;
1575 status = smb2_create(tree, mem_ctx, &io);
1576 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1578 ZERO_STRUCT(io);
1579 io.in.fname = "__non_existing_fname__";
1580 io.in.durable_handle_v2 = h;
1581 status = smb2_create(tree, mem_ctx, &io);
1582 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1584 ZERO_STRUCT(io);
1585 io.in.fname = fname;
1586 io.in.durable_handle_v2 = h;
1587 status = smb2_create(tree, mem_ctx, &io);
1588 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1591 * attempt with lease provided, but
1592 * with a changed lease key. => fails
1594 ZERO_STRUCT(io);
1595 io.in.fname = fname;
1596 io.in.durable_open_v2 = false;
1597 io.in.durable_handle_v2 = h;
1598 io.in.create_guid = create_guid;
1599 io.in.lease_request_v2 = &ls;
1600 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1601 /* a wrong lease key lets the request fail */
1602 ls.lease_key.data[0]++;
1604 status = smb2_create(tree, mem_ctx, &io);
1605 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1607 /* restore the correct lease key */
1608 ls.lease_key.data[0]--;
1612 * this last failing attempt is almost correct:
1613 * only problem is: we use the wrong filename...
1614 * Note that this gives INVALID_PARAMETER.
1615 * This is different from oplocks!
1617 ZERO_STRUCT(io);
1618 io.in.fname = "__non_existing_fname__";
1619 io.in.durable_open_v2 = false;
1620 io.in.durable_handle_v2 = h;
1621 io.in.create_guid = create_guid;
1622 io.in.lease_request_v2 = &ls;
1623 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1625 status = smb2_create(tree, mem_ctx, &io);
1626 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1629 * Now for a succeeding reconnect:
1632 ZERO_STRUCT(io);
1633 io.in.fname = fname;
1634 io.in.durable_open_v2 = false;
1635 io.in.durable_handle_v2 = h;
1636 io.in.create_guid = create_guid;
1637 io.in.lease_request_v2 = &ls;
1638 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1640 /* the requested lease state is irrelevant */
1641 ls.lease_state = smb2_util_lease_state("");
1643 h = NULL;
1645 status = smb2_create(tree, mem_ctx, &io);
1646 CHECK_STATUS(status, NT_STATUS_OK);
1648 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1649 CHECK_VAL(io.out.durable_open, false);
1650 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1651 CHECK_VAL(io.out.persistent_open, false);
1652 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1653 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1654 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1655 CHECK_VAL(io.out.lease_response_v2.lease_state,
1656 smb2_util_lease_state("RWH"));
1657 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1658 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1659 _h = io.out.file.handle;
1660 h = &_h;
1662 /* disconnect one more time */
1663 TALLOC_FREE(tree);
1665 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1666 torture_warning(tctx, "couldn't reconnect, bailing\n");
1667 ret = false;
1668 goto done;
1672 * demonstrate that various parameters are ignored
1673 * in the reconnect
1676 ZERO_STRUCT(io);
1678 * These are completely ignored by the server
1680 io.in.security_flags = 0x78;
1681 io.in.oplock_level = 0x78;
1682 io.in.impersonation_level = 0x12345678;
1683 io.in.create_flags = 0x12345678;
1684 io.in.reserved = 0x12345678;
1685 io.in.desired_access = 0x12345678;
1686 io.in.file_attributes = 0x12345678;
1687 io.in.share_access = 0x12345678;
1688 io.in.create_disposition = 0x12345678;
1689 io.in.create_options = 0x12345678;
1690 io.in.fname = "__non_existing_fname__";
1693 * only these are checked:
1694 * - io.in.fname
1695 * - io.in.durable_handle_v2,
1696 * - io.in.create_guid
1697 * - io.in.lease_request_v2->lease_key
1700 io.in.fname = fname;
1701 io.in.durable_open_v2 = false;
1702 io.in.durable_handle_v2 = h;
1703 io.in.create_guid = create_guid;
1704 io.in.lease_request_v2 = &ls;
1706 /* the requested lease state is irrelevant */
1707 ls.lease_state = smb2_util_lease_state("");
1709 h = NULL;
1711 status = smb2_create(tree, mem_ctx, &io);
1712 CHECK_STATUS(status, NT_STATUS_OK);
1713 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1714 CHECK_VAL(io.out.durable_open, false);
1715 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1716 CHECK_VAL(io.out.persistent_open, false);
1717 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1718 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1719 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1720 CHECK_VAL(io.out.lease_response_v2.lease_state,
1721 smb2_util_lease_state("RWH"));
1722 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1723 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1725 _h = io.out.file.handle;
1726 h = &_h;
1728 done:
1729 if (h != NULL) {
1730 smb2_util_close(tree, *h);
1733 smb2_util_unlink(tree, fname);
1734 smb2_deltree(tree, __func__);
1736 talloc_free(tree);
1738 talloc_free(mem_ctx);
1740 return ret;
1744 * Test durable request / reconnect with AppInstanceId
1746 bool test_durable_v2_open_app_instance(struct torture_context *tctx,
1747 struct smb2_tree *tree1,
1748 struct smb2_tree *tree2)
1750 NTSTATUS status;
1751 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1752 char fname[256];
1753 struct smb2_handle _h1, _h2;
1754 struct smb2_handle *h1 = NULL, *h2 = NULL;
1755 struct smb2_create io1, io2;
1756 bool ret = true;
1757 struct GUID create_guid_1 = GUID_random();
1758 struct GUID create_guid_2 = GUID_random();
1759 struct GUID app_instance_id = GUID_random();
1761 /* Choose a random name in case the state is left a little funky. */
1762 snprintf(fname, 256, "durable_v2_open_app_instance_%s.dat",
1763 generate_random_str(tctx, 8));
1765 smb2_util_unlink(tree1, fname);
1767 ZERO_STRUCT(break_info);
1768 tree1->session->transport->oplock.handler = torture_oplock_handler;
1769 tree1->session->transport->oplock.private_data = tree1;
1771 smb2_oplock_create_share(&io1, fname,
1772 smb2_util_share_access(""),
1773 smb2_util_oplock_level("b"));
1774 io1.in.durable_open = false;
1775 io1.in.durable_open_v2 = true;
1776 io1.in.persistent_open = false;
1777 io1.in.create_guid = create_guid_1;
1778 io1.in.app_instance_id = &app_instance_id;
1779 io1.in.timeout = UINT32_MAX;
1781 status = smb2_create(tree1, mem_ctx, &io1);
1782 CHECK_STATUS(status, NT_STATUS_OK);
1783 _h1 = io1.out.file.handle;
1784 h1 = &_h1;
1785 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1786 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1787 CHECK_VAL(io1.out.durable_open, false);
1788 CHECK_VAL(io1.out.durable_open_v2, true);
1789 CHECK_VAL(io1.out.persistent_open, false);
1790 CHECK_VAL(io1.out.timeout, io1.in.timeout);
1793 * try to open the file as durable from a second tree with
1794 * a different create guid but the same app_instance_id
1795 * while the first handle is still open.
1798 smb2_oplock_create_share(&io2, fname,
1799 smb2_util_share_access(""),
1800 smb2_util_oplock_level("b"));
1801 io2.in.durable_open = false;
1802 io2.in.durable_open_v2 = true;
1803 io2.in.persistent_open = false;
1804 io2.in.create_guid = create_guid_2;
1805 io2.in.app_instance_id = &app_instance_id;
1806 io2.in.timeout = UINT32_MAX;
1808 status = smb2_create(tree2, mem_ctx, &io2);
1809 CHECK_STATUS(status, NT_STATUS_OK);
1810 _h2 = io2.out.file.handle;
1811 h2 = &_h2;
1812 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1813 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1814 CHECK_VAL(io2.out.durable_open, false);
1815 CHECK_VAL(io2.out.durable_open_v2, true);
1816 CHECK_VAL(io2.out.persistent_open, false);
1817 CHECK_VAL(io2.out.timeout, io2.in.timeout);
1819 CHECK_VAL(break_info.count, 0);
1821 status = smb2_util_close(tree1, *h1);
1822 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1823 h1 = NULL;
1825 done:
1826 if (h1 != NULL) {
1827 smb2_util_close(tree1, *h1);
1829 if (h2 != NULL) {
1830 smb2_util_close(tree2, *h2);
1833 smb2_util_unlink(tree2, fname);
1835 talloc_free(tree1);
1836 talloc_free(tree2);
1838 talloc_free(mem_ctx);
1840 return ret;
1845 * basic persistent open test.
1847 * This test tests durable open with all possible oplock types.
1850 struct durable_open_vs_oplock persistent_open_oplock_ca_table[NUM_OPLOCK_OPEN_TESTS] =
1852 { "", "", true, true },
1853 { "", "R", true, true },
1854 { "", "W", true, true },
1855 { "", "D", true, true },
1856 { "", "RD", true, true },
1857 { "", "RW", true, true },
1858 { "", "WD", true, true },
1859 { "", "RWD", true, true },
1861 { "s", "", true, true },
1862 { "s", "R", true, true },
1863 { "s", "W", true, true },
1864 { "s", "D", true, true },
1865 { "s", "RD", true, true },
1866 { "s", "RW", true, true },
1867 { "s", "WD", true, true },
1868 { "s", "RWD", true, true },
1870 { "x", "", true, true },
1871 { "x", "R", true, true },
1872 { "x", "W", true, true },
1873 { "x", "D", true, true },
1874 { "x", "RD", true, true },
1875 { "x", "RW", true, true },
1876 { "x", "WD", true, true },
1877 { "x", "RWD", true, true },
1879 { "b", "", true, true },
1880 { "b", "R", true, true },
1881 { "b", "W", true, true },
1882 { "b", "D", true, true },
1883 { "b", "RD", true, true },
1884 { "b", "RW", true, true },
1885 { "b", "WD", true, true },
1886 { "b", "RWD", true, true },
1889 bool test_persistent_open_oplock(struct torture_context *tctx,
1890 struct smb2_tree *tree)
1892 char fname[256];
1893 bool ret = true;
1894 uint32_t share_capabilities;
1895 bool share_is_ca = false;
1896 struct durable_open_vs_oplock *table;
1898 /* Choose a random name in case the state is left a little funky. */
1899 snprintf(fname, 256, "persistent_open_oplock_%s.dat", generate_random_str(tctx, 8));
1901 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1902 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1904 if (share_is_ca) {
1905 table = persistent_open_oplock_ca_table;
1906 } else {
1907 table = durable_open_vs_oplock_table;
1910 ret = test_durable_v2_open_oplock_table(tctx, tree, fname,
1911 true, /* request_persistent */
1912 table,
1913 NUM_OPLOCK_OPEN_TESTS);
1915 talloc_free(tree);
1917 return ret;
1921 * basic persistent handle open test.
1922 * persistent state should only be granted when requested
1923 * along with a batch oplock or a handle lease.
1925 * This test tests persistent open with all valid lease types.
1928 struct durable_open_vs_lease persistent_open_lease_ca_table[NUM_LEASE_OPEN_TESTS] =
1930 { "", "", true, true },
1931 { "", "R", true, true },
1932 { "", "W", true, true },
1933 { "", "D", true, true },
1934 { "", "RW", true, true },
1935 { "", "RD", true, true },
1936 { "", "WD", true, true },
1937 { "", "RWD", true, true },
1939 { "R", "", true, true },
1940 { "R", "R", true, true },
1941 { "R", "W", true, true },
1942 { "R", "D", true, true },
1943 { "R", "RW", true, true },
1944 { "R", "RD", true, true },
1945 { "R", "DW", true, true },
1946 { "R", "RWD", true, true },
1948 { "RW", "", true, true },
1949 { "RW", "R", true, true },
1950 { "RW", "W", true, true },
1951 { "RW", "D", true, true },
1952 { "RW", "RW", true, true },
1953 { "RW", "RD", true, true },
1954 { "RW", "WD", true, true },
1955 { "RW", "RWD", true, true },
1957 { "RH", "", true, true },
1958 { "RH", "R", true, true },
1959 { "RH", "W", true, true },
1960 { "RH", "D", true, true },
1961 { "RH", "RW", true, true },
1962 { "RH", "RD", true, true },
1963 { "RH", "WD", true, true },
1964 { "RH", "RWD", true, true },
1966 { "RHW", "", true, true },
1967 { "RHW", "R", true, true },
1968 { "RHW", "W", true, true },
1969 { "RHW", "D", true, true },
1970 { "RHW", "RW", true, true },
1971 { "RHW", "RD", true, true },
1972 { "RHW", "WD", true, true },
1973 { "RHW", "RWD", true, true },
1976 bool test_persistent_open_lease(struct torture_context *tctx,
1977 struct smb2_tree *tree)
1979 char fname[256];
1980 bool ret = true;
1981 uint32_t caps;
1982 uint32_t share_capabilities;
1983 bool share_is_ca;
1984 struct durable_open_vs_lease *table;
1986 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1987 if (!(caps & SMB2_CAP_LEASING)) {
1988 torture_skip(tctx, "leases are not supported");
1991 /* Choose a random name in case the state is left a little funky. */
1992 snprintf(fname, 256, "persistent_open_lease_%s.dat", generate_random_str(tctx, 8));
1994 share_capabilities = smb2cli_tcon_capabilities(tree->smbXcli);
1995 share_is_ca = share_capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
1997 if (share_is_ca) {
1998 table = persistent_open_lease_ca_table;
1999 } else {
2000 table = durable_open_vs_lease_table;
2003 ret = test_durable_v2_open_lease_table(tctx, tree, fname,
2004 true, /* request_persistent */
2005 table,
2006 NUM_LEASE_OPEN_TESTS);
2008 talloc_free(tree);
2010 return ret;
2013 struct torture_suite *torture_smb2_durable_v2_open_init(TALLOC_CTX *ctx)
2015 struct torture_suite *suite =
2016 torture_suite_create(ctx, "durable-v2-open");
2018 torture_suite_add_1smb2_test(suite, "create-blob", test_durable_v2_open_create_blob);
2019 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_v2_open_oplock);
2020 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_v2_open_lease);
2021 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_v2_open_reopen1);
2022 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_v2_open_reopen1a);
2023 torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_v2_open_reopen1a_lease);
2024 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_v2_open_reopen2);
2025 torture_suite_add_1smb2_test(suite, "reopen2b", test_durable_v2_open_reopen2b);
2026 torture_suite_add_1smb2_test(suite, "reopen2c", test_durable_v2_open_reopen2c);
2027 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_v2_open_reopen2_lease);
2028 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_v2_open_reopen2_lease_v2);
2029 torture_suite_add_2smb2_test(suite, "app-instance", test_durable_v2_open_app_instance);
2030 torture_suite_add_1smb2_test(suite, "persistent-open-oplock", test_persistent_open_oplock);
2031 torture_suite_add_1smb2_test(suite, "persistent-open-lease", test_persistent_open_lease);
2033 suite->description = talloc_strdup(suite, "SMB2-DURABLE-V2-OPEN tests");
2035 return suite;
2039 * basic test for doing a durable open
2040 * tcp disconnect, reconnect, do a durable reopen (succeeds)
2042 static bool test_durable_v2_reconnect_delay(struct torture_context *tctx,
2043 struct smb2_tree *tree)
2045 NTSTATUS status;
2046 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2047 char fname[256];
2048 struct smb2_handle _h;
2049 struct smb2_handle *h = NULL;
2050 struct smb2_create io;
2051 struct GUID create_guid = GUID_random();
2052 struct smbcli_options options;
2053 uint64_t previous_session_id;
2054 uint8_t b = 0;
2055 bool ret = true;
2057 options = tree->session->transport->options;
2058 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2060 /* Choose a random name in case the state is left a little funky. */
2061 snprintf(fname, 256, "durable_v2_reconnect_delay_%s.dat",
2062 generate_random_str(tctx, 8));
2064 smb2_util_unlink(tree, fname);
2066 smb2_oplock_create_share(&io, fname,
2067 smb2_util_share_access(""),
2068 smb2_util_oplock_level("b"));
2069 io.in.durable_open = false;
2070 io.in.durable_open_v2 = true;
2071 io.in.persistent_open = false;
2072 io.in.create_guid = create_guid;
2073 io.in.timeout = 0;
2075 status = smb2_create(tree, mem_ctx, &io);
2076 CHECK_STATUS(status, NT_STATUS_OK);
2078 _h = io.out.file.handle;
2079 h = &_h;
2080 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2081 CHECK_VAL(io.out.durable_open_v2, true);
2083 status = smb2_util_write(tree, *h, &b, 0, 1);
2084 CHECK_STATUS(status, NT_STATUS_OK);
2086 /* disconnect, leaving the durable open */
2087 TALLOC_FREE(tree);
2089 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2090 &options, &tree)) {
2091 torture_warning(tctx, "couldn't reconnect, bailing\n");
2092 ret = false;
2093 goto done;
2096 ZERO_STRUCT(io);
2097 io.in.fname = fname;
2098 io.in.durable_open_v2 = false;
2099 io.in.durable_handle_v2 = h;
2100 io.in.create_guid = create_guid;
2101 h = NULL;
2103 status = smb2_create(tree, mem_ctx, &io);
2104 CHECK_STATUS(status, NT_STATUS_OK);
2105 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2106 _h = io.out.file.handle;
2107 h = &_h;
2109 done:
2110 if (h != NULL) {
2111 smb2_util_close(tree, *h);
2114 smb2_util_unlink(tree, fname);
2116 talloc_free(tree);
2118 talloc_free(mem_ctx);
2120 return ret;
2123 struct torture_suite *torture_smb2_durable_v2_delay_init(TALLOC_CTX *ctx)
2125 struct torture_suite *suite =
2126 torture_suite_create(ctx, "durable-v2-delay");
2128 torture_suite_add_1smb2_test(suite, "durable_v2_reconnect_delay", test_durable_v2_reconnect_delay);
2130 return suite;