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