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 */
451 io1.in.security_flags = 0x00;
452 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
453 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
454 io1.in.create_flags = 0x00000000;
455 io1.in.reserved = 0x00000000;
456 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
457 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
458 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
459 NTCREATEX_SHARE_ACCESS_WRITE |
460 NTCREATEX_SHARE_ACCESS_DELETE;
461 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
462 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
463 NTCREATEX_OPTIONS_ASYNC_ALERT |
464 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
466 io1.in.fname = fname;
467 io1.in.durable_open = true;
470 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
472 status = smb2_create(tree1, mem_ctx, &io1);
473 CHECK_STATUS(status, NT_STATUS_OK);
474 h1 = io1.out.file.handle;
475 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
476 CHECK_VAL(io1.out.durable_open, true);
477 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
479 /* Disconnect after getting the batch */
484 * Windows7 (build 7000) will break a batch oplock immediately if the
485 * original client is gone. (ZML: This seems like a bug. It should give
486 * some time for the client to reconnect!)
488 status = smb2_create(tree2, mem_ctx, &io2);
489 CHECK_STATUS(status, NT_STATUS_OK);
490 h2 = io2.out.file.handle;
491 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
492 CHECK_VAL(io2.out.durable_open, true);
493 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
495 /* What if tree1 tries to come back and reclaim? */
496 if (!torture_smb2_connection(tctx, &tree1)) {
497 torture_warning(tctx, "couldn't reconnect, bailing\n");
503 io1.in.fname = fname;
504 io1.in.durable_handle = &h1;
506 status = smb2_create(tree1, mem_ctx, &io1);
507 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
510 smb2_util_close(tree2, h2);
511 smb2_util_unlink(tree2, fname);
520 Open, disconnect, lease break, reconnect.
522 bool test_durable_open_lease(struct torture_context *tctx,
523 struct smb2_tree *tree1,
524 struct smb2_tree *tree2)
526 TALLOC_CTX *mem_ctx = talloc_new(tctx);
527 struct smb2_create io1, io2;
528 struct smb2_lease ls1, ls2;
529 struct smb2_handle h1, h2;
533 uint64_t lease1, lease2;
536 * Choose a random name and random lease in case the state is left a
541 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
544 smb2_util_unlink(tree1, fname);
546 /* Create with lease */
548 io1.in.security_flags = 0x00;
549 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
550 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
551 io1.in.create_flags = 0x00000000;
552 io1.in.reserved = 0x00000000;
553 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
554 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
555 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
556 NTCREATEX_SHARE_ACCESS_WRITE |
557 NTCREATEX_SHARE_ACCESS_DELETE;
558 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
559 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
560 NTCREATEX_OPTIONS_ASYNC_ALERT |
561 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
563 io1.in.fname = fname;
564 io1.in.durable_open = true;
567 ls1.lease_key.data[0] = lease1;
568 ls1.lease_key.data[1] = ~lease1;
569 ls1.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
570 io1.in.lease_request = &ls1;
574 ls2.lease_key.data[0] = lease2;
575 ls2.lease_key.data[1] = ~lease2;
576 io2.in.lease_request = &ls2;
577 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
579 status = smb2_create(tree1, mem_ctx, &io1);
580 CHECK_STATUS(status, NT_STATUS_OK);
581 h1 = io1.out.file.handle;
582 CHECK_VAL(io1.out.durable_open, true);
583 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
585 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
586 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
587 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
588 CHECK_VAL(io1.out.lease_response.lease_state,
589 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
591 /* Disconnect after getting the lease */
596 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
597 * even if the original client is gone. (ZML: This seems like a bug. It
598 * should give some time for the client to reconnect! And why RH?)
600 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
601 * Test is adapted accordingly.
603 status = smb2_create(tree2, mem_ctx, &io2);
604 CHECK_STATUS(status, NT_STATUS_OK);
605 h2 = io2.out.file.handle;
606 CHECK_VAL(io2.out.durable_open, true);
607 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
609 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
610 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
611 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
612 CHECK_VAL(io2.out.lease_response.lease_state,
613 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
615 /* What if tree1 tries to come back and reclaim? */
616 if (!torture_smb2_connection(tctx, &tree1)) {
617 torture_warning(tctx, "couldn't reconnect, bailing\n");
623 io1.in.fname = fname;
624 io1.in.durable_handle = &h1;
625 io1.in.lease_request = &ls1;
627 status = smb2_create(tree1, mem_ctx, &io1);
628 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
631 smb2_util_close(tree2, h2);
632 smb2_util_unlink(tree2, fname);
641 Open, take BRL, disconnect, reconnect.
643 bool test_durable_open_lock(struct torture_context *tctx,
644 struct smb2_tree *tree)
646 TALLOC_CTX *mem_ctx = talloc_new(tctx);
647 struct smb2_create io;
648 struct smb2_lease ls;
649 struct smb2_handle h;
650 struct smb2_lock lck;
651 struct smb2_lock_element el[2];
658 * Choose a random name and random lease in case the state is left a
662 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
665 smb2_util_unlink(tree, fname);
667 /* Create with lease */
669 io.in.security_flags = 0x00;
670 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
671 io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
672 io.in.create_flags = 0x00000000;
673 io.in.reserved = 0x00000000;
674 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
675 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
676 io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
677 NTCREATEX_SHARE_ACCESS_WRITE |
678 NTCREATEX_SHARE_ACCESS_DELETE;
679 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
680 io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
681 NTCREATEX_OPTIONS_ASYNC_ALERT |
682 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
685 io.in.durable_open = true;
688 ls.lease_key.data[0] = lease;
689 ls.lease_key.data[1] = ~lease;
690 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
691 io.in.lease_request = &ls;
693 status = smb2_create(tree, mem_ctx, &io);
694 CHECK_STATUS(status, NT_STATUS_OK);
695 h = io.out.file.handle;
696 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
698 CHECK_VAL(io.out.durable_open, true);
699 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
700 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
701 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
702 CHECK_VAL(io.out.lease_response.lease_state,
703 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
708 lck.in.lock_count = 0x0001;
709 lck.in.lock_sequence = 0x00000000;
710 lck.in.file.handle = h;
713 el[0].reserved = 0x00000000;
714 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
715 status = smb2_lock(tree, &lck);
716 CHECK_STATUS(status, NT_STATUS_OK);
718 /* Disconnect/Reconnect. */
722 if (!torture_smb2_connection(tctx, &tree)) {
723 torture_warning(tctx, "couldn't reconnect, bailing\n");
730 io.in.durable_handle = &h;
731 io.in.lease_request = &ls;
733 status = smb2_create(tree, mem_ctx, &io);
734 CHECK_STATUS(status, NT_STATUS_OK);
735 h = io.out.file.handle;
737 lck.in.file.handle = h;
738 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
739 status = smb2_lock(tree, &lck);
740 CHECK_STATUS(status, NT_STATUS_OK);
743 smb2_util_close(tree, h);
744 smb2_util_unlink(tree, fname);
751 Open, disconnect, open in another tree, reconnect.
753 This test actually demonstrates a minimum level of respect for the durable
754 open in the face of another open. As long as this test shows an inability to
755 reconnect after an open, the oplock/lease tests above will certainly
756 demonstrate an error on reconnect.
758 bool test_durable_open_open(struct torture_context *tctx,
759 struct smb2_tree *tree1,
760 struct smb2_tree *tree2)
762 TALLOC_CTX *mem_ctx = talloc_new(tctx);
763 struct smb2_create io1, io2;
764 struct smb2_lease ls;
765 struct smb2_handle h1, h2;
772 * Choose a random name and random lease in case the state is left a
776 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
779 smb2_util_unlink(tree1, fname);
781 /* Create with lease */
783 io1.in.security_flags = 0x00;
784 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
785 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
786 io1.in.create_flags = 0x00000000;
787 io1.in.reserved = 0x00000000;
788 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
789 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
790 io1.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
791 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
792 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
793 NTCREATEX_OPTIONS_ASYNC_ALERT |
794 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
796 io1.in.fname = fname;
797 io1.in.durable_open = true;
800 io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
801 io2.in.durable_open = false;
804 ls.lease_key.data[0] = lease;
805 ls.lease_key.data[1] = ~lease;
806 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
807 io1.in.lease_request = &ls;
809 status = smb2_create(tree1, mem_ctx, &io1);
810 CHECK_STATUS(status, NT_STATUS_OK);
811 h1 = io1.out.file.handle;
812 CHECK_VAL(io1.out.durable_open, true);
813 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
815 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
816 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
817 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
818 CHECK_VAL(io1.out.lease_response.lease_state,
819 SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
825 /* Open the file in tree2 */
826 status = smb2_create(tree2, mem_ctx, &io2);
827 CHECK_STATUS(status, NT_STATUS_OK);
828 h2 = io2.out.file.handle;
829 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
832 if (!torture_smb2_connection(tctx, &tree1)) {
833 torture_warning(tctx, "couldn't reconnect, bailing\n");
839 io1.in.fname = fname;
840 io1.in.durable_handle = &h1;
841 io1.in.lease_request = &ls;
844 * Windows7 (build 7000) will give away an open immediately if the
845 * original client is gone. (ZML: This seems like a bug. It should give
846 * some time for the client to reconnect!)
848 status = smb2_create(tree1, mem_ctx, &io1);
849 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
850 h1 = io1.out.file.handle;
853 smb2_util_close(tree2, h2);
854 smb2_util_unlink(tree2, fname);
855 smb2_util_close(tree1, h1);
856 smb2_util_unlink(tree1, fname);
864 struct torture_suite *torture_smb2_durable_open_init(void)
866 struct torture_suite *suite =
867 torture_suite_create(talloc_autofree_context(), "durable-open");
869 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
870 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
871 torture_suite_add_2smb2_test(suite, "file-position",
872 test_durable_open_file_position);
873 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
874 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
875 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
876 torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
878 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");