libads: Make ads_cldap_netlogon() static
[samba4-gss.git] / source4 / torture / smb2 / durable_open.c
blobb730fab3c29fa014e88ce2508fa572fda3ffa8cc
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) \
32 torture_assert_u64_equal_goto(tctx, v, correct, ret, done, __location__)
34 #define CHECK_NOT_VAL(v, incorrect) \
35 torture_assert_u64_not_equal_goto(tctx, v, incorrect, ret, done, __location__)
37 #define CHECK_NOT_NULL(p) \
38 torture_assert_not_null_goto(tctx, p, ret, done, __location__)
40 #define CHECK_STATUS(status, correct) \
41 torture_assert_ntstatus_equal_goto(tctx, status, correct, ret, done, __location__)
43 #define CHECK_CREATED(__io, __created, __attribute) \
44 do { \
45 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46 CHECK_VAL((__io)->out.size, 0); \
47 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
48 CHECK_VAL((__io)->out.reserved2, 0); \
49 } while(0)
51 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
52 do { \
53 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
54 if (__alloc_size != 0) { \
55 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
56 } \
57 CHECK_VAL((__io)->out.size, (__size)); \
58 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
59 CHECK_VAL((__io)->out.reserved2, 0); \
60 } while(0)
64 /**
65 * basic durable_open test.
66 * durable state should only be granted when requested
67 * along with a batch oplock or a handle lease.
69 * This test tests durable open with all possible oplock types.
72 struct durable_open_vs_oplock {
73 const char *level;
74 const char *share_mode;
75 bool expected;
78 #define NUM_OPLOCK_TYPES 4
79 #define NUM_SHARE_MODES 8
80 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
81 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
83 { "", "", false },
84 { "", "R", false },
85 { "", "W", false },
86 { "", "D", false },
87 { "", "RD", false },
88 { "", "RW", false },
89 { "", "WD", false },
90 { "", "RWD", false },
92 { "s", "", false },
93 { "s", "R", false },
94 { "s", "W", false },
95 { "s", "D", false },
96 { "s", "RD", false },
97 { "s", "RW", false },
98 { "s", "WD", false },
99 { "s", "RWD", false },
101 { "x", "", false },
102 { "x", "R", false },
103 { "x", "W", false },
104 { "x", "D", false },
105 { "x", "RD", false },
106 { "x", "RW", false },
107 { "x", "WD", false },
108 { "x", "RWD", false },
110 { "b", "", true },
111 { "b", "R", true },
112 { "b", "W", true },
113 { "b", "D", true },
114 { "b", "RD", true },
115 { "b", "RW", true },
116 { "b", "WD", true },
117 { "b", "RWD", true },
120 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
121 struct smb2_tree *tree,
122 const char *fname,
123 struct durable_open_vs_oplock test)
125 NTSTATUS status;
126 TALLOC_CTX *mem_ctx = talloc_new(tctx);
127 struct smb2_handle _h;
128 struct smb2_handle *h = NULL;
129 bool ret = true;
130 struct smb2_create io;
132 smb2_util_unlink(tree, fname);
134 smb2_oplock_create_share(&io, fname,
135 smb2_util_share_access(test.share_mode),
136 smb2_util_oplock_level(test.level));
137 io.in.durable_open = true;
139 status = smb2_create(tree, mem_ctx, &io);
140 CHECK_STATUS(status, NT_STATUS_OK);
141 _h = io.out.file.handle;
142 h = &_h;
143 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
144 CHECK_VAL(io.out.durable_open, test.expected);
145 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
147 done:
148 if (h != NULL) {
149 smb2_util_close(tree, *h);
151 smb2_util_unlink(tree, fname);
152 talloc_free(mem_ctx);
154 return ret;
157 static bool test_durable_open_open_oplock(struct torture_context *tctx,
158 struct smb2_tree *tree)
160 TALLOC_CTX *mem_ctx = talloc_new(tctx);
161 char fname[256];
162 bool ret = true;
163 int i;
165 /* Choose a random name in case the state is left a little funky. */
166 snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
168 smb2_util_unlink(tree, fname);
170 /* test various oplock levels with durable open */
172 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
173 ret = test_one_durable_open_open_oplock(tctx,
174 tree,
175 fname,
176 durable_open_vs_oplock_table[i]);
177 if (ret == false) {
178 goto done;
182 done:
183 smb2_util_unlink(tree, fname);
184 talloc_free(tree);
185 talloc_free(mem_ctx);
187 return ret;
191 * basic durable_open test.
192 * durable state should only be granted when requested
193 * along with a batch oplock or a handle lease.
195 * This test tests durable open with all valid lease types.
198 struct durable_open_vs_lease {
199 const char *type;
200 const char *share_mode;
201 bool expected;
204 #define NUM_LEASE_TYPES 5
205 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
206 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
208 { "", "", false },
209 { "", "R", false },
210 { "", "W", false },
211 { "", "D", false },
212 { "", "RW", false },
213 { "", "RD", false },
214 { "", "WD", false },
215 { "", "RWD", false },
217 { "R", "", false },
218 { "R", "R", false },
219 { "R", "W", false },
220 { "R", "D", false },
221 { "R", "RW", false },
222 { "R", "RD", false },
223 { "R", "DW", false },
224 { "R", "RWD", false },
226 { "RW", "", false },
227 { "RW", "R", false },
228 { "RW", "W", false },
229 { "RW", "D", false },
230 { "RW", "RW", false },
231 { "RW", "RD", false },
232 { "RW", "WD", false },
233 { "RW", "RWD", false },
235 { "RH", "", true },
236 { "RH", "R", true },
237 { "RH", "W", true },
238 { "RH", "D", true },
239 { "RH", "RW", true },
240 { "RH", "RD", true },
241 { "RH", "WD", true },
242 { "RH", "RWD", true },
244 { "RHW", "", true },
245 { "RHW", "R", true },
246 { "RHW", "W", true },
247 { "RHW", "D", true },
248 { "RHW", "RW", true },
249 { "RHW", "RD", true },
250 { "RHW", "WD", true },
251 { "RHW", "RWD", true },
254 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
255 struct smb2_tree *tree,
256 const char *fname,
257 struct durable_open_vs_lease test)
259 NTSTATUS status;
260 TALLOC_CTX *mem_ctx = talloc_new(tctx);
261 struct smb2_handle _h;
262 struct smb2_handle *h = NULL;
263 bool ret = true;
264 struct smb2_create io;
265 struct smb2_lease ls;
266 uint64_t lease;
267 uint32_t caps;
269 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
270 if (!(caps & SMB2_CAP_LEASING)) {
271 torture_skip(tctx, "leases are not supported");
274 smb2_util_unlink(tree, fname);
276 lease = random();
278 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
279 smb2_util_share_access(test.share_mode),
280 lease,
281 smb2_util_lease_state(test.type));
282 io.in.durable_open = true;
284 status = smb2_create(tree, mem_ctx, &io);
285 CHECK_STATUS(status, NT_STATUS_OK);
286 _h = io.out.file.handle;
287 h = &_h;
288 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
289 CHECK_VAL(io.out.durable_open, test.expected);
290 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
291 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
292 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
293 CHECK_VAL(io.out.lease_response.lease_state,
294 smb2_util_lease_state(test.type));
295 done:
296 if (h != NULL) {
297 smb2_util_close(tree, *h);
299 smb2_util_unlink(tree, fname);
300 talloc_free(mem_ctx);
302 return ret;
305 static bool test_durable_open_open_lease(struct torture_context *tctx,
306 struct smb2_tree *tree)
308 TALLOC_CTX *mem_ctx = talloc_new(tctx);
309 char fname[256];
310 bool ret = true;
311 int i;
312 uint32_t caps;
314 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
315 if (!(caps & SMB2_CAP_LEASING)) {
316 torture_skip(tctx, "leases are not supported");
319 /* Choose a random name in case the state is left a little funky. */
320 snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
322 smb2_util_unlink(tree, fname);
325 /* test various oplock levels with durable open */
327 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
328 ret = test_one_durable_open_open_lease(tctx,
329 tree,
330 fname,
331 durable_open_vs_lease_table[i]);
332 if (ret == false) {
333 goto done;
337 done:
338 smb2_util_unlink(tree, fname);
339 talloc_free(tree);
340 talloc_free(mem_ctx);
342 return ret;
346 * basic test for doing a durable open
347 * and do a durable reopen on the same connection
348 * while the first open is still active (fails)
350 static bool test_durable_open_reopen1(struct torture_context *tctx,
351 struct smb2_tree *tree)
353 NTSTATUS status;
354 TALLOC_CTX *mem_ctx = talloc_new(tctx);
355 char fname[256];
356 struct smb2_handle _h;
357 struct smb2_handle *h = NULL;
358 struct smb2_create io1, io2;
359 bool ret = true;
361 /* Choose a random name in case the state is left a little funky. */
362 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
363 generate_random_str(tctx, 8));
365 smb2_util_unlink(tree, fname);
367 smb2_oplock_create_share(&io1, fname,
368 smb2_util_share_access(""),
369 smb2_util_oplock_level("b"));
370 io1.in.durable_open = true;
372 status = smb2_create(tree, mem_ctx, &io1);
373 CHECK_STATUS(status, NT_STATUS_OK);
374 _h = io1.out.file.handle;
375 h = &_h;
376 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
377 CHECK_VAL(io1.out.durable_open, true);
378 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
380 /* try a durable reconnect while the file is still open */
381 ZERO_STRUCT(io2);
382 io2.in.fname = fname;
383 io2.in.durable_handle = h;
385 status = smb2_create(tree, mem_ctx, &io2);
386 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
388 done:
389 if (h != NULL) {
390 smb2_util_close(tree, *h);
393 smb2_util_unlink(tree, fname);
395 talloc_free(tree);
397 talloc_free(mem_ctx);
399 return ret;
403 * Basic test for doing a durable open
404 * and do a session reconnect while the first
405 * session is still active and the handle is
406 * still open in the client.
407 * This closes the original session and a
408 * durable reconnect on the new session succeeds.
410 static bool test_durable_open_reopen1a(struct torture_context *tctx,
411 struct smb2_tree *tree)
413 NTSTATUS status;
414 TALLOC_CTX *mem_ctx = talloc_new(tctx);
415 char fname[256];
416 struct smb2_handle _h;
417 struct smb2_handle *h = NULL;
418 struct smb2_create io;
419 bool ret = true;
420 struct smb2_tree *tree2 = NULL;
421 struct smb2_tree *tree3 = NULL;
422 uint64_t previous_session_id;
423 struct smbcli_options options;
424 struct GUID orig_client_guid;
426 options = tree->session->transport->options;
427 orig_client_guid = options.client_guid;
429 /* Choose a random name in case the state is left a little funky. */
430 snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
431 generate_random_str(tctx, 8));
433 smb2_util_unlink(tree, fname);
435 smb2_oplock_create_share(&io, fname,
436 smb2_util_share_access(""),
437 smb2_util_oplock_level("b"));
438 io.in.durable_open = true;
440 status = smb2_create(tree, mem_ctx, &io);
441 CHECK_STATUS(status, NT_STATUS_OK);
442 _h = io.out.file.handle;
443 h = &_h;
444 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
445 CHECK_VAL(io.out.durable_open, true);
446 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
449 * a session reconnect on a second tcp connection
452 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
454 /* for oplocks, the client guid can be different: */
455 options.client_guid = GUID_random();
457 ret = torture_smb2_connection_ext(tctx, previous_session_id,
458 &options, &tree2);
459 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
462 * check that this has deleted the old session
465 ZERO_STRUCT(io);
466 io.in.fname = fname;
467 io.in.durable_handle = h;
469 status = smb2_create(tree, mem_ctx, &io);
470 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
472 TALLOC_FREE(tree);
475 * but a durable reconnect on the new session succeeds:
478 ZERO_STRUCT(io);
479 io.in.fname = fname;
480 io.in.durable_handle = h;
482 status = smb2_create(tree2, mem_ctx, &io);
483 CHECK_STATUS(status, NT_STATUS_OK);
484 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
485 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
486 _h = io.out.file.handle;
487 h = &_h;
490 * a session reconnect on a second tcp connection
493 previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
495 /* the original client_guid works just the same */
496 options.client_guid = orig_client_guid;
498 ret = torture_smb2_connection_ext(tctx, previous_session_id,
499 &options, &tree3);
500 torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
503 * check that this has deleted the old session
506 ZERO_STRUCT(io);
507 io.in.fname = fname;
508 io.in.durable_handle = h;
510 status = smb2_create(tree2, mem_ctx, &io);
511 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
513 TALLOC_FREE(tree2);
516 * but a durable reconnect on the new session succeeds:
519 ZERO_STRUCT(io);
520 io.in.fname = fname;
521 io.in.durable_handle = h;
523 status = smb2_create(tree3, mem_ctx, &io);
524 CHECK_STATUS(status, NT_STATUS_OK);
525 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
526 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
527 _h = io.out.file.handle;
528 h = &_h;
530 done:
531 if (tree == NULL) {
532 tree = tree2;
535 if (tree == NULL) {
536 tree = tree3;
539 if (tree != NULL) {
540 if (h != NULL) {
541 smb2_util_close(tree, *h);
542 h = NULL;
544 smb2_util_unlink(tree, fname);
546 talloc_free(tree);
549 talloc_free(mem_ctx);
551 return ret;
555 * lease variant of reopen1a
557 * Basic test for doing a durable open and doing a session
558 * reconnect while the first session is still active and the
559 * handle is still open in the client.
560 * This closes the original session and a durable reconnect on
561 * the new session succeeds depending on the client guid:
563 * Durable reconnect on a session with a different client guid fails.
564 * Durable reconnect on a session with the original client guid succeeds.
566 bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
567 struct smb2_tree *tree)
569 NTSTATUS status;
570 TALLOC_CTX *mem_ctx = talloc_new(tctx);
571 char fname[256];
572 struct smb2_handle _h;
573 struct smb2_handle *h = NULL;
574 struct smb2_create io;
575 struct smb2_lease ls;
576 uint64_t lease_key;
577 bool ret = true;
578 struct smb2_tree *tree2 = NULL;
579 struct smb2_tree *tree3 = NULL;
580 uint64_t previous_session_id;
581 struct smbcli_options options;
582 struct GUID orig_client_guid;
584 options = tree->session->transport->options;
585 orig_client_guid = options.client_guid;
587 /* Choose a random name in case the state is left a little funky. */
588 snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
589 generate_random_str(tctx, 8));
591 smb2_util_unlink(tree, fname);
593 lease_key = random();
594 smb2_lease_create(&io, &ls, false /* dir */, fname,
595 lease_key, smb2_util_lease_state("RWH"));
596 io.in.durable_open = true;
598 status = smb2_create(tree, mem_ctx, &io);
599 CHECK_STATUS(status, NT_STATUS_OK);
600 _h = io.out.file.handle;
601 h = &_h;
602 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
603 CHECK_VAL(io.out.durable_open, true);
604 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
605 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
606 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
607 CHECK_VAL(io.out.lease_response.lease_state,
608 smb2_util_lease_state("RWH"));
609 CHECK_VAL(io.out.lease_response.lease_flags, 0);
610 CHECK_VAL(io.out.lease_response.lease_duration, 0);
612 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
615 * a session reconnect on a second tcp connection
616 * with a different client_guid does not allow
617 * the durable reconnect.
620 options.client_guid = GUID_random();
622 ret = torture_smb2_connection_ext(tctx, previous_session_id,
623 &options, &tree2);
624 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
627 * check that this has deleted the old session
630 ZERO_STRUCT(io);
631 io.in.fname = fname;
632 io.in.durable_handle = h;
633 io.in.lease_request = &ls;
634 status = smb2_create(tree, mem_ctx, &io);
635 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
636 TALLOC_FREE(tree);
640 * but a durable reconnect on the new session with the wrong
641 * client guid fails
644 ZERO_STRUCT(io);
645 io.in.fname = fname;
646 io.in.durable_handle = h;
647 io.in.lease_request = &ls;
648 status = smb2_create(tree2, mem_ctx, &io);
649 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
652 * now a session reconnect on a second tcp connection
653 * with original client_guid allows the durable reconnect.
656 options.client_guid = orig_client_guid;
658 ret = torture_smb2_connection_ext(tctx, previous_session_id,
659 &options, &tree3);
660 torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
663 * check that this has deleted the old session
664 * In this case, a durable reconnect attempt with the
665 * correct client_guid yields a different error code.
668 ZERO_STRUCT(io);
669 io.in.fname = fname;
670 io.in.durable_handle = h;
671 io.in.lease_request = &ls;
672 status = smb2_create(tree2, mem_ctx, &io);
673 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
674 TALLOC_FREE(tree2);
677 * but a durable reconnect on the new session succeeds:
680 ZERO_STRUCT(io);
681 io.in.fname = fname;
682 io.in.durable_handle = h;
683 io.in.lease_request = &ls;
684 status = smb2_create(tree3, mem_ctx, &io);
685 CHECK_STATUS(status, NT_STATUS_OK);
686 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
687 CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
688 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
689 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
690 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
691 CHECK_VAL(io.out.lease_response.lease_state,
692 smb2_util_lease_state("RWH"));
693 CHECK_VAL(io.out.lease_response.lease_flags, 0);
694 CHECK_VAL(io.out.lease_response.lease_duration, 0);
695 _h = io.out.file.handle;
696 h = &_h;
698 done:
699 if (tree == NULL) {
700 tree = tree2;
703 if (tree == NULL) {
704 tree = tree3;
707 if (tree != NULL) {
708 if (h != NULL) {
709 smb2_util_close(tree, *h);
712 smb2_util_unlink(tree, fname);
714 talloc_free(tree);
717 talloc_free(mem_ctx);
719 return ret;
724 * basic test for doing a durable open
725 * tcp disconnect, reconnect, do a durable reopen (succeeds)
727 static bool test_durable_open_reopen2(struct torture_context *tctx,
728 struct smb2_tree *tree)
730 NTSTATUS status;
731 TALLOC_CTX *mem_ctx = talloc_new(tctx);
732 char fname[256];
733 struct smb2_handle _h;
734 struct smb2_handle *h = NULL;
735 struct smb2_create io;
736 bool ret = true;
738 /* Choose a random name in case the state is left a little funky. */
739 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
740 generate_random_str(tctx, 8));
742 smb2_util_unlink(tree, fname);
744 smb2_oplock_create_share(&io, fname,
745 smb2_util_share_access(""),
746 smb2_util_oplock_level("b"));
747 io.in.durable_open = true;
749 status = smb2_create(tree, mem_ctx, &io);
750 CHECK_STATUS(status, NT_STATUS_OK);
751 _h = io.out.file.handle;
752 h = &_h;
753 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
754 CHECK_VAL(io.out.durable_open, true);
755 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
757 /* disconnect, leaving the durable in place */
758 TALLOC_FREE(tree);
760 if (!torture_smb2_connection(tctx, &tree)) {
761 torture_warning(tctx, "couldn't reconnect, bailing\n");
762 ret = false;
763 goto done;
766 ZERO_STRUCT(io);
767 /* the path name is ignored by the server */
768 io.in.fname = fname;
769 io.in.durable_handle = h; /* durable v1 reconnect request */
770 h = NULL;
772 status = smb2_create(tree, mem_ctx, &io);
773 CHECK_STATUS(status, NT_STATUS_OK);
774 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
775 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
776 _h = io.out.file.handle;
777 h = &_h;
779 /* disconnect again, leaving the durable in place */
780 TALLOC_FREE(tree);
782 if (!torture_smb2_connection(tctx, &tree)) {
783 torture_warning(tctx, "couldn't reconnect, bailing\n");
784 ret = false;
785 goto done;
789 * show that the filename and many other fields
790 * are ignored. only the reconnect request blob
791 * is important.
793 ZERO_STRUCT(io);
794 /* the path name is ignored by the server */
795 io.in.security_flags = 0x78;
796 io.in.oplock_level = 0x78;
797 io.in.impersonation_level = 0x12345678;
798 io.in.create_flags = 0x12345678;
799 io.in.reserved = 0x12345678;
800 io.in.desired_access = 0x12345678;
801 io.in.file_attributes = 0x12345678;
802 io.in.share_access = 0x12345678;
803 io.in.create_disposition = 0x12345678;
804 io.in.create_options = 0x12345678;
805 io.in.fname = "__non_existing_fname__";
806 io.in.durable_handle = h; /* durable v1 reconnect request */
807 h = NULL;
809 status = smb2_create(tree, mem_ctx, &io);
810 CHECK_STATUS(status, NT_STATUS_OK);
811 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
812 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
813 _h = io.out.file.handle;
814 h = &_h;
816 /* disconnect, leaving the durable in place */
817 TALLOC_FREE(tree);
819 if (!torture_smb2_connection(tctx, &tree)) {
820 torture_warning(tctx, "couldn't reconnect, bailing\n");
821 ret = false;
822 goto done;
826 * show that an additionally specified durable v1 request
827 * is ignored by the server.
828 * See MS-SMB2, 3.3.5.9.7
829 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
831 ZERO_STRUCT(io);
832 /* the path name is ignored by the server */
833 io.in.fname = fname;
834 io.in.durable_handle = h; /* durable v1 reconnect request */
835 io.in.durable_open = true; /* durable v1 handle request */
836 h = NULL;
838 status = smb2_create(tree, mem_ctx, &io);
839 CHECK_STATUS(status, NT_STATUS_OK);
840 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
841 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
842 _h = io.out.file.handle;
843 h = &_h;
845 done:
846 if (tree != NULL) {
847 if (h != NULL) {
848 smb2_util_close(tree, *h);
851 smb2_util_unlink(tree, fname);
853 talloc_free(tree);
856 talloc_free(mem_ctx);
858 return ret;
862 * lease variant of reopen2
863 * basic test for doing a durable open
864 * tcp disconnect, reconnect, do a durable reopen (succeeds)
866 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
867 struct smb2_tree *tree)
869 NTSTATUS status;
870 TALLOC_CTX *mem_ctx = talloc_new(tctx);
871 char fname[256];
872 struct smb2_handle _h;
873 struct smb2_handle *h = NULL;
874 struct smb2_create io;
875 struct smb2_lease ls;
876 uint64_t lease_key;
877 bool ret = true;
878 struct smbcli_options options;
879 uint32_t caps;
881 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
882 if (!(caps & SMB2_CAP_LEASING)) {
883 torture_skip(tctx, "leases are not supported");
886 options = tree->session->transport->options;
888 /* Choose a random name in case the state is left a little funky. */
889 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
890 generate_random_str(tctx, 8));
892 smb2_util_unlink(tree, fname);
894 lease_key = random();
895 smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
896 smb2_util_lease_state("RWH"));
897 io.in.durable_open = true;
899 status = smb2_create(tree, mem_ctx, &io);
900 CHECK_STATUS(status, NT_STATUS_OK);
901 _h = io.out.file.handle;
902 h = &_h;
903 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
905 CHECK_VAL(io.out.durable_open, true);
906 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
907 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
908 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
909 CHECK_VAL(io.out.lease_response.lease_state,
910 smb2_util_lease_state("RWH"));
911 CHECK_VAL(io.out.lease_response.lease_flags, 0);
912 CHECK_VAL(io.out.lease_response.lease_duration, 0);
914 /* disconnect, reconnect and then do durable reopen */
915 TALLOC_FREE(tree);
917 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
918 torture_warning(tctx, "couldn't reconnect, bailing\n");
919 ret = false;
920 goto done;
924 /* a few failure tests: */
927 * several attempts without lease attached:
928 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
929 * irrespective of file name provided
932 ZERO_STRUCT(io);
933 io.in.fname = "";
934 io.in.durable_handle = h;
935 status = smb2_create(tree, mem_ctx, &io);
936 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
938 ZERO_STRUCT(io);
939 io.in.fname = "__non_existing_fname__";
940 io.in.durable_handle = h;
941 status = smb2_create(tree, mem_ctx, &io);
942 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
944 ZERO_STRUCT(io);
945 io.in.fname = fname;
946 io.in.durable_handle = h;
947 status = smb2_create(tree, mem_ctx, &io);
948 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
951 * attempt with lease provided, but
952 * with a changed lease key. => fails
954 ZERO_STRUCT(io);
955 io.in.fname = fname;
956 io.in.durable_open = false;
957 io.in.durable_handle = h;
958 io.in.lease_request = &ls;
959 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
960 /* a wrong lease key lets the request fail */
961 ls.lease_key.data[0]++;
963 status = smb2_create(tree, mem_ctx, &io);
964 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
966 /* restore the correct lease key */
967 ls.lease_key.data[0]--;
970 * this last failing attempt is almost correct:
971 * only problem is: we use the wrong filename...
972 * Note that this gives INVALID_PARAMETER.
973 * This is different from oplocks!
975 ZERO_STRUCT(io);
976 io.in.fname = "__non_existing_fname__";
977 io.in.durable_open = false;
978 io.in.durable_handle = h;
979 io.in.lease_request = &ls;
980 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
982 status = smb2_create(tree, mem_ctx, &io);
983 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
986 * Now for a succeeding reconnect:
989 ZERO_STRUCT(io);
990 io.in.fname = fname;
991 io.in.durable_open = false;
992 io.in.durable_handle = h;
993 io.in.lease_request = &ls;
994 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
996 /* the requested lease state is irrelevant */
997 ls.lease_state = smb2_util_lease_state("");
999 h = NULL;
1001 status = smb2_create(tree, mem_ctx, &io);
1002 CHECK_STATUS(status, NT_STATUS_OK);
1004 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1005 CHECK_VAL(io.out.durable_open, false);
1006 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1007 CHECK_VAL(io.out.persistent_open, false);
1008 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1009 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1010 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1011 CHECK_VAL(io.out.lease_response.lease_state,
1012 smb2_util_lease_state("RWH"));
1013 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1014 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1015 _h = io.out.file.handle;
1016 h = &_h;
1018 /* disconnect one more time */
1019 TALLOC_FREE(tree);
1021 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1022 torture_warning(tctx, "couldn't reconnect, bailing\n");
1023 ret = false;
1024 goto done;
1028 * demonstrate that various parameters are ignored
1029 * in the reconnect
1032 ZERO_STRUCT(io);
1034 * These are completely ignored by the server
1036 io.in.security_flags = 0x78;
1037 io.in.oplock_level = 0x78;
1038 io.in.impersonation_level = 0x12345678;
1039 io.in.create_flags = 0x12345678;
1040 io.in.reserved = 0x12345678;
1041 io.in.desired_access = 0x12345678;
1042 io.in.file_attributes = 0x12345678;
1043 io.in.share_access = 0x12345678;
1044 io.in.create_disposition = 0x12345678;
1045 io.in.create_options = 0x12345678;
1048 * only these are checked:
1049 * - io.in.fname
1050 * - io.in.durable_handle,
1051 * - io.in.lease_request->lease_key
1054 io.in.fname = fname;
1055 io.in.durable_open_v2 = false;
1056 io.in.durable_handle_v2 = h;
1057 io.in.lease_request = &ls;
1059 /* the requested lease state is irrelevant */
1060 ls.lease_state = smb2_util_lease_state("");
1062 h = NULL;
1064 status = smb2_create(tree, mem_ctx, &io);
1065 CHECK_STATUS(status, NT_STATUS_OK);
1067 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1068 CHECK_VAL(io.out.durable_open, false);
1069 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1070 CHECK_VAL(io.out.persistent_open, false);
1071 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1072 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1073 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1074 CHECK_VAL(io.out.lease_response.lease_state,
1075 smb2_util_lease_state("RWH"));
1076 CHECK_VAL(io.out.lease_response.lease_flags, 0);
1077 CHECK_VAL(io.out.lease_response.lease_duration, 0);
1079 _h = io.out.file.handle;
1080 h = &_h;
1082 done:
1083 if (tree != NULL) {
1084 if (h != NULL) {
1085 smb2_util_close(tree, *h);
1088 smb2_util_unlink(tree, fname);
1090 talloc_free(tree);
1093 talloc_free(mem_ctx);
1095 return ret;
1099 * lease v2 variant of reopen2
1100 * basic test for doing a durable open
1101 * tcp disconnect, reconnect, do a durable reopen (succeeds)
1103 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
1104 struct smb2_tree *tree)
1106 NTSTATUS status;
1107 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1108 char fname[256];
1109 struct smb2_handle _h;
1110 struct smb2_handle *h = NULL;
1111 struct smb2_create io;
1112 struct smb2_lease ls;
1113 uint64_t lease_key;
1114 bool ret = true;
1115 struct smbcli_options options;
1116 uint32_t caps;
1118 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1119 if (!(caps & SMB2_CAP_LEASING)) {
1120 torture_skip(tctx, "leases are not supported");
1123 options = tree->session->transport->options;
1125 /* Choose a random name in case the state is left a little funky. */
1126 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1127 generate_random_str(tctx, 8));
1129 smb2_util_unlink(tree, fname);
1131 lease_key = random();
1132 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1133 lease_key, 0, /* parent lease key */
1134 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1135 io.in.durable_open = true;
1137 status = smb2_create(tree, mem_ctx, &io);
1138 CHECK_STATUS(status, NT_STATUS_OK);
1139 _h = io.out.file.handle;
1140 h = &_h;
1141 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1143 CHECK_VAL(io.out.durable_open, true);
1144 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1145 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1146 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1147 CHECK_VAL(io.out.lease_response_v2.lease_state,
1148 smb2_util_lease_state("RWH"));
1149 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1150 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1152 /* disconnect, reconnect and then do durable reopen */
1153 TALLOC_FREE(tree);
1155 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1156 torture_warning(tctx, "couldn't reconnect, bailing\n");
1157 ret = false;
1158 goto done;
1161 /* a few failure tests: */
1164 * several attempts without lease attached:
1165 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1166 * irrespective of file name provided
1169 ZERO_STRUCT(io);
1170 io.in.fname = "";
1171 io.in.durable_handle = h;
1172 status = smb2_create(tree, mem_ctx, &io);
1173 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1175 ZERO_STRUCT(io);
1176 io.in.fname = "__non_existing_fname__";
1177 io.in.durable_handle = h;
1178 status = smb2_create(tree, mem_ctx, &io);
1179 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1181 ZERO_STRUCT(io);
1182 io.in.fname = fname;
1183 io.in.durable_handle = h;
1184 status = smb2_create(tree, mem_ctx, &io);
1185 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1188 * attempt with lease provided, but
1189 * with a changed lease key. => fails
1191 ZERO_STRUCT(io);
1192 io.in.fname = fname;
1193 io.in.durable_open = false;
1194 io.in.durable_handle = h;
1195 io.in.lease_request_v2 = &ls;
1196 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1197 /* a wrong lease key lets the request fail */
1198 ls.lease_key.data[0]++;
1200 status = smb2_create(tree, mem_ctx, &io);
1201 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1203 /* restore the correct lease key */
1204 ls.lease_key.data[0]--;
1207 * this last failing attempt is almost correct:
1208 * only problem is: we use the wrong filename...
1209 * Note that this gives INVALID_PARAMETER.
1210 * This is different from oplocks!
1212 ZERO_STRUCT(io);
1213 io.in.fname = "__non_existing_fname__";
1214 io.in.durable_open = false;
1215 io.in.durable_handle = h;
1216 io.in.lease_request_v2 = &ls;
1217 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1219 status = smb2_create(tree, mem_ctx, &io);
1220 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1223 * Now for a succeeding reconnect:
1226 ZERO_STRUCT(io);
1227 io.in.fname = fname;
1228 io.in.durable_open = false;
1229 io.in.durable_handle = h;
1230 io.in.lease_request_v2 = &ls;
1231 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1233 /* the requested lease state is irrelevant */
1234 ls.lease_state = smb2_util_lease_state("");
1236 h = NULL;
1238 status = smb2_create(tree, mem_ctx, &io);
1239 CHECK_STATUS(status, NT_STATUS_OK);
1241 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1242 CHECK_VAL(io.out.durable_open, false);
1243 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1244 CHECK_VAL(io.out.persistent_open, false);
1245 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1246 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1247 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1248 CHECK_VAL(io.out.lease_response_v2.lease_state,
1249 smb2_util_lease_state("RWH"));
1250 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1251 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1252 _h = io.out.file.handle;
1253 h = &_h;
1255 /* disconnect one more time */
1256 TALLOC_FREE(tree);
1258 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1259 torture_warning(tctx, "couldn't reconnect, bailing\n");
1260 ret = false;
1261 goto done;
1265 * demonstrate that various parameters are ignored
1266 * in the reconnect
1269 ZERO_STRUCT(io);
1271 * These are completely ignored by the server
1273 io.in.security_flags = 0x78;
1274 io.in.oplock_level = 0x78;
1275 io.in.impersonation_level = 0x12345678;
1276 io.in.create_flags = 0x12345678;
1277 io.in.reserved = 0x12345678;
1278 io.in.desired_access = 0x12345678;
1279 io.in.file_attributes = 0x12345678;
1280 io.in.share_access = 0x12345678;
1281 io.in.create_disposition = 0x12345678;
1282 io.in.create_options = 0x12345678;
1285 * only these are checked:
1286 * - io.in.fname
1287 * - io.in.durable_handle,
1288 * - io.in.lease_request->lease_key
1291 io.in.fname = fname;
1292 io.in.durable_open_v2 = false;
1293 io.in.durable_handle_v2 = h;
1294 io.in.lease_request_v2 = &ls;
1296 /* the requested lease state is irrelevant */
1297 ls.lease_state = smb2_util_lease_state("");
1299 h = NULL;
1301 status = smb2_create(tree, mem_ctx, &io);
1302 CHECK_STATUS(status, NT_STATUS_OK);
1304 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1305 CHECK_VAL(io.out.durable_open, false);
1306 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1307 CHECK_VAL(io.out.persistent_open, false);
1308 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1309 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1310 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1311 CHECK_VAL(io.out.lease_response_v2.lease_state,
1312 smb2_util_lease_state("RWH"));
1313 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1314 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1316 _h = io.out.file.handle;
1317 h = &_h;
1319 done:
1320 if (tree != NULL) {
1321 if (h != NULL) {
1322 smb2_util_close(tree, *h);
1325 smb2_util_unlink(tree, fname);
1327 talloc_free(tree);
1330 talloc_free(mem_ctx);
1332 return ret;
1336 * basic test for doing a durable open
1337 * tcp disconnect, reconnect with a session reconnect and
1338 * do a durable reopen (succeeds)
1340 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1341 struct smb2_tree *tree)
1343 NTSTATUS status;
1344 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1345 char fname[256];
1346 struct smb2_handle _h;
1347 struct smb2_handle *h = NULL;
1348 struct smb2_create io1, io2;
1349 uint64_t previous_session_id;
1350 bool ret = true;
1351 struct smbcli_options options;
1353 options = tree->session->transport->options;
1355 /* Choose a random name in case the state is left a little funky. */
1356 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1357 generate_random_str(tctx, 8));
1359 smb2_util_unlink(tree, fname);
1361 smb2_oplock_create_share(&io1, fname,
1362 smb2_util_share_access(""),
1363 smb2_util_oplock_level("b"));
1364 io1.in.durable_open = true;
1366 status = smb2_create(tree, mem_ctx, &io1);
1367 CHECK_STATUS(status, NT_STATUS_OK);
1368 _h = io1.out.file.handle;
1369 h = &_h;
1370 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1371 CHECK_VAL(io1.out.durable_open, true);
1372 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1374 /* disconnect, reconnect and then do durable reopen */
1375 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1376 talloc_free(tree);
1377 tree = NULL;
1379 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1380 &options, &tree))
1382 torture_warning(tctx, "couldn't reconnect, bailing\n");
1383 ret = false;
1384 goto done;
1387 ZERO_STRUCT(io2);
1388 io2.in.fname = fname;
1389 io2.in.durable_handle = h;
1390 h = NULL;
1392 status = smb2_create(tree, mem_ctx, &io2);
1393 CHECK_STATUS(status, NT_STATUS_OK);
1394 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1395 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1396 _h = io2.out.file.handle;
1397 h = &_h;
1399 done:
1400 if (tree != NULL) {
1401 if (h != NULL) {
1402 smb2_util_close(tree, *h);
1405 smb2_util_unlink(tree, fname);
1407 talloc_free(tree);
1410 talloc_free(mem_ctx);
1412 return ret;
1417 * basic test for doing a durable open:
1418 * tdis, new tcon, try durable reopen (fails)
1420 static bool test_durable_open_reopen3(struct torture_context *tctx,
1421 struct smb2_tree *tree)
1423 NTSTATUS status;
1424 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1425 char fname[256];
1426 struct smb2_handle _h;
1427 struct smb2_handle *h = NULL;
1428 struct smb2_create io1, io2;
1429 bool ret = true;
1430 struct smb2_tree *tree2 = NULL;
1432 /* Choose a random name in case the state is left a little funky. */
1433 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1434 generate_random_str(tctx, 8));
1436 smb2_util_unlink(tree, fname);
1438 smb2_oplock_create_share(&io1, fname,
1439 smb2_util_share_access(""),
1440 smb2_util_oplock_level("b"));
1441 io1.in.durable_open = true;
1443 status = smb2_create(tree, mem_ctx, &io1);
1444 CHECK_STATUS(status, NT_STATUS_OK);
1445 _h = io1.out.file.handle;
1446 h = &_h;
1447 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1448 CHECK_VAL(io1.out.durable_open, true);
1449 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1451 /* disconnect, reconnect and then do durable reopen */
1452 status = smb2_tdis(tree);
1453 CHECK_STATUS(status, NT_STATUS_OK);
1455 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1456 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1457 ret = false;
1458 goto done;
1462 ZERO_STRUCT(io2);
1463 io2.in.fname = fname;
1464 io2.in.durable_handle = h;
1466 status = smb2_create(tree2, mem_ctx, &io2);
1467 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1469 done:
1470 if (tree != NULL) {
1471 if (h != NULL) {
1472 smb2_util_close(tree, *h);
1475 smb2_util_unlink(tree2, fname);
1477 talloc_free(tree);
1480 talloc_free(mem_ctx);
1482 return ret;
1486 * basic test for doing a durable open:
1487 * logoff, create a new session, do a durable reopen (succeeds)
1489 static bool test_durable_open_reopen4(struct torture_context *tctx,
1490 struct smb2_tree *tree)
1492 NTSTATUS status;
1493 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1494 char fname[256];
1495 struct smb2_handle _h;
1496 struct smb2_handle *h = NULL;
1497 struct smb2_create io1, io2;
1498 bool ret = true;
1499 struct smb2_transport *transport = NULL;
1500 struct smb2_session *session2 = NULL;
1501 struct smb2_tree *tree2 = NULL;
1503 /* Choose a random name in case the state is left a little funky. */
1504 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1505 generate_random_str(tctx, 8));
1507 smb2_util_unlink(tree, fname);
1509 smb2_oplock_create_share(&io1, fname,
1510 smb2_util_share_access(""),
1511 smb2_util_oplock_level("b"));
1512 io1.in.durable_open = true;
1514 status = smb2_create(tree, mem_ctx, &io1);
1515 CHECK_STATUS(status, NT_STATUS_OK);
1516 _h = io1.out.file.handle;
1517 h = &_h;
1518 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1519 CHECK_VAL(io1.out.durable_open, true);
1520 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1523 * do a session logoff, establish a new session and tree
1524 * connect on the same transport, and try a durable reopen
1526 transport = tree->session->transport;
1527 status = smb2_logoff(tree->session);
1528 CHECK_STATUS(status, NT_STATUS_OK);
1530 if (!torture_smb2_session_setup(tctx, transport,
1531 0, /* previous_session_id */
1532 mem_ctx, &session2))
1534 torture_warning(tctx, "session setup failed.\n");
1535 ret = false;
1536 goto done;
1540 * the session setup has talloc-stolen the transport,
1541 * so we can safely free the old tree+session for clarity
1543 TALLOC_FREE(tree);
1545 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1546 torture_warning(tctx, "tree connect failed.\n");
1547 ret = false;
1548 goto done;
1551 ZERO_STRUCT(io2);
1552 io2.in.fname = fname;
1553 io2.in.durable_handle = h;
1554 h = NULL;
1556 status = smb2_create(tree2, mem_ctx, &io2);
1557 CHECK_STATUS(status, NT_STATUS_OK);
1559 _h = io2.out.file.handle;
1560 h = &_h;
1561 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1562 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1564 done:
1565 if (tree != NULL) {
1566 if (h != NULL) {
1567 smb2_util_close(tree2, *h);
1570 smb2_util_unlink(tree2, fname);
1572 talloc_free(tree);
1575 talloc_free(mem_ctx);
1577 return ret;
1580 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1581 struct smb2_tree *tree)
1583 NTSTATUS status;
1584 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1585 char fname[256];
1586 struct smb2_handle _h;
1587 struct smb2_handle *h = NULL;
1588 struct smb2_create io1, io2;
1589 bool ret = true;
1590 uint8_t b = 0;
1592 /* Choose a random name in case the state is left a little funky. */
1593 snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1594 generate_random_str(tctx, 8));
1596 smb2_util_unlink(tree, fname);
1598 smb2_oplock_create_share(&io1, fname,
1599 smb2_util_share_access(""),
1600 smb2_util_oplock_level("b"));
1601 io1.in.durable_open = true;
1602 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1604 status = smb2_create(tree, mem_ctx, &io1);
1605 CHECK_STATUS(status, NT_STATUS_OK);
1606 _h = io1.out.file.handle;
1607 h = &_h;
1608 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1609 CHECK_VAL(io1.out.durable_open, true);
1610 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1612 status = smb2_util_write(tree, *h, &b, 0, 1);
1613 CHECK_STATUS(status, NT_STATUS_OK);
1615 /* disconnect, leaving the durable handle in place */
1616 TALLOC_FREE(tree);
1618 if (!torture_smb2_connection(tctx, &tree)) {
1619 torture_warning(tctx, "could not reconnect, bailing\n");
1620 ret = false;
1621 goto done;
1625 * Open the file on the new connection again
1626 * and check that it has been newly created,
1627 * i.e. delete on close was effective on the disconnected handle.
1628 * Also check that the file is really empty,
1629 * the previously written byte gone.
1631 smb2_oplock_create_share(&io2, fname,
1632 smb2_util_share_access(""),
1633 smb2_util_oplock_level("b"));
1634 io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1636 status = smb2_create(tree, mem_ctx, &io2);
1637 CHECK_STATUS(status, NT_STATUS_OK);
1638 _h = io2.out.file.handle;
1639 h = &_h;
1640 CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1641 CHECK_VAL(io2.out.durable_open, false);
1642 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1644 done:
1645 if (tree != NULL) {
1646 if (h != NULL) {
1647 smb2_util_close(tree, *h);
1650 smb2_util_unlink(tree, fname);
1652 talloc_free(tree);
1655 talloc_free(mem_ctx);
1657 return ret;
1661 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1662 struct smb2_tree *tree)
1664 NTSTATUS status;
1665 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1666 char fname[256];
1667 struct smb2_handle _h;
1668 struct smb2_handle *h = NULL;
1669 struct smb2_create io;
1670 bool ret = true;
1671 uint8_t b = 0;
1672 uint64_t previous_session_id;
1673 uint64_t alloc_size_step;
1674 struct smbcli_options options;
1676 options = tree->session->transport->options;
1678 /* Choose a random name in case the state is left a little funky. */
1679 snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1680 generate_random_str(tctx, 8));
1682 smb2_util_unlink(tree, fname);
1684 smb2_oplock_create_share(&io, fname,
1685 smb2_util_share_access(""),
1686 smb2_util_oplock_level("b"));
1687 io.in.durable_open = true;
1688 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1690 status = smb2_create(tree, mem_ctx, &io);
1691 CHECK_STATUS(status, NT_STATUS_OK);
1692 _h = io.out.file.handle;
1693 h = &_h;
1694 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1695 CHECK_VAL(io.out.durable_open, true);
1696 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1698 status = smb2_util_write(tree, *h, &b, 0, 1);
1699 CHECK_STATUS(status, NT_STATUS_OK);
1701 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1703 /* disconnect, leaving the durable handle in place */
1704 TALLOC_FREE(tree);
1706 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1707 &options, &tree))
1709 torture_warning(tctx, "could not reconnect, bailing\n");
1710 ret = false;
1711 goto done;
1714 ZERO_STRUCT(io);
1715 io.in.fname = fname;
1716 io.in.durable_handle = h;
1718 status = smb2_create(tree, mem_ctx, &io);
1719 CHECK_STATUS(status, NT_STATUS_OK);
1720 _h = io.out.file.handle;
1721 h = &_h;
1722 alloc_size_step = io.out.alloc_size;
1723 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1724 CHECK_VAL(io.out.durable_open, false);
1725 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1727 /* close the file, thereby deleting it */
1728 smb2_util_close(tree, *h);
1729 status = smb2_logoff(tree->session);
1730 TALLOC_FREE(tree);
1732 if (!torture_smb2_connection(tctx, &tree)) {
1733 torture_warning(tctx, "could not reconnect, bailing\n");
1734 ret = false;
1735 goto done;
1739 * Open the file on the new connection again
1740 * and check that it has been newly created,
1741 * i.e. delete on close was effective on the reconnected handle.
1742 * Also check that the file is really empty,
1743 * the previously written byte gone.
1745 smb2_oplock_create_share(&io, fname,
1746 smb2_util_share_access(""),
1747 smb2_util_oplock_level("b"));
1748 io.in.durable_open = true;
1749 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1751 status = smb2_create(tree, mem_ctx, &io);
1752 CHECK_STATUS(status, NT_STATUS_OK);
1753 _h = io.out.file.handle;
1754 h = &_h;
1755 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1756 CHECK_VAL(io.out.durable_open, true);
1757 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1759 done:
1760 if (tree != NULL) {
1761 if (h != NULL) {
1762 smb2_util_close(tree, *h);
1765 smb2_util_unlink(tree, fname);
1767 talloc_free(tree);
1770 talloc_free(mem_ctx);
1772 return ret;
1776 basic testing of SMB2 durable opens
1777 regarding the position information on the handle
1779 static bool test_durable_open_file_position(struct torture_context *tctx,
1780 struct smb2_tree *tree)
1782 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1783 struct smb2_handle h;
1784 struct smb2_create io;
1785 NTSTATUS status;
1786 const char *fname = "durable_open_position.dat";
1787 union smb_fileinfo qfinfo;
1788 union smb_setfileinfo sfinfo;
1789 bool ret = true;
1790 uint64_t pos;
1791 uint64_t previous_session_id;
1792 struct smbcli_options options;
1794 options = tree->session->transport->options;
1796 smb2_util_unlink(tree, fname);
1798 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1799 io.in.durable_open = true;
1801 status = smb2_create(tree, mem_ctx, &io);
1802 CHECK_STATUS(status, NT_STATUS_OK);
1803 h = io.out.file.handle;
1804 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1805 CHECK_VAL(io.out.durable_open, true);
1806 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1808 /* TODO: check extra blob content */
1810 ZERO_STRUCT(qfinfo);
1811 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1812 qfinfo.generic.in.file.handle = h;
1813 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1814 CHECK_STATUS(status, NT_STATUS_OK);
1815 CHECK_VAL(qfinfo.position_information.out.position, 0);
1816 pos = qfinfo.position_information.out.position;
1817 torture_comment(tctx, "position: %llu\n",
1818 (unsigned long long)pos);
1820 ZERO_STRUCT(sfinfo);
1821 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1822 sfinfo.generic.in.file.handle = h;
1823 sfinfo.position_information.in.position = 0x1000;
1824 status = smb2_setinfo_file(tree, &sfinfo);
1825 CHECK_STATUS(status, NT_STATUS_OK);
1827 ZERO_STRUCT(qfinfo);
1828 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1829 qfinfo.generic.in.file.handle = h;
1830 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1831 CHECK_STATUS(status, NT_STATUS_OK);
1832 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1833 pos = qfinfo.position_information.out.position;
1834 torture_comment(tctx, "position: %llu\n",
1835 (unsigned long long)pos);
1837 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1839 /* tcp disconnect */
1840 talloc_free(tree);
1841 tree = NULL;
1843 /* do a session reconnect */
1844 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1845 &options, &tree))
1847 torture_warning(tctx, "couldn't reconnect, bailing\n");
1848 ret = false;
1849 goto done;
1852 ZERO_STRUCT(qfinfo);
1853 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1854 qfinfo.generic.in.file.handle = h;
1855 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1856 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1858 ZERO_STRUCT(io);
1859 io.in.fname = fname;
1860 io.in.durable_handle = &h;
1862 status = smb2_create(tree, mem_ctx, &io);
1863 CHECK_STATUS(status, NT_STATUS_OK);
1864 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1865 CHECK_VAL(io.out.reserved, 0x00);
1866 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1867 CHECK_VAL(io.out.size, 0);
1868 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1869 CHECK_VAL(io.out.reserved2, 0);
1871 h = io.out.file.handle;
1873 ZERO_STRUCT(qfinfo);
1874 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1875 qfinfo.generic.in.file.handle = h;
1876 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1877 CHECK_STATUS(status, NT_STATUS_OK);
1878 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1879 pos = qfinfo.position_information.out.position;
1880 torture_comment(tctx, "position: %llu\n",
1881 (unsigned long long)pos);
1883 smb2_util_close(tree, h);
1885 talloc_free(mem_ctx);
1887 smb2_util_unlink(tree, fname);
1889 done:
1890 talloc_free(tree);
1892 return ret;
1896 Open, disconnect, oplock break, reconnect.
1898 static bool test_durable_open_oplock(struct torture_context *tctx,
1899 struct smb2_tree *tree1,
1900 struct smb2_tree *tree2)
1902 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1903 struct smb2_create io1, io2;
1904 struct smb2_handle h1 = {{0}};
1905 struct smb2_handle h2 = {{0}};
1906 NTSTATUS status;
1907 char fname[256];
1908 bool ret = true;
1910 /* Choose a random name in case the state is left a little funky. */
1911 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1913 /* Clean slate */
1914 smb2_util_unlink(tree1, fname);
1916 /* Create with batch oplock */
1917 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1918 io1.in.durable_open = true;
1920 io2 = io1;
1921 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1923 status = smb2_create(tree1, mem_ctx, &io1);
1924 CHECK_STATUS(status, NT_STATUS_OK);
1925 h1 = io1.out.file.handle;
1926 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1927 CHECK_VAL(io1.out.durable_open, true);
1928 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1930 /* Disconnect after getting the batch */
1931 talloc_free(tree1);
1932 tree1 = NULL;
1935 * Windows7 (build 7000) will break a batch oplock immediately if the
1936 * original client is gone. (ZML: This seems like a bug. It should give
1937 * some time for the client to reconnect!)
1939 status = smb2_create(tree2, mem_ctx, &io2);
1940 CHECK_STATUS(status, NT_STATUS_OK);
1941 h2 = io2.out.file.handle;
1942 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1943 CHECK_VAL(io2.out.durable_open, true);
1944 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1946 /* What if tree1 tries to come back and reclaim? */
1947 if (!torture_smb2_connection(tctx, &tree1)) {
1948 torture_warning(tctx, "couldn't reconnect, bailing\n");
1949 ret = false;
1950 goto done;
1953 ZERO_STRUCT(io1);
1954 io1.in.fname = fname;
1955 io1.in.durable_handle = &h1;
1957 status = smb2_create(tree1, mem_ctx, &io1);
1958 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1960 done:
1961 smb2_util_close(tree2, h2);
1962 smb2_util_unlink(tree2, fname);
1964 talloc_free(tree1);
1965 talloc_free(tree2);
1967 return ret;
1971 Open, disconnect, lease break, reconnect.
1973 static bool test_durable_open_lease(struct torture_context *tctx,
1974 struct smb2_tree *tree1,
1975 struct smb2_tree *tree2)
1977 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1978 struct smb2_create io1, io2;
1979 struct smb2_lease ls1, ls2;
1980 struct smb2_handle h1 = {{0}};
1981 struct smb2_handle h2 = {{0}};
1982 NTSTATUS status;
1983 char fname[256];
1984 bool ret = true;
1985 uint64_t lease1, lease2;
1986 uint32_t caps;
1988 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1989 if (!(caps & SMB2_CAP_LEASING)) {
1990 torture_skip(tctx, "leases are not supported");
1994 * Choose a random name and random lease in case the state is left a
1995 * little funky.
1997 lease1 = random();
1998 lease2 = random();
1999 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
2001 /* Clean slate */
2002 smb2_util_unlink(tree1, fname);
2004 /* Create with lease */
2005 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
2006 lease1, smb2_util_lease_state("RHW"));
2007 io1.in.durable_open = true;
2009 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
2010 lease2, smb2_util_lease_state("RHW"));
2011 io2.in.durable_open = true;
2012 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
2014 status = smb2_create(tree1, mem_ctx, &io1);
2015 CHECK_STATUS(status, NT_STATUS_OK);
2016 h1 = io1.out.file.handle;
2017 CHECK_VAL(io1.out.durable_open, true);
2018 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2020 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2021 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
2022 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
2023 CHECK_VAL(io1.out.lease_response.lease_state,
2024 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2026 /* Disconnect after getting the lease */
2027 talloc_free(tree1);
2028 tree1 = NULL;
2031 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
2032 * even if the original client is gone. (ZML: This seems like a bug. It
2033 * should give some time for the client to reconnect! And why RH?)
2035 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
2036 * Test is adapted accordingly.
2038 status = smb2_create(tree2, mem_ctx, &io2);
2039 CHECK_STATUS(status, NT_STATUS_OK);
2040 h2 = io2.out.file.handle;
2041 CHECK_VAL(io2.out.durable_open, true);
2042 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2044 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2045 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
2046 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
2047 CHECK_VAL(io2.out.lease_response.lease_state,
2048 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2050 /* What if tree1 tries to come back and reclaim? */
2051 if (!torture_smb2_connection(tctx, &tree1)) {
2052 torture_warning(tctx, "couldn't reconnect, bailing\n");
2053 ret = false;
2054 goto done;
2057 ZERO_STRUCT(io1);
2058 io1.in.fname = fname;
2059 io1.in.durable_handle = &h1;
2060 io1.in.lease_request = &ls1;
2062 status = smb2_create(tree1, mem_ctx, &io1);
2063 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2065 done:
2066 smb2_util_close(tree2, h2);
2067 smb2_util_unlink(tree2, fname);
2069 talloc_free(tree1);
2070 talloc_free(tree2);
2072 return ret;
2075 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
2076 struct smb2_tree *tree)
2078 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2079 struct smb2_create io;
2080 struct smb2_handle h = {{0}};
2081 struct smb2_lock lck;
2082 struct smb2_lock_element el[2];
2083 NTSTATUS status;
2084 char fname[256];
2085 bool ret = true;
2089 snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
2091 /* Clean slate */
2092 smb2_util_unlink(tree, fname);
2094 /* Create with oplock */
2096 smb2_oplock_create_share(&io, fname,
2097 smb2_util_share_access(""),
2098 smb2_util_oplock_level("b"));
2099 io.in.durable_open = true;
2101 status = smb2_create(tree, mem_ctx, &io);
2102 CHECK_STATUS(status, NT_STATUS_OK);
2103 h = io.out.file.handle;
2104 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2106 CHECK_VAL(io.out.durable_open, true);
2107 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2109 ZERO_STRUCT(lck);
2110 ZERO_STRUCT(el);
2111 lck.in.locks = el;
2112 lck.in.lock_count = 0x0001;
2113 lck.in.lock_sequence = 0x00000000;
2114 lck.in.file.handle = h;
2115 el[0].offset = 0;
2116 el[0].length = 1;
2117 el[0].reserved = 0x00000000;
2118 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2119 status = smb2_lock(tree, &lck);
2120 CHECK_STATUS(status, NT_STATUS_OK);
2122 /* Disconnect/Reconnect. */
2123 talloc_free(tree);
2124 tree = NULL;
2126 if (!torture_smb2_connection(tctx, &tree)) {
2127 torture_warning(tctx, "couldn't reconnect, bailing\n");
2128 ret = false;
2129 goto done;
2132 ZERO_STRUCT(io);
2133 io.in.fname = fname;
2134 io.in.durable_handle = &h;
2136 status = smb2_create(tree, mem_ctx, &io);
2137 CHECK_STATUS(status, NT_STATUS_OK);
2138 h = io.out.file.handle;
2140 lck.in.file.handle = h;
2141 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2142 status = smb2_lock(tree, &lck);
2143 CHECK_STATUS(status, NT_STATUS_OK);
2145 done:
2146 smb2_util_close(tree, h);
2147 smb2_util_unlink(tree, fname);
2148 talloc_free(tree);
2150 return ret;
2154 Open(RWH), take BRL, disconnect, reconnect.
2156 static bool test_durable_open_lock_lease(struct torture_context *tctx,
2157 struct smb2_tree *tree)
2159 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2160 struct smb2_create io;
2161 struct smb2_lease ls;
2162 struct smb2_handle h = {{0}};
2163 struct smb2_lock lck;
2164 struct smb2_lock_element el[2];
2165 NTSTATUS status;
2166 char fname[256];
2167 bool ret = true;
2168 uint64_t lease;
2169 uint32_t caps;
2170 struct smbcli_options options;
2172 options = tree->session->transport->options;
2174 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2175 if (!(caps & SMB2_CAP_LEASING)) {
2176 torture_skip(tctx, "leases are not supported");
2180 * Choose a random name and random lease in case the state is left a
2181 * little funky.
2183 lease = random();
2184 snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2186 /* Clean slate */
2187 smb2_util_unlink(tree, fname);
2189 /* Create with lease */
2191 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2192 smb2_util_lease_state("RWH"));
2193 io.in.durable_open = true;
2195 status = smb2_create(tree, mem_ctx, &io);
2196 CHECK_STATUS(status, NT_STATUS_OK);
2197 h = io.out.file.handle;
2198 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2200 CHECK_VAL(io.out.durable_open, true);
2201 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2202 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2203 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2204 CHECK_VAL(io.out.lease_response.lease_state,
2205 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2207 ZERO_STRUCT(lck);
2208 ZERO_STRUCT(el);
2209 lck.in.locks = el;
2210 lck.in.lock_count = 0x0001;
2211 lck.in.lock_sequence = 0x00000000;
2212 lck.in.file.handle = h;
2213 el[0].offset = 0;
2214 el[0].length = 1;
2215 el[0].reserved = 0x00000000;
2216 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2217 status = smb2_lock(tree, &lck);
2218 CHECK_STATUS(status, NT_STATUS_OK);
2220 /* Disconnect/Reconnect. */
2221 talloc_free(tree);
2222 tree = NULL;
2224 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2225 torture_warning(tctx, "couldn't reconnect, bailing\n");
2226 ret = false;
2227 goto done;
2230 ZERO_STRUCT(io);
2231 io.in.fname = fname;
2232 io.in.durable_handle = &h;
2233 io.in.lease_request = &ls;
2235 status = smb2_create(tree, mem_ctx, &io);
2236 CHECK_STATUS(status, NT_STATUS_OK);
2237 h = io.out.file.handle;
2239 lck.in.file.handle = h;
2240 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2241 status = smb2_lock(tree, &lck);
2242 CHECK_STATUS(status, NT_STATUS_OK);
2244 done:
2245 smb2_util_close(tree, h);
2246 smb2_util_unlink(tree, fname);
2247 talloc_free(tree);
2249 return ret;
2253 Open(RH), take BRL, disconnect, fails reconnect without W LEASE
2255 static bool test_durable_open_lock_noW_lease(struct torture_context *tctx,
2256 struct smb2_tree *tree)
2258 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2259 struct smb2_create io;
2260 struct smb2_lease ls;
2261 struct smb2_handle h = {{0}};
2262 struct smb2_lock lck;
2263 struct smb2_lock_element el[2];
2264 NTSTATUS status;
2265 char fname[256];
2266 bool ret = true;
2267 uint64_t lease;
2268 uint32_t caps;
2269 struct smbcli_options options;
2271 options = tree->session->transport->options;
2273 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2274 if (!(caps & SMB2_CAP_LEASING)) {
2275 torture_skip(tctx, "leases are not supported");
2279 * Choose a random name and random lease in case the state is left a
2280 * little funky.
2282 lease = random();
2283 snprintf(fname, 256, "durable_open_lease_noW_lock_%s.dat", generate_random_str(tctx, 8));
2285 /* Clean slate */
2286 smb2_util_unlink(tree, fname);
2288 /* Create with lease */
2290 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2291 smb2_util_lease_state("RH"));
2292 io.in.durable_open = true;
2294 status = smb2_create(tree, mem_ctx, &io);
2295 CHECK_STATUS(status, NT_STATUS_OK);
2296 h = io.out.file.handle;
2297 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2299 CHECK_VAL(io.out.durable_open, true);
2300 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2301 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2302 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2303 CHECK_VAL(io.out.lease_response.lease_state,
2304 SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
2306 ZERO_STRUCT(lck);
2307 ZERO_STRUCT(el);
2308 lck.in.locks = el;
2309 lck.in.lock_count = 0x0001;
2310 lck.in.lock_sequence = 0x00000000;
2311 lck.in.file.handle = h;
2312 el[0].offset = 0;
2313 el[0].length = 1;
2314 el[0].reserved = 0x00000000;
2315 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2316 status = smb2_lock(tree, &lck);
2317 CHECK_STATUS(status, NT_STATUS_OK);
2319 /* Disconnect/Reconnect. */
2320 talloc_free(tree);
2321 tree = NULL;
2323 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2324 torture_warning(tctx, "couldn't reconnect, bailing\n");
2325 ret = false;
2326 goto done;
2329 ZERO_STRUCT(io);
2330 io.in.fname = fname;
2331 io.in.durable_handle = &h;
2332 io.in.lease_request = &ls;
2334 status = smb2_create(tree, mem_ctx, &io);
2335 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2336 h = io.out.file.handle;
2338 done:
2339 smb2_util_close(tree, h);
2340 smb2_util_unlink(tree, fname);
2341 talloc_free(tree);
2343 return ret;
2347 * Open with a RH lease, disconnect, open in another tree, reconnect.
2349 * This test actually demonstrates a minimum level of respect for the durable
2350 * open in the face of another open. As long as this test shows an inability to
2351 * reconnect after an open, the oplock/lease tests above will certainly
2352 * demonstrate an error on reconnect.
2354 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2355 struct smb2_tree *tree1,
2356 struct smb2_tree *tree2)
2358 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2359 struct smb2_create io1, io2;
2360 struct smb2_lease ls;
2361 struct smb2_handle h1 = {{0}};
2362 struct smb2_handle h2 = {{0}};
2363 NTSTATUS status;
2364 char fname[256];
2365 bool ret = true;
2366 uint64_t lease;
2367 uint32_t caps;
2368 struct smbcli_options options;
2370 options = tree1->session->transport->options;
2372 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2373 if (!(caps & SMB2_CAP_LEASING)) {
2374 torture_skip(tctx, "leases are not supported");
2378 * Choose a random name and random lease in case the state is left a
2379 * little funky.
2381 lease = random();
2382 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2383 generate_random_str(tctx, 8));
2385 /* Clean slate */
2386 smb2_util_unlink(tree1, fname);
2388 /* Create with lease */
2389 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2390 smb2_util_share_access(""),
2391 lease,
2392 smb2_util_lease_state("RH"));
2393 io1.in.durable_open = true;
2395 status = smb2_create(tree1, mem_ctx, &io1);
2396 CHECK_STATUS(status, NT_STATUS_OK);
2397 h1 = io1.out.file.handle;
2398 CHECK_VAL(io1.out.durable_open, true);
2399 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2401 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2402 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2403 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2404 CHECK_VAL(io1.out.lease_response.lease_state,
2405 smb2_util_lease_state("RH"));
2407 /* Disconnect */
2408 talloc_free(tree1);
2409 tree1 = NULL;
2411 /* Open the file in tree2 */
2412 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2414 status = smb2_create(tree2, mem_ctx, &io2);
2415 CHECK_STATUS(status, NT_STATUS_OK);
2416 h2 = io2.out.file.handle;
2417 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2419 /* Reconnect */
2420 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2421 torture_warning(tctx, "couldn't reconnect, bailing\n");
2422 ret = false;
2423 goto done;
2426 ZERO_STRUCT(io1);
2427 io1.in.fname = fname;
2428 io1.in.durable_handle = &h1;
2429 io1.in.lease_request = &ls;
2432 * Windows7 (build 7000) will give away an open immediately if the
2433 * original client is gone. (ZML: This seems like a bug. It should give
2434 * some time for the client to reconnect!)
2436 status = smb2_create(tree1, mem_ctx, &io1);
2437 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2438 h1 = io1.out.file.handle;
2440 done:
2441 if (tree1 != NULL){
2442 smb2_util_close(tree1, h1);
2443 smb2_util_unlink(tree1, fname);
2444 talloc_free(tree1);
2447 smb2_util_close(tree2, h2);
2448 smb2_util_unlink(tree2, fname);
2449 talloc_free(tree2);
2451 return ret;
2455 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2457 * This test actually demonstrates a minimum level of respect for the durable
2458 * open in the face of another open. As long as this test shows an inability to
2459 * reconnect after an open, the oplock/lease tests above will certainly
2460 * demonstrate an error on reconnect.
2462 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2463 struct smb2_tree *tree1,
2464 struct smb2_tree *tree2)
2466 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2467 struct smb2_create io1, io2;
2468 struct smb2_handle h1 = {{0}};
2469 struct smb2_handle h2 = {{0}};
2470 NTSTATUS status;
2471 char fname[256];
2472 bool ret = true;
2475 * Choose a random name and random lease in case the state is left a
2476 * little funky.
2478 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2479 generate_random_str(tctx, 8));
2481 /* Clean slate */
2482 smb2_util_unlink(tree1, fname);
2484 /* Create with batch oplock */
2485 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2486 io1.in.durable_open = true;
2488 status = smb2_create(tree1, mem_ctx, &io1);
2489 CHECK_STATUS(status, NT_STATUS_OK);
2490 h1 = io1.out.file.handle;
2491 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2492 CHECK_VAL(io1.out.durable_open, true);
2493 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2495 /* Disconnect */
2496 talloc_free(tree1);
2497 tree1 = NULL;
2499 /* Open the file in tree2 */
2500 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2502 status = smb2_create(tree2, mem_ctx, &io2);
2503 CHECK_STATUS(status, NT_STATUS_OK);
2504 h2 = io2.out.file.handle;
2505 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2507 /* Reconnect */
2508 if (!torture_smb2_connection(tctx, &tree1)) {
2509 torture_warning(tctx, "couldn't reconnect, bailing\n");
2510 ret = false;
2511 goto done;
2514 ZERO_STRUCT(io1);
2515 io1.in.fname = fname;
2516 io1.in.durable_handle = &h1;
2518 status = smb2_create(tree1, mem_ctx, &io1);
2519 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2520 h1 = io1.out.file.handle;
2522 done:
2523 smb2_util_close(tree2, h2);
2524 smb2_util_unlink(tree2, fname);
2525 if (tree1 != NULL) {
2526 smb2_util_close(tree1, h1);
2527 smb2_util_unlink(tree1, fname);
2530 talloc_free(tree1);
2531 talloc_free(tree2);
2533 return ret;
2537 * test behaviour with initial allocation size
2539 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2540 struct smb2_tree *tree)
2542 NTSTATUS status;
2543 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2544 char fname[256];
2545 struct smb2_handle _h;
2546 struct smb2_handle *h = NULL;
2547 struct smb2_create io;
2548 bool ret = true;
2549 uint64_t previous_session_id;
2550 uint64_t alloc_size_step;
2551 uint64_t initial_alloc_size = 0x1000;
2552 const uint8_t *b = NULL;
2553 struct smbcli_options options;
2555 options = tree->session->transport->options;
2557 /* Choose a random name in case the state is left a little funky. */
2558 snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2559 generate_random_str(tctx, 8));
2561 smb2_util_unlink(tree, fname);
2563 smb2_oplock_create_share(&io, fname,
2564 smb2_util_share_access(""),
2565 smb2_util_oplock_level("b"));
2566 io.in.durable_open = true;
2567 io.in.alloc_size = initial_alloc_size;
2569 status = smb2_create(tree, mem_ctx, &io);
2570 CHECK_STATUS(status, NT_STATUS_OK);
2571 _h = io.out.file.handle;
2572 h = &_h;
2573 CHECK_NOT_VAL(io.out.alloc_size, 0);
2574 alloc_size_step = io.out.alloc_size;
2575 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2576 alloc_size_step, 0);
2577 CHECK_VAL(io.out.durable_open, true);
2578 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2580 /* prepare buffer */
2581 b = talloc_zero_size(mem_ctx, alloc_size_step);
2582 CHECK_NOT_NULL(b);
2584 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2586 /* disconnect, reconnect and then do durable reopen */
2587 talloc_free(tree);
2588 tree = NULL;
2590 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2591 &options, &tree))
2593 torture_warning(tctx, "couldn't reconnect, bailing\n");
2594 ret = false;
2595 goto done;
2598 ZERO_STRUCT(io);
2599 io.in.fname = fname;
2600 io.in.durable_handle = h;
2601 h = NULL;
2603 status = smb2_create(tree, mem_ctx, &io);
2604 CHECK_STATUS(status, NT_STATUS_OK);
2605 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2606 alloc_size_step, 0);
2607 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2608 _h = io.out.file.handle;
2609 h = &_h;
2611 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2613 /* write one byte */
2614 status = smb2_util_write(tree, *h, b, 0, 1);
2615 CHECK_STATUS(status, NT_STATUS_OK);
2617 /* disconnect, reconnect and then do durable reopen */
2618 talloc_free(tree);
2619 tree = NULL;
2621 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2622 &options, &tree))
2624 torture_warning(tctx, "couldn't reconnect, bailing\n");
2625 ret = false;
2626 goto done;
2629 ZERO_STRUCT(io);
2630 io.in.fname = fname;
2631 io.in.durable_handle = h;
2632 h = NULL;
2634 status = smb2_create(tree, mem_ctx, &io);
2635 CHECK_STATUS(status, NT_STATUS_OK);
2636 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2637 alloc_size_step, 1);
2638 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2639 _h = io.out.file.handle;
2640 h = &_h;
2642 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2644 /* write more byte than initial allocation size */
2645 status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2647 /* disconnect, reconnect and then do durable reopen */
2648 talloc_free(tree);
2649 tree = NULL;
2651 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2652 &options, &tree))
2654 torture_warning(tctx, "couldn't reconnect, bailing\n");
2655 ret = false;
2656 goto done;
2659 ZERO_STRUCT(io);
2660 io.in.fname = fname;
2661 io.in.durable_handle = h;
2662 h = NULL;
2664 status = smb2_create(tree, mem_ctx, &io);
2665 CHECK_STATUS(status, NT_STATUS_OK);
2666 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2667 alloc_size_step * 2, alloc_size_step + 1);
2668 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2669 _h = io.out.file.handle;
2670 h = &_h;
2672 done:
2673 if (h != NULL) {
2674 smb2_util_close(tree, *h);
2677 smb2_util_unlink(tree, fname);
2679 talloc_free(tree);
2681 talloc_free(mem_ctx);
2683 return ret;
2687 * test behaviour when a disconnect happens while creating a read-only file
2689 static bool test_durable_open_read_only(struct torture_context *tctx,
2690 struct smb2_tree *tree)
2692 NTSTATUS status;
2693 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2694 char fname[256];
2695 struct smb2_handle _h;
2696 struct smb2_handle *h = NULL;
2697 struct smb2_create io;
2698 bool ret = true;
2699 uint64_t previous_session_id;
2700 const uint8_t b = 0;
2701 uint64_t alloc_size = 0;
2702 struct smbcli_options options;
2704 options = tree->session->transport->options;
2706 /* Choose a random name in case the state is left a little funky. */
2707 snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2708 generate_random_str(tctx, 8));
2710 smb2_util_unlink(tree, fname);
2712 smb2_oplock_create_share(&io, fname,
2713 smb2_util_share_access(""),
2714 smb2_util_oplock_level("b"));
2715 io.in.durable_open = true;
2716 io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2718 status = smb2_create(tree, mem_ctx, &io);
2719 CHECK_STATUS(status, NT_STATUS_OK);
2720 _h = io.out.file.handle;
2721 h = &_h;
2722 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2723 CHECK_VAL(io.out.durable_open, true);
2724 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2726 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2728 /* write one byte */
2729 status = smb2_util_write(tree, *h, &b, 0, 1);
2730 CHECK_STATUS(status, NT_STATUS_OK);
2732 /* disconnect, reconnect and then do durable reopen */
2733 talloc_free(tree);
2734 tree = NULL;
2736 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2737 &options, &tree))
2739 torture_warning(tctx, "couldn't reconnect, bailing\n");
2740 ret = false;
2741 goto done;
2744 ZERO_STRUCT(io);
2745 io.in.fname = fname;
2746 io.in.durable_handle = h;
2747 h = NULL;
2749 status = smb2_create(tree, mem_ctx, &io);
2750 CHECK_STATUS(status, NT_STATUS_OK);
2751 alloc_size = io.out.alloc_size;
2752 CHECK_CREATED_SIZE(&io, EXISTED,
2753 FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2754 alloc_size, 1);
2755 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2756 _h = io.out.file.handle;
2757 h = &_h;
2759 /* write one byte */
2760 status = smb2_util_write(tree, *h, &b, 1, 1);
2761 CHECK_STATUS(status, NT_STATUS_OK);
2763 done:
2764 if (h != NULL) {
2765 union smb_setfileinfo sfinfo;
2767 ZERO_STRUCT(sfinfo);
2768 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2769 sfinfo.basic_info.in.file.handle = *h;
2770 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2771 smb2_setinfo_file(tree, &sfinfo);
2773 smb2_util_close(tree, *h);
2776 smb2_util_unlink(tree, fname);
2778 talloc_free(tree);
2780 talloc_free(mem_ctx);
2782 return ret;
2786 * durable open with oplock, disconnect, exit
2788 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2789 struct smb2_tree *tree)
2791 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2792 struct smb2_create io;
2793 struct smb2_handle _h;
2794 struct smb2_handle *h = NULL;
2795 NTSTATUS status;
2796 char fname[256];
2797 bool ret = true;
2799 snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2800 generate_random_str(mem_ctx, 8));
2802 smb2_util_unlink(tree, fname);
2804 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2805 io.in.durable_open = true;
2807 status = smb2_create(tree, mem_ctx, &io);
2808 CHECK_STATUS(status, NT_STATUS_OK);
2810 _h = io.out.file.handle;
2811 h = &_h;
2813 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2814 CHECK_VAL(io.out.durable_open, true);
2815 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2817 /* disconnect */
2818 talloc_free(tree);
2819 tree = NULL;
2821 done:
2822 if (tree != NULL) {
2823 if (h != NULL) {
2824 smb2_util_close(tree, *h);
2826 smb2_util_unlink(tree, fname);
2828 talloc_free(mem_ctx);
2829 return ret;
2833 * durable stat open with lease.
2835 static bool test_durable_open_stat_open(struct torture_context *tctx,
2836 struct smb2_tree *tree)
2838 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2839 struct smb2_create io;
2840 struct smb2_handle _h;
2841 struct smb2_handle *h = NULL;
2842 struct smb2_lease ls;
2843 NTSTATUS status;
2844 char fname[256];
2845 bool ret = true;
2846 uint64_t lease;
2848 snprintf(fname, 256, "durable_open_stat_open_%s.dat",
2849 generate_random_str(mem_ctx, 8));
2851 /* Ensure file doesn't exist. */
2852 smb2_util_unlink(tree, fname);
2854 /* Create a normal file. */
2855 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
2856 status = smb2_create(tree, mem_ctx, &io);
2857 CHECK_STATUS(status, NT_STATUS_OK);
2858 _h = io.out.file.handle;
2859 h = &_h;
2860 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2861 /* Close. */
2862 smb2_util_close(tree, *h);
2863 h = NULL;
2865 /* Now try a leased, durable handle stat open. */
2866 lease = random();
2867 /* Create with lease */
2868 smb2_lease_create(&io,
2869 &ls,
2870 false /* dir */,
2871 fname,
2872 lease,
2873 smb2_util_lease_state("RH"));
2874 io.in.durable_open = true;
2875 io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
2876 io.in.create_disposition = NTCREATEX_DISP_OPEN;
2878 status = smb2_create(tree, mem_ctx, &io);
2879 CHECK_STATUS(status, NT_STATUS_OK);
2880 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2881 CHECK_VAL(io.out.durable_open, true);
2882 _h = io.out.file.handle;
2883 h = &_h;
2885 done:
2886 if (h != NULL) {
2887 smb2_util_close(tree, *h);
2889 smb2_util_unlink(tree, fname);
2890 talloc_free(mem_ctx);
2891 return ret;
2894 struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
2896 struct torture_suite *suite =
2897 torture_suite_create(ctx, "durable-open");
2899 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2900 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2901 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2902 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2903 torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
2904 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2905 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2906 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2907 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2908 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2909 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2910 torture_suite_add_1smb2_test(suite, "delete_on_close1",
2911 test_durable_open_delete_on_close1);
2912 torture_suite_add_1smb2_test(suite, "delete_on_close2",
2913 test_durable_open_delete_on_close2);
2914 torture_suite_add_1smb2_test(suite, "file-position",
2915 test_durable_open_file_position);
2916 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2917 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2918 torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2919 torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2920 torture_suite_add_1smb2_test(suite, "lock-noW-lease", test_durable_open_lock_noW_lease);
2921 torture_suite_add_2smb2_test(suite, "open2-lease",
2922 test_durable_open_open2_lease);
2923 torture_suite_add_2smb2_test(suite, "open2-oplock",
2924 test_durable_open_open2_oplock);
2925 torture_suite_add_1smb2_test(suite, "alloc-size",
2926 test_durable_open_alloc_size);
2927 torture_suite_add_1smb2_test(suite, "read-only",
2928 test_durable_open_read_only);
2929 torture_suite_add_1smb2_test(suite, "stat-open",
2930 test_durable_open_stat_open);
2932 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2934 return suite;
2937 struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
2939 struct torture_suite *suite =
2940 torture_suite_create(ctx,
2941 "durable-open-disconnect");
2943 torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2944 test_durable_open_oplock_disconnect);
2946 suite->description = talloc_strdup(suite,
2947 "SMB2-DURABLE-OPEN-DISCONNECT tests");
2949 return suite;