Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : test suite for SMB2 durable opens
5 :
6 : Copyright (C) Stefan Metzmacher 2008
7 : Copyright (C) Michael Adam 2011-2012
8 :
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.
13 :
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.
18 :
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/>.
21 : */
22 :
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"
30 :
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)
37 :
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)
44 :
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)
51 :
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)
59 :
60 : #define CHECK_CREATED(__io, __created, __attribute) \
61 : do { \
62 : CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
63 : CHECK_VAL((__io)->out.size, 0); \
64 : CHECK_VAL((__io)->out.file_attr, (__attribute)); \
65 : CHECK_VAL((__io)->out.reserved2, 0); \
66 : } while(0)
67 :
68 : #define CHECK_CREATED_SIZE(__io, __created, __attribute, __alloc_size, __size) \
69 : do { \
70 : CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
71 : if (__alloc_size != 0) { \
72 : CHECK_VAL((__io)->out.alloc_size, (__alloc_size)); \
73 : } \
74 : CHECK_VAL((__io)->out.size, (__size)); \
75 : CHECK_VAL((__io)->out.file_attr, (__attribute)); \
76 : CHECK_VAL((__io)->out.reserved2, 0); \
77 : } while(0)
78 :
79 :
80 :
81 : /**
82 : * basic durable_open test.
83 : * durable state should only be granted when requested
84 : * along with a batch oplock or a handle lease.
85 : *
86 : * This test tests durable open with all possible oplock types.
87 : */
88 :
89 : struct durable_open_vs_oplock {
90 : const char *level;
91 : const char *share_mode;
92 : bool expected;
93 : };
94 :
95 : #define NUM_OPLOCK_TYPES 4
96 : #define NUM_SHARE_MODES 8
97 : #define NUM_OPLOCK_OPEN_TESTS ( NUM_OPLOCK_TYPES * NUM_SHARE_MODES )
98 : static struct durable_open_vs_oplock durable_open_vs_oplock_table[NUM_OPLOCK_OPEN_TESTS] =
99 : {
100 : { "", "", false },
101 : { "", "R", false },
102 : { "", "W", false },
103 : { "", "D", false },
104 : { "", "RD", false },
105 : { "", "RW", false },
106 : { "", "WD", false },
107 : { "", "RWD", false },
108 :
109 : { "s", "", false },
110 : { "s", "R", false },
111 : { "s", "W", false },
112 : { "s", "D", false },
113 : { "s", "RD", false },
114 : { "s", "RW", false },
115 : { "s", "WD", false },
116 : { "s", "RWD", false },
117 :
118 : { "x", "", false },
119 : { "x", "R", false },
120 : { "x", "W", false },
121 : { "x", "D", false },
122 : { "x", "RD", false },
123 : { "x", "RW", false },
124 : { "x", "WD", false },
125 : { "x", "RWD", false },
126 :
127 : { "b", "", true },
128 : { "b", "R", true },
129 : { "b", "W", true },
130 : { "b", "D", true },
131 : { "b", "RD", true },
132 : { "b", "RW", true },
133 : { "b", "WD", true },
134 : { "b", "RWD", true },
135 : };
136 :
137 0 : static bool test_one_durable_open_open_oplock(struct torture_context *tctx,
138 : struct smb2_tree *tree,
139 : const char *fname,
140 : struct durable_open_vs_oplock test)
141 : {
142 : NTSTATUS status;
143 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
144 : struct smb2_handle _h;
145 0 : struct smb2_handle *h = NULL;
146 0 : bool ret = true;
147 : struct smb2_create io;
148 :
149 0 : smb2_util_unlink(tree, fname);
150 :
151 0 : smb2_oplock_create_share(&io, fname,
152 : smb2_util_share_access(test.share_mode),
153 0 : smb2_util_oplock_level(test.level));
154 0 : io.in.durable_open = true;
155 :
156 0 : status = smb2_create(tree, mem_ctx, &io);
157 0 : CHECK_STATUS(status, NT_STATUS_OK);
158 0 : _h = io.out.file.handle;
159 0 : h = &_h;
160 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
161 0 : CHECK_VAL(io.out.durable_open, test.expected);
162 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
163 :
164 0 : done:
165 0 : if (h != NULL) {
166 0 : smb2_util_close(tree, *h);
167 : }
168 0 : smb2_util_unlink(tree, fname);
169 0 : talloc_free(mem_ctx);
170 :
171 0 : return ret;
172 : }
173 :
174 0 : static bool test_durable_open_open_oplock(struct torture_context *tctx,
175 : struct smb2_tree *tree)
176 : {
177 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
178 : char fname[256];
179 0 : bool ret = true;
180 : int i;
181 :
182 : /* Choose a random name in case the state is left a little funky. */
183 0 : snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
184 :
185 0 : smb2_util_unlink(tree, fname);
186 :
187 : /* test various oplock levels with durable open */
188 :
189 0 : for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
190 0 : ret = test_one_durable_open_open_oplock(tctx,
191 : tree,
192 : fname,
193 : durable_open_vs_oplock_table[i]);
194 0 : if (ret == false) {
195 0 : goto done;
196 : }
197 : }
198 :
199 0 : done:
200 0 : smb2_util_unlink(tree, fname);
201 0 : talloc_free(tree);
202 0 : talloc_free(mem_ctx);
203 :
204 0 : return ret;
205 : }
206 :
207 : /**
208 : * basic durable_open test.
209 : * durable state should only be granted when requested
210 : * along with a batch oplock or a handle lease.
211 : *
212 : * This test tests durable open with all valid lease types.
213 : */
214 :
215 : struct durable_open_vs_lease {
216 : const char *type;
217 : const char *share_mode;
218 : bool expected;
219 : };
220 :
221 : #define NUM_LEASE_TYPES 5
222 : #define NUM_LEASE_OPEN_TESTS ( NUM_LEASE_TYPES * NUM_SHARE_MODES )
223 : static struct durable_open_vs_lease durable_open_vs_lease_table[NUM_LEASE_OPEN_TESTS] =
224 : {
225 : { "", "", false },
226 : { "", "R", false },
227 : { "", "W", false },
228 : { "", "D", false },
229 : { "", "RW", false },
230 : { "", "RD", false },
231 : { "", "WD", false },
232 : { "", "RWD", false },
233 :
234 : { "R", "", false },
235 : { "R", "R", false },
236 : { "R", "W", false },
237 : { "R", "D", false },
238 : { "R", "RW", false },
239 : { "R", "RD", false },
240 : { "R", "DW", false },
241 : { "R", "RWD", false },
242 :
243 : { "RW", "", false },
244 : { "RW", "R", false },
245 : { "RW", "W", false },
246 : { "RW", "D", false },
247 : { "RW", "RW", false },
248 : { "RW", "RD", false },
249 : { "RW", "WD", false },
250 : { "RW", "RWD", false },
251 :
252 : { "RH", "", true },
253 : { "RH", "R", true },
254 : { "RH", "W", true },
255 : { "RH", "D", true },
256 : { "RH", "RW", true },
257 : { "RH", "RD", true },
258 : { "RH", "WD", true },
259 : { "RH", "RWD", true },
260 :
261 : { "RHW", "", true },
262 : { "RHW", "R", true },
263 : { "RHW", "W", true },
264 : { "RHW", "D", true },
265 : { "RHW", "RW", true },
266 : { "RHW", "RD", true },
267 : { "RHW", "WD", true },
268 : { "RHW", "RWD", true },
269 : };
270 :
271 0 : static bool test_one_durable_open_open_lease(struct torture_context *tctx,
272 : struct smb2_tree *tree,
273 : const char *fname,
274 : struct durable_open_vs_lease test)
275 : {
276 : NTSTATUS status;
277 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
278 : struct smb2_handle _h;
279 0 : struct smb2_handle *h = NULL;
280 0 : bool ret = true;
281 : struct smb2_create io;
282 : struct smb2_lease ls;
283 : uint64_t lease;
284 : uint32_t caps;
285 :
286 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
287 0 : if (!(caps & SMB2_CAP_LEASING)) {
288 0 : torture_skip(tctx, "leases are not supported");
289 : }
290 :
291 0 : smb2_util_unlink(tree, fname);
292 :
293 0 : lease = random();
294 :
295 0 : smb2_lease_create_share(&io, &ls, false /* dir */, fname,
296 : smb2_util_share_access(test.share_mode),
297 : lease,
298 : smb2_util_lease_state(test.type));
299 0 : io.in.durable_open = true;
300 :
301 0 : status = smb2_create(tree, mem_ctx, &io);
302 0 : CHECK_STATUS(status, NT_STATUS_OK);
303 0 : _h = io.out.file.handle;
304 0 : h = &_h;
305 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
306 0 : CHECK_VAL(io.out.durable_open, test.expected);
307 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
308 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
309 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
310 0 : CHECK_VAL(io.out.lease_response.lease_state,
311 : smb2_util_lease_state(test.type));
312 0 : done:
313 0 : if (h != NULL) {
314 0 : smb2_util_close(tree, *h);
315 : }
316 0 : smb2_util_unlink(tree, fname);
317 0 : talloc_free(mem_ctx);
318 :
319 0 : return ret;
320 : }
321 :
322 0 : static bool test_durable_open_open_lease(struct torture_context *tctx,
323 : struct smb2_tree *tree)
324 : {
325 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
326 : char fname[256];
327 0 : bool ret = true;
328 : int i;
329 : uint32_t caps;
330 :
331 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
332 0 : if (!(caps & SMB2_CAP_LEASING)) {
333 0 : torture_skip(tctx, "leases are not supported");
334 : }
335 :
336 : /* Choose a random name in case the state is left a little funky. */
337 0 : snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
338 :
339 0 : smb2_util_unlink(tree, fname);
340 :
341 :
342 : /* test various oplock levels with durable open */
343 :
344 0 : for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
345 0 : ret = test_one_durable_open_open_lease(tctx,
346 : tree,
347 : fname,
348 : durable_open_vs_lease_table[i]);
349 0 : if (ret == false) {
350 0 : goto done;
351 : }
352 : }
353 :
354 0 : done:
355 0 : smb2_util_unlink(tree, fname);
356 0 : talloc_free(tree);
357 0 : talloc_free(mem_ctx);
358 :
359 0 : return ret;
360 : }
361 :
362 : /**
363 : * basic test for doing a durable open
364 : * and do a durable reopen on the same connection
365 : * while the first open is still active (fails)
366 : */
367 0 : static bool test_durable_open_reopen1(struct torture_context *tctx,
368 : struct smb2_tree *tree)
369 : {
370 : NTSTATUS status;
371 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
372 : char fname[256];
373 : struct smb2_handle _h;
374 0 : struct smb2_handle *h = NULL;
375 : struct smb2_create io1, io2;
376 0 : bool ret = true;
377 :
378 : /* Choose a random name in case the state is left a little funky. */
379 0 : snprintf(fname, 256, "durable_open_reopen1_%s.dat",
380 : generate_random_str(tctx, 8));
381 :
382 0 : smb2_util_unlink(tree, fname);
383 :
384 0 : smb2_oplock_create_share(&io1, fname,
385 : smb2_util_share_access(""),
386 0 : smb2_util_oplock_level("b"));
387 0 : io1.in.durable_open = true;
388 :
389 0 : status = smb2_create(tree, mem_ctx, &io1);
390 0 : CHECK_STATUS(status, NT_STATUS_OK);
391 0 : _h = io1.out.file.handle;
392 0 : h = &_h;
393 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
394 0 : CHECK_VAL(io1.out.durable_open, true);
395 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
396 :
397 : /* try a durable reconnect while the file is still open */
398 0 : ZERO_STRUCT(io2);
399 0 : io2.in.fname = fname;
400 0 : io2.in.durable_handle = h;
401 :
402 0 : status = smb2_create(tree, mem_ctx, &io2);
403 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
404 :
405 0 : done:
406 0 : if (h != NULL) {
407 0 : smb2_util_close(tree, *h);
408 : }
409 :
410 0 : smb2_util_unlink(tree, fname);
411 :
412 0 : talloc_free(tree);
413 :
414 0 : talloc_free(mem_ctx);
415 :
416 0 : return ret;
417 : }
418 :
419 : /**
420 : * Basic test for doing a durable open
421 : * and do a session reconnect while the first
422 : * session is still active and the handle is
423 : * still open in the client.
424 : * This closes the original session and a
425 : * durable reconnect on the new session succeeds.
426 : */
427 0 : static bool test_durable_open_reopen1a(struct torture_context *tctx,
428 : struct smb2_tree *tree)
429 : {
430 : NTSTATUS status;
431 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
432 : char fname[256];
433 : struct smb2_handle _h;
434 0 : struct smb2_handle *h = NULL;
435 : struct smb2_create io;
436 0 : bool ret = true;
437 0 : struct smb2_tree *tree2 = NULL;
438 0 : struct smb2_tree *tree3 = NULL;
439 : uint64_t previous_session_id;
440 : struct smbcli_options options;
441 : struct GUID orig_client_guid;
442 :
443 0 : options = tree->session->transport->options;
444 0 : orig_client_guid = options.client_guid;
445 :
446 : /* Choose a random name in case the state is left a little funky. */
447 0 : snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
448 : generate_random_str(tctx, 8));
449 :
450 0 : smb2_util_unlink(tree, fname);
451 :
452 0 : smb2_oplock_create_share(&io, fname,
453 : smb2_util_share_access(""),
454 0 : smb2_util_oplock_level("b"));
455 0 : io.in.durable_open = true;
456 :
457 0 : status = smb2_create(tree, mem_ctx, &io);
458 0 : CHECK_STATUS(status, NT_STATUS_OK);
459 0 : _h = io.out.file.handle;
460 0 : h = &_h;
461 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
462 0 : CHECK_VAL(io.out.durable_open, true);
463 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
464 :
465 : /*
466 : * a session reconnect on a second tcp connection
467 : */
468 :
469 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
470 :
471 : /* for oplocks, the client guid can be different: */
472 0 : options.client_guid = GUID_random();
473 :
474 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
475 : &options, &tree2);
476 0 : torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
477 :
478 : /*
479 : * check that this has deleted the old session
480 : */
481 :
482 0 : ZERO_STRUCT(io);
483 0 : io.in.fname = fname;
484 0 : io.in.durable_handle = h;
485 :
486 0 : status = smb2_create(tree, mem_ctx, &io);
487 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
488 :
489 0 : TALLOC_FREE(tree);
490 :
491 : /*
492 : * but a durable reconnect on the new session succeeds:
493 : */
494 :
495 0 : ZERO_STRUCT(io);
496 0 : io.in.fname = fname;
497 0 : io.in.durable_handle = h;
498 :
499 0 : status = smb2_create(tree2, mem_ctx, &io);
500 0 : CHECK_STATUS(status, NT_STATUS_OK);
501 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
502 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
503 0 : _h = io.out.file.handle;
504 0 : h = &_h;
505 :
506 : /*
507 : * a session reconnect on a second tcp connection
508 : */
509 :
510 0 : previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
511 :
512 : /* the original client_guid works just the same */
513 0 : options.client_guid = orig_client_guid;
514 :
515 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
516 : &options, &tree3);
517 0 : torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
518 :
519 : /*
520 : * check that this has deleted the old session
521 : */
522 :
523 0 : ZERO_STRUCT(io);
524 0 : io.in.fname = fname;
525 0 : io.in.durable_handle = h;
526 :
527 0 : status = smb2_create(tree2, mem_ctx, &io);
528 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
529 :
530 0 : TALLOC_FREE(tree2);
531 :
532 : /*
533 : * but a durable reconnect on the new session succeeds:
534 : */
535 :
536 0 : ZERO_STRUCT(io);
537 0 : io.in.fname = fname;
538 0 : io.in.durable_handle = h;
539 :
540 0 : status = smb2_create(tree3, mem_ctx, &io);
541 0 : CHECK_STATUS(status, NT_STATUS_OK);
542 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
543 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
544 0 : _h = io.out.file.handle;
545 0 : h = &_h;
546 :
547 0 : done:
548 0 : if (tree == NULL) {
549 0 : tree = tree2;
550 : }
551 :
552 0 : if (tree == NULL) {
553 0 : tree = tree3;
554 : }
555 :
556 0 : if (tree != NULL) {
557 0 : if (h != NULL) {
558 0 : smb2_util_close(tree, *h);
559 0 : h = NULL;
560 : }
561 0 : smb2_util_unlink(tree, fname);
562 :
563 0 : talloc_free(tree);
564 : }
565 :
566 0 : talloc_free(mem_ctx);
567 :
568 0 : return ret;
569 : }
570 :
571 : /**
572 : * lease variant of reopen1a
573 : *
574 : * Basic test for doing a durable open and doing a session
575 : * reconnect while the first session is still active and the
576 : * handle is still open in the client.
577 : * This closes the original session and a durable reconnect on
578 : * the new session succeeds depending on the client guid:
579 : *
580 : * Durable reconnect on a session with a different client guid fails.
581 : * Durable reconnect on a session with the original client guid succeeds.
582 : */
583 0 : bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
584 : struct smb2_tree *tree)
585 : {
586 : NTSTATUS status;
587 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
588 : char fname[256];
589 : struct smb2_handle _h;
590 0 : struct smb2_handle *h = NULL;
591 : struct smb2_create io;
592 : struct smb2_lease ls;
593 : uint64_t lease_key;
594 0 : bool ret = true;
595 0 : struct smb2_tree *tree2 = NULL;
596 0 : struct smb2_tree *tree3 = NULL;
597 : uint64_t previous_session_id;
598 : struct smbcli_options options;
599 : struct GUID orig_client_guid;
600 :
601 0 : options = tree->session->transport->options;
602 0 : orig_client_guid = options.client_guid;
603 :
604 : /* Choose a random name in case the state is left a little funky. */
605 0 : snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
606 : generate_random_str(tctx, 8));
607 :
608 0 : smb2_util_unlink(tree, fname);
609 :
610 0 : lease_key = random();
611 0 : smb2_lease_create(&io, &ls, false /* dir */, fname,
612 : lease_key, smb2_util_lease_state("RWH"));
613 0 : io.in.durable_open = true;
614 :
615 0 : status = smb2_create(tree, mem_ctx, &io);
616 0 : CHECK_STATUS(status, NT_STATUS_OK);
617 0 : _h = io.out.file.handle;
618 0 : h = &_h;
619 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
620 0 : CHECK_VAL(io.out.durable_open, true);
621 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
622 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
623 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
624 0 : CHECK_VAL(io.out.lease_response.lease_state,
625 : smb2_util_lease_state("RWH"));
626 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
627 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
628 :
629 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
630 :
631 : /*
632 : * a session reconnect on a second tcp connection
633 : * with a different client_guid does not allow
634 : * the durable reconnect.
635 : */
636 :
637 0 : options.client_guid = GUID_random();
638 :
639 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
640 : &options, &tree2);
641 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
642 :
643 : /*
644 : * check that this has deleted the old session
645 : */
646 :
647 0 : ZERO_STRUCT(io);
648 0 : io.in.fname = fname;
649 0 : io.in.durable_handle = h;
650 0 : io.in.lease_request = &ls;
651 0 : status = smb2_create(tree, mem_ctx, &io);
652 0 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
653 0 : TALLOC_FREE(tree);
654 :
655 :
656 : /*
657 : * but a durable reconnect on the new session with the wrong
658 : * client guid fails
659 : */
660 :
661 0 : ZERO_STRUCT(io);
662 0 : io.in.fname = fname;
663 0 : io.in.durable_handle = h;
664 0 : io.in.lease_request = &ls;
665 0 : status = smb2_create(tree2, mem_ctx, &io);
666 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
667 :
668 : /*
669 : * now a session reconnect on a second tcp connection
670 : * with original client_guid allows the durable reconnect.
671 : */
672 :
673 0 : options.client_guid = orig_client_guid;
674 :
675 0 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
676 : &options, &tree3);
677 0 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
678 :
679 : /*
680 : * check that this has deleted the old session
681 : * In this case, a durable reconnect attempt with the
682 : * correct client_guid yields a different error code.
683 : */
684 :
685 0 : ZERO_STRUCT(io);
686 0 : io.in.fname = fname;
687 0 : io.in.durable_handle = h;
688 0 : io.in.lease_request = &ls;
689 0 : status = smb2_create(tree2, mem_ctx, &io);
690 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
691 0 : TALLOC_FREE(tree2);
692 :
693 : /*
694 : * but a durable reconnect on the new session succeeds:
695 : */
696 :
697 0 : ZERO_STRUCT(io);
698 0 : io.in.fname = fname;
699 0 : io.in.durable_handle = h;
700 0 : io.in.lease_request = &ls;
701 0 : status = smb2_create(tree3, mem_ctx, &io);
702 0 : CHECK_STATUS(status, NT_STATUS_OK);
703 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
704 0 : CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
705 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
706 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
707 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
708 0 : CHECK_VAL(io.out.lease_response.lease_state,
709 : smb2_util_lease_state("RWH"));
710 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
711 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
712 0 : _h = io.out.file.handle;
713 0 : h = &_h;
714 :
715 0 : done:
716 0 : if (tree == NULL) {
717 0 : tree = tree2;
718 : }
719 :
720 0 : if (tree == NULL) {
721 0 : tree = tree3;
722 : }
723 :
724 0 : if (tree != NULL) {
725 0 : if (h != NULL) {
726 0 : smb2_util_close(tree, *h);
727 : }
728 :
729 0 : smb2_util_unlink(tree, fname);
730 :
731 0 : talloc_free(tree);
732 : }
733 :
734 0 : talloc_free(mem_ctx);
735 :
736 0 : return ret;
737 : }
738 :
739 :
740 : /**
741 : * basic test for doing a durable open
742 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
743 : */
744 0 : static bool test_durable_open_reopen2(struct torture_context *tctx,
745 : struct smb2_tree *tree)
746 : {
747 : NTSTATUS status;
748 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
749 : char fname[256];
750 : struct smb2_handle _h;
751 0 : struct smb2_handle *h = NULL;
752 : struct smb2_create io;
753 0 : bool ret = true;
754 :
755 : /* Choose a random name in case the state is left a little funky. */
756 0 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
757 : generate_random_str(tctx, 8));
758 :
759 0 : smb2_util_unlink(tree, fname);
760 :
761 0 : smb2_oplock_create_share(&io, fname,
762 : smb2_util_share_access(""),
763 0 : smb2_util_oplock_level("b"));
764 0 : io.in.durable_open = true;
765 :
766 0 : status = smb2_create(tree, mem_ctx, &io);
767 0 : CHECK_STATUS(status, NT_STATUS_OK);
768 0 : _h = io.out.file.handle;
769 0 : h = &_h;
770 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
771 0 : CHECK_VAL(io.out.durable_open, true);
772 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
773 :
774 : /* disconnect, leaving the durable in place */
775 0 : TALLOC_FREE(tree);
776 :
777 0 : if (!torture_smb2_connection(tctx, &tree)) {
778 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
779 0 : ret = false;
780 0 : goto done;
781 : }
782 :
783 0 : ZERO_STRUCT(io);
784 : /* the path name is ignored by the server */
785 0 : io.in.fname = fname;
786 0 : io.in.durable_handle = h; /* durable v1 reconnect request */
787 0 : h = NULL;
788 :
789 0 : status = smb2_create(tree, mem_ctx, &io);
790 0 : CHECK_STATUS(status, NT_STATUS_OK);
791 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
792 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
793 0 : _h = io.out.file.handle;
794 0 : h = &_h;
795 :
796 : /* disconnect again, leaving the durable in place */
797 0 : TALLOC_FREE(tree);
798 :
799 0 : if (!torture_smb2_connection(tctx, &tree)) {
800 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
801 0 : ret = false;
802 0 : goto done;
803 : }
804 :
805 : /*
806 : * show that the filename and many other fields
807 : * are ignored. only the reconnect request blob
808 : * is important.
809 : */
810 0 : ZERO_STRUCT(io);
811 : /* the path name is ignored by the server */
812 0 : io.in.security_flags = 0x78;
813 0 : io.in.oplock_level = 0x78;
814 0 : io.in.impersonation_level = 0x12345678;
815 0 : io.in.create_flags = 0x12345678;
816 0 : io.in.reserved = 0x12345678;
817 0 : io.in.desired_access = 0x12345678;
818 0 : io.in.file_attributes = 0x12345678;
819 0 : io.in.share_access = 0x12345678;
820 0 : io.in.create_disposition = 0x12345678;
821 0 : io.in.create_options = 0x12345678;
822 0 : io.in.fname = "__non_existing_fname__";
823 0 : io.in.durable_handle = h; /* durable v1 reconnect request */
824 0 : h = NULL;
825 :
826 0 : status = smb2_create(tree, mem_ctx, &io);
827 0 : CHECK_STATUS(status, NT_STATUS_OK);
828 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
829 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
830 0 : _h = io.out.file.handle;
831 0 : h = &_h;
832 :
833 : /* disconnect, leaving the durable in place */
834 0 : TALLOC_FREE(tree);
835 :
836 0 : if (!torture_smb2_connection(tctx, &tree)) {
837 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
838 0 : ret = false;
839 0 : goto done;
840 : }
841 :
842 : /*
843 : * show that an additionally specified durable v1 request
844 : * is ignored by the server.
845 : * See MS-SMB2, 3.3.5.9.7
846 : * Handling the SMB2_CREATE_DURABLE_HANDLE_RECONNECT Create Context
847 : */
848 0 : ZERO_STRUCT(io);
849 : /* the path name is ignored by the server */
850 0 : io.in.fname = fname;
851 0 : io.in.durable_handle = h; /* durable v1 reconnect request */
852 0 : io.in.durable_open = true; /* durable v1 handle request */
853 0 : h = NULL;
854 :
855 0 : status = smb2_create(tree, mem_ctx, &io);
856 0 : CHECK_STATUS(status, NT_STATUS_OK);
857 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
858 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
859 0 : _h = io.out.file.handle;
860 0 : h = &_h;
861 :
862 0 : done:
863 0 : if (tree != NULL) {
864 0 : if (h != NULL) {
865 0 : smb2_util_close(tree, *h);
866 : }
867 :
868 0 : smb2_util_unlink(tree, fname);
869 :
870 0 : talloc_free(tree);
871 : }
872 :
873 0 : talloc_free(mem_ctx);
874 :
875 0 : return ret;
876 : }
877 :
878 : /**
879 : * lease variant of reopen2
880 : * basic test for doing a durable open
881 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
882 : */
883 0 : static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
884 : struct smb2_tree *tree)
885 : {
886 : NTSTATUS status;
887 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
888 : char fname[256];
889 : struct smb2_handle _h;
890 0 : struct smb2_handle *h = NULL;
891 : struct smb2_create io;
892 : struct smb2_lease ls;
893 : uint64_t lease_key;
894 0 : bool ret = true;
895 : struct smbcli_options options;
896 : uint32_t caps;
897 :
898 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
899 0 : if (!(caps & SMB2_CAP_LEASING)) {
900 0 : torture_skip(tctx, "leases are not supported");
901 : }
902 :
903 0 : options = tree->session->transport->options;
904 :
905 : /* Choose a random name in case the state is left a little funky. */
906 0 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
907 : generate_random_str(tctx, 8));
908 :
909 0 : smb2_util_unlink(tree, fname);
910 :
911 0 : lease_key = random();
912 0 : smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
913 : smb2_util_lease_state("RWH"));
914 0 : io.in.durable_open = true;
915 :
916 0 : status = smb2_create(tree, mem_ctx, &io);
917 0 : CHECK_STATUS(status, NT_STATUS_OK);
918 0 : _h = io.out.file.handle;
919 0 : h = &_h;
920 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
921 :
922 0 : CHECK_VAL(io.out.durable_open, true);
923 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
924 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
925 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
926 0 : CHECK_VAL(io.out.lease_response.lease_state,
927 : smb2_util_lease_state("RWH"));
928 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
929 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
930 :
931 : /* disconnect, reconnect and then do durable reopen */
932 0 : TALLOC_FREE(tree);
933 :
934 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
935 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
936 0 : ret = false;
937 0 : goto done;
938 : }
939 :
940 :
941 : /* a few failure tests: */
942 :
943 : /*
944 : * several attempts without lease attached:
945 : * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
946 : * irrespective of file name provided
947 : */
948 :
949 0 : ZERO_STRUCT(io);
950 0 : io.in.fname = "";
951 0 : io.in.durable_handle = h;
952 0 : status = smb2_create(tree, mem_ctx, &io);
953 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
954 :
955 0 : ZERO_STRUCT(io);
956 0 : io.in.fname = "__non_existing_fname__";
957 0 : io.in.durable_handle = h;
958 0 : status = smb2_create(tree, mem_ctx, &io);
959 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
960 :
961 0 : ZERO_STRUCT(io);
962 0 : io.in.fname = fname;
963 0 : io.in.durable_handle = h;
964 0 : status = smb2_create(tree, mem_ctx, &io);
965 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
966 :
967 : /*
968 : * attempt with lease provided, but
969 : * with a changed lease key. => fails
970 : */
971 0 : ZERO_STRUCT(io);
972 0 : io.in.fname = fname;
973 0 : io.in.durable_open = false;
974 0 : io.in.durable_handle = h;
975 0 : io.in.lease_request = &ls;
976 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
977 : /* a wrong lease key lets the request fail */
978 0 : ls.lease_key.data[0]++;
979 :
980 0 : status = smb2_create(tree, mem_ctx, &io);
981 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
982 :
983 : /* restore the correct lease key */
984 0 : ls.lease_key.data[0]--;
985 :
986 : /*
987 : * this last failing attempt is almost correct:
988 : * only problem is: we use the wrong filename...
989 : * Note that this gives INVALID_PARAMETER.
990 : * This is different from oplocks!
991 : */
992 0 : ZERO_STRUCT(io);
993 0 : io.in.fname = "__non_existing_fname__";
994 0 : io.in.durable_open = false;
995 0 : io.in.durable_handle = h;
996 0 : io.in.lease_request = &ls;
997 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
998 :
999 0 : status = smb2_create(tree, mem_ctx, &io);
1000 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1001 :
1002 : /*
1003 : * Now for a succeeding reconnect:
1004 : */
1005 :
1006 0 : ZERO_STRUCT(io);
1007 0 : io.in.fname = fname;
1008 0 : io.in.durable_open = false;
1009 0 : io.in.durable_handle = h;
1010 0 : io.in.lease_request = &ls;
1011 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1012 :
1013 : /* the requested lease state is irrelevant */
1014 0 : ls.lease_state = smb2_util_lease_state("");
1015 :
1016 0 : h = NULL;
1017 :
1018 0 : status = smb2_create(tree, mem_ctx, &io);
1019 0 : CHECK_STATUS(status, NT_STATUS_OK);
1020 :
1021 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1022 0 : CHECK_VAL(io.out.durable_open, false);
1023 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1024 0 : CHECK_VAL(io.out.persistent_open, false);
1025 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1026 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1027 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1028 0 : CHECK_VAL(io.out.lease_response.lease_state,
1029 : smb2_util_lease_state("RWH"));
1030 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1031 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1032 0 : _h = io.out.file.handle;
1033 0 : h = &_h;
1034 :
1035 : /* disconnect one more time */
1036 0 : TALLOC_FREE(tree);
1037 :
1038 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1039 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1040 0 : ret = false;
1041 0 : goto done;
1042 : }
1043 :
1044 : /*
1045 : * demonstrate that various parameters are ignored
1046 : * in the reconnect
1047 : */
1048 :
1049 0 : ZERO_STRUCT(io);
1050 : /*
1051 : * These are completely ignored by the server
1052 : */
1053 0 : io.in.security_flags = 0x78;
1054 0 : io.in.oplock_level = 0x78;
1055 0 : io.in.impersonation_level = 0x12345678;
1056 0 : io.in.create_flags = 0x12345678;
1057 0 : io.in.reserved = 0x12345678;
1058 0 : io.in.desired_access = 0x12345678;
1059 0 : io.in.file_attributes = 0x12345678;
1060 0 : io.in.share_access = 0x12345678;
1061 0 : io.in.create_disposition = 0x12345678;
1062 0 : io.in.create_options = 0x12345678;
1063 :
1064 : /*
1065 : * only these are checked:
1066 : * - io.in.fname
1067 : * - io.in.durable_handle,
1068 : * - io.in.lease_request->lease_key
1069 : */
1070 :
1071 0 : io.in.fname = fname;
1072 0 : io.in.durable_open_v2 = false;
1073 0 : io.in.durable_handle_v2 = h;
1074 0 : io.in.lease_request = &ls;
1075 :
1076 : /* the requested lease state is irrelevant */
1077 0 : ls.lease_state = smb2_util_lease_state("");
1078 :
1079 0 : h = NULL;
1080 :
1081 0 : status = smb2_create(tree, mem_ctx, &io);
1082 0 : CHECK_STATUS(status, NT_STATUS_OK);
1083 :
1084 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1085 0 : CHECK_VAL(io.out.durable_open, false);
1086 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1087 0 : CHECK_VAL(io.out.persistent_open, false);
1088 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1089 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1090 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1091 0 : CHECK_VAL(io.out.lease_response.lease_state,
1092 : smb2_util_lease_state("RWH"));
1093 0 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1094 0 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1095 :
1096 0 : _h = io.out.file.handle;
1097 0 : h = &_h;
1098 :
1099 0 : done:
1100 0 : if (tree != NULL) {
1101 0 : if (h != NULL) {
1102 0 : smb2_util_close(tree, *h);
1103 : }
1104 :
1105 0 : smb2_util_unlink(tree, fname);
1106 :
1107 0 : talloc_free(tree);
1108 : }
1109 :
1110 0 : talloc_free(mem_ctx);
1111 :
1112 0 : return ret;
1113 : }
1114 :
1115 : /**
1116 : * lease v2 variant of reopen2
1117 : * basic test for doing a durable open
1118 : * tcp disconnect, reconnect, do a durable reopen (succeeds)
1119 : */
1120 0 : static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
1121 : struct smb2_tree *tree)
1122 : {
1123 : NTSTATUS status;
1124 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1125 : char fname[256];
1126 : struct smb2_handle _h;
1127 0 : struct smb2_handle *h = NULL;
1128 : struct smb2_create io;
1129 : struct smb2_lease ls;
1130 : uint64_t lease_key;
1131 0 : bool ret = true;
1132 : struct smbcli_options options;
1133 : uint32_t caps;
1134 :
1135 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1136 0 : if (!(caps & SMB2_CAP_LEASING)) {
1137 0 : torture_skip(tctx, "leases are not supported");
1138 : }
1139 :
1140 0 : options = tree->session->transport->options;
1141 :
1142 : /* Choose a random name in case the state is left a little funky. */
1143 0 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1144 : generate_random_str(tctx, 8));
1145 :
1146 0 : smb2_util_unlink(tree, fname);
1147 :
1148 0 : lease_key = random();
1149 0 : smb2_lease_v2_create(&io, &ls, false /* dir */, fname,
1150 : lease_key, 0, /* parent lease key */
1151 : smb2_util_lease_state("RWH"), 0 /* lease epoch */);
1152 0 : io.in.durable_open = true;
1153 :
1154 0 : status = smb2_create(tree, mem_ctx, &io);
1155 0 : CHECK_STATUS(status, NT_STATUS_OK);
1156 0 : _h = io.out.file.handle;
1157 0 : h = &_h;
1158 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1159 :
1160 0 : CHECK_VAL(io.out.durable_open, true);
1161 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1162 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1163 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1164 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1165 : smb2_util_lease_state("RWH"));
1166 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1167 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1168 :
1169 : /* disconnect, reconnect and then do durable reopen */
1170 0 : TALLOC_FREE(tree);
1171 :
1172 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1173 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1174 0 : ret = false;
1175 0 : goto done;
1176 : }
1177 :
1178 : /* a few failure tests: */
1179 :
1180 : /*
1181 : * several attempts without lease attached:
1182 : * all fail with NT_STATUS_OBJECT_NAME_NOT_FOUND
1183 : * irrespective of file name provided
1184 : */
1185 :
1186 0 : ZERO_STRUCT(io);
1187 0 : io.in.fname = "";
1188 0 : io.in.durable_handle = h;
1189 0 : status = smb2_create(tree, mem_ctx, &io);
1190 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1191 :
1192 0 : ZERO_STRUCT(io);
1193 0 : io.in.fname = "__non_existing_fname__";
1194 0 : io.in.durable_handle = h;
1195 0 : status = smb2_create(tree, mem_ctx, &io);
1196 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1197 :
1198 0 : ZERO_STRUCT(io);
1199 0 : io.in.fname = fname;
1200 0 : io.in.durable_handle = h;
1201 0 : status = smb2_create(tree, mem_ctx, &io);
1202 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1203 :
1204 : /*
1205 : * attempt with lease provided, but
1206 : * with a changed lease key. => fails
1207 : */
1208 0 : ZERO_STRUCT(io);
1209 0 : io.in.fname = fname;
1210 0 : io.in.durable_open = false;
1211 0 : io.in.durable_handle = h;
1212 0 : io.in.lease_request_v2 = &ls;
1213 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1214 : /* a wrong lease key lets the request fail */
1215 0 : ls.lease_key.data[0]++;
1216 :
1217 0 : status = smb2_create(tree, mem_ctx, &io);
1218 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1219 :
1220 : /* restore the correct lease key */
1221 0 : ls.lease_key.data[0]--;
1222 :
1223 : /*
1224 : * this last failing attempt is almost correct:
1225 : * only problem is: we use the wrong filename...
1226 : * Note that this gives INVALID_PARAMETER.
1227 : * This is different from oplocks!
1228 : */
1229 0 : ZERO_STRUCT(io);
1230 0 : io.in.fname = "__non_existing_fname__";
1231 0 : io.in.durable_open = false;
1232 0 : io.in.durable_handle = h;
1233 0 : io.in.lease_request_v2 = &ls;
1234 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1235 :
1236 0 : status = smb2_create(tree, mem_ctx, &io);
1237 0 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1238 :
1239 : /*
1240 : * Now for a succeeding reconnect:
1241 : */
1242 :
1243 0 : ZERO_STRUCT(io);
1244 0 : io.in.fname = fname;
1245 0 : io.in.durable_open = false;
1246 0 : io.in.durable_handle = h;
1247 0 : io.in.lease_request_v2 = &ls;
1248 0 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1249 :
1250 : /* the requested lease state is irrelevant */
1251 0 : ls.lease_state = smb2_util_lease_state("");
1252 :
1253 0 : h = NULL;
1254 :
1255 0 : status = smb2_create(tree, mem_ctx, &io);
1256 0 : CHECK_STATUS(status, NT_STATUS_OK);
1257 :
1258 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1259 0 : CHECK_VAL(io.out.durable_open, false);
1260 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1261 0 : CHECK_VAL(io.out.persistent_open, false);
1262 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1263 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1264 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1265 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1266 : smb2_util_lease_state("RWH"));
1267 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1268 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1269 0 : _h = io.out.file.handle;
1270 0 : h = &_h;
1271 :
1272 : /* disconnect one more time */
1273 0 : TALLOC_FREE(tree);
1274 :
1275 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
1276 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1277 0 : ret = false;
1278 0 : goto done;
1279 : }
1280 :
1281 : /*
1282 : * demonstrate that various parameters are ignored
1283 : * in the reconnect
1284 : */
1285 :
1286 0 : ZERO_STRUCT(io);
1287 : /*
1288 : * These are completely ignored by the server
1289 : */
1290 0 : io.in.security_flags = 0x78;
1291 0 : io.in.oplock_level = 0x78;
1292 0 : io.in.impersonation_level = 0x12345678;
1293 0 : io.in.create_flags = 0x12345678;
1294 0 : io.in.reserved = 0x12345678;
1295 0 : io.in.desired_access = 0x12345678;
1296 0 : io.in.file_attributes = 0x12345678;
1297 0 : io.in.share_access = 0x12345678;
1298 0 : io.in.create_disposition = 0x12345678;
1299 0 : io.in.create_options = 0x12345678;
1300 :
1301 : /*
1302 : * only these are checked:
1303 : * - io.in.fname
1304 : * - io.in.durable_handle,
1305 : * - io.in.lease_request->lease_key
1306 : */
1307 :
1308 0 : io.in.fname = fname;
1309 0 : io.in.durable_open_v2 = false;
1310 0 : io.in.durable_handle_v2 = h;
1311 0 : io.in.lease_request_v2 = &ls;
1312 :
1313 : /* the requested lease state is irrelevant */
1314 0 : ls.lease_state = smb2_util_lease_state("");
1315 :
1316 0 : h = NULL;
1317 :
1318 0 : status = smb2_create(tree, mem_ctx, &io);
1319 0 : CHECK_STATUS(status, NT_STATUS_OK);
1320 :
1321 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1322 0 : CHECK_VAL(io.out.durable_open, false);
1323 0 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1324 0 : CHECK_VAL(io.out.persistent_open, false);
1325 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1326 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1327 0 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1328 0 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1329 : smb2_util_lease_state("RWH"));
1330 0 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1331 0 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1332 :
1333 0 : _h = io.out.file.handle;
1334 0 : h = &_h;
1335 :
1336 0 : done:
1337 0 : if (tree != NULL) {
1338 0 : if (h != NULL) {
1339 0 : smb2_util_close(tree, *h);
1340 : }
1341 :
1342 0 : smb2_util_unlink(tree, fname);
1343 :
1344 0 : talloc_free(tree);
1345 : }
1346 :
1347 0 : talloc_free(mem_ctx);
1348 :
1349 0 : return ret;
1350 : }
1351 :
1352 : /**
1353 : * basic test for doing a durable open
1354 : * tcp disconnect, reconnect with a session reconnect and
1355 : * do a durable reopen (succeeds)
1356 : */
1357 0 : static bool test_durable_open_reopen2a(struct torture_context *tctx,
1358 : struct smb2_tree *tree)
1359 : {
1360 : NTSTATUS status;
1361 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1362 : char fname[256];
1363 : struct smb2_handle _h;
1364 0 : struct smb2_handle *h = NULL;
1365 : struct smb2_create io1, io2;
1366 : uint64_t previous_session_id;
1367 0 : bool ret = true;
1368 : struct smbcli_options options;
1369 :
1370 0 : options = tree->session->transport->options;
1371 :
1372 : /* Choose a random name in case the state is left a little funky. */
1373 0 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1374 : generate_random_str(tctx, 8));
1375 :
1376 0 : smb2_util_unlink(tree, fname);
1377 :
1378 0 : smb2_oplock_create_share(&io1, fname,
1379 : smb2_util_share_access(""),
1380 0 : smb2_util_oplock_level("b"));
1381 0 : io1.in.durable_open = true;
1382 :
1383 0 : status = smb2_create(tree, mem_ctx, &io1);
1384 0 : CHECK_STATUS(status, NT_STATUS_OK);
1385 0 : _h = io1.out.file.handle;
1386 0 : h = &_h;
1387 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1388 0 : CHECK_VAL(io1.out.durable_open, true);
1389 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1390 :
1391 : /* disconnect, reconnect and then do durable reopen */
1392 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1393 0 : talloc_free(tree);
1394 0 : tree = NULL;
1395 :
1396 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
1397 : &options, &tree))
1398 : {
1399 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1400 0 : ret = false;
1401 0 : goto done;
1402 : }
1403 :
1404 0 : ZERO_STRUCT(io2);
1405 0 : io2.in.fname = fname;
1406 0 : io2.in.durable_handle = h;
1407 0 : h = NULL;
1408 :
1409 0 : status = smb2_create(tree, mem_ctx, &io2);
1410 0 : CHECK_STATUS(status, NT_STATUS_OK);
1411 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1412 0 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1413 0 : _h = io2.out.file.handle;
1414 0 : h = &_h;
1415 :
1416 0 : done:
1417 0 : if (tree != NULL) {
1418 0 : if (h != NULL) {
1419 0 : smb2_util_close(tree, *h);
1420 : }
1421 :
1422 0 : smb2_util_unlink(tree, fname);
1423 :
1424 0 : talloc_free(tree);
1425 : }
1426 :
1427 0 : talloc_free(mem_ctx);
1428 :
1429 0 : return ret;
1430 : }
1431 :
1432 :
1433 : /**
1434 : * basic test for doing a durable open:
1435 : * tdis, new tcon, try durable reopen (fails)
1436 : */
1437 0 : static bool test_durable_open_reopen3(struct torture_context *tctx,
1438 : struct smb2_tree *tree)
1439 : {
1440 : NTSTATUS status;
1441 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1442 : char fname[256];
1443 : struct smb2_handle _h;
1444 0 : struct smb2_handle *h = NULL;
1445 : struct smb2_create io1, io2;
1446 0 : bool ret = true;
1447 : struct smb2_tree *tree2;
1448 :
1449 : /* Choose a random name in case the state is left a little funky. */
1450 0 : snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1451 : generate_random_str(tctx, 8));
1452 :
1453 0 : smb2_util_unlink(tree, fname);
1454 :
1455 0 : smb2_oplock_create_share(&io1, fname,
1456 : smb2_util_share_access(""),
1457 0 : smb2_util_oplock_level("b"));
1458 0 : io1.in.durable_open = true;
1459 :
1460 0 : status = smb2_create(tree, mem_ctx, &io1);
1461 0 : CHECK_STATUS(status, NT_STATUS_OK);
1462 0 : _h = io1.out.file.handle;
1463 0 : h = &_h;
1464 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1465 0 : CHECK_VAL(io1.out.durable_open, true);
1466 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1467 :
1468 : /* disconnect, reconnect and then do durable reopen */
1469 0 : status = smb2_tdis(tree);
1470 0 : CHECK_STATUS(status, NT_STATUS_OK);
1471 :
1472 0 : if (!torture_smb2_tree_connect(tctx, tree->session, mem_ctx, &tree2)) {
1473 0 : torture_warning(tctx, "couldn't reconnect to share, bailing\n");
1474 0 : ret = false;
1475 0 : goto done;
1476 : }
1477 :
1478 :
1479 0 : ZERO_STRUCT(io2);
1480 0 : io2.in.fname = fname;
1481 0 : io2.in.durable_handle = h;
1482 :
1483 0 : status = smb2_create(tree2, mem_ctx, &io2);
1484 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1485 :
1486 0 : done:
1487 0 : if (tree != NULL) {
1488 0 : if (h != NULL) {
1489 0 : smb2_util_close(tree, *h);
1490 : }
1491 :
1492 0 : smb2_util_unlink(tree2, fname);
1493 :
1494 0 : talloc_free(tree);
1495 : }
1496 :
1497 0 : talloc_free(mem_ctx);
1498 :
1499 0 : return ret;
1500 : }
1501 :
1502 : /**
1503 : * basic test for doing a durable open:
1504 : * logoff, create a new session, do a durable reopen (succeeds)
1505 : */
1506 0 : static bool test_durable_open_reopen4(struct torture_context *tctx,
1507 : struct smb2_tree *tree)
1508 : {
1509 : NTSTATUS status;
1510 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1511 : char fname[256];
1512 : struct smb2_handle _h;
1513 0 : struct smb2_handle *h = NULL;
1514 : struct smb2_create io1, io2;
1515 0 : bool ret = true;
1516 : struct smb2_transport *transport;
1517 : struct smb2_session *session2;
1518 : struct smb2_tree *tree2;
1519 :
1520 : /* Choose a random name in case the state is left a little funky. */
1521 0 : snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1522 : generate_random_str(tctx, 8));
1523 :
1524 0 : smb2_util_unlink(tree, fname);
1525 :
1526 0 : smb2_oplock_create_share(&io1, fname,
1527 : smb2_util_share_access(""),
1528 0 : smb2_util_oplock_level("b"));
1529 0 : io1.in.durable_open = true;
1530 :
1531 0 : status = smb2_create(tree, mem_ctx, &io1);
1532 0 : CHECK_STATUS(status, NT_STATUS_OK);
1533 0 : _h = io1.out.file.handle;
1534 0 : h = &_h;
1535 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1536 0 : CHECK_VAL(io1.out.durable_open, true);
1537 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1538 :
1539 : /*
1540 : * do a session logoff, establish a new session and tree
1541 : * connect on the same transport, and try a durable reopen
1542 : */
1543 0 : transport = tree->session->transport;
1544 0 : status = smb2_logoff(tree->session);
1545 0 : CHECK_STATUS(status, NT_STATUS_OK);
1546 :
1547 0 : if (!torture_smb2_session_setup(tctx, transport,
1548 : 0, /* previous_session_id */
1549 : mem_ctx, &session2))
1550 : {
1551 0 : torture_warning(tctx, "session setup failed.\n");
1552 0 : ret = false;
1553 0 : goto done;
1554 : }
1555 :
1556 : /*
1557 : * the session setup has talloc-stolen the transport,
1558 : * so we can safely free the old tree+session for clarity
1559 : */
1560 0 : TALLOC_FREE(tree);
1561 :
1562 0 : if (!torture_smb2_tree_connect(tctx, session2, mem_ctx, &tree2)) {
1563 0 : torture_warning(tctx, "tree connect failed.\n");
1564 0 : ret = false;
1565 0 : goto done;
1566 : }
1567 :
1568 0 : ZERO_STRUCT(io2);
1569 0 : io2.in.fname = fname;
1570 0 : io2.in.durable_handle = h;
1571 0 : h = NULL;
1572 :
1573 0 : status = smb2_create(tree2, mem_ctx, &io2);
1574 0 : CHECK_STATUS(status, NT_STATUS_OK);
1575 :
1576 0 : _h = io2.out.file.handle;
1577 0 : h = &_h;
1578 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1579 0 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1580 :
1581 0 : done:
1582 0 : if (tree != NULL) {
1583 0 : if (h != NULL) {
1584 0 : smb2_util_close(tree2, *h);
1585 : }
1586 :
1587 0 : smb2_util_unlink(tree2, fname);
1588 :
1589 0 : talloc_free(tree);
1590 : }
1591 :
1592 0 : talloc_free(mem_ctx);
1593 :
1594 0 : return ret;
1595 : }
1596 :
1597 0 : static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1598 : struct smb2_tree *tree)
1599 : {
1600 : NTSTATUS status;
1601 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1602 : char fname[256];
1603 : struct smb2_handle _h;
1604 0 : struct smb2_handle *h = NULL;
1605 : struct smb2_create io1, io2;
1606 0 : bool ret = true;
1607 0 : uint8_t b = 0;
1608 :
1609 : /* Choose a random name in case the state is left a little funky. */
1610 0 : snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1611 : generate_random_str(tctx, 8));
1612 :
1613 0 : smb2_util_unlink(tree, fname);
1614 :
1615 0 : smb2_oplock_create_share(&io1, fname,
1616 : smb2_util_share_access(""),
1617 0 : smb2_util_oplock_level("b"));
1618 0 : io1.in.durable_open = true;
1619 0 : io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1620 :
1621 0 : status = smb2_create(tree, mem_ctx, &io1);
1622 0 : CHECK_STATUS(status, NT_STATUS_OK);
1623 0 : _h = io1.out.file.handle;
1624 0 : h = &_h;
1625 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1626 0 : CHECK_VAL(io1.out.durable_open, true);
1627 0 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1628 :
1629 0 : status = smb2_util_write(tree, *h, &b, 0, 1);
1630 0 : CHECK_STATUS(status, NT_STATUS_OK);
1631 :
1632 : /* disconnect, leaving the durable handle in place */
1633 0 : TALLOC_FREE(tree);
1634 :
1635 0 : if (!torture_smb2_connection(tctx, &tree)) {
1636 0 : torture_warning(tctx, "could not reconnect, bailing\n");
1637 0 : ret = false;
1638 0 : goto done;
1639 : }
1640 :
1641 : /*
1642 : * Open the file on the new connection again
1643 : * and check that it has been newly created,
1644 : * i.e. delete on close was effective on the disconnected handle.
1645 : * Also check that the file is really empty,
1646 : * the previously written byte gone.
1647 : */
1648 0 : smb2_oplock_create_share(&io2, fname,
1649 : smb2_util_share_access(""),
1650 0 : smb2_util_oplock_level("b"));
1651 0 : io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1652 :
1653 0 : status = smb2_create(tree, mem_ctx, &io2);
1654 0 : CHECK_STATUS(status, NT_STATUS_OK);
1655 0 : _h = io2.out.file.handle;
1656 0 : h = &_h;
1657 0 : CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1658 0 : CHECK_VAL(io2.out.durable_open, false);
1659 0 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1660 :
1661 0 : done:
1662 0 : if (tree != NULL) {
1663 0 : if (h != NULL) {
1664 0 : smb2_util_close(tree, *h);
1665 : }
1666 :
1667 0 : smb2_util_unlink(tree, fname);
1668 :
1669 0 : talloc_free(tree);
1670 : }
1671 :
1672 0 : talloc_free(mem_ctx);
1673 :
1674 0 : return ret;
1675 : }
1676 :
1677 :
1678 0 : static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1679 : struct smb2_tree *tree)
1680 : {
1681 : NTSTATUS status;
1682 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1683 : char fname[256];
1684 : struct smb2_handle _h;
1685 0 : struct smb2_handle *h = NULL;
1686 : struct smb2_create io;
1687 0 : bool ret = true;
1688 0 : uint8_t b = 0;
1689 : uint64_t previous_session_id;
1690 : uint64_t alloc_size_step;
1691 : struct smbcli_options options;
1692 :
1693 0 : options = tree->session->transport->options;
1694 :
1695 : /* Choose a random name in case the state is left a little funky. */
1696 0 : snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1697 : generate_random_str(tctx, 8));
1698 :
1699 0 : smb2_util_unlink(tree, fname);
1700 :
1701 0 : smb2_oplock_create_share(&io, fname,
1702 : smb2_util_share_access(""),
1703 0 : smb2_util_oplock_level("b"));
1704 0 : io.in.durable_open = true;
1705 0 : io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1706 :
1707 0 : status = smb2_create(tree, mem_ctx, &io);
1708 0 : CHECK_STATUS(status, NT_STATUS_OK);
1709 0 : _h = io.out.file.handle;
1710 0 : h = &_h;
1711 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1712 0 : CHECK_VAL(io.out.durable_open, true);
1713 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1714 :
1715 0 : status = smb2_util_write(tree, *h, &b, 0, 1);
1716 0 : CHECK_STATUS(status, NT_STATUS_OK);
1717 :
1718 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1719 :
1720 : /* disconnect, leaving the durable handle in place */
1721 0 : TALLOC_FREE(tree);
1722 :
1723 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
1724 : &options, &tree))
1725 : {
1726 0 : torture_warning(tctx, "could not reconnect, bailing\n");
1727 0 : ret = false;
1728 0 : goto done;
1729 : }
1730 :
1731 0 : ZERO_STRUCT(io);
1732 0 : io.in.fname = fname;
1733 0 : io.in.durable_handle = h;
1734 :
1735 0 : status = smb2_create(tree, mem_ctx, &io);
1736 0 : CHECK_STATUS(status, NT_STATUS_OK);
1737 0 : _h = io.out.file.handle;
1738 0 : h = &_h;
1739 0 : alloc_size_step = io.out.alloc_size;
1740 0 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE, alloc_size_step, 1);
1741 0 : CHECK_VAL(io.out.durable_open, false);
1742 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1743 :
1744 : /* close the file, thereby deleting it */
1745 0 : smb2_util_close(tree, *h);
1746 0 : status = smb2_logoff(tree->session);
1747 0 : TALLOC_FREE(tree);
1748 :
1749 0 : if (!torture_smb2_connection(tctx, &tree)) {
1750 0 : torture_warning(tctx, "could not reconnect, bailing\n");
1751 0 : ret = false;
1752 0 : goto done;
1753 : }
1754 :
1755 : /*
1756 : * Open the file on the new connection again
1757 : * and check that it has been newly created,
1758 : * i.e. delete on close was effective on the reconnected handle.
1759 : * Also check that the file is really empty,
1760 : * the previously written byte gone.
1761 : */
1762 0 : smb2_oplock_create_share(&io, fname,
1763 : smb2_util_share_access(""),
1764 0 : smb2_util_oplock_level("b"));
1765 0 : io.in.durable_open = true;
1766 0 : io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1767 :
1768 0 : status = smb2_create(tree, mem_ctx, &io);
1769 0 : CHECK_STATUS(status, NT_STATUS_OK);
1770 0 : _h = io.out.file.handle;
1771 0 : h = &_h;
1772 0 : CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1773 0 : CHECK_VAL(io.out.durable_open, true);
1774 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1775 :
1776 0 : done:
1777 0 : if (tree != NULL) {
1778 0 : if (h != NULL) {
1779 0 : smb2_util_close(tree, *h);
1780 : }
1781 :
1782 0 : smb2_util_unlink(tree, fname);
1783 :
1784 0 : talloc_free(tree);
1785 : }
1786 :
1787 0 : talloc_free(mem_ctx);
1788 :
1789 0 : return ret;
1790 : }
1791 :
1792 : /*
1793 : basic testing of SMB2 durable opens
1794 : regarding the position information on the handle
1795 : */
1796 0 : static bool test_durable_open_file_position(struct torture_context *tctx,
1797 : struct smb2_tree *tree)
1798 : {
1799 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1800 : struct smb2_handle h;
1801 : struct smb2_create io;
1802 : NTSTATUS status;
1803 0 : const char *fname = "durable_open_position.dat";
1804 : union smb_fileinfo qfinfo;
1805 : union smb_setfileinfo sfinfo;
1806 0 : bool ret = true;
1807 : uint64_t pos;
1808 : uint64_t previous_session_id;
1809 : struct smbcli_options options;
1810 :
1811 0 : options = tree->session->transport->options;
1812 :
1813 0 : smb2_util_unlink(tree, fname);
1814 :
1815 0 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1816 0 : io.in.durable_open = true;
1817 :
1818 0 : status = smb2_create(tree, mem_ctx, &io);
1819 0 : CHECK_STATUS(status, NT_STATUS_OK);
1820 0 : h = io.out.file.handle;
1821 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1822 0 : CHECK_VAL(io.out.durable_open, true);
1823 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1824 :
1825 : /* TODO: check extra blob content */
1826 :
1827 0 : ZERO_STRUCT(qfinfo);
1828 0 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1829 0 : qfinfo.generic.in.file.handle = h;
1830 0 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1831 0 : CHECK_STATUS(status, NT_STATUS_OK);
1832 0 : CHECK_VAL(qfinfo.position_information.out.position, 0);
1833 0 : pos = qfinfo.position_information.out.position;
1834 0 : torture_comment(tctx, "position: %llu\n",
1835 : (unsigned long long)pos);
1836 :
1837 0 : ZERO_STRUCT(sfinfo);
1838 0 : sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1839 0 : sfinfo.generic.in.file.handle = h;
1840 0 : sfinfo.position_information.in.position = 0x1000;
1841 0 : status = smb2_setinfo_file(tree, &sfinfo);
1842 0 : CHECK_STATUS(status, NT_STATUS_OK);
1843 :
1844 0 : ZERO_STRUCT(qfinfo);
1845 0 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1846 0 : qfinfo.generic.in.file.handle = h;
1847 0 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1848 0 : CHECK_STATUS(status, NT_STATUS_OK);
1849 0 : CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1850 0 : pos = qfinfo.position_information.out.position;
1851 0 : torture_comment(tctx, "position: %llu\n",
1852 : (unsigned long long)pos);
1853 :
1854 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1855 :
1856 : /* tcp disconnect */
1857 0 : talloc_free(tree);
1858 0 : tree = NULL;
1859 :
1860 : /* do a session reconnect */
1861 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
1862 : &options, &tree))
1863 : {
1864 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1865 0 : ret = false;
1866 0 : goto done;
1867 : }
1868 :
1869 0 : ZERO_STRUCT(qfinfo);
1870 0 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1871 0 : qfinfo.generic.in.file.handle = h;
1872 0 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1873 0 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1874 :
1875 0 : ZERO_STRUCT(io);
1876 0 : io.in.fname = fname;
1877 0 : io.in.durable_handle = &h;
1878 :
1879 0 : status = smb2_create(tree, mem_ctx, &io);
1880 0 : CHECK_STATUS(status, NT_STATUS_OK);
1881 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1882 0 : CHECK_VAL(io.out.reserved, 0x00);
1883 0 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1884 0 : CHECK_VAL(io.out.size, 0);
1885 0 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1886 0 : CHECK_VAL(io.out.reserved2, 0);
1887 :
1888 0 : h = io.out.file.handle;
1889 :
1890 0 : ZERO_STRUCT(qfinfo);
1891 0 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1892 0 : qfinfo.generic.in.file.handle = h;
1893 0 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1894 0 : CHECK_STATUS(status, NT_STATUS_OK);
1895 0 : CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1896 0 : pos = qfinfo.position_information.out.position;
1897 0 : torture_comment(tctx, "position: %llu\n",
1898 : (unsigned long long)pos);
1899 :
1900 0 : smb2_util_close(tree, h);
1901 :
1902 0 : talloc_free(mem_ctx);
1903 :
1904 0 : smb2_util_unlink(tree, fname);
1905 :
1906 0 : done:
1907 0 : talloc_free(tree);
1908 :
1909 0 : return ret;
1910 : }
1911 :
1912 : /*
1913 : Open, disconnect, oplock break, reconnect.
1914 : */
1915 0 : static bool test_durable_open_oplock(struct torture_context *tctx,
1916 : struct smb2_tree *tree1,
1917 : struct smb2_tree *tree2)
1918 : {
1919 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1920 : struct smb2_create io1, io2;
1921 0 : struct smb2_handle h1 = {{0}};
1922 0 : struct smb2_handle h2 = {{0}};
1923 : NTSTATUS status;
1924 : char fname[256];
1925 0 : bool ret = true;
1926 :
1927 : /* Choose a random name in case the state is left a little funky. */
1928 0 : snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1929 :
1930 : /* Clean slate */
1931 0 : smb2_util_unlink(tree1, fname);
1932 :
1933 : /* Create with batch oplock */
1934 0 : smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1935 0 : io1.in.durable_open = true;
1936 :
1937 0 : io2 = io1;
1938 0 : io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1939 :
1940 0 : status = smb2_create(tree1, mem_ctx, &io1);
1941 0 : CHECK_STATUS(status, NT_STATUS_OK);
1942 0 : h1 = io1.out.file.handle;
1943 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1944 0 : CHECK_VAL(io1.out.durable_open, true);
1945 0 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1946 :
1947 : /* Disconnect after getting the batch */
1948 0 : talloc_free(tree1);
1949 0 : tree1 = NULL;
1950 :
1951 : /*
1952 : * Windows7 (build 7000) will break a batch oplock immediately if the
1953 : * original client is gone. (ZML: This seems like a bug. It should give
1954 : * some time for the client to reconnect!)
1955 : */
1956 0 : status = smb2_create(tree2, mem_ctx, &io2);
1957 0 : CHECK_STATUS(status, NT_STATUS_OK);
1958 0 : h2 = io2.out.file.handle;
1959 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1960 0 : CHECK_VAL(io2.out.durable_open, true);
1961 0 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1962 :
1963 : /* What if tree1 tries to come back and reclaim? */
1964 0 : if (!torture_smb2_connection(tctx, &tree1)) {
1965 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
1966 0 : ret = false;
1967 0 : goto done;
1968 : }
1969 :
1970 0 : ZERO_STRUCT(io1);
1971 0 : io1.in.fname = fname;
1972 0 : io1.in.durable_handle = &h1;
1973 :
1974 0 : status = smb2_create(tree1, mem_ctx, &io1);
1975 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1976 :
1977 0 : done:
1978 0 : smb2_util_close(tree2, h2);
1979 0 : smb2_util_unlink(tree2, fname);
1980 :
1981 0 : talloc_free(tree1);
1982 0 : talloc_free(tree2);
1983 :
1984 0 : return ret;
1985 : }
1986 :
1987 : /*
1988 : Open, disconnect, lease break, reconnect.
1989 : */
1990 0 : static bool test_durable_open_lease(struct torture_context *tctx,
1991 : struct smb2_tree *tree1,
1992 : struct smb2_tree *tree2)
1993 : {
1994 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1995 : struct smb2_create io1, io2;
1996 : struct smb2_lease ls1, ls2;
1997 0 : struct smb2_handle h1 = {{0}};
1998 0 : struct smb2_handle h2 = {{0}};
1999 : NTSTATUS status;
2000 : char fname[256];
2001 0 : bool ret = true;
2002 : uint64_t lease1, lease2;
2003 : uint32_t caps;
2004 :
2005 0 : caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2006 0 : if (!(caps & SMB2_CAP_LEASING)) {
2007 0 : torture_skip(tctx, "leases are not supported");
2008 : }
2009 :
2010 : /*
2011 : * Choose a random name and random lease in case the state is left a
2012 : * little funky.
2013 : */
2014 0 : lease1 = random();
2015 0 : lease2 = random();
2016 0 : snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
2017 :
2018 : /* Clean slate */
2019 0 : smb2_util_unlink(tree1, fname);
2020 :
2021 : /* Create with lease */
2022 0 : smb2_lease_create(&io1, &ls1, false /* dir */, fname,
2023 : lease1, smb2_util_lease_state("RHW"));
2024 0 : io1.in.durable_open = true;
2025 :
2026 0 : smb2_lease_create(&io2, &ls2, false /* dir */, fname,
2027 : lease2, smb2_util_lease_state("RHW"));
2028 0 : io2.in.durable_open = true;
2029 0 : io2.in.create_disposition = NTCREATEX_DISP_OPEN;
2030 :
2031 0 : status = smb2_create(tree1, mem_ctx, &io1);
2032 0 : CHECK_STATUS(status, NT_STATUS_OK);
2033 0 : h1 = io1.out.file.handle;
2034 0 : CHECK_VAL(io1.out.durable_open, true);
2035 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2036 :
2037 0 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2038 0 : CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
2039 0 : CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
2040 0 : CHECK_VAL(io1.out.lease_response.lease_state,
2041 : SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2042 :
2043 : /* Disconnect after getting the lease */
2044 0 : talloc_free(tree1);
2045 0 : tree1 = NULL;
2046 :
2047 : /*
2048 : * Windows7 (build 7000) will grant an RH lease immediate (not an RHW?)
2049 : * even if the original client is gone. (ZML: This seems like a bug. It
2050 : * should give some time for the client to reconnect! And why RH?)
2051 : *
2052 : * obnox: Current windows 7 and w2k8r2 grant RHW instead of RH.
2053 : * Test is adapted accordingly.
2054 : */
2055 0 : status = smb2_create(tree2, mem_ctx, &io2);
2056 0 : CHECK_STATUS(status, NT_STATUS_OK);
2057 0 : h2 = io2.out.file.handle;
2058 0 : CHECK_VAL(io2.out.durable_open, true);
2059 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2060 :
2061 0 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2062 0 : CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
2063 0 : CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
2064 0 : CHECK_VAL(io2.out.lease_response.lease_state,
2065 : SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2066 :
2067 : /* What if tree1 tries to come back and reclaim? */
2068 0 : if (!torture_smb2_connection(tctx, &tree1)) {
2069 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2070 0 : ret = false;
2071 0 : goto done;
2072 : }
2073 :
2074 0 : ZERO_STRUCT(io1);
2075 0 : io1.in.fname = fname;
2076 0 : io1.in.durable_handle = &h1;
2077 0 : io1.in.lease_request = &ls1;
2078 :
2079 0 : status = smb2_create(tree1, mem_ctx, &io1);
2080 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2081 :
2082 0 : done:
2083 0 : smb2_util_close(tree2, h2);
2084 0 : smb2_util_unlink(tree2, fname);
2085 :
2086 0 : talloc_free(tree1);
2087 0 : talloc_free(tree2);
2088 :
2089 0 : return ret;
2090 : }
2091 :
2092 0 : static bool test_durable_open_lock_oplock(struct torture_context *tctx,
2093 : struct smb2_tree *tree)
2094 : {
2095 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2096 : struct smb2_create io;
2097 0 : struct smb2_handle h = {{0}};
2098 : struct smb2_lock lck;
2099 : struct smb2_lock_element el[2];
2100 : NTSTATUS status;
2101 : char fname[256];
2102 0 : bool ret = true;
2103 :
2104 : /*
2105 : */
2106 0 : snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
2107 :
2108 : /* Clean slate */
2109 0 : smb2_util_unlink(tree, fname);
2110 :
2111 : /* Create with oplock */
2112 :
2113 0 : smb2_oplock_create_share(&io, fname,
2114 : smb2_util_share_access(""),
2115 0 : smb2_util_oplock_level("b"));
2116 0 : io.in.durable_open = true;
2117 :
2118 0 : status = smb2_create(tree, mem_ctx, &io);
2119 0 : CHECK_STATUS(status, NT_STATUS_OK);
2120 0 : h = io.out.file.handle;
2121 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2122 :
2123 0 : CHECK_VAL(io.out.durable_open, true);
2124 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2125 :
2126 0 : ZERO_STRUCT(lck);
2127 0 : ZERO_STRUCT(el);
2128 0 : lck.in.locks = el;
2129 0 : lck.in.lock_count = 0x0001;
2130 0 : lck.in.lock_sequence = 0x00000000;
2131 0 : lck.in.file.handle = h;
2132 0 : el[0].offset = 0;
2133 0 : el[0].length = 1;
2134 0 : el[0].reserved = 0x00000000;
2135 0 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2136 0 : status = smb2_lock(tree, &lck);
2137 0 : CHECK_STATUS(status, NT_STATUS_OK);
2138 :
2139 : /* Disconnect/Reconnect. */
2140 0 : talloc_free(tree);
2141 0 : tree = NULL;
2142 :
2143 0 : if (!torture_smb2_connection(tctx, &tree)) {
2144 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2145 0 : ret = false;
2146 0 : goto done;
2147 : }
2148 :
2149 0 : ZERO_STRUCT(io);
2150 0 : io.in.fname = fname;
2151 0 : io.in.durable_handle = &h;
2152 :
2153 0 : status = smb2_create(tree, mem_ctx, &io);
2154 0 : CHECK_STATUS(status, NT_STATUS_OK);
2155 0 : h = io.out.file.handle;
2156 :
2157 0 : lck.in.file.handle = h;
2158 0 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2159 0 : status = smb2_lock(tree, &lck);
2160 0 : CHECK_STATUS(status, NT_STATUS_OK);
2161 :
2162 0 : done:
2163 0 : smb2_util_close(tree, h);
2164 0 : smb2_util_unlink(tree, fname);
2165 0 : talloc_free(tree);
2166 :
2167 0 : return ret;
2168 : }
2169 :
2170 : /*
2171 : Open, take BRL, disconnect, reconnect.
2172 : */
2173 0 : static bool test_durable_open_lock_lease(struct torture_context *tctx,
2174 : struct smb2_tree *tree)
2175 : {
2176 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2177 : struct smb2_create io;
2178 : struct smb2_lease ls;
2179 0 : struct smb2_handle h = {{0}};
2180 : struct smb2_lock lck;
2181 : struct smb2_lock_element el[2];
2182 : NTSTATUS status;
2183 : char fname[256];
2184 0 : bool ret = true;
2185 : uint64_t lease;
2186 : uint32_t caps;
2187 : struct smbcli_options options;
2188 :
2189 0 : options = tree->session->transport->options;
2190 :
2191 0 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2192 0 : if (!(caps & SMB2_CAP_LEASING)) {
2193 0 : torture_skip(tctx, "leases are not supported");
2194 : }
2195 :
2196 : /*
2197 : * Choose a random name and random lease in case the state is left a
2198 : * little funky.
2199 : */
2200 0 : lease = random();
2201 0 : snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2202 :
2203 : /* Clean slate */
2204 0 : smb2_util_unlink(tree, fname);
2205 :
2206 : /* Create with lease */
2207 :
2208 0 : smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2209 : smb2_util_lease_state("RWH"));
2210 0 : io.in.durable_open = true;
2211 :
2212 0 : status = smb2_create(tree, mem_ctx, &io);
2213 0 : CHECK_STATUS(status, NT_STATUS_OK);
2214 0 : h = io.out.file.handle;
2215 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2216 :
2217 0 : CHECK_VAL(io.out.durable_open, true);
2218 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2219 0 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2220 0 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2221 0 : CHECK_VAL(io.out.lease_response.lease_state,
2222 : SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2223 :
2224 0 : ZERO_STRUCT(lck);
2225 0 : ZERO_STRUCT(el);
2226 0 : lck.in.locks = el;
2227 0 : lck.in.lock_count = 0x0001;
2228 0 : lck.in.lock_sequence = 0x00000000;
2229 0 : lck.in.file.handle = h;
2230 0 : el[0].offset = 0;
2231 0 : el[0].length = 1;
2232 0 : el[0].reserved = 0x00000000;
2233 0 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2234 0 : status = smb2_lock(tree, &lck);
2235 0 : CHECK_STATUS(status, NT_STATUS_OK);
2236 :
2237 : /* Disconnect/Reconnect. */
2238 0 : talloc_free(tree);
2239 0 : tree = NULL;
2240 :
2241 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree)) {
2242 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2243 0 : ret = false;
2244 0 : goto done;
2245 : }
2246 :
2247 0 : ZERO_STRUCT(io);
2248 0 : io.in.fname = fname;
2249 0 : io.in.durable_handle = &h;
2250 0 : io.in.lease_request = &ls;
2251 :
2252 0 : status = smb2_create(tree, mem_ctx, &io);
2253 0 : CHECK_STATUS(status, NT_STATUS_OK);
2254 0 : h = io.out.file.handle;
2255 :
2256 0 : lck.in.file.handle = h;
2257 0 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2258 0 : status = smb2_lock(tree, &lck);
2259 0 : CHECK_STATUS(status, NT_STATUS_OK);
2260 :
2261 0 : done:
2262 0 : smb2_util_close(tree, h);
2263 0 : smb2_util_unlink(tree, fname);
2264 0 : talloc_free(tree);
2265 :
2266 0 : return ret;
2267 : }
2268 :
2269 : /**
2270 : * Open with a RH lease, disconnect, open in another tree, reconnect.
2271 : *
2272 : * This test actually demonstrates a minimum level of respect for the durable
2273 : * open in the face of another open. As long as this test shows an inability to
2274 : * reconnect after an open, the oplock/lease tests above will certainly
2275 : * demonstrate an error on reconnect.
2276 : */
2277 0 : static bool test_durable_open_open2_lease(struct torture_context *tctx,
2278 : struct smb2_tree *tree1,
2279 : struct smb2_tree *tree2)
2280 : {
2281 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2282 : struct smb2_create io1, io2;
2283 : struct smb2_lease ls;
2284 0 : struct smb2_handle h1 = {{0}};
2285 0 : struct smb2_handle h2 = {{0}};
2286 : NTSTATUS status;
2287 : char fname[256];
2288 0 : bool ret = true;
2289 : uint64_t lease;
2290 : uint32_t caps;
2291 : struct smbcli_options options;
2292 :
2293 0 : options = tree1->session->transport->options;
2294 :
2295 0 : caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2296 0 : if (!(caps & SMB2_CAP_LEASING)) {
2297 0 : torture_skip(tctx, "leases are not supported");
2298 : }
2299 :
2300 : /*
2301 : * Choose a random name and random lease in case the state is left a
2302 : * little funky.
2303 : */
2304 0 : lease = random();
2305 0 : snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2306 : generate_random_str(tctx, 8));
2307 :
2308 : /* Clean slate */
2309 0 : smb2_util_unlink(tree1, fname);
2310 :
2311 : /* Create with lease */
2312 0 : smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2313 : smb2_util_share_access(""),
2314 : lease,
2315 : smb2_util_lease_state("RH"));
2316 0 : io1.in.durable_open = true;
2317 :
2318 0 : status = smb2_create(tree1, mem_ctx, &io1);
2319 0 : CHECK_STATUS(status, NT_STATUS_OK);
2320 0 : h1 = io1.out.file.handle;
2321 0 : CHECK_VAL(io1.out.durable_open, true);
2322 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2323 :
2324 0 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2325 0 : CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2326 0 : CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2327 0 : CHECK_VAL(io1.out.lease_response.lease_state,
2328 : smb2_util_lease_state("RH"));
2329 :
2330 : /* Disconnect */
2331 0 : talloc_free(tree1);
2332 0 : tree1 = NULL;
2333 :
2334 : /* Open the file in tree2 */
2335 0 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2336 :
2337 0 : status = smb2_create(tree2, mem_ctx, &io2);
2338 0 : CHECK_STATUS(status, NT_STATUS_OK);
2339 0 : h2 = io2.out.file.handle;
2340 0 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2341 :
2342 : /* Reconnect */
2343 0 : if (!torture_smb2_connection_ext(tctx, 0, &options, &tree1)) {
2344 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2345 0 : ret = false;
2346 0 : goto done;
2347 : }
2348 :
2349 0 : ZERO_STRUCT(io1);
2350 0 : io1.in.fname = fname;
2351 0 : io1.in.durable_handle = &h1;
2352 0 : io1.in.lease_request = &ls;
2353 :
2354 : /*
2355 : * Windows7 (build 7000) will give away an open immediately if the
2356 : * original client is gone. (ZML: This seems like a bug. It should give
2357 : * some time for the client to reconnect!)
2358 : */
2359 0 : status = smb2_create(tree1, mem_ctx, &io1);
2360 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2361 0 : h1 = io1.out.file.handle;
2362 :
2363 0 : done:
2364 0 : if (tree1 != NULL){
2365 0 : smb2_util_close(tree1, h1);
2366 0 : smb2_util_unlink(tree1, fname);
2367 0 : talloc_free(tree1);
2368 : }
2369 :
2370 0 : smb2_util_close(tree2, h2);
2371 0 : smb2_util_unlink(tree2, fname);
2372 0 : talloc_free(tree2);
2373 :
2374 0 : return ret;
2375 : }
2376 :
2377 : /**
2378 : * Open with a batch oplock, disconnect, open in another tree, reconnect.
2379 : *
2380 : * This test actually demonstrates a minimum level of respect for the durable
2381 : * open in the face of another open. As long as this test shows an inability to
2382 : * reconnect after an open, the oplock/lease tests above will certainly
2383 : * demonstrate an error on reconnect.
2384 : */
2385 0 : static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2386 : struct smb2_tree *tree1,
2387 : struct smb2_tree *tree2)
2388 : {
2389 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2390 : struct smb2_create io1, io2;
2391 0 : struct smb2_handle h1 = {{0}};
2392 0 : struct smb2_handle h2 = {{0}};
2393 : NTSTATUS status;
2394 : char fname[256];
2395 0 : bool ret = true;
2396 :
2397 : /*
2398 : * Choose a random name and random lease in case the state is left a
2399 : * little funky.
2400 : */
2401 0 : snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2402 : generate_random_str(tctx, 8));
2403 :
2404 : /* Clean slate */
2405 0 : smb2_util_unlink(tree1, fname);
2406 :
2407 : /* Create with batch oplock */
2408 0 : smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2409 0 : io1.in.durable_open = true;
2410 :
2411 0 : status = smb2_create(tree1, mem_ctx, &io1);
2412 0 : CHECK_STATUS(status, NT_STATUS_OK);
2413 0 : h1 = io1.out.file.handle;
2414 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2415 0 : CHECK_VAL(io1.out.durable_open, true);
2416 0 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2417 :
2418 : /* Disconnect */
2419 0 : talloc_free(tree1);
2420 0 : tree1 = NULL;
2421 :
2422 : /* Open the file in tree2 */
2423 0 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2424 :
2425 0 : status = smb2_create(tree2, mem_ctx, &io2);
2426 0 : CHECK_STATUS(status, NT_STATUS_OK);
2427 0 : h2 = io2.out.file.handle;
2428 0 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2429 :
2430 : /* Reconnect */
2431 0 : if (!torture_smb2_connection(tctx, &tree1)) {
2432 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2433 0 : ret = false;
2434 0 : goto done;
2435 : }
2436 :
2437 0 : ZERO_STRUCT(io1);
2438 0 : io1.in.fname = fname;
2439 0 : io1.in.durable_handle = &h1;
2440 :
2441 0 : status = smb2_create(tree1, mem_ctx, &io1);
2442 0 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2443 0 : h1 = io1.out.file.handle;
2444 :
2445 0 : done:
2446 0 : smb2_util_close(tree2, h2);
2447 0 : smb2_util_unlink(tree2, fname);
2448 0 : if (tree1 != NULL) {
2449 0 : smb2_util_close(tree1, h1);
2450 0 : smb2_util_unlink(tree1, fname);
2451 : }
2452 :
2453 0 : talloc_free(tree1);
2454 0 : talloc_free(tree2);
2455 :
2456 0 : return ret;
2457 : }
2458 :
2459 : /**
2460 : * test behaviour with initial allocation size
2461 : */
2462 0 : static bool test_durable_open_alloc_size(struct torture_context *tctx,
2463 : struct smb2_tree *tree)
2464 : {
2465 : NTSTATUS status;
2466 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2467 : char fname[256];
2468 : struct smb2_handle _h;
2469 0 : struct smb2_handle *h = NULL;
2470 : struct smb2_create io;
2471 0 : bool ret = true;
2472 : uint64_t previous_session_id;
2473 : uint64_t alloc_size_step;
2474 0 : uint64_t initial_alloc_size = 0x1000;
2475 0 : const uint8_t *b = NULL;
2476 : struct smbcli_options options;
2477 :
2478 0 : options = tree->session->transport->options;
2479 :
2480 : /* Choose a random name in case the state is left a little funky. */
2481 0 : snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2482 : generate_random_str(tctx, 8));
2483 :
2484 0 : smb2_util_unlink(tree, fname);
2485 :
2486 0 : smb2_oplock_create_share(&io, fname,
2487 : smb2_util_share_access(""),
2488 0 : smb2_util_oplock_level("b"));
2489 0 : io.in.durable_open = true;
2490 0 : io.in.alloc_size = initial_alloc_size;
2491 :
2492 0 : status = smb2_create(tree, mem_ctx, &io);
2493 0 : CHECK_STATUS(status, NT_STATUS_OK);
2494 0 : _h = io.out.file.handle;
2495 0 : h = &_h;
2496 0 : CHECK_NOT_VAL(io.out.alloc_size, 0);
2497 0 : alloc_size_step = io.out.alloc_size;
2498 0 : CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2499 : alloc_size_step, 0);
2500 0 : CHECK_VAL(io.out.durable_open, true);
2501 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2502 :
2503 : /* prepare buffer */
2504 0 : b = talloc_zero_size(mem_ctx, alloc_size_step);
2505 0 : CHECK_NOT_NULL(b);
2506 :
2507 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2508 :
2509 : /* disconnect, reconnect and then do durable reopen */
2510 0 : talloc_free(tree);
2511 0 : tree = NULL;
2512 :
2513 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
2514 : &options, &tree))
2515 : {
2516 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2517 0 : ret = false;
2518 0 : goto done;
2519 : }
2520 :
2521 0 : ZERO_STRUCT(io);
2522 0 : io.in.fname = fname;
2523 0 : io.in.durable_handle = h;
2524 0 : h = NULL;
2525 :
2526 0 : status = smb2_create(tree, mem_ctx, &io);
2527 0 : CHECK_STATUS(status, NT_STATUS_OK);
2528 0 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2529 : alloc_size_step, 0);
2530 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2531 0 : _h = io.out.file.handle;
2532 0 : h = &_h;
2533 :
2534 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2535 :
2536 : /* write one byte */
2537 0 : status = smb2_util_write(tree, *h, b, 0, 1);
2538 0 : CHECK_STATUS(status, NT_STATUS_OK);
2539 :
2540 : /* disconnect, reconnect and then do durable reopen */
2541 0 : talloc_free(tree);
2542 0 : tree = NULL;
2543 :
2544 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
2545 : &options, &tree))
2546 : {
2547 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2548 0 : ret = false;
2549 0 : goto done;
2550 : }
2551 :
2552 0 : ZERO_STRUCT(io);
2553 0 : io.in.fname = fname;
2554 0 : io.in.durable_handle = h;
2555 0 : h = NULL;
2556 :
2557 0 : status = smb2_create(tree, mem_ctx, &io);
2558 0 : CHECK_STATUS(status, NT_STATUS_OK);
2559 0 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2560 : alloc_size_step, 1);
2561 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2562 0 : _h = io.out.file.handle;
2563 0 : h = &_h;
2564 :
2565 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2566 :
2567 : /* write more byte than initial allocation size */
2568 0 : status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2569 :
2570 : /* disconnect, reconnect and then do durable reopen */
2571 0 : talloc_free(tree);
2572 0 : tree = NULL;
2573 :
2574 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
2575 : &options, &tree))
2576 : {
2577 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2578 0 : ret = false;
2579 0 : goto done;
2580 : }
2581 :
2582 0 : ZERO_STRUCT(io);
2583 0 : io.in.fname = fname;
2584 0 : io.in.durable_handle = h;
2585 0 : h = NULL;
2586 :
2587 0 : status = smb2_create(tree, mem_ctx, &io);
2588 0 : CHECK_STATUS(status, NT_STATUS_OK);
2589 0 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2590 : alloc_size_step * 2, alloc_size_step + 1);
2591 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2592 0 : _h = io.out.file.handle;
2593 0 : h = &_h;
2594 :
2595 0 : done:
2596 0 : if (h != NULL) {
2597 0 : smb2_util_close(tree, *h);
2598 : }
2599 :
2600 0 : smb2_util_unlink(tree, fname);
2601 :
2602 0 : talloc_free(tree);
2603 :
2604 0 : talloc_free(mem_ctx);
2605 :
2606 0 : return ret;
2607 : }
2608 :
2609 : /**
2610 : * test behaviour when a disconnect happens while creating a read-only file
2611 : */
2612 0 : static bool test_durable_open_read_only(struct torture_context *tctx,
2613 : struct smb2_tree *tree)
2614 : {
2615 : NTSTATUS status;
2616 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2617 : char fname[256];
2618 : struct smb2_handle _h;
2619 0 : struct smb2_handle *h = NULL;
2620 : struct smb2_create io;
2621 0 : bool ret = true;
2622 : uint64_t previous_session_id;
2623 0 : const uint8_t b = 0;
2624 0 : uint64_t alloc_size = 0;
2625 : struct smbcli_options options;
2626 :
2627 0 : options = tree->session->transport->options;
2628 :
2629 : /* Choose a random name in case the state is left a little funky. */
2630 0 : snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2631 : generate_random_str(tctx, 8));
2632 :
2633 0 : smb2_util_unlink(tree, fname);
2634 :
2635 0 : smb2_oplock_create_share(&io, fname,
2636 : smb2_util_share_access(""),
2637 0 : smb2_util_oplock_level("b"));
2638 0 : io.in.durable_open = true;
2639 0 : io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2640 :
2641 0 : status = smb2_create(tree, mem_ctx, &io);
2642 0 : CHECK_STATUS(status, NT_STATUS_OK);
2643 0 : _h = io.out.file.handle;
2644 0 : h = &_h;
2645 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2646 0 : CHECK_VAL(io.out.durable_open, true);
2647 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2648 :
2649 0 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2650 :
2651 : /* write one byte */
2652 0 : status = smb2_util_write(tree, *h, &b, 0, 1);
2653 0 : CHECK_STATUS(status, NT_STATUS_OK);
2654 :
2655 : /* disconnect, reconnect and then do durable reopen */
2656 0 : talloc_free(tree);
2657 0 : tree = NULL;
2658 :
2659 0 : if (!torture_smb2_connection_ext(tctx, previous_session_id,
2660 : &options, &tree))
2661 : {
2662 0 : torture_warning(tctx, "couldn't reconnect, bailing\n");
2663 0 : ret = false;
2664 0 : goto done;
2665 : }
2666 :
2667 0 : ZERO_STRUCT(io);
2668 0 : io.in.fname = fname;
2669 0 : io.in.durable_handle = h;
2670 0 : h = NULL;
2671 :
2672 0 : status = smb2_create(tree, mem_ctx, &io);
2673 0 : CHECK_STATUS(status, NT_STATUS_OK);
2674 0 : alloc_size = io.out.alloc_size;
2675 0 : CHECK_CREATED_SIZE(&io, EXISTED,
2676 : FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2677 : alloc_size, 1);
2678 0 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2679 0 : _h = io.out.file.handle;
2680 0 : h = &_h;
2681 :
2682 : /* write one byte */
2683 0 : status = smb2_util_write(tree, *h, &b, 1, 1);
2684 0 : CHECK_STATUS(status, NT_STATUS_OK);
2685 :
2686 0 : done:
2687 0 : if (h != NULL) {
2688 : union smb_setfileinfo sfinfo;
2689 :
2690 0 : ZERO_STRUCT(sfinfo);
2691 0 : sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2692 0 : sfinfo.basic_info.in.file.handle = *h;
2693 0 : sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2694 0 : smb2_setinfo_file(tree, &sfinfo);
2695 :
2696 0 : smb2_util_close(tree, *h);
2697 : }
2698 :
2699 0 : smb2_util_unlink(tree, fname);
2700 :
2701 0 : talloc_free(tree);
2702 :
2703 0 : talloc_free(mem_ctx);
2704 :
2705 0 : return ret;
2706 : }
2707 :
2708 : /**
2709 : * durable open with oplock, disconnect, exit
2710 : */
2711 0 : static bool test_durable_open_oplock_disconnect(struct torture_context *tctx,
2712 : struct smb2_tree *tree)
2713 : {
2714 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2715 : struct smb2_create io;
2716 : struct smb2_handle _h;
2717 0 : struct smb2_handle *h = NULL;
2718 : NTSTATUS status;
2719 : char fname[256];
2720 0 : bool ret = true;
2721 :
2722 0 : snprintf(fname, 256, "durable_open_oplock_disconnect_%s.dat",
2723 : generate_random_str(mem_ctx, 8));
2724 :
2725 0 : smb2_util_unlink(tree, fname);
2726 :
2727 0 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
2728 0 : io.in.durable_open = true;
2729 :
2730 0 : status = smb2_create(tree, mem_ctx, &io);
2731 0 : CHECK_STATUS(status, NT_STATUS_OK);
2732 :
2733 0 : _h = io.out.file.handle;
2734 0 : h = &_h;
2735 :
2736 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2737 0 : CHECK_VAL(io.out.durable_open, true);
2738 0 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2739 :
2740 : /* disconnect */
2741 0 : talloc_free(tree);
2742 0 : tree = NULL;
2743 :
2744 0 : done:
2745 0 : if (tree != NULL) {
2746 0 : if (h != NULL) {
2747 0 : smb2_util_close(tree, *h);
2748 : }
2749 0 : smb2_util_unlink(tree, fname);
2750 : }
2751 0 : talloc_free(mem_ctx);
2752 0 : return ret;
2753 : }
2754 :
2755 : /**
2756 : * durable stat open with lease.
2757 : */
2758 0 : static bool test_durable_open_stat_open(struct torture_context *tctx,
2759 : struct smb2_tree *tree)
2760 : {
2761 0 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2762 : struct smb2_create io;
2763 : struct smb2_handle _h;
2764 0 : struct smb2_handle *h = NULL;
2765 : struct smb2_lease ls;
2766 : NTSTATUS status;
2767 : char fname[256];
2768 0 : bool ret = true;
2769 : uint64_t lease;
2770 :
2771 0 : snprintf(fname, 256, "durable_open_stat_open_%s.dat",
2772 : generate_random_str(mem_ctx, 8));
2773 :
2774 : /* Ensure file doesn't exist. */
2775 0 : smb2_util_unlink(tree, fname);
2776 :
2777 : /* Create a normal file. */
2778 0 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
2779 0 : status = smb2_create(tree, mem_ctx, &io);
2780 0 : CHECK_STATUS(status, NT_STATUS_OK);
2781 0 : _h = io.out.file.handle;
2782 0 : h = &_h;
2783 0 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2784 : /* Close. */
2785 0 : smb2_util_close(tree, *h);
2786 0 : h = NULL;
2787 :
2788 : /* Now try a leased, durable handle stat open. */
2789 0 : lease = random();
2790 : /* Create with lease */
2791 0 : smb2_lease_create(&io,
2792 : &ls,
2793 : false /* dir */,
2794 : fname,
2795 : lease,
2796 : smb2_util_lease_state("RH"));
2797 0 : io.in.durable_open = true;
2798 0 : io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
2799 0 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
2800 :
2801 0 : status = smb2_create(tree, mem_ctx, &io);
2802 0 : CHECK_STATUS(status, NT_STATUS_OK);
2803 0 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2804 0 : CHECK_VAL(io.out.durable_open, true);
2805 0 : _h = io.out.file.handle;
2806 0 : h = &_h;
2807 :
2808 0 : done:
2809 0 : if (h != NULL) {
2810 0 : smb2_util_close(tree, *h);
2811 : }
2812 0 : smb2_util_unlink(tree, fname);
2813 0 : talloc_free(mem_ctx);
2814 0 : return ret;
2815 : }
2816 :
2817 966 : struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
2818 : {
2819 : struct torture_suite *suite =
2820 966 : torture_suite_create(ctx, "durable-open");
2821 :
2822 966 : torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2823 966 : torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2824 966 : torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2825 966 : torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2826 966 : torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
2827 966 : torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2828 966 : torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2829 966 : torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2830 966 : torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2831 966 : torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2832 966 : torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2833 966 : torture_suite_add_1smb2_test(suite, "delete_on_close1",
2834 : test_durable_open_delete_on_close1);
2835 966 : torture_suite_add_1smb2_test(suite, "delete_on_close2",
2836 : test_durable_open_delete_on_close2);
2837 966 : torture_suite_add_1smb2_test(suite, "file-position",
2838 : test_durable_open_file_position);
2839 966 : torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2840 966 : torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2841 966 : torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2842 966 : torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2843 966 : torture_suite_add_2smb2_test(suite, "open2-lease",
2844 : test_durable_open_open2_lease);
2845 966 : torture_suite_add_2smb2_test(suite, "open2-oplock",
2846 : test_durable_open_open2_oplock);
2847 966 : torture_suite_add_1smb2_test(suite, "alloc-size",
2848 : test_durable_open_alloc_size);
2849 966 : torture_suite_add_1smb2_test(suite, "read-only",
2850 : test_durable_open_read_only);
2851 966 : torture_suite_add_1smb2_test(suite, "stat-open",
2852 : test_durable_open_stat_open);
2853 :
2854 966 : suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2855 :
2856 966 : return suite;
2857 : }
2858 :
2859 966 : struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
2860 : {
2861 : struct torture_suite *suite =
2862 966 : torture_suite_create(ctx,
2863 : "durable-open-disconnect");
2864 :
2865 966 : torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2866 : test_durable_open_oplock_disconnect);
2867 :
2868 966 : suite->description = talloc_strdup(suite,
2869 : "SMB2-DURABLE-OPEN-DISCONNECT tests");
2870 :
2871 966 : return suite;
2872 : }
|