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/>.
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
28 #define CHECK_VAL(v, correct) do { \
29 if ((v) != (correct)) { \
30 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
31 __location__, #v, (int)v, (int)correct); \
35 #define CHECK_STATUS(status, correct) do { \
36 if (!NT_STATUS_EQUAL(status, correct)) { \
37 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
38 nt_errstr(status), nt_errstr(correct)); \
43 #define CHECK_CREATED(__io, __created, __attribute) \
45 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46 CHECK_VAL((__io)->out.alloc_size, 0); \
47 CHECK_VAL((__io)->out.size, 0); \
48 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
49 CHECK_VAL((__io)->out.reserved2, 0); \
54 * basic durable_open test.
55 * durable state should only be granted when requested
56 * along with a batch oplock or a handle lease.
58 * This test tests durable open with all possible oplock types.
61 struct durable_open_vs_oplock {
63 const char *share_mode;
67 #define NUM_OPLOCK_TYPES 4
68 #define NUM_SHARE_MODES 8
69 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
70 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
88 { "s", "RWD", false },
97 { "x", "RWD", false },
106 { "b", "RWD", true },
109 static bool test_one_durable_open_open1(struct torture_context *tctx,
110 struct smb2_tree *tree,
112 struct durable_open_vs_oplock test)
115 TALLOC_CTX *mem_ctx = talloc_new(tctx);
116 struct smb2_handle _h;
117 struct smb2_handle *h = NULL;
119 struct smb2_create io;
121 smb2_util_unlink(tree, fname);
123 smb2_oplock_create_share(&io, fname,
124 smb2_util_share_access(test.share_mode),
125 smb2_util_oplock_level(test.level));
126 io.in.durable_open = true;
128 status = smb2_create(tree, mem_ctx, &io);
129 CHECK_STATUS(status, NT_STATUS_OK);
130 _h = io.out.file.handle;
132 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
133 CHECK_VAL(io.out.durable_open, test.expected);
134 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
138 smb2_util_close(tree, *h);
140 smb2_util_unlink(tree, fname);
141 talloc_free(mem_ctx);
146 bool test_durable_open_open1(struct torture_context *tctx,
147 struct smb2_tree *tree)
149 TALLOC_CTX *mem_ctx = talloc_new(tctx);
154 /* Choose a random name in case the state is left a little funky. */
155 snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
157 smb2_util_unlink(tree, fname);
159 /* test various oplock levels with durable open */
161 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
162 ret = test_one_durable_open_open1(tctx,
165 durable_open_vs_oplock_table[i]);
172 smb2_util_unlink(tree, fname);
174 talloc_free(mem_ctx);
180 * basic durable_open test.
181 * durable state should only be granted when requested
182 * along with a batch oplock or a handle lease.
184 * This test tests durable open with all valid lease types.
187 struct durable_open_vs_lease {
189 const char *share_mode;
193 #define NUM_LEASE_TYPES 5
194 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
195 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
204 { "", "RWD", false },
210 { "R", "RW", false },
211 { "R", "RD", false },
212 { "R", "DW", false },
213 { "R", "RWD", false },
216 { "RW", "R", false },
217 { "RW", "W", false },
218 { "RW", "D", false },
219 { "RW", "RW", false },
220 { "RW", "RD", false },
221 { "RW", "WD", false },
222 { "RW", "RWD", false },
228 { "RH", "RW", true },
229 { "RH", "RD", true },
230 { "RH", "WD", true },
231 { "RH", "RWD", true },
234 { "RHW", "R", true },
235 { "RHW", "W", true },
236 { "RHW", "D", true },
237 { "RHW", "RW", true },
238 { "RHW", "RD", true },
239 { "RHW", "WD", true },
240 { "RHW", "RWD", true },
243 static bool test_one_durable_open_open2(struct torture_context *tctx,
244 struct smb2_tree *tree,
246 struct durable_open_vs_lease test)
249 TALLOC_CTX *mem_ctx = talloc_new(tctx);
250 struct smb2_handle _h;
251 struct smb2_handle *h = NULL;
253 struct smb2_create io;
254 struct smb2_lease ls;
257 smb2_util_unlink(tree, fname);
261 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
262 smb2_util_share_access(test.share_mode),
264 smb2_util_lease_state(test.type));
265 io.in.durable_open = true;
267 status = smb2_create(tree, mem_ctx, &io);
268 CHECK_STATUS(status, NT_STATUS_OK);
269 _h = io.out.file.handle;
271 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
272 CHECK_VAL(io.out.durable_open, test.expected);
273 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
274 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
275 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
276 CHECK_VAL(io.out.lease_response.lease_state,
277 smb2_util_lease_state(test.type));
280 smb2_util_close(tree, *h);
282 smb2_util_unlink(tree, fname);
283 talloc_free(mem_ctx);
288 bool test_durable_open_open2(struct torture_context *tctx,
289 struct smb2_tree *tree)
291 TALLOC_CTX *mem_ctx = talloc_new(tctx);
296 /* Choose a random name in case the state is left a little funky. */
297 snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
299 smb2_util_unlink(tree, fname);
302 /* test various oplock levels with durable open */
304 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
305 ret = test_one_durable_open_open2(tctx,
308 durable_open_vs_lease_table[i]);
315 smb2_util_unlink(tree, fname);
317 talloc_free(mem_ctx);
323 * basic test for doing a durable open
324 * and do a durable reopen on the same connection
326 bool test_durable_open_reopen1(struct torture_context *tctx,
327 struct smb2_tree *tree)
330 TALLOC_CTX *mem_ctx = talloc_new(tctx);
332 struct smb2_handle _h;
333 struct smb2_handle *h = NULL;
334 struct smb2_create io1, io2;
337 /* Choose a random name in case the state is left a little funky. */
338 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
339 generate_random_str(tctx, 8));
341 smb2_util_unlink(tree, fname);
343 smb2_oplock_create_share(&io1, fname,
344 smb2_util_share_access(""),
345 smb2_util_oplock_level("b"));
346 io1.in.durable_open = true;
348 status = smb2_create(tree, mem_ctx, &io1);
349 CHECK_STATUS(status, NT_STATUS_OK);
350 _h = io1.out.file.handle;
352 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
353 CHECK_VAL(io1.out.durable_open, true);
354 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
356 /* try a durable reconnect while the file is still open */
358 io2.in.fname = fname;
359 io2.in.durable_handle = h;
361 status = smb2_create(tree, mem_ctx, &io2);
362 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
366 smb2_util_close(tree, *h);
369 smb2_util_unlink(tree, fname);
373 talloc_free(mem_ctx);
379 * basic test for doing a durable open
380 * tcp disconnect, reconnect, do a durable reopen (succeeds)
382 bool test_durable_open_reopen2(struct torture_context *tctx,
383 struct smb2_tree *tree)
386 TALLOC_CTX *mem_ctx = talloc_new(tctx);
388 struct smb2_handle _h;
389 struct smb2_handle *h = NULL;
390 struct smb2_create io1, io2;
393 /* Choose a random name in case the state is left a little funky. */
394 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
395 generate_random_str(tctx, 8));
397 smb2_util_unlink(tree, fname);
399 smb2_oplock_create_share(&io1, fname,
400 smb2_util_share_access(""),
401 smb2_util_oplock_level("b"));
402 io1.in.durable_open = true;
404 status = smb2_create(tree, mem_ctx, &io1);
405 CHECK_STATUS(status, NT_STATUS_OK);
406 _h = io1.out.file.handle;
408 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
409 CHECK_VAL(io1.out.durable_open, true);
410 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
412 /* disconnect, reconnect and then do durable reopen */
416 if (!torture_smb2_connection(tctx, &tree)) {
417 torture_warning(tctx, "couldn't reconnect, bailing\n");
423 io2.in.fname = fname;
424 io2.in.durable_handle = h;
426 status = smb2_create(tree, mem_ctx, &io2);
427 CHECK_STATUS(status, NT_STATUS_OK);
428 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
429 CHECK_VAL(io2.out.durable_open, true);
430 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
431 _h = io2.out.file.handle;
436 smb2_util_close(tree, *h);
439 smb2_util_unlink(tree, fname);
443 talloc_free(mem_ctx);
449 * basic test for doing a durable open:
450 * tdis, new tcon, try durable reopen (fails)
452 bool test_durable_open_reopen3(struct torture_context *tctx,
453 struct smb2_tree *tree)
456 TALLOC_CTX *mem_ctx = talloc_new(tctx);
458 struct smb2_handle _h;
459 struct smb2_handle *h = NULL;
460 struct smb2_create io1, io2;
462 struct smb2_tree *tree2;
464 /* Choose a random name in case the state is left a little funky. */
465 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
466 generate_random_str(tctx, 8));
468 smb2_util_unlink(tree, fname);
470 smb2_oplock_create_share(&io1, fname,
471 smb2_util_share_access(""),
472 smb2_util_oplock_level("b"));
473 io1.in.durable_open = true;
475 status = smb2_create(tree, mem_ctx, &io1);
476 CHECK_STATUS(status, NT_STATUS_OK);
477 _h = io1.out.file.handle;
479 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
480 CHECK_VAL(io1.out.durable_open, true);
481 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
483 /* disconnect, reconnect and then do durable reopen */
484 status = smb2_tdis(tree);
485 CHECK_STATUS(status, NT_STATUS_OK);
487 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
488 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
495 io2.in.fname = fname;
496 io2.in.durable_handle = h;
498 status = smb2_create(tree2, mem_ctx, &io2);
499 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
503 smb2_util_close(tree, *h);
506 smb2_util_unlink(tree2, fname);
510 talloc_free(mem_ctx);
516 * basic test for doing a durable open:
517 * logoff, create a new session, do a durable reopen (succeeds)
519 bool test_durable_open_reopen4(struct torture_context *tctx,
520 struct smb2_tree *tree)
523 TALLOC_CTX *mem_ctx = talloc_new(tctx);
525 struct smb2_handle _h;
526 struct smb2_handle *h = NULL;
527 struct smb2_create io1, io2;
529 struct smb2_transport *transport;
530 struct smb2_tree *tree2;
532 /* Choose a random name in case the state is left a little funky. */
533 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
534 generate_random_str(tctx, 8));
536 smb2_util_unlink(tree, fname);
538 smb2_oplock_create_share(&io1, fname,
539 smb2_util_share_access(""),
540 smb2_util_oplock_level("b"));
541 io1.in.durable_open = true;
542 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
544 status = smb2_create(tree, mem_ctx, &io1);
545 CHECK_STATUS(status, NT_STATUS_OK);
546 _h = io1.out.file.handle;
548 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
549 CHECK_VAL(io1.out.durable_open, true);
550 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
552 /* disconnect, reconnect and then do durable reopen */
553 transport = tree->session->transport;
554 status = smb2_logoff(tree->session);
555 CHECK_STATUS(status, NT_STATUS_OK);
557 if (!torture_smb2_session_setup(tctx, transport, mem_ctx, &tree->session)) {
558 torture_warning(tctx, "session setup failed.\n");
564 io2.in.fname = fname;
565 io2.in.durable_handle = h;
567 status = smb2_create(tree, mem_ctx, &io2);
568 CHECK_STATUS(status, NT_STATUS_NETWORK_NAME_DELETED);
570 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
571 torture_warning(tctx, "tree connect failed.\n");
577 io2.in.fname = fname;
578 io2.in.durable_handle = h;
580 status = smb2_create(tree2, mem_ctx, &io2);
581 CHECK_STATUS(status, NT_STATUS_OK);
583 _h = io2.out.file.handle;
585 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
586 CHECK_VAL(io2.out.durable_open, true);
587 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
591 smb2_util_close(tree, *h);
594 smb2_util_unlink(tree2, fname);
598 talloc_free(mem_ctx);
604 basic testing of SMB2 durable opens
605 regarding the position information on the handle
607 bool test_durable_open_file_position(struct torture_context *tctx,
608 struct smb2_tree *tree1,
609 struct smb2_tree *tree2)
611 TALLOC_CTX *mem_ctx = talloc_new(tctx);
612 struct smb2_handle h1, h2;
613 struct smb2_create io1, io2;
615 const char *fname = "durable_open_position.dat";
616 union smb_fileinfo qfinfo;
617 union smb_setfileinfo sfinfo;
621 smb2_util_unlink(tree1, fname);
623 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
624 io1.in.durable_open = true;
626 status = smb2_create(tree1, mem_ctx, &io1);
627 CHECK_STATUS(status, NT_STATUS_OK);
628 h1 = io1.out.file.handle;
629 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
630 CHECK_VAL(io1.out.durable_open, true);
631 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
633 /* TODO: check extra blob content */
636 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
637 qfinfo.generic.in.file.handle = h1;
638 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
639 CHECK_STATUS(status, NT_STATUS_OK);
640 CHECK_VAL(qfinfo.position_information.out.position, 0);
641 pos = qfinfo.position_information.out.position;
642 torture_comment(tctx, "position: %llu\n",
643 (unsigned long long)pos);
646 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
647 sfinfo.generic.in.file.handle = h1;
648 sfinfo.position_information.in.position = 0x1000;
649 status = smb2_setinfo_file(tree1, &sfinfo);
650 CHECK_STATUS(status, NT_STATUS_OK);
653 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
654 qfinfo.generic.in.file.handle = h1;
655 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
656 CHECK_STATUS(status, NT_STATUS_OK);
657 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
658 pos = qfinfo.position_information.out.position;
659 torture_comment(tctx, "position: %llu\n",
660 (unsigned long long)pos);
666 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
667 qfinfo.generic.in.file.handle = h1;
668 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
669 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
672 io2.in.fname = fname;
673 io2.in.durable_handle = &h1;
675 status = smb2_create(tree2, mem_ctx, &io2);
676 CHECK_STATUS(status, NT_STATUS_OK);
677 CHECK_VAL(io2.out.durable_open, true);
678 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
679 CHECK_VAL(io2.out.reserved, 0x00);
680 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
681 CHECK_VAL(io2.out.alloc_size, 0);
682 CHECK_VAL(io2.out.size, 0);
683 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
684 CHECK_VAL(io2.out.reserved2, 0);
686 h2 = io2.out.file.handle;
689 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
690 qfinfo.generic.in.file.handle = h2;
691 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
692 CHECK_STATUS(status, NT_STATUS_OK);
693 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
694 pos = qfinfo.position_information.out.position;
695 torture_comment(tctx, "position: %llu\n",
696 (unsigned long long)pos);
698 smb2_util_close(tree2, h2);
700 talloc_free(mem_ctx);
702 smb2_util_unlink(tree2, fname);
711 Open, disconnect, oplock break, reconnect.
713 bool test_durable_open_oplock(struct torture_context *tctx,
714 struct smb2_tree *tree1,
715 struct smb2_tree *tree2)
717 TALLOC_CTX *mem_ctx = talloc_new(tctx);
718 struct smb2_create io1, io2;
719 struct smb2_handle h1, h2;
724 /* Choose a random name in case the state is left a little funky. */
725 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
728 smb2_util_unlink(tree1, fname);
730 /* Create with batch oplock */
731 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
732 io1.in.durable_open = true;
735 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
737 status = smb2_create(tree1, mem_ctx, &io1);
738 CHECK_STATUS(status, NT_STATUS_OK);
739 h1 = io1.out.file.handle;
740 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
741 CHECK_VAL(io1.out.durable_open, true);
742 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
744 /* Disconnect after getting the batch */
749 * Windows7 (build 7000) will break a batch oplock immediately if the
750 * original client is gone. (ZML: This seems like a bug. It should give
751 * some time for the client to reconnect!)
753 status = smb2_create(tree2, mem_ctx, &io2);
754 CHECK_STATUS(status, NT_STATUS_OK);
755 h2 = io2.out.file.handle;
756 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
757 CHECK_VAL(io2.out.durable_open, true);
758 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
760 /* What if tree1 tries to come back and reclaim? */
761 if (!torture_smb2_connection(tctx, &tree1)) {
762 torture_warning(tctx, "couldn't reconnect, bailing\n");
768 io1.in.fname = fname;
769 io1.in.durable_handle = &h1;
771 status = smb2_create(tree1, mem_ctx, &io1);
772 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
775 smb2_util_close(tree2, h2);
776 smb2_util_unlink(tree2, fname);
785 Open, disconnect, lease break, reconnect.
787 bool test_durable_open_lease(struct torture_context *tctx,
788 struct smb2_tree *tree1,
789 struct smb2_tree *tree2)
791 TALLOC_CTX *mem_ctx = talloc_new(tctx);
792 struct smb2_create io1, io2;
793 struct smb2_lease ls1, ls2;
794 struct smb2_handle h1, h2;
798 uint64_t lease1, lease2;
801 * Choose a random name and random lease in case the state is left a
806 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
809 smb2_util_unlink(tree1, fname);
811 /* Create with lease */
812 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
813 lease1, smb2_util_lease_state("RHW"));
814 io1.in.durable_open = true;
816 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
817 lease2, smb2_util_lease_state("RHW"));
818 io2.in.durable_open = true;
819 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
821 status = smb2_create(tree1, mem_ctx, &io1);
822 CHECK_STATUS(status, NT_STATUS_OK);
823 h1 = io1.out.file.handle;
824 CHECK_VAL(io1.out.durable_open, true);
825 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
827 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
828 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
829 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
830 CHECK_VAL(io1.out.lease_response.lease_state,
831 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
833 /* Disconnect after getting the lease */
838 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
839 * even if the original client is gone. (ZML: This seems like a bug. It
840 * should give some time for the client to reconnect! And why RH?)
842 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
843 * Test is adapted accordingly.
845 status = smb2_create(tree2, mem_ctx, &io2);
846 CHECK_STATUS(status, NT_STATUS_OK);
847 h2 = io2.out.file.handle;
848 CHECK_VAL(io2.out.durable_open, true);
849 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
851 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
852 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
853 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
854 CHECK_VAL(io2.out.lease_response.lease_state,
855 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
857 /* What if tree1 tries to come back and reclaim? */
858 if (!torture_smb2_connection(tctx, &tree1)) {
859 torture_warning(tctx, "couldn't reconnect, bailing\n");
865 io1.in.fname = fname;
866 io1.in.durable_handle = &h1;
867 io1.in.lease_request = &ls1;
869 status = smb2_create(tree1, mem_ctx, &io1);
870 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
873 smb2_util_close(tree2, h2);
874 smb2_util_unlink(tree2, fname);
883 Open, take BRL, disconnect, reconnect.
885 bool test_durable_open_lock(struct torture_context *tctx,
886 struct smb2_tree *tree)
888 TALLOC_CTX *mem_ctx = talloc_new(tctx);
889 struct smb2_create io;
890 struct smb2_lease ls;
891 struct smb2_handle h;
892 struct smb2_lock lck;
893 struct smb2_lock_element el[2];
900 * Choose a random name and random lease in case the state is left a
904 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
907 smb2_util_unlink(tree, fname);
909 /* Create with lease */
911 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
912 smb2_util_lease_state("RWH"));
913 io.in.durable_open = true;
915 status = smb2_create(tree, mem_ctx, &io);
916 CHECK_STATUS(status, NT_STATUS_OK);
917 h = io.out.file.handle;
918 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
920 CHECK_VAL(io.out.durable_open, true);
921 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
922 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
923 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
924 CHECK_VAL(io.out.lease_response.lease_state,
925 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
930 lck.in.lock_count = 0x0001;
931 lck.in.lock_sequence = 0x00000000;
932 lck.in.file.handle = h;
935 el[0].reserved = 0x00000000;
936 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
937 status = smb2_lock(tree, &lck);
938 CHECK_STATUS(status, NT_STATUS_OK);
940 /* Disconnect/Reconnect. */
944 if (!torture_smb2_connection(tctx, &tree)) {
945 torture_warning(tctx, "couldn't reconnect, bailing\n");
952 io.in.durable_handle = &h;
953 io.in.lease_request = &ls;
955 status = smb2_create(tree, mem_ctx, &io);
956 CHECK_STATUS(status, NT_STATUS_OK);
957 h = io.out.file.handle;
959 lck.in.file.handle = h;
960 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
961 status = smb2_lock(tree, &lck);
962 CHECK_STATUS(status, NT_STATUS_OK);
965 smb2_util_close(tree, h);
966 smb2_util_unlink(tree, fname);
973 Open, disconnect, open in another tree, reconnect.
975 This test actually demonstrates a minimum level of respect for the durable
976 open in the face of another open. As long as this test shows an inability to
977 reconnect after an open, the oplock/lease tests above will certainly
978 demonstrate an error on reconnect.
980 bool test_durable_open_open(struct torture_context *tctx,
981 struct smb2_tree *tree1,
982 struct smb2_tree *tree2)
984 TALLOC_CTX *mem_ctx = talloc_new(tctx);
985 struct smb2_create io1, io2;
986 struct smb2_lease ls;
987 struct smb2_handle h1, h2;
994 * Choose a random name and random lease in case the state is left a
998 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1001 smb2_util_unlink(tree1, fname);
1003 /* Create with lease */
1004 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1005 smb2_util_share_access(""),
1007 smb2_util_lease_state("RH"));
1008 io1.in.durable_open = true;
1010 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1012 status = smb2_create(tree1, mem_ctx, &io1);
1013 CHECK_STATUS(status, NT_STATUS_OK);
1014 h1 = io1.out.file.handle;
1015 CHECK_VAL(io1.out.durable_open, true);
1016 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1018 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1019 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1020 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1021 CHECK_VAL(io1.out.lease_response.lease_state,
1022 smb2_util_lease_state("RH"));
1028 /* Open the file in tree2 */
1029 status = smb2_create(tree2, mem_ctx, &io2);
1030 CHECK_STATUS(status, NT_STATUS_OK);
1031 h2 = io2.out.file.handle;
1032 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1035 if (!torture_smb2_connection(tctx, &tree1)) {
1036 torture_warning(tctx, "couldn't reconnect, bailing\n");
1042 io1.in.fname = fname;
1043 io1.in.durable_handle = &h1;
1044 io1.in.lease_request = &ls;
1047 * Windows7 (build 7000) will give away an open immediately if the
1048 * original client is gone. (ZML: This seems like a bug. It should give
1049 * some time for the client to reconnect!)
1051 status = smb2_create(tree1, mem_ctx, &io1);
1052 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1053 h1 = io1.out.file.handle;
1056 smb2_util_close(tree2, h2);
1057 smb2_util_unlink(tree2, fname);
1058 smb2_util_close(tree1, h1);
1059 smb2_util_unlink(tree1, fname);
1067 struct torture_suite *torture_smb2_durable_open_init(void)
1069 struct torture_suite *suite =
1070 torture_suite_create(talloc_autofree_context(), "durable-open");
1072 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1073 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1074 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1075 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1076 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1077 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1078 torture_suite_add_2smb2_test(suite, "file-position",
1079 test_durable_open_file_position);
1080 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1081 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1082 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1083 torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
1085 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");