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); \
53 static inline uint32_t map_lease(const char *ls)
58 for (i = 0; i < strlen(ls); i++) {
61 val |= SMB2_LEASE_READ;
64 val |= SMB2_LEASE_HANDLE;
67 val |= SMB2_LEASE_WRITE;
75 static inline uint32_t map_sharemode(const char *sharemode)
77 uint32_t val = NTCREATEX_SHARE_ACCESS_NONE; /* 0 */
80 for (i = 0; i < strlen(sharemode); i++) {
81 switch(sharemode[i]) {
83 val |= NTCREATEX_SHARE_ACCESS_READ;
86 val |= NTCREATEX_SHARE_ACCESS_WRITE;
89 val |= NTCREATEX_SHARE_ACCESS_DELETE;
98 * basic durable_open test.
99 * durable state should only be granted when requested
100 * along with a batch oplock or a handle lease.
102 * This test tests durable open with all possible oplock types.
105 struct durable_open_vs_oplock {
107 const char *share_mode;
111 #define NUM_OPLOCK_TYPES 4
112 #define NUM_SHARE_MODES 8
113 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
114 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
116 { SMB2_OPLOCK_LEVEL_NONE, "", false },
117 { SMB2_OPLOCK_LEVEL_NONE, "R", false },
118 { SMB2_OPLOCK_LEVEL_NONE, "W", false },
119 { SMB2_OPLOCK_LEVEL_NONE, "D", false },
120 { SMB2_OPLOCK_LEVEL_NONE, "RD", false },
121 { SMB2_OPLOCK_LEVEL_NONE, "RW", false },
122 { SMB2_OPLOCK_LEVEL_NONE, "WD", false },
123 { SMB2_OPLOCK_LEVEL_NONE, "RWD", false },
125 { SMB2_OPLOCK_LEVEL_II, "", false },
126 { SMB2_OPLOCK_LEVEL_II, "R", false },
127 { SMB2_OPLOCK_LEVEL_II, "W", false },
128 { SMB2_OPLOCK_LEVEL_II, "D", false },
129 { SMB2_OPLOCK_LEVEL_II, "RD", false },
130 { SMB2_OPLOCK_LEVEL_II, "RW", false },
131 { SMB2_OPLOCK_LEVEL_II, "WD", false },
132 { SMB2_OPLOCK_LEVEL_II, "RWD", false },
134 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "", false },
135 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "R", false },
136 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "W", false },
137 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "D", false },
138 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "RD", false },
139 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "RW", false },
140 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "WD", false },
141 { SMB2_OPLOCK_LEVEL_EXCLUSIVE, "RWD", false },
143 { SMB2_OPLOCK_LEVEL_BATCH, "", true },
144 { SMB2_OPLOCK_LEVEL_BATCH, "R", true },
145 { SMB2_OPLOCK_LEVEL_BATCH, "W", true },
146 { SMB2_OPLOCK_LEVEL_BATCH, "D", true },
147 { SMB2_OPLOCK_LEVEL_BATCH, "RD", true },
148 { SMB2_OPLOCK_LEVEL_BATCH, "RW", true },
149 { SMB2_OPLOCK_LEVEL_BATCH, "WD", true },
150 { SMB2_OPLOCK_LEVEL_BATCH, "RWD", true },
153 static bool test_one_durable_open_open1(struct torture_context *tctx,
154 struct smb2_tree *tree,
156 struct smb2_create io,
157 struct durable_open_vs_oplock test)
160 TALLOC_CTX *mem_ctx = talloc_new(tctx);
161 struct smb2_handle _h;
162 struct smb2_handle *h = NULL;
165 smb2_util_unlink(tree, fname);
168 io.in.share_access = map_sharemode(test.share_mode);
169 io.in.oplock_level = test.level;
170 status = smb2_create(tree, mem_ctx, &io);
171 CHECK_STATUS(status, NT_STATUS_OK);
172 _h = io.out.file.handle;
174 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
175 CHECK_VAL(io.out.durable_open, test.expected);
176 CHECK_VAL(io.out.oplock_level, test.level);
180 smb2_util_close(tree, *h);
182 smb2_util_unlink(tree, fname);
183 talloc_free(mem_ctx);
188 bool test_durable_open_open1(struct torture_context *tctx,
189 struct smb2_tree *tree)
191 TALLOC_CTX *mem_ctx = talloc_new(tctx);
192 struct smb2_create io;
197 /* Choose a random name in case the state is left a little funky. */
198 snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
200 smb2_util_unlink(tree, fname);
203 io.in.security_flags = 0x00;
204 io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
205 io.in.create_flags = 0x00000000;
206 io.in.reserved = 0x00000000;
207 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
208 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
209 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
210 io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
211 NTCREATEX_OPTIONS_ASYNC_ALERT |
212 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
214 io.in.durable_open = true;
217 /* test various oplock levels with durable open */
219 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
220 ret = test_one_durable_open_open1(tctx,
224 durable_open_vs_oplock_table[i]);
231 smb2_util_unlink(tree, fname);
233 talloc_free(mem_ctx);
239 * basic durable_open test.
240 * durable state should only be granted when requested
241 * along with a batch oplock or a handle lease.
243 * This test tests durable open with all valid lease types.
246 struct durable_open_vs_lease {
248 const char *share_mode;
252 #define NUM_LEASE_TYPES 5
253 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
254 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
263 { "", "RWD", false },
269 { "R", "RW", false },
270 { "R", "RD", false },
271 { "R", "DW", false },
272 { "R", "RWD", false },
275 { "RW", "R", false },
276 { "RW", "W", false },
277 { "RW", "D", false },
278 { "RW", "RW", false },
279 { "RW", "RD", false },
280 { "RW", "WD", false },
281 { "RW", "RWD", false },
287 { "RH", "RW", true },
288 { "RH", "RD", true },
289 { "RH", "WD", true },
290 { "RH", "RWD", true },
293 { "RHW", "R", true },
294 { "RHW", "W", true },
295 { "RHW", "D", true },
296 { "RHW", "RW", true },
297 { "RHW", "RD", true },
298 { "RHW", "WD", true },
299 { "RHW", "RWD", true },
302 static bool test_one_durable_open_open2(struct torture_context *tctx,
303 struct smb2_tree *tree,
305 struct smb2_create io,
306 struct durable_open_vs_lease test)
309 TALLOC_CTX *mem_ctx = talloc_new(tctx);
310 struct smb2_handle _h;
311 struct smb2_handle *h = NULL;
313 struct smb2_lease ls;
316 smb2_util_unlink(tree, fname);
319 io.in.share_access = map_sharemode(test.share_mode);
320 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
325 ls.lease_key.data[0] = lease;
326 ls.lease_key.data[1] = ~lease;
327 ls.lease_state = map_lease(test.type);
328 io.in.lease_request = &ls;
330 status = smb2_create(tree, mem_ctx, &io);
331 CHECK_STATUS(status, NT_STATUS_OK);
332 _h = io.out.file.handle;
334 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
335 CHECK_VAL(io.out.durable_open, test.expected);
336 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
337 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
338 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
339 CHECK_VAL(io.out.lease_response.lease_state, map_lease(test.type));
342 smb2_util_close(tree, *h);
344 smb2_util_unlink(tree, fname);
345 talloc_free(mem_ctx);
350 bool test_durable_open_open2(struct torture_context *tctx,
351 struct smb2_tree *tree)
353 TALLOC_CTX *mem_ctx = talloc_new(tctx);
354 struct smb2_create io;
359 /* Choose a random name in case the state is left a little funky. */
360 snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
362 smb2_util_unlink(tree, fname);
365 io.in.security_flags = 0x00;
366 io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
367 io.in.create_flags = 0x00000000;
368 io.in.reserved = 0x00000000;
369 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
370 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
371 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
372 io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
373 NTCREATEX_OPTIONS_ASYNC_ALERT |
374 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
376 io.in.durable_open = true;
379 /* test various oplock levels with durable open */
381 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
382 ret = test_one_durable_open_open2(tctx,
386 durable_open_vs_lease_table[i]);
393 smb2_util_unlink(tree, fname);
395 talloc_free(mem_ctx);
401 basic testing of SMB2 durable opens
402 regarding the position information on the handle
404 bool test_durable_open_file_position(struct torture_context *tctx,
405 struct smb2_tree *tree1,
406 struct smb2_tree *tree2)
408 TALLOC_CTX *mem_ctx = talloc_new(tctx);
409 struct smb2_handle h1, h2;
410 struct smb2_create io1, io2;
412 const char *fname = "durable_open_position.dat";
413 union smb_fileinfo qfinfo;
414 union smb_setfileinfo sfinfo;
418 smb2_util_unlink(tree1, fname);
421 io1.in.security_flags = 0x00;
422 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
423 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
424 io1.in.create_flags = 0x00000000;
425 io1.in.reserved = 0x00000000;
426 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
427 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
428 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
429 NTCREATEX_SHARE_ACCESS_WRITE |
430 NTCREATEX_SHARE_ACCESS_DELETE;
431 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
432 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
433 NTCREATEX_OPTIONS_ASYNC_ALERT |
434 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
436 io1.in.durable_open = true;
437 io1.in.fname = fname;
439 status = smb2_create(tree1, mem_ctx, &io1);
440 CHECK_STATUS(status, NT_STATUS_OK);
441 h1 = io1.out.file.handle;
442 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
443 CHECK_VAL(io1.out.durable_open, true);
444 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
446 /* TODO: check extra blob content */
449 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
450 qfinfo.generic.in.file.handle = h1;
451 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
452 CHECK_STATUS(status, NT_STATUS_OK);
453 CHECK_VAL(qfinfo.position_information.out.position, 0);
454 pos = qfinfo.position_information.out.position;
455 torture_comment(tctx, "position: %llu\n",
456 (unsigned long long)pos);
459 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
460 sfinfo.generic.in.file.handle = h1;
461 sfinfo.position_information.in.position = 0x1000;
462 status = smb2_setinfo_file(tree1, &sfinfo);
463 CHECK_STATUS(status, NT_STATUS_OK);
466 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
467 qfinfo.generic.in.file.handle = h1;
468 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
469 CHECK_STATUS(status, NT_STATUS_OK);
470 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
471 pos = qfinfo.position_information.out.position;
472 torture_comment(tctx, "position: %llu\n",
473 (unsigned long long)pos);
479 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
480 qfinfo.generic.in.file.handle = h1;
481 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
482 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
485 io2.in.fname = fname;
486 io2.in.durable_handle = &h1;
488 status = smb2_create(tree2, mem_ctx, &io2);
489 CHECK_STATUS(status, NT_STATUS_OK);
490 CHECK_VAL(io2.out.durable_open, true);
491 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
492 CHECK_VAL(io2.out.reserved, 0x00);
493 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
494 CHECK_VAL(io2.out.alloc_size, 0);
495 CHECK_VAL(io2.out.size, 0);
496 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
497 CHECK_VAL(io2.out.reserved2, 0);
499 h2 = io2.out.file.handle;
502 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
503 qfinfo.generic.in.file.handle = h2;
504 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
505 CHECK_STATUS(status, NT_STATUS_OK);
506 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
507 pos = qfinfo.position_information.out.position;
508 torture_comment(tctx, "position: %llu\n",
509 (unsigned long long)pos);
511 smb2_util_close(tree2, h2);
513 talloc_free(mem_ctx);
515 smb2_util_unlink(tree2, fname);
524 Open, disconnect, oplock break, reconnect.
526 bool test_durable_open_oplock(struct torture_context *tctx,
527 struct smb2_tree *tree1,
528 struct smb2_tree *tree2)
530 TALLOC_CTX *mem_ctx = talloc_new(tctx);
531 struct smb2_create io1, io2;
532 struct smb2_handle h1, h2;
537 /* Choose a random name in case the state is left a little funky. */
538 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
541 smb2_util_unlink(tree1, fname);
543 /* Create with batch oplock */
545 io1.in.security_flags = 0x00;
546 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
547 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
548 io1.in.create_flags = 0x00000000;
549 io1.in.reserved = 0x00000000;
550 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
551 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
552 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
553 NTCREATEX_SHARE_ACCESS_WRITE |
554 NTCREATEX_SHARE_ACCESS_DELETE;
555 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
556 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
557 NTCREATEX_OPTIONS_ASYNC_ALERT |
558 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
560 io1.in.fname = fname;
561 io1.in.durable_open = true;
564 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
566 status = smb2_create(tree1, mem_ctx, &io1);
567 CHECK_STATUS(status, NT_STATUS_OK);
568 h1 = io1.out.file.handle;
569 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
570 CHECK_VAL(io1.out.durable_open, true);
571 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
573 /* Disconnect after getting the batch */
578 * Windows7 (build 7000) will break a batch oplock immediately if the
579 * original client is gone. (ZML: This seems like a bug. It should give
580 * some time for the client to reconnect!)
582 status = smb2_create(tree2, mem_ctx, &io2);
583 CHECK_STATUS(status, NT_STATUS_OK);
584 h2 = 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_OPLOCK_LEVEL_BATCH);
589 /* What if tree1 tries to come back and reclaim? */
590 if (!torture_smb2_connection(tctx, &tree1)) {
591 torture_warning(tctx, "couldn't reconnect, bailing\n");
597 io1.in.fname = fname;
598 io1.in.durable_handle = &h1;
600 status = smb2_create(tree1, mem_ctx, &io1);
601 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
604 smb2_util_close(tree2, h2);
605 smb2_util_unlink(tree2, fname);
614 Open, disconnect, lease break, reconnect.
616 bool test_durable_open_lease(struct torture_context *tctx,
617 struct smb2_tree *tree1,
618 struct smb2_tree *tree2)
620 TALLOC_CTX *mem_ctx = talloc_new(tctx);
621 struct smb2_create io1, io2;
622 struct smb2_lease ls1, ls2;
623 struct smb2_handle h1, h2;
627 uint64_t lease1, lease2;
630 * Choose a random name and random lease in case the state is left a
635 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
638 smb2_util_unlink(tree1, fname);
640 /* Create with lease */
642 io1.in.security_flags = 0x00;
643 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
644 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
645 io1.in.create_flags = 0x00000000;
646 io1.in.reserved = 0x00000000;
647 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
648 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
649 io1.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
650 NTCREATEX_SHARE_ACCESS_WRITE |
651 NTCREATEX_SHARE_ACCESS_DELETE;
652 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
653 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
654 NTCREATEX_OPTIONS_ASYNC_ALERT |
655 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
657 io1.in.fname = fname;
658 io1.in.durable_open = true;
661 ls1.lease_key.data[0] = lease1;
662 ls1.lease_key.data[1] = ~lease1;
663 ls1.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
664 io1.in.lease_request = &ls1;
668 ls2.lease_key.data[0] = lease2;
669 ls2.lease_key.data[1] = ~lease2;
670 io2.in.lease_request = &ls2;
671 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
673 status = smb2_create(tree1, mem_ctx, &io1);
674 CHECK_STATUS(status, NT_STATUS_OK);
675 h1 = io1.out.file.handle;
676 CHECK_VAL(io1.out.durable_open, true);
677 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
679 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
680 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
681 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
682 CHECK_VAL(io1.out.lease_response.lease_state,
683 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
685 /* Disconnect after getting the lease */
690 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
691 * even if the original client is gone. (ZML: This seems like a bug. It
692 * should give some time for the client to reconnect! And why RH?)
694 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
695 * Test is adapted accordingly.
697 status = smb2_create(tree2, mem_ctx, &io2);
698 CHECK_STATUS(status, NT_STATUS_OK);
699 h2 = io2.out.file.handle;
700 CHECK_VAL(io2.out.durable_open, true);
701 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
703 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
704 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
705 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
706 CHECK_VAL(io2.out.lease_response.lease_state,
707 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
709 /* What if tree1 tries to come back and reclaim? */
710 if (!torture_smb2_connection(tctx, &tree1)) {
711 torture_warning(tctx, "couldn't reconnect, bailing\n");
717 io1.in.fname = fname;
718 io1.in.durable_handle = &h1;
719 io1.in.lease_request = &ls1;
721 status = smb2_create(tree1, mem_ctx, &io1);
722 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
725 smb2_util_close(tree2, h2);
726 smb2_util_unlink(tree2, fname);
735 Open, take BRL, disconnect, reconnect.
737 bool test_durable_open_lock(struct torture_context *tctx,
738 struct smb2_tree *tree)
740 TALLOC_CTX *mem_ctx = talloc_new(tctx);
741 struct smb2_create io;
742 struct smb2_lease ls;
743 struct smb2_handle h;
744 struct smb2_lock lck;
745 struct smb2_lock_element el[2];
752 * Choose a random name and random lease in case the state is left a
756 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
759 smb2_util_unlink(tree, fname);
761 /* Create with lease */
763 io.in.security_flags = 0x00;
764 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
765 io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
766 io.in.create_flags = 0x00000000;
767 io.in.reserved = 0x00000000;
768 io.in.desired_access = SEC_RIGHTS_FILE_ALL;
769 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
770 io.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
771 NTCREATEX_SHARE_ACCESS_WRITE |
772 NTCREATEX_SHARE_ACCESS_DELETE;
773 io.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
774 io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
775 NTCREATEX_OPTIONS_ASYNC_ALERT |
776 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
779 io.in.durable_open = true;
782 ls.lease_key.data[0] = lease;
783 ls.lease_key.data[1] = ~lease;
784 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE;
785 io.in.lease_request = &ls;
787 status = smb2_create(tree, mem_ctx, &io);
788 CHECK_STATUS(status, NT_STATUS_OK);
789 h = io.out.file.handle;
790 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
792 CHECK_VAL(io.out.durable_open, true);
793 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
794 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
795 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
796 CHECK_VAL(io.out.lease_response.lease_state,
797 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
802 lck.in.lock_count = 0x0001;
803 lck.in.lock_sequence = 0x00000000;
804 lck.in.file.handle = h;
807 el[0].reserved = 0x00000000;
808 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
809 status = smb2_lock(tree, &lck);
810 CHECK_STATUS(status, NT_STATUS_OK);
812 /* Disconnect/Reconnect. */
816 if (!torture_smb2_connection(tctx, &tree)) {
817 torture_warning(tctx, "couldn't reconnect, bailing\n");
824 io.in.durable_handle = &h;
825 io.in.lease_request = &ls;
827 status = smb2_create(tree, mem_ctx, &io);
828 CHECK_STATUS(status, NT_STATUS_OK);
829 h = io.out.file.handle;
831 lck.in.file.handle = h;
832 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
833 status = smb2_lock(tree, &lck);
834 CHECK_STATUS(status, NT_STATUS_OK);
837 smb2_util_close(tree, h);
838 smb2_util_unlink(tree, fname);
845 Open, disconnect, open in another tree, reconnect.
847 This test actually demonstrates a minimum level of respect for the durable
848 open in the face of another open. As long as this test shows an inability to
849 reconnect after an open, the oplock/lease tests above will certainly
850 demonstrate an error on reconnect.
852 bool test_durable_open_open(struct torture_context *tctx,
853 struct smb2_tree *tree1,
854 struct smb2_tree *tree2)
856 TALLOC_CTX *mem_ctx = talloc_new(tctx);
857 struct smb2_create io1, io2;
858 struct smb2_lease ls;
859 struct smb2_handle h1, h2;
866 * Choose a random name and random lease in case the state is left a
870 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
873 smb2_util_unlink(tree1, fname);
875 /* Create with lease */
877 io1.in.security_flags = 0x00;
878 io1.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
879 io1.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
880 io1.in.create_flags = 0x00000000;
881 io1.in.reserved = 0x00000000;
882 io1.in.desired_access = SEC_RIGHTS_FILE_ALL;
883 io1.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
884 io1.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
885 io1.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
886 io1.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
887 NTCREATEX_OPTIONS_ASYNC_ALERT |
888 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
890 io1.in.fname = fname;
891 io1.in.durable_open = true;
894 io2.in.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
895 io2.in.durable_open = false;
898 ls.lease_key.data[0] = lease;
899 ls.lease_key.data[1] = ~lease;
900 ls.lease_state = SMB2_LEASE_READ|SMB2_LEASE_HANDLE;
901 io1.in.lease_request = &ls;
903 status = smb2_create(tree1, mem_ctx, &io1);
904 CHECK_STATUS(status, NT_STATUS_OK);
905 h1 = io1.out.file.handle;
906 CHECK_VAL(io1.out.durable_open, true);
907 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
909 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
910 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
911 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
912 CHECK_VAL(io1.out.lease_response.lease_state,
913 SMB2_LEASE_READ|SMB2_LEASE_HANDLE);
919 /* Open the file in tree2 */
920 status = smb2_create(tree2, mem_ctx, &io2);
921 CHECK_STATUS(status, NT_STATUS_OK);
922 h2 = io2.out.file.handle;
923 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
926 if (!torture_smb2_connection(tctx, &tree1)) {
927 torture_warning(tctx, "couldn't reconnect, bailing\n");
933 io1.in.fname = fname;
934 io1.in.durable_handle = &h1;
935 io1.in.lease_request = &ls;
938 * Windows7 (build 7000) will give away an open immediately if the
939 * original client is gone. (ZML: This seems like a bug. It should give
940 * some time for the client to reconnect!)
942 status = smb2_create(tree1, mem_ctx, &io1);
943 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
944 h1 = io1.out.file.handle;
947 smb2_util_close(tree2, h2);
948 smb2_util_unlink(tree2, fname);
949 smb2_util_close(tree1, h1);
950 smb2_util_unlink(tree1, fname);
958 struct torture_suite *torture_smb2_durable_open_init(void)
960 struct torture_suite *suite =
961 torture_suite_create(talloc_autofree_context(), "durable-open");
963 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
964 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
965 torture_suite_add_2smb2_test(suite, "file-position",
966 test_durable_open_file_position);
967 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
968 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
969 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
970 torture_suite_add_2smb2_test(suite, "open", test_durable_open_open);
972 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");