lib/util: Build iov_buf library only when building samba
[Samba.git] / source4 / torture / smb2 / durable_open.c
blobc3d63d19a032b9af10000ba39c526b16248f4408
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, correct) do { \
39 if ((v) == (correct)) { \
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)correct); \
42 ret = false; \
43 }} while (0)
45 #define CHECK_STATUS(status, correct) do { \
46 if (!NT_STATUS_EQUAL(status, correct)) { \
47 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
48 nt_errstr(status), nt_errstr(correct)); \
49 ret = false; \
50 goto done; \
51 }} while (0)
53 #define CHECK_CREATED(__io, __created, __attribute) \
54 do { \
55 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
56 CHECK_VAL((__io)->out.alloc_size, 0); \
57 CHECK_VAL((__io)->out.size, 0); \
58 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
59 CHECK_VAL((__io)->out.reserved2, 0); \
60 } while(0)
62 #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
63 do { \
64 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
65 CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
66 CHECK_VAL((__io)->out.size, (__size)); \
67 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
68 CHECK_VAL((__io)->out.reserved2, 0); \
69 } while(0)
73 /**
74 * basic durable_open test.
75 * durable state should only be granted when requested
76 * along with a batch oplock or a handle lease.
78 * This test tests durable open with all possible oplock types.
81 struct durable_open_vs_oplock {
82 const char *level;
83 const char *share_mode;
84 bool expected;
87 #define NUM_OPLOCK_TYPES 4
88 #define NUM_SHARE_MODES 8
89 #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
90 static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
92 { "", "", false },
93 { "", "R", false },
94 { "", "W", false },
95 { "", "D", false },
96 { "", "RD", false },
97 { "", "RW", false },
98 { "", "WD", false },
99 { "", "RWD", false },
101 { "s", "", false },
102 { "s", "R", false },
103 { "s", "W", false },
104 { "s", "D", false },
105 { "s", "RD", false },
106 { "s", "RW", false },
107 { "s", "WD", false },
108 { "s", "RWD", false },
110 { "x", "", false },
111 { "x", "R", false },
112 { "x", "W", false },
113 { "x", "D", false },
114 { "x", "RD", false },
115 { "x", "RW", false },
116 { "x", "WD", false },
117 { "x", "RWD", false },
119 { "b", "", true },
120 { "b", "R", true },
121 { "b", "W", true },
122 { "b", "D", true },
123 { "b", "RD", true },
124 { "b", "RW", true },
125 { "b", "WD", true },
126 { "b", "RWD", true },
129 static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
130 struct smb2_tree *tree,
131 const char *fname,
132 struct durable_open_vs_oplock test)
134 NTSTATUS status;
135 TALLOC_CTX *mem_ctx = talloc_new(tctx);
136 struct smb2_handle _h;
137 struct smb2_handle *h = NULL;
138 bool ret = true;
139 struct smb2_create io;
141 smb2_util_unlink(tree, fname);
143 smb2_oplock_create_share(&io, fname,
144 smb2_util_share_access(test.share_mode),
145 smb2_util_oplock_level(test.level));
146 io.in.durable_open = true;
148 status = smb2_create(tree, mem_ctx, &io);
149 CHECK_STATUS(status, NT_STATUS_OK);
150 _h = io.out.file.handle;
151 h = &_h;
152 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
153 CHECK_VAL(io.out.durable_open, test.expected);
154 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
156 done:
157 if (h != NULL) {
158 smb2_util_close(tree, *h);
160 smb2_util_unlink(tree, fname);
161 talloc_free(mem_ctx);
163 return ret;
166 static bool test_durable_open_open_oplock(struct torture_context *tctx,
167 struct smb2_tree *tree)
169 TALLOC_CTX *mem_ctx = talloc_new(tctx);
170 char fname[256];
171 bool ret = true;
172 int i;
174 /* Choose a random name in case the state is left a little funky. */
175 snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
177 smb2_util_unlink(tree, fname);
179 /* test various oplock levels with durable open */
181 for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
182 ret = test_one_durable_open_open_oplock(tctx,
183 tree,
184 fname,
185 durable_open_vs_oplock_table[i]);
186 if (ret == false) {
187 goto done;
191 done:
192 smb2_util_unlink(tree, fname);
193 talloc_free(tree);
194 talloc_free(mem_ctx);
196 return ret;
200 * basic durable_open test.
201 * durable state should only be granted when requested
202 * along with a batch oplock or a handle lease.
204 * This test tests durable open with all valid lease types.
207 struct durable_open_vs_lease {
208 const char *type;
209 const char *share_mode;
210 bool expected;
213 #define NUM_LEASE_TYPES 5
214 #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
215 static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
217 { "", "", false },
218 { "", "R", false },
219 { "", "W", false },
220 { "", "D", false },
221 { "", "RW", false },
222 { "", "RD", false },
223 { "", "WD", false },
224 { "", "RWD", false },
226 { "R", "", false },
227 { "R", "R", false },
228 { "R", "W", false },
229 { "R", "D", false },
230 { "R", "RW", false },
231 { "R", "RD", false },
232 { "R", "DW", false },
233 { "R", "RWD", false },
235 { "RW", "", false },
236 { "RW", "R", false },
237 { "RW", "W", false },
238 { "RW", "D", false },
239 { "RW", "RW", false },
240 { "RW", "RD", false },
241 { "RW", "WD", false },
242 { "RW", "RWD", false },
244 { "RH", "", true },
245 { "RH", "R", true },
246 { "RH", "W", true },
247 { "RH", "D", true },
248 { "RH", "RW", true },
249 { "RH", "RD", true },
250 { "RH", "WD", true },
251 { "RH", "RWD", true },
253 { "RHW", "", true },
254 { "RHW", "R", true },
255 { "RHW", "W", true },
256 { "RHW", "D", true },
257 { "RHW", "RW", true },
258 { "RHW", "RD", true },
259 { "RHW", "WD", true },
260 { "RHW", "RWD", true },
263 static bool test_one_durable_open_open_lease(struct torture_context *tctx,
264 struct smb2_tree *tree,
265 const char *fname,
266 struct durable_open_vs_lease test)
268 NTSTATUS status;
269 TALLOC_CTX *mem_ctx = talloc_new(tctx);
270 struct smb2_handle _h;
271 struct smb2_handle *h = NULL;
272 bool ret = true;
273 struct smb2_create io;
274 struct smb2_lease ls;
275 uint64_t lease;
276 uint32_t caps;
278 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
279 if (!(caps & SMB2_CAP_LEASING)) {
280 torture_skip(tctx, "leases are not supported");
283 smb2_util_unlink(tree, fname);
285 lease = random();
287 smb2_lease_create_share(&io, &ls, false /* dir */, fname,
288 smb2_util_share_access(test.share_mode),
289 lease,
290 smb2_util_lease_state(test.type));
291 io.in.durable_open = true;
293 status = smb2_create(tree, mem_ctx, &io);
294 CHECK_STATUS(status, NT_STATUS_OK);
295 _h = io.out.file.handle;
296 h = &_h;
297 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
298 CHECK_VAL(io.out.durable_open, test.expected);
299 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
300 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
301 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
302 CHECK_VAL(io.out.lease_response.lease_state,
303 smb2_util_lease_state(test.type));
304 done:
305 if (h != NULL) {
306 smb2_util_close(tree, *h);
308 smb2_util_unlink(tree, fname);
309 talloc_free(mem_ctx);
311 return ret;
314 static bool test_durable_open_open_lease(struct torture_context *tctx,
315 struct smb2_tree *tree)
317 TALLOC_CTX *mem_ctx = talloc_new(tctx);
318 char fname[256];
319 bool ret = true;
320 int i;
321 uint32_t caps;
323 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
324 if (!(caps & SMB2_CAP_LEASING)) {
325 torture_skip(tctx, "leases are not supported");
328 /* Choose a random name in case the state is left a little funky. */
329 snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
331 smb2_util_unlink(tree, fname);
334 /* test various oplock levels with durable open */
336 for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
337 ret = test_one_durable_open_open_lease(tctx,
338 tree,
339 fname,
340 durable_open_vs_lease_table[i]);
341 if (ret == false) {
342 goto done;
346 done:
347 smb2_util_unlink(tree, fname);
348 talloc_free(tree);
349 talloc_free(mem_ctx);
351 return ret;
355 * basic test for doing a durable open
356 * and do a durable reopen on the same connection
357 * while the first open is still active (fails)
359 static bool test_durable_open_reopen1(struct torture_context *tctx,
360 struct smb2_tree *tree)
362 NTSTATUS status;
363 TALLOC_CTX *mem_ctx = talloc_new(tctx);
364 char fname[256];
365 struct smb2_handle _h;
366 struct smb2_handle *h = NULL;
367 struct smb2_create io1, io2;
368 bool ret = true;
370 /* Choose a random name in case the state is left a little funky. */
371 snprintf(fname, 256, "durable_open_reopen1_%s.dat",
372 generate_random_str(tctx, 8));
374 smb2_util_unlink(tree, fname);
376 smb2_oplock_create_share(&io1, fname,
377 smb2_util_share_access(""),
378 smb2_util_oplock_level("b"));
379 io1.in.durable_open = true;
381 status = smb2_create(tree, mem_ctx, &io1);
382 CHECK_STATUS(status, NT_STATUS_OK);
383 _h = io1.out.file.handle;
384 h = &_h;
385 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
386 CHECK_VAL(io1.out.durable_open, true);
387 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
389 /* try a durable reconnect while the file is still open */
390 ZERO_STRUCT(io2);
391 io2.in.fname = fname;
392 io2.in.durable_handle = h;
394 status = smb2_create(tree, mem_ctx, &io2);
395 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
397 done:
398 if (h != NULL) {
399 smb2_util_close(tree, *h);
402 smb2_util_unlink(tree, fname);
404 talloc_free(tree);
406 talloc_free(mem_ctx);
408 return ret;
412 * Basic test for doing a durable open
413 * and do a session reconnect while the first
414 * session is still active and the handle is
415 * still open in the client.
416 * This closes the original session and a
417 * durable reconnect on the new session succeeds.
419 static bool test_durable_open_reopen1a(struct torture_context *tctx,
420 struct smb2_tree *tree)
422 NTSTATUS status;
423 TALLOC_CTX *mem_ctx = talloc_new(tctx);
424 char fname[256];
425 struct smb2_handle _h;
426 struct smb2_handle *h = NULL;
427 struct smb2_create io1, io2;
428 bool ret = true;
429 struct smb2_tree *tree2 = NULL;
430 uint64_t previous_session_id;
431 struct smbcli_options options;
433 options = tree->session->transport->options;
435 /* Choose a random name in case the state is left a little funky. */
436 snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
437 generate_random_str(tctx, 8));
439 smb2_util_unlink(tree, fname);
441 smb2_oplock_create_share(&io1, fname,
442 smb2_util_share_access(""),
443 smb2_util_oplock_level("b"));
444 io1.in.durable_open = true;
446 status = smb2_create(tree, mem_ctx, &io1);
447 CHECK_STATUS(status, NT_STATUS_OK);
448 _h = io1.out.file.handle;
449 h = &_h;
450 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
451 CHECK_VAL(io1.out.durable_open, true);
452 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
455 * a session reconnect on a second tcp connection
458 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
460 if (!torture_smb2_connection_ext(tctx, previous_session_id,
461 &options, &tree2))
463 torture_warning(tctx, "couldn't reconnect, bailing\n");
464 ret = false;
465 goto done;
469 * check that this has deleted the old session
472 ZERO_STRUCT(io2);
473 io2.in.fname = fname;
474 io2.in.durable_handle = h;
476 status = smb2_create(tree, mem_ctx, &io2);
477 CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
480 * but a durable reconnect on the new session succeeds:
483 ZERO_STRUCT(io2);
484 io2.in.fname = fname;
485 io2.in.durable_handle = h;
487 status = smb2_create(tree2, mem_ctx, &io2);
488 CHECK_STATUS(status, NT_STATUS_OK);
489 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
490 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
491 _h = io2.out.file.handle;
492 h = &_h;
494 done:
495 if (h != NULL) {
496 smb2_util_close(tree2, *h);
499 smb2_util_unlink(tree2, fname);
501 talloc_free(tree2);
503 talloc_free(tree);
505 talloc_free(mem_ctx);
507 return ret;
511 * basic test for doing a durable open
512 * tcp disconnect, reconnect, do a durable reopen (succeeds)
514 static bool test_durable_open_reopen2(struct torture_context *tctx,
515 struct smb2_tree *tree)
517 NTSTATUS status;
518 TALLOC_CTX *mem_ctx = talloc_new(tctx);
519 char fname[256];
520 struct smb2_handle _h;
521 struct smb2_handle *h = NULL;
522 struct smb2_create io;
523 bool ret = true;
525 /* Choose a random name in case the state is left a little funky. */
526 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
527 generate_random_str(tctx, 8));
529 smb2_util_unlink(tree, fname);
531 smb2_oplock_create_share(&io, fname,
532 smb2_util_share_access(""),
533 smb2_util_oplock_level("b"));
534 io.in.durable_open = true;
536 status = smb2_create(tree, mem_ctx, &io);
537 CHECK_STATUS(status, NT_STATUS_OK);
538 _h = io.out.file.handle;
539 h = &_h;
540 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
541 CHECK_VAL(io.out.durable_open, true);
542 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
544 /* disconnect, leaving the durable in place */
545 TALLOC_FREE(tree);
547 if (!torture_smb2_connection(tctx, &tree)) {
548 torture_warning(tctx, "couldn't reconnect, bailing\n");
549 ret = false;
550 goto done;
553 ZERO_STRUCT(io);
554 /* the path name is ignored by the server */
555 io.in.fname = fname;
556 io.in.durable_handle = h; /* durable v1 reconnect request */
557 h = NULL;
559 status = smb2_create(tree, mem_ctx, &io);
560 CHECK_STATUS(status, NT_STATUS_OK);
561 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
562 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
563 _h = io.out.file.handle;
564 h = &_h;
566 /* disconnect again, leaving the durable in place */
567 TALLOC_FREE(tree);
569 if (!torture_smb2_connection(tctx, &tree)) {
570 torture_warning(tctx, "couldn't reconnect, bailing\n");
571 ret = false;
572 goto done;
576 * show that the filename and many other fields
577 * are ignored. only the reconnect request blob
578 * is important.
580 ZERO_STRUCT(io);
581 /* the path name is ignored by the server */
582 io.in.security_flags = 0x78;
583 io.in.oplock_level = 0x78;
584 io.in.impersonation_level = 0x12345678;
585 io.in.create_flags = 0x12345678;
586 io.in.reserved = 0x12345678;
587 io.in.desired_access = 0x12345678;
588 io.in.file_attributes = 0x12345678;
589 io.in.share_access = 0x12345678;
590 io.in.create_disposition = 0x12345678;
591 io.in.create_options = 0x12345678;
592 io.in.fname = "__non_existing_fname__";
593 io.in.durable_handle = h; /* durable v1 reconnect request */
594 h = NULL;
596 status = smb2_create(tree, mem_ctx, &io);
597 CHECK_STATUS(status, NT_STATUS_OK);
598 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
599 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
600 _h = io.out.file.handle;
601 h = &_h;
603 /* disconnect, leaving the durable in place */
604 TALLOC_FREE(tree);
606 if (!torture_smb2_connection(tctx, &tree)) {
607 torture_warning(tctx, "couldn't reconnect, bailing\n");
608 ret = false;
609 goto done;
613 * show that an additionally specified durable v1 request
614 * is ignored by the server.
615 * See MS-SMB2, 3.3.5.9.7
616 * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
618 ZERO_STRUCT(io);
619 /* the path name is ignored by the server */
620 io.in.fname = fname;
621 io.in.durable_handle = h; /* durable v1 reconnect request */
622 io.in.durable_open = true; /* durable v1 handle request */
623 h = NULL;
625 status = smb2_create(tree, mem_ctx, &io);
626 CHECK_STATUS(status, NT_STATUS_OK);
627 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
628 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
629 _h = io.out.file.handle;
630 h = &_h;
632 done:
633 if (tree != NULL) {
634 if (h != NULL) {
635 smb2_util_close(tree, *h);
638 smb2_util_unlink(tree, fname);
640 talloc_free(tree);
643 talloc_free(mem_ctx);
645 return ret;
649 * lease variant of reopen2
650 * basic test for doing a durable open
651 * tcp disconnect, reconnect, do a durable reopen (succeeds)
653 static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
654 struct smb2_tree *tree)
656 NTSTATUS status;
657 TALLOC_CTX *mem_ctx = talloc_new(tctx);
658 char fname[256];
659 struct smb2_handle _h;
660 struct smb2_handle *h = NULL;
661 struct smb2_create io;
662 struct smb2_lease ls;
663 uint64_t lease_key;
664 bool ret = true;
665 struct smbcli_options options;
666 uint32_t caps;
668 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
669 if (!(caps & SMB2_CAP_LEASING)) {
670 torture_skip(tctx, "leases are not supported");
673 options = tree->session->transport->options;
675 /* Choose a random name in case the state is left a little funky. */
676 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
677 generate_random_str(tctx, 8));
679 smb2_util_unlink(tree, fname);
681 lease_key = random();
682 smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
683 smb2_util_lease_state("RWH"));
684 io.in.durable_open = true;
686 status = smb2_create(tree, mem_ctx, &io);
687 CHECK_STATUS(status, NT_STATUS_OK);
688 _h = io.out.file.handle;
689 h = &_h;
690 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
692 CHECK_VAL(io.out.durable_open, true);
693 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
694 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
695 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
696 CHECK_VAL(io.out.lease_response.lease_state,
697 smb2_util_lease_state("RWH"));
698 CHECK_VAL(io.out.lease_response.lease_flags, 0);
699 CHECK_VAL(io.out.lease_response.lease_duration, 0);
701 /* disconnect, reconnect and then do durable reopen */
702 TALLOC_FREE(tree);
704 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
705 torture_warning(tctx, "couldn't reconnect, bailing\n");
706 ret = false;
707 goto done;
711 /* a few failure tests: */
714 * several attempts without lease attached:
715 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
716 * irrespective of file name provided
719 ZERO_STRUCT(io);
720 io.in.fname = "";
721 io.in.durable_handle = h;
722 status = smb2_create(tree, mem_ctx, &io);
723 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
725 ZERO_STRUCT(io);
726 io.in.fname = "__non_existing_fname__";
727 io.in.durable_handle = h;
728 status = smb2_create(tree, mem_ctx, &io);
729 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
731 ZERO_STRUCT(io);
732 io.in.fname = fname;
733 io.in.durable_handle = h;
734 status = smb2_create(tree, mem_ctx, &io);
735 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
738 * attempt with lease provided, but
739 * with a changed lease key. => fails
741 ZERO_STRUCT(io);
742 io.in.fname = fname;
743 io.in.durable_open = false;
744 io.in.durable_handle = h;
745 io.in.lease_request = &ls;
746 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
747 /* a wrong lease key lets the request fail */
748 ls.lease_key.data[0]++;
750 status = smb2_create(tree, mem_ctx, &io);
751 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
753 /* restore the correct lease key */
754 ls.lease_key.data[0]--;
757 * this last failing attempt is almost correct:
758 * only problem is: we use the wrong filename...
759 * Note that this gives INVALID_PARAMETER.
760 * This is different from oplocks!
762 ZERO_STRUCT(io);
763 io.in.fname = "__non_existing_fname__";
764 io.in.durable_open = false;
765 io.in.durable_handle = h;
766 io.in.lease_request = &ls;
767 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
769 status = smb2_create(tree, mem_ctx, &io);
770 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
773 * Now for a succeeding reconnect:
776 ZERO_STRUCT(io);
777 io.in.fname = fname;
778 io.in.durable_open = false;
779 io.in.durable_handle = h;
780 io.in.lease_request = &ls;
781 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
783 /* the requested lease state is irrelevant */
784 ls.lease_state = smb2_util_lease_state("");
786 h = NULL;
788 status = smb2_create(tree, mem_ctx, &io);
789 CHECK_STATUS(status, NT_STATUS_OK);
791 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
792 CHECK_VAL(io.out.durable_open, false);
793 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
794 CHECK_VAL(io.out.persistent_open, false);
795 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
796 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
797 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
798 CHECK_VAL(io.out.lease_response.lease_state,
799 smb2_util_lease_state("RWH"));
800 CHECK_VAL(io.out.lease_response.lease_flags, 0);
801 CHECK_VAL(io.out.lease_response.lease_duration, 0);
802 _h = io.out.file.handle;
803 h = &_h;
805 /* disconnect one more time */
806 TALLOC_FREE(tree);
808 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
809 torture_warning(tctx, "couldn't reconnect, bailing\n");
810 ret = false;
811 goto done;
815 * demonstrate that various parameters are ignored
816 * in the reconnect
819 ZERO_STRUCT(io);
821 * These are completely ignored by the server
823 io.in.security_flags = 0x78;
824 io.in.oplock_level = 0x78;
825 io.in.impersonation_level = 0x12345678;
826 io.in.create_flags = 0x12345678;
827 io.in.reserved = 0x12345678;
828 io.in.desired_access = 0x12345678;
829 io.in.file_attributes = 0x12345678;
830 io.in.share_access = 0x12345678;
831 io.in.create_disposition = 0x12345678;
832 io.in.create_options = 0x12345678;
835 * only these are checked:
836 * - io.in.fname
837 * - io.in.durable_handle,
838 * - io.in.lease_request->lease_key
841 io.in.fname = fname;
842 io.in.durable_open_v2 = false;
843 io.in.durable_handle_v2 = h;
844 io.in.lease_request = &ls;
846 /* the requested lease state is irrelevant */
847 ls.lease_state = smb2_util_lease_state("");
849 h = NULL;
851 status = smb2_create(tree, mem_ctx, &io);
852 CHECK_STATUS(status, NT_STATUS_OK);
854 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
855 CHECK_VAL(io.out.durable_open, false);
856 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
857 CHECK_VAL(io.out.persistent_open, false);
858 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
859 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
860 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
861 CHECK_VAL(io.out.lease_response.lease_state,
862 smb2_util_lease_state("RWH"));
863 CHECK_VAL(io.out.lease_response.lease_flags, 0);
864 CHECK_VAL(io.out.lease_response.lease_duration, 0);
866 _h = io.out.file.handle;
867 h = &_h;
869 done:
870 if (tree != NULL) {
871 if (h != NULL) {
872 smb2_util_close(tree, *h);
875 smb2_util_unlink(tree, fname);
877 talloc_free(tree);
880 talloc_free(mem_ctx);
882 return ret;
886 * lease v2 variant of reopen2
887 * basic test for doing a durable open
888 * tcp disconnect, reconnect, do a durable reopen (succeeds)
890 static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
891 struct smb2_tree *tree)
893 NTSTATUS status;
894 TALLOC_CTX *mem_ctx = talloc_new(tctx);
895 char fname[256];
896 struct smb2_handle _h;
897 struct smb2_handle *h = NULL;
898 struct smb2_create io;
899 struct smb2_lease ls;
900 uint64_t lease_key;
901 bool ret = true;
902 struct smbcli_options options;
903 uint32_t caps;
905 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
906 if (!(caps & SMB2_CAP_LEASING)) {
907 torture_skip(tctx, "leases are not supported");
910 options = tree->session->transport->options;
912 /* Choose a random name in case the state is left a little funky. */
913 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
914 generate_random_str(tctx, 8));
916 smb2_util_unlink(tree, fname);
918 lease_key = random();
919 smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
920 lease_key, 0, /* parent lease key */
921 smb2_util_lease_state("RWH"), 0 /* lease epoch */);
922 io.in.durable_open = true;
924 status = smb2_create(tree, mem_ctx, &io);
925 CHECK_STATUS(status, NT_STATUS_OK);
926 _h = io.out.file.handle;
927 h = &_h;
928 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
930 CHECK_VAL(io.out.durable_open, true);
931 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
932 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
933 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
934 CHECK_VAL(io.out.lease_response_v2.lease_state,
935 smb2_util_lease_state("RWH"));
936 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
937 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
939 /* disconnect, reconnect and then do durable reopen */
940 TALLOC_FREE(tree);
942 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
943 torture_warning(tctx, "couldn't reconnect, bailing\n");
944 ret = false;
945 goto done;
948 /* a few failure tests: */
951 * several attempts without lease attached:
952 * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
953 * irrespective of file name provided
956 ZERO_STRUCT(io);
957 io.in.fname = "";
958 io.in.durable_handle = h;
959 status = smb2_create(tree, mem_ctx, &io);
960 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
962 ZERO_STRUCT(io);
963 io.in.fname = "__non_existing_fname__";
964 io.in.durable_handle = h;
965 status = smb2_create(tree, mem_ctx, &io);
966 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
968 ZERO_STRUCT(io);
969 io.in.fname = fname;
970 io.in.durable_handle = h;
971 status = smb2_create(tree, mem_ctx, &io);
972 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
975 * attempt with lease provided, but
976 * with a changed lease key. => fails
978 ZERO_STRUCT(io);
979 io.in.fname = fname;
980 io.in.durable_open = false;
981 io.in.durable_handle = h;
982 io.in.lease_request_v2 = &ls;
983 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
984 /* a wrong lease key lets the request fail */
985 ls.lease_key.data[0]++;
987 status = smb2_create(tree, mem_ctx, &io);
988 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
990 /* restore the correct lease key */
991 ls.lease_key.data[0]--;
994 * this last failing attempt is almost correct:
995 * only problem is: we use the wrong filename...
996 * Note that this gives INVALID_PARAMETER.
997 * This is different from oplocks!
999 ZERO_STRUCT(io);
1000 io.in.fname = "__non_existing_fname__";
1001 io.in.durable_open = false;
1002 io.in.durable_handle = h;
1003 io.in.lease_request_v2 = &ls;
1004 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1006 status = smb2_create(tree, mem_ctx, &io);
1007 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1010 * Now for a succeeding reconnect:
1013 ZERO_STRUCT(io);
1014 io.in.fname = fname;
1015 io.in.durable_open = false;
1016 io.in.durable_handle = h;
1017 io.in.lease_request_v2 = &ls;
1018 io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1020 /* the requested lease state is irrelevant */
1021 ls.lease_state = smb2_util_lease_state("");
1023 h = NULL;
1025 status = smb2_create(tree, mem_ctx, &io);
1026 CHECK_STATUS(status, NT_STATUS_OK);
1028 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1029 CHECK_VAL(io.out.durable_open, false);
1030 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1031 CHECK_VAL(io.out.persistent_open, false);
1032 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1033 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1034 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1035 CHECK_VAL(io.out.lease_response_v2.lease_state,
1036 smb2_util_lease_state("RWH"));
1037 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1038 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1039 _h = io.out.file.handle;
1040 h = &_h;
1042 /* disconnect one more time */
1043 TALLOC_FREE(tree);
1045 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1046 torture_warning(tctx, "couldn't reconnect, bailing\n");
1047 ret = false;
1048 goto done;
1052 * demonstrate that various parameters are ignored
1053 * in the reconnect
1056 ZERO_STRUCT(io);
1058 * These are completely ignored by the server
1060 io.in.security_flags = 0x78;
1061 io.in.oplock_level = 0x78;
1062 io.in.impersonation_level = 0x12345678;
1063 io.in.create_flags = 0x12345678;
1064 io.in.reserved = 0x12345678;
1065 io.in.desired_access = 0x12345678;
1066 io.in.file_attributes = 0x12345678;
1067 io.in.share_access = 0x12345678;
1068 io.in.create_disposition = 0x12345678;
1069 io.in.create_options = 0x12345678;
1072 * only these are checked:
1073 * - io.in.fname
1074 * - io.in.durable_handle,
1075 * - io.in.lease_request->lease_key
1078 io.in.fname = fname;
1079 io.in.durable_open_v2 = false;
1080 io.in.durable_handle_v2 = h;
1081 io.in.lease_request_v2 = &ls;
1083 /* the requested lease state is irrelevant */
1084 ls.lease_state = smb2_util_lease_state("");
1086 h = NULL;
1088 status = smb2_create(tree, mem_ctx, &io);
1089 CHECK_STATUS(status, NT_STATUS_OK);
1091 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1092 CHECK_VAL(io.out.durable_open, false);
1093 CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1094 CHECK_VAL(io.out.persistent_open, false);
1095 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1096 CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1097 CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1098 CHECK_VAL(io.out.lease_response_v2.lease_state,
1099 smb2_util_lease_state("RWH"));
1100 CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1101 CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1103 _h = io.out.file.handle;
1104 h = &_h;
1106 done:
1107 if (tree != NULL) {
1108 if (h != NULL) {
1109 smb2_util_close(tree, *h);
1112 smb2_util_unlink(tree, fname);
1114 talloc_free(tree);
1117 talloc_free(mem_ctx);
1119 return ret;
1123 * basic test for doing a durable open
1124 * tcp disconnect, reconnect with a session reconnect and
1125 * do a durable reopen (succeeds)
1127 static bool test_durable_open_reopen2a(struct torture_context *tctx,
1128 struct smb2_tree *tree)
1130 NTSTATUS status;
1131 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1132 char fname[256];
1133 struct smb2_handle _h;
1134 struct smb2_handle *h = NULL;
1135 struct smb2_create io1, io2;
1136 uint64_t previous_session_id;
1137 bool ret = true;
1138 struct smbcli_options options;
1140 options = tree->session->transport->options;
1142 /* Choose a random name in case the state is left a little funky. */
1143 snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1144 generate_random_str(tctx, 8));
1146 smb2_util_unlink(tree, fname);
1148 smb2_oplock_create_share(&io1, fname,
1149 smb2_util_share_access(""),
1150 smb2_util_oplock_level("b"));
1151 io1.in.durable_open = true;
1153 status = smb2_create(tree, mem_ctx, &io1);
1154 CHECK_STATUS(status, NT_STATUS_OK);
1155 _h = io1.out.file.handle;
1156 h = &_h;
1157 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1158 CHECK_VAL(io1.out.durable_open, true);
1159 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1161 /* disconnect, reconnect and then do durable reopen */
1162 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1163 talloc_free(tree);
1164 tree = NULL;
1166 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1167 &options, &tree))
1169 torture_warning(tctx, "couldn't reconnect, bailing\n");
1170 ret = false;
1171 goto done;
1174 ZERO_STRUCT(io2);
1175 io2.in.fname = fname;
1176 io2.in.durable_handle = h;
1177 h = NULL;
1179 status = smb2_create(tree, mem_ctx, &io2);
1180 CHECK_STATUS(status, NT_STATUS_OK);
1181 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1182 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1183 _h = io2.out.file.handle;
1184 h = &_h;
1186 done:
1187 if (tree != NULL) {
1188 if (h != NULL) {
1189 smb2_util_close(tree, *h);
1192 smb2_util_unlink(tree, fname);
1194 talloc_free(tree);
1197 talloc_free(mem_ctx);
1199 return ret;
1204 * basic test for doing a durable open:
1205 * tdis, new tcon, try durable reopen (fails)
1207 static bool test_durable_open_reopen3(struct torture_context *tctx,
1208 struct smb2_tree *tree)
1210 NTSTATUS status;
1211 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1212 char fname[256];
1213 struct smb2_handle _h;
1214 struct smb2_handle *h = NULL;
1215 struct smb2_create io1, io2;
1216 bool ret = true;
1217 struct smb2_tree *tree2;
1219 /* Choose a random name in case the state is left a little funky. */
1220 snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1221 generate_random_str(tctx, 8));
1223 smb2_util_unlink(tree, fname);
1225 smb2_oplock_create_share(&io1, fname,
1226 smb2_util_share_access(""),
1227 smb2_util_oplock_level("b"));
1228 io1.in.durable_open = true;
1230 status = smb2_create(tree, mem_ctx, &io1);
1231 CHECK_STATUS(status, NT_STATUS_OK);
1232 _h = io1.out.file.handle;
1233 h = &_h;
1234 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1235 CHECK_VAL(io1.out.durable_open, true);
1236 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1238 /* disconnect, reconnect and then do durable reopen */
1239 status = smb2_tdis(tree);
1240 CHECK_STATUS(status, NT_STATUS_OK);
1242 if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1243 torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1244 ret = false;
1245 goto done;
1249 ZERO_STRUCT(io2);
1250 io2.in.fname = fname;
1251 io2.in.durable_handle = h;
1253 status = smb2_create(tree2, mem_ctx, &io2);
1254 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1256 done:
1257 if (tree != NULL) {
1258 if (h != NULL) {
1259 smb2_util_close(tree, *h);
1262 smb2_util_unlink(tree2, fname);
1264 talloc_free(tree);
1267 talloc_free(mem_ctx);
1269 return ret;
1273 * basic test for doing a durable open:
1274 * logoff, create a new session, do a durable reopen (succeeds)
1276 static bool test_durable_open_reopen4(struct torture_context *tctx,
1277 struct smb2_tree *tree)
1279 NTSTATUS status;
1280 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1281 char fname[256];
1282 struct smb2_handle _h;
1283 struct smb2_handle *h = NULL;
1284 struct smb2_create io1, io2;
1285 bool ret = true;
1286 struct smb2_transport *transport;
1287 struct smb2_session *session2;
1288 struct smb2_tree *tree2;
1290 /* Choose a random name in case the state is left a little funky. */
1291 snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1292 generate_random_str(tctx, 8));
1294 smb2_util_unlink(tree, fname);
1296 smb2_oplock_create_share(&io1, fname,
1297 smb2_util_share_access(""),
1298 smb2_util_oplock_level("b"));
1299 io1.in.durable_open = true;
1301 status = smb2_create(tree, mem_ctx, &io1);
1302 CHECK_STATUS(status, NT_STATUS_OK);
1303 _h = io1.out.file.handle;
1304 h = &_h;
1305 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1306 CHECK_VAL(io1.out.durable_open, true);
1307 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1310 * do a session logoff, establish a new session and tree
1311 * connect on the same transport, and try a durable reopen
1313 transport = tree->session->transport;
1314 status = smb2_logoff(tree->session);
1315 CHECK_STATUS(status, NT_STATUS_OK);
1317 if (!torture_smb2_session_setup(tctx, transport,
1318 0, /* previous_session_id */
1319 mem_ctx, &session2))
1321 torture_warning(tctx, "session setup failed.\n");
1322 ret = false;
1323 goto done;
1327 * the session setup has talloc-stolen the transport,
1328 * so we can safely free the old tree+session for clarity
1330 TALLOC_FREE(tree);
1332 if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1333 torture_warning(tctx, "tree connect failed.\n");
1334 ret = false;
1335 goto done;
1338 ZERO_STRUCT(io2);
1339 io2.in.fname = fname;
1340 io2.in.durable_handle = h;
1341 h = NULL;
1343 status = smb2_create(tree2, mem_ctx, &io2);
1344 CHECK_STATUS(status, NT_STATUS_OK);
1346 _h = io2.out.file.handle;
1347 h = &_h;
1348 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1349 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1351 done:
1352 if (tree != NULL) {
1353 if (h != NULL) {
1354 smb2_util_close(tree2, *h);
1357 smb2_util_unlink(tree2, fname);
1359 talloc_free(tree);
1362 talloc_free(mem_ctx);
1364 return ret;
1367 static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1368 struct smb2_tree *tree)
1370 NTSTATUS status;
1371 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1372 char fname[256];
1373 struct smb2_handle _h;
1374 struct smb2_handle *h = NULL;
1375 struct smb2_create io1, io2;
1376 bool ret = true;
1377 uint8_t b = 0;
1379 /* Choose a random name in case the state is left a little funky. */
1380 snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1381 generate_random_str(tctx, 8));
1383 smb2_util_unlink(tree, fname);
1385 smb2_oplock_create_share(&io1, fname,
1386 smb2_util_share_access(""),
1387 smb2_util_oplock_level("b"));
1388 io1.in.durable_open = true;
1389 io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1391 status = smb2_create(tree, mem_ctx, &io1);
1392 CHECK_STATUS(status, NT_STATUS_OK);
1393 _h = io1.out.file.handle;
1394 h = &_h;
1395 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1396 CHECK_VAL(io1.out.durable_open, true);
1397 CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1399 status = smb2_util_write(tree, *h, &b, 0, 1);
1400 CHECK_STATUS(status, NT_STATUS_OK);
1402 /* disconnect, leaving the durable handle in place */
1403 TALLOC_FREE(tree);
1405 if (!torture_smb2_connection(tctx, &tree)) {
1406 torture_warning(tctx, "could not reconnect, bailing\n");
1407 ret = false;
1408 goto done;
1412 * Open the file on the new connection again
1413 * and check that it has been newly created,
1414 * i.e. delete on close was effective on the disconnected handle.
1415 * Also check that the file is really empty,
1416 * the previously written byte gone.
1418 smb2_oplock_create_share(&io2, fname,
1419 smb2_util_share_access(""),
1420 smb2_util_oplock_level("b"));
1421 io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1423 status = smb2_create(tree, mem_ctx, &io2);
1424 CHECK_STATUS(status, NT_STATUS_OK);
1425 _h = io2.out.file.handle;
1426 h = &_h;
1427 CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1428 CHECK_VAL(io2.out.durable_open, false);
1429 CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1431 done:
1432 if (tree != NULL) {
1433 if (h != NULL) {
1434 smb2_util_close(tree, *h);
1437 smb2_util_unlink(tree, fname);
1439 talloc_free(tree);
1442 talloc_free(mem_ctx);
1444 return ret;
1448 static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1449 struct smb2_tree *tree)
1451 NTSTATUS status;
1452 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1453 char fname[256];
1454 struct smb2_handle _h;
1455 struct smb2_handle *h = NULL;
1456 struct smb2_create io;
1457 bool ret = true;
1458 uint8_t b = 0;
1459 uint64_t previous_session_id;
1460 uint64_t alloc_size_step;
1461 struct smbcli_options options;
1463 options = tree->session->transport->options;
1465 /* Choose a random name in case the state is left a little funky. */
1466 snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1467 generate_random_str(tctx, 8));
1469 smb2_util_unlink(tree, fname);
1471 smb2_oplock_create_share(&io, fname,
1472 smb2_util_share_access(""),
1473 smb2_util_oplock_level("b"));
1474 io.in.durable_open = true;
1475 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1477 status = smb2_create(tree, mem_ctx, &io);
1478 CHECK_STATUS(status, NT_STATUS_OK);
1479 _h = io.out.file.handle;
1480 h = &_h;
1481 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1482 CHECK_VAL(io.out.durable_open, true);
1483 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1485 status = smb2_util_write(tree, *h, &b, 0, 1);
1486 CHECK_STATUS(status, NT_STATUS_OK);
1488 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1490 /* disconnect, leaving the durable handle in place */
1491 TALLOC_FREE(tree);
1493 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1494 &options, &tree))
1496 torture_warning(tctx, "could not reconnect, bailing\n");
1497 ret = false;
1498 goto done;
1501 ZERO_STRUCT(io);
1502 io.in.fname = fname;
1503 io.in.durable_handle = h;
1505 status = smb2_create(tree, mem_ctx, &io);
1506 CHECK_STATUS(status, NT_STATUS_OK);
1507 _h = io.out.file.handle;
1508 h = &_h;
1509 alloc_size_step = io.out.alloc_size;
1510 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1511 CHECK_VAL(io.out.durable_open, false);
1512 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1514 /* close the file, thereby deleting it */
1515 smb2_util_close(tree, *h);
1516 status = smb2_logoff(tree->session);
1517 TALLOC_FREE(tree);
1519 if (!torture_smb2_connection(tctx, &tree)) {
1520 torture_warning(tctx, "could not reconnect, bailing\n");
1521 ret = false;
1522 goto done;
1526 * Open the file on the new connection again
1527 * and check that it has been newly created,
1528 * i.e. delete on close was effective on the reconnected handle.
1529 * Also check that the file is really empty,
1530 * the previously written byte gone.
1532 smb2_oplock_create_share(&io, fname,
1533 smb2_util_share_access(""),
1534 smb2_util_oplock_level("b"));
1535 io.in.durable_open = true;
1536 io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1538 status = smb2_create(tree, mem_ctx, &io);
1539 CHECK_STATUS(status, NT_STATUS_OK);
1540 _h = io.out.file.handle;
1541 h = &_h;
1542 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1543 CHECK_VAL(io.out.durable_open, true);
1544 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1546 done:
1547 if (tree != NULL) {
1548 if (h != NULL) {
1549 smb2_util_close(tree, *h);
1552 smb2_util_unlink(tree, fname);
1554 talloc_free(tree);
1557 talloc_free(mem_ctx);
1559 return ret;
1563 basic testing of SMB2 durable opens
1564 regarding the position information on the handle
1566 static bool test_durable_open_file_position(struct torture_context *tctx,
1567 struct smb2_tree *tree)
1569 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1570 struct smb2_handle h;
1571 struct smb2_create io;
1572 NTSTATUS status;
1573 const char *fname = "durable_open_position.dat";
1574 union smb_fileinfo qfinfo;
1575 union smb_setfileinfo sfinfo;
1576 bool ret = true;
1577 uint64_t pos;
1578 uint64_t previous_session_id;
1579 struct smbcli_options options;
1581 options = tree->session->transport->options;
1583 smb2_util_unlink(tree, fname);
1585 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1586 io.in.durable_open = true;
1588 status = smb2_create(tree, mem_ctx, &io);
1589 CHECK_STATUS(status, NT_STATUS_OK);
1590 h = io.out.file.handle;
1591 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1592 CHECK_VAL(io.out.durable_open, true);
1593 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1595 /* TODO: check extra blob content */
1597 ZERO_STRUCT(qfinfo);
1598 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1599 qfinfo.generic.in.file.handle = h;
1600 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1601 CHECK_STATUS(status, NT_STATUS_OK);
1602 CHECK_VAL(qfinfo.position_information.out.position, 0);
1603 pos = qfinfo.position_information.out.position;
1604 torture_comment(tctx, "position: %llu\n",
1605 (unsigned long long)pos);
1607 ZERO_STRUCT(sfinfo);
1608 sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1609 sfinfo.generic.in.file.handle = h;
1610 sfinfo.position_information.in.position = 0x1000;
1611 status = smb2_setinfo_file(tree, &sfinfo);
1612 CHECK_STATUS(status, NT_STATUS_OK);
1614 ZERO_STRUCT(qfinfo);
1615 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1616 qfinfo.generic.in.file.handle = h;
1617 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1618 CHECK_STATUS(status, NT_STATUS_OK);
1619 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1620 pos = qfinfo.position_information.out.position;
1621 torture_comment(tctx, "position: %llu\n",
1622 (unsigned long long)pos);
1624 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1626 /* tcp disconnect */
1627 talloc_free(tree);
1628 tree = NULL;
1630 /* do a session reconnect */
1631 if (!torture_smb2_connection_ext(tctx, previous_session_id,
1632 &options, &tree))
1634 torture_warning(tctx, "couldn't reconnect, bailing\n");
1635 ret = false;
1636 goto done;
1639 ZERO_STRUCT(qfinfo);
1640 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1641 qfinfo.generic.in.file.handle = h;
1642 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1643 CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1645 ZERO_STRUCT(io);
1646 io.in.fname = fname;
1647 io.in.durable_handle = &h;
1649 status = smb2_create(tree, mem_ctx, &io);
1650 CHECK_STATUS(status, NT_STATUS_OK);
1651 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1652 CHECK_VAL(io.out.reserved, 0x00);
1653 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1654 CHECK_VAL(io.out.alloc_size, 0);
1655 CHECK_VAL(io.out.size, 0);
1656 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1657 CHECK_VAL(io.out.reserved2, 0);
1659 h = io.out.file.handle;
1661 ZERO_STRUCT(qfinfo);
1662 qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1663 qfinfo.generic.in.file.handle = h;
1664 status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1665 CHECK_STATUS(status, NT_STATUS_OK);
1666 CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1667 pos = qfinfo.position_information.out.position;
1668 torture_comment(tctx, "position: %llu\n",
1669 (unsigned long long)pos);
1671 smb2_util_close(tree, h);
1673 talloc_free(mem_ctx);
1675 smb2_util_unlink(tree, fname);
1677 done:
1678 talloc_free(tree);
1680 return ret;
1684 Open, disconnect, oplock break, reconnect.
1686 static bool test_durable_open_oplock(struct torture_context *tctx,
1687 struct smb2_tree *tree1,
1688 struct smb2_tree *tree2)
1690 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1691 struct smb2_create io1, io2;
1692 struct smb2_handle h1, h2;
1693 NTSTATUS status;
1694 char fname[256];
1695 bool ret = true;
1697 /* Choose a random name in case the state is left a little funky. */
1698 snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1700 /* Clean slate */
1701 smb2_util_unlink(tree1, fname);
1703 /* Create with batch oplock */
1704 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1705 io1.in.durable_open = true;
1707 io2 = io1;
1708 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1710 status = smb2_create(tree1, mem_ctx, &io1);
1711 CHECK_STATUS(status, NT_STATUS_OK);
1712 h1 = io1.out.file.handle;
1713 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1714 CHECK_VAL(io1.out.durable_open, true);
1715 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1717 /* Disconnect after getting the batch */
1718 talloc_free(tree1);
1719 tree1 = NULL;
1722 * Windows7 (build 7000) will break a batch oplock immediately if the
1723 * original client is gone. (ZML: This seems like a bug. It should give
1724 * some time for the client to reconnect!)
1726 status = smb2_create(tree2, mem_ctx, &io2);
1727 CHECK_STATUS(status, NT_STATUS_OK);
1728 h2 = io2.out.file.handle;
1729 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1730 CHECK_VAL(io2.out.durable_open, true);
1731 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1733 /* What if tree1 tries to come back and reclaim? */
1734 if (!torture_smb2_connection(tctx, &tree1)) {
1735 torture_warning(tctx, "couldn't reconnect, bailing\n");
1736 ret = false;
1737 goto done;
1740 ZERO_STRUCT(io1);
1741 io1.in.fname = fname;
1742 io1.in.durable_handle = &h1;
1744 status = smb2_create(tree1, mem_ctx, &io1);
1745 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1747 done:
1748 smb2_util_close(tree2, h2);
1749 smb2_util_unlink(tree2, fname);
1751 talloc_free(tree1);
1752 talloc_free(tree2);
1754 return ret;
1758 Open, disconnect, lease break, reconnect.
1760 static bool test_durable_open_lease(struct torture_context *tctx,
1761 struct smb2_tree *tree1,
1762 struct smb2_tree *tree2)
1764 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1765 struct smb2_create io1, io2;
1766 struct smb2_lease ls1, ls2;
1767 struct smb2_handle h1, h2;
1768 NTSTATUS status;
1769 char fname[256];
1770 bool ret = true;
1771 uint64_t lease1, lease2;
1772 uint32_t caps;
1774 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
1775 if (!(caps & SMB2_CAP_LEASING)) {
1776 torture_skip(tctx, "leases are not supported");
1780 * Choose a random name and random lease in case the state is left a
1781 * little funky.
1783 lease1 = random();
1784 lease2 = random();
1785 snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
1787 /* Clean slate */
1788 smb2_util_unlink(tree1, fname);
1790 /* Create with lease */
1791 smb2_lease_create(&io1, &ls1, false /* dir */, fname,
1792 lease1, smb2_util_lease_state("RHW"));
1793 io1.in.durable_open = true;
1795 smb2_lease_create(&io2, &ls2, false /* dir */, fname,
1796 lease2, smb2_util_lease_state("RHW"));
1797 io2.in.durable_open = true;
1798 io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1800 status = smb2_create(tree1, mem_ctx, &io1);
1801 CHECK_STATUS(status, NT_STATUS_OK);
1802 h1 = io1.out.file.handle;
1803 CHECK_VAL(io1.out.durable_open, true);
1804 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1806 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1807 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
1808 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
1809 CHECK_VAL(io1.out.lease_response.lease_state,
1810 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1812 /* Disconnect after getting the lease */
1813 talloc_free(tree1);
1814 tree1 = NULL;
1817 * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
1818 * even if the original client is gone. (ZML: This seems like a bug. It
1819 * should give some time for the client to reconnect! And why RH?)
1821 * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
1822 * Test is adapted accordingly.
1824 status = smb2_create(tree2, mem_ctx, &io2);
1825 CHECK_STATUS(status, NT_STATUS_OK);
1826 h2 = io2.out.file.handle;
1827 CHECK_VAL(io2.out.durable_open, true);
1828 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1830 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1831 CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
1832 CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
1833 CHECK_VAL(io2.out.lease_response.lease_state,
1834 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1836 /* What if tree1 tries to come back and reclaim? */
1837 if (!torture_smb2_connection(tctx, &tree1)) {
1838 torture_warning(tctx, "couldn't reconnect, bailing\n");
1839 ret = false;
1840 goto done;
1843 ZERO_STRUCT(io1);
1844 io1.in.fname = fname;
1845 io1.in.durable_handle = &h1;
1846 io1.in.lease_request = &ls1;
1848 status = smb2_create(tree1, mem_ctx, &io1);
1849 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1851 done:
1852 smb2_util_close(tree2, h2);
1853 smb2_util_unlink(tree2, fname);
1855 talloc_free(tree1);
1856 talloc_free(tree2);
1858 return ret;
1861 static bool test_durable_open_lock_oplock(struct torture_context *tctx,
1862 struct smb2_tree *tree)
1864 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1865 struct smb2_create io;
1866 struct smb2_handle h;
1867 struct smb2_lock lck;
1868 struct smb2_lock_element el[2];
1869 NTSTATUS status;
1870 char fname[256];
1871 bool ret = true;
1875 snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
1877 /* Clean slate */
1878 smb2_util_unlink(tree, fname);
1880 /* Create with oplock */
1882 smb2_oplock_create_share(&io, fname,
1883 smb2_util_share_access(""),
1884 smb2_util_oplock_level("b"));
1885 io.in.durable_open = true;
1887 status = smb2_create(tree, mem_ctx, &io);
1888 CHECK_STATUS(status, NT_STATUS_OK);
1889 h = io.out.file.handle;
1890 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1892 CHECK_VAL(io.out.durable_open, true);
1893 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1895 ZERO_STRUCT(lck);
1896 ZERO_STRUCT(el);
1897 lck.in.locks = el;
1898 lck.in.lock_count = 0x0001;
1899 lck.in.lock_sequence = 0x00000000;
1900 lck.in.file.handle = h;
1901 el[0].offset = 0;
1902 el[0].length = 1;
1903 el[0].reserved = 0x00000000;
1904 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
1905 status = smb2_lock(tree, &lck);
1906 CHECK_STATUS(status, NT_STATUS_OK);
1908 /* Disconnect/Reconnect. */
1909 talloc_free(tree);
1910 tree = NULL;
1912 if (!torture_smb2_connection(tctx, &tree)) {
1913 torture_warning(tctx, "couldn't reconnect, bailing\n");
1914 ret = false;
1915 goto done;
1918 ZERO_STRUCT(io);
1919 io.in.fname = fname;
1920 io.in.durable_handle = &h;
1922 status = smb2_create(tree, mem_ctx, &io);
1923 CHECK_STATUS(status, NT_STATUS_OK);
1924 h = io.out.file.handle;
1926 lck.in.file.handle = h;
1927 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
1928 status = smb2_lock(tree, &lck);
1929 CHECK_STATUS(status, NT_STATUS_OK);
1931 done:
1932 smb2_util_close(tree, h);
1933 smb2_util_unlink(tree, fname);
1934 talloc_free(tree);
1936 return ret;
1940 Open, take BRL, disconnect, reconnect.
1942 static bool test_durable_open_lock_lease(struct torture_context *tctx,
1943 struct smb2_tree *tree)
1945 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1946 struct smb2_create io;
1947 struct smb2_lease ls;
1948 struct smb2_handle h;
1949 struct smb2_lock lck;
1950 struct smb2_lock_element el[2];
1951 NTSTATUS status;
1952 char fname[256];
1953 bool ret = true;
1954 uint64_t lease;
1955 uint32_t caps;
1956 struct smbcli_options options;
1958 options = tree->session->transport->options;
1960 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1961 if (!(caps & SMB2_CAP_LEASING)) {
1962 torture_skip(tctx, "leases are not supported");
1966 * Choose a random name and random lease in case the state is left a
1967 * little funky.
1969 lease = random();
1970 snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
1972 /* Clean slate */
1973 smb2_util_unlink(tree, fname);
1975 /* Create with lease */
1977 smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
1978 smb2_util_lease_state("RWH"));
1979 io.in.durable_open = true;
1981 status = smb2_create(tree, mem_ctx, &io);
1982 CHECK_STATUS(status, NT_STATUS_OK);
1983 h = io.out.file.handle;
1984 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1986 CHECK_VAL(io.out.durable_open, true);
1987 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1988 CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
1989 CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
1990 CHECK_VAL(io.out.lease_response.lease_state,
1991 SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
1993 ZERO_STRUCT(lck);
1994 ZERO_STRUCT(el);
1995 lck.in.locks = el;
1996 lck.in.lock_count = 0x0001;
1997 lck.in.lock_sequence = 0x00000000;
1998 lck.in.file.handle = h;
1999 el[0].offset = 0;
2000 el[0].length = 1;
2001 el[0].reserved = 0x00000000;
2002 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2003 status = smb2_lock(tree, &lck);
2004 CHECK_STATUS(status, NT_STATUS_OK);
2006 /* Disconnect/Reconnect. */
2007 talloc_free(tree);
2008 tree = NULL;
2010 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2011 torture_warning(tctx, "couldn't reconnect, bailing\n");
2012 ret = false;
2013 goto done;
2016 ZERO_STRUCT(io);
2017 io.in.fname = fname;
2018 io.in.durable_handle = &h;
2019 io.in.lease_request = &ls;
2021 status = smb2_create(tree, mem_ctx, &io);
2022 CHECK_STATUS(status, NT_STATUS_OK);
2023 h = io.out.file.handle;
2025 lck.in.file.handle = h;
2026 el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2027 status = smb2_lock(tree, &lck);
2028 CHECK_STATUS(status, NT_STATUS_OK);
2030 done:
2031 smb2_util_close(tree, h);
2032 smb2_util_unlink(tree, fname);
2033 talloc_free(tree);
2035 return ret;
2039 * Open with a RH lease, disconnect, open in another tree, reconnect.
2041 * This test actually demonstrates a minimum level of respect for the durable
2042 * open in the face of another open. As long as this test shows an inability to
2043 * reconnect after an open, the oplock/lease tests above will certainly
2044 * demonstrate an error on reconnect.
2046 static bool test_durable_open_open2_lease(struct torture_context *tctx,
2047 struct smb2_tree *tree1,
2048 struct smb2_tree *tree2)
2050 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2051 struct smb2_create io1, io2;
2052 struct smb2_lease ls;
2053 struct smb2_handle h1, h2;
2054 NTSTATUS status;
2055 char fname[256];
2056 bool ret = true;
2057 uint64_t lease;
2058 uint32_t caps;
2059 struct smbcli_options options;
2061 options = tree1->session->transport->options;
2063 caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2064 if (!(caps & SMB2_CAP_LEASING)) {
2065 torture_skip(tctx, "leases are not supported");
2069 * Choose a random name and random lease in case the state is left a
2070 * little funky.
2072 lease = random();
2073 snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2074 generate_random_str(tctx, 8));
2076 /* Clean slate */
2077 smb2_util_unlink(tree1, fname);
2079 /* Create with lease */
2080 smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2081 smb2_util_share_access(""),
2082 lease,
2083 smb2_util_lease_state("RH"));
2084 io1.in.durable_open = true;
2086 status = smb2_create(tree1, mem_ctx, &io1);
2087 CHECK_STATUS(status, NT_STATUS_OK);
2088 h1 = io1.out.file.handle;
2089 CHECK_VAL(io1.out.durable_open, true);
2090 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2092 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2093 CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2094 CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2095 CHECK_VAL(io1.out.lease_response.lease_state,
2096 smb2_util_lease_state("RH"));
2098 /* Disconnect */
2099 talloc_free(tree1);
2100 tree1 = NULL;
2102 /* Open the file in tree2 */
2103 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2105 status = smb2_create(tree2, mem_ctx, &io2);
2106 CHECK_STATUS(status, NT_STATUS_OK);
2107 h2 = io2.out.file.handle;
2108 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2110 /* Reconnect */
2111 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2112 torture_warning(tctx, "couldn't reconnect, bailing\n");
2113 ret = false;
2114 goto done;
2117 ZERO_STRUCT(io1);
2118 io1.in.fname = fname;
2119 io1.in.durable_handle = &h1;
2120 io1.in.lease_request = &ls;
2123 * Windows7 (build 7000) will give away an open immediately if the
2124 * original client is gone. (ZML: This seems like a bug. It should give
2125 * some time for the client to reconnect!)
2127 status = smb2_create(tree1, mem_ctx, &io1);
2128 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2129 h1 = io1.out.file.handle;
2131 done:
2132 smb2_util_close(tree2, h2);
2133 smb2_util_unlink(tree2, fname);
2134 smb2_util_close(tree1, h1);
2135 smb2_util_unlink(tree1, fname);
2137 talloc_free(tree1);
2138 talloc_free(tree2);
2140 return ret;
2144 * Open with a batch oplock, disconnect, open in another tree, reconnect.
2146 * This test actually demonstrates a minimum level of respect for the durable
2147 * open in the face of another open. As long as this test shows an inability to
2148 * reconnect after an open, the oplock/lease tests above will certainly
2149 * demonstrate an error on reconnect.
2151 static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2152 struct smb2_tree *tree1,
2153 struct smb2_tree *tree2)
2155 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2156 struct smb2_create io1, io2;
2157 struct smb2_handle h1, h2;
2158 NTSTATUS status;
2159 char fname[256];
2160 bool ret = true;
2163 * Choose a random name and random lease in case the state is left a
2164 * little funky.
2166 snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2167 generate_random_str(tctx, 8));
2169 /* Clean slate */
2170 smb2_util_unlink(tree1, fname);
2172 /* Create with batch oplock */
2173 smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2174 io1.in.durable_open = true;
2176 status = smb2_create(tree1, mem_ctx, &io1);
2177 CHECK_STATUS(status, NT_STATUS_OK);
2178 h1 = io1.out.file.handle;
2179 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2180 CHECK_VAL(io1.out.durable_open, true);
2181 CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2183 /* Disconnect */
2184 talloc_free(tree1);
2185 tree1 = NULL;
2187 /* Open the file in tree2 */
2188 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2190 status = smb2_create(tree2, mem_ctx, &io2);
2191 CHECK_STATUS(status, NT_STATUS_OK);
2192 h2 = io2.out.file.handle;
2193 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2195 /* Reconnect */
2196 if (!torture_smb2_connection(tctx, &tree1)) {
2197 torture_warning(tctx, "couldn't reconnect, bailing\n");
2198 ret = false;
2199 goto done;
2202 ZERO_STRUCT(io1);
2203 io1.in.fname = fname;
2204 io1.in.durable_handle = &h1;
2206 status = smb2_create(tree1, mem_ctx, &io1);
2207 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2208 h1 = io1.out.file.handle;
2210 done:
2211 smb2_util_close(tree2, h2);
2212 smb2_util_unlink(tree2, fname);
2213 if (tree1 != NULL) {
2214 smb2_util_close(tree1, h1);
2215 smb2_util_unlink(tree1, fname);
2218 talloc_free(tree1);
2219 talloc_free(tree2);
2221 return ret;
2225 * test behaviour with initial allocation size
2227 static bool test_durable_open_alloc_size(struct torture_context *tctx,
2228 struct smb2_tree *tree)
2230 NTSTATUS status;
2231 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2232 char fname[256];
2233 struct smb2_handle _h;
2234 struct smb2_handle *h = NULL;
2235 struct smb2_create io;
2236 bool ret = true;
2237 uint64_t previous_session_id;
2238 uint64_t alloc_size_step;
2239 uint64_t initial_alloc_size = 0x100;
2240 const uint8_t *b = NULL;
2241 struct smbcli_options options;
2243 options = tree->session->transport->options;
2245 /* Choose a random name in case the state is left a little funky. */
2246 snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2247 generate_random_str(tctx, 8));
2249 smb2_util_unlink(tree, fname);
2251 smb2_oplock_create_share(&io, fname,
2252 smb2_util_share_access(""),
2253 smb2_util_oplock_level("b"));
2254 io.in.durable_open = true;
2255 io.in.alloc_size = initial_alloc_size;
2257 status = smb2_create(tree, mem_ctx, &io);
2258 CHECK_STATUS(status, NT_STATUS_OK);
2259 _h = io.out.file.handle;
2260 h = &_h;
2261 CHECK_NOT_VAL(io.out.alloc_size, 0);
2262 alloc_size_step = io.out.alloc_size;
2263 CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2264 alloc_size_step, 0);
2265 CHECK_VAL(io.out.durable_open, true);
2266 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2268 /* prepare buffer */
2269 b = talloc_zero_size(mem_ctx, alloc_size_step);
2270 CHECK_NOT_VAL(b, NULL);
2272 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2274 /* disconnect, reconnect and then do durable reopen */
2275 talloc_free(tree);
2276 tree = NULL;
2278 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2279 &options, &tree))
2281 torture_warning(tctx, "couldn't reconnect, bailing\n");
2282 ret = false;
2283 goto done;
2286 ZERO_STRUCT(io);
2287 io.in.fname = fname;
2288 io.in.durable_handle = h;
2289 h = NULL;
2291 status = smb2_create(tree, mem_ctx, &io);
2292 CHECK_STATUS(status, NT_STATUS_OK);
2293 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2294 alloc_size_step, 0);
2295 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2296 _h = io.out.file.handle;
2297 h = &_h;
2299 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2301 /* write one byte */
2302 status = smb2_util_write(tree, *h, b, 0, 1);
2303 CHECK_STATUS(status, NT_STATUS_OK);
2305 /* disconnect, reconnect and then do durable reopen */
2306 talloc_free(tree);
2307 tree = NULL;
2309 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2310 &options, &tree))
2312 torture_warning(tctx, "couldn't reconnect, bailing\n");
2313 ret = false;
2314 goto done;
2317 ZERO_STRUCT(io);
2318 io.in.fname = fname;
2319 io.in.durable_handle = h;
2320 h = NULL;
2322 status = smb2_create(tree, mem_ctx, &io);
2323 CHECK_STATUS(status, NT_STATUS_OK);
2324 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2325 alloc_size_step, 1);
2326 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2327 _h = io.out.file.handle;
2328 h = &_h;
2330 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2332 /* write more byte than initial allocation size */
2333 status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2335 /* disconnect, reconnect and then do durable reopen */
2336 talloc_free(tree);
2337 tree = NULL;
2339 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2340 &options, &tree))
2342 torture_warning(tctx, "couldn't reconnect, bailing\n");
2343 ret = false;
2344 goto done;
2347 ZERO_STRUCT(io);
2348 io.in.fname = fname;
2349 io.in.durable_handle = h;
2350 h = NULL;
2352 status = smb2_create(tree, mem_ctx, &io);
2353 CHECK_STATUS(status, NT_STATUS_OK);
2354 CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2355 alloc_size_step * 2, alloc_size_step + 1);
2356 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2357 _h = io.out.file.handle;
2358 h = &_h;
2360 done:
2361 if (h != NULL) {
2362 smb2_util_close(tree, *h);
2365 smb2_util_unlink(tree, fname);
2367 talloc_free(tree);
2369 talloc_free(mem_ctx);
2371 return ret;
2375 * test behaviour when a disconnect happens while creating a read-only file
2377 static bool test_durable_open_read_only(struct torture_context *tctx,
2378 struct smb2_tree *tree)
2380 NTSTATUS status;
2381 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2382 char fname[256];
2383 struct smb2_handle _h;
2384 struct smb2_handle *h = NULL;
2385 struct smb2_create io;
2386 bool ret = true;
2387 uint64_t previous_session_id;
2388 const uint8_t b = 0;
2389 uint64_t alloc_size = 0;
2390 struct smbcli_options options;
2392 options = tree->session->transport->options;
2394 /* Choose a random name in case the state is left a little funky. */
2395 snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2396 generate_random_str(tctx, 8));
2398 smb2_util_unlink(tree, fname);
2400 smb2_oplock_create_share(&io, fname,
2401 smb2_util_share_access(""),
2402 smb2_util_oplock_level("b"));
2403 io.in.durable_open = true;
2404 io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2406 status = smb2_create(tree, mem_ctx, &io);
2407 CHECK_STATUS(status, NT_STATUS_OK);
2408 _h = io.out.file.handle;
2409 h = &_h;
2410 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2411 CHECK_VAL(io.out.durable_open, true);
2412 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2414 previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2416 /* write one byte */
2417 status = smb2_util_write(tree, *h, &b, 0, 1);
2418 CHECK_STATUS(status, NT_STATUS_OK);
2420 /* disconnect, reconnect and then do durable reopen */
2421 talloc_free(tree);
2422 tree = NULL;
2424 if (!torture_smb2_connection_ext(tctx, previous_session_id,
2425 &options, &tree))
2427 torture_warning(tctx, "couldn't reconnect, bailing\n");
2428 ret = false;
2429 goto done;
2432 ZERO_STRUCT(io);
2433 io.in.fname = fname;
2434 io.in.durable_handle = h;
2435 h = NULL;
2437 status = smb2_create(tree, mem_ctx, &io);
2438 CHECK_STATUS(status, NT_STATUS_OK);
2439 alloc_size = io.out.alloc_size;
2440 CHECK_CREATED_SIZE(&io, EXISTED,
2441 FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2442 alloc_size, 1);
2443 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2444 _h = io.out.file.handle;
2445 h = &_h;
2447 /* write one byte */
2448 status = smb2_util_write(tree, *h, &b, 1, 1);
2449 CHECK_STATUS(status, NT_STATUS_OK);
2451 done:
2452 if (h != NULL) {
2453 union smb_setfileinfo sfinfo;
2455 ZERO_STRUCT(sfinfo);
2456 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2457 sfinfo.basic_info.in.file.handle = *h;
2458 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2459 smb2_setinfo_file(tree, &sfinfo);
2461 smb2_util_close(tree, *h);
2464 smb2_util_unlink(tree, fname);
2466 talloc_free(tree);
2468 talloc_free(mem_ctx);
2470 return ret;
2474 * durable open with oplock, disconnect, exit
2476 static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2477 struct smb2_tree *tree)
2479 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2480 struct smb2_create io;
2481 struct smb2_handle _h;
2482 struct smb2_handle *h = NULL;
2483 NTSTATUS status;
2484 char fname[256];
2485 bool ret = true;
2487 snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2488 generate_random_str(mem_ctx, 8));
2490 smb2_util_unlink(tree, fname);
2492 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2493 io.in.durable_open = true;
2495 status = smb2_create(tree, mem_ctx, &io);
2496 CHECK_STATUS(status, NT_STATUS_OK);
2498 _h = io.out.file.handle;
2499 h = &_h;
2501 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2502 CHECK_VAL(io.out.durable_open, true);
2503 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2505 /* disconnect */
2506 talloc_free(tree);
2507 tree = NULL;
2509 done:
2510 if (tree != NULL) {
2511 if (h != NULL) {
2512 smb2_util_close(tree, *h);
2514 smb2_util_unlink(tree, fname);
2516 talloc_free(mem_ctx);
2517 return ret;
2521 struct torture_suite *torture_smb2_durable_open_init(void)
2523 struct torture_suite *suite =
2524 torture_suite_create(talloc_autofree_context(), "durable-open");
2526 torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2527 torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2528 torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2529 torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2530 torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2531 torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2532 torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2533 torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2534 torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2535 torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2536 torture_suite_add_1smb2_test(suite, "delete_on_close1",
2537 test_durable_open_delete_on_close1);
2538 torture_suite_add_1smb2_test(suite, "delete_on_close2",
2539 test_durable_open_delete_on_close2);
2540 torture_suite_add_1smb2_test(suite, "file-position",
2541 test_durable_open_file_position);
2542 torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2543 torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2544 torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2545 torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2546 torture_suite_add_2smb2_test(suite, "open2-lease",
2547 test_durable_open_open2_lease);
2548 torture_suite_add_2smb2_test(suite, "open2-oplock",
2549 test_durable_open_open2_oplock);
2550 torture_suite_add_1smb2_test(suite, "alloc-size",
2551 test_durable_open_alloc_size);
2552 torture_suite_add_1smb2_test(suite, "read-only",
2553 test_durable_open_read_only);
2555 suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2557 return suite;
2560 struct torture_suite *torture_smb2_durable_open_disconnect_init(void)
2562 struct torture_suite *suite =
2563 torture_suite_create(talloc_autofree_context(),
2564 "durable-open-disconnect");
2566 torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2567 test_durable_open_oplock_disconnect);
2569 suite->description = talloc_strdup(suite,
2570 "SMB2-DURABLE-OPEN-DISCONNECT tests");
2572 return suite;