Avoid segfault in durable_open tests
[Samba.git] / source4 / torture / smb2 / durable_open.c
blobbb32f96e9ec979f6e8d4cbb4f9e29cece399baf4
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%llx - should be 0x%llx\n", \
34 __location__, #v, (unsigned long long)v, (unsigned long long)correct); \
35 ret = false; \
36 }} while (0)
38 #define CHECK_NOT_VAL(v, incorrect) do { \
39 if ((v) == (incorrect)) { \
40 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%llx - should not be 0x%llx\n", \
41 __location__, #v, (unsigned long long)v, (unsigned long long)incorrect); \
42 ret = false; \
43 }} while (0)
45 #define CHECK_NOT_NULL(p) do { \
46 if ((p) == NULL) { \
47 torture_result(tctx, TORTURE_FAIL, "(%s): %s is NULL but it should not be.\n", \
48 __location__, #p); \
49 ret = false; \
50 }} while (0)
52 #define CHECK_STATUS(status, correct) do { \
53 if (!NT_STATUS_EQUAL(status, correct)) { \
54 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
55 nt_errstr(status), nt_errstr(correct)); \
56 ret = false; \
57 goto done; \
58 }} while (0)
60 #define CHECK_CREATED(__io, __created, __attribute) \
61 do { \
62 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
63 CHECK_VAL((__io)->out.alloc_size, 0); \
64 CHECK_VAL((__io)->out.size, 0); \
65 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
66 CHECK_VAL((__io)->out.reserved2, 0); \
67 } while(0)
69 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
70 do { \
71 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
72 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
73 CHECK_VAL((__io)->out.size, (__size)); \
74 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
75 CHECK_VAL((__io)->out.reserved2, 0); \
76 } while(0)
80 /**
81 * basic durable_open test.
82 * durable state should only be granted when requested
83 * along with a batch oplock or a handle lease.
85 * This test tests durable open with all possible oplock types.
88 struct durable_open_vs_oplock {
89 const char *level;
90 const char *share_mode;
91 bool expected;
94 #define NUM_OPLOCK_TYPES 4
95 #define NUM_SHARE_MODES 8
96 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
97 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
99 { "", "", false },
100 { "", "R", false },
101 { "", "W", false },
102 { "", "D", false },
103 { "", "RD", false },
104 { "", "RW", false },
105 { "", "WD", false },
106 { "", "RWD", false },
108 { "s", "", false },
109 { "s", "R", false },
110 { "s", "W", false },
111 { "s", "D", false },
112 { "s", "RD", false },
113 { "s", "RW", false },
114 { "s", "WD", false },
115 { "s", "RWD", false },
117 { "x", "", false },
118 { "x", "R", false },
119 { "x", "W", false },
120 { "x", "D", false },
121 { "x", "RD", false },
122 { "x", "RW", false },
123 { "x", "WD", false },
124 { "x", "RWD", false },
126 { "b", "", true },
127 { "b", "R", true },
128 { "b", "W", true },
129 { "b", "D", true },
130 { "b", "RD", true },
131 { "b", "RW", true },
132 { "b", "WD", true },
133 { "b", "RWD", true },
136 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
137 struct smb2_tree *tree,
138 const char *fname,
139 struct durable_open_vs_oplock test)
141 NTSTATUS status;
142 TALLOC_CTX *mem_ctx = talloc_new(tctx);
143 struct smb2_handle _h;
144 struct smb2_handle *h = NULL;
145 bool ret = true;
146 struct smb2_create io;
148 smb2_util_unlink(tree, fname);
150 smb2_oplock_create_share(&io, fname,
151 smb2_util_share_access(test.share_mode),
152 smb2_util_oplock_level(test.level));
153 io.in.durable_open = true;
155 status = smb2_create(tree, mem_ctx, &io);
156 CHECK_STATUS(status, NT_STATUS_OK);
157 _h = io.out.file.handle;
158 h = &_h;
159 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
160 CHECK_VAL(io.out.durable_open, test.expected);
161 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
163 done:
164 if (h != NULL) {
165 smb2_util_close(tree, *h);
167 smb2_util_unlink(tree, fname);
168 talloc_free(mem_ctx);
170 return ret;
173 static bool test_durable_open_open_oplock(struct torture_context *tctx,
174 struct smb2_tree *tree)
176 TALLOC_CTX *mem_ctx = talloc_new(tctx);
177 char fname[256];
178 bool ret = true;
179 int i;
181 /* Choose a random name in case the state is left a little funky. */
182 snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
184 smb2_util_unlink(tree, fname);
186 /* test various oplock levels with durable open */
188 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
189 ret = test_one_durable_open_open_oplock(tctx,
190 tree,
191 fname,
192 durable_open_vs_oplock_table[i]);
193 if (ret == false) {
194 goto done;
198 done:
199 smb2_util_unlink(tree, fname);
200 talloc_free(tree);
201 talloc_free(mem_ctx);
203 return ret;
207 * basic durable_open test.
208 * durable state should only be granted when requested
209 * along with a batch oplock or a handle lease.
211 * This test tests durable open with all valid lease types.
214 struct durable_open_vs_lease {
215 const char *type;
216 const char *share_mode;
217 bool expected;
220 #define NUM_LEASE_TYPES 5
221 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
222 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
224 { "", "", false },
225 { "", "R", false },
226 { "", "W", false },
227 { "", "D", false },
228 { "", "RW", false },
229 { "", "RD", false },
230 { "", "WD", false },
231 { "", "RWD", false },
233 { "R", "", false },
234 { "R", "R", false },
235 { "R", "W", false },
236 { "R", "D", false },
237 { "R", "RW", false },
238 { "R", "RD", false },
239 { "R", "DW", false },
240 { "R", "RWD", false },
242 { "RW", "", false },
243 { "RW", "R", false },
244 { "RW", "W", false },
245 { "RW", "D", false },
246 { "RW", "RW", false },
247 { "RW", "RD", false },
248 { "RW", "WD", false },
249 { "RW", "RWD", false },
251 { "RH", "", true },
252 { "RH", "R", true },
253 { "RH", "W", true },
254 { "RH", "D", true },
255 { "RH", "RW", true },
256 { "RH", "RD", true },
257 { "RH", "WD", true },
258 { "RH", "RWD", true },
260 { "RHW", "", true },
261 { "RHW", "R", true },
262 { "RHW", "W", true },
263 { "RHW", "D", true },
264 { "RHW", "RW", true },
265 { "RHW", "RD", true },
266 { "RHW", "WD", true },
267 { "RHW", "RWD", true },
270 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
271 struct smb2_tree *tree,
272 const char *fname,
273 struct durable_open_vs_lease test)
275 NTSTATUS status;
276 TALLOC_CTX *mem_ctx = talloc_new(tctx);
277 struct smb2_handle _h;
278 struct smb2_handle *h = NULL;
279 bool ret = true;
280 struct smb2_create io;
281 struct smb2_lease ls;
282 uint64_t lease;
283 uint32_t caps;
285 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
286 if (!(caps & SMB2_CAP_LEASING)) {
287 torture_skip(tctx, "leases are not supported");
290 smb2_util_unlink(tree, fname);
292 lease = random();
294 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
295 smb2_util_share_access(test.share_mode),
296 lease,
297 smb2_util_lease_state(test.type));
298 io.in.durable_open = true;
300 status = smb2_create(tree, mem_ctx, &io);
301 CHECK_STATUS(status, NT_STATUS_OK);
302 _h = io.out.file.handle;
303 h = &_h;
304 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
305 CHECK_VAL(io.out.durable_open, test.expected);
306 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
307 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
308 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
309 CHECK_VAL(io.out.lease_response.lease_state,
310 smb2_util_lease_state(test.type));
311 done:
312 if (h != NULL) {
313 smb2_util_close(tree, *h);
315 smb2_util_unlink(tree, fname);
316 talloc_free(mem_ctx);
318 return ret;
321 static bool test_durable_open_open_lease(struct torture_context *tctx,
322 struct smb2_tree *tree)
324 TALLOC_CTX *mem_ctx = talloc_new(tctx);
325 char fname[256];
326 bool ret = true;
327 int i;
328 uint32_t caps;
330 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
331 if (!(caps & SMB2_CAP_LEASING)) {
332 torture_skip(tctx, "leases are not supported");
335 /* Choose a random name in case the state is left a little funky. */
336 snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
338 smb2_util_unlink(tree, fname);
341 /* test various oplock levels with durable open */
343 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
344 ret = test_one_durable_open_open_lease(tctx,
345 tree,
346 fname,
347 durable_open_vs_lease_table[i]);
348 if (ret == false) {
349 goto done;
353 done:
354 smb2_util_unlink(tree, fname);
355 talloc_free(tree);
356 talloc_free(mem_ctx);
358 return ret;
362 * basic test for doing a durable open
363 * and do a durable reopen on the same connection
364 * while the first open is still active (fails)
366 static bool test_durable_open_reopen1(struct torture_context *tctx,
367 struct smb2_tree *tree)
369 NTSTATUS status;
370 TALLOC_CTX *mem_ctx = talloc_new(tctx);
371 char fname[256];
372 struct smb2_handle _h;
373 struct smb2_handle *h = NULL;
374 struct smb2_create io1, io2;
375 bool ret = true;
377 /* Choose a random name in case the state is left a little funky. */
378 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
379 generate_random_str(tctx, 8));
381 smb2_util_unlink(tree, fname);
383 smb2_oplock_create_share(&io1, fname,
384 smb2_util_share_access(""),
385 smb2_util_oplock_level("b"));
386 io1.in.durable_open = true;
388 status = smb2_create(tree, mem_ctx, &io1);
389 CHECK_STATUS(status, NT_STATUS_OK);
390 _h = io1.out.file.handle;
391 h = &_h;
392 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
393 CHECK_VAL(io1.out.durable_open, true);
394 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
396 /* try a durable reconnect while the file is still open */
397 ZERO_STRUCT(io2);
398 io2.in.fname = fname;
399 io2.in.durable_handle = h;
401 status = smb2_create(tree, mem_ctx, &io2);
402 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
404 done:
405 if (h != NULL) {
406 smb2_util_close(tree, *h);
409 smb2_util_unlink(tree, fname);
411 talloc_free(tree);
413 talloc_free(mem_ctx);
415 return ret;
419 * Basic test for doing a durable open
420 * and do a session reconnect while the first
421 * session is still active and the handle is
422 * still open in the client.
423 * This closes the original session and a
424 * durable reconnect on the new session succeeds.
426 static bool test_durable_open_reopen1a(struct torture_context *tctx,
427 struct smb2_tree *tree)
429 NTSTATUS status;
430 TALLOC_CTX *mem_ctx = talloc_new(tctx);
431 char fname[256];
432 struct smb2_handle _h;
433 struct smb2_handle *h = NULL;
434 struct smb2_create io1, io2;
435 bool ret = true;
436 struct smb2_tree *tree2 = NULL;
437 uint64_t previous_session_id;
438 struct smbcli_options options;
440 options = tree->session->transport->options;
442 /* Choose a random name in case the state is left a little funky. */
443 snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
444 generate_random_str(tctx, 8));
446 smb2_util_unlink(tree, fname);
448 smb2_oplock_create_share(&io1, fname,
449 smb2_util_share_access(""),
450 smb2_util_oplock_level("b"));
451 io1.in.durable_open = true;
453 status = smb2_create(tree, mem_ctx, &io1);
454 CHECK_STATUS(status, NT_STATUS_OK);
455 _h = io1.out.file.handle;
456 h = &_h;
457 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
458 CHECK_VAL(io1.out.durable_open, true);
459 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
462 * a session reconnect on a second tcp connection
465 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
467 if (!torture_smb2_connection_ext(tctx, previous_session_id,
468 &options, &tree2))
470 torture_warning(tctx, "couldn't reconnect, bailing\n");
471 ret = false;
472 goto done;
476 * check that this has deleted the old session
479 ZERO_STRUCT(io2);
480 io2.in.fname = fname;
481 io2.in.durable_handle = h;
483 status = smb2_create(tree, mem_ctx, &io2);
484 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
487 * but a durable reconnect on the new session succeeds:
490 ZERO_STRUCT(io2);
491 io2.in.fname = fname;
492 io2.in.durable_handle = h;
494 status = smb2_create(tree2, mem_ctx, &io2);
495 CHECK_STATUS(status, NT_STATUS_OK);
496 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
497 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
498 _h = io2.out.file.handle;
499 h = &_h;
501 done:
502 if (h != NULL) {
503 smb2_util_close(tree2, *h);
506 smb2_util_unlink(tree2, fname);
508 talloc_free(tree2);
510 talloc_free(tree);
512 talloc_free(mem_ctx);
514 return ret;
518 * basic test for doing a durable open
519 * tcp disconnect, reconnect, do a durable reopen (succeeds)
521 static bool test_durable_open_reopen2(struct torture_context *tctx,
522 struct smb2_tree *tree)
524 NTSTATUS status;
525 TALLOC_CTX *mem_ctx = talloc_new(tctx);
526 char fname[256];
527 struct smb2_handle _h;
528 struct smb2_handle *h = NULL;
529 struct smb2_create io;
530 bool ret = true;
532 /* Choose a random name in case the state is left a little funky. */
533 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
534 generate_random_str(tctx, 8));
536 smb2_util_unlink(tree, fname);
538 smb2_oplock_create_share(&io, fname,
539 smb2_util_share_access(""),
540 smb2_util_oplock_level("b"));
541 io.in.durable_open = true;
543 status = smb2_create(tree, mem_ctx, &io);
544 CHECK_STATUS(status, NT_STATUS_OK);
545 _h = io.out.file.handle;
546 h = &_h;
547 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
548 CHECK_VAL(io.out.durable_open, true);
549 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
551 /* disconnect, leaving the durable in place */
552 TALLOC_FREE(tree);
554 if (!torture_smb2_connection(tctx, &tree)) {
555 torture_warning(tctx, "couldn't reconnect, bailing\n");
556 ret = false;
557 goto done;
560 ZERO_STRUCT(io);
561 /* the path name is ignored by the server */
562 io.in.fname = fname;
563 io.in.durable_handle = h; /* durable v1 reconnect request */
564 h = NULL;
566 status = smb2_create(tree, mem_ctx, &io);
567 CHECK_STATUS(status, NT_STATUS_OK);
568 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
569 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
570 _h = io.out.file.handle;
571 h = &_h;
573 /* disconnect again, leaving the durable in place */
574 TALLOC_FREE(tree);
576 if (!torture_smb2_connection(tctx, &tree)) {
577 torture_warning(tctx, "couldn't reconnect, bailing\n");
578 ret = false;
579 goto done;
583 * show that the filename and many other fields
584 * are ignored. only the reconnect request blob
585 * is important.
587 ZERO_STRUCT(io);
588 /* the path name is ignored by the server */
589 io.in.security_flags = 0x78;
590 io.in.oplock_level = 0x78;
591 io.in.impersonation_level = 0x12345678;
592 io.in.create_flags = 0x12345678;
593 io.in.reserved = 0x12345678;
594 io.in.desired_access = 0x12345678;
595 io.in.file_attributes = 0x12345678;
596 io.in.share_access = 0x12345678;
597 io.in.create_disposition = 0x12345678;
598 io.in.create_options = 0x12345678;
599 io.in.fname = "__non_existing_fname__";
600 io.in.durable_handle = h; /* durable v1 reconnect request */
601 h = NULL;
603 status = smb2_create(tree, mem_ctx, &io);
604 CHECK_STATUS(status, NT_STATUS_OK);
605 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
606 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
607 _h = io.out.file.handle;
608 h = &_h;
610 /* disconnect, leaving the durable in place */
611 TALLOC_FREE(tree);
613 if (!torture_smb2_connection(tctx, &tree)) {
614 torture_warning(tctx, "couldn't reconnect, bailing\n");
615 ret = false;
616 goto done;
620 * show that an additionally specified durable v1 request
621 * is ignored by the server.
622 * See MS-SMB2, 3.3.5.9.7
623 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
625 ZERO_STRUCT(io);
626 /* the path name is ignored by the server */
627 io.in.fname = fname;
628 io.in.durable_handle = h; /* durable v1 reconnect request */
629 io.in.durable_open = true; /* durable v1 handle request */
630 h = NULL;
632 status = smb2_create(tree, mem_ctx, &io);
633 CHECK_STATUS(status, NT_STATUS_OK);
634 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
635 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
636 _h = io.out.file.handle;
637 h = &_h;
639 done:
640 if (tree != NULL) {
641 if (h != NULL) {
642 smb2_util_close(tree, *h);
645 smb2_util_unlink(tree, fname);
647 talloc_free(tree);
650 talloc_free(mem_ctx);
652 return ret;
656 * lease variant of reopen2
657 * basic test for doing a durable open
658 * tcp disconnect, reconnect, do a durable reopen (succeeds)
660 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
661 struct smb2_tree *tree)
663 NTSTATUS status;
664 TALLOC_CTX *mem_ctx = talloc_new(tctx);
665 char fname[256];
666 struct smb2_handle _h;
667 struct smb2_handle *h = NULL;
668 struct smb2_create io;
669 struct smb2_lease ls;
670 uint64_t lease_key;
671 bool ret = true;
672 struct smbcli_options options;
673 uint32_t caps;
675 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
676 if (!(caps & SMB2_CAP_LEASING)) {
677 torture_skip(tctx, "leases are not supported");
680 options = tree->session->transport->options;
682 /* Choose a random name in case the state is left a little funky. */
683 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
684 generate_random_str(tctx, 8));
686 smb2_util_unlink(tree, fname);
688 lease_key = random();
689 smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
690 smb2_util_lease_state("RWH"));
691 io.in.durable_open = true;
693 status = smb2_create(tree, mem_ctx, &io);
694 CHECK_STATUS(status, NT_STATUS_OK);
695 _h = io.out.file.handle;
696 h = &_h;
697 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
699 CHECK_VAL(io.out.durable_open, true);
700 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
701 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
702 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
703 CHECK_VAL(io.out.lease_response.lease_state,
704 smb2_util_lease_state("RWH"));
705 CHECK_VAL(io.out.lease_response.lease_flags, 0);
706 CHECK_VAL(io.out.lease_response.lease_duration, 0);
708 /* disconnect, reconnect and then do durable reopen */
709 TALLOC_FREE(tree);
711 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
712 torture_warning(tctx, "couldn't reconnect, bailing\n");
713 ret = false;
714 goto done;
718 /* a few failure tests: */
721 * several attempts without lease attached:
722 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
723 * irrespective of file name provided
726 ZERO_STRUCT(io);
727 io.in.fname = "";
728 io.in.durable_handle = h;
729 status = smb2_create(tree, mem_ctx, &io);
730 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
732 ZERO_STRUCT(io);
733 io.in.fname = "__non_existing_fname__";
734 io.in.durable_handle = h;
735 status = smb2_create(tree, mem_ctx, &io);
736 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
738 ZERO_STRUCT(io);
739 io.in.fname = fname;
740 io.in.durable_handle = h;
741 status = smb2_create(tree, mem_ctx, &io);
742 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
745 * attempt with lease provided, but
746 * with a changed lease key. => fails
748 ZERO_STRUCT(io);
749 io.in.fname = fname;
750 io.in.durable_open = false;
751 io.in.durable_handle = h;
752 io.in.lease_request = &ls;
753 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
754 /* a wrong lease key lets the request fail */
755 ls.lease_key.data[0]++;
757 status = smb2_create(tree, mem_ctx, &io);
758 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
760 /* restore the correct lease key */
761 ls.lease_key.data[0]--;
764 * this last failing attempt is almost correct:
765 * only problem is: we use the wrong filename...
766 * Note that this gives INVALID_PARAMETER.
767 * This is different from oplocks!
769 ZERO_STRUCT(io);
770 io.in.fname = "__non_existing_fname__";
771 io.in.durable_open = false;
772 io.in.durable_handle = h;
773 io.in.lease_request = &ls;
774 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
776 status = smb2_create(tree, mem_ctx, &io);
777 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
780 * Now for a succeeding reconnect:
783 ZERO_STRUCT(io);
784 io.in.fname = fname;
785 io.in.durable_open = false;
786 io.in.durable_handle = h;
787 io.in.lease_request = &ls;
788 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
790 /* the requested lease state is irrelevant */
791 ls.lease_state = smb2_util_lease_state("");
793 h = NULL;
795 status = smb2_create(tree, mem_ctx, &io);
796 CHECK_STATUS(status, NT_STATUS_OK);
798 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
799 CHECK_VAL(io.out.durable_open, false);
800 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
801 CHECK_VAL(io.out.persistent_open, false);
802 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
803 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
804 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
805 CHECK_VAL(io.out.lease_response.lease_state,
806 smb2_util_lease_state("RWH"));
807 CHECK_VAL(io.out.lease_response.lease_flags, 0);
808 CHECK_VAL(io.out.lease_response.lease_duration, 0);
809 _h = io.out.file.handle;
810 h = &_h;
812 /* disconnect one more time */
813 TALLOC_FREE(tree);
815 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
816 torture_warning(tctx, "couldn't reconnect, bailing\n");
817 ret = false;
818 goto done;
822 * demonstrate that various parameters are ignored
823 * in the reconnect
826 ZERO_STRUCT(io);
828 * These are completely ignored by the server
830 io.in.security_flags = 0x78;
831 io.in.oplock_level = 0x78;
832 io.in.impersonation_level = 0x12345678;
833 io.in.create_flags = 0x12345678;
834 io.in.reserved = 0x12345678;
835 io.in.desired_access = 0x12345678;
836 io.in.file_attributes = 0x12345678;
837 io.in.share_access = 0x12345678;
838 io.in.create_disposition = 0x12345678;
839 io.in.create_options = 0x12345678;
842 * only these are checked:
843 * - io.in.fname
844 * - io.in.durable_handle,
845 * - io.in.lease_request->lease_key
848 io.in.fname = fname;
849 io.in.durable_open_v2 = false;
850 io.in.durable_handle_v2 = h;
851 io.in.lease_request = &ls;
853 /* the requested lease state is irrelevant */
854 ls.lease_state = smb2_util_lease_state("");
856 h = NULL;
858 status = smb2_create(tree, mem_ctx, &io);
859 CHECK_STATUS(status, NT_STATUS_OK);
861 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
862 CHECK_VAL(io.out.durable_open, false);
863 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
864 CHECK_VAL(io.out.persistent_open, false);
865 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
866 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
867 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
868 CHECK_VAL(io.out.lease_response.lease_state,
869 smb2_util_lease_state("RWH"));
870 CHECK_VAL(io.out.lease_response.lease_flags, 0);
871 CHECK_VAL(io.out.lease_response.lease_duration, 0);
873 _h = io.out.file.handle;
874 h = &_h;
876 done:
877 if (tree != NULL) {
878 if (h != NULL) {
879 smb2_util_close(tree, *h);
882 smb2_util_unlink(tree, fname);
884 talloc_free(tree);
887 talloc_free(mem_ctx);
889 return ret;
893 * lease v2 variant of reopen2
894 * basic test for doing a durable open
895 * tcp disconnect, reconnect, do a durable reopen (succeeds)
897 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
898 struct smb2_tree *tree)
900 NTSTATUS status;
901 TALLOC_CTX *mem_ctx = talloc_new(tctx);
902 char fname[256];
903 struct smb2_handle _h;
904 struct smb2_handle *h = NULL;
905 struct smb2_create io;
906 struct smb2_lease ls;
907 uint64_t lease_key;
908 bool ret = true;
909 struct smbcli_options options;
910 uint32_t caps;
912 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
913 if (!(caps & SMB2_CAP_LEASING)) {
914 torture_skip(tctx, "leases are not supported");
917 options = tree->session->transport->options;
919 /* Choose a random name in case the state is left a little funky. */
920 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
921 generate_random_str(tctx, 8));
923 smb2_util_unlink(tree, fname);
925 lease_key = random();
926 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
927 lease_key, 0, /* parent lease key */
928 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
929 io.in.durable_open = true;
931 status = smb2_create(tree, mem_ctx, &io);
932 CHECK_STATUS(status, NT_STATUS_OK);
933 _h = io.out.file.handle;
934 h = &_h;
935 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
937 CHECK_VAL(io.out.durable_open, true);
938 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
939 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
940 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
941 CHECK_VAL(io.out.lease_response_v2.lease_state,
942 smb2_util_lease_state("RWH"));
943 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
944 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
946 /* disconnect, reconnect and then do durable reopen */
947 TALLOC_FREE(tree);
949 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
950 torture_warning(tctx, "couldn't reconnect, bailing\n");
951 ret = false;
952 goto done;
955 /* a few failure tests: */
958 * several attempts without lease attached:
959 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
960 * irrespective of file name provided
963 ZERO_STRUCT(io);
964 io.in.fname = "";
965 io.in.durable_handle = h;
966 status = smb2_create(tree, mem_ctx, &io);
967 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
969 ZERO_STRUCT(io);
970 io.in.fname = "__non_existing_fname__";
971 io.in.durable_handle = h;
972 status = smb2_create(tree, mem_ctx, &io);
973 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
975 ZERO_STRUCT(io);
976 io.in.fname = fname;
977 io.in.durable_handle = h;
978 status = smb2_create(tree, mem_ctx, &io);
979 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
982 * attempt with lease provided, but
983 * with a changed lease key. => fails
985 ZERO_STRUCT(io);
986 io.in.fname = fname;
987 io.in.durable_open = false;
988 io.in.durable_handle = h;
989 io.in.lease_request_v2 = &ls;
990 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
991 /* a wrong lease key lets the request fail */
992 ls.lease_key.data[0]++;
994 status = smb2_create(tree, mem_ctx, &io);
995 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
997 /* restore the correct lease key */
998 ls.lease_key.data[0]--;
1001 * this last failing attempt is almost correct:
1002 * only problem is: we use the wrong filename...
1003 * Note that this gives INVALID_PARAMETER.
1004 * This is different from oplocks!
1006 ZERO_STRUCT(io);
1007 io.in.fname = "__non_existing_fname__";
1008 io.in.durable_open = false;
1009 io.in.durable_handle = h;
1010 io.in.lease_request_v2 = &ls;
1011 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1013 status = smb2_create(tree, mem_ctx, &io);
1014 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1017 * Now for a succeeding reconnect:
1020 ZERO_STRUCT(io);
1021 io.in.fname = fname;
1022 io.in.durable_open = false;
1023 io.in.durable_handle = h;
1024 io.in.lease_request_v2 = &ls;
1025 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1027 /* the requested lease state is irrelevant */
1028 ls.lease_state = smb2_util_lease_state("");
1030 h = NULL;
1032 status = smb2_create(tree, mem_ctx, &io);
1033 CHECK_STATUS(status, NT_STATUS_OK);
1035 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1036 CHECK_VAL(io.out.durable_open, false);
1037 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1038 CHECK_VAL(io.out.persistent_open, false);
1039 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1040 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1041 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1042 CHECK_VAL(io.out.lease_response_v2.lease_state,
1043 smb2_util_lease_state("RWH"));
1044 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1045 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1046 _h = io.out.file.handle;
1047 h = &_h;
1049 /* disconnect one more time */
1050 TALLOC_FREE(tree);
1052 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1053 torture_warning(tctx, "couldn't reconnect, bailing\n");
1054 ret = false;
1055 goto done;
1059 * demonstrate that various parameters are ignored
1060 * in the reconnect
1063 ZERO_STRUCT(io);
1065 * These are completely ignored by the server
1067 io.in.security_flags = 0x78;
1068 io.in.oplock_level = 0x78;
1069 io.in.impersonation_level = 0x12345678;
1070 io.in.create_flags = 0x12345678;
1071 io.in.reserved = 0x12345678;
1072 io.in.desired_access = 0x12345678;
1073 io.in.file_attributes = 0x12345678;
1074 io.in.share_access = 0x12345678;
1075 io.in.create_disposition = 0x12345678;
1076 io.in.create_options = 0x12345678;
1079 * only these are checked:
1080 * - io.in.fname
1081 * - io.in.durable_handle,
1082 * - io.in.lease_request->lease_key
1085 io.in.fname = fname;
1086 io.in.durable_open_v2 = false;
1087 io.in.durable_handle_v2 = h;
1088 io.in.lease_request_v2 = &ls;
1090 /* the requested lease state is irrelevant */
1091 ls.lease_state = smb2_util_lease_state("");
1093 h = NULL;
1095 status = smb2_create(tree, mem_ctx, &io);
1096 CHECK_STATUS(status, NT_STATUS_OK);
1098 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1099 CHECK_VAL(io.out.durable_open, false);
1100 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1101 CHECK_VAL(io.out.persistent_open, false);
1102 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1103 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1104 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1105 CHECK_VAL(io.out.lease_response_v2.lease_state,
1106 smb2_util_lease_state("RWH"));
1107 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1108 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1110 _h = io.out.file.handle;
1111 h = &_h;
1113 done:
1114 if (tree != NULL) {
1115 if (h != NULL) {
1116 smb2_util_close(tree, *h);
1119 smb2_util_unlink(tree, fname);
1121 talloc_free(tree);
1124 talloc_free(mem_ctx);
1126 return ret;
1130 * basic test for doing a durable open
1131 * tcp disconnect, reconnect with a session reconnect and
1132 * do a durable reopen (succeeds)
1134 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1135 struct smb2_tree *tree)
1137 NTSTATUS status;
1138 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1139 char fname[256];
1140 struct smb2_handle _h;
1141 struct smb2_handle *h = NULL;
1142 struct smb2_create io1, io2;
1143 uint64_t previous_session_id;
1144 bool ret = true;
1145 struct smbcli_options options;
1147 options = tree->session->transport->options;
1149 /* Choose a random name in case the state is left a little funky. */
1150 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1151 generate_random_str(tctx, 8));
1153 smb2_util_unlink(tree, fname);
1155 smb2_oplock_create_share(&io1, fname,
1156 smb2_util_share_access(""),
1157 smb2_util_oplock_level("b"));
1158 io1.in.durable_open = true;
1160 status = smb2_create(tree, mem_ctx, &io1);
1161 CHECK_STATUS(status, NT_STATUS_OK);
1162 _h = io1.out.file.handle;
1163 h = &_h;
1164 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1165 CHECK_VAL(io1.out.durable_open, true);
1166 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1168 /* disconnect, reconnect and then do durable reopen */
1169 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1170 talloc_free(tree);
1171 tree = NULL;
1173 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1174 &options, &tree))
1176 torture_warning(tctx, "couldn't reconnect, bailing\n");
1177 ret = false;
1178 goto done;
1181 ZERO_STRUCT(io2);
1182 io2.in.fname = fname;
1183 io2.in.durable_handle = h;
1184 h = NULL;
1186 status = smb2_create(tree, mem_ctx, &io2);
1187 CHECK_STATUS(status, NT_STATUS_OK);
1188 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1189 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1190 _h = io2.out.file.handle;
1191 h = &_h;
1193 done:
1194 if (tree != NULL) {
1195 if (h != NULL) {
1196 smb2_util_close(tree, *h);
1199 smb2_util_unlink(tree, fname);
1201 talloc_free(tree);
1204 talloc_free(mem_ctx);
1206 return ret;
1211 * basic test for doing a durable open:
1212 * tdis, new tcon, try durable reopen (fails)
1214 static bool test_durable_open_reopen3(struct torture_context *tctx,
1215 struct smb2_tree *tree)
1217 NTSTATUS status;
1218 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1219 char fname[256];
1220 struct smb2_handle _h;
1221 struct smb2_handle *h = NULL;
1222 struct smb2_create io1, io2;
1223 bool ret = true;
1224 struct smb2_tree *tree2;
1226 /* Choose a random name in case the state is left a little funky. */
1227 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1228 generate_random_str(tctx, 8));
1230 smb2_util_unlink(tree, fname);
1232 smb2_oplock_create_share(&io1, fname,
1233 smb2_util_share_access(""),
1234 smb2_util_oplock_level("b"));
1235 io1.in.durable_open = true;
1237 status = smb2_create(tree, mem_ctx, &io1);
1238 CHECK_STATUS(status, NT_STATUS_OK);
1239 _h = io1.out.file.handle;
1240 h = &_h;
1241 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1242 CHECK_VAL(io1.out.durable_open, true);
1243 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1245 /* disconnect, reconnect and then do durable reopen */
1246 status = smb2_tdis(tree);
1247 CHECK_STATUS(status, NT_STATUS_OK);
1249 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1250 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1251 ret = false;
1252 goto done;
1256 ZERO_STRUCT(io2);
1257 io2.in.fname = fname;
1258 io2.in.durable_handle = h;
1260 status = smb2_create(tree2, mem_ctx, &io2);
1261 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1263 done:
1264 if (tree != NULL) {
1265 if (h != NULL) {
1266 smb2_util_close(tree, *h);
1269 smb2_util_unlink(tree2, fname);
1271 talloc_free(tree);
1274 talloc_free(mem_ctx);
1276 return ret;
1280 * basic test for doing a durable open:
1281 * logoff, create a new session, do a durable reopen (succeeds)
1283 static bool test_durable_open_reopen4(struct torture_context *tctx,
1284 struct smb2_tree *tree)
1286 NTSTATUS status;
1287 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1288 char fname[256];
1289 struct smb2_handle _h;
1290 struct smb2_handle *h = NULL;
1291 struct smb2_create io1, io2;
1292 bool ret = true;
1293 struct smb2_transport *transport;
1294 struct smb2_session *session2;
1295 struct smb2_tree *tree2;
1297 /* Choose a random name in case the state is left a little funky. */
1298 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1299 generate_random_str(tctx, 8));
1301 smb2_util_unlink(tree, fname);
1303 smb2_oplock_create_share(&io1, fname,
1304 smb2_util_share_access(""),
1305 smb2_util_oplock_level("b"));
1306 io1.in.durable_open = true;
1308 status = smb2_create(tree, mem_ctx, &io1);
1309 CHECK_STATUS(status, NT_STATUS_OK);
1310 _h = io1.out.file.handle;
1311 h = &_h;
1312 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1313 CHECK_VAL(io1.out.durable_open, true);
1314 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1317 * do a session logoff, establish a new session and tree
1318 * connect on the same transport, and try a durable reopen
1320 transport = tree->session->transport;
1321 status = smb2_logoff(tree->session);
1322 CHECK_STATUS(status, NT_STATUS_OK);
1324 if (!torture_smb2_session_setup(tctx, transport,
1325 0, /* previous_session_id */
1326 mem_ctx, &session2))
1328 torture_warning(tctx, "session setup failed.\n");
1329 ret = false;
1330 goto done;
1334 * the session setup has talloc-stolen the transport,
1335 * so we can safely free the old tree+session for clarity
1337 TALLOC_FREE(tree);
1339 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1340 torture_warning(tctx, "tree connect failed.\n");
1341 ret = false;
1342 goto done;
1345 ZERO_STRUCT(io2);
1346 io2.in.fname = fname;
1347 io2.in.durable_handle = h;
1348 h = NULL;
1350 status = smb2_create(tree2, mem_ctx, &io2);
1351 CHECK_STATUS(status, NT_STATUS_OK);
1353 _h = io2.out.file.handle;
1354 h = &_h;
1355 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1356 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1358 done:
1359 if (tree != NULL) {
1360 if (h != NULL) {
1361 smb2_util_close(tree2, *h);
1364 smb2_util_unlink(tree2, fname);
1366 talloc_free(tree);
1369 talloc_free(mem_ctx);
1371 return ret;
1374 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1375 struct smb2_tree *tree)
1377 NTSTATUS status;
1378 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1379 char fname[256];
1380 struct smb2_handle _h;
1381 struct smb2_handle *h = NULL;
1382 struct smb2_create io1, io2;
1383 bool ret = true;
1384 uint8_t b = 0;
1386 /* Choose a random name in case the state is left a little funky. */
1387 snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1388 generate_random_str(tctx, 8));
1390 smb2_util_unlink(tree, fname);
1392 smb2_oplock_create_share(&io1, fname,
1393 smb2_util_share_access(""),
1394 smb2_util_oplock_level("b"));
1395 io1.in.durable_open = true;
1396 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1398 status = smb2_create(tree, mem_ctx, &io1);
1399 CHECK_STATUS(status, NT_STATUS_OK);
1400 _h = io1.out.file.handle;
1401 h = &_h;
1402 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1403 CHECK_VAL(io1.out.durable_open, true);
1404 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1406 status = smb2_util_write(tree, *h, &b, 0, 1);
1407 CHECK_STATUS(status, NT_STATUS_OK);
1409 /* disconnect, leaving the durable handle in place */
1410 TALLOC_FREE(tree);
1412 if (!torture_smb2_connection(tctx, &tree)) {
1413 torture_warning(tctx, "could not reconnect, bailing\n");
1414 ret = false;
1415 goto done;
1419 * Open the file on the new connection again
1420 * and check that it has been newly created,
1421 * i.e. delete on close was effective on the disconnected handle.
1422 * Also check that the file is really empty,
1423 * the previously written byte gone.
1425 smb2_oplock_create_share(&io2, fname,
1426 smb2_util_share_access(""),
1427 smb2_util_oplock_level("b"));
1428 io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1430 status = smb2_create(tree, mem_ctx, &io2);
1431 CHECK_STATUS(status, NT_STATUS_OK);
1432 _h = io2.out.file.handle;
1433 h = &_h;
1434 CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1435 CHECK_VAL(io2.out.durable_open, false);
1436 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1438 done:
1439 if (tree != NULL) {
1440 if (h != NULL) {
1441 smb2_util_close(tree, *h);
1444 smb2_util_unlink(tree, fname);
1446 talloc_free(tree);
1449 talloc_free(mem_ctx);
1451 return ret;
1455 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1456 struct smb2_tree *tree)
1458 NTSTATUS status;
1459 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1460 char fname[256];
1461 struct smb2_handle _h;
1462 struct smb2_handle *h = NULL;
1463 struct smb2_create io;
1464 bool ret = true;
1465 uint8_t b = 0;
1466 uint64_t previous_session_id;
1467 uint64_t alloc_size_step;
1468 struct smbcli_options options;
1470 options = tree->session->transport->options;
1472 /* Choose a random name in case the state is left a little funky. */
1473 snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1474 generate_random_str(tctx, 8));
1476 smb2_util_unlink(tree, fname);
1478 smb2_oplock_create_share(&io, fname,
1479 smb2_util_share_access(""),
1480 smb2_util_oplock_level("b"));
1481 io.in.durable_open = true;
1482 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1484 status = smb2_create(tree, mem_ctx, &io);
1485 CHECK_STATUS(status, NT_STATUS_OK);
1486 _h = io.out.file.handle;
1487 h = &_h;
1488 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1489 CHECK_VAL(io.out.durable_open, true);
1490 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1492 status = smb2_util_write(tree, *h, &b, 0, 1);
1493 CHECK_STATUS(status, NT_STATUS_OK);
1495 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1497 /* disconnect, leaving the durable handle in place */
1498 TALLOC_FREE(tree);
1500 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1501 &options, &tree))
1503 torture_warning(tctx, "could not reconnect, bailing\n");
1504 ret = false;
1505 goto done;
1508 ZERO_STRUCT(io);
1509 io.in.fname = fname;
1510 io.in.durable_handle = h;
1512 status = smb2_create(tree, mem_ctx, &io);
1513 CHECK_STATUS(status, NT_STATUS_OK);
1514 _h = io.out.file.handle;
1515 h = &_h;
1516 alloc_size_step = io.out.alloc_size;
1517 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1518 CHECK_VAL(io.out.durable_open, false);
1519 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1521 /* close the file, thereby deleting it */
1522 smb2_util_close(tree, *h);
1523 status = smb2_logoff(tree->session);
1524 TALLOC_FREE(tree);
1526 if (!torture_smb2_connection(tctx, &tree)) {
1527 torture_warning(tctx, "could not reconnect, bailing\n");
1528 ret = false;
1529 goto done;
1533 * Open the file on the new connection again
1534 * and check that it has been newly created,
1535 * i.e. delete on close was effective on the reconnected handle.
1536 * Also check that the file is really empty,
1537 * the previously written byte gone.
1539 smb2_oplock_create_share(&io, fname,
1540 smb2_util_share_access(""),
1541 smb2_util_oplock_level("b"));
1542 io.in.durable_open = true;
1543 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1545 status = smb2_create(tree, mem_ctx, &io);
1546 CHECK_STATUS(status, NT_STATUS_OK);
1547 _h = io.out.file.handle;
1548 h = &_h;
1549 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1550 CHECK_VAL(io.out.durable_open, true);
1551 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1553 done:
1554 if (tree != NULL) {
1555 if (h != NULL) {
1556 smb2_util_close(tree, *h);
1559 smb2_util_unlink(tree, fname);
1561 talloc_free(tree);
1564 talloc_free(mem_ctx);
1566 return ret;
1570 basic testing of SMB2 durable opens
1571 regarding the position information on the handle
1573 static bool test_durable_open_file_position(struct torture_context *tctx,
1574 struct smb2_tree *tree)
1576 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1577 struct smb2_handle h;
1578 struct smb2_create io;
1579 NTSTATUS status;
1580 const char *fname = "durable_open_position.dat";
1581 union smb_fileinfo qfinfo;
1582 union smb_setfileinfo sfinfo;
1583 bool ret = true;
1584 uint64_t pos;
1585 uint64_t previous_session_id;
1586 struct smbcli_options options;
1588 options = tree->session->transport->options;
1590 smb2_util_unlink(tree, fname);
1592 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1593 io.in.durable_open = true;
1595 status = smb2_create(tree, mem_ctx, &io);
1596 CHECK_STATUS(status, NT_STATUS_OK);
1597 h = io.out.file.handle;
1598 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1599 CHECK_VAL(io.out.durable_open, true);
1600 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1602 /* TODO: check extra blob content */
1604 ZERO_STRUCT(qfinfo);
1605 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1606 qfinfo.generic.in.file.handle = h;
1607 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1608 CHECK_STATUS(status, NT_STATUS_OK);
1609 CHECK_VAL(qfinfo.position_information.out.position, 0);
1610 pos = qfinfo.position_information.out.position;
1611 torture_comment(tctx, "position: %llu\n",
1612 (unsigned long long)pos);
1614 ZERO_STRUCT(sfinfo);
1615 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1616 sfinfo.generic.in.file.handle = h;
1617 sfinfo.position_information.in.position = 0x1000;
1618 status = smb2_setinfo_file(tree, &sfinfo);
1619 CHECK_STATUS(status, NT_STATUS_OK);
1621 ZERO_STRUCT(qfinfo);
1622 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1623 qfinfo.generic.in.file.handle = h;
1624 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1625 CHECK_STATUS(status, NT_STATUS_OK);
1626 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1627 pos = qfinfo.position_information.out.position;
1628 torture_comment(tctx, "position: %llu\n",
1629 (unsigned long long)pos);
1631 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1633 /* tcp disconnect */
1634 talloc_free(tree);
1635 tree = NULL;
1637 /* do a session reconnect */
1638 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1639 &options, &tree))
1641 torture_warning(tctx, "couldn't reconnect, bailing\n");
1642 ret = false;
1643 goto done;
1646 ZERO_STRUCT(qfinfo);
1647 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1648 qfinfo.generic.in.file.handle = h;
1649 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1650 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1652 ZERO_STRUCT(io);
1653 io.in.fname = fname;
1654 io.in.durable_handle = &h;
1656 status = smb2_create(tree, mem_ctx, &io);
1657 CHECK_STATUS(status, NT_STATUS_OK);
1658 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1659 CHECK_VAL(io.out.reserved, 0x00);
1660 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1661 CHECK_VAL(io.out.alloc_size, 0);
1662 CHECK_VAL(io.out.size, 0);
1663 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1664 CHECK_VAL(io.out.reserved2, 0);
1666 h = io.out.file.handle;
1668 ZERO_STRUCT(qfinfo);
1669 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1670 qfinfo.generic.in.file.handle = h;
1671 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1672 CHECK_STATUS(status, NT_STATUS_OK);
1673 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1674 pos = qfinfo.position_information.out.position;
1675 torture_comment(tctx, "position: %llu\n",
1676 (unsigned long long)pos);
1678 smb2_util_close(tree, h);
1680 talloc_free(mem_ctx);
1682 smb2_util_unlink(tree, fname);
1684 done:
1685 talloc_free(tree);
1687 return ret;
1691 Open, disconnect, oplock break, reconnect.
1693 static bool test_durable_open_oplock(struct torture_context *tctx,
1694 struct smb2_tree *tree1,
1695 struct smb2_tree *tree2)
1697 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1698 struct smb2_create io1, io2;
1699 struct smb2_handle h1, h2;
1700 NTSTATUS status;
1701 char fname[256];
1702 bool ret = true;
1704 /* Choose a random name in case the state is left a little funky. */
1705 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1707 /* Clean slate */
1708 smb2_util_unlink(tree1, fname);
1710 /* Create with batch oplock */
1711 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1712 io1.in.durable_open = true;
1714 io2 = io1;
1715 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1717 status = smb2_create(tree1, mem_ctx, &io1);
1718 CHECK_STATUS(status, NT_STATUS_OK);
1719 h1 = io1.out.file.handle;
1720 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1721 CHECK_VAL(io1.out.durable_open, true);
1722 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1724 /* Disconnect after getting the batch */
1725 talloc_free(tree1);
1726 tree1 = NULL;
1729 * Windows7 (build 7000) will break a batch oplock immediately if the
1730 * original client is gone. (ZML: This seems like a bug. It should give
1731 * some time for the client to reconnect!)
1733 status = smb2_create(tree2, mem_ctx, &io2);
1734 CHECK_STATUS(status, NT_STATUS_OK);
1735 h2 = io2.out.file.handle;
1736 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1737 CHECK_VAL(io2.out.durable_open, true);
1738 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1740 /* What if tree1 tries to come back and reclaim? */
1741 if (!torture_smb2_connection(tctx, &tree1)) {
1742 torture_warning(tctx, "couldn't reconnect, bailing\n");
1743 ret = false;
1744 goto done;
1747 ZERO_STRUCT(io1);
1748 io1.in.fname = fname;
1749 io1.in.durable_handle = &h1;
1751 status = smb2_create(tree1, mem_ctx, &io1);
1752 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1754 done:
1755 smb2_util_close(tree2, h2);
1756 smb2_util_unlink(tree2, fname);
1758 talloc_free(tree1);
1759 talloc_free(tree2);
1761 return ret;
1765 Open, disconnect, lease break, reconnect.
1767 static bool test_durable_open_lease(struct torture_context *tctx,
1768 struct smb2_tree *tree1,
1769 struct smb2_tree *tree2)
1771 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1772 struct smb2_create io1, io2;
1773 struct smb2_lease ls1, ls2;
1774 struct smb2_handle h1, h2;
1775 NTSTATUS status;
1776 char fname[256];
1777 bool ret = true;
1778 uint64_t lease1, lease2;
1779 uint32_t caps;
1781 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1782 if (!(caps & SMB2_CAP_LEASING)) {
1783 torture_skip(tctx, "leases are not supported");
1787 * Choose a random name and random lease in case the state is left a
1788 * little funky.
1790 lease1 = random();
1791 lease2 = random();
1792 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1794 /* Clean slate */
1795 smb2_util_unlink(tree1, fname);
1797 /* Create with lease */
1798 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1799 lease1, smb2_util_lease_state("RHW"));
1800 io1.in.durable_open = true;
1802 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1803 lease2, smb2_util_lease_state("RHW"));
1804 io2.in.durable_open = true;
1805 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1807 status = smb2_create(tree1, mem_ctx, &io1);
1808 CHECK_STATUS(status, NT_STATUS_OK);
1809 h1 = io1.out.file.handle;
1810 CHECK_VAL(io1.out.durable_open, true);
1811 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1813 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1814 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1815 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1816 CHECK_VAL(io1.out.lease_response.lease_state,
1817 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1819 /* Disconnect after getting the lease */
1820 talloc_free(tree1);
1821 tree1 = NULL;
1824 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1825 * even if the original client is gone. (ZML: This seems like a bug. It
1826 * should give some time for the client to reconnect! And why RH?)
1828 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1829 * Test is adapted accordingly.
1831 status = smb2_create(tree2, mem_ctx, &io2);
1832 CHECK_STATUS(status, NT_STATUS_OK);
1833 h2 = io2.out.file.handle;
1834 CHECK_VAL(io2.out.durable_open, true);
1835 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1837 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1838 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1839 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1840 CHECK_VAL(io2.out.lease_response.lease_state,
1841 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1843 /* What if tree1 tries to come back and reclaim? */
1844 if (!torture_smb2_connection(tctx, &tree1)) {
1845 torture_warning(tctx, "couldn't reconnect, bailing\n");
1846 ret = false;
1847 goto done;
1850 ZERO_STRUCT(io1);
1851 io1.in.fname = fname;
1852 io1.in.durable_handle = &h1;
1853 io1.in.lease_request = &ls1;
1855 status = smb2_create(tree1, mem_ctx, &io1);
1856 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1858 done:
1859 smb2_util_close(tree2, h2);
1860 smb2_util_unlink(tree2, fname);
1862 talloc_free(tree1);
1863 talloc_free(tree2);
1865 return ret;
1868 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1869 struct smb2_tree *tree)
1871 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1872 struct smb2_create io;
1873 struct smb2_handle h;
1874 struct smb2_lock lck;
1875 struct smb2_lock_element el[2];
1876 NTSTATUS status;
1877 char fname[256];
1878 bool ret = true;
1882 snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1884 /* Clean slate */
1885 smb2_util_unlink(tree, fname);
1887 /* Create with oplock */
1889 smb2_oplock_create_share(&io, fname,
1890 smb2_util_share_access(""),
1891 smb2_util_oplock_level("b"));
1892 io.in.durable_open = true;
1894 status = smb2_create(tree, mem_ctx, &io);
1895 CHECK_STATUS(status, NT_STATUS_OK);
1896 h = io.out.file.handle;
1897 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1899 CHECK_VAL(io.out.durable_open, true);
1900 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1902 ZERO_STRUCT(lck);
1903 ZERO_STRUCT(el);
1904 lck.in.locks = el;
1905 lck.in.lock_count = 0x0001;
1906 lck.in.lock_sequence = 0x00000000;
1907 lck.in.file.handle = h;
1908 el[0].offset = 0;
1909 el[0].length = 1;
1910 el[0].reserved = 0x00000000;
1911 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1912 status = smb2_lock(tree, &lck);
1913 CHECK_STATUS(status, NT_STATUS_OK);
1915 /* Disconnect/Reconnect. */
1916 talloc_free(tree);
1917 tree = NULL;
1919 if (!torture_smb2_connection(tctx, &tree)) {
1920 torture_warning(tctx, "couldn't reconnect, bailing\n");
1921 ret = false;
1922 goto done;
1925 ZERO_STRUCT(io);
1926 io.in.fname = fname;
1927 io.in.durable_handle = &h;
1929 status = smb2_create(tree, mem_ctx, &io);
1930 CHECK_STATUS(status, NT_STATUS_OK);
1931 h = io.out.file.handle;
1933 lck.in.file.handle = h;
1934 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1935 status = smb2_lock(tree, &lck);
1936 CHECK_STATUS(status, NT_STATUS_OK);
1938 done:
1939 smb2_util_close(tree, h);
1940 smb2_util_unlink(tree, fname);
1941 talloc_free(tree);
1943 return ret;
1947 Open, take BRL, disconnect, reconnect.
1949 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1950 struct smb2_tree *tree)
1952 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1953 struct smb2_create io;
1954 struct smb2_lease ls;
1955 struct smb2_handle h;
1956 struct smb2_lock lck;
1957 struct smb2_lock_element el[2];
1958 NTSTATUS status;
1959 char fname[256];
1960 bool ret = true;
1961 uint64_t lease;
1962 uint32_t caps;
1963 struct smbcli_options options;
1965 options = tree->session->transport->options;
1967 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1968 if (!(caps & SMB2_CAP_LEASING)) {
1969 torture_skip(tctx, "leases are not supported");
1973 * Choose a random name and random lease in case the state is left a
1974 * little funky.
1976 lease = random();
1977 snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1979 /* Clean slate */
1980 smb2_util_unlink(tree, fname);
1982 /* Create with lease */
1984 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1985 smb2_util_lease_state("RWH"));
1986 io.in.durable_open = true;
1988 status = smb2_create(tree, mem_ctx, &io);
1989 CHECK_STATUS(status, NT_STATUS_OK);
1990 h = io.out.file.handle;
1991 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1993 CHECK_VAL(io.out.durable_open, true);
1994 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1995 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1996 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1997 CHECK_VAL(io.out.lease_response.lease_state,
1998 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2000 ZERO_STRUCT(lck);
2001 ZERO_STRUCT(el);
2002 lck.in.locks = el;
2003 lck.in.lock_count = 0x0001;
2004 lck.in.lock_sequence = 0x00000000;
2005 lck.in.file.handle = h;
2006 el[0].offset = 0;
2007 el[0].length = 1;
2008 el[0].reserved = 0x00000000;
2009 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2010 status = smb2_lock(tree, &lck);
2011 CHECK_STATUS(status, NT_STATUS_OK);
2013 /* Disconnect/Reconnect. */
2014 talloc_free(tree);
2015 tree = NULL;
2017 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2018 torture_warning(tctx, "couldn't reconnect, bailing\n");
2019 ret = false;
2020 goto done;
2023 ZERO_STRUCT(io);
2024 io.in.fname = fname;
2025 io.in.durable_handle = &h;
2026 io.in.lease_request = &ls;
2028 status = smb2_create(tree, mem_ctx, &io);
2029 CHECK_STATUS(status, NT_STATUS_OK);
2030 h = io.out.file.handle;
2032 lck.in.file.handle = h;
2033 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2034 status = smb2_lock(tree, &lck);
2035 CHECK_STATUS(status, NT_STATUS_OK);
2037 done:
2038 smb2_util_close(tree, h);
2039 smb2_util_unlink(tree, fname);
2040 talloc_free(tree);
2042 return ret;
2046 * Open with a RH lease, disconnect, open in another tree, reconnect.
2048 * This test actually demonstrates a minimum level of respect for the durable
2049 * open in the face of another open. As long as this test shows an inability to
2050 * reconnect after an open, the oplock/lease tests above will certainly
2051 * demonstrate an error on reconnect.
2053 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2054 struct smb2_tree *tree1,
2055 struct smb2_tree *tree2)
2057 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2058 struct smb2_create io1, io2;
2059 struct smb2_lease ls;
2060 struct smb2_handle h1, h2;
2061 NTSTATUS status;
2062 char fname[256];
2063 bool ret = true;
2064 uint64_t lease;
2065 uint32_t caps;
2066 struct smbcli_options options;
2068 options = tree1->session->transport->options;
2070 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2071 if (!(caps & SMB2_CAP_LEASING)) {
2072 torture_skip(tctx, "leases are not supported");
2076 * Choose a random name and random lease in case the state is left a
2077 * little funky.
2079 lease = random();
2080 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2081 generate_random_str(tctx, 8));
2083 /* Clean slate */
2084 smb2_util_unlink(tree1, fname);
2086 /* Create with lease */
2087 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2088 smb2_util_share_access(""),
2089 lease,
2090 smb2_util_lease_state("RH"));
2091 io1.in.durable_open = true;
2093 status = smb2_create(tree1, mem_ctx, &io1);
2094 CHECK_STATUS(status, NT_STATUS_OK);
2095 h1 = io1.out.file.handle;
2096 CHECK_VAL(io1.out.durable_open, true);
2097 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2099 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2100 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2101 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2102 CHECK_VAL(io1.out.lease_response.lease_state,
2103 smb2_util_lease_state("RH"));
2105 /* Disconnect */
2106 talloc_free(tree1);
2107 tree1 = NULL;
2109 /* Open the file in tree2 */
2110 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2112 status = smb2_create(tree2, mem_ctx, &io2);
2113 CHECK_STATUS(status, NT_STATUS_OK);
2114 h2 = io2.out.file.handle;
2115 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2117 /* Reconnect */
2118 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2119 torture_warning(tctx, "couldn't reconnect, bailing\n");
2120 ret = false;
2121 goto done;
2124 ZERO_STRUCT(io1);
2125 io1.in.fname = fname;
2126 io1.in.durable_handle = &h1;
2127 io1.in.lease_request = &ls;
2130 * Windows7 (build 7000) will give away an open immediately if the
2131 * original client is gone. (ZML: This seems like a bug. It should give
2132 * some time for the client to reconnect!)
2134 status = smb2_create(tree1, mem_ctx, &io1);
2135 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2136 h1 = io1.out.file.handle;
2138 done:
2139 if (tree1 != NULL){
2140 smb2_util_close(tree1, h1);
2141 smb2_util_unlink(tree1, fname);
2142 talloc_free(tree1);
2145 smb2_util_close(tree2, h2);
2146 smb2_util_unlink(tree2, fname);
2147 talloc_free(tree2);
2149 return ret;
2153 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2155 * This test actually demonstrates a minimum level of respect for the durable
2156 * open in the face of another open. As long as this test shows an inability to
2157 * reconnect after an open, the oplock/lease tests above will certainly
2158 * demonstrate an error on reconnect.
2160 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2161 struct smb2_tree *tree1,
2162 struct smb2_tree *tree2)
2164 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2165 struct smb2_create io1, io2;
2166 struct smb2_handle h1, h2;
2167 NTSTATUS status;
2168 char fname[256];
2169 bool ret = true;
2172 * Choose a random name and random lease in case the state is left a
2173 * little funky.
2175 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2176 generate_random_str(tctx, 8));
2178 /* Clean slate */
2179 smb2_util_unlink(tree1, fname);
2181 /* Create with batch oplock */
2182 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2183 io1.in.durable_open = true;
2185 status = smb2_create(tree1, mem_ctx, &io1);
2186 CHECK_STATUS(status, NT_STATUS_OK);
2187 h1 = io1.out.file.handle;
2188 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2189 CHECK_VAL(io1.out.durable_open, true);
2190 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2192 /* Disconnect */
2193 talloc_free(tree1);
2194 tree1 = NULL;
2196 /* Open the file in tree2 */
2197 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2199 status = smb2_create(tree2, mem_ctx, &io2);
2200 CHECK_STATUS(status, NT_STATUS_OK);
2201 h2 = io2.out.file.handle;
2202 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2204 /* Reconnect */
2205 if (!torture_smb2_connection(tctx, &tree1)) {
2206 torture_warning(tctx, "couldn't reconnect, bailing\n");
2207 ret = false;
2208 goto done;
2211 ZERO_STRUCT(io1);
2212 io1.in.fname = fname;
2213 io1.in.durable_handle = &h1;
2215 status = smb2_create(tree1, mem_ctx, &io1);
2216 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2217 h1 = io1.out.file.handle;
2219 done:
2220 smb2_util_close(tree2, h2);
2221 smb2_util_unlink(tree2, fname);
2222 if (tree1 != NULL) {
2223 smb2_util_close(tree1, h1);
2224 smb2_util_unlink(tree1, fname);
2227 talloc_free(tree1);
2228 talloc_free(tree2);
2230 return ret;
2234 * test behaviour with initial allocation size
2236 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2237 struct smb2_tree *tree)
2239 NTSTATUS status;
2240 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2241 char fname[256];
2242 struct smb2_handle _h;
2243 struct smb2_handle *h = NULL;
2244 struct smb2_create io;
2245 bool ret = true;
2246 uint64_t previous_session_id;
2247 uint64_t alloc_size_step;
2248 uint64_t initial_alloc_size = 0x100;
2249 const uint8_t *b = NULL;
2250 struct smbcli_options options;
2252 options = tree->session->transport->options;
2254 /* Choose a random name in case the state is left a little funky. */
2255 snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2256 generate_random_str(tctx, 8));
2258 smb2_util_unlink(tree, fname);
2260 smb2_oplock_create_share(&io, fname,
2261 smb2_util_share_access(""),
2262 smb2_util_oplock_level("b"));
2263 io.in.durable_open = true;
2264 io.in.alloc_size = initial_alloc_size;
2266 status = smb2_create(tree, mem_ctx, &io);
2267 CHECK_STATUS(status, NT_STATUS_OK);
2268 _h = io.out.file.handle;
2269 h = &_h;
2270 CHECK_NOT_VAL(io.out.alloc_size, 0);
2271 alloc_size_step = io.out.alloc_size;
2272 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2273 alloc_size_step, 0);
2274 CHECK_VAL(io.out.durable_open, true);
2275 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2277 /* prepare buffer */
2278 b = talloc_zero_size(mem_ctx, alloc_size_step);
2279 CHECK_NOT_NULL(b);
2281 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2283 /* disconnect, reconnect and then do durable reopen */
2284 talloc_free(tree);
2285 tree = NULL;
2287 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2288 &options, &tree))
2290 torture_warning(tctx, "couldn't reconnect, bailing\n");
2291 ret = false;
2292 goto done;
2295 ZERO_STRUCT(io);
2296 io.in.fname = fname;
2297 io.in.durable_handle = h;
2298 h = NULL;
2300 status = smb2_create(tree, mem_ctx, &io);
2301 CHECK_STATUS(status, NT_STATUS_OK);
2302 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2303 alloc_size_step, 0);
2304 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2305 _h = io.out.file.handle;
2306 h = &_h;
2308 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2310 /* write one byte */
2311 status = smb2_util_write(tree, *h, b, 0, 1);
2312 CHECK_STATUS(status, NT_STATUS_OK);
2314 /* disconnect, reconnect and then do durable reopen */
2315 talloc_free(tree);
2316 tree = NULL;
2318 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2319 &options, &tree))
2321 torture_warning(tctx, "couldn't reconnect, bailing\n");
2322 ret = false;
2323 goto done;
2326 ZERO_STRUCT(io);
2327 io.in.fname = fname;
2328 io.in.durable_handle = h;
2329 h = NULL;
2331 status = smb2_create(tree, mem_ctx, &io);
2332 CHECK_STATUS(status, NT_STATUS_OK);
2333 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2334 alloc_size_step, 1);
2335 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2336 _h = io.out.file.handle;
2337 h = &_h;
2339 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2341 /* write more byte than initial allocation size */
2342 status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2344 /* disconnect, reconnect and then do durable reopen */
2345 talloc_free(tree);
2346 tree = NULL;
2348 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2349 &options, &tree))
2351 torture_warning(tctx, "couldn't reconnect, bailing\n");
2352 ret = false;
2353 goto done;
2356 ZERO_STRUCT(io);
2357 io.in.fname = fname;
2358 io.in.durable_handle = h;
2359 h = NULL;
2361 status = smb2_create(tree, mem_ctx, &io);
2362 CHECK_STATUS(status, NT_STATUS_OK);
2363 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2364 alloc_size_step * 2, alloc_size_step + 1);
2365 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2366 _h = io.out.file.handle;
2367 h = &_h;
2369 done:
2370 if (h != NULL) {
2371 smb2_util_close(tree, *h);
2374 smb2_util_unlink(tree, fname);
2376 talloc_free(tree);
2378 talloc_free(mem_ctx);
2380 return ret;
2384 * test behaviour when a disconnect happens while creating a read-only file
2386 static bool test_durable_open_read_only(struct torture_context *tctx,
2387 struct smb2_tree *tree)
2389 NTSTATUS status;
2390 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2391 char fname[256];
2392 struct smb2_handle _h;
2393 struct smb2_handle *h = NULL;
2394 struct smb2_create io;
2395 bool ret = true;
2396 uint64_t previous_session_id;
2397 const uint8_t b = 0;
2398 uint64_t alloc_size = 0;
2399 struct smbcli_options options;
2401 options = tree->session->transport->options;
2403 /* Choose a random name in case the state is left a little funky. */
2404 snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2405 generate_random_str(tctx, 8));
2407 smb2_util_unlink(tree, fname);
2409 smb2_oplock_create_share(&io, fname,
2410 smb2_util_share_access(""),
2411 smb2_util_oplock_level("b"));
2412 io.in.durable_open = true;
2413 io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2415 status = smb2_create(tree, mem_ctx, &io);
2416 CHECK_STATUS(status, NT_STATUS_OK);
2417 _h = io.out.file.handle;
2418 h = &_h;
2419 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2420 CHECK_VAL(io.out.durable_open, true);
2421 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2423 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2425 /* write one byte */
2426 status = smb2_util_write(tree, *h, &b, 0, 1);
2427 CHECK_STATUS(status, NT_STATUS_OK);
2429 /* disconnect, reconnect and then do durable reopen */
2430 talloc_free(tree);
2431 tree = NULL;
2433 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2434 &options, &tree))
2436 torture_warning(tctx, "couldn't reconnect, bailing\n");
2437 ret = false;
2438 goto done;
2441 ZERO_STRUCT(io);
2442 io.in.fname = fname;
2443 io.in.durable_handle = h;
2444 h = NULL;
2446 status = smb2_create(tree, mem_ctx, &io);
2447 CHECK_STATUS(status, NT_STATUS_OK);
2448 alloc_size = io.out.alloc_size;
2449 CHECK_CREATED_SIZE(&io, EXISTED,
2450 FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2451 alloc_size, 1);
2452 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2453 _h = io.out.file.handle;
2454 h = &_h;
2456 /* write one byte */
2457 status = smb2_util_write(tree, *h, &b, 1, 1);
2458 CHECK_STATUS(status, NT_STATUS_OK);
2460 done:
2461 if (h != NULL) {
2462 union smb_setfileinfo sfinfo;
2464 ZERO_STRUCT(sfinfo);
2465 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2466 sfinfo.basic_info.in.file.handle = *h;
2467 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2468 smb2_setinfo_file(tree, &sfinfo);
2470 smb2_util_close(tree, *h);
2473 smb2_util_unlink(tree, fname);
2475 talloc_free(tree);
2477 talloc_free(mem_ctx);
2479 return ret;
2483 * durable open with oplock, disconnect, exit
2485 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2486 struct smb2_tree *tree)
2488 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2489 struct smb2_create io;
2490 struct smb2_handle _h;
2491 struct smb2_handle *h = NULL;
2492 NTSTATUS status;
2493 char fname[256];
2494 bool ret = true;
2496 snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2497 generate_random_str(mem_ctx, 8));
2499 smb2_util_unlink(tree, fname);
2501 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2502 io.in.durable_open = true;
2504 status = smb2_create(tree, mem_ctx, &io);
2505 CHECK_STATUS(status, NT_STATUS_OK);
2507 _h = io.out.file.handle;
2508 h = &_h;
2510 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2511 CHECK_VAL(io.out.durable_open, true);
2512 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2514 /* disconnect */
2515 talloc_free(tree);
2516 tree = NULL;
2518 done:
2519 if (tree != NULL) {
2520 if (h != NULL) {
2521 smb2_util_close(tree, *h);
2523 smb2_util_unlink(tree, fname);
2525 talloc_free(mem_ctx);
2526 return ret;
2530 struct torture_suite *torture_smb2_durable_open_init(void)
2532 struct torture_suite *suite =
2533 torture_suite_create(talloc_autofree_context(), "durable-open");
2535 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2536 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2537 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2538 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2539 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2540 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2541 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2542 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2543 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2544 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2545 torture_suite_add_1smb2_test(suite, "delete_on_close1",
2546 test_durable_open_delete_on_close1);
2547 torture_suite_add_1smb2_test(suite, "delete_on_close2",
2548 test_durable_open_delete_on_close2);
2549 torture_suite_add_1smb2_test(suite, "file-position",
2550 test_durable_open_file_position);
2551 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2552 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2553 torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2554 torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2555 torture_suite_add_2smb2_test(suite, "open2-lease",
2556 test_durable_open_open2_lease);
2557 torture_suite_add_2smb2_test(suite, "open2-oplock",
2558 test_durable_open_open2_oplock);
2559 torture_suite_add_1smb2_test(suite, "alloc-size",
2560 test_durable_open_alloc_size);
2561 torture_suite_add_1smb2_test(suite, "read-only",
2562 test_durable_open_read_only);
2564 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2566 return suite;
2569 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
2571 struct torture_suite *suite =
2572 torture_suite_create(talloc_autofree_context(),
2573 "durable-open-disconnect");
2575 torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2576 test_durable_open_oplock_disconnect);
2578 suite->description = talloc_strdup(suite,
2579 "SMB2-DURABLE-OPEN-DISCONNECT tests");
2581 return suite;