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 128 : 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 0 : NTSTATUS status;
143 128 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
144 0 : struct smb2_handle _h;
145 128 : struct smb2_handle *h = NULL;
146 128 : bool ret = true;
147 0 : struct smb2_create io;
148 :
149 128 : smb2_util_unlink(tree, fname);
150 :
151 128 : smb2_oplock_create_share(&io, fname,
152 : smb2_util_share_access(test.share_mode),
153 128 : smb2_util_oplock_level(test.level));
154 128 : io.in.durable_open = true;
155 :
156 128 : status = smb2_create(tree, mem_ctx, &io);
157 128 : CHECK_STATUS(status, NT_STATUS_OK);
158 128 : _h = io.out.file.handle;
159 128 : h = &_h;
160 128 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
161 128 : CHECK_VAL(io.out.durable_open, test.expected);
162 128 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(test.level));
163 :
164 128 : done:
165 128 : if (h != NULL) {
166 128 : smb2_util_close(tree, *h);
167 : }
168 128 : smb2_util_unlink(tree, fname);
169 128 : talloc_free(mem_ctx);
170 :
171 128 : return ret;
172 : }
173 :
174 4 : static bool test_durable_open_open_oplock(struct torture_context *tctx,
175 : struct smb2_tree *tree)
176 : {
177 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
178 0 : char fname[256];
179 4 : bool ret = true;
180 0 : int i;
181 :
182 : /* Choose a random name in case the state is left a little funky. */
183 4 : snprintf(fname, 256, "durable_open_open_oplock_%s.dat", generate_random_str(tctx, 8));
184 :
185 4 : smb2_util_unlink(tree, fname);
186 :
187 : /* test various oplock levels with durable open */
188 :
189 132 : for (i = 0; i < NUM_OPLOCK_OPEN_TESTS; i++) {
190 128 : ret = test_one_durable_open_open_oplock(tctx,
191 : tree,
192 : fname,
193 : durable_open_vs_oplock_table[i]);
194 128 : if (ret == false) {
195 0 : goto done;
196 : }
197 : }
198 :
199 4 : done:
200 4 : smb2_util_unlink(tree, fname);
201 4 : talloc_free(tree);
202 4 : talloc_free(mem_ctx);
203 :
204 4 : 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 80 : 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 0 : NTSTATUS status;
277 80 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
278 0 : struct smb2_handle _h;
279 80 : struct smb2_handle *h = NULL;
280 80 : bool ret = true;
281 0 : struct smb2_create io;
282 0 : struct smb2_lease ls;
283 0 : uint64_t lease;
284 0 : uint32_t caps;
285 :
286 80 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
287 80 : if (!(caps & SMB2_CAP_LEASING)) {
288 0 : torture_skip(tctx, "leases are not supported");
289 : }
290 :
291 80 : smb2_util_unlink(tree, fname);
292 :
293 80 : lease = random();
294 :
295 80 : 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 80 : io.in.durable_open = true;
300 :
301 80 : status = smb2_create(tree, mem_ctx, &io);
302 80 : CHECK_STATUS(status, NT_STATUS_OK);
303 80 : _h = io.out.file.handle;
304 80 : h = &_h;
305 80 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
306 80 : CHECK_VAL(io.out.durable_open, test.expected);
307 80 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
308 80 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
309 80 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
310 80 : CHECK_VAL(io.out.lease_response.lease_state,
311 : smb2_util_lease_state(test.type));
312 80 : done:
313 80 : if (h != NULL) {
314 80 : smb2_util_close(tree, *h);
315 : }
316 80 : smb2_util_unlink(tree, fname);
317 80 : talloc_free(mem_ctx);
318 :
319 80 : return ret;
320 : }
321 :
322 4 : static bool test_durable_open_open_lease(struct torture_context *tctx,
323 : struct smb2_tree *tree)
324 : {
325 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
326 0 : char fname[256];
327 4 : bool ret = true;
328 0 : int i;
329 0 : uint32_t caps;
330 :
331 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
332 4 : if (!(caps & SMB2_CAP_LEASING)) {
333 2 : torture_skip(tctx, "leases are not supported");
334 : }
335 :
336 : /* Choose a random name in case the state is left a little funky. */
337 2 : snprintf(fname, 256, "durable_open_open_lease_%s.dat", generate_random_str(tctx, 8));
338 :
339 2 : smb2_util_unlink(tree, fname);
340 :
341 :
342 : /* test various oplock levels with durable open */
343 :
344 82 : for (i = 0; i < NUM_LEASE_OPEN_TESTS; i++) {
345 80 : ret = test_one_durable_open_open_lease(tctx,
346 : tree,
347 : fname,
348 : durable_open_vs_lease_table[i]);
349 80 : if (ret == false) {
350 0 : goto done;
351 : }
352 : }
353 :
354 2 : done:
355 2 : smb2_util_unlink(tree, fname);
356 2 : talloc_free(tree);
357 2 : talloc_free(mem_ctx);
358 :
359 2 : 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 4 : static bool test_durable_open_reopen1(struct torture_context *tctx,
368 : struct smb2_tree *tree)
369 : {
370 0 : NTSTATUS status;
371 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
372 0 : char fname[256];
373 0 : struct smb2_handle _h;
374 4 : struct smb2_handle *h = NULL;
375 0 : struct smb2_create io1, io2;
376 4 : bool ret = true;
377 :
378 : /* Choose a random name in case the state is left a little funky. */
379 4 : snprintf(fname, 256, "durable_open_reopen1_%s.dat",
380 : generate_random_str(tctx, 8));
381 :
382 4 : smb2_util_unlink(tree, fname);
383 :
384 4 : smb2_oplock_create_share(&io1, fname,
385 : smb2_util_share_access(""),
386 4 : smb2_util_oplock_level("b"));
387 4 : io1.in.durable_open = true;
388 :
389 4 : status = smb2_create(tree, mem_ctx, &io1);
390 4 : CHECK_STATUS(status, NT_STATUS_OK);
391 4 : _h = io1.out.file.handle;
392 4 : h = &_h;
393 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
394 4 : CHECK_VAL(io1.out.durable_open, true);
395 4 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
396 :
397 : /* try a durable reconnect while the file is still open */
398 4 : ZERO_STRUCT(io2);
399 4 : io2.in.fname = fname;
400 4 : io2.in.durable_handle = h;
401 :
402 4 : status = smb2_create(tree, mem_ctx, &io2);
403 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
404 :
405 4 : done:
406 4 : if (h != NULL) {
407 4 : smb2_util_close(tree, *h);
408 : }
409 :
410 4 : smb2_util_unlink(tree, fname);
411 :
412 4 : talloc_free(tree);
413 :
414 4 : talloc_free(mem_ctx);
415 :
416 4 : 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 4 : static bool test_durable_open_reopen1a(struct torture_context *tctx,
428 : struct smb2_tree *tree)
429 : {
430 0 : NTSTATUS status;
431 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
432 0 : char fname[256];
433 0 : struct smb2_handle _h;
434 4 : struct smb2_handle *h = NULL;
435 0 : struct smb2_create io;
436 4 : bool ret = true;
437 4 : struct smb2_tree *tree2 = NULL;
438 4 : struct smb2_tree *tree3 = NULL;
439 0 : uint64_t previous_session_id;
440 0 : struct smbcli_options options;
441 0 : struct GUID orig_client_guid;
442 :
443 4 : options = tree->session->transport->options;
444 4 : orig_client_guid = options.client_guid;
445 :
446 : /* Choose a random name in case the state is left a little funky. */
447 4 : snprintf(fname, 256, "durable_open_reopen1a_%s.dat",
448 : generate_random_str(tctx, 8));
449 :
450 4 : smb2_util_unlink(tree, fname);
451 :
452 4 : smb2_oplock_create_share(&io, fname,
453 : smb2_util_share_access(""),
454 4 : smb2_util_oplock_level("b"));
455 4 : io.in.durable_open = true;
456 :
457 4 : status = smb2_create(tree, mem_ctx, &io);
458 4 : CHECK_STATUS(status, NT_STATUS_OK);
459 4 : _h = io.out.file.handle;
460 4 : h = &_h;
461 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
462 4 : CHECK_VAL(io.out.durable_open, true);
463 4 : 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 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
470 :
471 : /* for oplocks, the client guid can be different: */
472 4 : options.client_guid = GUID_random();
473 :
474 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
475 : &options, &tree2);
476 4 : torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
477 :
478 : /*
479 : * check that this has deleted the old session
480 : */
481 :
482 4 : ZERO_STRUCT(io);
483 4 : io.in.fname = fname;
484 4 : io.in.durable_handle = h;
485 :
486 4 : status = smb2_create(tree, mem_ctx, &io);
487 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
488 :
489 4 : TALLOC_FREE(tree);
490 :
491 : /*
492 : * but a durable reconnect on the new session succeeds:
493 : */
494 :
495 4 : ZERO_STRUCT(io);
496 4 : io.in.fname = fname;
497 4 : io.in.durable_handle = h;
498 :
499 4 : status = smb2_create(tree2, mem_ctx, &io);
500 4 : CHECK_STATUS(status, NT_STATUS_OK);
501 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
502 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
503 4 : _h = io.out.file.handle;
504 4 : h = &_h;
505 :
506 : /*
507 : * a session reconnect on a second tcp connection
508 : */
509 :
510 4 : previous_session_id = smb2cli_session_current_id(tree2->session->smbXcli);
511 :
512 : /* the original client_guid works just the same */
513 4 : options.client_guid = orig_client_guid;
514 :
515 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
516 : &options, &tree3);
517 4 : torture_assert_goto(tctx, ret, ret, done, "could not reconnect");
518 :
519 : /*
520 : * check that this has deleted the old session
521 : */
522 :
523 4 : ZERO_STRUCT(io);
524 4 : io.in.fname = fname;
525 4 : io.in.durable_handle = h;
526 :
527 4 : status = smb2_create(tree2, mem_ctx, &io);
528 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
529 :
530 4 : TALLOC_FREE(tree2);
531 :
532 : /*
533 : * but a durable reconnect on the new session succeeds:
534 : */
535 :
536 4 : ZERO_STRUCT(io);
537 4 : io.in.fname = fname;
538 4 : io.in.durable_handle = h;
539 :
540 4 : status = smb2_create(tree3, mem_ctx, &io);
541 4 : CHECK_STATUS(status, NT_STATUS_OK);
542 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
543 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
544 4 : _h = io.out.file.handle;
545 4 : h = &_h;
546 :
547 4 : done:
548 4 : if (tree == NULL) {
549 4 : tree = tree2;
550 : }
551 :
552 4 : if (tree == NULL) {
553 4 : tree = tree3;
554 : }
555 :
556 4 : if (tree != NULL) {
557 4 : if (h != NULL) {
558 4 : smb2_util_close(tree, *h);
559 4 : h = NULL;
560 : }
561 4 : smb2_util_unlink(tree, fname);
562 :
563 4 : talloc_free(tree);
564 : }
565 :
566 4 : talloc_free(mem_ctx);
567 :
568 4 : 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 4 : bool test_durable_open_reopen1a_lease(struct torture_context *tctx,
584 : struct smb2_tree *tree)
585 : {
586 0 : NTSTATUS status;
587 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
588 0 : char fname[256];
589 0 : struct smb2_handle _h;
590 4 : struct smb2_handle *h = NULL;
591 0 : struct smb2_create io;
592 0 : struct smb2_lease ls;
593 0 : uint64_t lease_key;
594 4 : bool ret = true;
595 4 : struct smb2_tree *tree2 = NULL;
596 4 : struct smb2_tree *tree3 = NULL;
597 0 : uint64_t previous_session_id;
598 0 : struct smbcli_options options;
599 0 : struct GUID orig_client_guid;
600 :
601 4 : options = tree->session->transport->options;
602 4 : orig_client_guid = options.client_guid;
603 :
604 : /* Choose a random name in case the state is left a little funky. */
605 4 : snprintf(fname, 256, "durable_v2_open_reopen1a_lease_%s.dat",
606 : generate_random_str(tctx, 8));
607 :
608 4 : smb2_util_unlink(tree, fname);
609 :
610 4 : lease_key = random();
611 4 : smb2_lease_create(&io, &ls, false /* dir */, fname,
612 : lease_key, smb2_util_lease_state("RWH"));
613 4 : io.in.durable_open = true;
614 :
615 4 : status = smb2_create(tree, mem_ctx, &io);
616 4 : CHECK_STATUS(status, NT_STATUS_OK);
617 4 : _h = io.out.file.handle;
618 4 : h = &_h;
619 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
620 4 : CHECK_VAL(io.out.durable_open, true);
621 4 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
622 4 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
623 4 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
624 4 : CHECK_VAL(io.out.lease_response.lease_state,
625 : smb2_util_lease_state("RWH"));
626 4 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
627 4 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
628 :
629 4 : 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 4 : options.client_guid = GUID_random();
638 :
639 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
640 : &options, &tree2);
641 4 : torture_assert_goto(tctx, ret, ret, done, "couldn't reconnect");
642 :
643 : /*
644 : * check that this has deleted the old session
645 : */
646 :
647 4 : ZERO_STRUCT(io);
648 4 : io.in.fname = fname;
649 4 : io.in.durable_handle = h;
650 4 : io.in.lease_request = &ls;
651 4 : status = smb2_create(tree, mem_ctx, &io);
652 4 : CHECK_STATUS(status, NT_STATUS_USER_SESSION_DELETED);
653 4 : 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 4 : ZERO_STRUCT(io);
662 4 : io.in.fname = fname;
663 4 : io.in.durable_handle = h;
664 4 : io.in.lease_request = &ls;
665 4 : status = smb2_create(tree2, mem_ctx, &io);
666 4 : 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 4 : options.client_guid = orig_client_guid;
674 :
675 4 : ret = torture_smb2_connection_ext(tctx, previous_session_id,
676 : &options, &tree3);
677 4 : 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 4 : ZERO_STRUCT(io);
686 4 : io.in.fname = fname;
687 4 : io.in.durable_handle = h;
688 4 : io.in.lease_request = &ls;
689 4 : status = smb2_create(tree2, mem_ctx, &io);
690 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
691 4 : TALLOC_FREE(tree2);
692 :
693 : /*
694 : * but a durable reconnect on the new session succeeds:
695 : */
696 :
697 4 : ZERO_STRUCT(io);
698 4 : io.in.fname = fname;
699 4 : io.in.durable_handle = h;
700 4 : io.in.lease_request = &ls;
701 4 : status = smb2_create(tree3, mem_ctx, &io);
702 4 : CHECK_STATUS(status, NT_STATUS_OK);
703 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
704 2 : CHECK_VAL(io.out.durable_open, false); /* no dh response context... */
705 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
706 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
707 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
708 2 : CHECK_VAL(io.out.lease_response.lease_state,
709 : smb2_util_lease_state("RWH"));
710 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
711 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
712 2 : _h = io.out.file.handle;
713 2 : h = &_h;
714 :
715 4 : done:
716 4 : if (tree == NULL) {
717 4 : tree = tree2;
718 : }
719 :
720 4 : if (tree == NULL) {
721 4 : tree = tree3;
722 : }
723 :
724 4 : if (tree != NULL) {
725 4 : if (h != NULL) {
726 4 : smb2_util_close(tree, *h);
727 : }
728 :
729 4 : smb2_util_unlink(tree, fname);
730 :
731 4 : talloc_free(tree);
732 : }
733 :
734 4 : talloc_free(mem_ctx);
735 :
736 4 : 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 4 : static bool test_durable_open_reopen2(struct torture_context *tctx,
745 : struct smb2_tree *tree)
746 : {
747 0 : NTSTATUS status;
748 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
749 0 : char fname[256];
750 0 : struct smb2_handle _h;
751 4 : struct smb2_handle *h = NULL;
752 0 : struct smb2_create io;
753 4 : bool ret = true;
754 :
755 : /* Choose a random name in case the state is left a little funky. */
756 4 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
757 : generate_random_str(tctx, 8));
758 :
759 4 : smb2_util_unlink(tree, fname);
760 :
761 4 : smb2_oplock_create_share(&io, fname,
762 : smb2_util_share_access(""),
763 4 : smb2_util_oplock_level("b"));
764 4 : io.in.durable_open = true;
765 :
766 4 : status = smb2_create(tree, mem_ctx, &io);
767 4 : CHECK_STATUS(status, NT_STATUS_OK);
768 4 : _h = io.out.file.handle;
769 4 : h = &_h;
770 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
771 4 : CHECK_VAL(io.out.durable_open, true);
772 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
773 :
774 : /* disconnect, leaving the durable in place */
775 4 : TALLOC_FREE(tree);
776 :
777 4 : 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 4 : ZERO_STRUCT(io);
784 : /* the path name is ignored by the server */
785 4 : io.in.fname = fname;
786 4 : io.in.durable_handle = h; /* durable v1 reconnect request */
787 4 : h = NULL;
788 :
789 4 : status = smb2_create(tree, mem_ctx, &io);
790 4 : CHECK_STATUS(status, NT_STATUS_OK);
791 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
792 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
793 4 : _h = io.out.file.handle;
794 4 : h = &_h;
795 :
796 : /* disconnect again, leaving the durable in place */
797 4 : TALLOC_FREE(tree);
798 :
799 4 : 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 4 : ZERO_STRUCT(io);
811 : /* the path name is ignored by the server */
812 4 : io.in.security_flags = 0x78;
813 4 : io.in.oplock_level = 0x78;
814 4 : io.in.impersonation_level = 0x12345678;
815 4 : io.in.create_flags = 0x12345678;
816 4 : io.in.reserved = 0x12345678;
817 4 : io.in.desired_access = 0x12345678;
818 4 : io.in.file_attributes = 0x12345678;
819 4 : io.in.share_access = 0x12345678;
820 4 : io.in.create_disposition = 0x12345678;
821 4 : io.in.create_options = 0x12345678;
822 4 : io.in.fname = "__non_existing_fname__";
823 4 : io.in.durable_handle = h; /* durable v1 reconnect request */
824 4 : h = NULL;
825 :
826 4 : status = smb2_create(tree, mem_ctx, &io);
827 4 : CHECK_STATUS(status, NT_STATUS_OK);
828 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
829 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
830 4 : _h = io.out.file.handle;
831 4 : h = &_h;
832 :
833 : /* disconnect, leaving the durable in place */
834 4 : TALLOC_FREE(tree);
835 :
836 4 : 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 4 : ZERO_STRUCT(io);
849 : /* the path name is ignored by the server */
850 4 : io.in.fname = fname;
851 4 : io.in.durable_handle = h; /* durable v1 reconnect request */
852 4 : io.in.durable_open = true; /* durable v1 handle request */
853 4 : h = NULL;
854 :
855 4 : status = smb2_create(tree, mem_ctx, &io);
856 4 : CHECK_STATUS(status, NT_STATUS_OK);
857 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
858 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
859 4 : _h = io.out.file.handle;
860 4 : h = &_h;
861 :
862 4 : done:
863 4 : if (tree != NULL) {
864 4 : if (h != NULL) {
865 4 : smb2_util_close(tree, *h);
866 : }
867 :
868 4 : smb2_util_unlink(tree, fname);
869 :
870 4 : talloc_free(tree);
871 : }
872 :
873 4 : talloc_free(mem_ctx);
874 :
875 4 : 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 4 : static bool test_durable_open_reopen2_lease(struct torture_context *tctx,
884 : struct smb2_tree *tree)
885 : {
886 0 : NTSTATUS status;
887 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
888 0 : char fname[256];
889 0 : struct smb2_handle _h;
890 4 : struct smb2_handle *h = NULL;
891 0 : struct smb2_create io;
892 0 : struct smb2_lease ls;
893 0 : uint64_t lease_key;
894 4 : bool ret = true;
895 0 : struct smbcli_options options;
896 0 : uint32_t caps;
897 :
898 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
899 4 : if (!(caps & SMB2_CAP_LEASING)) {
900 2 : torture_skip(tctx, "leases are not supported");
901 : }
902 :
903 2 : options = tree->session->transport->options;
904 :
905 : /* Choose a random name in case the state is left a little funky. */
906 2 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
907 : generate_random_str(tctx, 8));
908 :
909 2 : smb2_util_unlink(tree, fname);
910 :
911 2 : lease_key = random();
912 2 : smb2_lease_create(&io, &ls, false /* dir */, fname, lease_key,
913 : smb2_util_lease_state("RWH"));
914 2 : io.in.durable_open = true;
915 :
916 2 : status = smb2_create(tree, mem_ctx, &io);
917 2 : CHECK_STATUS(status, NT_STATUS_OK);
918 2 : _h = io.out.file.handle;
919 2 : h = &_h;
920 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
921 :
922 2 : CHECK_VAL(io.out.durable_open, true);
923 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
924 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
925 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
926 2 : CHECK_VAL(io.out.lease_response.lease_state,
927 : smb2_util_lease_state("RWH"));
928 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
929 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
930 :
931 : /* disconnect, reconnect and then do durable reopen */
932 2 : TALLOC_FREE(tree);
933 :
934 2 : 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 2 : ZERO_STRUCT(io);
950 2 : io.in.fname = "";
951 2 : io.in.durable_handle = h;
952 2 : status = smb2_create(tree, mem_ctx, &io);
953 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
954 :
955 2 : ZERO_STRUCT(io);
956 2 : io.in.fname = "__non_existing_fname__";
957 2 : io.in.durable_handle = h;
958 2 : status = smb2_create(tree, mem_ctx, &io);
959 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
960 :
961 2 : ZERO_STRUCT(io);
962 2 : io.in.fname = fname;
963 2 : io.in.durable_handle = h;
964 2 : status = smb2_create(tree, mem_ctx, &io);
965 2 : 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 2 : ZERO_STRUCT(io);
972 2 : io.in.fname = fname;
973 2 : io.in.durable_open = false;
974 2 : io.in.durable_handle = h;
975 2 : io.in.lease_request = &ls;
976 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
977 : /* a wrong lease key lets the request fail */
978 2 : ls.lease_key.data[0]++;
979 :
980 2 : status = smb2_create(tree, mem_ctx, &io);
981 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
982 :
983 : /* restore the correct lease key */
984 2 : 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 2 : ZERO_STRUCT(io);
993 2 : io.in.fname = "__non_existing_fname__";
994 2 : io.in.durable_open = false;
995 2 : io.in.durable_handle = h;
996 2 : io.in.lease_request = &ls;
997 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
998 :
999 2 : status = smb2_create(tree, mem_ctx, &io);
1000 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1001 :
1002 : /*
1003 : * Now for a succeeding reconnect:
1004 : */
1005 :
1006 2 : ZERO_STRUCT(io);
1007 2 : io.in.fname = fname;
1008 2 : io.in.durable_open = false;
1009 2 : io.in.durable_handle = h;
1010 2 : io.in.lease_request = &ls;
1011 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1012 :
1013 : /* the requested lease state is irrelevant */
1014 2 : ls.lease_state = smb2_util_lease_state("");
1015 :
1016 2 : h = NULL;
1017 :
1018 2 : status = smb2_create(tree, mem_ctx, &io);
1019 2 : CHECK_STATUS(status, NT_STATUS_OK);
1020 :
1021 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1022 2 : CHECK_VAL(io.out.durable_open, false);
1023 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1024 2 : CHECK_VAL(io.out.persistent_open, false);
1025 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1026 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1027 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1028 2 : CHECK_VAL(io.out.lease_response.lease_state,
1029 : smb2_util_lease_state("RWH"));
1030 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1031 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1032 2 : _h = io.out.file.handle;
1033 2 : h = &_h;
1034 :
1035 : /* disconnect one more time */
1036 2 : TALLOC_FREE(tree);
1037 :
1038 2 : 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 2 : ZERO_STRUCT(io);
1050 : /*
1051 : * These are completely ignored by the server
1052 : */
1053 2 : io.in.security_flags = 0x78;
1054 2 : io.in.oplock_level = 0x78;
1055 2 : io.in.impersonation_level = 0x12345678;
1056 2 : io.in.create_flags = 0x12345678;
1057 2 : io.in.reserved = 0x12345678;
1058 2 : io.in.desired_access = 0x12345678;
1059 2 : io.in.file_attributes = 0x12345678;
1060 2 : io.in.share_access = 0x12345678;
1061 2 : io.in.create_disposition = 0x12345678;
1062 2 : 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 2 : io.in.fname = fname;
1072 2 : io.in.durable_open_v2 = false;
1073 2 : io.in.durable_handle_v2 = h;
1074 2 : io.in.lease_request = &ls;
1075 :
1076 : /* the requested lease state is irrelevant */
1077 2 : ls.lease_state = smb2_util_lease_state("");
1078 :
1079 2 : h = NULL;
1080 :
1081 2 : status = smb2_create(tree, mem_ctx, &io);
1082 2 : CHECK_STATUS(status, NT_STATUS_OK);
1083 :
1084 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1085 2 : CHECK_VAL(io.out.durable_open, false);
1086 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1087 2 : CHECK_VAL(io.out.persistent_open, false);
1088 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1089 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease_key);
1090 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease_key);
1091 2 : CHECK_VAL(io.out.lease_response.lease_state,
1092 : smb2_util_lease_state("RWH"));
1093 2 : CHECK_VAL(io.out.lease_response.lease_flags, 0);
1094 2 : CHECK_VAL(io.out.lease_response.lease_duration, 0);
1095 :
1096 2 : _h = io.out.file.handle;
1097 2 : h = &_h;
1098 :
1099 2 : done:
1100 2 : if (tree != NULL) {
1101 2 : if (h != NULL) {
1102 2 : smb2_util_close(tree, *h);
1103 : }
1104 :
1105 2 : smb2_util_unlink(tree, fname);
1106 :
1107 2 : talloc_free(tree);
1108 : }
1109 :
1110 2 : talloc_free(mem_ctx);
1111 :
1112 2 : 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 4 : static bool test_durable_open_reopen2_lease_v2(struct torture_context *tctx,
1121 : struct smb2_tree *tree)
1122 : {
1123 0 : NTSTATUS status;
1124 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1125 0 : char fname[256];
1126 0 : struct smb2_handle _h;
1127 4 : struct smb2_handle *h = NULL;
1128 0 : struct smb2_create io;
1129 0 : struct smb2_lease ls;
1130 0 : uint64_t lease_key;
1131 4 : bool ret = true;
1132 0 : struct smbcli_options options;
1133 0 : uint32_t caps;
1134 :
1135 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1136 4 : if (!(caps & SMB2_CAP_LEASING)) {
1137 2 : torture_skip(tctx, "leases are not supported");
1138 : }
1139 :
1140 2 : options = tree->session->transport->options;
1141 :
1142 : /* Choose a random name in case the state is left a little funky. */
1143 2 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1144 : generate_random_str(tctx, 8));
1145 :
1146 2 : smb2_util_unlink(tree, fname);
1147 :
1148 2 : lease_key = random();
1149 2 : 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 2 : io.in.durable_open = true;
1153 :
1154 2 : status = smb2_create(tree, mem_ctx, &io);
1155 2 : CHECK_STATUS(status, NT_STATUS_OK);
1156 2 : _h = io.out.file.handle;
1157 2 : h = &_h;
1158 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1159 :
1160 2 : CHECK_VAL(io.out.durable_open, true);
1161 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1162 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1163 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1164 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1165 : smb2_util_lease_state("RWH"));
1166 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1167 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1168 :
1169 : /* disconnect, reconnect and then do durable reopen */
1170 2 : TALLOC_FREE(tree);
1171 :
1172 2 : 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 2 : ZERO_STRUCT(io);
1187 2 : io.in.fname = "";
1188 2 : io.in.durable_handle = h;
1189 2 : status = smb2_create(tree, mem_ctx, &io);
1190 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1191 :
1192 2 : ZERO_STRUCT(io);
1193 2 : io.in.fname = "__non_existing_fname__";
1194 2 : io.in.durable_handle = h;
1195 2 : status = smb2_create(tree, mem_ctx, &io);
1196 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1197 :
1198 2 : ZERO_STRUCT(io);
1199 2 : io.in.fname = fname;
1200 2 : io.in.durable_handle = h;
1201 2 : status = smb2_create(tree, mem_ctx, &io);
1202 2 : 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 2 : ZERO_STRUCT(io);
1209 2 : io.in.fname = fname;
1210 2 : io.in.durable_open = false;
1211 2 : io.in.durable_handle = h;
1212 2 : io.in.lease_request_v2 = &ls;
1213 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1214 : /* a wrong lease key lets the request fail */
1215 2 : ls.lease_key.data[0]++;
1216 :
1217 2 : status = smb2_create(tree, mem_ctx, &io);
1218 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1219 :
1220 : /* restore the correct lease key */
1221 2 : 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 2 : ZERO_STRUCT(io);
1230 2 : io.in.fname = "__non_existing_fname__";
1231 2 : io.in.durable_open = false;
1232 2 : io.in.durable_handle = h;
1233 2 : io.in.lease_request_v2 = &ls;
1234 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1235 :
1236 2 : status = smb2_create(tree, mem_ctx, &io);
1237 2 : CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
1238 :
1239 : /*
1240 : * Now for a succeeding reconnect:
1241 : */
1242 :
1243 2 : ZERO_STRUCT(io);
1244 2 : io.in.fname = fname;
1245 2 : io.in.durable_open = false;
1246 2 : io.in.durable_handle = h;
1247 2 : io.in.lease_request_v2 = &ls;
1248 2 : io.in.oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
1249 :
1250 : /* the requested lease state is irrelevant */
1251 2 : ls.lease_state = smb2_util_lease_state("");
1252 :
1253 2 : h = NULL;
1254 :
1255 2 : status = smb2_create(tree, mem_ctx, &io);
1256 2 : CHECK_STATUS(status, NT_STATUS_OK);
1257 :
1258 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1259 2 : CHECK_VAL(io.out.durable_open, false);
1260 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1261 2 : CHECK_VAL(io.out.persistent_open, false);
1262 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1263 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1264 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1265 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1266 : smb2_util_lease_state("RWH"));
1267 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1268 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1269 2 : _h = io.out.file.handle;
1270 2 : h = &_h;
1271 :
1272 : /* disconnect one more time */
1273 2 : TALLOC_FREE(tree);
1274 :
1275 2 : 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 2 : ZERO_STRUCT(io);
1287 : /*
1288 : * These are completely ignored by the server
1289 : */
1290 2 : io.in.security_flags = 0x78;
1291 2 : io.in.oplock_level = 0x78;
1292 2 : io.in.impersonation_level = 0x12345678;
1293 2 : io.in.create_flags = 0x12345678;
1294 2 : io.in.reserved = 0x12345678;
1295 2 : io.in.desired_access = 0x12345678;
1296 2 : io.in.file_attributes = 0x12345678;
1297 2 : io.in.share_access = 0x12345678;
1298 2 : io.in.create_disposition = 0x12345678;
1299 2 : 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 2 : io.in.fname = fname;
1309 2 : io.in.durable_open_v2 = false;
1310 2 : io.in.durable_handle_v2 = h;
1311 2 : io.in.lease_request_v2 = &ls;
1312 :
1313 : /* the requested lease state is irrelevant */
1314 2 : ls.lease_state = smb2_util_lease_state("");
1315 :
1316 2 : h = NULL;
1317 :
1318 2 : status = smb2_create(tree, mem_ctx, &io);
1319 2 : CHECK_STATUS(status, NT_STATUS_OK);
1320 :
1321 2 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1322 2 : CHECK_VAL(io.out.durable_open, false);
1323 2 : CHECK_VAL(io.out.durable_open_v2, false); /* no dh2q response blob */
1324 2 : CHECK_VAL(io.out.persistent_open, false);
1325 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
1326 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[0], lease_key);
1327 2 : CHECK_VAL(io.out.lease_response_v2.lease_key.data[1], ~lease_key);
1328 2 : CHECK_VAL(io.out.lease_response_v2.lease_state,
1329 : smb2_util_lease_state("RWH"));
1330 2 : CHECK_VAL(io.out.lease_response_v2.lease_flags, 0);
1331 2 : CHECK_VAL(io.out.lease_response_v2.lease_duration, 0);
1332 :
1333 2 : _h = io.out.file.handle;
1334 2 : h = &_h;
1335 :
1336 2 : done:
1337 2 : if (tree != NULL) {
1338 2 : if (h != NULL) {
1339 2 : smb2_util_close(tree, *h);
1340 : }
1341 :
1342 2 : smb2_util_unlink(tree, fname);
1343 :
1344 2 : talloc_free(tree);
1345 : }
1346 :
1347 2 : talloc_free(mem_ctx);
1348 :
1349 2 : 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 4 : static bool test_durable_open_reopen2a(struct torture_context *tctx,
1358 : struct smb2_tree *tree)
1359 : {
1360 0 : NTSTATUS status;
1361 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1362 0 : char fname[256];
1363 0 : struct smb2_handle _h;
1364 4 : struct smb2_handle *h = NULL;
1365 0 : struct smb2_create io1, io2;
1366 0 : uint64_t previous_session_id;
1367 4 : bool ret = true;
1368 0 : struct smbcli_options options;
1369 :
1370 4 : options = tree->session->transport->options;
1371 :
1372 : /* Choose a random name in case the state is left a little funky. */
1373 4 : snprintf(fname, 256, "durable_open_reopen2_%s.dat",
1374 : generate_random_str(tctx, 8));
1375 :
1376 4 : smb2_util_unlink(tree, fname);
1377 :
1378 4 : smb2_oplock_create_share(&io1, fname,
1379 : smb2_util_share_access(""),
1380 4 : smb2_util_oplock_level("b"));
1381 4 : io1.in.durable_open = true;
1382 :
1383 4 : status = smb2_create(tree, mem_ctx, &io1);
1384 4 : CHECK_STATUS(status, NT_STATUS_OK);
1385 4 : _h = io1.out.file.handle;
1386 4 : h = &_h;
1387 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1388 4 : CHECK_VAL(io1.out.durable_open, true);
1389 4 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1390 :
1391 : /* disconnect, reconnect and then do durable reopen */
1392 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1393 4 : talloc_free(tree);
1394 4 : tree = NULL;
1395 :
1396 4 : 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 4 : ZERO_STRUCT(io2);
1405 4 : io2.in.fname = fname;
1406 4 : io2.in.durable_handle = h;
1407 4 : h = NULL;
1408 :
1409 4 : status = smb2_create(tree, mem_ctx, &io2);
1410 4 : CHECK_STATUS(status, NT_STATUS_OK);
1411 4 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1412 4 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1413 4 : _h = io2.out.file.handle;
1414 4 : h = &_h;
1415 :
1416 4 : done:
1417 4 : if (tree != NULL) {
1418 4 : if (h != NULL) {
1419 4 : smb2_util_close(tree, *h);
1420 : }
1421 :
1422 4 : smb2_util_unlink(tree, fname);
1423 :
1424 4 : talloc_free(tree);
1425 : }
1426 :
1427 4 : talloc_free(mem_ctx);
1428 :
1429 4 : return ret;
1430 : }
1431 :
1432 :
1433 : /**
1434 : * basic test for doing a durable open:
1435 : * tdis, new tcon, try durable reopen (fails)
1436 : */
1437 4 : static bool test_durable_open_reopen3(struct torture_context *tctx,
1438 : struct smb2_tree *tree)
1439 : {
1440 0 : NTSTATUS status;
1441 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1442 0 : char fname[256];
1443 0 : struct smb2_handle _h;
1444 4 : struct smb2_handle *h = NULL;
1445 0 : struct smb2_create io1, io2;
1446 4 : bool ret = true;
1447 0 : struct smb2_tree *tree2;
1448 :
1449 : /* Choose a random name in case the state is left a little funky. */
1450 4 : snprintf(fname, 256, "durable_open_reopen3_%s.dat",
1451 : generate_random_str(tctx, 8));
1452 :
1453 4 : smb2_util_unlink(tree, fname);
1454 :
1455 4 : smb2_oplock_create_share(&io1, fname,
1456 : smb2_util_share_access(""),
1457 4 : smb2_util_oplock_level("b"));
1458 4 : io1.in.durable_open = true;
1459 :
1460 4 : status = smb2_create(tree, mem_ctx, &io1);
1461 4 : CHECK_STATUS(status, NT_STATUS_OK);
1462 4 : _h = io1.out.file.handle;
1463 4 : h = &_h;
1464 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1465 4 : CHECK_VAL(io1.out.durable_open, true);
1466 4 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1467 :
1468 : /* disconnect, reconnect and then do durable reopen */
1469 4 : status = smb2_tdis(tree);
1470 4 : CHECK_STATUS(status, NT_STATUS_OK);
1471 :
1472 4 : 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 4 : ZERO_STRUCT(io2);
1480 4 : io2.in.fname = fname;
1481 4 : io2.in.durable_handle = h;
1482 :
1483 4 : status = smb2_create(tree2, mem_ctx, &io2);
1484 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1485 :
1486 4 : done:
1487 4 : if (tree != NULL) {
1488 4 : if (h != NULL) {
1489 4 : smb2_util_close(tree, *h);
1490 : }
1491 :
1492 4 : smb2_util_unlink(tree2, fname);
1493 :
1494 4 : talloc_free(tree);
1495 : }
1496 :
1497 4 : talloc_free(mem_ctx);
1498 :
1499 4 : 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 4 : static bool test_durable_open_reopen4(struct torture_context *tctx,
1507 : struct smb2_tree *tree)
1508 : {
1509 0 : NTSTATUS status;
1510 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1511 0 : char fname[256];
1512 0 : struct smb2_handle _h;
1513 4 : struct smb2_handle *h = NULL;
1514 0 : struct smb2_create io1, io2;
1515 4 : bool ret = true;
1516 0 : struct smb2_transport *transport;
1517 0 : struct smb2_session *session2;
1518 0 : struct smb2_tree *tree2;
1519 :
1520 : /* Choose a random name in case the state is left a little funky. */
1521 4 : snprintf(fname, 256, "durable_open_reopen4_%s.dat",
1522 : generate_random_str(tctx, 8));
1523 :
1524 4 : smb2_util_unlink(tree, fname);
1525 :
1526 4 : smb2_oplock_create_share(&io1, fname,
1527 : smb2_util_share_access(""),
1528 4 : smb2_util_oplock_level("b"));
1529 4 : io1.in.durable_open = true;
1530 :
1531 4 : status = smb2_create(tree, mem_ctx, &io1);
1532 4 : CHECK_STATUS(status, NT_STATUS_OK);
1533 4 : _h = io1.out.file.handle;
1534 4 : h = &_h;
1535 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1536 4 : CHECK_VAL(io1.out.durable_open, true);
1537 4 : 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 4 : transport = tree->session->transport;
1544 4 : status = smb2_logoff(tree->session);
1545 4 : CHECK_STATUS(status, NT_STATUS_OK);
1546 :
1547 4 : 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 4 : TALLOC_FREE(tree);
1561 :
1562 4 : 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 4 : ZERO_STRUCT(io2);
1569 4 : io2.in.fname = fname;
1570 4 : io2.in.durable_handle = h;
1571 4 : h = NULL;
1572 :
1573 4 : status = smb2_create(tree2, mem_ctx, &io2);
1574 4 : CHECK_STATUS(status, NT_STATUS_OK);
1575 :
1576 4 : _h = io2.out.file.handle;
1577 4 : h = &_h;
1578 4 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1579 4 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1580 :
1581 4 : done:
1582 4 : 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 4 : talloc_free(mem_ctx);
1593 :
1594 4 : return ret;
1595 : }
1596 :
1597 4 : static bool test_durable_open_delete_on_close1(struct torture_context *tctx,
1598 : struct smb2_tree *tree)
1599 : {
1600 0 : NTSTATUS status;
1601 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1602 0 : char fname[256];
1603 0 : struct smb2_handle _h;
1604 4 : struct smb2_handle *h = NULL;
1605 0 : struct smb2_create io1, io2;
1606 4 : bool ret = true;
1607 4 : uint8_t b = 0;
1608 :
1609 : /* Choose a random name in case the state is left a little funky. */
1610 4 : snprintf(fname, 256, "durable_open_delete_on_close1_%s.dat",
1611 : generate_random_str(tctx, 8));
1612 :
1613 4 : smb2_util_unlink(tree, fname);
1614 :
1615 4 : smb2_oplock_create_share(&io1, fname,
1616 : smb2_util_share_access(""),
1617 4 : smb2_util_oplock_level("b"));
1618 4 : io1.in.durable_open = true;
1619 4 : io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1620 :
1621 4 : status = smb2_create(tree, mem_ctx, &io1);
1622 4 : CHECK_STATUS(status, NT_STATUS_OK);
1623 4 : _h = io1.out.file.handle;
1624 4 : h = &_h;
1625 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1626 4 : CHECK_VAL(io1.out.durable_open, true);
1627 4 : CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("b"));
1628 :
1629 4 : status = smb2_util_write(tree, *h, &b, 0, 1);
1630 4 : CHECK_STATUS(status, NT_STATUS_OK);
1631 :
1632 : /* disconnect, leaving the durable handle in place */
1633 4 : TALLOC_FREE(tree);
1634 :
1635 4 : 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 4 : smb2_oplock_create_share(&io2, fname,
1649 : smb2_util_share_access(""),
1650 4 : smb2_util_oplock_level("b"));
1651 4 : io2.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1652 :
1653 4 : status = smb2_create(tree, mem_ctx, &io2);
1654 4 : CHECK_STATUS(status, NT_STATUS_OK);
1655 4 : _h = io2.out.file.handle;
1656 4 : h = &_h;
1657 4 : CHECK_CREATED_SIZE(&io2, CREATED, FILE_ATTRIBUTE_ARCHIVE, 0, 0);
1658 4 : CHECK_VAL(io2.out.durable_open, false);
1659 4 : CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("b"));
1660 :
1661 4 : done:
1662 4 : if (tree != NULL) {
1663 4 : if (h != NULL) {
1664 4 : smb2_util_close(tree, *h);
1665 : }
1666 :
1667 4 : smb2_util_unlink(tree, fname);
1668 :
1669 4 : talloc_free(tree);
1670 : }
1671 :
1672 4 : talloc_free(mem_ctx);
1673 :
1674 4 : return ret;
1675 : }
1676 :
1677 :
1678 4 : static bool test_durable_open_delete_on_close2(struct torture_context *tctx,
1679 : struct smb2_tree *tree)
1680 : {
1681 0 : NTSTATUS status;
1682 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1683 0 : char fname[256];
1684 0 : struct smb2_handle _h;
1685 4 : struct smb2_handle *h = NULL;
1686 0 : struct smb2_create io;
1687 4 : bool ret = true;
1688 4 : uint8_t b = 0;
1689 0 : uint64_t previous_session_id;
1690 0 : uint64_t alloc_size_step;
1691 0 : struct smbcli_options options;
1692 :
1693 4 : options = tree->session->transport->options;
1694 :
1695 : /* Choose a random name in case the state is left a little funky. */
1696 4 : snprintf(fname, 256, "durable_open_delete_on_close2_%s.dat",
1697 : generate_random_str(tctx, 8));
1698 :
1699 4 : smb2_util_unlink(tree, fname);
1700 :
1701 4 : smb2_oplock_create_share(&io, fname,
1702 : smb2_util_share_access(""),
1703 4 : smb2_util_oplock_level("b"));
1704 4 : io.in.durable_open = true;
1705 4 : io.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1706 :
1707 4 : status = smb2_create(tree, mem_ctx, &io);
1708 4 : CHECK_STATUS(status, NT_STATUS_OK);
1709 4 : _h = io.out.file.handle;
1710 4 : h = &_h;
1711 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1712 4 : CHECK_VAL(io.out.durable_open, true);
1713 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
1714 :
1715 4 : status = smb2_util_write(tree, *h, &b, 0, 1);
1716 4 : CHECK_STATUS(status, NT_STATUS_OK);
1717 :
1718 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1719 :
1720 : /* disconnect, leaving the durable handle in place */
1721 4 : TALLOC_FREE(tree);
1722 :
1723 4 : 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 4 : ZERO_STRUCT(io);
1732 4 : io.in.fname = fname;
1733 4 : io.in.durable_handle = h;
1734 :
1735 4 : status = smb2_create(tree, mem_ctx, &io);
1736 4 : 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 4 : if (tree != NULL) {
1778 4 : if (h != NULL) {
1779 4 : smb2_util_close(tree, *h);
1780 : }
1781 :
1782 4 : smb2_util_unlink(tree, fname);
1783 :
1784 4 : talloc_free(tree);
1785 : }
1786 :
1787 4 : talloc_free(mem_ctx);
1788 :
1789 4 : return ret;
1790 : }
1791 :
1792 : /*
1793 : basic testing of SMB2 durable opens
1794 : regarding the position information on the handle
1795 : */
1796 4 : static bool test_durable_open_file_position(struct torture_context *tctx,
1797 : struct smb2_tree *tree)
1798 : {
1799 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1800 0 : struct smb2_handle h;
1801 0 : struct smb2_create io;
1802 0 : NTSTATUS status;
1803 4 : const char *fname = "durable_open_position.dat";
1804 0 : union smb_fileinfo qfinfo;
1805 0 : union smb_setfileinfo sfinfo;
1806 4 : bool ret = true;
1807 0 : uint64_t pos;
1808 0 : uint64_t previous_session_id;
1809 0 : struct smbcli_options options;
1810 :
1811 4 : options = tree->session->transport->options;
1812 :
1813 4 : smb2_util_unlink(tree, fname);
1814 :
1815 4 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_BATCH);
1816 4 : io.in.durable_open = true;
1817 :
1818 4 : status = smb2_create(tree, mem_ctx, &io);
1819 4 : CHECK_STATUS(status, NT_STATUS_OK);
1820 4 : h = io.out.file.handle;
1821 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1822 4 : CHECK_VAL(io.out.durable_open, true);
1823 4 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1824 :
1825 : /* TODO: check extra blob content */
1826 :
1827 4 : ZERO_STRUCT(qfinfo);
1828 4 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1829 4 : qfinfo.generic.in.file.handle = h;
1830 4 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1831 4 : CHECK_STATUS(status, NT_STATUS_OK);
1832 4 : CHECK_VAL(qfinfo.position_information.out.position, 0);
1833 4 : pos = qfinfo.position_information.out.position;
1834 4 : torture_comment(tctx, "position: %llu\n",
1835 : (unsigned long long)pos);
1836 :
1837 4 : ZERO_STRUCT(sfinfo);
1838 4 : sfinfo.generic.level = RAW_SFILEINFO_POSITION_INFORMATION;
1839 4 : sfinfo.generic.in.file.handle = h;
1840 4 : sfinfo.position_information.in.position = 0x1000;
1841 4 : status = smb2_setinfo_file(tree, &sfinfo);
1842 4 : CHECK_STATUS(status, NT_STATUS_OK);
1843 :
1844 4 : ZERO_STRUCT(qfinfo);
1845 4 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1846 4 : qfinfo.generic.in.file.handle = h;
1847 4 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1848 4 : CHECK_STATUS(status, NT_STATUS_OK);
1849 4 : CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1850 4 : pos = qfinfo.position_information.out.position;
1851 4 : torture_comment(tctx, "position: %llu\n",
1852 : (unsigned long long)pos);
1853 :
1854 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
1855 :
1856 : /* tcp disconnect */
1857 4 : talloc_free(tree);
1858 4 : tree = NULL;
1859 :
1860 : /* do a session reconnect */
1861 4 : 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 4 : ZERO_STRUCT(qfinfo);
1870 4 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1871 4 : qfinfo.generic.in.file.handle = h;
1872 4 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1873 4 : CHECK_STATUS(status, NT_STATUS_FILE_CLOSED);
1874 :
1875 4 : ZERO_STRUCT(io);
1876 4 : io.in.fname = fname;
1877 4 : io.in.durable_handle = &h;
1878 :
1879 4 : status = smb2_create(tree, mem_ctx, &io);
1880 4 : CHECK_STATUS(status, NT_STATUS_OK);
1881 4 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1882 4 : CHECK_VAL(io.out.reserved, 0x00);
1883 4 : CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
1884 4 : CHECK_VAL(io.out.size, 0);
1885 4 : CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
1886 4 : CHECK_VAL(io.out.reserved2, 0);
1887 :
1888 4 : h = io.out.file.handle;
1889 :
1890 4 : ZERO_STRUCT(qfinfo);
1891 4 : qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION;
1892 4 : qfinfo.generic.in.file.handle = h;
1893 4 : status = smb2_getinfo_file(tree, mem_ctx, &qfinfo);
1894 4 : CHECK_STATUS(status, NT_STATUS_OK);
1895 4 : CHECK_VAL(qfinfo.position_information.out.position, 0x1000);
1896 4 : pos = qfinfo.position_information.out.position;
1897 4 : torture_comment(tctx, "position: %llu\n",
1898 : (unsigned long long)pos);
1899 :
1900 4 : smb2_util_close(tree, h);
1901 :
1902 4 : talloc_free(mem_ctx);
1903 :
1904 4 : smb2_util_unlink(tree, fname);
1905 :
1906 4 : done:
1907 4 : talloc_free(tree);
1908 :
1909 4 : return ret;
1910 : }
1911 :
1912 : /*
1913 : Open, disconnect, oplock break, reconnect.
1914 : */
1915 4 : static bool test_durable_open_oplock(struct torture_context *tctx,
1916 : struct smb2_tree *tree1,
1917 : struct smb2_tree *tree2)
1918 : {
1919 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1920 0 : struct smb2_create io1, io2;
1921 4 : struct smb2_handle h1 = {{0}};
1922 4 : struct smb2_handle h2 = {{0}};
1923 0 : NTSTATUS status;
1924 0 : char fname[256];
1925 4 : bool ret = true;
1926 :
1927 : /* Choose a random name in case the state is left a little funky. */
1928 4 : snprintf(fname, 256, "durable_open_oplock_%s.dat", generate_random_str(tctx, 8));
1929 :
1930 : /* Clean slate */
1931 4 : smb2_util_unlink(tree1, fname);
1932 :
1933 : /* Create with batch oplock */
1934 4 : smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
1935 4 : io1.in.durable_open = true;
1936 :
1937 4 : io2 = io1;
1938 4 : io2.in.create_disposition = NTCREATEX_DISP_OPEN;
1939 :
1940 4 : status = smb2_create(tree1, mem_ctx, &io1);
1941 4 : CHECK_STATUS(status, NT_STATUS_OK);
1942 4 : h1 = io1.out.file.handle;
1943 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1944 4 : CHECK_VAL(io1.out.durable_open, true);
1945 4 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1946 :
1947 : /* Disconnect after getting the batch */
1948 4 : talloc_free(tree1);
1949 4 : 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 4 : status = smb2_create(tree2, mem_ctx, &io2);
1957 4 : CHECK_STATUS(status, NT_STATUS_OK);
1958 4 : h2 = io2.out.file.handle;
1959 4 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1960 4 : CHECK_VAL(io2.out.durable_open, true);
1961 4 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
1962 :
1963 : /* What if tree1 tries to come back and reclaim? */
1964 4 : 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 4 : ZERO_STRUCT(io1);
1971 4 : io1.in.fname = fname;
1972 4 : io1.in.durable_handle = &h1;
1973 :
1974 4 : status = smb2_create(tree1, mem_ctx, &io1);
1975 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
1976 :
1977 4 : done:
1978 4 : smb2_util_close(tree2, h2);
1979 4 : smb2_util_unlink(tree2, fname);
1980 :
1981 4 : talloc_free(tree1);
1982 4 : talloc_free(tree2);
1983 :
1984 4 : return ret;
1985 : }
1986 :
1987 : /*
1988 : Open, disconnect, lease break, reconnect.
1989 : */
1990 4 : static bool test_durable_open_lease(struct torture_context *tctx,
1991 : struct smb2_tree *tree1,
1992 : struct smb2_tree *tree2)
1993 : {
1994 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
1995 0 : struct smb2_create io1, io2;
1996 0 : struct smb2_lease ls1, ls2;
1997 4 : struct smb2_handle h1 = {{0}};
1998 4 : struct smb2_handle h2 = {{0}};
1999 0 : NTSTATUS status;
2000 0 : char fname[256];
2001 4 : bool ret = true;
2002 0 : uint64_t lease1, lease2;
2003 0 : uint32_t caps;
2004 :
2005 4 : caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2006 4 : if (!(caps & SMB2_CAP_LEASING)) {
2007 2 : 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 2 : lease1 = random();
2015 2 : lease2 = random();
2016 2 : snprintf(fname, 256, "durable_open_lease_%s.dat", generate_random_str(tctx, 8));
2017 :
2018 : /* Clean slate */
2019 2 : smb2_util_unlink(tree1, fname);
2020 :
2021 : /* Create with lease */
2022 2 : smb2_lease_create(&io1, &ls1, false /* dir */, fname,
2023 : lease1, smb2_util_lease_state("RHW"));
2024 2 : io1.in.durable_open = true;
2025 :
2026 2 : smb2_lease_create(&io2, &ls2, false /* dir */, fname,
2027 : lease2, smb2_util_lease_state("RHW"));
2028 2 : io2.in.durable_open = true;
2029 2 : io2.in.create_disposition = NTCREATEX_DISP_OPEN;
2030 :
2031 2 : status = smb2_create(tree1, mem_ctx, &io1);
2032 2 : CHECK_STATUS(status, NT_STATUS_OK);
2033 2 : h1 = io1.out.file.handle;
2034 2 : CHECK_VAL(io1.out.durable_open, true);
2035 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2036 :
2037 2 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2038 2 : CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease1);
2039 2 : CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease1);
2040 2 : 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 2 : talloc_free(tree1);
2045 2 : 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 2 : status = smb2_create(tree2, mem_ctx, &io2);
2056 2 : CHECK_STATUS(status, NT_STATUS_OK);
2057 2 : h2 = io2.out.file.handle;
2058 2 : CHECK_VAL(io2.out.durable_open, true);
2059 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2060 :
2061 2 : CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2062 2 : CHECK_VAL(io2.out.lease_response.lease_key.data[0], lease2);
2063 2 : CHECK_VAL(io2.out.lease_response.lease_key.data[1], ~lease2);
2064 2 : 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 2 : 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 2 : ZERO_STRUCT(io1);
2075 2 : io1.in.fname = fname;
2076 2 : io1.in.durable_handle = &h1;
2077 2 : io1.in.lease_request = &ls1;
2078 :
2079 2 : status = smb2_create(tree1, mem_ctx, &io1);
2080 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2081 :
2082 2 : done:
2083 2 : smb2_util_close(tree2, h2);
2084 2 : smb2_util_unlink(tree2, fname);
2085 :
2086 2 : talloc_free(tree1);
2087 2 : talloc_free(tree2);
2088 :
2089 2 : return ret;
2090 : }
2091 :
2092 4 : static bool test_durable_open_lock_oplock(struct torture_context *tctx,
2093 : struct smb2_tree *tree)
2094 : {
2095 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2096 0 : struct smb2_create io;
2097 4 : struct smb2_handle h = {{0}};
2098 0 : struct smb2_lock lck;
2099 0 : struct smb2_lock_element el[2];
2100 0 : NTSTATUS status;
2101 0 : char fname[256];
2102 4 : bool ret = true;
2103 :
2104 : /*
2105 : */
2106 4 : snprintf(fname, 256, "durable_open_oplock_lock_%s.dat", generate_random_str(tctx, 8));
2107 :
2108 : /* Clean slate */
2109 4 : smb2_util_unlink(tree, fname);
2110 :
2111 : /* Create with oplock */
2112 :
2113 4 : smb2_oplock_create_share(&io, fname,
2114 : smb2_util_share_access(""),
2115 4 : smb2_util_oplock_level("b"));
2116 4 : io.in.durable_open = true;
2117 :
2118 4 : status = smb2_create(tree, mem_ctx, &io);
2119 4 : CHECK_STATUS(status, NT_STATUS_OK);
2120 4 : h = io.out.file.handle;
2121 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2122 :
2123 4 : CHECK_VAL(io.out.durable_open, true);
2124 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2125 :
2126 4 : ZERO_STRUCT(lck);
2127 4 : ZERO_STRUCT(el);
2128 4 : lck.in.locks = el;
2129 4 : lck.in.lock_count = 0x0001;
2130 4 : lck.in.lock_sequence = 0x00000000;
2131 4 : lck.in.file.handle = h;
2132 4 : el[0].offset = 0;
2133 4 : el[0].length = 1;
2134 4 : el[0].reserved = 0x00000000;
2135 4 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2136 4 : status = smb2_lock(tree, &lck);
2137 4 : CHECK_STATUS(status, NT_STATUS_OK);
2138 :
2139 : /* Disconnect/Reconnect. */
2140 4 : talloc_free(tree);
2141 4 : tree = NULL;
2142 :
2143 4 : 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 4 : ZERO_STRUCT(io);
2150 4 : io.in.fname = fname;
2151 4 : io.in.durable_handle = &h;
2152 :
2153 4 : status = smb2_create(tree, mem_ctx, &io);
2154 4 : CHECK_STATUS(status, NT_STATUS_OK);
2155 4 : h = io.out.file.handle;
2156 :
2157 4 : lck.in.file.handle = h;
2158 4 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2159 4 : status = smb2_lock(tree, &lck);
2160 4 : CHECK_STATUS(status, NT_STATUS_OK);
2161 :
2162 4 : done:
2163 4 : smb2_util_close(tree, h);
2164 4 : smb2_util_unlink(tree, fname);
2165 4 : talloc_free(tree);
2166 :
2167 4 : return ret;
2168 : }
2169 :
2170 : /*
2171 : Open, take BRL, disconnect, reconnect.
2172 : */
2173 4 : static bool test_durable_open_lock_lease(struct torture_context *tctx,
2174 : struct smb2_tree *tree)
2175 : {
2176 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2177 0 : struct smb2_create io;
2178 0 : struct smb2_lease ls;
2179 4 : struct smb2_handle h = {{0}};
2180 0 : struct smb2_lock lck;
2181 0 : struct smb2_lock_element el[2];
2182 0 : NTSTATUS status;
2183 0 : char fname[256];
2184 4 : bool ret = true;
2185 0 : uint64_t lease;
2186 0 : uint32_t caps;
2187 0 : struct smbcli_options options;
2188 :
2189 4 : options = tree->session->transport->options;
2190 :
2191 4 : caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2192 4 : if (!(caps & SMB2_CAP_LEASING)) {
2193 2 : 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 2 : lease = random();
2201 2 : snprintf(fname, 256, "durable_open_lease_lock_%s.dat", generate_random_str(tctx, 8));
2202 :
2203 : /* Clean slate */
2204 2 : smb2_util_unlink(tree, fname);
2205 :
2206 : /* Create with lease */
2207 :
2208 2 : smb2_lease_create(&io, &ls, false /* dir */, fname, lease,
2209 : smb2_util_lease_state("RWH"));
2210 2 : io.in.durable_open = true;
2211 :
2212 2 : status = smb2_create(tree, mem_ctx, &io);
2213 2 : CHECK_STATUS(status, NT_STATUS_OK);
2214 2 : h = io.out.file.handle;
2215 2 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2216 :
2217 2 : CHECK_VAL(io.out.durable_open, true);
2218 2 : CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2219 2 : CHECK_VAL(io.out.lease_response.lease_key.data[0], lease);
2220 2 : CHECK_VAL(io.out.lease_response.lease_key.data[1], ~lease);
2221 2 : CHECK_VAL(io.out.lease_response.lease_state,
2222 : SMB2_LEASE_READ|SMB2_LEASE_HANDLE|SMB2_LEASE_WRITE);
2223 :
2224 2 : ZERO_STRUCT(lck);
2225 2 : ZERO_STRUCT(el);
2226 2 : lck.in.locks = el;
2227 2 : lck.in.lock_count = 0x0001;
2228 2 : lck.in.lock_sequence = 0x00000000;
2229 2 : lck.in.file.handle = h;
2230 2 : el[0].offset = 0;
2231 2 : el[0].length = 1;
2232 2 : el[0].reserved = 0x00000000;
2233 2 : el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
2234 2 : status = smb2_lock(tree, &lck);
2235 2 : CHECK_STATUS(status, NT_STATUS_OK);
2236 :
2237 : /* Disconnect/Reconnect. */
2238 2 : talloc_free(tree);
2239 2 : tree = NULL;
2240 :
2241 2 : 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 2 : ZERO_STRUCT(io);
2248 2 : io.in.fname = fname;
2249 2 : io.in.durable_handle = &h;
2250 2 : io.in.lease_request = &ls;
2251 :
2252 2 : status = smb2_create(tree, mem_ctx, &io);
2253 2 : CHECK_STATUS(status, NT_STATUS_OK);
2254 2 : h = io.out.file.handle;
2255 :
2256 2 : lck.in.file.handle = h;
2257 2 : el[0].flags = SMB2_LOCK_FLAG_UNLOCK;
2258 2 : status = smb2_lock(tree, &lck);
2259 2 : CHECK_STATUS(status, NT_STATUS_OK);
2260 :
2261 2 : done:
2262 2 : smb2_util_close(tree, h);
2263 2 : smb2_util_unlink(tree, fname);
2264 2 : talloc_free(tree);
2265 :
2266 2 : 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 4 : static bool test_durable_open_open2_lease(struct torture_context *tctx,
2278 : struct smb2_tree *tree1,
2279 : struct smb2_tree *tree2)
2280 : {
2281 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2282 0 : struct smb2_create io1, io2;
2283 0 : struct smb2_lease ls;
2284 4 : struct smb2_handle h1 = {{0}};
2285 4 : struct smb2_handle h2 = {{0}};
2286 0 : NTSTATUS status;
2287 0 : char fname[256];
2288 4 : bool ret = true;
2289 0 : uint64_t lease;
2290 0 : uint32_t caps;
2291 0 : struct smbcli_options options;
2292 :
2293 4 : options = tree1->session->transport->options;
2294 :
2295 4 : caps = smb2cli_conn_server_capabilities(tree1->session->transport->conn);
2296 4 : if (!(caps & SMB2_CAP_LEASING)) {
2297 2 : 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 2 : lease = random();
2305 2 : snprintf(fname, 256, "durable_open_open2_lease_%s.dat",
2306 : generate_random_str(tctx, 8));
2307 :
2308 : /* Clean slate */
2309 2 : smb2_util_unlink(tree1, fname);
2310 :
2311 : /* Create with lease */
2312 2 : smb2_lease_create_share(&io1, &ls, false /* dir */, fname,
2313 : smb2_util_share_access(""),
2314 : lease,
2315 : smb2_util_lease_state("RH"));
2316 2 : io1.in.durable_open = true;
2317 :
2318 2 : status = smb2_create(tree1, mem_ctx, &io1);
2319 2 : CHECK_STATUS(status, NT_STATUS_OK);
2320 2 : h1 = io1.out.file.handle;
2321 2 : CHECK_VAL(io1.out.durable_open, true);
2322 2 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2323 :
2324 2 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE);
2325 2 : CHECK_VAL(io1.out.lease_response.lease_key.data[0], lease);
2326 2 : CHECK_VAL(io1.out.lease_response.lease_key.data[1], ~lease);
2327 2 : CHECK_VAL(io1.out.lease_response.lease_state,
2328 : smb2_util_lease_state("RH"));
2329 :
2330 : /* Disconnect */
2331 2 : talloc_free(tree1);
2332 2 : tree1 = NULL;
2333 :
2334 : /* Open the file in tree2 */
2335 2 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2336 :
2337 2 : status = smb2_create(tree2, mem_ctx, &io2);
2338 2 : CHECK_STATUS(status, NT_STATUS_OK);
2339 2 : h2 = io2.out.file.handle;
2340 2 : CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2341 :
2342 : /* Reconnect */
2343 2 : 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 2 : ZERO_STRUCT(io1);
2350 2 : io1.in.fname = fname;
2351 2 : io1.in.durable_handle = &h1;
2352 2 : 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 2 : status = smb2_create(tree1, mem_ctx, &io1);
2360 2 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2361 2 : h1 = io1.out.file.handle;
2362 :
2363 2 : done:
2364 2 : if (tree1 != NULL){
2365 2 : smb2_util_close(tree1, h1);
2366 2 : smb2_util_unlink(tree1, fname);
2367 2 : talloc_free(tree1);
2368 : }
2369 :
2370 2 : smb2_util_close(tree2, h2);
2371 2 : smb2_util_unlink(tree2, fname);
2372 2 : talloc_free(tree2);
2373 :
2374 2 : 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 4 : static bool test_durable_open_open2_oplock(struct torture_context *tctx,
2386 : struct smb2_tree *tree1,
2387 : struct smb2_tree *tree2)
2388 : {
2389 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2390 0 : struct smb2_create io1, io2;
2391 4 : struct smb2_handle h1 = {{0}};
2392 4 : struct smb2_handle h2 = {{0}};
2393 0 : NTSTATUS status;
2394 0 : char fname[256];
2395 4 : 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 4 : snprintf(fname, 256, "durable_open_open2_oplock_%s.dat",
2402 : generate_random_str(tctx, 8));
2403 :
2404 : /* Clean slate */
2405 4 : smb2_util_unlink(tree1, fname);
2406 :
2407 : /* Create with batch oplock */
2408 4 : smb2_oplock_create(&io1, fname, SMB2_OPLOCK_LEVEL_BATCH);
2409 4 : io1.in.durable_open = true;
2410 :
2411 4 : status = smb2_create(tree1, mem_ctx, &io1);
2412 4 : CHECK_STATUS(status, NT_STATUS_OK);
2413 4 : h1 = io1.out.file.handle;
2414 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2415 4 : CHECK_VAL(io1.out.durable_open, true);
2416 4 : CHECK_VAL(io1.out.oplock_level, SMB2_OPLOCK_LEVEL_BATCH);
2417 :
2418 : /* Disconnect */
2419 4 : talloc_free(tree1);
2420 4 : tree1 = NULL;
2421 :
2422 : /* Open the file in tree2 */
2423 4 : smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2424 :
2425 4 : status = smb2_create(tree2, mem_ctx, &io2);
2426 4 : CHECK_STATUS(status, NT_STATUS_OK);
2427 4 : h2 = io2.out.file.handle;
2428 4 : CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2429 :
2430 : /* Reconnect */
2431 4 : 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 4 : ZERO_STRUCT(io1);
2438 4 : io1.in.fname = fname;
2439 4 : io1.in.durable_handle = &h1;
2440 :
2441 4 : status = smb2_create(tree1, mem_ctx, &io1);
2442 4 : CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
2443 4 : h1 = io1.out.file.handle;
2444 :
2445 4 : done:
2446 4 : smb2_util_close(tree2, h2);
2447 4 : smb2_util_unlink(tree2, fname);
2448 4 : if (tree1 != NULL) {
2449 4 : smb2_util_close(tree1, h1);
2450 4 : smb2_util_unlink(tree1, fname);
2451 : }
2452 :
2453 4 : talloc_free(tree1);
2454 4 : talloc_free(tree2);
2455 :
2456 4 : return ret;
2457 : }
2458 :
2459 : /**
2460 : * test behaviour with initial allocation size
2461 : */
2462 4 : static bool test_durable_open_alloc_size(struct torture_context *tctx,
2463 : struct smb2_tree *tree)
2464 : {
2465 0 : NTSTATUS status;
2466 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2467 0 : char fname[256];
2468 0 : struct smb2_handle _h;
2469 4 : struct smb2_handle *h = NULL;
2470 0 : struct smb2_create io;
2471 4 : bool ret = true;
2472 0 : uint64_t previous_session_id;
2473 0 : uint64_t alloc_size_step;
2474 4 : uint64_t initial_alloc_size = 0x1000;
2475 4 : const uint8_t *b = NULL;
2476 0 : struct smbcli_options options;
2477 :
2478 4 : options = tree->session->transport->options;
2479 :
2480 : /* Choose a random name in case the state is left a little funky. */
2481 4 : snprintf(fname, 256, "durable_open_alloc_size_%s.dat",
2482 : generate_random_str(tctx, 8));
2483 :
2484 4 : smb2_util_unlink(tree, fname);
2485 :
2486 4 : smb2_oplock_create_share(&io, fname,
2487 : smb2_util_share_access(""),
2488 4 : smb2_util_oplock_level("b"));
2489 4 : io.in.durable_open = true;
2490 4 : io.in.alloc_size = initial_alloc_size;
2491 :
2492 4 : status = smb2_create(tree, mem_ctx, &io);
2493 4 : CHECK_STATUS(status, NT_STATUS_OK);
2494 4 : _h = io.out.file.handle;
2495 4 : h = &_h;
2496 4 : CHECK_NOT_VAL(io.out.alloc_size, 0);
2497 4 : alloc_size_step = io.out.alloc_size;
2498 4 : CHECK_CREATED_SIZE(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE,
2499 : alloc_size_step, 0);
2500 4 : CHECK_VAL(io.out.durable_open, true);
2501 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2502 :
2503 : /* prepare buffer */
2504 4 : b = talloc_zero_size(mem_ctx, alloc_size_step);
2505 4 : CHECK_NOT_NULL(b);
2506 :
2507 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2508 :
2509 : /* disconnect, reconnect and then do durable reopen */
2510 4 : talloc_free(tree);
2511 4 : tree = NULL;
2512 :
2513 4 : 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 4 : ZERO_STRUCT(io);
2522 4 : io.in.fname = fname;
2523 4 : io.in.durable_handle = h;
2524 4 : h = NULL;
2525 :
2526 4 : status = smb2_create(tree, mem_ctx, &io);
2527 4 : CHECK_STATUS(status, NT_STATUS_OK);
2528 4 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2529 : alloc_size_step, 0);
2530 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2531 4 : _h = io.out.file.handle;
2532 4 : h = &_h;
2533 :
2534 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2535 :
2536 : /* write one byte */
2537 4 : status = smb2_util_write(tree, *h, b, 0, 1);
2538 4 : CHECK_STATUS(status, NT_STATUS_OK);
2539 :
2540 : /* disconnect, reconnect and then do durable reopen */
2541 4 : talloc_free(tree);
2542 4 : tree = NULL;
2543 :
2544 4 : 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 4 : ZERO_STRUCT(io);
2553 4 : io.in.fname = fname;
2554 4 : io.in.durable_handle = h;
2555 4 : h = NULL;
2556 :
2557 4 : status = smb2_create(tree, mem_ctx, &io);
2558 4 : CHECK_STATUS(status, NT_STATUS_OK);
2559 4 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2560 : alloc_size_step, 1);
2561 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2562 4 : _h = io.out.file.handle;
2563 4 : h = &_h;
2564 :
2565 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2566 :
2567 : /* write more byte than initial allocation size */
2568 4 : status = smb2_util_write(tree, *h, b, 1, alloc_size_step);
2569 :
2570 : /* disconnect, reconnect and then do durable reopen */
2571 4 : talloc_free(tree);
2572 4 : tree = NULL;
2573 :
2574 4 : 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 4 : ZERO_STRUCT(io);
2583 4 : io.in.fname = fname;
2584 4 : io.in.durable_handle = h;
2585 4 : h = NULL;
2586 :
2587 4 : status = smb2_create(tree, mem_ctx, &io);
2588 4 : CHECK_STATUS(status, NT_STATUS_OK);
2589 4 : CHECK_CREATED_SIZE(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE,
2590 : alloc_size_step * 2, alloc_size_step + 1);
2591 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2592 4 : _h = io.out.file.handle;
2593 4 : h = &_h;
2594 :
2595 4 : done:
2596 4 : if (h != NULL) {
2597 4 : smb2_util_close(tree, *h);
2598 : }
2599 :
2600 4 : smb2_util_unlink(tree, fname);
2601 :
2602 4 : talloc_free(tree);
2603 :
2604 4 : talloc_free(mem_ctx);
2605 :
2606 4 : return ret;
2607 : }
2608 :
2609 : /**
2610 : * test behaviour when a disconnect happens while creating a read-only file
2611 : */
2612 4 : static bool test_durable_open_read_only(struct torture_context *tctx,
2613 : struct smb2_tree *tree)
2614 : {
2615 0 : NTSTATUS status;
2616 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2617 0 : char fname[256];
2618 0 : struct smb2_handle _h;
2619 4 : struct smb2_handle *h = NULL;
2620 0 : struct smb2_create io;
2621 4 : bool ret = true;
2622 0 : uint64_t previous_session_id;
2623 4 : const uint8_t b = 0;
2624 4 : uint64_t alloc_size = 0;
2625 0 : struct smbcli_options options;
2626 :
2627 4 : options = tree->session->transport->options;
2628 :
2629 : /* Choose a random name in case the state is left a little funky. */
2630 4 : snprintf(fname, 256, "durable_open_initial_alloc_%s.dat",
2631 : generate_random_str(tctx, 8));
2632 :
2633 4 : smb2_util_unlink(tree, fname);
2634 :
2635 4 : smb2_oplock_create_share(&io, fname,
2636 : smb2_util_share_access(""),
2637 4 : smb2_util_oplock_level("b"));
2638 4 : io.in.durable_open = true;
2639 4 : io.in.file_attributes = FILE_ATTRIBUTE_READONLY;
2640 :
2641 4 : status = smb2_create(tree, mem_ctx, &io);
2642 4 : CHECK_STATUS(status, NT_STATUS_OK);
2643 4 : _h = io.out.file.handle;
2644 4 : h = &_h;
2645 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE);
2646 4 : CHECK_VAL(io.out.durable_open, true);
2647 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2648 :
2649 4 : previous_session_id = smb2cli_session_current_id(tree->session->smbXcli);
2650 :
2651 : /* write one byte */
2652 4 : status = smb2_util_write(tree, *h, &b, 0, 1);
2653 4 : CHECK_STATUS(status, NT_STATUS_OK);
2654 :
2655 : /* disconnect, reconnect and then do durable reopen */
2656 4 : talloc_free(tree);
2657 4 : tree = NULL;
2658 :
2659 4 : 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 4 : ZERO_STRUCT(io);
2668 4 : io.in.fname = fname;
2669 4 : io.in.durable_handle = h;
2670 4 : h = NULL;
2671 :
2672 4 : status = smb2_create(tree, mem_ctx, &io);
2673 4 : CHECK_STATUS(status, NT_STATUS_OK);
2674 4 : alloc_size = io.out.alloc_size;
2675 4 : CHECK_CREATED_SIZE(&io, EXISTED,
2676 : FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_ARCHIVE,
2677 : alloc_size, 1);
2678 4 : CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("b"));
2679 4 : _h = io.out.file.handle;
2680 4 : h = &_h;
2681 :
2682 : /* write one byte */
2683 4 : status = smb2_util_write(tree, *h, &b, 1, 1);
2684 4 : CHECK_STATUS(status, NT_STATUS_OK);
2685 :
2686 4 : done:
2687 4 : if (h != NULL) {
2688 0 : union smb_setfileinfo sfinfo;
2689 :
2690 4 : ZERO_STRUCT(sfinfo);
2691 4 : sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
2692 4 : sfinfo.basic_info.in.file.handle = *h;
2693 4 : sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
2694 4 : smb2_setinfo_file(tree, &sfinfo);
2695 :
2696 4 : smb2_util_close(tree, *h);
2697 : }
2698 :
2699 4 : smb2_util_unlink(tree, fname);
2700 :
2701 4 : talloc_free(tree);
2702 :
2703 4 : talloc_free(mem_ctx);
2704 :
2705 4 : 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 0 : struct smb2_create io;
2716 0 : struct smb2_handle _h;
2717 0 : struct smb2_handle *h = NULL;
2718 0 : NTSTATUS status;
2719 0 : 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 4 : static bool test_durable_open_stat_open(struct torture_context *tctx,
2759 : struct smb2_tree *tree)
2760 : {
2761 4 : TALLOC_CTX *mem_ctx = talloc_new(tctx);
2762 0 : struct smb2_create io;
2763 0 : struct smb2_handle _h;
2764 4 : struct smb2_handle *h = NULL;
2765 0 : struct smb2_lease ls;
2766 0 : NTSTATUS status;
2767 0 : char fname[256];
2768 4 : bool ret = true;
2769 0 : uint64_t lease;
2770 :
2771 4 : snprintf(fname, 256, "durable_open_stat_open_%s.dat",
2772 : generate_random_str(mem_ctx, 8));
2773 :
2774 : /* Ensure file doesn't exist. */
2775 4 : smb2_util_unlink(tree, fname);
2776 :
2777 : /* Create a normal file. */
2778 4 : smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
2779 4 : status = smb2_create(tree, mem_ctx, &io);
2780 4 : CHECK_STATUS(status, NT_STATUS_OK);
2781 4 : _h = io.out.file.handle;
2782 4 : h = &_h;
2783 4 : CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2784 : /* Close. */
2785 4 : smb2_util_close(tree, *h);
2786 4 : h = NULL;
2787 :
2788 : /* Now try a leased, durable handle stat open. */
2789 4 : lease = random();
2790 : /* Create with lease */
2791 4 : smb2_lease_create(&io,
2792 : &ls,
2793 : false /* dir */,
2794 : fname,
2795 : lease,
2796 : smb2_util_lease_state("RH"));
2797 4 : io.in.durable_open = true;
2798 4 : io.in.desired_access = SEC_FILE_READ_ATTRIBUTE;
2799 4 : io.in.create_disposition = NTCREATEX_DISP_OPEN;
2800 :
2801 4 : status = smb2_create(tree, mem_ctx, &io);
2802 4 : CHECK_STATUS(status, NT_STATUS_OK);
2803 4 : CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2804 4 : CHECK_VAL(io.out.durable_open, true);
2805 4 : _h = io.out.file.handle;
2806 4 : h = &_h;
2807 :
2808 4 : done:
2809 4 : if (h != NULL) {
2810 4 : smb2_util_close(tree, *h);
2811 : }
2812 4 : smb2_util_unlink(tree, fname);
2813 4 : talloc_free(mem_ctx);
2814 4 : return ret;
2815 : }
2816 :
2817 2354 : struct torture_suite *torture_smb2_durable_open_init(TALLOC_CTX *ctx)
2818 : {
2819 125 : struct torture_suite *suite =
2820 2354 : torture_suite_create(ctx, "durable-open");
2821 :
2822 2354 : torture_suite_add_1smb2_test(suite, "open-oplock", test_durable_open_open_oplock);
2823 2354 : torture_suite_add_1smb2_test(suite, "open-lease", test_durable_open_open_lease);
2824 2354 : torture_suite_add_1smb2_test(suite, "reopen1", test_durable_open_reopen1);
2825 2354 : torture_suite_add_1smb2_test(suite, "reopen1a", test_durable_open_reopen1a);
2826 2354 : torture_suite_add_1smb2_test(suite, "reopen1a-lease", test_durable_open_reopen1a_lease);
2827 2354 : torture_suite_add_1smb2_test(suite, "reopen2", test_durable_open_reopen2);
2828 2354 : torture_suite_add_1smb2_test(suite, "reopen2-lease", test_durable_open_reopen2_lease);
2829 2354 : torture_suite_add_1smb2_test(suite, "reopen2-lease-v2", test_durable_open_reopen2_lease_v2);
2830 2354 : torture_suite_add_1smb2_test(suite, "reopen2a", test_durable_open_reopen2a);
2831 2354 : torture_suite_add_1smb2_test(suite, "reopen3", test_durable_open_reopen3);
2832 2354 : torture_suite_add_1smb2_test(suite, "reopen4", test_durable_open_reopen4);
2833 2354 : torture_suite_add_1smb2_test(suite, "delete_on_close1",
2834 : test_durable_open_delete_on_close1);
2835 2354 : torture_suite_add_1smb2_test(suite, "delete_on_close2",
2836 : test_durable_open_delete_on_close2);
2837 2354 : torture_suite_add_1smb2_test(suite, "file-position",
2838 : test_durable_open_file_position);
2839 2354 : torture_suite_add_2smb2_test(suite, "oplock", test_durable_open_oplock);
2840 2354 : torture_suite_add_2smb2_test(suite, "lease", test_durable_open_lease);
2841 2354 : torture_suite_add_1smb2_test(suite, "lock-oplock", test_durable_open_lock_oplock);
2842 2354 : torture_suite_add_1smb2_test(suite, "lock-lease", test_durable_open_lock_lease);
2843 2354 : torture_suite_add_2smb2_test(suite, "open2-lease",
2844 : test_durable_open_open2_lease);
2845 2354 : torture_suite_add_2smb2_test(suite, "open2-oplock",
2846 : test_durable_open_open2_oplock);
2847 2354 : torture_suite_add_1smb2_test(suite, "alloc-size",
2848 : test_durable_open_alloc_size);
2849 2354 : torture_suite_add_1smb2_test(suite, "read-only",
2850 : test_durable_open_read_only);
2851 2354 : torture_suite_add_1smb2_test(suite, "stat-open",
2852 : test_durable_open_stat_open);
2853 :
2854 2354 : suite->description = talloc_strdup(suite, "SMB2-DURABLE-OPEN tests");
2855 :
2856 2354 : return suite;
2857 : }
2858 :
2859 2354 : struct torture_suite *torture_smb2_durable_open_disconnect_init(TALLOC_CTX *ctx)
2860 : {
2861 125 : struct torture_suite *suite =
2862 2354 : torture_suite_create(ctx,
2863 : "durable-open-disconnect");
2864 :
2865 2354 : torture_suite_add_1smb2_test(suite, "open-oplock-disconnect",
2866 : test_durable_open_oplock_disconnect);
2867 :
2868 2354 : suite->description = talloc_strdup(suite,
2869 : "SMB2-DURABLE-OPEN-DISCONNECT tests");
2870 :
2871 2354 : return suite;
2872 : }
|