s4:torture:smb2: add durable_open.reopen2a - variant of reopen2 with session reconnect
[Samba/vl.git] / source4 / torture / smb2 / durable_open.c
blob6a3f52b3810f38bab6e95821171d5f49658d08a3
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
27 #include "../libcli/smb/smbXcli_base.h"
29 #define CHECK_VAL(v, correct) do { \
30 if ((v) != (correct)) { \
31 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
32 __location__, #v, (int)v, (int)correct); \
33 ret = false; \
34 }} while (0)
36 #define CHECK_STATUS(status, correct) do { \
37 if (!NT_STATUS_EQUAL(status, correct)) { \
38 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
39 nt_errstr(status), nt_errstr(correct)); \
40 ret = false; \
41 goto done; \
42 }} while (0)
44 #define CHECK_CREATED(__io, __created, __attribute) \
45 do { \
46 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
47 CHECK_VAL((__io)->out.alloc_size, 0); \
48 CHECK_VAL((__io)->out.size, 0); \
49 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
50 CHECK_VAL((__io)->out.reserved2, 0); \
51 } while(0)
54 /**
55 * basic durable_open test.
56 * durable state should only be granted when requested
57 * along with a batch oplock or a handle lease.
59 * This test tests durable open with all possible oplock types.
62 struct durable_open_vs_oplock {
63 const char *level;
64 const char *share_mode;
65 bool expected;
68 #define NUM_OPLOCK_TYPES 4
69 #define NUM_SHARE_MODES 8
70 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
71 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
73 { "", "", false },
74 { "", "R", false },
75 { "", "W", false },
76 { "", "D", false },
77 { "", "RD", false },
78 { "", "RW", false },
79 { "", "WD", false },
80 { "", "RWD", false },
82 { "s", "", false },
83 { "s", "R", false },
84 { "s", "W", false },
85 { "s", "D", false },
86 { "s", "RD", false },
87 { "s", "RW", false },
88 { "s", "WD", false },
89 { "s", "RWD", false },
91 { "x", "", false },
92 { "x", "R", false },
93 { "x", "W", false },
94 { "x", "D", false },
95 { "x", "RD", false },
96 { "x", "RW", false },
97 { "x", "WD", false },
98 { "x", "RWD", false },
100 { "b", "", true },
101 { "b", "R", true },
102 { "b", "W", true },
103 { "b", "D", true },
104 { "b", "RD", true },
105 { "b", "RW", true },
106 { "b", "WD", true },
107 { "b", "RWD", true },
110 static bool test_one_durable_open_open1(struct torture_context *tctx,
111 struct smb2_tree *tree,
112 const char *fname,
113 struct durable_open_vs_oplock test)
115 NTSTATUS status;
116 TALLOC_CTX *mem_ctx = talloc_new(tctx);
117 struct smb2_handle _h;
118 struct smb2_handle *h = NULL;
119 bool ret = true;
120 struct smb2_create io;
122 smb2_util_unlink(tree, fname);
124 smb2_oplock_create_share(&io, fname,
125 smb2_util_share_access(test.share_mode),
126 smb2_util_oplock_level(test.level));
127 io.in.durable_open = true;
129 status = smb2_create(tree, mem_ctx, &io);
130 CHECK_STATUS(status, NT_STATUS_OK);
131 _h = io.out.file.handle;
132 h = &_h;
133 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
134 CHECK_VAL(io.out.durable_open, test.expected);
135 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
137 done:
138 if (h != NULL) {
139 smb2_util_close(tree, *h);
141 smb2_util_unlink(tree, fname);
142 talloc_free(mem_ctx);
144 return ret;
147 bool test_durable_open_open1(struct torture_context *tctx,
148 struct smb2_tree *tree)
150 TALLOC_CTX *mem_ctx = talloc_new(tctx);
151 char fname[256];
152 bool ret = true;
153 int i;
155 /* Choose a random name in case the state is left a little funky. */
156 snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
158 smb2_util_unlink(tree, fname);
160 /* test various oplock levels with durable open */
162 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
163 ret = test_one_durable_open_open1(tctx,
164 tree,
165 fname,
166 durable_open_vs_oplock_table[i]);
167 if (ret == false) {
168 goto done;
172 done:
173 smb2_util_unlink(tree, fname);
174 talloc_free(tree);
175 talloc_free(mem_ctx);
177 return ret;
181 * basic durable_open test.
182 * durable state should only be granted when requested
183 * along with a batch oplock or a handle lease.
185 * This test tests durable open with all valid lease types.
188 struct durable_open_vs_lease {
189 const char *type;
190 const char *share_mode;
191 bool expected;
194 #define NUM_LEASE_TYPES 5
195 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
196 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
198 { "", "", false },
199 { "", "R", false },
200 { "", "W", false },
201 { "", "D", false },
202 { "", "RW", false },
203 { "", "RD", false },
204 { "", "WD", false },
205 { "", "RWD", false },
207 { "R", "", false },
208 { "R", "R", false },
209 { "R", "W", false },
210 { "R", "D", false },
211 { "R", "RW", false },
212 { "R", "RD", false },
213 { "R", "DW", false },
214 { "R", "RWD", false },
216 { "RW", "", false },
217 { "RW", "R", false },
218 { "RW", "W", false },
219 { "RW", "D", false },
220 { "RW", "RW", false },
221 { "RW", "RD", false },
222 { "RW", "WD", false },
223 { "RW", "RWD", false },
225 { "RH", "", true },
226 { "RH", "R", true },
227 { "RH", "W", true },
228 { "RH", "D", true },
229 { "RH", "RW", true },
230 { "RH", "RD", true },
231 { "RH", "WD", true },
232 { "RH", "RWD", true },
234 { "RHW", "", true },
235 { "RHW", "R", true },
236 { "RHW", "W", true },
237 { "RHW", "D", true },
238 { "RHW", "RW", true },
239 { "RHW", "RD", true },
240 { "RHW", "WD", true },
241 { "RHW", "RWD", true },
244 static bool test_one_durable_open_open2(struct torture_context *tctx,
245 struct smb2_tree *tree,
246 const char *fname,
247 struct durable_open_vs_lease test)
249 NTSTATUS status;
250 TALLOC_CTX *mem_ctx = talloc_new(tctx);
251 struct smb2_handle _h;
252 struct smb2_handle *h = NULL;
253 bool ret = true;
254 struct smb2_create io;
255 struct smb2_lease ls;
256 uint64_t lease;
258 smb2_util_unlink(tree, fname);
260 lease = random();
262 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
263 smb2_util_share_access(test.share_mode),
264 lease,
265 smb2_util_lease_state(test.type));
266 io.in.durable_open = true;
268 status = smb2_create(tree, mem_ctx, &io);
269 CHECK_STATUS(status, NT_STATUS_OK);
270 _h = io.out.file.handle;
271 h = &_h;
272 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
273 CHECK_VAL(io.out.durable_open, test.expected);
274 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
275 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
276 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
277 CHECK_VAL(io.out.lease_response.lease_state,
278 smb2_util_lease_state(test.type));
279 done:
280 if (h != NULL) {
281 smb2_util_close(tree, *h);
283 smb2_util_unlink(tree, fname);
284 talloc_free(mem_ctx);
286 return ret;
289 bool test_durable_open_open2(struct torture_context *tctx,
290 struct smb2_tree *tree)
292 TALLOC_CTX *mem_ctx = talloc_new(tctx);
293 char fname[256];
294 bool ret = true;
295 int i;
297 /* Choose a random name in case the state is left a little funky. */
298 snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
300 smb2_util_unlink(tree, fname);
303 /* test various oplock levels with durable open */
305 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
306 ret = test_one_durable_open_open2(tctx,
307 tree,
308 fname,
309 durable_open_vs_lease_table[i]);
310 if (ret == false) {
311 goto done;
315 done:
316 smb2_util_unlink(tree, fname);
317 talloc_free(tree);
318 talloc_free(mem_ctx);
320 return ret;
324 * basic test for doing a durable open
325 * and do a durable reopen on the same connection
326 * while the first open is still active (fails)
328 bool test_durable_open_reopen1(struct torture_context *tctx,
329 struct smb2_tree *tree)
331 NTSTATUS status;
332 TALLOC_CTX *mem_ctx = talloc_new(tctx);
333 char fname[256];
334 struct smb2_handle _h;
335 struct smb2_handle *h = NULL;
336 struct smb2_create io1, io2;
337 bool ret = true;
339 /* Choose a random name in case the state is left a little funky. */
340 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
341 generate_random_str(tctx, 8));
343 smb2_util_unlink(tree, fname);
345 smb2_oplock_create_share(&io1, fname,
346 smb2_util_share_access(""),
347 smb2_util_oplock_level("b"));
348 io1.in.durable_open = true;
350 status = smb2_create(tree, mem_ctx, &io1);
351 CHECK_STATUS(status, NT_STATUS_OK);
352 _h = io1.out.file.handle;
353 h = &_h;
354 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
355 CHECK_VAL(io1.out.durable_open, true);
356 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
358 /* try a durable reconnect while the file is still open */
359 ZERO_STRUCT(io2);
360 io2.in.fname = fname;
361 io2.in.durable_handle = h;
363 status = smb2_create(tree, mem_ctx, &io2);
364 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
366 done:
367 if (h != NULL) {
368 smb2_util_close(tree, *h);
371 smb2_util_unlink(tree, fname);
373 talloc_free(tree);
375 talloc_free(mem_ctx);
377 return ret;
381 * basic test for doing a durable open
382 * tcp disconnect, reconnect, do a durable reopen (succeeds)
384 bool test_durable_open_reopen2(struct torture_context *tctx,
385 struct smb2_tree *tree)
387 NTSTATUS status;
388 TALLOC_CTX *mem_ctx = talloc_new(tctx);
389 char fname[256];
390 struct smb2_handle _h;
391 struct smb2_handle *h = NULL;
392 struct smb2_create io1, io2;
393 bool ret = true;
395 /* Choose a random name in case the state is left a little funky. */
396 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
397 generate_random_str(tctx, 8));
399 smb2_util_unlink(tree, fname);
401 smb2_oplock_create_share(&io1, fname,
402 smb2_util_share_access(""),
403 smb2_util_oplock_level("b"));
404 io1.in.durable_open = true;
406 status = smb2_create(tree, mem_ctx, &io1);
407 CHECK_STATUS(status, NT_STATUS_OK);
408 _h = io1.out.file.handle;
409 h = &_h;
410 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
411 CHECK_VAL(io1.out.durable_open, true);
412 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
414 /* disconnect, reconnect and then do durable reopen */
415 talloc_free(tree);
416 tree = NULL;
418 if (!torture_smb2_connection(tctx, &tree)) {
419 torture_warning(tctx, "couldn't reconnect, bailing\n");
420 ret = false;
421 goto done;
424 ZERO_STRUCT(io2);
425 io2.in.fname = fname;
426 io2.in.durable_handle = h;
427 h = NULL;
429 status = smb2_create(tree, mem_ctx, &io2);
430 CHECK_STATUS(status, NT_STATUS_OK);
431 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
432 CHECK_VAL(io2.out.durable_open, true);
433 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
434 _h = io2.out.file.handle;
435 h = &_h;
437 done:
438 if (h != NULL) {
439 smb2_util_close(tree, *h);
442 smb2_util_unlink(tree, fname);
444 talloc_free(tree);
446 talloc_free(mem_ctx);
448 return ret;
452 * basic test for doing a durable open
453 * tcp disconnect, reconnect with a session reconnect and
454 * do a durable reopen (succeeds)
456 bool test_durable_open_reopen2a(struct torture_context *tctx,
457 struct smb2_tree *tree)
459 NTSTATUS status;
460 TALLOC_CTX *mem_ctx = talloc_new(tctx);
461 char fname[256];
462 struct smb2_handle _h;
463 struct smb2_handle *h = NULL;
464 struct smb2_create io1, io2;
465 uint64_t previous_session_id;
466 bool ret = true;
468 /* Choose a random name in case the state is left a little funky. */
469 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
470 generate_random_str(tctx, 8));
472 smb2_util_unlink(tree, fname);
474 smb2_oplock_create_share(&io1, fname,
475 smb2_util_share_access(""),
476 smb2_util_oplock_level("b"));
477 io1.in.durable_open = true;
479 status = smb2_create(tree, mem_ctx, &io1);
480 CHECK_STATUS(status, NT_STATUS_OK);
481 _h = io1.out.file.handle;
482 h = &_h;
483 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
484 CHECK_VAL(io1.out.durable_open, true);
485 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
487 /* disconnect, reconnect and then do durable reopen */
488 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
489 talloc_free(tree);
490 tree = NULL;
492 if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
493 torture_warning(tctx, "couldn't reconnect, bailing\n");
494 ret = false;
495 goto done;
498 ZERO_STRUCT(io2);
499 io2.in.fname = fname;
500 io2.in.durable_handle = h;
501 h = NULL;
503 status = smb2_create(tree, mem_ctx, &io2);
504 CHECK_STATUS(status, NT_STATUS_OK);
505 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
506 CHECK_VAL(io2.out.durable_open, true);
507 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
508 _h = io2.out.file.handle;
509 h = &_h;
511 done:
512 if (h != NULL) {
513 smb2_util_close(tree, *h);
516 smb2_util_unlink(tree, fname);
518 talloc_free(tree);
520 talloc_free(mem_ctx);
522 return ret;
527 * basic test for doing a durable open:
528 * tdis, new tcon, try durable reopen (fails)
530 bool test_durable_open_reopen3(struct torture_context *tctx,
531 struct smb2_tree *tree)
533 NTSTATUS status;
534 TALLOC_CTX *mem_ctx = talloc_new(tctx);
535 char fname[256];
536 struct smb2_handle _h;
537 struct smb2_handle *h = NULL;
538 struct smb2_create io1, io2;
539 bool ret = true;
540 struct smb2_tree *tree2;
542 /* Choose a random name in case the state is left a little funky. */
543 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
544 generate_random_str(tctx, 8));
546 smb2_util_unlink(tree, fname);
548 smb2_oplock_create_share(&io1, fname,
549 smb2_util_share_access(""),
550 smb2_util_oplock_level("b"));
551 io1.in.durable_open = true;
553 status = smb2_create(tree, mem_ctx, &io1);
554 CHECK_STATUS(status, NT_STATUS_OK);
555 _h = io1.out.file.handle;
556 h = &_h;
557 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
558 CHECK_VAL(io1.out.durable_open, true);
559 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
561 /* disconnect, reconnect and then do durable reopen */
562 status = smb2_tdis(tree);
563 CHECK_STATUS(status, NT_STATUS_OK);
565 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
566 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
567 ret = false;
568 goto done;
572 ZERO_STRUCT(io2);
573 io2.in.fname = fname;
574 io2.in.durable_handle = h;
576 status = smb2_create(tree2, mem_ctx, &io2);
577 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
579 done:
580 if (h != NULL) {
581 smb2_util_close(tree, *h);
584 smb2_util_unlink(tree2, fname);
586 talloc_free(tree);
588 talloc_free(mem_ctx);
590 return ret;
594 * basic test for doing a durable open:
595 * logoff, create a new session, do a durable reopen (succeeds)
597 bool test_durable_open_reopen4(struct torture_context *tctx,
598 struct smb2_tree *tree)
600 NTSTATUS status;
601 TALLOC_CTX *mem_ctx = talloc_new(tctx);
602 char fname[256];
603 struct smb2_handle _h;
604 struct smb2_handle *h = NULL;
605 struct smb2_create io1, io2;
606 bool ret = true;
607 struct smb2_transport *transport;
608 struct smb2_session *session2;
609 struct smb2_tree *tree2;
611 /* Choose a random name in case the state is left a little funky. */
612 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
613 generate_random_str(tctx, 8));
615 smb2_util_unlink(tree, fname);
617 smb2_oplock_create_share(&io1, fname,
618 smb2_util_share_access(""),
619 smb2_util_oplock_level("b"));
620 io1.in.durable_open = true;
621 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
623 status = smb2_create(tree, mem_ctx, &io1);
624 CHECK_STATUS(status, NT_STATUS_OK);
625 _h = io1.out.file.handle;
626 h = &_h;
627 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
628 CHECK_VAL(io1.out.durable_open, true);
629 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
632 * do a session logoff, establish a new session and tree
633 * connect on the same transport, and try a durable reopen
635 transport = tree->session->transport;
636 status = smb2_logoff(tree->session);
637 CHECK_STATUS(status, NT_STATUS_OK);
639 if (!torture_smb2_session_setup(tctx, transport,
640 0, /* previous_session_id */
641 mem_ctx, &session2))
643 torture_warning(tctx, "session setup failed.\n");
644 ret = false;
645 goto done;
649 * the session setup has talloc-stolen the transport,
650 * so we can safely free the old tree+session for clarity
652 TALLOC_FREE(tree);
654 ZERO_STRUCT(io2);
655 io2.in.fname = fname;
656 io2.in.durable_handle = h;
658 status = smb2_create(tree, mem_ctx, &io2);
659 CHECK_STATUS(status, NT_STATUS_NETWORK_NAME_DELETED);
661 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
662 torture_warning(tctx, "tree connect failed.\n");
663 ret = false;
664 goto done;
667 ZERO_STRUCT(io2);
668 io2.in.fname = fname;
669 io2.in.durable_handle = h;
670 h = NULL;
672 status = smb2_create(tree2, mem_ctx, &io2);
673 CHECK_STATUS(status, NT_STATUS_OK);
675 _h = io2.out.file.handle;
676 h = &_h;
677 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
678 CHECK_VAL(io2.out.durable_open, true);
679 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
681 done:
682 if (h != NULL) {
683 smb2_util_close(tree2, *h);
686 smb2_util_unlink(tree2, fname);
688 talloc_free(tree);
690 talloc_free(mem_ctx);
692 return ret;
696 basic testing of SMB2 durable opens
697 regarding the position information on the handle
699 bool test_durable_open_file_position(struct torture_context *tctx,
700 struct smb2_tree *tree1,
701 struct smb2_tree *tree2)
703 TALLOC_CTX *mem_ctx = talloc_new(tctx);
704 struct smb2_handle h1, h2;
705 struct smb2_create io1, io2;
706 NTSTATUS status;
707 const char *fname = "durable_open_position.dat";
708 union smb_fileinfo qfinfo;
709 union smb_setfileinfo sfinfo;
710 bool ret = true;
711 uint64_t pos;
713 smb2_util_unlink(tree1, fname);
715 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
716 io1.in.durable_open = true;
718 status = smb2_create(tree1, mem_ctx, &io1);
719 CHECK_STATUS(status, NT_STATUS_OK);
720 h1 = io1.out.file.handle;
721 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
722 CHECK_VAL(io1.out.durable_open, true);
723 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
725 /* TODO: check extra blob content */
727 ZERO_STRUCT(qfinfo);
728 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
729 qfinfo.generic.in.file.handle = h1;
730 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
731 CHECK_STATUS(status, NT_STATUS_OK);
732 CHECK_VAL(qfinfo.position_information.out.position, 0);
733 pos = qfinfo.position_information.out.position;
734 torture_comment(tctx, "position: %llu\n",
735 (unsigned long long)pos);
737 ZERO_STRUCT(sfinfo);
738 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
739 sfinfo.generic.in.file.handle = h1;
740 sfinfo.position_information.in.position = 0x1000;
741 status = smb2_setinfo_file(tree1, &sfinfo);
742 CHECK_STATUS(status, NT_STATUS_OK);
744 ZERO_STRUCT(qfinfo);
745 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
746 qfinfo.generic.in.file.handle = h1;
747 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
748 CHECK_STATUS(status, NT_STATUS_OK);
749 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
750 pos = qfinfo.position_information.out.position;
751 torture_comment(tctx, "position: %llu\n",
752 (unsigned long long)pos);
754 talloc_free(tree1);
755 tree1 = NULL;
757 ZERO_STRUCT(qfinfo);
758 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
759 qfinfo.generic.in.file.handle = h1;
760 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
761 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
763 ZERO_STRUCT(io2);
764 io2.in.fname = fname;
765 io2.in.durable_handle = &h1;
767 status = smb2_create(tree2, mem_ctx, &io2);
768 CHECK_STATUS(status, NT_STATUS_OK);
769 CHECK_VAL(io2.out.durable_open, true);
770 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
771 CHECK_VAL(io2.out.reserved, 0x00);
772 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
773 CHECK_VAL(io2.out.alloc_size, 0);
774 CHECK_VAL(io2.out.size, 0);
775 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
776 CHECK_VAL(io2.out.reserved2, 0);
778 h2 = io2.out.file.handle;
780 ZERO_STRUCT(qfinfo);
781 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
782 qfinfo.generic.in.file.handle = h2;
783 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
784 CHECK_STATUS(status, NT_STATUS_OK);
785 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
786 pos = qfinfo.position_information.out.position;
787 torture_comment(tctx, "position: %llu\n",
788 (unsigned long long)pos);
790 smb2_util_close(tree2, h2);
792 talloc_free(mem_ctx);
794 smb2_util_unlink(tree2, fname);
795 done:
796 talloc_free(tree1);
797 talloc_free(tree2);
799 return ret;
803 Open, disconnect, oplock break, reconnect.
805 bool test_durable_open_oplock(struct torture_context *tctx,
806 struct smb2_tree *tree1,
807 struct smb2_tree *tree2)
809 TALLOC_CTX *mem_ctx = talloc_new(tctx);
810 struct smb2_create io1, io2;
811 struct smb2_handle h1, h2;
812 NTSTATUS status;
813 char fname[256];
814 bool ret = true;
816 /* Choose a random name in case the state is left a little funky. */
817 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
819 /* Clean slate */
820 smb2_util_unlink(tree1, fname);
822 /* Create with batch oplock */
823 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
824 io1.in.durable_open = true;
826 io2 = io1;
827 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
829 status = smb2_create(tree1, mem_ctx, &io1);
830 CHECK_STATUS(status, NT_STATUS_OK);
831 h1 = io1.out.file.handle;
832 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
833 CHECK_VAL(io1.out.durable_open, true);
834 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
836 /* Disconnect after getting the batch */
837 talloc_free(tree1);
838 tree1 = NULL;
841 * Windows7 (build 7000) will break a batch oplock immediately if the
842 * original client is gone. (ZML: This seems like a bug. It should give
843 * some time for the client to reconnect!)
845 status = smb2_create(tree2, mem_ctx, &io2);
846 CHECK_STATUS(status, NT_STATUS_OK);
847 h2 = io2.out.file.handle;
848 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
849 CHECK_VAL(io2.out.durable_open, true);
850 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
852 /* What if tree1 tries to come back and reclaim? */
853 if (!torture_smb2_connection(tctx, &tree1)) {
854 torture_warning(tctx, "couldn't reconnect, bailing\n");
855 ret = false;
856 goto done;
859 ZERO_STRUCT(io1);
860 io1.in.fname = fname;
861 io1.in.durable_handle = &h1;
863 status = smb2_create(tree1, mem_ctx, &io1);
864 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
866 done:
867 smb2_util_close(tree2, h2);
868 smb2_util_unlink(tree2, fname);
870 talloc_free(tree1);
871 talloc_free(tree2);
873 return ret;
877 Open, disconnect, lease break, reconnect.
879 bool test_durable_open_lease(struct torture_context *tctx,
880 struct smb2_tree *tree1,
881 struct smb2_tree *tree2)
883 TALLOC_CTX *mem_ctx = talloc_new(tctx);
884 struct smb2_create io1, io2;
885 struct smb2_lease ls1, ls2;
886 struct smb2_handle h1, h2;
887 NTSTATUS status;
888 char fname[256];
889 bool ret = true;
890 uint64_t lease1, lease2;
893 * Choose a random name and random lease in case the state is left a
894 * little funky.
896 lease1 = random();
897 lease2 = random();
898 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
900 /* Clean slate */
901 smb2_util_unlink(tree1, fname);
903 /* Create with lease */
904 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
905 lease1, smb2_util_lease_state("RHW"));
906 io1.in.durable_open = true;
908 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
909 lease2, smb2_util_lease_state("RHW"));
910 io2.in.durable_open = true;
911 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
913 status = smb2_create(tree1, mem_ctx, &io1);
914 CHECK_STATUS(status, NT_STATUS_OK);
915 h1 = io1.out.file.handle;
916 CHECK_VAL(io1.out.durable_open, true);
917 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
919 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
920 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
921 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
922 CHECK_VAL(io1.out.lease_response.lease_state,
923 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
925 /* Disconnect after getting the lease */
926 talloc_free(tree1);
927 tree1 = NULL;
930 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
931 * even if the original client is gone. (ZML: This seems like a bug. It
932 * should give some time for the client to reconnect! And why RH?)
934 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
935 * Test is adapted accordingly.
937 status = smb2_create(tree2, mem_ctx, &io2);
938 CHECK_STATUS(status, NT_STATUS_OK);
939 h2 = io2.out.file.handle;
940 CHECK_VAL(io2.out.durable_open, true);
941 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
943 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
944 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
945 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
946 CHECK_VAL(io2.out.lease_response.lease_state,
947 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
949 /* What if tree1 tries to come back and reclaim? */
950 if (!torture_smb2_connection(tctx, &tree1)) {
951 torture_warning(tctx, "couldn't reconnect, bailing\n");
952 ret = false;
953 goto done;
956 ZERO_STRUCT(io1);
957 io1.in.fname = fname;
958 io1.in.durable_handle = &h1;
959 io1.in.lease_request = &ls1;
961 status = smb2_create(tree1, mem_ctx, &io1);
962 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
964 done:
965 smb2_util_close(tree2, h2);
966 smb2_util_unlink(tree2, fname);
968 talloc_free(tree1);
969 talloc_free(tree2);
971 return ret;
975 Open, take BRL, disconnect, reconnect.
977 bool test_durable_open_lock(struct torture_context *tctx,
978 struct smb2_tree *tree)
980 TALLOC_CTX *mem_ctx = talloc_new(tctx);
981 struct smb2_create io;
982 struct smb2_lease ls;
983 struct smb2_handle h;
984 struct smb2_lock lck;
985 struct smb2_lock_element el[2];
986 NTSTATUS status;
987 char fname[256];
988 bool ret = true;
989 uint64_t lease;
992 * Choose a random name and random lease in case the state is left a
993 * little funky.
995 lease = random();
996 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
998 /* Clean slate */
999 smb2_util_unlink(tree, fname);
1001 /* Create with lease */
1003 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1004 smb2_util_lease_state("RWH"));
1005 io.in.durable_open = true;
1007 status = smb2_create(tree, mem_ctx, &io);
1008 CHECK_STATUS(status, NT_STATUS_OK);
1009 h = io.out.file.handle;
1010 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1012 CHECK_VAL(io.out.durable_open, true);
1013 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1014 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1015 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1016 CHECK_VAL(io.out.lease_response.lease_state,
1017 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1019 ZERO_STRUCT(lck);
1020 ZERO_STRUCT(el);
1021 lck.in.locks = el;
1022 lck.in.lock_count = 0x0001;
1023 lck.in.lock_sequence = 0x00000000;
1024 lck.in.file.handle = h;
1025 el[0].offset = 0;
1026 el[0].length = 1;
1027 el[0].reserved = 0x00000000;
1028 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1029 status = smb2_lock(tree, &lck);
1030 CHECK_STATUS(status, NT_STATUS_OK);
1032 /* Disconnect/Reconnect. */
1033 talloc_free(tree);
1034 tree = NULL;
1036 if (!torture_smb2_connection(tctx, &tree)) {
1037 torture_warning(tctx, "couldn't reconnect, bailing\n");
1038 ret = false;
1039 goto done;
1042 ZERO_STRUCT(io);
1043 io.in.fname = fname;
1044 io.in.durable_handle = &h;
1045 io.in.lease_request = &ls;
1047 status = smb2_create(tree, mem_ctx, &io);
1048 CHECK_STATUS(status, NT_STATUS_OK);
1049 h = io.out.file.handle;
1051 lck.in.file.handle = h;
1052 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1053 status = smb2_lock(tree, &lck);
1054 CHECK_STATUS(status, NT_STATUS_OK);
1056 done:
1057 smb2_util_close(tree, h);
1058 smb2_util_unlink(tree, fname);
1059 talloc_free(tree);
1061 return ret;
1065 * Open with a RH lease, disconnect, open in another tree, reconnect.
1067 * This test actually demonstrates a minimum level of respect for the durable
1068 * open in the face of another open. As long as this test shows an inability to
1069 * reconnect after an open, the oplock/lease tests above will certainly
1070 * demonstrate an error on reconnect.
1072 bool test_durable_open_open_lease(struct torture_context *tctx,
1073 struct smb2_tree *tree1,
1074 struct smb2_tree *tree2)
1076 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1077 struct smb2_create io1, io2;
1078 struct smb2_lease ls;
1079 struct smb2_handle h1, h2;
1080 NTSTATUS status;
1081 char fname[256];
1082 bool ret = true;
1083 uint64_t lease;
1086 * Choose a random name and random lease in case the state is left a
1087 * little funky.
1089 lease = random();
1090 snprintf(fname, 256, "durable_open_open_lease_%s.dat",
1091 generate_random_str(tctx, 8));
1093 /* Clean slate */
1094 smb2_util_unlink(tree1, fname);
1096 /* Create with lease */
1097 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1098 smb2_util_share_access(""),
1099 lease,
1100 smb2_util_lease_state("RH"));
1101 io1.in.durable_open = true;
1103 status = smb2_create(tree1, mem_ctx, &io1);
1104 CHECK_STATUS(status, NT_STATUS_OK);
1105 h1 = io1.out.file.handle;
1106 CHECK_VAL(io1.out.durable_open, true);
1107 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1109 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1110 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1111 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1112 CHECK_VAL(io1.out.lease_response.lease_state,
1113 smb2_util_lease_state("RH"));
1115 /* Disconnect */
1116 talloc_free(tree1);
1117 tree1 = NULL;
1119 /* Open the file in tree2 */
1120 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1122 status = smb2_create(tree2, mem_ctx, &io2);
1123 CHECK_STATUS(status, NT_STATUS_OK);
1124 h2 = io2.out.file.handle;
1125 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1127 /* Reconnect */
1128 if (!torture_smb2_connection(tctx, &tree1)) {
1129 torture_warning(tctx, "couldn't reconnect, bailing\n");
1130 ret = false;
1131 goto done;
1134 ZERO_STRUCT(io1);
1135 io1.in.fname = fname;
1136 io1.in.durable_handle = &h1;
1137 io1.in.lease_request = &ls;
1140 * Windows7 (build 7000) will give away an open immediately if the
1141 * original client is gone. (ZML: This seems like a bug. It should give
1142 * some time for the client to reconnect!)
1144 status = smb2_create(tree1, mem_ctx, &io1);
1145 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1146 h1 = io1.out.file.handle;
1148 done:
1149 smb2_util_close(tree2, h2);
1150 smb2_util_unlink(tree2, fname);
1151 smb2_util_close(tree1, h1);
1152 smb2_util_unlink(tree1, fname);
1154 talloc_free(tree1);
1155 talloc_free(tree2);
1157 return ret;
1161 * Open with a batch oplock, disconnect, open in another tree, reconnect.
1163 * This test actually demonstrates a minimum level of respect for the durable
1164 * open in the face of another open. As long as this test shows an inability to
1165 * reconnect after an open, the oplock/lease tests above will certainly
1166 * demonstrate an error on reconnect.
1168 bool test_durable_open_open_oplock(struct torture_context *tctx,
1169 struct smb2_tree *tree1,
1170 struct smb2_tree *tree2)
1172 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1173 struct smb2_create io1, io2;
1174 struct smb2_lease ls;
1175 struct smb2_handle h1, h2;
1176 NTSTATUS status;
1177 char fname[256];
1178 bool ret = true;
1179 uint64_t lease;
1182 * Choose a random name and random lease in case the state is left a
1183 * little funky.
1185 lease = random();
1186 snprintf(fname, 256, "durable_open_open_oplock_%s.dat",
1187 generate_random_str(tctx, 8));
1189 /* Clean slate */
1190 smb2_util_unlink(tree1, fname);
1192 /* Create with batch oplock */
1193 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1194 io1.in.durable_open = true;
1196 status = smb2_create(tree1, mem_ctx, &io1);
1197 CHECK_STATUS(status, NT_STATUS_OK);
1198 h1 = io1.out.file.handle;
1199 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1200 CHECK_VAL(io1.out.durable_open, true);
1201 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1203 /* Disconnect */
1204 talloc_free(tree1);
1205 tree1 = NULL;
1207 /* Open the file in tree2 */
1208 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1210 status = smb2_create(tree2, mem_ctx, &io2);
1211 CHECK_STATUS(status, NT_STATUS_OK);
1212 h2 = io2.out.file.handle;
1213 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1215 /* Reconnect */
1216 if (!torture_smb2_connection(tctx, &tree1)) {
1217 torture_warning(tctx, "couldn't reconnect, bailing\n");
1218 ret = false;
1219 goto done;
1222 ZERO_STRUCT(io1);
1223 io1.in.fname = fname;
1224 io1.in.durable_handle = &h1;
1225 io1.in.lease_request = &ls;
1227 status = smb2_create(tree1, mem_ctx, &io1);
1228 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1229 h1 = io1.out.file.handle;
1231 done:
1232 smb2_util_close(tree2, h2);
1233 smb2_util_unlink(tree2, fname);
1234 smb2_util_close(tree1, h1);
1235 smb2_util_unlink(tree1, fname);
1237 talloc_free(tree1);
1238 talloc_free(tree2);
1240 return ret;
1243 struct torture_suite *torture_smb2_durable_open_init(void)
1245 struct torture_suite *suite =
1246 torture_suite_create(talloc_autofree_context(), "durable-open");
1248 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1249 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1250 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1251 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1252 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1253 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1254 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1255 torture_suite_add_2smb2_test(suite, "file-position",
1256 test_durable_open_file_position);
1257 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1258 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1259 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1260 torture_suite_add_2smb2_test(suite, "open-lease",
1261 test_durable_open_open_lease);
1262 torture_suite_add_2smb2_test(suite, "open-oplock",
1263 test_durable_open_open_oplock);
1265 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1267 return suite;