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 testing of SMB2 durable opens
324 regarding the position information on the handle
326 bool test_durable_open_file_position(struct torture_context *tctx,
327 struct smb2_tree *tree1,
328 struct smb2_tree *tree2)
330 TALLOC_CTX *mem_ctx = talloc_new(tctx);
331 struct smb2_handle h1, h2;
332 struct smb2_create io1, io2;
334 const char *fname = "durable_open_position.dat";
335 union smb_fileinfo qfinfo;
336 union smb_setfileinfo sfinfo;
340 smb2_util_unlink(tree1, fname);
342 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
343 io1.in.durable_open = true;
345 status = smb2_create(tree1, mem_ctx, &io1);
346 CHECK_STATUS(status, NT_STATUS_OK);
347 h1 = io1.out.file.handle;
348 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
349 CHECK_VAL(io1.out.durable_open, true);
350 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
352 /* TODO: check extra blob content */
355 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
356 qfinfo.generic.in.file.handle = h1;
357 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
358 CHECK_STATUS(status, NT_STATUS_OK);
359 CHECK_VAL(qfinfo.position_information.out.position, 0);
360 pos = qfinfo.position_information.out.position;
361 torture_comment(tctx, "position: %llu\n",
362 (unsigned long long)pos);
365 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
366 sfinfo.generic.in.file.handle = h1;
367 sfinfo.position_information.in.position = 0x1000;
368 status = smb2_setinfo_file(tree1, &sfinfo);
369 CHECK_STATUS(status, NT_STATUS_OK);
372 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
373 qfinfo.generic.in.file.handle = h1;
374 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
375 CHECK_STATUS(status, NT_STATUS_OK);
376 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
377 pos = qfinfo.position_information.out.position;
378 torture_comment(tctx, "position: %llu\n",
379 (unsigned long long)pos);
385 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
386 qfinfo.generic.in.file.handle = h1;
387 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
388 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
391 io2.in.fname = fname;
392 io2.in.durable_handle = &h1;
394 status = smb2_create(tree2, mem_ctx, &io2);
395 CHECK_STATUS(status, NT_STATUS_OK);
396 CHECK_VAL(io2.out.durable_open, true);
397 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
398 CHECK_VAL(io2.out.reserved, 0x00);
399 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
400 CHECK_VAL(io2.out.alloc_size, 0);
401 CHECK_VAL(io2.out.size, 0);
402 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
403 CHECK_VAL(io2.out.reserved2, 0);
405 h2 = io2.out.file.handle;
408 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
409 qfinfo.generic.in.file.handle = h2;
410 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
411 CHECK_STATUS(status, NT_STATUS_OK);
412 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
413 pos = qfinfo.position_information.out.position;
414 torture_comment(tctx, "position: %llu\n",
415 (unsigned long long)pos);
417 smb2_util_close(tree2, h2);
419 talloc_free(mem_ctx);
421 smb2_util_unlink(tree2, fname);
430 Open, disconnect, oplock break, reconnect.
432 bool test_durable_open_oplock(struct torture_context *tctx,
433 struct smb2_tree *tree1,
434 struct smb2_tree *tree2)
436 TALLOC_CTX *mem_ctx = talloc_new(tctx);
437 struct smb2_create io1, io2;
438 struct smb2_handle h1, h2;
443 /* Choose a random name in case the state is left a little funky. */
444 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
447 smb2_util_unlink(tree1, fname);
449 /* Create with batch oplock */
450 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
451 io1.in.durable_open = true;
454 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
456 status = smb2_create(tree1, mem_ctx, &io1);
457 CHECK_STATUS(status, NT_STATUS_OK);
458 h1 = io1.out.file.handle;
459 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
460 CHECK_VAL(io1.out.durable_open, true);
461 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
463 /* Disconnect after getting the batch */
468 * Windows7 (build 7000) will break a batch oplock immediately if the
469 * original client is gone. (ZML: This seems like a bug. It should give
470 * some time for the client to reconnect!)
472 status = smb2_create(tree2, mem_ctx, &io2);
473 CHECK_STATUS(status, NT_STATUS_OK);
474 h2 = io2.out.file.handle;
475 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
476 CHECK_VAL(io2.out.durable_open, true);
477 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
479 /* What if tree1 tries to come back and reclaim? */
480 if (!torture_smb2_connection(tctx, &tree1)) {
481 torture_warning(tctx, "couldn't reconnect, bailing\n");
487 io1.in.fname = fname;
488 io1.in.durable_handle = &h1;
490 status = smb2_create(tree1, mem_ctx, &io1);
491 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
494 smb2_util_close(tree2, h2);
495 smb2_util_unlink(tree2, fname);
504 Open, disconnect, lease break, reconnect.
506 bool test_durable_open_lease(struct torture_context *tctx,
507 struct smb2_tree *tree1,
508 struct smb2_tree *tree2)
510 TALLOC_CTX *mem_ctx = talloc_new(tctx);
511 struct smb2_create io1, io2;
512 struct smb2_lease ls1, ls2;
513 struct smb2_handle h1, h2;
517 uint64_t lease1, lease2;
520 * Choose a random name and random lease in case the state is left a
525 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
528 smb2_util_unlink(tree1, fname);
530 /* Create with lease */
532 io1.in.security_flags = 0x00;
533 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
534 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
535 io1.in.create_flags = 0x00000000;
536 io1.in.reserved = 0x00000000;
537 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
538 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
539 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
540 NTCREATEX_SHARE_ACCESS_WRITE |
541 NTCREATEX_SHARE_ACCESS_DELETE;
542 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
543 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
544 NTCREATEX_OPTIONS_ASYNC_ALERT |
545 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
547 io1.in.fname = fname;
548 io1.in.durable_open = true;
551 ls1.lease_key.data[0] = lease1;
552 ls1.lease_key.data[1] = ~lease1;
553 ls1.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
554 io1.in.lease_request = &ls1;
558 ls2.lease_key.data[0] = lease2;
559 ls2.lease_key.data[1] = ~lease2;
560 io2.in.lease_request = &ls2;
561 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
563 status = smb2_create(tree1, mem_ctx, &io1);
564 CHECK_STATUS(status, NT_STATUS_OK);
565 h1 = io1.out.file.handle;
566 CHECK_VAL(io1.out.durable_open, true);
567 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
569 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
570 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
571 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
572 CHECK_VAL(io1.out.lease_response.lease_state,
573 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
575 /* Disconnect after getting the lease */
580 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
581 * even if the original client is gone. (ZML: This seems like a bug. It
582 * should give some time for the client to reconnect! And why RH?)
584 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
585 * Test is adapted accordingly.
587 status = smb2_create(tree2, mem_ctx, &io2);
588 CHECK_STATUS(status, NT_STATUS_OK);
589 h2 = io2.out.file.handle;
590 CHECK_VAL(io2.out.durable_open, true);
591 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
593 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
594 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
595 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
596 CHECK_VAL(io2.out.lease_response.lease_state,
597 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
599 /* What if tree1 tries to come back and reclaim? */
600 if (!torture_smb2_connection(tctx, &tree1)) {
601 torture_warning(tctx, "couldn't reconnect, bailing\n");
607 io1.in.fname = fname;
608 io1.in.durable_handle = &h1;
609 io1.in.lease_request = &ls1;
611 status = smb2_create(tree1, mem_ctx, &io1);
612 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
615 smb2_util_close(tree2, h2);
616 smb2_util_unlink(tree2, fname);
625 Open, take BRL, disconnect, reconnect.
627 bool test_durable_open_lock(struct torture_context *tctx,
628 struct smb2_tree *tree)
630 TALLOC_CTX *mem_ctx = talloc_new(tctx);
631 struct smb2_create io;
632 struct smb2_lease ls;
633 struct smb2_handle h;
634 struct smb2_lock lck;
635 struct smb2_lock_element el[2];
642 * Choose a random name and random lease in case the state is left a
646 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
649 smb2_util_unlink(tree, fname);
651 /* Create with lease */
653 io.in.security_flags = 0x00;
654 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
655 io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
656 io.in.create_flags = 0x00000000;
657 io.in.reserved = 0x00000000;
658 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
659 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
660 io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
661 NTCREATEX_SHARE_ACCESS_WRITE |
662 NTCREATEX_SHARE_ACCESS_DELETE;
663 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
664 io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
665 NTCREATEX_OPTIONS_ASYNC_ALERT |
666 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
669 io.in.durable_open = true;
672 ls.lease_key.data[0] = lease;
673 ls.lease_key.data[1] = ~lease;
674 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
675 io.in.lease_request = &ls;
677 status = smb2_create(tree, mem_ctx, &io);
678 CHECK_STATUS(status, NT_STATUS_OK);
679 h = io.out.file.handle;
680 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
682 CHECK_VAL(io.out.durable_open, true);
683 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
684 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
685 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
686 CHECK_VAL(io.out.lease_response.lease_state,
687 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
692 lck.in.lock_count = 0x0001;
693 lck.in.lock_sequence = 0x00000000;
694 lck.in.file.handle = h;
697 el[0].reserved = 0x00000000;
698 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
699 status = smb2_lock(tree, &lck);
700 CHECK_STATUS(status, NT_STATUS_OK);
702 /* Disconnect/Reconnect. */
706 if (!torture_smb2_connection(tctx, &tree)) {
707 torture_warning(tctx, "couldn't reconnect, bailing\n");
714 io.in.durable_handle = &h;
715 io.in.lease_request = &ls;
717 status = smb2_create(tree, mem_ctx, &io);
718 CHECK_STATUS(status, NT_STATUS_OK);
719 h = io.out.file.handle;
721 lck.in.file.handle = h;
722 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
723 status = smb2_lock(tree, &lck);
724 CHECK_STATUS(status, NT_STATUS_OK);
727 smb2_util_close(tree, h);
728 smb2_util_unlink(tree, fname);
735 Open, disconnect, open in another tree, reconnect.
737 This test actually demonstrates a minimum level of respect for the durable
738 open in the face of another open. As long as this test shows an inability to
739 reconnect after an open, the oplock/lease tests above will certainly
740 demonstrate an error on reconnect.
742 bool test_durable_open_open(struct torture_context *tctx,
743 struct smb2_tree *tree1,
744 struct smb2_tree *tree2)
746 TALLOC_CTX *mem_ctx = talloc_new(tctx);
747 struct smb2_create io1, io2;
748 struct smb2_lease ls;
749 struct smb2_handle h1, h2;
756 * Choose a random name and random lease in case the state is left a
760 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
763 smb2_util_unlink(tree1, fname);
765 /* Create with lease */
767 io1.in.security_flags = 0x00;
768 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
769 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
770 io1.in.create_flags = 0x00000000;
771 io1.in.reserved = 0x00000000;
772 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
773 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
774 io1.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
775 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
776 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
777 NTCREATEX_OPTIONS_ASYNC_ALERT |
778 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
780 io1.in.fname = fname;
781 io1.in.durable_open = true;
784 io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
785 io2.in.durable_open = false;
788 ls.lease_key.data[0] = lease;
789 ls.lease_key.data[1] = ~lease;
790 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
791 io1.in.lease_request = &ls;
793 status = smb2_create(tree1, mem_ctx, &io1);
794 CHECK_STATUS(status, NT_STATUS_OK);
795 h1 = io1.out.file.handle;
796 CHECK_VAL(io1.out.durable_open, true);
797 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
799 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
800 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
801 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
802 CHECK_VAL(io1.out.lease_response.lease_state,
803 SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
809 /* Open the file in tree2 */
810 status = smb2_create(tree2, mem_ctx, &io2);
811 CHECK_STATUS(status, NT_STATUS_OK);
812 h2 = io2.out.file.handle;
813 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
816 if (!torture_smb2_connection(tctx, &tree1)) {
817 torture_warning(tctx, "couldn't reconnect, bailing\n");
823 io1.in.fname = fname;
824 io1.in.durable_handle = &h1;
825 io1.in.lease_request = &ls;
828 * Windows7 (build 7000) will give away an open immediately if the
829 * original client is gone. (ZML: This seems like a bug. It should give
830 * some time for the client to reconnect!)
832 status = smb2_create(tree1, mem_ctx, &io1);
833 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
834 h1 = io1.out.file.handle;
837 smb2_util_close(tree2, h2);
838 smb2_util_unlink(tree2, fname);
839 smb2_util_close(tree1, h1);
840 smb2_util_unlink(tree1, fname);
848 struct torture_suite *torture_smb2_durable_open_init(void)
850 struct torture_suite *suite =
851 torture_suite_create(talloc_autofree_context(), "durable-open");
853 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
854 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
855 torture_suite_add_2smb2_test(suite, "file-position",
856 test_durable_open_file_position);
857 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
858 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
859 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
860 torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
862 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");