s4:torture: Do not check if the alloc_size is 0 on empty files
[Samba.git] / source4 / torture / smb2 / durable_open.c
blob229c39dce675fa26e0f777048f3c09728d89148c
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
7 Copyright (C) Michael Adam 2011-2012
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
31 #define CHECK_VAL(v, correct) do { \
32 if ((v) != (correct)) { \
33 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should be 0x%llx\n", \
34 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35 ret = false; \
36 }} while (0)
38 #define CHECK_NOT_VAL(v, incorrect) do { \
39 if ((v) == (incorrect)) { \
40 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41 __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
42 ret = false; \
43 }} while (0)
45 #define CHECK_NOT_NULL(p) do { \
46 if ((p) == NULL) { \
47 torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
48 __location__, #p); \
49 ret = false; \
50 }} while (0)
52 #define CHECK_STATUS(status, correct) do { \
53 if (!NT_STATUS_EQUAL(status, correct)) { \
54 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
55 nt_errstr(status), nt_errstr(correct)); \
56 ret = false; \
57 goto done; \
58 }} while (0)
60 #define CHECK_CREATED(__io, __created, __attribute) \
61 do { \
62 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
63 CHECK_VAL((__io)->out.size, 0); \
64 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
65 CHECK_VAL((__io)->out.reserved2, 0); \
66 } while(0)
68 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
69 do { \
70 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
71 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
72 CHECK_VAL((__io)->out.size, (__size)); \
73 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
74 CHECK_VAL((__io)->out.reserved2, 0); \
75 } while(0)
79 /**
80 * basic durable_open test.
81 * durable state should only be granted when requested
82 * along with a batch oplock or a handle lease.
84 * This test tests durable open with all possible oplock types.
87 struct durable_open_vs_oplock {
88 const char *level;
89 const char *share_mode;
90 bool expected;
93 #define NUM_OPLOCK_TYPES 4
94 #define NUM_SHARE_MODES 8
95 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
96 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
98 { "", "", false },
99 { "", "R", false },
100 { "", "W", false },
101 { "", "D", false },
102 { "", "RD", false },
103 { "", "RW", false },
104 { "", "WD", false },
105 { "", "RWD", false },
107 { "s", "", false },
108 { "s", "R", false },
109 { "s", "W", false },
110 { "s", "D", false },
111 { "s", "RD", false },
112 { "s", "RW", false },
113 { "s", "WD", false },
114 { "s", "RWD", false },
116 { "x", "", false },
117 { "x", "R", false },
118 { "x", "W", false },
119 { "x", "D", false },
120 { "x", "RD", false },
121 { "x", "RW", false },
122 { "x", "WD", false },
123 { "x", "RWD", false },
125 { "b", "", true },
126 { "b", "R", true },
127 { "b", "W", true },
128 { "b", "D", true },
129 { "b", "RD", true },
130 { "b", "RW", true },
131 { "b", "WD", true },
132 { "b", "RWD", true },
135 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
136 struct smb2_tree *tree,
137 const char *fname,
138 struct durable_open_vs_oplock test)
140 NTSTATUS status;
141 TALLOC_CTX *mem_ctx = talloc_new(tctx);
142 struct smb2_handle _h;
143 struct smb2_handle *h = NULL;
144 bool ret = true;
145 struct smb2_create io;
147 smb2_util_unlink(tree, fname);
149 smb2_oplock_create_share(&io, fname,
150 smb2_util_share_access(test.share_mode),
151 smb2_util_oplock_level(test.level));
152 io.in.durable_open = true;
154 status = smb2_create(tree, mem_ctx, &io);
155 CHECK_STATUS(status, NT_STATUS_OK);
156 _h = io.out.file.handle;
157 h = &_h;
158 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
159 CHECK_VAL(io.out.durable_open, test.expected);
160 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
162 done:
163 if (h != NULL) {
164 smb2_util_close(tree, *h);
166 smb2_util_unlink(tree, fname);
167 talloc_free(mem_ctx);
169 return ret;
172 static bool test_durable_open_open_oplock(struct torture_context *tctx,
173 struct smb2_tree *tree)
175 TALLOC_CTX *mem_ctx = talloc_new(tctx);
176 char fname[256];
177 bool ret = true;
178 int i;
180 /* Choose a random name in case the state is left a little funky. */
181 snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
183 smb2_util_unlink(tree, fname);
185 /* test various oplock levels with durable open */
187 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
188 ret = test_one_durable_open_open_oplock(tctx,
189 tree,
190 fname,
191 durable_open_vs_oplock_table[i]);
192 if (ret == false) {
193 goto done;
197 done:
198 smb2_util_unlink(tree, fname);
199 talloc_free(tree);
200 talloc_free(mem_ctx);
202 return ret;
206 * basic durable_open test.
207 * durable state should only be granted when requested
208 * along with a batch oplock or a handle lease.
210 * This test tests durable open with all valid lease types.
213 struct durable_open_vs_lease {
214 const char *type;
215 const char *share_mode;
216 bool expected;
219 #define NUM_LEASE_TYPES 5
220 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
221 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
223 { "", "", false },
224 { "", "R", false },
225 { "", "W", false },
226 { "", "D", false },
227 { "", "RW", false },
228 { "", "RD", false },
229 { "", "WD", false },
230 { "", "RWD", false },
232 { "R", "", false },
233 { "R", "R", false },
234 { "R", "W", false },
235 { "R", "D", false },
236 { "R", "RW", false },
237 { "R", "RD", false },
238 { "R", "DW", false },
239 { "R", "RWD", false },
241 { "RW", "", false },
242 { "RW", "R", false },
243 { "RW", "W", false },
244 { "RW", "D", false },
245 { "RW", "RW", false },
246 { "RW", "RD", false },
247 { "RW", "WD", false },
248 { "RW", "RWD", false },
250 { "RH", "", true },
251 { "RH", "R", true },
252 { "RH", "W", true },
253 { "RH", "D", true },
254 { "RH", "RW", true },
255 { "RH", "RD", true },
256 { "RH", "WD", true },
257 { "RH", "RWD", true },
259 { "RHW", "", true },
260 { "RHW", "R", true },
261 { "RHW", "W", true },
262 { "RHW", "D", true },
263 { "RHW", "RW", true },
264 { "RHW", "RD", true },
265 { "RHW", "WD", true },
266 { "RHW", "RWD", true },
269 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
270 struct smb2_tree *tree,
271 const char *fname,
272 struct durable_open_vs_lease test)
274 NTSTATUS status;
275 TALLOC_CTX *mem_ctx = talloc_new(tctx);
276 struct smb2_handle _h;
277 struct smb2_handle *h = NULL;
278 bool ret = true;
279 struct smb2_create io;
280 struct smb2_lease ls;
281 uint64_t lease;
282 uint32_t caps;
284 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
285 if (!(caps & SMB2_CAP_LEASING)) {
286 torture_skip(tctx, "leases are not supported");
289 smb2_util_unlink(tree, fname);
291 lease = random();
293 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
294 smb2_util_share_access(test.share_mode),
295 lease,
296 smb2_util_lease_state(test.type));
297 io.in.durable_open = true;
299 status = smb2_create(tree, mem_ctx, &io);
300 CHECK_STATUS(status, NT_STATUS_OK);
301 _h = io.out.file.handle;
302 h = &_h;
303 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
304 CHECK_VAL(io.out.durable_open, test.expected);
305 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
306 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
307 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
308 CHECK_VAL(io.out.lease_response.lease_state,
309 smb2_util_lease_state(test.type));
310 done:
311 if (h != NULL) {
312 smb2_util_close(tree, *h);
314 smb2_util_unlink(tree, fname);
315 talloc_free(mem_ctx);
317 return ret;
320 static bool test_durable_open_open_lease(struct torture_context *tctx,
321 struct smb2_tree *tree)
323 TALLOC_CTX *mem_ctx = talloc_new(tctx);
324 char fname[256];
325 bool ret = true;
326 int i;
327 uint32_t caps;
329 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
330 if (!(caps & SMB2_CAP_LEASING)) {
331 torture_skip(tctx, "leases are not supported");
334 /* Choose a random name in case the state is left a little funky. */
335 snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
337 smb2_util_unlink(tree, fname);
340 /* test various oplock levels with durable open */
342 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
343 ret = test_one_durable_open_open_lease(tctx,
344 tree,
345 fname,
346 durable_open_vs_lease_table[i]);
347 if (ret == false) {
348 goto done;
352 done:
353 smb2_util_unlink(tree, fname);
354 talloc_free(tree);
355 talloc_free(mem_ctx);
357 return ret;
361 * basic test for doing a durable open
362 * and do a durable reopen on the same connection
363 * while the first open is still active (fails)
365 static bool test_durable_open_reopen1(struct torture_context *tctx,
366 struct smb2_tree *tree)
368 NTSTATUS status;
369 TALLOC_CTX *mem_ctx = talloc_new(tctx);
370 char fname[256];
371 struct smb2_handle _h;
372 struct smb2_handle *h = NULL;
373 struct smb2_create io1, io2;
374 bool ret = true;
376 /* Choose a random name in case the state is left a little funky. */
377 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
378 generate_random_str(tctx, 8));
380 smb2_util_unlink(tree, fname);
382 smb2_oplock_create_share(&io1, fname,
383 smb2_util_share_access(""),
384 smb2_util_oplock_level("b"));
385 io1.in.durable_open = true;
387 status = smb2_create(tree, mem_ctx, &io1);
388 CHECK_STATUS(status, NT_STATUS_OK);
389 _h = io1.out.file.handle;
390 h = &_h;
391 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
392 CHECK_VAL(io1.out.durable_open, true);
393 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
395 /* try a durable reconnect while the file is still open */
396 ZERO_STRUCT(io2);
397 io2.in.fname = fname;
398 io2.in.durable_handle = h;
400 status = smb2_create(tree, mem_ctx, &io2);
401 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
403 done:
404 if (h != NULL) {
405 smb2_util_close(tree, *h);
408 smb2_util_unlink(tree, fname);
410 talloc_free(tree);
412 talloc_free(mem_ctx);
414 return ret;
418 * Basic test for doing a durable open
419 * and do a session reconnect while the first
420 * session is still active and the handle is
421 * still open in the client.
422 * This closes the original session and a
423 * durable reconnect on the new session succeeds.
425 static bool test_durable_open_reopen1a(struct torture_context *tctx,
426 struct smb2_tree *tree)
428 NTSTATUS status;
429 TALLOC_CTX *mem_ctx = talloc_new(tctx);
430 char fname[256];
431 struct smb2_handle _h;
432 struct smb2_handle *h = NULL;
433 struct smb2_create io;
434 bool ret = true;
435 struct smb2_tree *tree2 = NULL;
436 struct smb2_tree *tree3 = NULL;
437 uint64_t previous_session_id;
438 struct smbcli_options options;
439 struct GUID orig_client_guid;
441 options = tree->session->transport->options;
442 orig_client_guid = options.client_guid;
444 /* Choose a random name in case the state is left a little funky. */
445 snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
446 generate_random_str(tctx, 8));
448 smb2_util_unlink(tree, fname);
450 smb2_oplock_create_share(&io, fname,
451 smb2_util_share_access(""),
452 smb2_util_oplock_level("b"));
453 io.in.durable_open = true;
455 status = smb2_create(tree, mem_ctx, &io);
456 CHECK_STATUS(status, NT_STATUS_OK);
457 _h = io.out.file.handle;
458 h = &_h;
459 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
460 CHECK_VAL(io.out.durable_open, true);
461 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
464 * a session reconnect on a second tcp connection
467 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
469 /* for oplocks, the client guid can be different: */
470 options.client_guid = GUID_random();
472 ret = torture_smb2_connection_ext(tctx, previous_session_id,
473 &options, &tree2);
474 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
477 * check that this has deleted the old session
480 ZERO_STRUCT(io);
481 io.in.fname = fname;
482 io.in.durable_handle = h;
484 status = smb2_create(tree, mem_ctx, &io);
485 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
487 TALLOC_FREE(tree);
490 * but a durable reconnect on the new session succeeds:
493 ZERO_STRUCT(io);
494 io.in.fname = fname;
495 io.in.durable_handle = h;
497 status = smb2_create(tree2, mem_ctx, &io);
498 CHECK_STATUS(status, NT_STATUS_OK);
499 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
500 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
501 _h = io.out.file.handle;
502 h = &_h;
505 * a session reconnect on a second tcp connection
508 previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
510 /* the original client_guid works just the same */
511 options.client_guid = orig_client_guid;
513 ret = torture_smb2_connection_ext(tctx, previous_session_id,
514 &options, &tree3);
515 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
518 * check that this has deleted the old session
521 ZERO_STRUCT(io);
522 io.in.fname = fname;
523 io.in.durable_handle = h;
525 status = smb2_create(tree2, mem_ctx, &io);
526 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
528 TALLOC_FREE(tree2);
531 * but a durable reconnect on the new session succeeds:
534 ZERO_STRUCT(io);
535 io.in.fname = fname;
536 io.in.durable_handle = h;
538 status = smb2_create(tree3, mem_ctx, &io);
539 CHECK_STATUS(status, NT_STATUS_OK);
540 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
541 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
542 _h = io.out.file.handle;
543 h = &_h;
545 done:
546 if (tree == NULL) {
547 tree = tree2;
550 if (tree == NULL) {
551 tree = tree3;
554 if (tree != NULL) {
555 if (h != NULL) {
556 smb2_util_close(tree, *h);
557 h = NULL;
559 smb2_util_unlink(tree, fname);
561 talloc_free(tree);
564 talloc_free(mem_ctx);
566 return ret;
570 * lease variant of reopen1a
572 * Basic test for doing a durable open and doing a session
573 * reconnect while the first session is still active and the
574 * handle is still open in the client.
575 * This closes the original session and a durable reconnect on
576 * the new session succeeds depending on the client guid:
578 * Durable reconnect on a session with a different client guid fails.
579 * Durable reconnect on a session with the original client guid succeeds.
581 bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
582 struct smb2_tree *tree)
584 NTSTATUS status;
585 TALLOC_CTX *mem_ctx = talloc_new(tctx);
586 char fname[256];
587 struct smb2_handle _h;
588 struct smb2_handle *h = NULL;
589 struct smb2_create io;
590 struct smb2_lease ls;
591 uint64_t lease_key;
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_lease_%s.dat",
604 generate_random_str(tctx, 8));
606 smb2_util_unlink(tree, fname);
608 lease_key = random();
609 smb2_lease_create(&io, &ls, false /* dir */, fname,
610 lease_key, smb2_util_lease_state("RWH"));
611 io.in.durable_open = true;
613 status = smb2_create(tree, mem_ctx, &io);
614 CHECK_STATUS(status, NT_STATUS_OK);
615 _h = io.out.file.handle;
616 h = &_h;
617 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
618 CHECK_VAL(io.out.durable_open, true);
619 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
620 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
621 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
622 CHECK_VAL(io.out.lease_response.lease_state,
623 smb2_util_lease_state("RWH"));
624 CHECK_VAL(io.out.lease_response.lease_flags, 0);
625 CHECK_VAL(io.out.lease_response.lease_duration, 0);
627 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
630 * a session reconnect on a second tcp connection
631 * with a different client_guid does not allow
632 * the durable reconnect.
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 = fname;
647 io.in.durable_handle = h;
648 io.in.lease_request = &ls;
649 status = smb2_create(tree, mem_ctx, &io);
650 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
651 TALLOC_FREE(tree);
655 * but a durable reconnect on the new session with the wrong
656 * client guid fails
659 ZERO_STRUCT(io);
660 io.in.fname = fname;
661 io.in.durable_handle = h;
662 io.in.lease_request = &ls;
663 status = smb2_create(tree2, mem_ctx, &io);
664 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
667 * now a session reconnect on a second tcp connection
668 * with original client_guid allows the durable reconnect.
671 options.client_guid = orig_client_guid;
673 ret = torture_smb2_connection_ext(tctx, previous_session_id,
674 &options, &tree3);
675 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
678 * check that this has deleted the old session
679 * In this case, a durable reconnect attempt with the
680 * correct client_guid yields a different error code.
683 ZERO_STRUCT(io);
684 io.in.fname = fname;
685 io.in.durable_handle = h;
686 io.in.lease_request = &ls;
687 status = smb2_create(tree2, mem_ctx, &io);
688 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
689 TALLOC_FREE(tree2);
692 * but a durable reconnect on the new session succeeds:
695 ZERO_STRUCT(io);
696 io.in.fname = fname;
697 io.in.durable_handle = h;
698 io.in.lease_request = &ls;
699 status = smb2_create(tree3, mem_ctx, &io);
700 CHECK_STATUS(status, NT_STATUS_OK);
701 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
702 CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
703 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
704 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
705 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
706 CHECK_VAL(io.out.lease_response.lease_state,
707 smb2_util_lease_state("RWH"));
708 CHECK_VAL(io.out.lease_response.lease_flags, 0);
709 CHECK_VAL(io.out.lease_response.lease_duration, 0);
710 _h = io.out.file.handle;
711 h = &_h;
713 done:
714 if (tree == NULL) {
715 tree = tree2;
718 if (tree == NULL) {
719 tree = tree3;
722 if (tree != NULL) {
723 if (h != NULL) {
724 smb2_util_close(tree, *h);
727 smb2_util_unlink(tree, fname);
729 talloc_free(tree);
732 talloc_free(mem_ctx);
734 return ret;
739 * basic test for doing a durable open
740 * tcp disconnect, reconnect, do a durable reopen (succeeds)
742 static bool test_durable_open_reopen2(struct torture_context *tctx,
743 struct smb2_tree *tree)
745 NTSTATUS status;
746 TALLOC_CTX *mem_ctx = talloc_new(tctx);
747 char fname[256];
748 struct smb2_handle _h;
749 struct smb2_handle *h = NULL;
750 struct smb2_create io;
751 bool ret = true;
753 /* Choose a random name in case the state is left a little funky. */
754 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
755 generate_random_str(tctx, 8));
757 smb2_util_unlink(tree, fname);
759 smb2_oplock_create_share(&io, fname,
760 smb2_util_share_access(""),
761 smb2_util_oplock_level("b"));
762 io.in.durable_open = true;
764 status = smb2_create(tree, mem_ctx, &io);
765 CHECK_STATUS(status, NT_STATUS_OK);
766 _h = io.out.file.handle;
767 h = &_h;
768 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
769 CHECK_VAL(io.out.durable_open, true);
770 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
772 /* disconnect, leaving the durable in place */
773 TALLOC_FREE(tree);
775 if (!torture_smb2_connection(tctx, &tree)) {
776 torture_warning(tctx, "couldn't reconnect, bailing\n");
777 ret = false;
778 goto done;
781 ZERO_STRUCT(io);
782 /* the path name is ignored by the server */
783 io.in.fname = fname;
784 io.in.durable_handle = h; /* durable v1 reconnect request */
785 h = NULL;
787 status = smb2_create(tree, mem_ctx, &io);
788 CHECK_STATUS(status, NT_STATUS_OK);
789 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
790 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
791 _h = io.out.file.handle;
792 h = &_h;
794 /* disconnect again, leaving the durable in place */
795 TALLOC_FREE(tree);
797 if (!torture_smb2_connection(tctx, &tree)) {
798 torture_warning(tctx, "couldn't reconnect, bailing\n");
799 ret = false;
800 goto done;
804 * show that the filename and many other fields
805 * are ignored. only the reconnect request blob
806 * is important.
808 ZERO_STRUCT(io);
809 /* the path name is ignored by the server */
810 io.in.security_flags = 0x78;
811 io.in.oplock_level = 0x78;
812 io.in.impersonation_level = 0x12345678;
813 io.in.create_flags = 0x12345678;
814 io.in.reserved = 0x12345678;
815 io.in.desired_access = 0x12345678;
816 io.in.file_attributes = 0x12345678;
817 io.in.share_access = 0x12345678;
818 io.in.create_disposition = 0x12345678;
819 io.in.create_options = 0x12345678;
820 io.in.fname = "__non_existing_fname__";
821 io.in.durable_handle = h; /* durable v1 reconnect request */
822 h = NULL;
824 status = smb2_create(tree, mem_ctx, &io);
825 CHECK_STATUS(status, NT_STATUS_OK);
826 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
827 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
828 _h = io.out.file.handle;
829 h = &_h;
831 /* disconnect, leaving the durable in place */
832 TALLOC_FREE(tree);
834 if (!torture_smb2_connection(tctx, &tree)) {
835 torture_warning(tctx, "couldn't reconnect, bailing\n");
836 ret = false;
837 goto done;
841 * show that an additionally specified durable v1 request
842 * is ignored by the server.
843 * See MS-SMB2, 3.3.5.9.7
844 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
846 ZERO_STRUCT(io);
847 /* the path name is ignored by the server */
848 io.in.fname = fname;
849 io.in.durable_handle = h; /* durable v1 reconnect request */
850 io.in.durable_open = true; /* durable v1 handle request */
851 h = NULL;
853 status = smb2_create(tree, mem_ctx, &io);
854 CHECK_STATUS(status, NT_STATUS_OK);
855 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
856 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
857 _h = io.out.file.handle;
858 h = &_h;
860 done:
861 if (tree != NULL) {
862 if (h != NULL) {
863 smb2_util_close(tree, *h);
866 smb2_util_unlink(tree, fname);
868 talloc_free(tree);
871 talloc_free(mem_ctx);
873 return ret;
877 * lease variant of reopen2
878 * basic test for doing a durable open
879 * tcp disconnect, reconnect, do a durable reopen (succeeds)
881 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
882 struct smb2_tree *tree)
884 NTSTATUS status;
885 TALLOC_CTX *mem_ctx = talloc_new(tctx);
886 char fname[256];
887 struct smb2_handle _h;
888 struct smb2_handle *h = NULL;
889 struct smb2_create io;
890 struct smb2_lease ls;
891 uint64_t lease_key;
892 bool ret = true;
893 struct smbcli_options options;
894 uint32_t caps;
896 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
897 if (!(caps & SMB2_CAP_LEASING)) {
898 torture_skip(tctx, "leases are not supported");
901 options = tree->session->transport->options;
903 /* Choose a random name in case the state is left a little funky. */
904 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
905 generate_random_str(tctx, 8));
907 smb2_util_unlink(tree, fname);
909 lease_key = random();
910 smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
911 smb2_util_lease_state("RWH"));
912 io.in.durable_open = true;
914 status = smb2_create(tree, mem_ctx, &io);
915 CHECK_STATUS(status, NT_STATUS_OK);
916 _h = io.out.file.handle;
917 h = &_h;
918 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
920 CHECK_VAL(io.out.durable_open, true);
921 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
922 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
923 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
924 CHECK_VAL(io.out.lease_response.lease_state,
925 smb2_util_lease_state("RWH"));
926 CHECK_VAL(io.out.lease_response.lease_flags, 0);
927 CHECK_VAL(io.out.lease_response.lease_duration, 0);
929 /* disconnect, reconnect and then do durable reopen */
930 TALLOC_FREE(tree);
932 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
933 torture_warning(tctx, "couldn't reconnect, bailing\n");
934 ret = false;
935 goto done;
939 /* a few failure tests: */
942 * several attempts without lease attached:
943 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
944 * irrespective of file name provided
947 ZERO_STRUCT(io);
948 io.in.fname = "";
949 io.in.durable_handle = h;
950 status = smb2_create(tree, mem_ctx, &io);
951 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
953 ZERO_STRUCT(io);
954 io.in.fname = "__non_existing_fname__";
955 io.in.durable_handle = h;
956 status = smb2_create(tree, mem_ctx, &io);
957 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
959 ZERO_STRUCT(io);
960 io.in.fname = fname;
961 io.in.durable_handle = h;
962 status = smb2_create(tree, mem_ctx, &io);
963 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
966 * attempt with lease provided, but
967 * with a changed lease key. => fails
969 ZERO_STRUCT(io);
970 io.in.fname = fname;
971 io.in.durable_open = false;
972 io.in.durable_handle = h;
973 io.in.lease_request = &ls;
974 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
975 /* a wrong lease key lets the request fail */
976 ls.lease_key.data[0]++;
978 status = smb2_create(tree, mem_ctx, &io);
979 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
981 /* restore the correct lease key */
982 ls.lease_key.data[0]--;
985 * this last failing attempt is almost correct:
986 * only problem is: we use the wrong filename...
987 * Note that this gives INVALID_PARAMETER.
988 * This is different from oplocks!
990 ZERO_STRUCT(io);
991 io.in.fname = "__non_existing_fname__";
992 io.in.durable_open = false;
993 io.in.durable_handle = h;
994 io.in.lease_request = &ls;
995 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
997 status = smb2_create(tree, mem_ctx, &io);
998 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1001 * Now for a succeeding reconnect:
1004 ZERO_STRUCT(io);
1005 io.in.fname = fname;
1006 io.in.durable_open = false;
1007 io.in.durable_handle = h;
1008 io.in.lease_request = &ls;
1009 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1011 /* the requested lease state is irrelevant */
1012 ls.lease_state = smb2_util_lease_state("");
1014 h = NULL;
1016 status = smb2_create(tree, mem_ctx, &io);
1017 CHECK_STATUS(status, NT_STATUS_OK);
1019 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1020 CHECK_VAL(io.out.durable_open, false);
1021 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1022 CHECK_VAL(io.out.persistent_open, false);
1023 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1024 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1025 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1026 CHECK_VAL(io.out.lease_response.lease_state,
1027 smb2_util_lease_state("RWH"));
1028 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1029 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1030 _h = io.out.file.handle;
1031 h = &_h;
1033 /* disconnect one more time */
1034 TALLOC_FREE(tree);
1036 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1037 torture_warning(tctx, "couldn't reconnect, bailing\n");
1038 ret = false;
1039 goto done;
1043 * demonstrate that various parameters are ignored
1044 * in the reconnect
1047 ZERO_STRUCT(io);
1049 * These are completely ignored by the server
1051 io.in.security_flags = 0x78;
1052 io.in.oplock_level = 0x78;
1053 io.in.impersonation_level = 0x12345678;
1054 io.in.create_flags = 0x12345678;
1055 io.in.reserved = 0x12345678;
1056 io.in.desired_access = 0x12345678;
1057 io.in.file_attributes = 0x12345678;
1058 io.in.share_access = 0x12345678;
1059 io.in.create_disposition = 0x12345678;
1060 io.in.create_options = 0x12345678;
1063 * only these are checked:
1064 * - io.in.fname
1065 * - io.in.durable_handle,
1066 * - io.in.lease_request->lease_key
1069 io.in.fname = fname;
1070 io.in.durable_open_v2 = false;
1071 io.in.durable_handle_v2 = h;
1072 io.in.lease_request = &ls;
1074 /* the requested lease state is irrelevant */
1075 ls.lease_state = smb2_util_lease_state("");
1077 h = NULL;
1079 status = smb2_create(tree, mem_ctx, &io);
1080 CHECK_STATUS(status, NT_STATUS_OK);
1082 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1083 CHECK_VAL(io.out.durable_open, false);
1084 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1085 CHECK_VAL(io.out.persistent_open, false);
1086 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1087 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1088 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1089 CHECK_VAL(io.out.lease_response.lease_state,
1090 smb2_util_lease_state("RWH"));
1091 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1092 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1094 _h = io.out.file.handle;
1095 h = &_h;
1097 done:
1098 if (tree != NULL) {
1099 if (h != NULL) {
1100 smb2_util_close(tree, *h);
1103 smb2_util_unlink(tree, fname);
1105 talloc_free(tree);
1108 talloc_free(mem_ctx);
1110 return ret;
1114 * lease v2 variant of reopen2
1115 * basic test for doing a durable open
1116 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1118 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
1119 struct smb2_tree *tree)
1121 NTSTATUS status;
1122 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1123 char fname[256];
1124 struct smb2_handle _h;
1125 struct smb2_handle *h = NULL;
1126 struct smb2_create io;
1127 struct smb2_lease ls;
1128 uint64_t lease_key;
1129 bool ret = true;
1130 struct smbcli_options options;
1131 uint32_t caps;
1133 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1134 if (!(caps & SMB2_CAP_LEASING)) {
1135 torture_skip(tctx, "leases are not supported");
1138 options = tree->session->transport->options;
1140 /* Choose a random name in case the state is left a little funky. */
1141 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1142 generate_random_str(tctx, 8));
1144 smb2_util_unlink(tree, fname);
1146 lease_key = random();
1147 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1148 lease_key, 0, /* parent lease key */
1149 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1150 io.in.durable_open = true;
1152 status = smb2_create(tree, mem_ctx, &io);
1153 CHECK_STATUS(status, NT_STATUS_OK);
1154 _h = io.out.file.handle;
1155 h = &_h;
1156 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1158 CHECK_VAL(io.out.durable_open, true);
1159 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1160 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1161 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1162 CHECK_VAL(io.out.lease_response_v2.lease_state,
1163 smb2_util_lease_state("RWH"));
1164 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1165 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1167 /* disconnect, reconnect and then do durable reopen */
1168 TALLOC_FREE(tree);
1170 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1171 torture_warning(tctx, "couldn't reconnect, bailing\n");
1172 ret = false;
1173 goto done;
1176 /* a few failure tests: */
1179 * several attempts without lease attached:
1180 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1181 * irrespective of file name provided
1184 ZERO_STRUCT(io);
1185 io.in.fname = "";
1186 io.in.durable_handle = h;
1187 status = smb2_create(tree, mem_ctx, &io);
1188 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1190 ZERO_STRUCT(io);
1191 io.in.fname = "__non_existing_fname__";
1192 io.in.durable_handle = h;
1193 status = smb2_create(tree, mem_ctx, &io);
1194 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1196 ZERO_STRUCT(io);
1197 io.in.fname = fname;
1198 io.in.durable_handle = h;
1199 status = smb2_create(tree, mem_ctx, &io);
1200 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1203 * attempt with lease provided, but
1204 * with a changed lease key. => fails
1206 ZERO_STRUCT(io);
1207 io.in.fname = fname;
1208 io.in.durable_open = false;
1209 io.in.durable_handle = h;
1210 io.in.lease_request_v2 = &ls;
1211 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1212 /* a wrong lease key lets the request fail */
1213 ls.lease_key.data[0]++;
1215 status = smb2_create(tree, mem_ctx, &io);
1216 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1218 /* restore the correct lease key */
1219 ls.lease_key.data[0]--;
1222 * this last failing attempt is almost correct:
1223 * only problem is: we use the wrong filename...
1224 * Note that this gives INVALID_PARAMETER.
1225 * This is different from oplocks!
1227 ZERO_STRUCT(io);
1228 io.in.fname = "__non_existing_fname__";
1229 io.in.durable_open = false;
1230 io.in.durable_handle = h;
1231 io.in.lease_request_v2 = &ls;
1232 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1234 status = smb2_create(tree, mem_ctx, &io);
1235 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1238 * Now for a succeeding reconnect:
1241 ZERO_STRUCT(io);
1242 io.in.fname = fname;
1243 io.in.durable_open = false;
1244 io.in.durable_handle = h;
1245 io.in.lease_request_v2 = &ls;
1246 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1248 /* the requested lease state is irrelevant */
1249 ls.lease_state = smb2_util_lease_state("");
1251 h = NULL;
1253 status = smb2_create(tree, mem_ctx, &io);
1254 CHECK_STATUS(status, NT_STATUS_OK);
1256 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1257 CHECK_VAL(io.out.durable_open, false);
1258 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1259 CHECK_VAL(io.out.persistent_open, false);
1260 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1261 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1262 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1263 CHECK_VAL(io.out.lease_response_v2.lease_state,
1264 smb2_util_lease_state("RWH"));
1265 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1266 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1267 _h = io.out.file.handle;
1268 h = &_h;
1270 /* disconnect one more time */
1271 TALLOC_FREE(tree);
1273 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1274 torture_warning(tctx, "couldn't reconnect, bailing\n");
1275 ret = false;
1276 goto done;
1280 * demonstrate that various parameters are ignored
1281 * in the reconnect
1284 ZERO_STRUCT(io);
1286 * These are completely ignored by the server
1288 io.in.security_flags = 0x78;
1289 io.in.oplock_level = 0x78;
1290 io.in.impersonation_level = 0x12345678;
1291 io.in.create_flags = 0x12345678;
1292 io.in.reserved = 0x12345678;
1293 io.in.desired_access = 0x12345678;
1294 io.in.file_attributes = 0x12345678;
1295 io.in.share_access = 0x12345678;
1296 io.in.create_disposition = 0x12345678;
1297 io.in.create_options = 0x12345678;
1300 * only these are checked:
1301 * - io.in.fname
1302 * - io.in.durable_handle,
1303 * - io.in.lease_request->lease_key
1306 io.in.fname = fname;
1307 io.in.durable_open_v2 = false;
1308 io.in.durable_handle_v2 = h;
1309 io.in.lease_request_v2 = &ls;
1311 /* the requested lease state is irrelevant */
1312 ls.lease_state = smb2_util_lease_state("");
1314 h = NULL;
1316 status = smb2_create(tree, mem_ctx, &io);
1317 CHECK_STATUS(status, NT_STATUS_OK);
1319 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1320 CHECK_VAL(io.out.durable_open, false);
1321 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1322 CHECK_VAL(io.out.persistent_open, false);
1323 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1324 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1325 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1326 CHECK_VAL(io.out.lease_response_v2.lease_state,
1327 smb2_util_lease_state("RWH"));
1328 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1329 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1331 _h = io.out.file.handle;
1332 h = &_h;
1334 done:
1335 if (tree != NULL) {
1336 if (h != NULL) {
1337 smb2_util_close(tree, *h);
1340 smb2_util_unlink(tree, fname);
1342 talloc_free(tree);
1345 talloc_free(mem_ctx);
1347 return ret;
1351 * basic test for doing a durable open
1352 * tcp disconnect, reconnect with a session reconnect and
1353 * do a durable reopen (succeeds)
1355 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1356 struct smb2_tree *tree)
1358 NTSTATUS status;
1359 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1360 char fname[256];
1361 struct smb2_handle _h;
1362 struct smb2_handle *h = NULL;
1363 struct smb2_create io1, io2;
1364 uint64_t previous_session_id;
1365 bool ret = true;
1366 struct smbcli_options options;
1368 options = tree->session->transport->options;
1370 /* Choose a random name in case the state is left a little funky. */
1371 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1372 generate_random_str(tctx, 8));
1374 smb2_util_unlink(tree, fname);
1376 smb2_oplock_create_share(&io1, fname,
1377 smb2_util_share_access(""),
1378 smb2_util_oplock_level("b"));
1379 io1.in.durable_open = true;
1381 status = smb2_create(tree, mem_ctx, &io1);
1382 CHECK_STATUS(status, NT_STATUS_OK);
1383 _h = io1.out.file.handle;
1384 h = &_h;
1385 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1386 CHECK_VAL(io1.out.durable_open, true);
1387 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1389 /* disconnect, reconnect and then do durable reopen */
1390 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1391 talloc_free(tree);
1392 tree = NULL;
1394 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1395 &options, &tree))
1397 torture_warning(tctx, "couldn't reconnect, bailing\n");
1398 ret = false;
1399 goto done;
1402 ZERO_STRUCT(io2);
1403 io2.in.fname = fname;
1404 io2.in.durable_handle = h;
1405 h = NULL;
1407 status = smb2_create(tree, mem_ctx, &io2);
1408 CHECK_STATUS(status, NT_STATUS_OK);
1409 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1410 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1411 _h = io2.out.file.handle;
1412 h = &_h;
1414 done:
1415 if (tree != NULL) {
1416 if (h != NULL) {
1417 smb2_util_close(tree, *h);
1420 smb2_util_unlink(tree, fname);
1422 talloc_free(tree);
1425 talloc_free(mem_ctx);
1427 return ret;
1432 * basic test for doing a durable open:
1433 * tdis, new tcon, try durable reopen (fails)
1435 static bool test_durable_open_reopen3(struct torture_context *tctx,
1436 struct smb2_tree *tree)
1438 NTSTATUS status;
1439 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1440 char fname[256];
1441 struct smb2_handle _h;
1442 struct smb2_handle *h = NULL;
1443 struct smb2_create io1, io2;
1444 bool ret = true;
1445 struct smb2_tree *tree2;
1447 /* Choose a random name in case the state is left a little funky. */
1448 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1449 generate_random_str(tctx, 8));
1451 smb2_util_unlink(tree, fname);
1453 smb2_oplock_create_share(&io1, fname,
1454 smb2_util_share_access(""),
1455 smb2_util_oplock_level("b"));
1456 io1.in.durable_open = true;
1458 status = smb2_create(tree, mem_ctx, &io1);
1459 CHECK_STATUS(status, NT_STATUS_OK);
1460 _h = io1.out.file.handle;
1461 h = &_h;
1462 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1463 CHECK_VAL(io1.out.durable_open, true);
1464 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1466 /* disconnect, reconnect and then do durable reopen */
1467 status = smb2_tdis(tree);
1468 CHECK_STATUS(status, NT_STATUS_OK);
1470 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1471 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1472 ret = false;
1473 goto done;
1477 ZERO_STRUCT(io2);
1478 io2.in.fname = fname;
1479 io2.in.durable_handle = h;
1481 status = smb2_create(tree2, mem_ctx, &io2);
1482 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1484 done:
1485 if (tree != NULL) {
1486 if (h != NULL) {
1487 smb2_util_close(tree, *h);
1490 smb2_util_unlink(tree2, fname);
1492 talloc_free(tree);
1495 talloc_free(mem_ctx);
1497 return ret;
1501 * basic test for doing a durable open:
1502 * logoff, create a new session, do a durable reopen (succeeds)
1504 static bool test_durable_open_reopen4(struct torture_context *tctx,
1505 struct smb2_tree *tree)
1507 NTSTATUS status;
1508 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1509 char fname[256];
1510 struct smb2_handle _h;
1511 struct smb2_handle *h = NULL;
1512 struct smb2_create io1, io2;
1513 bool ret = true;
1514 struct smb2_transport *transport;
1515 struct smb2_session *session2;
1516 struct smb2_tree *tree2;
1518 /* Choose a random name in case the state is left a little funky. */
1519 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1520 generate_random_str(tctx, 8));
1522 smb2_util_unlink(tree, fname);
1524 smb2_oplock_create_share(&io1, fname,
1525 smb2_util_share_access(""),
1526 smb2_util_oplock_level("b"));
1527 io1.in.durable_open = true;
1529 status = smb2_create(tree, mem_ctx, &io1);
1530 CHECK_STATUS(status, NT_STATUS_OK);
1531 _h = io1.out.file.handle;
1532 h = &_h;
1533 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1534 CHECK_VAL(io1.out.durable_open, true);
1535 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1538 * do a session logoff, establish a new session and tree
1539 * connect on the same transport, and try a durable reopen
1541 transport = tree->session->transport;
1542 status = smb2_logoff(tree->session);
1543 CHECK_STATUS(status, NT_STATUS_OK);
1545 if (!torture_smb2_session_setup(tctx, transport,
1546 0, /* previous_session_id */
1547 mem_ctx, &session2))
1549 torture_warning(tctx, "session setup failed.\n");
1550 ret = false;
1551 goto done;
1555 * the session setup has talloc-stolen the transport,
1556 * so we can safely free the old tree+session for clarity
1558 TALLOC_FREE(tree);
1560 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1561 torture_warning(tctx, "tree connect failed.\n");
1562 ret = false;
1563 goto done;
1566 ZERO_STRUCT(io2);
1567 io2.in.fname = fname;
1568 io2.in.durable_handle = h;
1569 h = NULL;
1571 status = smb2_create(tree2, mem_ctx, &io2);
1572 CHECK_STATUS(status, NT_STATUS_OK);
1574 _h = io2.out.file.handle;
1575 h = &_h;
1576 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1577 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1579 done:
1580 if (tree != NULL) {
1581 if (h != NULL) {
1582 smb2_util_close(tree2, *h);
1585 smb2_util_unlink(tree2, fname);
1587 talloc_free(tree);
1590 talloc_free(mem_ctx);
1592 return ret;
1595 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1596 struct smb2_tree *tree)
1598 NTSTATUS status;
1599 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1600 char fname[256];
1601 struct smb2_handle _h;
1602 struct smb2_handle *h = NULL;
1603 struct smb2_create io1, io2;
1604 bool ret = true;
1605 uint8_t b = 0;
1607 /* Choose a random name in case the state is left a little funky. */
1608 snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1609 generate_random_str(tctx, 8));
1611 smb2_util_unlink(tree, fname);
1613 smb2_oplock_create_share(&io1, fname,
1614 smb2_util_share_access(""),
1615 smb2_util_oplock_level("b"));
1616 io1.in.durable_open = true;
1617 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1619 status = smb2_create(tree, mem_ctx, &io1);
1620 CHECK_STATUS(status, NT_STATUS_OK);
1621 _h = io1.out.file.handle;
1622 h = &_h;
1623 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1624 CHECK_VAL(io1.out.durable_open, true);
1625 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1627 status = smb2_util_write(tree, *h, &b, 0, 1);
1628 CHECK_STATUS(status, NT_STATUS_OK);
1630 /* disconnect, leaving the durable handle in place */
1631 TALLOC_FREE(tree);
1633 if (!torture_smb2_connection(tctx, &tree)) {
1634 torture_warning(tctx, "could not reconnect, bailing\n");
1635 ret = false;
1636 goto done;
1640 * Open the file on the new connection again
1641 * and check that it has been newly created,
1642 * i.e. delete on close was effective on the disconnected handle.
1643 * Also check that the file is really empty,
1644 * the previously written byte gone.
1646 smb2_oplock_create_share(&io2, fname,
1647 smb2_util_share_access(""),
1648 smb2_util_oplock_level("b"));
1649 io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1651 status = smb2_create(tree, mem_ctx, &io2);
1652 CHECK_STATUS(status, NT_STATUS_OK);
1653 _h = io2.out.file.handle;
1654 h = &_h;
1655 CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1656 CHECK_VAL(io2.out.durable_open, false);
1657 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1659 done:
1660 if (tree != NULL) {
1661 if (h != NULL) {
1662 smb2_util_close(tree, *h);
1665 smb2_util_unlink(tree, fname);
1667 talloc_free(tree);
1670 talloc_free(mem_ctx);
1672 return ret;
1676 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1677 struct smb2_tree *tree)
1679 NTSTATUS status;
1680 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1681 char fname[256];
1682 struct smb2_handle _h;
1683 struct smb2_handle *h = NULL;
1684 struct smb2_create io;
1685 bool ret = true;
1686 uint8_t b = 0;
1687 uint64_t previous_session_id;
1688 uint64_t alloc_size_step;
1689 struct smbcli_options options;
1691 options = tree->session->transport->options;
1693 /* Choose a random name in case the state is left a little funky. */
1694 snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1695 generate_random_str(tctx, 8));
1697 smb2_util_unlink(tree, fname);
1699 smb2_oplock_create_share(&io, fname,
1700 smb2_util_share_access(""),
1701 smb2_util_oplock_level("b"));
1702 io.in.durable_open = true;
1703 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1705 status = smb2_create(tree, mem_ctx, &io);
1706 CHECK_STATUS(status, NT_STATUS_OK);
1707 _h = io.out.file.handle;
1708 h = &_h;
1709 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1710 CHECK_VAL(io.out.durable_open, true);
1711 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1713 status = smb2_util_write(tree, *h, &b, 0, 1);
1714 CHECK_STATUS(status, NT_STATUS_OK);
1716 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1718 /* disconnect, leaving the durable handle in place */
1719 TALLOC_FREE(tree);
1721 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1722 &options, &tree))
1724 torture_warning(tctx, "could not reconnect, bailing\n");
1725 ret = false;
1726 goto done;
1729 ZERO_STRUCT(io);
1730 io.in.fname = fname;
1731 io.in.durable_handle = h;
1733 status = smb2_create(tree, mem_ctx, &io);
1734 CHECK_STATUS(status, NT_STATUS_OK);
1735 _h = io.out.file.handle;
1736 h = &_h;
1737 alloc_size_step = io.out.alloc_size;
1738 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1739 CHECK_VAL(io.out.durable_open, false);
1740 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1742 /* close the file, thereby deleting it */
1743 smb2_util_close(tree, *h);
1744 status = smb2_logoff(tree->session);
1745 TALLOC_FREE(tree);
1747 if (!torture_smb2_connection(tctx, &tree)) {
1748 torture_warning(tctx, "could not reconnect, bailing\n");
1749 ret = false;
1750 goto done;
1754 * Open the file on the new connection again
1755 * and check that it has been newly created,
1756 * i.e. delete on close was effective on the reconnected handle.
1757 * Also check that the file is really empty,
1758 * the previously written byte gone.
1760 smb2_oplock_create_share(&io, fname,
1761 smb2_util_share_access(""),
1762 smb2_util_oplock_level("b"));
1763 io.in.durable_open = true;
1764 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1766 status = smb2_create(tree, mem_ctx, &io);
1767 CHECK_STATUS(status, NT_STATUS_OK);
1768 _h = io.out.file.handle;
1769 h = &_h;
1770 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1771 CHECK_VAL(io.out.durable_open, true);
1772 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1774 done:
1775 if (tree != NULL) {
1776 if (h != NULL) {
1777 smb2_util_close(tree, *h);
1780 smb2_util_unlink(tree, fname);
1782 talloc_free(tree);
1785 talloc_free(mem_ctx);
1787 return ret;
1791 basic testing of SMB2 durable opens
1792 regarding the position information on the handle
1794 static bool test_durable_open_file_position(struct torture_context *tctx,
1795 struct smb2_tree *tree)
1797 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1798 struct smb2_handle h;
1799 struct smb2_create io;
1800 NTSTATUS status;
1801 const char *fname = "durable_open_position.dat";
1802 union smb_fileinfo qfinfo;
1803 union smb_setfileinfo sfinfo;
1804 bool ret = true;
1805 uint64_t pos;
1806 uint64_t previous_session_id;
1807 struct smbcli_options options;
1809 options = tree->session->transport->options;
1811 smb2_util_unlink(tree, fname);
1813 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1814 io.in.durable_open = true;
1816 status = smb2_create(tree, mem_ctx, &io);
1817 CHECK_STATUS(status, NT_STATUS_OK);
1818 h = io.out.file.handle;
1819 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1820 CHECK_VAL(io.out.durable_open, true);
1821 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1823 /* TODO: check extra blob content */
1825 ZERO_STRUCT(qfinfo);
1826 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1827 qfinfo.generic.in.file.handle = h;
1828 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1829 CHECK_STATUS(status, NT_STATUS_OK);
1830 CHECK_VAL(qfinfo.position_information.out.position, 0);
1831 pos = qfinfo.position_information.out.position;
1832 torture_comment(tctx, "position: %llu\n",
1833 (unsigned long long)pos);
1835 ZERO_STRUCT(sfinfo);
1836 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1837 sfinfo.generic.in.file.handle = h;
1838 sfinfo.position_information.in.position = 0x1000;
1839 status = smb2_setinfo_file(tree, &sfinfo);
1840 CHECK_STATUS(status, NT_STATUS_OK);
1842 ZERO_STRUCT(qfinfo);
1843 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1844 qfinfo.generic.in.file.handle = h;
1845 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1846 CHECK_STATUS(status, NT_STATUS_OK);
1847 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1848 pos = qfinfo.position_information.out.position;
1849 torture_comment(tctx, "position: %llu\n",
1850 (unsigned long long)pos);
1852 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1854 /* tcp disconnect */
1855 talloc_free(tree);
1856 tree = NULL;
1858 /* do a session reconnect */
1859 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1860 &options, &tree))
1862 torture_warning(tctx, "couldn't reconnect, bailing\n");
1863 ret = false;
1864 goto done;
1867 ZERO_STRUCT(qfinfo);
1868 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1869 qfinfo.generic.in.file.handle = h;
1870 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1871 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1873 ZERO_STRUCT(io);
1874 io.in.fname = fname;
1875 io.in.durable_handle = &h;
1877 status = smb2_create(tree, mem_ctx, &io);
1878 CHECK_STATUS(status, NT_STATUS_OK);
1879 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1880 CHECK_VAL(io.out.reserved, 0x00);
1881 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1882 CHECK_VAL(io.out.alloc_size, 0);
1883 CHECK_VAL(io.out.size, 0);
1884 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1885 CHECK_VAL(io.out.reserved2, 0);
1887 h = io.out.file.handle;
1889 ZERO_STRUCT(qfinfo);
1890 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1891 qfinfo.generic.in.file.handle = h;
1892 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1893 CHECK_STATUS(status, NT_STATUS_OK);
1894 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1895 pos = qfinfo.position_information.out.position;
1896 torture_comment(tctx, "position: %llu\n",
1897 (unsigned long long)pos);
1899 smb2_util_close(tree, h);
1901 talloc_free(mem_ctx);
1903 smb2_util_unlink(tree, fname);
1905 done:
1906 talloc_free(tree);
1908 return ret;
1912 Open, disconnect, oplock break, reconnect.
1914 static bool test_durable_open_oplock(struct torture_context *tctx,
1915 struct smb2_tree *tree1,
1916 struct smb2_tree *tree2)
1918 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1919 struct smb2_create io1, io2;
1920 struct smb2_handle h1 = {{0}};
1921 struct smb2_handle h2 = {{0}};
1922 NTSTATUS status;
1923 char fname[256];
1924 bool ret = true;
1926 /* Choose a random name in case the state is left a little funky. */
1927 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1929 /* Clean slate */
1930 smb2_util_unlink(tree1, fname);
1932 /* Create with batch oplock */
1933 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1934 io1.in.durable_open = true;
1936 io2 = io1;
1937 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1939 status = smb2_create(tree1, mem_ctx, &io1);
1940 CHECK_STATUS(status, NT_STATUS_OK);
1941 h1 = io1.out.file.handle;
1942 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1943 CHECK_VAL(io1.out.durable_open, true);
1944 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1946 /* Disconnect after getting the batch */
1947 talloc_free(tree1);
1948 tree1 = NULL;
1951 * Windows7 (build 7000) will break a batch oplock immediately if the
1952 * original client is gone. (ZML: This seems like a bug. It should give
1953 * some time for the client to reconnect!)
1955 status = smb2_create(tree2, mem_ctx, &io2);
1956 CHECK_STATUS(status, NT_STATUS_OK);
1957 h2 = io2.out.file.handle;
1958 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1959 CHECK_VAL(io2.out.durable_open, true);
1960 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1962 /* What if tree1 tries to come back and reclaim? */
1963 if (!torture_smb2_connection(tctx, &tree1)) {
1964 torture_warning(tctx, "couldn't reconnect, bailing\n");
1965 ret = false;
1966 goto done;
1969 ZERO_STRUCT(io1);
1970 io1.in.fname = fname;
1971 io1.in.durable_handle = &h1;
1973 status = smb2_create(tree1, mem_ctx, &io1);
1974 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1976 done:
1977 smb2_util_close(tree2, h2);
1978 smb2_util_unlink(tree2, fname);
1980 talloc_free(tree1);
1981 talloc_free(tree2);
1983 return ret;
1987 Open, disconnect, lease break, reconnect.
1989 static bool test_durable_open_lease(struct torture_context *tctx,
1990 struct smb2_tree *tree1,
1991 struct smb2_tree *tree2)
1993 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1994 struct smb2_create io1, io2;
1995 struct smb2_lease ls1, ls2;
1996 struct smb2_handle h1 = {{0}};
1997 struct smb2_handle h2 = {{0}};
1998 NTSTATUS status;
1999 char fname[256];
2000 bool ret = true;
2001 uint64_t lease1, lease2;
2002 uint32_t caps;
2004 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2005 if (!(caps & SMB2_CAP_LEASING)) {
2006 torture_skip(tctx, "leases are not supported");
2010 * Choose a random name and random lease in case the state is left a
2011 * little funky.
2013 lease1 = random();
2014 lease2 = random();
2015 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
2017 /* Clean slate */
2018 smb2_util_unlink(tree1, fname);
2020 /* Create with lease */
2021 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
2022 lease1, smb2_util_lease_state("RHW"));
2023 io1.in.durable_open = true;
2025 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
2026 lease2, smb2_util_lease_state("RHW"));
2027 io2.in.durable_open = true;
2028 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
2030 status = smb2_create(tree1, mem_ctx, &io1);
2031 CHECK_STATUS(status, NT_STATUS_OK);
2032 h1 = io1.out.file.handle;
2033 CHECK_VAL(io1.out.durable_open, true);
2034 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2036 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2037 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
2038 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
2039 CHECK_VAL(io1.out.lease_response.lease_state,
2040 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2042 /* Disconnect after getting the lease */
2043 talloc_free(tree1);
2044 tree1 = NULL;
2047 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
2048 * even if the original client is gone. (ZML: This seems like a bug. It
2049 * should give some time for the client to reconnect! And why RH?)
2051 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
2052 * Test is adapted accordingly.
2054 status = smb2_create(tree2, mem_ctx, &io2);
2055 CHECK_STATUS(status, NT_STATUS_OK);
2056 h2 = io2.out.file.handle;
2057 CHECK_VAL(io2.out.durable_open, true);
2058 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2060 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2061 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
2062 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
2063 CHECK_VAL(io2.out.lease_response.lease_state,
2064 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2066 /* What if tree1 tries to come back and reclaim? */
2067 if (!torture_smb2_connection(tctx, &tree1)) {
2068 torture_warning(tctx, "couldn't reconnect, bailing\n");
2069 ret = false;
2070 goto done;
2073 ZERO_STRUCT(io1);
2074 io1.in.fname = fname;
2075 io1.in.durable_handle = &h1;
2076 io1.in.lease_request = &ls1;
2078 status = smb2_create(tree1, mem_ctx, &io1);
2079 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2081 done:
2082 smb2_util_close(tree2, h2);
2083 smb2_util_unlink(tree2, fname);
2085 talloc_free(tree1);
2086 talloc_free(tree2);
2088 return ret;
2091 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
2092 struct smb2_tree *tree)
2094 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2095 struct smb2_create io;
2096 struct smb2_handle h = {{0}};
2097 struct smb2_lock lck;
2098 struct smb2_lock_element el[2];
2099 NTSTATUS status;
2100 char fname[256];
2101 bool ret = true;
2105 snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
2107 /* Clean slate */
2108 smb2_util_unlink(tree, fname);
2110 /* Create with oplock */
2112 smb2_oplock_create_share(&io, fname,
2113 smb2_util_share_access(""),
2114 smb2_util_oplock_level("b"));
2115 io.in.durable_open = true;
2117 status = smb2_create(tree, mem_ctx, &io);
2118 CHECK_STATUS(status, NT_STATUS_OK);
2119 h = io.out.file.handle;
2120 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2122 CHECK_VAL(io.out.durable_open, true);
2123 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2125 ZERO_STRUCT(lck);
2126 ZERO_STRUCT(el);
2127 lck.in.locks = el;
2128 lck.in.lock_count = 0x0001;
2129 lck.in.lock_sequence = 0x00000000;
2130 lck.in.file.handle = h;
2131 el[0].offset = 0;
2132 el[0].length = 1;
2133 el[0].reserved = 0x00000000;
2134 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2135 status = smb2_lock(tree, &lck);
2136 CHECK_STATUS(status, NT_STATUS_OK);
2138 /* Disconnect/Reconnect. */
2139 talloc_free(tree);
2140 tree = NULL;
2142 if (!torture_smb2_connection(tctx, &tree)) {
2143 torture_warning(tctx, "couldn't reconnect, bailing\n");
2144 ret = false;
2145 goto done;
2148 ZERO_STRUCT(io);
2149 io.in.fname = fname;
2150 io.in.durable_handle = &h;
2152 status = smb2_create(tree, mem_ctx, &io);
2153 CHECK_STATUS(status, NT_STATUS_OK);
2154 h = io.out.file.handle;
2156 lck.in.file.handle = h;
2157 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2158 status = smb2_lock(tree, &lck);
2159 CHECK_STATUS(status, NT_STATUS_OK);
2161 done:
2162 smb2_util_close(tree, h);
2163 smb2_util_unlink(tree, fname);
2164 talloc_free(tree);
2166 return ret;
2170 Open, take BRL, disconnect, reconnect.
2172 static bool test_durable_open_lock_lease(struct torture_context *tctx,
2173 struct smb2_tree *tree)
2175 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2176 struct smb2_create io;
2177 struct smb2_lease ls;
2178 struct smb2_handle h = {{0}};
2179 struct smb2_lock lck;
2180 struct smb2_lock_element el[2];
2181 NTSTATUS status;
2182 char fname[256];
2183 bool ret = true;
2184 uint64_t lease;
2185 uint32_t caps;
2186 struct smbcli_options options;
2188 options = tree->session->transport->options;
2190 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2191 if (!(caps & SMB2_CAP_LEASING)) {
2192 torture_skip(tctx, "leases are not supported");
2196 * Choose a random name and random lease in case the state is left a
2197 * little funky.
2199 lease = random();
2200 snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2202 /* Clean slate */
2203 smb2_util_unlink(tree, fname);
2205 /* Create with lease */
2207 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2208 smb2_util_lease_state("RWH"));
2209 io.in.durable_open = true;
2211 status = smb2_create(tree, mem_ctx, &io);
2212 CHECK_STATUS(status, NT_STATUS_OK);
2213 h = io.out.file.handle;
2214 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2216 CHECK_VAL(io.out.durable_open, true);
2217 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2218 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2219 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2220 CHECK_VAL(io.out.lease_response.lease_state,
2221 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2223 ZERO_STRUCT(lck);
2224 ZERO_STRUCT(el);
2225 lck.in.locks = el;
2226 lck.in.lock_count = 0x0001;
2227 lck.in.lock_sequence = 0x00000000;
2228 lck.in.file.handle = h;
2229 el[0].offset = 0;
2230 el[0].length = 1;
2231 el[0].reserved = 0x00000000;
2232 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2233 status = smb2_lock(tree, &lck);
2234 CHECK_STATUS(status, NT_STATUS_OK);
2236 /* Disconnect/Reconnect. */
2237 talloc_free(tree);
2238 tree = NULL;
2240 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2241 torture_warning(tctx, "couldn't reconnect, bailing\n");
2242 ret = false;
2243 goto done;
2246 ZERO_STRUCT(io);
2247 io.in.fname = fname;
2248 io.in.durable_handle = &h;
2249 io.in.lease_request = &ls;
2251 status = smb2_create(tree, mem_ctx, &io);
2252 CHECK_STATUS(status, NT_STATUS_OK);
2253 h = io.out.file.handle;
2255 lck.in.file.handle = h;
2256 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2257 status = smb2_lock(tree, &lck);
2258 CHECK_STATUS(status, NT_STATUS_OK);
2260 done:
2261 smb2_util_close(tree, h);
2262 smb2_util_unlink(tree, fname);
2263 talloc_free(tree);
2265 return ret;
2269 * Open with a RH lease, disconnect, open in another tree, reconnect.
2271 * This test actually demonstrates a minimum level of respect for the durable
2272 * open in the face of another open. As long as this test shows an inability to
2273 * reconnect after an open, the oplock/lease tests above will certainly
2274 * demonstrate an error on reconnect.
2276 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2277 struct smb2_tree *tree1,
2278 struct smb2_tree *tree2)
2280 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2281 struct smb2_create io1, io2;
2282 struct smb2_lease ls;
2283 struct smb2_handle h1 = {{0}};
2284 struct smb2_handle h2 = {{0}};
2285 NTSTATUS status;
2286 char fname[256];
2287 bool ret = true;
2288 uint64_t lease;
2289 uint32_t caps;
2290 struct smbcli_options options;
2292 options = tree1->session->transport->options;
2294 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2295 if (!(caps & SMB2_CAP_LEASING)) {
2296 torture_skip(tctx, "leases are not supported");
2300 * Choose a random name and random lease in case the state is left a
2301 * little funky.
2303 lease = random();
2304 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2305 generate_random_str(tctx, 8));
2307 /* Clean slate */
2308 smb2_util_unlink(tree1, fname);
2310 /* Create with lease */
2311 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2312 smb2_util_share_access(""),
2313 lease,
2314 smb2_util_lease_state("RH"));
2315 io1.in.durable_open = true;
2317 status = smb2_create(tree1, mem_ctx, &io1);
2318 CHECK_STATUS(status, NT_STATUS_OK);
2319 h1 = io1.out.file.handle;
2320 CHECK_VAL(io1.out.durable_open, true);
2321 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2323 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2324 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2325 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2326 CHECK_VAL(io1.out.lease_response.lease_state,
2327 smb2_util_lease_state("RH"));
2329 /* Disconnect */
2330 talloc_free(tree1);
2331 tree1 = NULL;
2333 /* Open the file in tree2 */
2334 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2336 status = smb2_create(tree2, mem_ctx, &io2);
2337 CHECK_STATUS(status, NT_STATUS_OK);
2338 h2 = io2.out.file.handle;
2339 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2341 /* Reconnect */
2342 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2343 torture_warning(tctx, "couldn't reconnect, bailing\n");
2344 ret = false;
2345 goto done;
2348 ZERO_STRUCT(io1);
2349 io1.in.fname = fname;
2350 io1.in.durable_handle = &h1;
2351 io1.in.lease_request = &ls;
2354 * Windows7 (build 7000) will give away an open immediately if the
2355 * original client is gone. (ZML: This seems like a bug. It should give
2356 * some time for the client to reconnect!)
2358 status = smb2_create(tree1, mem_ctx, &io1);
2359 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2360 h1 = io1.out.file.handle;
2362 done:
2363 if (tree1 != NULL){
2364 smb2_util_close(tree1, h1);
2365 smb2_util_unlink(tree1, fname);
2366 talloc_free(tree1);
2369 smb2_util_close(tree2, h2);
2370 smb2_util_unlink(tree2, fname);
2371 talloc_free(tree2);
2373 return ret;
2377 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2379 * This test actually demonstrates a minimum level of respect for the durable
2380 * open in the face of another open. As long as this test shows an inability to
2381 * reconnect after an open, the oplock/lease tests above will certainly
2382 * demonstrate an error on reconnect.
2384 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2385 struct smb2_tree *tree1,
2386 struct smb2_tree *tree2)
2388 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2389 struct smb2_create io1, io2;
2390 struct smb2_handle h1 = {{0}};
2391 struct smb2_handle h2 = {{0}};
2392 NTSTATUS status;
2393 char fname[256];
2394 bool ret = true;
2397 * Choose a random name and random lease in case the state is left a
2398 * little funky.
2400 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2401 generate_random_str(tctx, 8));
2403 /* Clean slate */
2404 smb2_util_unlink(tree1, fname);
2406 /* Create with batch oplock */
2407 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2408 io1.in.durable_open = true;
2410 status = smb2_create(tree1, mem_ctx, &io1);
2411 CHECK_STATUS(status, NT_STATUS_OK);
2412 h1 = io1.out.file.handle;
2413 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2414 CHECK_VAL(io1.out.durable_open, true);
2415 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2417 /* Disconnect */
2418 talloc_free(tree1);
2419 tree1 = NULL;
2421 /* Open the file in tree2 */
2422 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2424 status = smb2_create(tree2, mem_ctx, &io2);
2425 CHECK_STATUS(status, NT_STATUS_OK);
2426 h2 = io2.out.file.handle;
2427 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2429 /* Reconnect */
2430 if (!torture_smb2_connection(tctx, &tree1)) {
2431 torture_warning(tctx, "couldn't reconnect, bailing\n");
2432 ret = false;
2433 goto done;
2436 ZERO_STRUCT(io1);
2437 io1.in.fname = fname;
2438 io1.in.durable_handle = &h1;
2440 status = smb2_create(tree1, mem_ctx, &io1);
2441 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2442 h1 = io1.out.file.handle;
2444 done:
2445 smb2_util_close(tree2, h2);
2446 smb2_util_unlink(tree2, fname);
2447 if (tree1 != NULL) {
2448 smb2_util_close(tree1, h1);
2449 smb2_util_unlink(tree1, fname);
2452 talloc_free(tree1);
2453 talloc_free(tree2);
2455 return ret;
2459 * test behaviour with initial allocation size
2461 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2462 struct smb2_tree *tree)
2464 NTSTATUS status;
2465 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2466 char fname[256];
2467 struct smb2_handle _h;
2468 struct smb2_handle *h = NULL;
2469 struct smb2_create io;
2470 bool ret = true;
2471 uint64_t previous_session_id;
2472 uint64_t alloc_size_step;
2473 uint64_t initial_alloc_size = 0x100;
2474 const uint8_t *b = NULL;
2475 struct smbcli_options options;
2477 options = tree->session->transport->options;
2479 /* Choose a random name in case the state is left a little funky. */
2480 snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2481 generate_random_str(tctx, 8));
2483 smb2_util_unlink(tree, fname);
2485 smb2_oplock_create_share(&io, fname,
2486 smb2_util_share_access(""),
2487 smb2_util_oplock_level("b"));
2488 io.in.durable_open = true;
2489 io.in.alloc_size = initial_alloc_size;
2491 status = smb2_create(tree, mem_ctx, &io);
2492 CHECK_STATUS(status, NT_STATUS_OK);
2493 _h = io.out.file.handle;
2494 h = &_h;
2495 CHECK_NOT_VAL(io.out.alloc_size, 0);
2496 alloc_size_step = io.out.alloc_size;
2497 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2498 alloc_size_step, 0);
2499 CHECK_VAL(io.out.durable_open, true);
2500 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2502 /* prepare buffer */
2503 b = talloc_zero_size(mem_ctx, alloc_size_step);
2504 CHECK_NOT_NULL(b);
2506 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2508 /* disconnect, reconnect and then do durable reopen */
2509 talloc_free(tree);
2510 tree = NULL;
2512 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2513 &options, &tree))
2515 torture_warning(tctx, "couldn't reconnect, bailing\n");
2516 ret = false;
2517 goto done;
2520 ZERO_STRUCT(io);
2521 io.in.fname = fname;
2522 io.in.durable_handle = h;
2523 h = NULL;
2525 status = smb2_create(tree, mem_ctx, &io);
2526 CHECK_STATUS(status, NT_STATUS_OK);
2527 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2528 alloc_size_step, 0);
2529 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2530 _h = io.out.file.handle;
2531 h = &_h;
2533 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2535 /* write one byte */
2536 status = smb2_util_write(tree, *h, b, 0, 1);
2537 CHECK_STATUS(status, NT_STATUS_OK);
2539 /* disconnect, reconnect and then do durable reopen */
2540 talloc_free(tree);
2541 tree = NULL;
2543 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2544 &options, &tree))
2546 torture_warning(tctx, "couldn't reconnect, bailing\n");
2547 ret = false;
2548 goto done;
2551 ZERO_STRUCT(io);
2552 io.in.fname = fname;
2553 io.in.durable_handle = h;
2554 h = NULL;
2556 status = smb2_create(tree, mem_ctx, &io);
2557 CHECK_STATUS(status, NT_STATUS_OK);
2558 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2559 alloc_size_step, 1);
2560 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2561 _h = io.out.file.handle;
2562 h = &_h;
2564 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2566 /* write more byte than initial allocation size */
2567 status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2569 /* disconnect, reconnect and then do durable reopen */
2570 talloc_free(tree);
2571 tree = NULL;
2573 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2574 &options, &tree))
2576 torture_warning(tctx, "couldn't reconnect, bailing\n");
2577 ret = false;
2578 goto done;
2581 ZERO_STRUCT(io);
2582 io.in.fname = fname;
2583 io.in.durable_handle = h;
2584 h = NULL;
2586 status = smb2_create(tree, mem_ctx, &io);
2587 CHECK_STATUS(status, NT_STATUS_OK);
2588 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2589 alloc_size_step * 2, alloc_size_step + 1);
2590 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2591 _h = io.out.file.handle;
2592 h = &_h;
2594 done:
2595 if (h != NULL) {
2596 smb2_util_close(tree, *h);
2599 smb2_util_unlink(tree, fname);
2601 talloc_free(tree);
2603 talloc_free(mem_ctx);
2605 return ret;
2609 * test behaviour when a disconnect happens while creating a read-only file
2611 static bool test_durable_open_read_only(struct torture_context *tctx,
2612 struct smb2_tree *tree)
2614 NTSTATUS status;
2615 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2616 char fname[256];
2617 struct smb2_handle _h;
2618 struct smb2_handle *h = NULL;
2619 struct smb2_create io;
2620 bool ret = true;
2621 uint64_t previous_session_id;
2622 const uint8_t b = 0;
2623 uint64_t alloc_size = 0;
2624 struct smbcli_options options;
2626 options = tree->session->transport->options;
2628 /* Choose a random name in case the state is left a little funky. */
2629 snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2630 generate_random_str(tctx, 8));
2632 smb2_util_unlink(tree, fname);
2634 smb2_oplock_create_share(&io, fname,
2635 smb2_util_share_access(""),
2636 smb2_util_oplock_level("b"));
2637 io.in.durable_open = true;
2638 io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2640 status = smb2_create(tree, mem_ctx, &io);
2641 CHECK_STATUS(status, NT_STATUS_OK);
2642 _h = io.out.file.handle;
2643 h = &_h;
2644 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2645 CHECK_VAL(io.out.durable_open, true);
2646 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2648 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2650 /* write one byte */
2651 status = smb2_util_write(tree, *h, &b, 0, 1);
2652 CHECK_STATUS(status, NT_STATUS_OK);
2654 /* disconnect, reconnect and then do durable reopen */
2655 talloc_free(tree);
2656 tree = NULL;
2658 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2659 &options, &tree))
2661 torture_warning(tctx, "couldn't reconnect, bailing\n");
2662 ret = false;
2663 goto done;
2666 ZERO_STRUCT(io);
2667 io.in.fname = fname;
2668 io.in.durable_handle = h;
2669 h = NULL;
2671 status = smb2_create(tree, mem_ctx, &io);
2672 CHECK_STATUS(status, NT_STATUS_OK);
2673 alloc_size = io.out.alloc_size;
2674 CHECK_CREATED_SIZE(&io, EXISTED,
2675 FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2676 alloc_size, 1);
2677 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2678 _h = io.out.file.handle;
2679 h = &_h;
2681 /* write one byte */
2682 status = smb2_util_write(tree, *h, &b, 1, 1);
2683 CHECK_STATUS(status, NT_STATUS_OK);
2685 done:
2686 if (h != NULL) {
2687 union smb_setfileinfo sfinfo;
2689 ZERO_STRUCT(sfinfo);
2690 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2691 sfinfo.basic_info.in.file.handle = *h;
2692 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2693 smb2_setinfo_file(tree, &sfinfo);
2695 smb2_util_close(tree, *h);
2698 smb2_util_unlink(tree, fname);
2700 talloc_free(tree);
2702 talloc_free(mem_ctx);
2704 return ret;
2708 * durable open with oplock, disconnect, exit
2710 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2711 struct smb2_tree *tree)
2713 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2714 struct smb2_create io;
2715 struct smb2_handle _h;
2716 struct smb2_handle *h = NULL;
2717 NTSTATUS status;
2718 char fname[256];
2719 bool ret = true;
2721 snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2722 generate_random_str(mem_ctx, 8));
2724 smb2_util_unlink(tree, fname);
2726 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2727 io.in.durable_open = true;
2729 status = smb2_create(tree, mem_ctx, &io);
2730 CHECK_STATUS(status, NT_STATUS_OK);
2732 _h = io.out.file.handle;
2733 h = &_h;
2735 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2736 CHECK_VAL(io.out.durable_open, true);
2737 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2739 /* disconnect */
2740 talloc_free(tree);
2741 tree = NULL;
2743 done:
2744 if (tree != NULL) {
2745 if (h != NULL) {
2746 smb2_util_close(tree, *h);
2748 smb2_util_unlink(tree, fname);
2750 talloc_free(mem_ctx);
2751 return ret;
2755 struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
2757 struct torture_suite *suite =
2758 torture_suite_create(ctx, "durable-open");
2760 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2761 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2762 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2763 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2764 torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
2765 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2766 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2767 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2768 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2769 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2770 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2771 torture_suite_add_1smb2_test(suite, "delete_on_close1",
2772 test_durable_open_delete_on_close1);
2773 torture_suite_add_1smb2_test(suite, "delete_on_close2",
2774 test_durable_open_delete_on_close2);
2775 torture_suite_add_1smb2_test(suite, "file-position",
2776 test_durable_open_file_position);
2777 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2778 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2779 torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2780 torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2781 torture_suite_add_2smb2_test(suite, "open2-lease",
2782 test_durable_open_open2_lease);
2783 torture_suite_add_2smb2_test(suite, "open2-oplock",
2784 test_durable_open_open2_oplock);
2785 torture_suite_add_1smb2_test(suite, "alloc-size",
2786 test_durable_open_alloc_size);
2787 torture_suite_add_1smb2_test(suite, "read-only",
2788 test_durable_open_read_only);
2790 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2792 return suite;
2795 struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
2797 struct torture_suite *suite =
2798 torture_suite_create(ctx,
2799 "durable-open-disconnect");
2801 torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2802 test_durable_open_oplock_disconnect);
2804 suite->description = talloc_strdup(suite,
2805 "SMB2-DURABLE-OPEN-DISCONNECT tests");
2807 return suite;