s4:test:smb2:durable_open: skip lease tests when the server does not support leases
[Samba.git] / source4 / torture / smb2 / durable_open.c
blobbd11d707e004d8878d3ad79d4bd0e2d3cc42d12d
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "../libcli/smb/smbXcli_base.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "../libcli/smb/smbXcli_base.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)v, (int)correct); \
34 ret = false; \
35 }} while (0)
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
41 ret = false; \
42 goto done; \
43 }} while (0)
45 #define CHECK_CREATED(__io, __created, __attribute) \
46 do { \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
52 } while(0)
55 /**
56 * basic durable_open test.
57 * durable state should only be granted when requested
58 * along with a batch oplock or a handle lease.
60 * This test tests durable open with all possible oplock types.
63 struct durable_open_vs_oplock {
64 const char *level;
65 const char *share_mode;
66 bool expected;
69 #define NUM_OPLOCK_TYPES 4
70 #define NUM_SHARE_MODES 8
71 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
72 struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
74 { "", "", false },
75 { "", "R", false },
76 { "", "W", false },
77 { "", "D", false },
78 { "", "RD", false },
79 { "", "RW", false },
80 { "", "WD", false },
81 { "", "RWD", false },
83 { "s", "", false },
84 { "s", "R", false },
85 { "s", "W", false },
86 { "s", "D", false },
87 { "s", "RD", false },
88 { "s", "RW", false },
89 { "s", "WD", false },
90 { "s", "RWD", false },
92 { "x", "", false },
93 { "x", "R", false },
94 { "x", "W", false },
95 { "x", "D", false },
96 { "x", "RD", false },
97 { "x", "RW", false },
98 { "x", "WD", false },
99 { "x", "RWD", false },
101 { "b", "", true },
102 { "b", "R", true },
103 { "b", "W", true },
104 { "b", "D", true },
105 { "b", "RD", true },
106 { "b", "RW", true },
107 { "b", "WD", true },
108 { "b", "RWD", true },
111 static bool test_one_durable_open_open1(struct torture_context *tctx,
112 struct smb2_tree *tree,
113 const char *fname,
114 struct durable_open_vs_oplock test)
116 NTSTATUS status;
117 TALLOC_CTX *mem_ctx = talloc_new(tctx);
118 struct smb2_handle _h;
119 struct smb2_handle *h = NULL;
120 bool ret = true;
121 struct smb2_create io;
123 smb2_util_unlink(tree, fname);
125 smb2_oplock_create_share(&io, fname,
126 smb2_util_share_access(test.share_mode),
127 smb2_util_oplock_level(test.level));
128 io.in.durable_open = true;
130 status = smb2_create(tree, mem_ctx, &io);
131 CHECK_STATUS(status, NT_STATUS_OK);
132 _h = io.out.file.handle;
133 h = &_h;
134 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
135 CHECK_VAL(io.out.durable_open, test.expected);
136 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
138 done:
139 if (h != NULL) {
140 smb2_util_close(tree, *h);
142 smb2_util_unlink(tree, fname);
143 talloc_free(mem_ctx);
145 return ret;
148 bool test_durable_open_open1(struct torture_context *tctx,
149 struct smb2_tree *tree)
151 TALLOC_CTX *mem_ctx = talloc_new(tctx);
152 char fname[256];
153 bool ret = true;
154 int i;
156 /* Choose a random name in case the state is left a little funky. */
157 snprintf(fname, 256, "durable_open_open1_%s.dat", generate_random_str(tctx, 8));
159 smb2_util_unlink(tree, fname);
161 /* test various oplock levels with durable open */
163 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
164 ret = test_one_durable_open_open1(tctx,
165 tree,
166 fname,
167 durable_open_vs_oplock_table[i]);
168 if (ret == false) {
169 goto done;
173 done:
174 smb2_util_unlink(tree, fname);
175 talloc_free(tree);
176 talloc_free(mem_ctx);
178 return ret;
182 * basic durable_open test.
183 * durable state should only be granted when requested
184 * along with a batch oplock or a handle lease.
186 * This test tests durable open with all valid lease types.
189 struct durable_open_vs_lease {
190 const char *type;
191 const char *share_mode;
192 bool expected;
195 #define NUM_LEASE_TYPES 5
196 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
197 struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
199 { "", "", false },
200 { "", "R", false },
201 { "", "W", false },
202 { "", "D", false },
203 { "", "RW", false },
204 { "", "RD", false },
205 { "", "WD", false },
206 { "", "RWD", false },
208 { "R", "", false },
209 { "R", "R", false },
210 { "R", "W", false },
211 { "R", "D", false },
212 { "R", "RW", false },
213 { "R", "RD", false },
214 { "R", "DW", false },
215 { "R", "RWD", false },
217 { "RW", "", false },
218 { "RW", "R", false },
219 { "RW", "W", false },
220 { "RW", "D", false },
221 { "RW", "RW", false },
222 { "RW", "RD", false },
223 { "RW", "WD", false },
224 { "RW", "RWD", false },
226 { "RH", "", true },
227 { "RH", "R", true },
228 { "RH", "W", true },
229 { "RH", "D", true },
230 { "RH", "RW", true },
231 { "RH", "RD", true },
232 { "RH", "WD", true },
233 { "RH", "RWD", true },
235 { "RHW", "", true },
236 { "RHW", "R", true },
237 { "RHW", "W", true },
238 { "RHW", "D", true },
239 { "RHW", "RW", true },
240 { "RHW", "RD", true },
241 { "RHW", "WD", true },
242 { "RHW", "RWD", true },
245 static bool test_one_durable_open_open2(struct torture_context *tctx,
246 struct smb2_tree *tree,
247 const char *fname,
248 struct durable_open_vs_lease test)
250 NTSTATUS status;
251 TALLOC_CTX *mem_ctx = talloc_new(tctx);
252 struct smb2_handle _h;
253 struct smb2_handle *h = NULL;
254 bool ret = true;
255 struct smb2_create io;
256 struct smb2_lease ls;
257 uint64_t lease;
259 smb2_util_unlink(tree, fname);
261 lease = random();
263 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
264 smb2_util_share_access(test.share_mode),
265 lease,
266 smb2_util_lease_state(test.type));
267 io.in.durable_open = true;
269 status = smb2_create(tree, mem_ctx, &io);
270 CHECK_STATUS(status, NT_STATUS_OK);
271 _h = io.out.file.handle;
272 h = &_h;
273 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
274 CHECK_VAL(io.out.durable_open, test.expected);
275 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
276 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
277 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
278 CHECK_VAL(io.out.lease_response.lease_state,
279 smb2_util_lease_state(test.type));
280 done:
281 if (h != NULL) {
282 smb2_util_close(tree, *h);
284 smb2_util_unlink(tree, fname);
285 talloc_free(mem_ctx);
287 return ret;
290 bool test_durable_open_open2(struct torture_context *tctx,
291 struct smb2_tree *tree)
293 TALLOC_CTX *mem_ctx = talloc_new(tctx);
294 char fname[256];
295 bool ret = true;
296 int i;
297 uint32_t caps;
299 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
300 if (!(caps & SMB2_CAP_LEASING)) {
301 torture_skip(tctx, "leases are not supported");
304 /* Choose a random name in case the state is left a little funky. */
305 snprintf(fname, 256, "durable_open_open2_%s.dat", generate_random_str(tctx, 8));
307 smb2_util_unlink(tree, fname);
310 /* test various oplock levels with durable open */
312 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
313 ret = test_one_durable_open_open2(tctx,
314 tree,
315 fname,
316 durable_open_vs_lease_table[i]);
317 if (ret == false) {
318 goto done;
322 done:
323 smb2_util_unlink(tree, fname);
324 talloc_free(tree);
325 talloc_free(mem_ctx);
327 return ret;
331 * basic test for doing a durable open
332 * and do a durable reopen on the same connection
333 * while the first open is still active (fails)
335 bool test_durable_open_reopen1(struct torture_context *tctx,
336 struct smb2_tree *tree)
338 NTSTATUS status;
339 TALLOC_CTX *mem_ctx = talloc_new(tctx);
340 char fname[256];
341 struct smb2_handle _h;
342 struct smb2_handle *h = NULL;
343 struct smb2_create io1, io2;
344 bool ret = true;
346 /* Choose a random name in case the state is left a little funky. */
347 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
348 generate_random_str(tctx, 8));
350 smb2_util_unlink(tree, fname);
352 smb2_oplock_create_share(&io1, fname,
353 smb2_util_share_access(""),
354 smb2_util_oplock_level("b"));
355 io1.in.durable_open = true;
357 status = smb2_create(tree, mem_ctx, &io1);
358 CHECK_STATUS(status, NT_STATUS_OK);
359 _h = io1.out.file.handle;
360 h = &_h;
361 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
362 CHECK_VAL(io1.out.durable_open, true);
363 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
365 /* try a durable reconnect while the file is still open */
366 ZERO_STRUCT(io2);
367 io2.in.fname = fname;
368 io2.in.durable_handle = h;
370 status = smb2_create(tree, mem_ctx, &io2);
371 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
373 done:
374 if (h != NULL) {
375 smb2_util_close(tree, *h);
378 smb2_util_unlink(tree, fname);
380 talloc_free(tree);
382 talloc_free(mem_ctx);
384 return ret;
388 * basic test for doing a durable open
389 * tcp disconnect, reconnect, do a durable reopen (succeeds)
391 bool test_durable_open_reopen2(struct torture_context *tctx,
392 struct smb2_tree *tree)
394 NTSTATUS status;
395 TALLOC_CTX *mem_ctx = talloc_new(tctx);
396 char fname[256];
397 struct smb2_handle _h;
398 struct smb2_handle *h = NULL;
399 struct smb2_create io1, io2;
400 bool ret = true;
402 /* Choose a random name in case the state is left a little funky. */
403 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
404 generate_random_str(tctx, 8));
406 smb2_util_unlink(tree, fname);
408 smb2_oplock_create_share(&io1, fname,
409 smb2_util_share_access(""),
410 smb2_util_oplock_level("b"));
411 io1.in.durable_open = true;
413 status = smb2_create(tree, mem_ctx, &io1);
414 CHECK_STATUS(status, NT_STATUS_OK);
415 _h = io1.out.file.handle;
416 h = &_h;
417 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
418 CHECK_VAL(io1.out.durable_open, true);
419 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
421 /* disconnect, reconnect and then do durable reopen */
422 talloc_free(tree);
423 tree = NULL;
425 if (!torture_smb2_connection(tctx, &tree)) {
426 torture_warning(tctx, "couldn't reconnect, bailing\n");
427 ret = false;
428 goto done;
431 ZERO_STRUCT(io2);
432 io2.in.fname = fname;
433 io2.in.durable_handle = h;
434 h = NULL;
436 status = smb2_create(tree, mem_ctx, &io2);
437 CHECK_STATUS(status, NT_STATUS_OK);
438 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
439 CHECK_VAL(io2.out.durable_open, true);
440 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
441 _h = io2.out.file.handle;
442 h = &_h;
444 done:
445 if (h != NULL) {
446 smb2_util_close(tree, *h);
449 smb2_util_unlink(tree, fname);
451 talloc_free(tree);
453 talloc_free(mem_ctx);
455 return ret;
459 * basic test for doing a durable open
460 * tcp disconnect, reconnect with a session reconnect and
461 * do a durable reopen (succeeds)
463 bool test_durable_open_reopen2a(struct torture_context *tctx,
464 struct smb2_tree *tree)
466 NTSTATUS status;
467 TALLOC_CTX *mem_ctx = talloc_new(tctx);
468 char fname[256];
469 struct smb2_handle _h;
470 struct smb2_handle *h = NULL;
471 struct smb2_create io1, io2;
472 uint64_t previous_session_id;
473 bool ret = true;
475 /* Choose a random name in case the state is left a little funky. */
476 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
477 generate_random_str(tctx, 8));
479 smb2_util_unlink(tree, fname);
481 smb2_oplock_create_share(&io1, fname,
482 smb2_util_share_access(""),
483 smb2_util_oplock_level("b"));
484 io1.in.durable_open = true;
486 status = smb2_create(tree, mem_ctx, &io1);
487 CHECK_STATUS(status, NT_STATUS_OK);
488 _h = io1.out.file.handle;
489 h = &_h;
490 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
491 CHECK_VAL(io1.out.durable_open, true);
492 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
494 /* disconnect, reconnect and then do durable reopen */
495 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
496 talloc_free(tree);
497 tree = NULL;
499 if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
500 torture_warning(tctx, "couldn't reconnect, bailing\n");
501 ret = false;
502 goto done;
505 ZERO_STRUCT(io2);
506 io2.in.fname = fname;
507 io2.in.durable_handle = h;
508 h = NULL;
510 status = smb2_create(tree, mem_ctx, &io2);
511 CHECK_STATUS(status, NT_STATUS_OK);
512 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
513 CHECK_VAL(io2.out.durable_open, true);
514 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
515 _h = io2.out.file.handle;
516 h = &_h;
518 done:
519 if (h != NULL) {
520 smb2_util_close(tree, *h);
523 smb2_util_unlink(tree, fname);
525 talloc_free(tree);
527 talloc_free(mem_ctx);
529 return ret;
534 * basic test for doing a durable open:
535 * tdis, new tcon, try durable reopen (fails)
537 bool test_durable_open_reopen3(struct torture_context *tctx,
538 struct smb2_tree *tree)
540 NTSTATUS status;
541 TALLOC_CTX *mem_ctx = talloc_new(tctx);
542 char fname[256];
543 struct smb2_handle _h;
544 struct smb2_handle *h = NULL;
545 struct smb2_create io1, io2;
546 bool ret = true;
547 struct smb2_tree *tree2;
549 /* Choose a random name in case the state is left a little funky. */
550 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
551 generate_random_str(tctx, 8));
553 smb2_util_unlink(tree, fname);
555 smb2_oplock_create_share(&io1, fname,
556 smb2_util_share_access(""),
557 smb2_util_oplock_level("b"));
558 io1.in.durable_open = true;
560 status = smb2_create(tree, mem_ctx, &io1);
561 CHECK_STATUS(status, NT_STATUS_OK);
562 _h = io1.out.file.handle;
563 h = &_h;
564 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
565 CHECK_VAL(io1.out.durable_open, true);
566 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
568 /* disconnect, reconnect and then do durable reopen */
569 status = smb2_tdis(tree);
570 CHECK_STATUS(status, NT_STATUS_OK);
572 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
573 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
574 ret = false;
575 goto done;
579 ZERO_STRUCT(io2);
580 io2.in.fname = fname;
581 io2.in.durable_handle = h;
583 status = smb2_create(tree2, mem_ctx, &io2);
584 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
586 done:
587 if (h != NULL) {
588 smb2_util_close(tree, *h);
591 smb2_util_unlink(tree2, fname);
593 talloc_free(tree);
595 talloc_free(mem_ctx);
597 return ret;
601 * basic test for doing a durable open:
602 * logoff, create a new session, do a durable reopen (succeeds)
604 bool test_durable_open_reopen4(struct torture_context *tctx,
605 struct smb2_tree *tree)
607 NTSTATUS status;
608 TALLOC_CTX *mem_ctx = talloc_new(tctx);
609 char fname[256];
610 struct smb2_handle _h;
611 struct smb2_handle *h = NULL;
612 struct smb2_create io1, io2;
613 bool ret = true;
614 struct smb2_transport *transport;
615 struct smb2_session *session2;
616 struct smb2_tree *tree2;
618 /* Choose a random name in case the state is left a little funky. */
619 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
620 generate_random_str(tctx, 8));
622 smb2_util_unlink(tree, fname);
624 smb2_oplock_create_share(&io1, fname,
625 smb2_util_share_access(""),
626 smb2_util_oplock_level("b"));
627 io1.in.durable_open = true;
628 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
630 status = smb2_create(tree, mem_ctx, &io1);
631 CHECK_STATUS(status, NT_STATUS_OK);
632 _h = io1.out.file.handle;
633 h = &_h;
634 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
635 CHECK_VAL(io1.out.durable_open, true);
636 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
639 * do a session logoff, establish a new session and tree
640 * connect on the same transport, and try a durable reopen
642 transport = tree->session->transport;
643 status = smb2_logoff(tree->session);
644 CHECK_STATUS(status, NT_STATUS_OK);
646 if (!torture_smb2_session_setup(tctx, transport,
647 0, /* previous_session_id */
648 mem_ctx, &session2))
650 torture_warning(tctx, "session setup failed.\n");
651 ret = false;
652 goto done;
656 * the session setup has talloc-stolen the transport,
657 * so we can safely free the old tree+session for clarity
659 TALLOC_FREE(tree);
661 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
662 torture_warning(tctx, "tree connect failed.\n");
663 ret = false;
664 goto done;
667 ZERO_STRUCT(io2);
668 io2.in.fname = fname;
669 io2.in.durable_handle = h;
670 h = NULL;
672 status = smb2_create(tree2, mem_ctx, &io2);
673 CHECK_STATUS(status, NT_STATUS_OK);
675 _h = io2.out.file.handle;
676 h = &_h;
677 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
678 CHECK_VAL(io2.out.durable_open, true);
679 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
681 done:
682 if (h != NULL) {
683 smb2_util_close(tree2, *h);
686 smb2_util_unlink(tree2, fname);
688 talloc_free(tree);
690 talloc_free(mem_ctx);
692 return ret;
696 basic testing of SMB2 durable opens
697 regarding the position information on the handle
699 bool test_durable_open_file_position(struct torture_context *tctx,
700 struct smb2_tree *tree1,
701 struct smb2_tree *tree2)
703 TALLOC_CTX *mem_ctx = talloc_new(tctx);
704 struct smb2_handle h1, h2;
705 struct smb2_create io1, io2;
706 NTSTATUS status;
707 const char *fname = "durable_open_position.dat";
708 union smb_fileinfo qfinfo;
709 union smb_setfileinfo sfinfo;
710 bool ret = true;
711 uint64_t pos;
713 smb2_util_unlink(tree1, fname);
715 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
716 io1.in.durable_open = true;
718 status = smb2_create(tree1, mem_ctx, &io1);
719 CHECK_STATUS(status, NT_STATUS_OK);
720 h1 = io1.out.file.handle;
721 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
722 CHECK_VAL(io1.out.durable_open, true);
723 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
725 /* TODO: check extra blob content */
727 ZERO_STRUCT(qfinfo);
728 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
729 qfinfo.generic.in.file.handle = h1;
730 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
731 CHECK_STATUS(status, NT_STATUS_OK);
732 CHECK_VAL(qfinfo.position_information.out.position, 0);
733 pos = qfinfo.position_information.out.position;
734 torture_comment(tctx, "position: %llu\n",
735 (unsigned long long)pos);
737 ZERO_STRUCT(sfinfo);
738 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
739 sfinfo.generic.in.file.handle = h1;
740 sfinfo.position_information.in.position = 0x1000;
741 status = smb2_setinfo_file(tree1, &sfinfo);
742 CHECK_STATUS(status, NT_STATUS_OK);
744 ZERO_STRUCT(qfinfo);
745 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
746 qfinfo.generic.in.file.handle = h1;
747 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
748 CHECK_STATUS(status, NT_STATUS_OK);
749 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
750 pos = qfinfo.position_information.out.position;
751 torture_comment(tctx, "position: %llu\n",
752 (unsigned long long)pos);
754 talloc_free(tree1);
755 tree1 = NULL;
757 ZERO_STRUCT(qfinfo);
758 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
759 qfinfo.generic.in.file.handle = h1;
760 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
761 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
763 ZERO_STRUCT(io2);
764 io2.in.fname = fname;
765 io2.in.durable_handle = &h1;
767 status = smb2_create(tree2, mem_ctx, &io2);
768 CHECK_STATUS(status, NT_STATUS_OK);
769 CHECK_VAL(io2.out.durable_open, true);
770 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
771 CHECK_VAL(io2.out.reserved, 0x00);
772 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
773 CHECK_VAL(io2.out.alloc_size, 0);
774 CHECK_VAL(io2.out.size, 0);
775 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
776 CHECK_VAL(io2.out.reserved2, 0);
778 h2 = io2.out.file.handle;
780 ZERO_STRUCT(qfinfo);
781 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
782 qfinfo.generic.in.file.handle = h2;
783 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
784 CHECK_STATUS(status, NT_STATUS_OK);
785 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
786 pos = qfinfo.position_information.out.position;
787 torture_comment(tctx, "position: %llu\n",
788 (unsigned long long)pos);
790 smb2_util_close(tree2, h2);
792 talloc_free(mem_ctx);
794 smb2_util_unlink(tree2, fname);
795 done:
796 talloc_free(tree1);
797 talloc_free(tree2);
799 return ret;
803 Open, disconnect, oplock break, reconnect.
805 bool test_durable_open_oplock(struct torture_context *tctx,
806 struct smb2_tree *tree1,
807 struct smb2_tree *tree2)
809 TALLOC_CTX *mem_ctx = talloc_new(tctx);
810 struct smb2_create io1, io2;
811 struct smb2_handle h1, h2;
812 NTSTATUS status;
813 char fname[256];
814 bool ret = true;
816 /* Choose a random name in case the state is left a little funky. */
817 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
819 /* Clean slate */
820 smb2_util_unlink(tree1, fname);
822 /* Create with batch oplock */
823 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
824 io1.in.durable_open = true;
826 io2 = io1;
827 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
829 status = smb2_create(tree1, mem_ctx, &io1);
830 CHECK_STATUS(status, NT_STATUS_OK);
831 h1 = io1.out.file.handle;
832 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
833 CHECK_VAL(io1.out.durable_open, true);
834 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
836 /* Disconnect after getting the batch */
837 talloc_free(tree1);
838 tree1 = NULL;
841 * Windows7 (build 7000) will break a batch oplock immediately if the
842 * original client is gone. (ZML: This seems like a bug. It should give
843 * some time for the client to reconnect!)
845 status = smb2_create(tree2, mem_ctx, &io2);
846 CHECK_STATUS(status, NT_STATUS_OK);
847 h2 = io2.out.file.handle;
848 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
849 CHECK_VAL(io2.out.durable_open, true);
850 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
852 /* What if tree1 tries to come back and reclaim? */
853 if (!torture_smb2_connection(tctx, &tree1)) {
854 torture_warning(tctx, "couldn't reconnect, bailing\n");
855 ret = false;
856 goto done;
859 ZERO_STRUCT(io1);
860 io1.in.fname = fname;
861 io1.in.durable_handle = &h1;
863 status = smb2_create(tree1, mem_ctx, &io1);
864 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
866 done:
867 smb2_util_close(tree2, h2);
868 smb2_util_unlink(tree2, fname);
870 talloc_free(tree1);
871 talloc_free(tree2);
873 return ret;
877 Open, disconnect, lease break, reconnect.
879 bool test_durable_open_lease(struct torture_context *tctx,
880 struct smb2_tree *tree1,
881 struct smb2_tree *tree2)
883 TALLOC_CTX *mem_ctx = talloc_new(tctx);
884 struct smb2_create io1, io2;
885 struct smb2_lease ls1, ls2;
886 struct smb2_handle h1, h2;
887 NTSTATUS status;
888 char fname[256];
889 bool ret = true;
890 uint64_t lease1, lease2;
891 uint32_t caps;
893 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
894 if (!(caps & SMB2_CAP_LEASING)) {
895 torture_skip(tctx, "leases are not supported");
899 * Choose a random name and random lease in case the state is left a
900 * little funky.
902 lease1 = random();
903 lease2 = random();
904 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
906 /* Clean slate */
907 smb2_util_unlink(tree1, fname);
909 /* Create with lease */
910 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
911 lease1, smb2_util_lease_state("RHW"));
912 io1.in.durable_open = true;
914 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
915 lease2, smb2_util_lease_state("RHW"));
916 io2.in.durable_open = true;
917 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
919 status = smb2_create(tree1, mem_ctx, &io1);
920 CHECK_STATUS(status, NT_STATUS_OK);
921 h1 = io1.out.file.handle;
922 CHECK_VAL(io1.out.durable_open, true);
923 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
925 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
926 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
927 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
928 CHECK_VAL(io1.out.lease_response.lease_state,
929 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
931 /* Disconnect after getting the lease */
932 talloc_free(tree1);
933 tree1 = NULL;
936 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
937 * even if the original client is gone. (ZML: This seems like a bug. It
938 * should give some time for the client to reconnect! And why RH?)
940 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
941 * Test is adapted accordingly.
943 status = smb2_create(tree2, mem_ctx, &io2);
944 CHECK_STATUS(status, NT_STATUS_OK);
945 h2 = io2.out.file.handle;
946 CHECK_VAL(io2.out.durable_open, true);
947 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
949 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
950 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
951 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
952 CHECK_VAL(io2.out.lease_response.lease_state,
953 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
955 /* What if tree1 tries to come back and reclaim? */
956 if (!torture_smb2_connection(tctx, &tree1)) {
957 torture_warning(tctx, "couldn't reconnect, bailing\n");
958 ret = false;
959 goto done;
962 ZERO_STRUCT(io1);
963 io1.in.fname = fname;
964 io1.in.durable_handle = &h1;
965 io1.in.lease_request = &ls1;
967 status = smb2_create(tree1, mem_ctx, &io1);
968 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
970 done:
971 smb2_util_close(tree2, h2);
972 smb2_util_unlink(tree2, fname);
974 talloc_free(tree1);
975 talloc_free(tree2);
977 return ret;
981 Open, take BRL, disconnect, reconnect.
983 bool test_durable_open_lock(struct torture_context *tctx,
984 struct smb2_tree *tree)
986 TALLOC_CTX *mem_ctx = talloc_new(tctx);
987 struct smb2_create io;
988 struct smb2_lease ls;
989 struct smb2_handle h;
990 struct smb2_lock lck;
991 struct smb2_lock_element el[2];
992 NTSTATUS status;
993 char fname[256];
994 bool ret = true;
995 uint64_t lease;
996 uint32_t caps;
998 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
999 if (!(caps & SMB2_CAP_LEASING)) {
1000 torture_skip(tctx, "leases are not supported");
1004 * Choose a random name and random lease in case the state is left a
1005 * little funky.
1007 lease = random();
1008 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1010 /* Clean slate */
1011 smb2_util_unlink(tree, fname);
1013 /* Create with lease */
1015 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1016 smb2_util_lease_state("RWH"));
1017 io.in.durable_open = true;
1019 status = smb2_create(tree, mem_ctx, &io);
1020 CHECK_STATUS(status, NT_STATUS_OK);
1021 h = io.out.file.handle;
1022 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1024 CHECK_VAL(io.out.durable_open, true);
1025 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1026 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1027 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1028 CHECK_VAL(io.out.lease_response.lease_state,
1029 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1031 ZERO_STRUCT(lck);
1032 ZERO_STRUCT(el);
1033 lck.in.locks = el;
1034 lck.in.lock_count = 0x0001;
1035 lck.in.lock_sequence = 0x00000000;
1036 lck.in.file.handle = h;
1037 el[0].offset = 0;
1038 el[0].length = 1;
1039 el[0].reserved = 0x00000000;
1040 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1041 status = smb2_lock(tree, &lck);
1042 CHECK_STATUS(status, NT_STATUS_OK);
1044 /* Disconnect/Reconnect. */
1045 talloc_free(tree);
1046 tree = NULL;
1048 if (!torture_smb2_connection(tctx, &tree)) {
1049 torture_warning(tctx, "couldn't reconnect, bailing\n");
1050 ret = false;
1051 goto done;
1054 ZERO_STRUCT(io);
1055 io.in.fname = fname;
1056 io.in.durable_handle = &h;
1057 io.in.lease_request = &ls;
1059 status = smb2_create(tree, mem_ctx, &io);
1060 CHECK_STATUS(status, NT_STATUS_OK);
1061 h = io.out.file.handle;
1063 lck.in.file.handle = h;
1064 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1065 status = smb2_lock(tree, &lck);
1066 CHECK_STATUS(status, NT_STATUS_OK);
1068 done:
1069 smb2_util_close(tree, h);
1070 smb2_util_unlink(tree, fname);
1071 talloc_free(tree);
1073 return ret;
1077 * Open with a RH lease, disconnect, open in another tree, reconnect.
1079 * This test actually demonstrates a minimum level of respect for the durable
1080 * open in the face of another open. As long as this test shows an inability to
1081 * reconnect after an open, the oplock/lease tests above will certainly
1082 * demonstrate an error on reconnect.
1084 bool test_durable_open_open_lease(struct torture_context *tctx,
1085 struct smb2_tree *tree1,
1086 struct smb2_tree *tree2)
1088 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1089 struct smb2_create io1, io2;
1090 struct smb2_lease ls;
1091 struct smb2_handle h1, h2;
1092 NTSTATUS status;
1093 char fname[256];
1094 bool ret = true;
1095 uint64_t lease;
1096 uint32_t caps;
1098 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1099 if (!(caps & SMB2_CAP_LEASING)) {
1100 torture_skip(tctx, "leases are not supported");
1104 * Choose a random name and random lease in case the state is left a
1105 * little funky.
1107 lease = random();
1108 snprintf(fname, 256, "durable_open_open_lease_%s.dat",
1109 generate_random_str(tctx, 8));
1111 /* Clean slate */
1112 smb2_util_unlink(tree1, fname);
1114 /* Create with lease */
1115 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1116 smb2_util_share_access(""),
1117 lease,
1118 smb2_util_lease_state("RH"));
1119 io1.in.durable_open = true;
1121 status = smb2_create(tree1, mem_ctx, &io1);
1122 CHECK_STATUS(status, NT_STATUS_OK);
1123 h1 = io1.out.file.handle;
1124 CHECK_VAL(io1.out.durable_open, true);
1125 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1127 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1128 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1129 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1130 CHECK_VAL(io1.out.lease_response.lease_state,
1131 smb2_util_lease_state("RH"));
1133 /* Disconnect */
1134 talloc_free(tree1);
1135 tree1 = NULL;
1137 /* Open the file in tree2 */
1138 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1140 status = smb2_create(tree2, mem_ctx, &io2);
1141 CHECK_STATUS(status, NT_STATUS_OK);
1142 h2 = io2.out.file.handle;
1143 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1145 /* Reconnect */
1146 if (!torture_smb2_connection(tctx, &tree1)) {
1147 torture_warning(tctx, "couldn't reconnect, bailing\n");
1148 ret = false;
1149 goto done;
1152 ZERO_STRUCT(io1);
1153 io1.in.fname = fname;
1154 io1.in.durable_handle = &h1;
1155 io1.in.lease_request = &ls;
1158 * Windows7 (build 7000) will give away an open immediately if the
1159 * original client is gone. (ZML: This seems like a bug. It should give
1160 * some time for the client to reconnect!)
1162 status = smb2_create(tree1, mem_ctx, &io1);
1163 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1164 h1 = io1.out.file.handle;
1166 done:
1167 smb2_util_close(tree2, h2);
1168 smb2_util_unlink(tree2, fname);
1169 smb2_util_close(tree1, h1);
1170 smb2_util_unlink(tree1, fname);
1172 talloc_free(tree1);
1173 talloc_free(tree2);
1175 return ret;
1179 * Open with a batch oplock, disconnect, open in another tree, reconnect.
1181 * This test actually demonstrates a minimum level of respect for the durable
1182 * open in the face of another open. As long as this test shows an inability to
1183 * reconnect after an open, the oplock/lease tests above will certainly
1184 * demonstrate an error on reconnect.
1186 bool test_durable_open_open_oplock(struct torture_context *tctx,
1187 struct smb2_tree *tree1,
1188 struct smb2_tree *tree2)
1190 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1191 struct smb2_create io1, io2;
1192 struct smb2_handle h1, h2;
1193 NTSTATUS status;
1194 char fname[256];
1195 bool ret = true;
1198 * Choose a random name and random lease in case the state is left a
1199 * little funky.
1201 snprintf(fname, 256, "durable_open_open_oplock_%s.dat",
1202 generate_random_str(tctx, 8));
1204 /* Clean slate */
1205 smb2_util_unlink(tree1, fname);
1207 /* Create with batch oplock */
1208 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1209 io1.in.durable_open = true;
1211 status = smb2_create(tree1, mem_ctx, &io1);
1212 CHECK_STATUS(status, NT_STATUS_OK);
1213 h1 = io1.out.file.handle;
1214 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1215 CHECK_VAL(io1.out.durable_open, true);
1216 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1218 /* Disconnect */
1219 talloc_free(tree1);
1220 tree1 = NULL;
1222 /* Open the file in tree2 */
1223 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1225 status = smb2_create(tree2, mem_ctx, &io2);
1226 CHECK_STATUS(status, NT_STATUS_OK);
1227 h2 = io2.out.file.handle;
1228 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1230 /* Reconnect */
1231 if (!torture_smb2_connection(tctx, &tree1)) {
1232 torture_warning(tctx, "couldn't reconnect, bailing\n");
1233 ret = false;
1234 goto done;
1237 ZERO_STRUCT(io1);
1238 io1.in.fname = fname;
1239 io1.in.durable_handle = &h1;
1241 status = smb2_create(tree1, mem_ctx, &io1);
1242 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1243 h1 = io1.out.file.handle;
1245 done:
1246 smb2_util_close(tree2, h2);
1247 smb2_util_unlink(tree2, fname);
1248 smb2_util_close(tree1, h1);
1249 smb2_util_unlink(tree1, fname);
1251 talloc_free(tree1);
1252 talloc_free(tree2);
1254 return ret;
1257 struct torture_suite *torture_smb2_durable_open_init(void)
1259 struct torture_suite *suite =
1260 torture_suite_create(talloc_autofree_context(), "durable-open");
1262 torture_suite_add_1smb2_test(suite, "open1", test_durable_open_open1);
1263 torture_suite_add_1smb2_test(suite, "open2", test_durable_open_open2);
1264 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1265 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1266 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1267 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1268 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1269 torture_suite_add_2smb2_test(suite, "file-position",
1270 test_durable_open_file_position);
1271 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1272 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1273 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1274 torture_suite_add_2smb2_test(suite, "open-lease",
1275 test_durable_open_open_lease);
1276 torture_suite_add_2smb2_test(suite, "open-oplock",
1277 test_durable_open_open_oplock);
1279 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1281 return suite;