s3-selftest: Remove some unnecessary comma
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / durable_open.c
blob0c20f776ea15af3aea2adc6028628df4d590a7e6
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 durable opens
6 Copyright (C) Stefan Metzmacher 2008
7 Copyright (C) Michael Adam 2011-2012
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "includes.h"
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "../libcli/smb/smbXcli_base.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
29 #include "../libcli/smb/smbXcli_base.h"
31 #define CHECK_VAL(v, correct) do { \
32 if ((v) != (correct)) { \
33 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
34 __location__, #v, (int)v, (int)correct); \
35 ret = false; \
36 }} while (0)
38 #define CHECK_STATUS(status, correct) do { \
39 if (!NT_STATUS_EQUAL(status, correct)) { \
40 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
41 nt_errstr(status), nt_errstr(correct)); \
42 ret = false; \
43 goto done; \
44 }} while (0)
46 #define CHECK_CREATED(__io, __created, __attribute) \
47 do { \
48 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
49 CHECK_VAL((__io)->out.alloc_size, 0); \
50 CHECK_VAL((__io)->out.size, 0); \
51 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
52 CHECK_VAL((__io)->out.reserved2, 0); \
53 } while(0)
56 /**
57 * basic durable_open test.
58 * durable state should only be granted when requested
59 * along with a batch oplock or a handle lease.
61 * This test tests durable open with all possible oplock types.
64 struct durable_open_vs_oplock {
65 const char *level;
66 const char *share_mode;
67 bool expected;
70 #define NUM_OPLOCK_TYPES 4
71 #define NUM_SHARE_MODES 8
72 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
73 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
75 { "", "", false },
76 { "", "R", false },
77 { "", "W", false },
78 { "", "D", false },
79 { "", "RD", false },
80 { "", "RW", false },
81 { "", "WD", false },
82 { "", "RWD", false },
84 { "s", "", false },
85 { "s", "R", false },
86 { "s", "W", false },
87 { "s", "D", false },
88 { "s", "RD", false },
89 { "s", "RW", false },
90 { "s", "WD", false },
91 { "s", "RWD", false },
93 { "x", "", false },
94 { "x", "R", false },
95 { "x", "W", false },
96 { "x", "D", false },
97 { "x", "RD", false },
98 { "x", "RW", false },
99 { "x", "WD", false },
100 { "x", "RWD", false },
102 { "b", "", true },
103 { "b", "R", true },
104 { "b", "W", true },
105 { "b", "D", true },
106 { "b", "RD", true },
107 { "b", "RW", true },
108 { "b", "WD", true },
109 { "b", "RWD", true },
112 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
113 struct smb2_tree *tree,
114 const char *fname,
115 struct durable_open_vs_oplock test)
117 NTSTATUS status;
118 TALLOC_CTX *mem_ctx = talloc_new(tctx);
119 struct smb2_handle _h;
120 struct smb2_handle *h = NULL;
121 bool ret = true;
122 struct smb2_create io;
124 smb2_util_unlink(tree, fname);
126 smb2_oplock_create_share(&io, fname,
127 smb2_util_share_access(test.share_mode),
128 smb2_util_oplock_level(test.level));
129 io.in.durable_open = true;
131 status = smb2_create(tree, mem_ctx, &io);
132 CHECK_STATUS(status, NT_STATUS_OK);
133 _h = io.out.file.handle;
134 h = &_h;
135 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
136 CHECK_VAL(io.out.durable_open, test.expected);
137 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
139 done:
140 if (h != NULL) {
141 smb2_util_close(tree, *h);
143 smb2_util_unlink(tree, fname);
144 talloc_free(mem_ctx);
146 return ret;
149 bool test_durable_open_open_oplock(struct torture_context *tctx,
150 struct smb2_tree *tree)
152 TALLOC_CTX *mem_ctx = talloc_new(tctx);
153 char fname[256];
154 bool ret = true;
155 int i;
157 /* Choose a random name in case the state is left a little funky. */
158 snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
160 smb2_util_unlink(tree, fname);
162 /* test various oplock levels with durable open */
164 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
165 ret = test_one_durable_open_open_oplock(tctx,
166 tree,
167 fname,
168 durable_open_vs_oplock_table[i]);
169 if (ret == false) {
170 goto done;
174 done:
175 smb2_util_unlink(tree, fname);
176 talloc_free(tree);
177 talloc_free(mem_ctx);
179 return ret;
183 * basic durable_open test.
184 * durable state should only be granted when requested
185 * along with a batch oplock or a handle lease.
187 * This test tests durable open with all valid lease types.
190 struct durable_open_vs_lease {
191 const char *type;
192 const char *share_mode;
193 bool expected;
196 #define NUM_LEASE_TYPES 5
197 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
198 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
200 { "", "", false },
201 { "", "R", false },
202 { "", "W", false },
203 { "", "D", false },
204 { "", "RW", false },
205 { "", "RD", false },
206 { "", "WD", false },
207 { "", "RWD", false },
209 { "R", "", false },
210 { "R", "R", false },
211 { "R", "W", false },
212 { "R", "D", false },
213 { "R", "RW", false },
214 { "R", "RD", false },
215 { "R", "DW", false },
216 { "R", "RWD", false },
218 { "RW", "", false },
219 { "RW", "R", false },
220 { "RW", "W", false },
221 { "RW", "D", false },
222 { "RW", "RW", false },
223 { "RW", "RD", false },
224 { "RW", "WD", false },
225 { "RW", "RWD", false },
227 { "RH", "", true },
228 { "RH", "R", true },
229 { "RH", "W", true },
230 { "RH", "D", true },
231 { "RH", "RW", true },
232 { "RH", "RD", true },
233 { "RH", "WD", true },
234 { "RH", "RWD", true },
236 { "RHW", "", true },
237 { "RHW", "R", true },
238 { "RHW", "W", true },
239 { "RHW", "D", true },
240 { "RHW", "RW", true },
241 { "RHW", "RD", true },
242 { "RHW", "WD", true },
243 { "RHW", "RWD", true },
246 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
247 struct smb2_tree *tree,
248 const char *fname,
249 struct durable_open_vs_lease test)
251 NTSTATUS status;
252 TALLOC_CTX *mem_ctx = talloc_new(tctx);
253 struct smb2_handle _h;
254 struct smb2_handle *h = NULL;
255 bool ret = true;
256 struct smb2_create io;
257 struct smb2_lease ls;
258 uint64_t lease;
259 uint32_t caps;
261 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
262 if (!(caps & SMB2_CAP_LEASING)) {
263 torture_skip(tctx, "leases are not supported");
266 smb2_util_unlink(tree, fname);
268 lease = random();
270 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
271 smb2_util_share_access(test.share_mode),
272 lease,
273 smb2_util_lease_state(test.type));
274 io.in.durable_open = true;
276 status = smb2_create(tree, mem_ctx, &io);
277 CHECK_STATUS(status, NT_STATUS_OK);
278 _h = io.out.file.handle;
279 h = &_h;
280 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
281 CHECK_VAL(io.out.durable_open, test.expected);
282 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
283 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
284 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
285 CHECK_VAL(io.out.lease_response.lease_state,
286 smb2_util_lease_state(test.type));
287 done:
288 if (h != NULL) {
289 smb2_util_close(tree, *h);
291 smb2_util_unlink(tree, fname);
292 talloc_free(mem_ctx);
294 return ret;
297 bool test_durable_open_open_lease(struct torture_context *tctx,
298 struct smb2_tree *tree)
300 TALLOC_CTX *mem_ctx = talloc_new(tctx);
301 char fname[256];
302 bool ret = true;
303 int i;
304 uint32_t caps;
306 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
307 if (!(caps & SMB2_CAP_LEASING)) {
308 torture_skip(tctx, "leases are not supported");
311 /* Choose a random name in case the state is left a little funky. */
312 snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
314 smb2_util_unlink(tree, fname);
317 /* test various oplock levels with durable open */
319 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
320 ret = test_one_durable_open_open_lease(tctx,
321 tree,
322 fname,
323 durable_open_vs_lease_table[i]);
324 if (ret == false) {
325 goto done;
329 done:
330 smb2_util_unlink(tree, fname);
331 talloc_free(tree);
332 talloc_free(mem_ctx);
334 return ret;
338 * basic test for doing a durable open
339 * and do a durable reopen on the same connection
340 * while the first open is still active (fails)
342 bool test_durable_open_reopen1(struct torture_context *tctx,
343 struct smb2_tree *tree)
345 NTSTATUS status;
346 TALLOC_CTX *mem_ctx = talloc_new(tctx);
347 char fname[256];
348 struct smb2_handle _h;
349 struct smb2_handle *h = NULL;
350 struct smb2_create io1, io2;
351 bool ret = true;
353 /* Choose a random name in case the state is left a little funky. */
354 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
355 generate_random_str(tctx, 8));
357 smb2_util_unlink(tree, fname);
359 smb2_oplock_create_share(&io1, fname,
360 smb2_util_share_access(""),
361 smb2_util_oplock_level("b"));
362 io1.in.durable_open = true;
364 status = smb2_create(tree, mem_ctx, &io1);
365 CHECK_STATUS(status, NT_STATUS_OK);
366 _h = io1.out.file.handle;
367 h = &_h;
368 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
369 CHECK_VAL(io1.out.durable_open, true);
370 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
372 /* try a durable reconnect while the file is still open */
373 ZERO_STRUCT(io2);
374 io2.in.fname = fname;
375 io2.in.durable_handle = h;
377 status = smb2_create(tree, mem_ctx, &io2);
378 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
380 done:
381 if (h != NULL) {
382 smb2_util_close(tree, *h);
385 smb2_util_unlink(tree, fname);
387 talloc_free(tree);
389 talloc_free(mem_ctx);
391 return ret;
395 * basic test for doing a durable open
396 * tcp disconnect, reconnect, do a durable reopen (succeeds)
398 bool test_durable_open_reopen2(struct torture_context *tctx,
399 struct smb2_tree *tree)
401 NTSTATUS status;
402 TALLOC_CTX *mem_ctx = talloc_new(tctx);
403 char fname[256];
404 struct smb2_handle _h;
405 struct smb2_handle *h = NULL;
406 struct smb2_create io1, io2;
407 bool ret = true;
409 /* Choose a random name in case the state is left a little funky. */
410 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
411 generate_random_str(tctx, 8));
413 smb2_util_unlink(tree, fname);
415 smb2_oplock_create_share(&io1, fname,
416 smb2_util_share_access(""),
417 smb2_util_oplock_level("b"));
418 io1.in.durable_open = true;
420 status = smb2_create(tree, mem_ctx, &io1);
421 CHECK_STATUS(status, NT_STATUS_OK);
422 _h = io1.out.file.handle;
423 h = &_h;
424 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
425 CHECK_VAL(io1.out.durable_open, true);
426 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
428 /* disconnect, reconnect and then do durable reopen */
429 talloc_free(tree);
430 tree = NULL;
432 if (!torture_smb2_connection(tctx, &tree)) {
433 torture_warning(tctx, "couldn't reconnect, bailing\n");
434 ret = false;
435 goto done;
438 ZERO_STRUCT(io2);
439 io2.in.fname = fname;
440 io2.in.durable_handle = h;
441 h = NULL;
443 status = smb2_create(tree, mem_ctx, &io2);
444 CHECK_STATUS(status, NT_STATUS_OK);
445 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
446 CHECK_VAL(io2.out.durable_open, true);
447 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
448 _h = io2.out.file.handle;
449 h = &_h;
451 done:
452 if (h != NULL) {
453 smb2_util_close(tree, *h);
456 smb2_util_unlink(tree, fname);
458 talloc_free(tree);
460 talloc_free(mem_ctx);
462 return ret;
466 * basic test for doing a durable open
467 * tcp disconnect, reconnect with a session reconnect and
468 * do a durable reopen (succeeds)
470 bool test_durable_open_reopen2a(struct torture_context *tctx,
471 struct smb2_tree *tree)
473 NTSTATUS status;
474 TALLOC_CTX *mem_ctx = talloc_new(tctx);
475 char fname[256];
476 struct smb2_handle _h;
477 struct smb2_handle *h = NULL;
478 struct smb2_create io1, io2;
479 uint64_t previous_session_id;
480 bool ret = true;
482 /* Choose a random name in case the state is left a little funky. */
483 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
484 generate_random_str(tctx, 8));
486 smb2_util_unlink(tree, fname);
488 smb2_oplock_create_share(&io1, fname,
489 smb2_util_share_access(""),
490 smb2_util_oplock_level("b"));
491 io1.in.durable_open = true;
493 status = smb2_create(tree, mem_ctx, &io1);
494 CHECK_STATUS(status, NT_STATUS_OK);
495 _h = io1.out.file.handle;
496 h = &_h;
497 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
498 CHECK_VAL(io1.out.durable_open, true);
499 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
501 /* disconnect, reconnect and then do durable reopen */
502 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
503 talloc_free(tree);
504 tree = NULL;
506 if (!torture_smb2_connection_ext(tctx, previous_session_id, &tree)) {
507 torture_warning(tctx, "couldn't reconnect, bailing\n");
508 ret = false;
509 goto done;
512 ZERO_STRUCT(io2);
513 io2.in.fname = fname;
514 io2.in.durable_handle = h;
515 h = NULL;
517 status = smb2_create(tree, mem_ctx, &io2);
518 CHECK_STATUS(status, NT_STATUS_OK);
519 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
520 CHECK_VAL(io2.out.durable_open, true);
521 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
522 _h = io2.out.file.handle;
523 h = &_h;
525 done:
526 if (h != NULL) {
527 smb2_util_close(tree, *h);
530 smb2_util_unlink(tree, fname);
532 talloc_free(tree);
534 talloc_free(mem_ctx);
536 return ret;
541 * basic test for doing a durable open:
542 * tdis, new tcon, try durable reopen (fails)
544 bool test_durable_open_reopen3(struct torture_context *tctx,
545 struct smb2_tree *tree)
547 NTSTATUS status;
548 TALLOC_CTX *mem_ctx = talloc_new(tctx);
549 char fname[256];
550 struct smb2_handle _h;
551 struct smb2_handle *h = NULL;
552 struct smb2_create io1, io2;
553 bool ret = true;
554 struct smb2_tree *tree2;
556 /* Choose a random name in case the state is left a little funky. */
557 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
558 generate_random_str(tctx, 8));
560 smb2_util_unlink(tree, fname);
562 smb2_oplock_create_share(&io1, fname,
563 smb2_util_share_access(""),
564 smb2_util_oplock_level("b"));
565 io1.in.durable_open = true;
567 status = smb2_create(tree, mem_ctx, &io1);
568 CHECK_STATUS(status, NT_STATUS_OK);
569 _h = io1.out.file.handle;
570 h = &_h;
571 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
572 CHECK_VAL(io1.out.durable_open, true);
573 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
575 /* disconnect, reconnect and then do durable reopen */
576 status = smb2_tdis(tree);
577 CHECK_STATUS(status, NT_STATUS_OK);
579 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
580 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
581 ret = false;
582 goto done;
586 ZERO_STRUCT(io2);
587 io2.in.fname = fname;
588 io2.in.durable_handle = h;
590 status = smb2_create(tree2, mem_ctx, &io2);
591 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
593 done:
594 if (h != NULL) {
595 smb2_util_close(tree, *h);
598 smb2_util_unlink(tree2, fname);
600 talloc_free(tree);
602 talloc_free(mem_ctx);
604 return ret;
608 * basic test for doing a durable open:
609 * logoff, create a new session, do a durable reopen (succeeds)
611 bool test_durable_open_reopen4(struct torture_context *tctx,
612 struct smb2_tree *tree)
614 NTSTATUS status;
615 TALLOC_CTX *mem_ctx = talloc_new(tctx);
616 char fname[256];
617 struct smb2_handle _h;
618 struct smb2_handle *h = NULL;
619 struct smb2_create io1, io2;
620 bool ret = true;
621 struct smb2_transport *transport;
622 struct smb2_session *session2;
623 struct smb2_tree *tree2;
625 /* Choose a random name in case the state is left a little funky. */
626 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
627 generate_random_str(tctx, 8));
629 smb2_util_unlink(tree, fname);
631 smb2_oplock_create_share(&io1, fname,
632 smb2_util_share_access(""),
633 smb2_util_oplock_level("b"));
634 io1.in.durable_open = true;
635 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
637 status = smb2_create(tree, mem_ctx, &io1);
638 CHECK_STATUS(status, NT_STATUS_OK);
639 _h = io1.out.file.handle;
640 h = &_h;
641 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
642 CHECK_VAL(io1.out.durable_open, true);
643 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
646 * do a session logoff, establish a new session and tree
647 * connect on the same transport, and try a durable reopen
649 transport = tree->session->transport;
650 status = smb2_logoff(tree->session);
651 CHECK_STATUS(status, NT_STATUS_OK);
653 if (!torture_smb2_session_setup(tctx, transport,
654 0, /* previous_session_id */
655 mem_ctx, &session2))
657 torture_warning(tctx, "session setup failed.\n");
658 ret = false;
659 goto done;
663 * the session setup has talloc-stolen the transport,
664 * so we can safely free the old tree+session for clarity
666 TALLOC_FREE(tree);
668 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
669 torture_warning(tctx, "tree connect failed.\n");
670 ret = false;
671 goto done;
674 ZERO_STRUCT(io2);
675 io2.in.fname = fname;
676 io2.in.durable_handle = h;
677 h = NULL;
679 status = smb2_create(tree2, mem_ctx, &io2);
680 CHECK_STATUS(status, NT_STATUS_OK);
682 _h = io2.out.file.handle;
683 h = &_h;
684 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
685 CHECK_VAL(io2.out.durable_open, true);
686 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
688 done:
689 if (h != NULL) {
690 smb2_util_close(tree2, *h);
693 smb2_util_unlink(tree2, fname);
695 talloc_free(tree);
697 talloc_free(mem_ctx);
699 return ret;
703 basic testing of SMB2 durable opens
704 regarding the position information on the handle
706 bool test_durable_open_file_position(struct torture_context *tctx,
707 struct smb2_tree *tree1,
708 struct smb2_tree *tree2)
710 TALLOC_CTX *mem_ctx = talloc_new(tctx);
711 struct smb2_handle h1, h2;
712 struct smb2_create io1, io2;
713 NTSTATUS status;
714 const char *fname = "durable_open_position.dat";
715 union smb_fileinfo qfinfo;
716 union smb_setfileinfo sfinfo;
717 bool ret = true;
718 uint64_t pos;
720 smb2_util_unlink(tree1, fname);
722 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
723 io1.in.durable_open = true;
725 status = smb2_create(tree1, mem_ctx, &io1);
726 CHECK_STATUS(status, NT_STATUS_OK);
727 h1 = io1.out.file.handle;
728 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
729 CHECK_VAL(io1.out.durable_open, true);
730 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
732 /* TODO: check extra blob content */
734 ZERO_STRUCT(qfinfo);
735 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
736 qfinfo.generic.in.file.handle = h1;
737 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
738 CHECK_STATUS(status, NT_STATUS_OK);
739 CHECK_VAL(qfinfo.position_information.out.position, 0);
740 pos = qfinfo.position_information.out.position;
741 torture_comment(tctx, "position: %llu\n",
742 (unsigned long long)pos);
744 ZERO_STRUCT(sfinfo);
745 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
746 sfinfo.generic.in.file.handle = h1;
747 sfinfo.position_information.in.position = 0x1000;
748 status = smb2_setinfo_file(tree1, &sfinfo);
749 CHECK_STATUS(status, NT_STATUS_OK);
751 ZERO_STRUCT(qfinfo);
752 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
753 qfinfo.generic.in.file.handle = h1;
754 status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo);
755 CHECK_STATUS(status, NT_STATUS_OK);
756 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
757 pos = qfinfo.position_information.out.position;
758 torture_comment(tctx, "position: %llu\n",
759 (unsigned long long)pos);
761 talloc_free(tree1);
762 tree1 = NULL;
764 ZERO_STRUCT(qfinfo);
765 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
766 qfinfo.generic.in.file.handle = h1;
767 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
768 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
770 ZERO_STRUCT(io2);
771 io2.in.fname = fname;
772 io2.in.durable_handle = &h1;
774 status = smb2_create(tree2, mem_ctx, &io2);
775 CHECK_STATUS(status, NT_STATUS_OK);
776 CHECK_VAL(io2.out.durable_open, true);
777 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
778 CHECK_VAL(io2.out.reserved, 0x00);
779 CHECK_VAL(io2.out.create_action, NTCREATEX_ACTION_EXISTED);
780 CHECK_VAL(io2.out.alloc_size, 0);
781 CHECK_VAL(io2.out.size, 0);
782 CHECK_VAL(io2.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
783 CHECK_VAL(io2.out.reserved2, 0);
785 h2 = io2.out.file.handle;
787 ZERO_STRUCT(qfinfo);
788 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
789 qfinfo.generic.in.file.handle = h2;
790 status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo);
791 CHECK_STATUS(status, NT_STATUS_OK);
792 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
793 pos = qfinfo.position_information.out.position;
794 torture_comment(tctx, "position: %llu\n",
795 (unsigned long long)pos);
797 smb2_util_close(tree2, h2);
799 talloc_free(mem_ctx);
801 smb2_util_unlink(tree2, fname);
802 done:
803 talloc_free(tree1);
804 talloc_free(tree2);
806 return ret;
810 Open, disconnect, oplock break, reconnect.
812 bool test_durable_open_oplock(struct torture_context *tctx,
813 struct smb2_tree *tree1,
814 struct smb2_tree *tree2)
816 TALLOC_CTX *mem_ctx = talloc_new(tctx);
817 struct smb2_create io1, io2;
818 struct smb2_handle h1, h2;
819 NTSTATUS status;
820 char fname[256];
821 bool ret = true;
823 /* Choose a random name in case the state is left a little funky. */
824 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
826 /* Clean slate */
827 smb2_util_unlink(tree1, fname);
829 /* Create with batch oplock */
830 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
831 io1.in.durable_open = true;
833 io2 = io1;
834 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
836 status = smb2_create(tree1, mem_ctx, &io1);
837 CHECK_STATUS(status, NT_STATUS_OK);
838 h1 = io1.out.file.handle;
839 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
840 CHECK_VAL(io1.out.durable_open, true);
841 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
843 /* Disconnect after getting the batch */
844 talloc_free(tree1);
845 tree1 = NULL;
848 * Windows7 (build 7000) will break a batch oplock immediately if the
849 * original client is gone. (ZML: This seems like a bug. It should give
850 * some time for the client to reconnect!)
852 status = smb2_create(tree2, mem_ctx, &io2);
853 CHECK_STATUS(status, NT_STATUS_OK);
854 h2 = io2.out.file.handle;
855 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
856 CHECK_VAL(io2.out.durable_open, true);
857 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
859 /* What if tree1 tries to come back and reclaim? */
860 if (!torture_smb2_connection(tctx, &tree1)) {
861 torture_warning(tctx, "couldn't reconnect, bailing\n");
862 ret = false;
863 goto done;
866 ZERO_STRUCT(io1);
867 io1.in.fname = fname;
868 io1.in.durable_handle = &h1;
870 status = smb2_create(tree1, mem_ctx, &io1);
871 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
873 done:
874 smb2_util_close(tree2, h2);
875 smb2_util_unlink(tree2, fname);
877 talloc_free(tree1);
878 talloc_free(tree2);
880 return ret;
884 Open, disconnect, lease break, reconnect.
886 bool test_durable_open_lease(struct torture_context *tctx,
887 struct smb2_tree *tree1,
888 struct smb2_tree *tree2)
890 TALLOC_CTX *mem_ctx = talloc_new(tctx);
891 struct smb2_create io1, io2;
892 struct smb2_lease ls1, ls2;
893 struct smb2_handle h1, h2;
894 NTSTATUS status;
895 char fname[256];
896 bool ret = true;
897 uint64_t lease1, lease2;
898 uint32_t caps;
900 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
901 if (!(caps & SMB2_CAP_LEASING)) {
902 torture_skip(tctx, "leases are not supported");
906 * Choose a random name and random lease in case the state is left a
907 * little funky.
909 lease1 = random();
910 lease2 = random();
911 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
913 /* Clean slate */
914 smb2_util_unlink(tree1, fname);
916 /* Create with lease */
917 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
918 lease1, smb2_util_lease_state("RHW"));
919 io1.in.durable_open = true;
921 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
922 lease2, smb2_util_lease_state("RHW"));
923 io2.in.durable_open = true;
924 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
926 status = smb2_create(tree1, mem_ctx, &io1);
927 CHECK_STATUS(status, NT_STATUS_OK);
928 h1 = io1.out.file.handle;
929 CHECK_VAL(io1.out.durable_open, true);
930 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
932 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
933 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
934 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
935 CHECK_VAL(io1.out.lease_response.lease_state,
936 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
938 /* Disconnect after getting the lease */
939 talloc_free(tree1);
940 tree1 = NULL;
943 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
944 * even if the original client is gone. (ZML: This seems like a bug. It
945 * should give some time for the client to reconnect! And why RH?)
947 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
948 * Test is adapted accordingly.
950 status = smb2_create(tree2, mem_ctx, &io2);
951 CHECK_STATUS(status, NT_STATUS_OK);
952 h2 = io2.out.file.handle;
953 CHECK_VAL(io2.out.durable_open, true);
954 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
956 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
957 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
958 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
959 CHECK_VAL(io2.out.lease_response.lease_state,
960 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
962 /* What if tree1 tries to come back and reclaim? */
963 if (!torture_smb2_connection(tctx, &tree1)) {
964 torture_warning(tctx, "couldn't reconnect, bailing\n");
965 ret = false;
966 goto done;
969 ZERO_STRUCT(io1);
970 io1.in.fname = fname;
971 io1.in.durable_handle = &h1;
972 io1.in.lease_request = &ls1;
974 status = smb2_create(tree1, mem_ctx, &io1);
975 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
977 done:
978 smb2_util_close(tree2, h2);
979 smb2_util_unlink(tree2, fname);
981 talloc_free(tree1);
982 talloc_free(tree2);
984 return ret;
988 Open, take BRL, disconnect, reconnect.
990 bool test_durable_open_lock(struct torture_context *tctx,
991 struct smb2_tree *tree)
993 TALLOC_CTX *mem_ctx = talloc_new(tctx);
994 struct smb2_create io;
995 struct smb2_lease ls;
996 struct smb2_handle h;
997 struct smb2_lock lck;
998 struct smb2_lock_element el[2];
999 NTSTATUS status;
1000 char fname[256];
1001 bool ret = true;
1002 uint64_t lease;
1003 uint32_t caps;
1005 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1006 if (!(caps & SMB2_CAP_LEASING)) {
1007 torture_skip(tctx, "leases are not supported");
1011 * Choose a random name and random lease in case the state is left a
1012 * little funky.
1014 lease = random();
1015 snprintf(fname, 256, "durable_open_lock_%s.dat", generate_random_str(tctx, 8));
1017 /* Clean slate */
1018 smb2_util_unlink(tree, fname);
1020 /* Create with lease */
1022 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1023 smb2_util_lease_state("RWH"));
1024 io.in.durable_open = true;
1026 status = smb2_create(tree, mem_ctx, &io);
1027 CHECK_STATUS(status, NT_STATUS_OK);
1028 h = io.out.file.handle;
1029 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1031 CHECK_VAL(io.out.durable_open, true);
1032 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1033 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1034 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1035 CHECK_VAL(io.out.lease_response.lease_state,
1036 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1038 ZERO_STRUCT(lck);
1039 ZERO_STRUCT(el);
1040 lck.in.locks = el;
1041 lck.in.lock_count = 0x0001;
1042 lck.in.lock_sequence = 0x00000000;
1043 lck.in.file.handle = h;
1044 el[0].offset = 0;
1045 el[0].length = 1;
1046 el[0].reserved = 0x00000000;
1047 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1048 status = smb2_lock(tree, &lck);
1049 CHECK_STATUS(status, NT_STATUS_OK);
1051 /* Disconnect/Reconnect. */
1052 talloc_free(tree);
1053 tree = NULL;
1055 if (!torture_smb2_connection(tctx, &tree)) {
1056 torture_warning(tctx, "couldn't reconnect, bailing\n");
1057 ret = false;
1058 goto done;
1061 ZERO_STRUCT(io);
1062 io.in.fname = fname;
1063 io.in.durable_handle = &h;
1064 io.in.lease_request = &ls;
1066 status = smb2_create(tree, mem_ctx, &io);
1067 CHECK_STATUS(status, NT_STATUS_OK);
1068 h = io.out.file.handle;
1070 lck.in.file.handle = h;
1071 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1072 status = smb2_lock(tree, &lck);
1073 CHECK_STATUS(status, NT_STATUS_OK);
1075 done:
1076 smb2_util_close(tree, h);
1077 smb2_util_unlink(tree, fname);
1078 talloc_free(tree);
1080 return ret;
1084 * Open with a RH lease, disconnect, open in another tree, reconnect.
1086 * This test actually demonstrates a minimum level of respect for the durable
1087 * open in the face of another open. As long as this test shows an inability to
1088 * reconnect after an open, the oplock/lease tests above will certainly
1089 * demonstrate an error on reconnect.
1091 bool test_durable_open_open2_lease(struct torture_context *tctx,
1092 struct smb2_tree *tree1,
1093 struct smb2_tree *tree2)
1095 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1096 struct smb2_create io1, io2;
1097 struct smb2_lease ls;
1098 struct smb2_handle h1, h2;
1099 NTSTATUS status;
1100 char fname[256];
1101 bool ret = true;
1102 uint64_t lease;
1103 uint32_t caps;
1105 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1106 if (!(caps & SMB2_CAP_LEASING)) {
1107 torture_skip(tctx, "leases are not supported");
1111 * Choose a random name and random lease in case the state is left a
1112 * little funky.
1114 lease = random();
1115 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
1116 generate_random_str(tctx, 8));
1118 /* Clean slate */
1119 smb2_util_unlink(tree1, fname);
1121 /* Create with lease */
1122 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
1123 smb2_util_share_access(""),
1124 lease,
1125 smb2_util_lease_state("RH"));
1126 io1.in.durable_open = true;
1128 status = smb2_create(tree1, mem_ctx, &io1);
1129 CHECK_STATUS(status, NT_STATUS_OK);
1130 h1 = io1.out.file.handle;
1131 CHECK_VAL(io1.out.durable_open, true);
1132 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1134 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1135 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
1136 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
1137 CHECK_VAL(io1.out.lease_response.lease_state,
1138 smb2_util_lease_state("RH"));
1140 /* Disconnect */
1141 talloc_free(tree1);
1142 tree1 = NULL;
1144 /* Open the file in tree2 */
1145 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1147 status = smb2_create(tree2, mem_ctx, &io2);
1148 CHECK_STATUS(status, NT_STATUS_OK);
1149 h2 = io2.out.file.handle;
1150 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1152 /* Reconnect */
1153 if (!torture_smb2_connection(tctx, &tree1)) {
1154 torture_warning(tctx, "couldn't reconnect, bailing\n");
1155 ret = false;
1156 goto done;
1159 ZERO_STRUCT(io1);
1160 io1.in.fname = fname;
1161 io1.in.durable_handle = &h1;
1162 io1.in.lease_request = &ls;
1165 * Windows7 (build 7000) will give away an open immediately if the
1166 * original client is gone. (ZML: This seems like a bug. It should give
1167 * some time for the client to reconnect!)
1169 status = smb2_create(tree1, mem_ctx, &io1);
1170 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1171 h1 = io1.out.file.handle;
1173 done:
1174 smb2_util_close(tree2, h2);
1175 smb2_util_unlink(tree2, fname);
1176 smb2_util_close(tree1, h1);
1177 smb2_util_unlink(tree1, fname);
1179 talloc_free(tree1);
1180 talloc_free(tree2);
1182 return ret;
1186 * Open with a batch oplock, disconnect, open in another tree, reconnect.
1188 * This test actually demonstrates a minimum level of respect for the durable
1189 * open in the face of another open. As long as this test shows an inability to
1190 * reconnect after an open, the oplock/lease tests above will certainly
1191 * demonstrate an error on reconnect.
1193 bool test_durable_open_open2_oplock(struct torture_context *tctx,
1194 struct smb2_tree *tree1,
1195 struct smb2_tree *tree2)
1197 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1198 struct smb2_create io1, io2;
1199 struct smb2_handle h1, h2;
1200 NTSTATUS status;
1201 char fname[256];
1202 bool ret = true;
1205 * Choose a random name and random lease in case the state is left a
1206 * little funky.
1208 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
1209 generate_random_str(tctx, 8));
1211 /* Clean slate */
1212 smb2_util_unlink(tree1, fname);
1214 /* Create with batch oplock */
1215 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1216 io1.in.durable_open = true;
1218 status = smb2_create(tree1, mem_ctx, &io1);
1219 CHECK_STATUS(status, NT_STATUS_OK);
1220 h1 = io1.out.file.handle;
1221 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1222 CHECK_VAL(io1.out.durable_open, true);
1223 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1225 /* Disconnect */
1226 talloc_free(tree1);
1227 tree1 = NULL;
1229 /* Open the file in tree2 */
1230 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
1232 status = smb2_create(tree2, mem_ctx, &io2);
1233 CHECK_STATUS(status, NT_STATUS_OK);
1234 h2 = io2.out.file.handle;
1235 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1237 /* Reconnect */
1238 if (!torture_smb2_connection(tctx, &tree1)) {
1239 torture_warning(tctx, "couldn't reconnect, bailing\n");
1240 ret = false;
1241 goto done;
1244 ZERO_STRUCT(io1);
1245 io1.in.fname = fname;
1246 io1.in.durable_handle = &h1;
1248 status = smb2_create(tree1, mem_ctx, &io1);
1249 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1250 h1 = io1.out.file.handle;
1252 done:
1253 smb2_util_close(tree2, h2);
1254 smb2_util_unlink(tree2, fname);
1255 smb2_util_close(tree1, h1);
1256 smb2_util_unlink(tree1, fname);
1258 talloc_free(tree1);
1259 talloc_free(tree2);
1261 return ret;
1264 struct torture_suite *torture_smb2_durable_open_init(void)
1266 struct torture_suite *suite =
1267 torture_suite_create(talloc_autofree_context(), "durable-open");
1269 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
1270 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
1271 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
1272 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
1273 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
1274 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
1275 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
1276 torture_suite_add_2smb2_test(suite, "file-position",
1277 test_durable_open_file_position);
1278 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
1279 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
1280 torture_suite_add_1smb2_test(suite, "lock", test_durable_open_lock);
1281 torture_suite_add_2smb2_test(suite, "open2-lease",
1282 test_durable_open_open2_lease);
1283 torture_suite_add_2smb2_test(suite, "open2-oplock",
1284 test_durable_open_open2_oplock);
1286 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
1288 return suite;