ctdb-scripts: Support storing statd-callout state in cluster filesystem
[samba4-gss.git] / source4 / torture / smb2 / lease.c
blob6966f97ccb4930178bea1f791f08ac90f6daf976
1 /*
2 Unix SMB/CIFS implementation.
4 test suite for SMB2 leases
6 Copyright (C) Zachary Loafman 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "includes.h"
23 #include <tevent.h>
24 #include "libcli/smb2/smb2.h"
25 #include "libcli/smb2/smb2_calls.h"
26 #include "torture/torture.h"
27 #include "torture/smb2/proto.h"
28 #include "torture/util.h"
29 #include "libcli/smb/smbXcli_base.h"
30 #include "libcli/security/security.h"
31 #include "lib/param/param.h"
32 #include "lease_break_handler.h"
34 #define CHECK_VAL(v, correct) do { \
35 if ((v) != (correct)) { \
36 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
37 __location__, #v, (int)(v), (int)(correct)); \
38 ret = false; \
39 }} while (0)
41 #define CHECK_STATUS(status, correct) do { \
42 if (!NT_STATUS_EQUAL(status, correct)) { \
43 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
44 nt_errstr(status), nt_errstr(correct)); \
45 ret = false; \
46 goto done; \
47 }} while (0)
49 #define CHECK_CREATED(__io, __created, __attribute) \
50 do { \
51 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
52 CHECK_VAL((__io)->out.size, 0); \
53 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
54 CHECK_VAL((__io)->out.reserved2, 0); \
55 } while(0)
57 #define CHECK_LEASE(__io, __state, __oplevel, __key, __flags) \
58 do { \
59 CHECK_VAL((__io)->out.lease_response.lease_version, 1); \
60 if (__oplevel) { \
61 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
62 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
63 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
64 CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
65 } else { \
66 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
67 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
68 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
69 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
70 } \
72 CHECK_VAL((__io)->out.lease_response.lease_flags, (__flags)); \
73 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
74 CHECK_VAL((__io)->out.lease_response.lease_epoch, 0); \
75 } while(0)
77 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags, __parent, __epoch) \
78 do { \
79 CHECK_VAL((__io)->out.lease_response_v2.lease_version, 2); \
80 if (__oplevel) { \
81 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
82 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
83 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
84 CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
85 } else { \
86 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
87 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
88 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
89 CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
90 } \
92 CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
93 if (__flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET) { \
94 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], (__parent)); \
95 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], ~(__parent)); \
96 } else { \
97 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[0], 0); \
98 CHECK_VAL((__io)->out.lease_response_v2.parent_lease_key.data[1], 0); \
99 } \
100 CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
101 CHECK_VAL((__io)->out.lease_response_v2.lease_epoch, (__epoch)); \
102 } while(0)
104 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
105 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
106 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
107 static const uint64_t LEASE4 = 0xBAD0FFEDD00DF00Dull;
109 #define NREQUEST_RESULTS 8
110 static const char *request_results[NREQUEST_RESULTS][2] = {
111 { "", "" },
112 { "R", "R" },
113 { "H", "" },
114 { "W", "" },
115 { "RH", "RH" },
116 { "RW", "RW" },
117 { "HW", "" },
118 { "RHW", "RHW" },
121 static bool test_lease_request(struct torture_context *tctx,
122 struct smb2_tree *tree)
124 TALLOC_CTX *mem_ctx = talloc_new(tctx);
125 struct smb2_create io;
126 struct smb2_lease ls;
127 struct smb2_handle h1 = {};
128 struct smb2_handle h2 = {};
129 NTSTATUS status;
130 const char *fname = "lease_request.dat";
131 const char *fname2 = "lease_request.2.dat";
132 const char *sname = "lease_request.dat:stream";
133 const char *dname = "lease_request.dir";
134 bool ret = true;
135 int i;
136 uint32_t caps;
138 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
139 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
141 smb2_util_unlink(tree, fname);
142 smb2_util_unlink(tree, fname2);
143 smb2_util_rmdir(tree, dname);
145 /* Win7 is happy to grant RHW leases on files. */
146 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
147 status = smb2_create(tree, mem_ctx, &io);
148 CHECK_STATUS(status, NT_STATUS_OK);
149 h1 = io.out.file.handle;
150 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
151 CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
153 /* But will reject leases on directories. */
154 if (!(caps & SMB2_CAP_DIRECTORY_LEASING)) {
155 smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
156 status = smb2_create(tree, mem_ctx, &io);
157 CHECK_STATUS(status, NT_STATUS_OK);
158 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
159 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
160 smb2_util_close(tree, io.out.file.handle);
163 /* Also rejects multiple files leased under the same key. */
164 smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
165 status = smb2_create(tree, mem_ctx, &io);
166 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
168 /* And grants leases on streams (with separate leasekey). */
169 smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW"));
170 status = smb2_create(tree, mem_ctx, &io);
171 h2 = io.out.file.handle;
172 CHECK_STATUS(status, NT_STATUS_OK);
173 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
174 CHECK_LEASE(&io, "RHW", true, LEASE2, 0);
175 smb2_util_close(tree, h2);
177 smb2_util_close(tree, h1);
179 /* Now see what combos are actually granted. */
180 for (i = 0; i < NREQUEST_RESULTS; i++) {
181 torture_comment(tctx, "Requesting lease type %s(%x),"
182 " expecting %s(%x)\n",
183 request_results[i][0], smb2_util_lease_state(request_results[i][0]),
184 request_results[i][1], smb2_util_lease_state(request_results[i][1]));
185 smb2_lease_create(&io, &ls, false, fname, LEASE1,
186 smb2_util_lease_state(request_results[i][0]));
187 status = smb2_create(tree, mem_ctx, &io);
188 h2 = io.out.file.handle;
189 CHECK_STATUS(status, NT_STATUS_OK);
190 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
191 CHECK_LEASE(&io, request_results[i][1], true, LEASE1, 0);
192 smb2_util_close(tree, io.out.file.handle);
195 done:
196 smb2_util_close(tree, h1);
197 smb2_util_close(tree, h2);
199 smb2_util_unlink(tree, fname);
200 smb2_util_unlink(tree, fname2);
201 smb2_util_rmdir(tree, dname);
203 talloc_free(mem_ctx);
205 return ret;
208 static bool test_lease_upgrade(struct torture_context *tctx,
209 struct smb2_tree *tree)
211 TALLOC_CTX *mem_ctx = talloc_new(tctx);
212 struct smb2_create io;
213 struct smb2_lease ls;
214 struct smb2_handle h = {};
215 struct smb2_handle hnew = {};
216 NTSTATUS status;
217 const char *fname = "lease_upgrade.dat";
218 bool ret = true;
219 uint32_t caps;
221 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
222 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
224 smb2_util_unlink(tree, fname);
226 /* Grab a RH lease. */
227 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
228 status = smb2_create(tree, mem_ctx, &io);
229 CHECK_STATUS(status, NT_STATUS_OK);
230 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
231 CHECK_LEASE(&io, "RH", true, LEASE1, 0);
232 h = io.out.file.handle;
234 /* Upgrades (sidegrades?) to RW leave us with an RH. */
235 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW"));
236 status = smb2_create(tree, mem_ctx, &io);
237 CHECK_STATUS(status, NT_STATUS_OK);
238 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
239 CHECK_LEASE(&io, "RH", true, LEASE1, 0);
240 hnew = io.out.file.handle;
242 smb2_util_close(tree, hnew);
244 /* Upgrade to RHW lease. */
245 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
246 status = smb2_create(tree, mem_ctx, &io);
247 CHECK_STATUS(status, NT_STATUS_OK);
248 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
249 CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
250 hnew = io.out.file.handle;
252 smb2_util_close(tree, h);
253 h = hnew;
255 /* Attempt to downgrade - original lease state is maintained. */
256 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
257 status = smb2_create(tree, mem_ctx, &io);
258 CHECK_STATUS(status, NT_STATUS_OK);
259 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
260 CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
261 hnew = io.out.file.handle;
263 smb2_util_close(tree, hnew);
265 done:
266 smb2_util_close(tree, h);
267 smb2_util_close(tree, hnew);
269 smb2_util_unlink(tree, fname);
271 talloc_free(mem_ctx);
273 return ret;
277 * upgrade2 test.
278 * full matrix of lease upgrade combinations
279 * (non-contended case)
281 * The summary of the behaviour is this:
282 * -------------------------------------
283 * An uncontended lease upgrade results in a change
284 * if and only if the requested lease state is
285 * - valid, and
286 * - strictly a superset of the lease state already held.
288 * In that case the resulting lease state is the one
289 * requested in the upgrade.
291 struct lease_upgrade2_test {
292 const char *initial;
293 const char *upgrade_to;
294 const char *expected;
297 #define NUM_LEASE_TYPES 5
298 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
299 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
300 { "", "", "" },
301 { "", "R", "R" },
302 { "", "RH", "RH" },
303 { "", "RW", "RW" },
304 { "", "RWH", "RWH" },
306 { "R", "", "R" },
307 { "R", "R", "R" },
308 { "R", "RH", "RH" },
309 { "R", "RW", "RW" },
310 { "R", "RWH", "RWH" },
312 { "RH", "", "RH" },
313 { "RH", "R", "RH" },
314 { "RH", "RH", "RH" },
315 { "RH", "RW", "RH" },
316 { "RH", "RWH", "RWH" },
318 { "RW", "", "RW" },
319 { "RW", "R", "RW" },
320 { "RW", "RH", "RW" },
321 { "RW", "RW", "RW" },
322 { "RW", "RWH", "RWH" },
324 { "RWH", "", "RWH" },
325 { "RWH", "R", "RWH" },
326 { "RWH", "RH", "RWH" },
327 { "RWH", "RW", "RWH" },
328 { "RWH", "RWH", "RWH" },
331 static bool test_lease_upgrade2(struct torture_context *tctx,
332 struct smb2_tree *tree)
334 TALLOC_CTX *mem_ctx = talloc_new(tctx);
335 struct smb2_handle h = {};
336 struct smb2_handle hnew = {};
337 NTSTATUS status;
338 struct smb2_create io;
339 struct smb2_lease ls;
340 const char *fname = "lease_upgrade2.dat";
341 bool ret = true;
342 int i;
343 uint32_t caps;
345 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
346 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
348 for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
349 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
351 smb2_util_unlink(tree, fname);
353 /* Grab a lease. */
354 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
355 status = smb2_create(tree, mem_ctx, &io);
356 CHECK_STATUS(status, NT_STATUS_OK);
357 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
358 CHECK_LEASE(&io, t.initial, true, LEASE1, 0);
359 h = io.out.file.handle;
361 /* Upgrade. */
362 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
363 status = smb2_create(tree, mem_ctx, &io);
364 CHECK_STATUS(status, NT_STATUS_OK);
365 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
366 CHECK_LEASE(&io, t.expected, true, LEASE1, 0);
367 hnew = io.out.file.handle;
369 smb2_util_close(tree, hnew);
370 smb2_util_close(tree, h);
373 done:
374 smb2_util_close(tree, h);
375 smb2_util_close(tree, hnew);
377 smb2_util_unlink(tree, fname);
379 talloc_free(mem_ctx);
381 return ret;
386 * upgrade3:
387 * full matrix of lease upgrade combinations
388 * (contended case)
390 * We start with 2 leases, and check how one can
391 * be upgraded
393 * The summary of the behaviour is this:
394 * -------------------------------------
396 * If we have two leases (lease1 and lease2) on the same file,
397 * then attempt to upgrade lease1 results in a change if and only
398 * if the requested lease state:
399 * - is valid,
400 * - is strictly a superset of lease1, and
401 * - can held together with lease2.
403 * In that case, the resulting lease state of the upgraded lease1
404 * is the state requested in the upgrade. lease2 is not broken
405 * and remains unchanged.
407 * Note that this contrasts the case of directly opening with
408 * an initial requested lease state, in which case you get that
409 * portion of the requested state that can be shared with the
410 * already existing leases (or the states that they get broken to).
412 struct lease_upgrade3_test {
413 const char *held1;
414 const char *held2;
415 const char *upgrade_to;
416 const char *upgraded_to;
419 #define NUM_UPGRADE3_TESTS ( 20 )
420 struct lease_upgrade3_test lease_upgrade3_tests[NUM_UPGRADE3_TESTS] = {
421 {"R", "R", "", "R" },
422 {"R", "R", "R", "R" },
423 {"R", "R", "RW", "R" },
424 {"R", "R", "RH", "RH" },
425 {"R", "R", "RHW", "R" },
427 {"R", "RH", "", "R" },
428 {"R", "RH", "R", "R" },
429 {"R", "RH", "RW", "R" },
430 {"R", "RH", "RH", "RH" },
431 {"R", "RH", "RHW", "R" },
433 {"RH", "R", "", "RH" },
434 {"RH", "R", "R", "RH" },
435 {"RH", "R", "RW", "RH" },
436 {"RH", "R", "RH", "RH" },
437 {"RH", "R", "RHW", "RH" },
439 {"RH", "RH", "", "RH" },
440 {"RH", "RH", "R", "RH" },
441 {"RH", "RH", "RW", "RH" },
442 {"RH", "RH", "RH", "RH" },
443 {"RH", "RH", "RHW", "RH" },
446 static bool test_lease_upgrade3(struct torture_context *tctx,
447 struct smb2_tree *tree)
449 TALLOC_CTX *mem_ctx = talloc_new(tctx);
450 struct smb2_handle h = {};
451 struct smb2_handle h2 = {};
452 struct smb2_handle hnew = {};
453 NTSTATUS status;
454 struct smb2_create io;
455 struct smb2_lease ls;
456 const char *fname = "lease_upgrade3.dat";
457 bool ret = true;
458 int i;
459 uint32_t caps;
461 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
462 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
464 tree->session->transport->lease.handler = torture_lease_handler;
465 tree->session->transport->lease.private_data = tree;
467 smb2_util_unlink(tree, fname);
469 for (i = 0; i < NUM_UPGRADE3_TESTS; i++) {
470 struct lease_upgrade3_test t = lease_upgrade3_tests[i];
472 smb2_util_unlink(tree, fname);
474 torture_reset_lease_break_info(tctx, &lease_break_info);
476 /* grab first lease */
477 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.held1));
478 status = smb2_create(tree, mem_ctx, &io);
479 CHECK_STATUS(status, NT_STATUS_OK);
480 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
481 CHECK_LEASE(&io, t.held1, true, LEASE1, 0);
482 h = io.out.file.handle;
484 /* grab second lease */
485 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(t.held2));
486 status = smb2_create(tree, mem_ctx, &io);
487 CHECK_STATUS(status, NT_STATUS_OK);
488 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
489 CHECK_LEASE(&io, t.held2, true, LEASE2, 0);
490 h2 = io.out.file.handle;
492 /* no break has happened */
493 CHECK_VAL(lease_break_info.count, 0);
494 CHECK_VAL(lease_break_info.failures, 0);
496 /* try to upgrade lease1 */
497 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
498 status = smb2_create(tree, mem_ctx, &io);
499 CHECK_STATUS(status, NT_STATUS_OK);
500 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
501 CHECK_LEASE(&io, t.upgraded_to, true, LEASE1, 0);
502 hnew = io.out.file.handle;
504 /* no break has happened */
505 CHECK_VAL(lease_break_info.count, 0);
506 CHECK_VAL(lease_break_info.failures, 0);
508 smb2_util_close(tree, hnew);
509 smb2_util_close(tree, h);
510 smb2_util_close(tree, h2);
513 done:
514 smb2_util_close(tree, h);
515 smb2_util_close(tree, hnew);
516 smb2_util_close(tree, h2);
518 smb2_util_unlink(tree, fname);
520 talloc_free(mem_ctx);
522 return ret;
528 break_results should be read as "held lease, new lease, hold broken to, new
529 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
530 tries for RW, key1 will be broken to RH (in this case, not broken at all)
531 and key2 will be granted R.
533 Note: break_results only includes things that Win7 will actually grant (see
534 request_results above).
536 #define NBREAK_RESULTS 16
537 static const char *break_results[NBREAK_RESULTS][4] = {
538 {"R", "R", "R", "R"},
539 {"R", "RH", "R", "RH"},
540 {"R", "RW", "R", "R"},
541 {"R", "RHW", "R", "RH"},
543 {"RH", "R", "RH", "R"},
544 {"RH", "RH", "RH", "RH"},
545 {"RH", "RW", "RH", "R"},
546 {"RH", "RHW", "RH", "RH"},
548 {"RW", "R", "R", "R"},
549 {"RW", "RH", "R", "RH"},
550 {"RW", "RW", "R", "R"},
551 {"RW", "RHW", "R", "RH"},
553 {"RHW", "R", "RH", "R"},
554 {"RHW", "RH", "RH", "RH"},
555 {"RHW", "RW", "RH", "R"},
556 {"RHW", "RHW", "RH", "RH"},
559 static bool test_lease_break(struct torture_context *tctx,
560 struct smb2_tree *tree)
562 TALLOC_CTX *mem_ctx = talloc_new(tctx);
563 struct smb2_create io;
564 struct smb2_lease ls;
565 struct smb2_handle h = {};
566 struct smb2_handle h2 = {};
567 struct smb2_handle h3 = {};
568 NTSTATUS status;
569 const char *fname = "lease_break.dat";
570 bool ret = true;
571 int i;
572 uint32_t caps;
574 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
575 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
577 tree->session->transport->lease.handler = torture_lease_handler;
578 tree->session->transport->lease.private_data = tree;
580 smb2_util_unlink(tree, fname);
582 for (i = 0; i < NBREAK_RESULTS; i++) {
583 const char *held = break_results[i][0];
584 const char *contend = break_results[i][1];
585 const char *brokento = break_results[i][2];
586 const char *granted = break_results[i][3];
587 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
588 "expecting break to %s(%x) and grant of %s(%x)\n",
589 held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
590 brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
592 torture_reset_lease_break_info(tctx, &lease_break_info);
594 /* Grab lease. */
595 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
596 status = smb2_create(tree, mem_ctx, &io);
597 CHECK_STATUS(status, NT_STATUS_OK);
598 h = io.out.file.handle;
599 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
600 CHECK_LEASE(&io, held, true, LEASE1, 0);
602 /* Possibly contend lease. */
603 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
604 status = smb2_create(tree, mem_ctx, &io);
605 CHECK_STATUS(status, NT_STATUS_OK);
606 h2 = io.out.file.handle;
607 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
608 CHECK_LEASE(&io, granted, true, LEASE2, 0);
610 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
611 CHECK_BREAK_INFO(held, brokento, LEASE1);
612 } else {
613 CHECK_NO_BREAK(tctx);
616 torture_reset_lease_break_info(tctx, &lease_break_info);
619 Now verify that an attempt to upgrade LEASE1 results in no
620 break and no change in LEASE1.
622 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
623 status = smb2_create(tree, mem_ctx, &io);
624 CHECK_STATUS(status, NT_STATUS_OK);
625 h3 = io.out.file.handle;
626 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
627 CHECK_LEASE(&io, brokento, true, LEASE1, 0);
628 CHECK_VAL(lease_break_info.count, 0);
629 CHECK_VAL(lease_break_info.failures, 0);
631 smb2_util_close(tree, h);
632 smb2_util_close(tree, h2);
633 smb2_util_close(tree, h3);
635 status = smb2_util_unlink(tree, fname);
636 CHECK_STATUS(status, NT_STATUS_OK);
639 done:
640 smb2_util_close(tree, h);
641 smb2_util_close(tree, h2);
643 smb2_util_unlink(tree, fname);
645 talloc_free(mem_ctx);
647 return ret;
650 static bool test_lease_nobreakself(struct torture_context *tctx,
651 struct smb2_tree *tree)
653 TALLOC_CTX *mem_ctx = talloc_new(tctx);
654 struct smb2_create io;
655 struct smb2_lease ls;
656 struct smb2_handle h1 = {};
657 struct smb2_handle h2 = {};
658 NTSTATUS status;
659 const char *fname = "lease_nobreakself.dat";
660 bool ret = true;
661 uint32_t caps;
662 char c = 0;
664 caps = smb2cli_conn_server_capabilities(
665 tree->session->transport->conn);
666 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
668 smb2_util_unlink(tree, fname);
670 /* Win7 is happy to grant RHW leases on files. */
671 smb2_lease_create(&io, &ls, false, fname, LEASE1,
672 smb2_util_lease_state("R"));
673 status = smb2_create(tree, mem_ctx, &io);
674 CHECK_STATUS(status, NT_STATUS_OK);
675 h1 = io.out.file.handle;
676 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
677 CHECK_LEASE(&io, "R", true, LEASE1, 0);
679 smb2_lease_create(&io, &ls, false, fname, LEASE2,
680 smb2_util_lease_state("R"));
681 status = smb2_create(tree, mem_ctx, &io);
682 CHECK_STATUS(status, NT_STATUS_OK);
683 h2 = io.out.file.handle;
684 CHECK_LEASE(&io, "R", true, LEASE2, 0);
686 torture_reset_lease_break_info(tctx, &lease_break_info);
688 tree->session->transport->lease.handler = torture_lease_handler;
689 tree->session->transport->lease.private_data = tree;
691 /* Make sure we don't break ourselves on write */
693 status = smb2_util_write(tree, h1, &c, 0, 1);
694 CHECK_STATUS(status, NT_STATUS_OK);
695 CHECK_BREAK_INFO("R", "", LEASE2);
697 /* Try the other way round. First, upgrade LEASE2 to R again */
699 smb2_lease_create(&io, &ls, false, fname, LEASE2,
700 smb2_util_lease_state("R"));
701 status = smb2_create(tree, mem_ctx, &io);
702 CHECK_STATUS(status, NT_STATUS_OK);
703 CHECK_LEASE(&io, "R", true, LEASE2, 0);
704 smb2_util_close(tree, io.out.file.handle);
706 /* Now break LEASE1 via h2 */
708 torture_reset_lease_break_info(tctx, &lease_break_info);
709 status = smb2_util_write(tree, h2, &c, 0, 1);
710 CHECK_STATUS(status, NT_STATUS_OK);
711 CHECK_BREAK_INFO("R", "", LEASE1);
713 /* .. and break LEASE2 via h1 */
715 torture_reset_lease_break_info(tctx, &lease_break_info);
716 status = smb2_util_write(tree, h1, &c, 0, 1);
717 CHECK_STATUS(status, NT_STATUS_OK);
718 CHECK_BREAK_INFO("R", "", LEASE2);
720 done:
721 smb2_util_close(tree, h2);
722 smb2_util_close(tree, h1);
723 smb2_util_unlink(tree, fname);
724 talloc_free(mem_ctx);
725 return ret;
728 static bool test_lease_statopen(struct torture_context *tctx,
729 struct smb2_tree *tree)
731 TALLOC_CTX *mem_ctx = talloc_new(tctx);
732 struct smb2_create io;
733 struct smb2_lease ls;
734 struct smb2_handle h1 = {};
735 struct smb2_handle h2 = {};
736 NTSTATUS status;
737 const char *fname = "lease_statopen.dat";
738 bool ret = true;
739 uint32_t caps;
741 caps = smb2cli_conn_server_capabilities(
742 tree->session->transport->conn);
743 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
745 smb2_util_unlink(tree, fname);
747 /* Create file. */
748 smb2_lease_create(&io, &ls, false, fname, LEASE1,
749 smb2_util_lease_state("RWH"));
750 status = smb2_create(tree, mem_ctx, &io);
751 CHECK_STATUS(status, NT_STATUS_OK);
752 h1 = io.out.file.handle;
753 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
754 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
755 smb2_util_close(tree, h1);
757 /* Stat open file with RWH lease. */
758 smb2_lease_create_share(&io, &ls, false, fname, 0, LEASE1,
759 smb2_util_lease_state("RWH"));
760 io.in.desired_access = FILE_READ_ATTRIBUTES;
761 status = smb2_create(tree, mem_ctx, &io);
762 CHECK_STATUS(status, NT_STATUS_OK);
763 h2 = io.out.file.handle;
764 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
766 torture_reset_lease_break_info(tctx, &lease_break_info);
768 tree->session->transport->lease.handler = torture_lease_handler;
769 tree->session->transport->lease.private_data = tree;
771 /* Ensure non-stat open doesn't break and gets same lease
772 state as existing stat open. */
773 smb2_lease_create(&io, &ls, false, fname, LEASE1,
774 smb2_util_lease_state(""));
775 status = smb2_create(tree, mem_ctx, &io);
776 CHECK_STATUS(status, NT_STATUS_OK);
777 h1 = io.out.file.handle;
778 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
779 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
781 CHECK_NO_BREAK(tctx);
782 smb2_util_close(tree, h1);
784 /* Open with conflicting lease. stat open should break down to RH */
785 smb2_lease_create(&io, &ls, false, fname, LEASE2,
786 smb2_util_lease_state("RWH"));
787 status = smb2_create(tree, mem_ctx, &io);
788 CHECK_STATUS(status, NT_STATUS_OK);
789 h1 = io.out.file.handle;
790 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
791 CHECK_LEASE(&io, "RH", true, LEASE2, 0);
793 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
795 done:
796 smb2_util_close(tree, h2);
797 smb2_util_close(tree, h1);
798 smb2_util_unlink(tree, fname);
799 talloc_free(mem_ctx);
800 return ret;
803 static bool test_lease_statopen2(struct torture_context *tctx,
804 struct smb2_tree *tree)
806 TALLOC_CTX *mem_ctx = talloc_new(tctx);
807 struct smb2_create io;
808 struct smb2_lease ls;
809 struct smb2_handle h1 = {};
810 struct smb2_handle h2 = {};
811 struct smb2_handle h3 = {};
812 NTSTATUS status;
813 const char *fname = "lease_statopen2.dat";
814 bool ret = true;
815 uint32_t caps;
817 caps = smb2cli_conn_server_capabilities(
818 tree->session->transport->conn);
819 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
821 smb2_util_unlink(tree, fname);
822 torture_reset_lease_break_info(tctx, &lease_break_info);
823 tree->session->transport->lease.handler = torture_lease_handler;
824 tree->session->transport->lease.private_data = tree;
826 status = torture_smb2_testfile(tree, fname, &h1);
827 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
828 "smb2_create failed\n");
829 smb2_util_close(tree, h1);
830 ZERO_STRUCT(h1);
832 /* Open file with RWH lease. */
833 smb2_lease_create_share(&io, &ls, false, fname,
834 smb2_util_share_access("RWD"),
835 LEASE1,
836 smb2_util_lease_state("RWH"));
837 io.in.desired_access = SEC_FILE_WRITE_DATA;
838 status = smb2_create(tree, mem_ctx, &io);
839 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
840 "smb2_create failed\n");
841 h1 = io.out.file.handle;
842 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
844 /* Stat open */
845 ZERO_STRUCT(io);
846 io.in.desired_access = FILE_READ_ATTRIBUTES;
847 io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
848 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
849 io.in.create_disposition = NTCREATEX_DISP_OPEN;
850 io.in.fname = fname;
851 status = smb2_create(tree, mem_ctx, &io);
852 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
853 "smb2_create failed\n");
854 h2 = io.out.file.handle;
856 /* Open file with RWH lease. */
857 smb2_lease_create_share(&io, &ls, false, fname,
858 smb2_util_share_access("RWD"),
859 LEASE1,
860 smb2_util_lease_state("RWH"));
861 io.in.desired_access = SEC_FILE_WRITE_DATA;
862 status = smb2_create(tree, mem_ctx, &io);
863 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
864 "smb2_create failed\n");
865 h3 = io.out.file.handle;
866 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
868 done:
869 if (!smb2_util_handle_empty(h3)) {
870 smb2_util_close(tree, h3);
872 if (!smb2_util_handle_empty(h2)) {
873 smb2_util_close(tree, h2);
875 if (!smb2_util_handle_empty(h1)) {
876 smb2_util_close(tree, h1);
878 smb2_util_unlink(tree, fname);
879 talloc_free(mem_ctx);
880 return ret;
883 static bool test_lease_statopen3(struct torture_context *tctx,
884 struct smb2_tree *tree)
886 TALLOC_CTX *mem_ctx = talloc_new(tctx);
887 struct smb2_create io;
888 struct smb2_lease ls;
889 struct smb2_handle h1 = {};
890 struct smb2_handle h2 = {};
891 NTSTATUS status;
892 const char *fname = "lease_statopen3.dat";
893 bool ret = true;
894 uint32_t caps;
896 caps = smb2cli_conn_server_capabilities(
897 tree->session->transport->conn);
898 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
900 smb2_util_unlink(tree, fname);
901 torture_reset_lease_break_info(tctx, &lease_break_info);
902 tree->session->transport->lease.handler = torture_lease_handler;
903 tree->session->transport->lease.private_data = tree;
905 status = torture_smb2_testfile(tree, fname, &h1);
906 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
907 "smb2_create failed\n");
908 smb2_util_close(tree, h1);
909 ZERO_STRUCT(h1);
911 /* Stat open */
912 ZERO_STRUCT(io);
913 io.in.desired_access = FILE_READ_ATTRIBUTES;
914 io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
915 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
916 io.in.create_disposition = NTCREATEX_DISP_OPEN;
917 io.in.fname = fname;
918 status = smb2_create(tree, mem_ctx, &io);
919 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
920 "smb2_create failed\n");
921 h1 = io.out.file.handle;
923 /* Open file with RWH lease. */
924 smb2_lease_create_share(&io, &ls, false, fname,
925 smb2_util_share_access("RWD"),
926 LEASE1,
927 smb2_util_lease_state("RWH"));
928 io.in.desired_access = SEC_FILE_WRITE_DATA;
929 status = smb2_create(tree, mem_ctx, &io);
930 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
931 "smb2_create failed\n");
932 h2 = io.out.file.handle;
933 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
935 done:
936 if (!smb2_util_handle_empty(h1)) {
937 smb2_util_close(tree, h1);
939 if (!smb2_util_handle_empty(h2)) {
940 smb2_util_close(tree, h2);
942 smb2_util_unlink(tree, fname);
943 talloc_free(mem_ctx);
944 return ret;
947 static bool test_lease_statopen4_do(struct torture_context *tctx,
948 struct smb2_tree *tree,
949 uint32_t access_mask,
950 bool expect_stat_open)
952 TALLOC_CTX *mem_ctx = talloc_new(tctx);
953 struct smb2_create io;
954 struct smb2_lease ls;
955 struct smb2_handle h1 = {};
956 struct smb2_handle h2 = {};
957 struct smb2_handle h3 = {};
958 NTSTATUS status;
959 const char *fname = "lease_statopen2.dat";
960 bool ret = true;
962 /* Open file with RWH lease. */
963 smb2_lease_create_share(&io, &ls, false, fname,
964 smb2_util_share_access("RWD"),
965 LEASE1,
966 smb2_util_lease_state("RWH"));
967 io.in.desired_access = SEC_FILE_ALL;
968 status = smb2_create(tree, mem_ctx, &io);
969 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
970 "smb2_create failed\n");
971 h1 = io.out.file.handle;
972 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
974 /* Stat open */
975 ZERO_STRUCT(io);
976 io.in.desired_access = access_mask;
977 io.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
978 io.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
979 io.in.create_disposition = NTCREATEX_DISP_OPEN;
980 io.in.fname = fname;
981 status = smb2_create(tree, mem_ctx, &io);
982 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
983 "smb2_create failed\n");
984 h2 = io.out.file.handle;
986 if (expect_stat_open) {
987 CHECK_NO_BREAK(tctx);
988 if (!ret) {
989 goto done;
991 } else {
992 CHECK_VAL(lease_break_info.count, 1);
993 if (!ret) {
994 goto done;
997 * Don't bother checking the lease state of an additional open
998 * below...
1000 goto done;
1003 /* Open file with RWH lease. */
1004 smb2_lease_create_share(&io, &ls, false, fname,
1005 smb2_util_share_access("RWD"),
1006 LEASE1,
1007 smb2_util_lease_state("RWH"));
1008 io.in.desired_access = SEC_FILE_WRITE_DATA;
1009 status = smb2_create(tree, mem_ctx, &io);
1010 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1011 "smb2_create failed\n");
1012 h3 = io.out.file.handle;
1013 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
1015 done:
1016 if (!smb2_util_handle_empty(h3)) {
1017 smb2_util_close(tree, h3);
1019 if (!smb2_util_handle_empty(h2)) {
1020 smb2_util_close(tree, h2);
1022 if (!smb2_util_handle_empty(h1)) {
1023 smb2_util_close(tree, h1);
1025 talloc_free(mem_ctx);
1026 return ret;
1029 static bool test_lease_statopen4(struct torture_context *tctx,
1030 struct smb2_tree *tree)
1032 const char *fname = "lease_statopen4.dat";
1033 struct smb2_handle h1 = {};
1034 uint32_t caps;
1035 size_t i;
1036 NTSTATUS status;
1037 bool ret = true;
1038 struct {
1039 uint32_t access_mask;
1040 bool expect_stat_open;
1041 } tests[] = {
1043 .access_mask = FILE_READ_DATA,
1044 .expect_stat_open = false,
1047 .access_mask = FILE_WRITE_DATA,
1048 .expect_stat_open = false,
1051 .access_mask = FILE_READ_EA,
1052 .expect_stat_open = false,
1055 .access_mask = FILE_WRITE_EA,
1056 .expect_stat_open = false,
1059 .access_mask = FILE_EXECUTE,
1060 .expect_stat_open = false,
1063 .access_mask = FILE_READ_ATTRIBUTES,
1064 .expect_stat_open = true,
1067 .access_mask = FILE_WRITE_ATTRIBUTES,
1068 .expect_stat_open = true,
1071 .access_mask = DELETE_ACCESS,
1072 .expect_stat_open = false,
1075 .access_mask = READ_CONTROL_ACCESS,
1076 .expect_stat_open = true,
1079 .access_mask = WRITE_DAC_ACCESS,
1080 .expect_stat_open = false,
1083 .access_mask = WRITE_OWNER_ACCESS,
1084 .expect_stat_open = false,
1087 .access_mask = SYNCHRONIZE_ACCESS,
1088 .expect_stat_open = true,
1092 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1093 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1095 smb2_util_unlink(tree, fname);
1096 tree->session->transport->lease.handler = torture_lease_handler;
1097 tree->session->transport->lease.private_data = tree;
1099 status = torture_smb2_testfile(tree, fname, &h1);
1100 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
1101 "smb2_create failed\n");
1102 smb2_util_close(tree, h1);
1103 ZERO_STRUCT(h1);
1105 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1106 torture_reset_lease_break_info(tctx, &lease_break_info);
1108 ret = test_lease_statopen4_do(tctx,
1109 tree,
1110 tests[i].access_mask,
1111 tests[i].expect_stat_open);
1112 if (ret == true) {
1113 continue;
1115 torture_result(tctx, TORTURE_FAIL,
1116 "test %zu: access_mask: %s, "
1117 "expect_stat_open: %s\n",
1119 get_sec_mask_str(tree, tests[i].access_mask),
1120 tests[i].expect_stat_open ? "yes" : "no");
1121 goto done;
1124 done:
1125 smb2_util_unlink(tree, fname);
1126 return ret;
1129 static void torture_oplock_break_callback(struct smb2_request *req)
1131 NTSTATUS status;
1132 struct smb2_break br;
1134 ZERO_STRUCT(br);
1135 status = smb2_break_recv(req, &br);
1136 if (!NT_STATUS_IS_OK(status))
1137 lease_break_info.oplock_failures++;
1139 return;
1142 /* a oplock break request handler */
1143 static bool torture_oplock_handler(struct smb2_transport *transport,
1144 const struct smb2_handle *handle,
1145 uint8_t level, void *private_data)
1147 struct smb2_tree *tree = private_data;
1148 struct smb2_request *req;
1149 struct smb2_break br;
1151 lease_break_info.oplock_handle = *handle;
1152 lease_break_info.oplock_level = level;
1153 lease_break_info.oplock_count++;
1155 ZERO_STRUCT(br);
1156 br.in.file.handle = *handle;
1157 br.in.oplock_level = level;
1159 if (lease_break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
1160 req = smb2_break_send(tree, &br);
1161 req->async.fn = torture_oplock_break_callback;
1162 req->async.private_data = NULL;
1164 lease_break_info.held_oplock_level = level;
1166 return true;
1169 #define NOPLOCK_RESULTS 12
1170 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
1171 {"R", "s", "R", "s"},
1172 {"R", "x", "R", "s"},
1173 {"R", "b", "R", "s"},
1175 {"RH", "s", "RH", ""},
1176 {"RH", "x", "RH", ""},
1177 {"RH", "b", "RH", ""},
1179 {"RW", "s", "R", "s"},
1180 {"RW", "x", "R", "s"},
1181 {"RW", "b", "R", "s"},
1183 {"RHW", "s", "RH", ""},
1184 {"RHW", "x", "RH", ""},
1185 {"RHW", "b", "RH", ""},
1188 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
1189 {"s", "R", "s", "R"},
1190 {"s", "RH", "s", "R"},
1191 {"s", "RW", "s", "R"},
1192 {"s", "RHW", "s", "R"},
1194 {"x", "R", "s", "R"},
1195 {"x", "RH", "s", "R"},
1196 {"x", "RW", "s", "R"},
1197 {"x", "RHW", "s", "R"},
1199 {"b", "R", "s", "R"},
1200 {"b", "RH", "s", "R"},
1201 {"b", "RW", "s", "R"},
1202 {"b", "RHW", "s", "R"},
1205 static bool test_lease_oplock(struct torture_context *tctx,
1206 struct smb2_tree *tree)
1208 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1209 struct smb2_create io;
1210 struct smb2_lease ls;
1211 struct smb2_handle h = {};
1212 struct smb2_handle h2 = {};
1213 NTSTATUS status;
1214 const char *fname = "lease_oplock.dat";
1215 bool ret = true;
1216 int i;
1217 uint32_t caps;
1219 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1220 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1222 tree->session->transport->lease.handler = torture_lease_handler;
1223 tree->session->transport->lease.private_data = tree;
1224 tree->session->transport->oplock.handler = torture_oplock_handler;
1225 tree->session->transport->oplock.private_data = tree;
1227 smb2_util_unlink(tree, fname);
1229 for (i = 0; i < NOPLOCK_RESULTS; i++) {
1230 const char *held = oplock_results[i][0];
1231 const char *contend = oplock_results[i][1];
1232 const char *brokento = oplock_results[i][2];
1233 const char *granted = oplock_results[i][3];
1234 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
1235 "expecting break to %s(%x) and grant of %s(%x)\n",
1236 held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
1237 brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
1239 torture_reset_lease_break_info(tctx, &lease_break_info);
1241 /* Grab lease. */
1242 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
1243 status = smb2_create(tree, mem_ctx, &io);
1244 CHECK_STATUS(status, NT_STATUS_OK);
1245 h = io.out.file.handle;
1246 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1247 CHECK_LEASE(&io, held, true, LEASE1, 0);
1249 /* Does an oplock contend the lease? */
1250 smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
1251 status = smb2_create(tree, mem_ctx, &io);
1252 CHECK_STATUS(status, NT_STATUS_OK);
1253 h2 = io.out.file.handle;
1254 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1255 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
1256 lease_break_info.held_oplock_level = io.out.oplock_level;
1258 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
1259 CHECK_BREAK_INFO(held, brokento, LEASE1);
1260 } else {
1261 CHECK_NO_BREAK(tctx);
1264 smb2_util_close(tree, h);
1265 smb2_util_close(tree, h2);
1267 status = smb2_util_unlink(tree, fname);
1268 CHECK_STATUS(status, NT_STATUS_OK);
1271 for (i = 0; i < NOPLOCK_RESULTS; i++) {
1272 const char *held = oplock_results_2[i][0];
1273 const char *contend = oplock_results_2[i][1];
1274 const char *brokento = oplock_results_2[i][2];
1275 const char *granted = oplock_results_2[i][3];
1276 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
1277 "expecting break to %s(%x) and grant of %s(%x)\n",
1278 held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
1279 brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
1281 torture_reset_lease_break_info(tctx, &lease_break_info);
1283 /* Grab an oplock. */
1284 smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
1285 status = smb2_create(tree, mem_ctx, &io);
1286 CHECK_STATUS(status, NT_STATUS_OK);
1287 h = io.out.file.handle;
1288 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1289 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
1290 lease_break_info.held_oplock_level = io.out.oplock_level;
1292 /* Grab lease. */
1293 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
1294 status = smb2_create(tree, mem_ctx, &io);
1295 CHECK_STATUS(status, NT_STATUS_OK);
1296 h2 = io.out.file.handle;
1297 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1298 CHECK_LEASE(&io, granted, true, LEASE1, 0);
1300 if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
1301 CHECK_OPLOCK_BREAK(brokento);
1302 } else {
1303 CHECK_NO_BREAK(tctx);
1306 smb2_util_close(tree, h);
1307 smb2_util_close(tree, h2);
1309 status = smb2_util_unlink(tree, fname);
1310 CHECK_STATUS(status, NT_STATUS_OK);
1313 done:
1314 smb2_util_close(tree, h);
1315 smb2_util_close(tree, h2);
1317 smb2_util_unlink(tree, fname);
1319 talloc_free(mem_ctx);
1321 return ret;
1324 static bool test_lease_multibreak(struct torture_context *tctx,
1325 struct smb2_tree *tree)
1327 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1328 struct smb2_create io;
1329 struct smb2_lease ls;
1330 struct smb2_handle h = {};
1331 struct smb2_handle h2 = {};
1332 struct smb2_handle h3 = {};
1333 struct smb2_write w;
1334 NTSTATUS status;
1335 const char *fname = "lease_multibreak.dat";
1336 bool ret = true;
1337 uint32_t caps;
1339 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1340 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1342 tree->session->transport->lease.handler = torture_lease_handler;
1343 tree->session->transport->lease.private_data = tree;
1344 tree->session->transport->oplock.handler = torture_oplock_handler;
1345 tree->session->transport->oplock.private_data = tree;
1347 smb2_util_unlink(tree, fname);
1349 torture_reset_lease_break_info(tctx, &lease_break_info);
1351 /* Grab lease, upgrade to RHW .. */
1352 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
1353 status = smb2_create(tree, mem_ctx, &io);
1354 CHECK_STATUS(status, NT_STATUS_OK);
1355 h = io.out.file.handle;
1356 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1357 CHECK_LEASE(&io, "RH", true, LEASE1, 0);
1359 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
1360 status = smb2_create(tree, mem_ctx, &io);
1361 CHECK_STATUS(status, NT_STATUS_OK);
1362 h2 = io.out.file.handle;
1363 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1364 CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
1366 /* Contend with LEASE2. */
1367 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
1368 status = smb2_create(tree, mem_ctx, &io);
1369 CHECK_STATUS(status, NT_STATUS_OK);
1370 h3 = io.out.file.handle;
1371 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1372 CHECK_LEASE(&io, "RH", true, LEASE2, 0);
1374 /* Verify that we were only sent one break. */
1375 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
1377 /* Drop LEASE1 / LEASE2 */
1378 status = smb2_util_close(tree, h);
1379 CHECK_STATUS(status, NT_STATUS_OK);
1380 status = smb2_util_close(tree, h2);
1381 CHECK_STATUS(status, NT_STATUS_OK);
1382 status = smb2_util_close(tree, h3);
1383 CHECK_STATUS(status, NT_STATUS_OK);
1385 torture_reset_lease_break_info(tctx, &lease_break_info);
1387 /* Grab an R lease. */
1388 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
1389 status = smb2_create(tree, mem_ctx, &io);
1390 CHECK_STATUS(status, NT_STATUS_OK);
1391 h = io.out.file.handle;
1392 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1393 CHECK_LEASE(&io, "R", true, LEASE1, 0);
1395 /* Grab a level-II oplock. */
1396 smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
1397 status = smb2_create(tree, mem_ctx, &io);
1398 CHECK_STATUS(status, NT_STATUS_OK);
1399 h2 = io.out.file.handle;
1400 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
1401 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
1402 lease_break_info.held_oplock_level = io.out.oplock_level;
1404 /* Verify no breaks. */
1405 CHECK_NO_BREAK(tctx);
1407 /* Open for truncate, force a break. */
1408 smb2_generic_create(&io, NULL, false, fname,
1409 NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
1410 status = smb2_create(tree, mem_ctx, &io);
1411 CHECK_STATUS(status, NT_STATUS_OK);
1412 h3 = io.out.file.handle;
1413 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
1414 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
1415 lease_break_info.held_oplock_level = io.out.oplock_level;
1417 /* Sleep, use a write to clear the recv queue. */
1418 smb_msleep(250);
1419 ZERO_STRUCT(w);
1420 w.in.file.handle = h3;
1421 w.in.offset = 0;
1422 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1423 memset(w.in.data.data, 'o', w.in.data.length);
1424 status = smb2_write(tree, &w);
1425 CHECK_STATUS(status, NT_STATUS_OK);
1427 /* Verify one oplock break, one lease break. */
1428 CHECK_OPLOCK_BREAK("");
1429 CHECK_BREAK_INFO("R", "", LEASE1);
1431 done:
1432 smb2_util_close(tree, h);
1433 smb2_util_close(tree, h2);
1434 smb2_util_close(tree, h3);
1436 smb2_util_unlink(tree, fname);
1438 talloc_free(mem_ctx);
1440 return ret;
1443 static bool test_lease_v2_request_parent(struct torture_context *tctx,
1444 struct smb2_tree *tree)
1446 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1447 struct smb2_create io;
1448 struct smb2_lease ls;
1449 struct smb2_handle h1 = {};
1450 uint64_t parent = LEASE2;
1451 NTSTATUS status;
1452 const char *fname = "lease_v2_request_parent.dat";
1453 bool ret = true;
1454 uint32_t caps;
1455 enum protocol_types protocol;
1457 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1458 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1459 torture_assert_goto(tctx, caps & SMB2_CAP_DIRECTORY_LEASING, ret, done,
1460 "SMB3 Directory Leases are not supported\n");
1462 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1463 if (protocol < PROTOCOL_SMB3_00) {
1464 torture_skip(tctx, "v2 leases are not supported");
1467 smb2_util_unlink(tree, fname);
1469 torture_reset_lease_break_info(tctx, &lease_break_info);
1471 ZERO_STRUCT(io);
1472 smb2_lease_v2_create_share(&io, &ls, false, fname,
1473 smb2_util_share_access("RWD"),
1474 LEASE1, &parent,
1475 smb2_util_lease_state("RHW"),
1476 0x11);
1478 status = smb2_create(tree, mem_ctx, &io);
1479 CHECK_STATUS(status, NT_STATUS_OK);
1480 h1 = io.out.file.handle;
1481 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1482 CHECK_LEASE_V2(&io, "RHW", true, LEASE1,
1483 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
1484 ls.lease_epoch + 1);
1486 done:
1487 smb2_util_close(tree, h1);
1488 smb2_util_unlink(tree, fname);
1490 talloc_free(mem_ctx);
1492 return ret;
1496 * Checks server accepts "RWH", "RH" and "R" lease request and grants at most
1497 * (lease_request & "RH"), so no "W", but "R" without "H" if requested.
1499 static bool test_dirlease_leases(struct torture_context *tctx,
1500 struct smb2_tree *tree)
1502 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1503 struct smb2_create io;
1504 struct smb2_lease ls;
1505 struct smb2_handle h1 = {};
1506 NTSTATUS status;
1507 const char *dname = "test_dirlease_leases_dir";
1508 bool ret = true;
1509 uint32_t caps;
1511 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1512 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1513 torture_assert_goto(tctx, caps & SMB2_CAP_DIRECTORY_LEASING, ret, done,
1514 "SMB3 Directory Leases are not supported\n");
1516 smb2_deltree(tree, dname);
1518 torture_reset_lease_break_info(tctx, &lease_break_info);
1520 /* Request "RWH" -> grant "RH" */
1522 ZERO_STRUCT(io);
1523 smb2_lease_v2_create_share(&io, &ls, true, dname,
1524 smb2_util_share_access("RWD"),
1525 LEASE1, NULL,
1526 smb2_util_lease_state("RWH"),
1527 0x11);
1529 status = smb2_create(tree, mem_ctx, &io);
1530 CHECK_STATUS(status, NT_STATUS_OK);
1531 h1 = io.out.file.handle;
1532 CHECK_LEASE_V2(&io, "RH", true, LEASE1,
1533 0, 0, ++ls.lease_epoch);
1534 smb2_util_close(tree, h1);
1536 /* Request "RW" -> grant "R" */
1538 ZERO_STRUCT(io);
1539 smb2_lease_v2_create_share(&io, &ls, true, dname,
1540 smb2_util_share_access("RWD"),
1541 LEASE1, NULL,
1542 smb2_util_lease_state("RW"),
1543 0x11);
1545 status = smb2_create(tree, mem_ctx, &io);
1546 CHECK_STATUS(status, NT_STATUS_OK);
1547 h1 = io.out.file.handle;
1548 CHECK_LEASE_V2(&io, "R", true, LEASE1,
1549 0, 0, ++ls.lease_epoch);
1550 smb2_util_close(tree, h1);
1552 /* Request "RH" -> grant "RH" */
1554 ZERO_STRUCT(io);
1555 smb2_lease_v2_create_share(&io, &ls, true, dname,
1556 smb2_util_share_access("RWD"),
1557 LEASE1, NULL,
1558 smb2_util_lease_state("RH"),
1559 0x11);
1561 status = smb2_create(tree, mem_ctx, &io);
1562 CHECK_STATUS(status, NT_STATUS_OK);
1563 h1 = io.out.file.handle;
1564 CHECK_LEASE_V2(&io, "RH", true, LEASE1,
1565 0, 0, ++ls.lease_epoch);
1566 smb2_util_close(tree, h1);
1568 /* Request "R" -> grant "R" */
1570 ZERO_STRUCT(io);
1571 smb2_lease_v2_create_share(&io, &ls, true, dname,
1572 smb2_util_share_access("RWD"),
1573 LEASE1, NULL,
1574 smb2_util_lease_state("R"),
1575 0x11);
1577 status = smb2_create(tree, mem_ctx, &io);
1578 CHECK_STATUS(status, NT_STATUS_OK);
1579 h1 = io.out.file.handle;
1580 CHECK_LEASE_V2(&io, "R", true, LEASE1,
1581 0, 0, ++ls.lease_epoch);
1582 smb2_util_close(tree, h1);
1584 done:
1585 smb2_util_close(tree, h1);
1586 smb2_deltree(tree, dname);
1588 talloc_free(mem_ctx);
1590 return ret;
1593 static bool test_lease_break_twice(struct torture_context *tctx,
1594 struct smb2_tree *tree)
1596 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1597 struct smb2_create io;
1598 struct smb2_lease ls1;
1599 struct smb2_lease ls2;
1600 struct smb2_handle h1 = {};
1601 NTSTATUS status;
1602 const char *fname = "lease_break_twice.dat";
1603 bool ret = true;
1604 uint32_t caps;
1605 enum protocol_types protocol;
1607 caps = smb2cli_conn_server_capabilities(
1608 tree->session->transport->conn);
1609 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1611 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1612 if (protocol < PROTOCOL_SMB3_00) {
1613 torture_skip(tctx, "v2 leases are not supported");
1616 smb2_util_unlink(tree, fname);
1618 torture_reset_lease_break_info(tctx, &lease_break_info);
1619 ZERO_STRUCT(io);
1621 smb2_lease_v2_create_share(
1622 &io, &ls1, false, fname, smb2_util_share_access("RWD"),
1623 LEASE1, NULL, smb2_util_lease_state("RWH"), 0x11);
1625 status = smb2_create(tree, mem_ctx, &io);
1626 CHECK_STATUS(status, NT_STATUS_OK);
1627 h1 = io.out.file.handle;
1628 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1629 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
1631 tree->session->transport->lease.handler = torture_lease_handler;
1632 tree->session->transport->lease.private_data = tree;
1634 torture_reset_lease_break_info(tctx, &lease_break_info);
1636 smb2_lease_v2_create_share(
1637 &io, &ls2, false, fname, smb2_util_share_access("R"),
1638 LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
1640 status = smb2_create(tree, mem_ctx, &io);
1641 CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
1642 CHECK_BREAK_INFO_V2(tree->session->transport,
1643 "RWH", "RW", LEASE1, ls1.lease_epoch + 2);
1645 smb2_lease_v2_create_share(
1646 &io, &ls2, false, fname, smb2_util_share_access("RWD"),
1647 LEASE2, NULL, smb2_util_lease_state("RWH"), 0x22);
1649 torture_reset_lease_break_info(tctx, &lease_break_info);
1651 status = smb2_create(tree, mem_ctx, &io);
1652 CHECK_STATUS(status, NT_STATUS_OK);
1653 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch + 1);
1654 CHECK_BREAK_INFO_V2(tree->session->transport,
1655 "RW", "R", LEASE1, ls1.lease_epoch + 3);
1657 done:
1658 smb2_util_close(tree, h1);
1659 smb2_util_unlink(tree, fname);
1660 talloc_free(mem_ctx);
1661 return ret;
1664 static bool test_rearm_dirlease(TALLOC_CTX *mem_ctx,
1665 struct torture_context *tctx,
1666 struct smb2_tree *tree,
1667 const char *dname,
1668 uint64_t lease_key,
1669 uint16_t *lease_epoch)
1671 struct smb2_create io;
1672 struct smb2_lease ls;
1673 NTSTATUS status;
1674 bool ret = true;
1676 smb2_lease_v2_create_share(&io,
1677 &ls,
1678 true,
1679 dname,
1680 smb2_util_share_access("RWD"),
1681 lease_key,
1682 NULL,
1683 smb2_util_lease_state("RH"),
1684 *lease_epoch);
1686 io.in.create_disposition = NTCREATEX_DISP_OPEN;
1688 status = smb2_create(tree, mem_ctx, &io);
1689 torture_assert_ntstatus_ok_goto(tctx, status, ret, done, "rearm failed\n");
1691 smb2_util_close(tree, io.out.file.handle);
1693 (*lease_epoch)++;
1694 CHECK_LEASE_V2(&io, "RH", true, lease_key, 0, 0, *lease_epoch);
1696 done:
1697 return ret;
1700 static bool test_lease_v2_request(struct torture_context *tctx,
1701 struct smb2_tree *tree,
1702 struct smb2_tree *tree2)
1704 TALLOC_CTX *mem_ctx = talloc_new(tctx);
1705 struct smb2_create io;
1706 struct smb2_lease ls1, ls3, ls4, dirlease;
1707 struct smb2_handle h1 = {};
1708 struct smb2_handle h2 = {};
1709 struct smb2_handle h3 = {};
1710 struct smb2_handle h4 = {};
1711 struct smb2_handle h5 = {};
1712 struct smb2_write w;
1713 struct smb2_lease tr2_ls1;
1714 struct smb2_request *req = NULL;
1715 NTSTATUS status;
1716 const char *fname = "lease_v2_request.dat";
1717 const char *dname = "lease_v2_request.dir";
1718 const char *dnamefname = "lease_v2_request.dir\\lease.dat";
1719 const char *dnamefname2 = "lease_v2_request.dir\\lease2.dat";
1720 bool ret = true;
1721 uint32_t caps;
1722 enum protocol_types protocol;
1724 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1725 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1726 torture_assert_goto(tctx, caps & SMB2_CAP_DIRECTORY_LEASING, ret, done,
1727 "SMB3 Directory Leases are not supported\n");
1729 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1730 if (protocol < PROTOCOL_SMB3_00) {
1731 torture_skip(tctx, "v2 leases are not supported");
1734 smb2_util_unlink(tree, fname);
1735 smb2_deltree(tree, dname);
1737 tree->session->transport->lease.handler = torture_lease_handler;
1738 tree->session->transport->lease.private_data = tree;
1739 tree->session->transport->oplock.handler = torture_oplock_handler;
1740 tree->session->transport->oplock.private_data = tree;
1742 torture_reset_lease_break_info(tctx, &lease_break_info);
1744 ZERO_STRUCT(io);
1745 smb2_lease_v2_create_share(&io, &ls1, false, fname,
1746 smb2_util_share_access("RWD"),
1747 LEASE1, NULL,
1748 smb2_util_lease_state("RHW"),
1749 0x11);
1751 status = smb2_create(tree, mem_ctx, &io);
1752 CHECK_STATUS(status, NT_STATUS_OK);
1753 h1 = io.out.file.handle;
1754 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1755 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch + 1);
1757 ZERO_STRUCT(io);
1758 smb2_lease_v2_create_share(&io, &dirlease, true, dname,
1759 smb2_util_share_access("RWD"),
1760 LEASE2, NULL,
1761 smb2_util_lease_state("RHW"),
1762 0x22);
1763 status = smb2_create(tree, mem_ctx, &io);
1764 CHECK_STATUS(status, NT_STATUS_OK);
1765 h2 = io.out.file.handle;
1766 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
1767 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ++dirlease.lease_epoch);
1770 * TEST: second client opens the same directory as first client,
1771 * triggering a sharing violation
1773 ZERO_STRUCT(io);
1774 smb2_lease_v2_create_share(&io, &tr2_ls1, true, dname,
1775 smb2_util_share_access(""),
1776 LEASE3, NULL,
1777 smb2_util_lease_state("RHW"),
1778 0x22);
1779 status = smb2_create(tree2, mem_ctx, &io);
1780 torture_assert_ntstatus_equal_goto(
1781 tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done,
1782 "CREATE didn't fail with NT_STATUS_SHARING_VIOLATION\n");
1784 CHECK_BREAK_INFO_V2(tree->session->transport,
1785 "RH", "R", LEASE2, ++dirlease.lease_epoch);
1787 torture_reset_lease_break_info(tctx, &lease_break_info);
1788 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname, LEASE2, &dirlease.lease_epoch);
1789 torture_assert_goto(tctx, ret == true, ret, done, "Rearm dirlease failed\n");
1792 * TEST: second client opens the same directory as first client,
1793 * triggering a sharing violation, first client closes his handle, open
1794 * should pass.
1796 lease_break_info.lease_skip_ack = true;
1798 ZERO_STRUCT(io);
1799 smb2_lease_v2_create_share(&io, &tr2_ls1, true, dname,
1800 smb2_util_share_access(""),
1801 LEASE3, NULL,
1802 smb2_util_lease_state("RHW"),
1803 0x22);
1804 req = smb2_create_send(tree2, &io);
1805 torture_assert(tctx, req != NULL, "smb2_create_send");
1807 CHECK_BREAK_INFO_V2(tree->session->transport,
1808 "RH", "R", LEASE2, ++dirlease.lease_epoch);
1810 status = smb2_util_close(tree, h2);
1811 CHECK_STATUS(status, NT_STATUS_OK);
1813 status = smb2_create_recv(req, tctx, &io);
1814 CHECK_STATUS(status, NT_STATUS_OK);
1815 h2 = io.out.file.handle;
1817 status = smb2_util_close(tree2, h2);
1818 CHECK_STATUS(status, NT_STATUS_OK);
1820 /* Reopen directory for subsequent tests */
1821 ZERO_STRUCT(io);
1822 smb2_lease_v2_create_share(&io, &dirlease, true, dname,
1823 smb2_util_share_access("RWD"),
1824 LEASE2, NULL,
1825 smb2_util_lease_state("RHW"),
1826 0x22);
1827 status = smb2_create(tree, mem_ctx, &io);
1828 CHECK_STATUS(status, NT_STATUS_OK);
1829 h2 = io.out.file.handle;
1830 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ++dirlease.lease_epoch);
1832 torture_reset_lease_break_info(tctx, &lease_break_info);
1835 * TEST: create file in a directory with dirlease with valid parent key
1836 * -> no break
1839 ZERO_STRUCT(io);
1840 smb2_lease_v2_create_share(&io, &ls3, false, dnamefname,
1841 smb2_util_share_access("RWD"),
1842 LEASE3, &LEASE2,
1843 smb2_util_lease_state("RHW"),
1844 0x33);
1845 status = smb2_create(tree, mem_ctx, &io);
1846 CHECK_STATUS(status, NT_STATUS_OK);
1847 h3 = io.out.file.handle;
1848 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1849 CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
1850 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE2,
1851 ls3.lease_epoch + 1);
1853 CHECK_NO_BREAK(tctx);
1856 * TEST: create file in a directory with dirlease with invalid parent
1857 * key -> break
1860 ZERO_STRUCT(io);
1861 smb2_lease_v2_create_share(&io, &ls4, false, dnamefname2,
1862 smb2_util_share_access("RWD"),
1863 LEASE4, NULL,
1864 smb2_util_lease_state("RHW"),
1865 0x44);
1866 status = smb2_create(tree, mem_ctx, &io);
1867 CHECK_STATUS(status, NT_STATUS_OK);
1868 h4 = io.out.file.handle;
1869 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1870 CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0, 0, ls4.lease_epoch + 1);
1872 CHECK_BREAK_INFO_V2(tree->session->transport,
1873 "RH", "", LEASE2, ++dirlease.lease_epoch);
1876 * TEST: Write on handle without valid parent key -> break
1879 torture_reset_lease_break_info(tctx, &lease_break_info);
1880 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname, LEASE2, &dirlease.lease_epoch);
1881 torture_assert_goto(tctx, ret == true, ret, done, "Rearm dirlease failed\n");
1883 ZERO_STRUCT(w);
1884 w.in.file.handle = h4;
1885 w.in.offset = 0;
1886 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1887 memset(w.in.data.data, 'o', w.in.data.length);
1888 status = smb2_write(tree, &w);
1889 CHECK_STATUS(status, NT_STATUS_OK);
1892 * Wait 4 seconds in order to check if the write time
1893 * was updated (after 2 seconds).
1895 smb_msleep(4000);
1896 CHECK_NO_BREAK(tctx);
1899 * only the close on the modified file break the
1900 * directory lease.
1902 smb2_util_close(tree, h4);
1903 ZERO_STRUCT(h4);
1905 CHECK_BREAK_INFO_V2(tree->session->transport,
1906 "RH", "", LEASE2, ++dirlease.lease_epoch);
1909 * TEST: Write on handle with valid parent key -> no break
1912 torture_reset_lease_break_info(tctx, &lease_break_info);
1913 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname, LEASE2, &dirlease.lease_epoch);
1914 torture_assert_goto(tctx, ret == true, ret, done, "Rearm dirlease failed\n");
1916 ZERO_STRUCT(w);
1917 w.in.file.handle = h3;
1918 w.in.offset = 0;
1919 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1920 memset(w.in.data.data, 'o', w.in.data.length);
1921 status = smb2_write(tree, &w);
1922 CHECK_STATUS(status, NT_STATUS_OK);
1924 smb_msleep(4000);
1925 CHECK_NO_BREAK(tctx);
1926 smb2_util_close(tree, h3);
1927 ZERO_STRUCT(h3);
1928 CHECK_NO_BREAK(tctx);
1930 done:
1931 smb2_util_close(tree, h1);
1932 smb2_util_close(tree, h2);
1933 smb2_util_close(tree, h3);
1934 smb2_util_close(tree, h4);
1935 smb2_util_close(tree, h5);
1937 smb2_util_unlink(tree, fname);
1938 smb2_deltree(tree, dname);
1940 talloc_free(mem_ctx);
1942 return ret;
1945 static bool test_lease_v2_flags_breaking(struct torture_context *tctx,
1946 struct smb2_tree *tree)
1948 struct smb2_create c = {};
1949 struct smb2_lease ls = {};
1950 struct smb2_handle h = {};
1951 const char *fname = "lease_v2_epoch1.dat";
1952 enum protocol_types protocol;
1953 uint32_t caps;
1954 NTSTATUS status;
1955 bool ret = true;
1957 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
1958 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
1960 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
1961 if (protocol < PROTOCOL_SMB3_00) {
1962 torture_skip(tctx, "v2 leases are not supported");
1965 smb2_util_unlink(tree, fname);
1967 smb2_lease_v2_create_share(&c, &ls, false, fname,
1968 smb2_util_share_access("RWD"),
1969 LEASE1, NULL,
1970 smb2_util_lease_state("RHW"),
1971 0x4711);
1972 ls.lease_flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS;
1974 status = smb2_create(tree, tree, &c);
1975 CHECK_STATUS(status, NT_STATUS_OK);
1976 h = c.out.file.handle;
1978 CHECK_CREATED(&c, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1979 CHECK_LEASE_V2(&c, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1);
1981 done:
1982 smb2_util_close(tree, h);
1983 smb2_util_unlink(tree, fname);
1984 return ret;
1988 * Verify server ignores the parent leasekey if
1989 * SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET is not set in the request.
1991 static bool test_lease_v2_flags_parentkey(struct torture_context *tctx,
1992 struct smb2_tree *tree)
1994 struct smb2_create c = {};
1995 struct smb2_lease ls = {};
1996 struct smb2_handle h = {};
1997 const char *fname = "lease_v2_epoch1.dat";
1998 enum protocol_types protocol;
1999 uint32_t caps;
2000 NTSTATUS status;
2001 bool ret = true;
2003 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2004 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2006 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2007 if (protocol < PROTOCOL_SMB3_00) {
2008 torture_skip(tctx, "v2 leases are not supported");
2011 smb2_util_unlink(tree, fname);
2013 smb2_lease_v2_create_share(&c, &ls, false, fname,
2014 smb2_util_share_access("RWD"),
2015 LEASE1, &LEASE1,
2016 smb2_util_lease_state("RHW"),
2017 0x4711);
2018 ls.lease_flags = 0;
2020 status = smb2_create(tree, tree, &c);
2021 CHECK_STATUS(status, NT_STATUS_OK);
2022 h = c.out.file.handle;
2024 CHECK_CREATED(&c, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2025 CHECK_LEASE_V2(&c, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1);
2027 done:
2028 smb2_util_close(tree, h);
2029 smb2_util_unlink(tree, fname);
2030 return ret;
2033 static bool test_lease_v2_epoch1(struct torture_context *tctx,
2034 struct smb2_tree *tree)
2036 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2037 struct smb2_create io;
2038 struct smb2_lease ls;
2039 struct smb2_handle h = {};
2040 const char *fname = "lease_v2_epoch1.dat";
2041 bool ret = true;
2042 NTSTATUS status;
2043 uint32_t caps;
2044 enum protocol_types protocol;
2046 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2047 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2049 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2050 if (protocol < PROTOCOL_SMB3_00) {
2051 torture_skip(tctx, "v2 leases are not supported");
2054 smb2_util_unlink(tree, fname);
2056 tree->session->transport->lease.handler = torture_lease_handler;
2057 tree->session->transport->lease.private_data = tree;
2058 tree->session->transport->oplock.handler = torture_oplock_handler;
2059 tree->session->transport->oplock.private_data = tree;
2061 torture_reset_lease_break_info(tctx, &lease_break_info);
2063 ZERO_STRUCT(io);
2064 smb2_lease_v2_create_share(&io, &ls, false, fname,
2065 smb2_util_share_access("RWD"),
2066 LEASE1, NULL,
2067 smb2_util_lease_state("RHW"),
2068 0x4711);
2069 status = smb2_create(tree, mem_ctx, &io);
2070 CHECK_STATUS(status, NT_STATUS_OK);
2071 h = io.out.file.handle;
2072 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2073 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls.lease_epoch + 1);
2074 smb2_util_close(tree, h);
2075 smb2_util_unlink(tree, fname);
2077 smb2_lease_v2_create_share(&io, &ls, false, fname,
2078 smb2_util_share_access("RWD"),
2079 LEASE1, NULL,
2080 smb2_util_lease_state("RHW"),
2081 0x11);
2083 status = smb2_create(tree, mem_ctx, &io);
2084 CHECK_STATUS(status, NT_STATUS_OK);
2085 h = io.out.file.handle;
2086 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2087 CHECK_LEASE_V2(&io, "RWH", true, LEASE1, 0, 0, ls.lease_epoch + 1);
2088 smb2_util_close(tree, h);
2090 done:
2091 smb2_util_unlink(tree, fname);
2092 talloc_free(mem_ctx);
2093 return ret;
2096 static bool test_lease_v2_epoch2(struct torture_context *tctx,
2097 struct smb2_tree *tree)
2099 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2100 struct smb2_create io;
2101 struct smb2_lease ls1v2, ls1v2t, ls1v1;
2102 struct smb2_handle hv2 = {}, hv1 = {};
2103 const char *fname = "lease_v2_epoch2.dat";
2104 bool ret = true;
2105 NTSTATUS status;
2106 uint32_t caps;
2107 enum protocol_types protocol;
2109 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2110 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2112 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2113 if (protocol < PROTOCOL_SMB3_00) {
2114 torture_skip(tctx, "v2 leases are not supported");
2117 smb2_util_unlink(tree, fname);
2119 tree->session->transport->lease.handler = torture_lease_handler;
2120 tree->session->transport->lease.private_data = tree;
2121 tree->session->transport->oplock.handler = torture_oplock_handler;
2122 tree->session->transport->oplock.private_data = tree;
2124 torture_reset_lease_break_info(tctx, &lease_break_info);
2126 ZERO_STRUCT(io);
2127 smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
2128 smb2_util_share_access("RWD"),
2129 LEASE1, NULL,
2130 smb2_util_lease_state("R"),
2131 0x4711);
2132 status = smb2_create(tree, mem_ctx, &io);
2133 CHECK_STATUS(status, NT_STATUS_OK);
2134 hv2 = io.out.file.handle;
2135 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2136 CHECK_LEASE_V2(&io, "R", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
2138 ZERO_STRUCT(io);
2139 smb2_lease_create_share(&io, &ls1v1, false, fname,
2140 smb2_util_share_access("RWD"),
2141 LEASE1,
2142 smb2_util_lease_state("RH"));
2143 status = smb2_create(tree, mem_ctx, &io);
2144 CHECK_STATUS(status, NT_STATUS_OK);
2145 hv1 = io.out.file.handle;
2146 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2147 CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1v2.lease_epoch + 2);
2149 smb2_util_close(tree, hv2);
2151 ZERO_STRUCT(io);
2152 smb2_lease_v2_create_share(&io, &ls1v2t, false, fname,
2153 smb2_util_share_access("RWD"),
2154 LEASE1, NULL,
2155 smb2_util_lease_state("RHW"),
2156 0x11);
2157 status = smb2_create(tree, mem_ctx, &io);
2158 CHECK_STATUS(status, NT_STATUS_OK);
2159 hv2 = io.out.file.handle;
2160 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2161 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 3);
2163 smb2_util_close(tree, hv2);
2165 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
2166 status = smb2_create(tree, mem_ctx, &io);
2167 CHECK_STATUS(status, NT_STATUS_OK);
2168 hv2 = io.out.file.handle;
2169 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2170 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2172 CHECK_BREAK_INFO_V2(tree->session->transport,
2173 "RWH", "RH", LEASE1, ls1v2.lease_epoch + 4);
2175 smb2_util_close(tree, hv2);
2176 smb2_util_close(tree, hv1);
2178 ZERO_STRUCT(io);
2179 smb2_lease_create_share(&io, &ls1v1, false, fname,
2180 smb2_util_share_access("RWD"),
2181 LEASE1,
2182 smb2_util_lease_state("RHW"));
2183 status = smb2_create(tree, mem_ctx, &io);
2184 CHECK_STATUS(status, NT_STATUS_OK);
2185 hv1 = io.out.file.handle;
2186 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2187 CHECK_LEASE(&io, "RHW", true, LEASE1, 0);
2189 smb2_util_close(tree, hv1);
2191 done:
2192 smb2_util_close(tree, hv2);
2193 smb2_util_close(tree, hv1);
2194 smb2_util_unlink(tree, fname);
2195 talloc_free(mem_ctx);
2196 return ret;
2199 static bool test_lease_v2_epoch3(struct torture_context *tctx,
2200 struct smb2_tree *tree)
2202 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2203 struct smb2_create io;
2204 struct smb2_lease ls1v1 = {}, ls1v1t = {},ls1v2 = {};
2205 struct smb2_handle hv1 = {}, hv2 = {};
2206 const char *fname = "lease_v2_epoch3.dat";
2207 bool ret = true;
2208 NTSTATUS status;
2209 uint32_t caps;
2210 enum protocol_types protocol;
2212 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2213 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2215 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2216 if (protocol < PROTOCOL_SMB3_00) {
2217 torture_skip(tctx, "v2 leases are not supported");
2220 smb2_util_unlink(tree, fname);
2222 tree->session->transport->lease.handler = torture_lease_handler;
2223 tree->session->transport->lease.private_data = tree;
2224 tree->session->transport->oplock.handler = torture_oplock_handler;
2225 tree->session->transport->oplock.private_data = tree;
2227 torture_reset_lease_break_info(tctx, &lease_break_info);
2229 ZERO_STRUCT(io);
2230 smb2_lease_create_share(&io, &ls1v1, false, fname,
2231 smb2_util_share_access("RWD"),
2232 LEASE1,
2233 smb2_util_lease_state("R"));
2234 status = smb2_create(tree, mem_ctx, &io);
2235 CHECK_STATUS(status, NT_STATUS_OK);
2236 hv1 = io.out.file.handle;
2237 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2238 CHECK_LEASE(&io, "R", true, LEASE1, 0);
2240 ZERO_STRUCT(io);
2241 smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
2242 smb2_util_share_access("RWD"),
2243 LEASE1, NULL,
2244 smb2_util_lease_state("RW"),
2245 0x4711);
2246 status = smb2_create(tree, mem_ctx, &io);
2247 CHECK_STATUS(status, NT_STATUS_OK);
2248 hv2 = io.out.file.handle;
2249 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2250 CHECK_LEASE(&io, "RW", true, LEASE1, 0);
2252 smb2_util_close(tree, hv1);
2254 ZERO_STRUCT(io);
2255 smb2_lease_create_share(&io, &ls1v1t, false, fname,
2256 smb2_util_share_access("RWD"),
2257 LEASE1,
2258 smb2_util_lease_state("RWH"));
2259 status = smb2_create(tree, mem_ctx, &io);
2260 CHECK_STATUS(status, NT_STATUS_OK);
2261 hv1 = io.out.file.handle;
2262 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2263 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
2265 smb2_util_close(tree, hv1);
2267 smb2_oplock_create(&io, fname, SMB2_OPLOCK_LEVEL_NONE);
2268 status = smb2_create(tree, mem_ctx, &io);
2269 CHECK_STATUS(status, NT_STATUS_OK);
2270 hv1 = io.out.file.handle;
2271 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2272 CHECK_VAL(io.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2274 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2276 smb2_util_close(tree, hv1);
2277 smb2_util_close(tree, hv2);
2279 ZERO_STRUCT(io);
2280 smb2_lease_v2_create_share(&io, &ls1v2, false, fname,
2281 smb2_util_share_access("RWD"),
2282 LEASE1, NULL,
2283 smb2_util_lease_state("RWH"),
2284 0x4711);
2285 status = smb2_create(tree, mem_ctx, &io);
2286 CHECK_STATUS(status, NT_STATUS_OK);
2287 hv2 = io.out.file.handle;
2288 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2289 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1v2.lease_epoch + 1);
2290 smb2_util_close(tree, hv2);
2292 done:
2293 smb2_util_close(tree, hv2);
2294 smb2_util_close(tree, hv1);
2295 smb2_util_unlink(tree, fname);
2296 talloc_free(mem_ctx);
2297 return ret;
2300 static bool test_lease_breaking1(struct torture_context *tctx,
2301 struct smb2_tree *tree)
2303 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2304 struct smb2_create io1 = {};
2305 struct smb2_create io2 = {};
2306 struct smb2_lease ls1 = {};
2307 struct smb2_handle h1a = {};
2308 struct smb2_handle h1b = {};
2309 struct smb2_handle h2 = {};
2310 struct smb2_request *req2 = NULL;
2311 struct smb2_lease_break_ack ack = {};
2312 const char *fname = "lease_breaking1.dat";
2313 bool ret = true;
2314 NTSTATUS status;
2315 uint32_t caps;
2317 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2318 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2320 smb2_util_unlink(tree, fname);
2322 tree->session->transport->lease.handler = torture_lease_handler;
2323 tree->session->transport->lease.private_data = tree;
2324 tree->session->transport->oplock.handler = torture_oplock_handler;
2325 tree->session->transport->oplock.private_data = tree;
2328 * we defer acking the lease break.
2330 torture_reset_lease_break_info(tctx, &lease_break_info);
2331 lease_break_info.lease_skip_ack = true;
2333 smb2_lease_create_share(&io1, &ls1, false, fname,
2334 smb2_util_share_access("RWD"),
2335 LEASE1,
2336 smb2_util_lease_state("RWH"));
2337 status = smb2_create(tree, mem_ctx, &io1);
2338 CHECK_STATUS(status, NT_STATUS_OK);
2339 h1a = io1.out.file.handle;
2340 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2341 CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2344 * a conflicting open is blocked until we ack the
2345 * lease break
2347 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2348 req2 = smb2_create_send(tree, &io2);
2349 torture_assert(tctx, req2 != NULL, "smb2_create_send");
2352 * we got the lease break, but defer the ack.
2354 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2356 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2358 ack.in.lease.lease_key =
2359 lease_break_info.lease_break.current_lease.lease_key;
2360 ack.in.lease.lease_state =
2361 lease_break_info.lease_break.new_lease_state;
2362 torture_reset_lease_break_info(tctx, &lease_break_info);
2365 * a open using the same lease key is still works,
2366 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2368 status = smb2_create(tree, mem_ctx, &io1);
2369 CHECK_STATUS(status, NT_STATUS_OK);
2370 h1b = io1.out.file.handle;
2371 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2372 CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2373 smb2_util_close(tree, h1b);
2375 CHECK_NO_BREAK(tctx);
2377 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2380 * We ack the lease break.
2382 status = smb2_lease_break_ack(tree, &ack);
2383 CHECK_STATUS(status, NT_STATUS_OK);
2384 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2386 torture_assert(tctx, req2->cancel.can_cancel,
2387 "req2 can_cancel");
2389 status = smb2_create_recv(req2, tctx, &io2);
2390 CHECK_STATUS(status, NT_STATUS_OK);
2391 h2 = io2.out.file.handle;
2392 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2393 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2395 CHECK_NO_BREAK(tctx);
2396 done:
2397 smb2_util_close(tree, h1a);
2398 smb2_util_close(tree, h1b);
2399 smb2_util_close(tree, h2);
2400 smb2_util_unlink(tree, fname);
2401 talloc_free(mem_ctx);
2402 return ret;
2405 static bool test_lease_breaking2(struct torture_context *tctx,
2406 struct smb2_tree *tree)
2408 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2409 struct smb2_create io1 = {};
2410 struct smb2_create io2 = {};
2411 struct smb2_lease ls1 = {};
2412 struct smb2_handle h1a = {};
2413 struct smb2_handle h1b = {};
2414 struct smb2_handle h2 = {};
2415 struct smb2_request *req2 = NULL;
2416 struct smb2_lease_break_ack ack = {};
2417 const char *fname = "lease_breaking2.dat";
2418 bool ret = true;
2419 NTSTATUS status;
2420 uint32_t caps;
2422 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2423 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2425 smb2_util_unlink(tree, fname);
2427 tree->session->transport->lease.handler = torture_lease_handler;
2428 tree->session->transport->lease.private_data = tree;
2429 tree->session->transport->oplock.handler = torture_oplock_handler;
2430 tree->session->transport->oplock.private_data = tree;
2433 * we defer acking the lease break.
2435 torture_reset_lease_break_info(tctx, &lease_break_info);
2436 lease_break_info.lease_skip_ack = true;
2438 smb2_lease_create_share(&io1, &ls1, false, fname,
2439 smb2_util_share_access("RWD"),
2440 LEASE1,
2441 smb2_util_lease_state("RWH"));
2442 status = smb2_create(tree, mem_ctx, &io1);
2443 CHECK_STATUS(status, NT_STATUS_OK);
2444 h1a = io1.out.file.handle;
2445 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2446 CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2449 * a conflicting open is blocked until we ack the
2450 * lease break
2452 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2453 io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2454 req2 = smb2_create_send(tree, &io2);
2455 torture_assert(tctx, req2 != NULL, "smb2_create_send");
2458 * we got the lease break, but defer the ack.
2460 CHECK_BREAK_INFO("RWH", "", LEASE1);
2462 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2464 ack.in.lease.lease_key =
2465 lease_break_info.lease_break.current_lease.lease_key;
2466 torture_reset_lease_break_info(tctx, &lease_break_info);
2469 * a open using the same lease key is still works,
2470 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2472 status = smb2_create(tree, mem_ctx, &io1);
2473 CHECK_STATUS(status, NT_STATUS_OK);
2474 h1b = io1.out.file.handle;
2475 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2476 CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2477 smb2_util_close(tree, h1b);
2479 CHECK_NO_BREAK(tctx);
2481 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2484 * We ack the lease break.
2486 ack.in.lease.lease_state =
2487 SMB2_LEASE_READ | SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE;
2488 status = smb2_lease_break_ack(tree, &ack);
2489 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2491 ack.in.lease.lease_state =
2492 SMB2_LEASE_READ | SMB2_LEASE_WRITE;
2493 status = smb2_lease_break_ack(tree, &ack);
2494 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2496 ack.in.lease.lease_state =
2497 SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE;
2498 status = smb2_lease_break_ack(tree, &ack);
2499 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2501 ack.in.lease.lease_state =
2502 SMB2_LEASE_READ | SMB2_LEASE_HANDLE;
2503 status = smb2_lease_break_ack(tree, &ack);
2504 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2506 ack.in.lease.lease_state = SMB2_LEASE_WRITE;
2507 status = smb2_lease_break_ack(tree, &ack);
2508 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2510 ack.in.lease.lease_state = SMB2_LEASE_HANDLE;
2511 status = smb2_lease_break_ack(tree, &ack);
2512 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2514 ack.in.lease.lease_state = SMB2_LEASE_READ;
2515 status = smb2_lease_break_ack(tree, &ack);
2516 CHECK_STATUS(status, NT_STATUS_REQUEST_NOT_ACCEPTED);
2518 /* Try again with the correct state this time. */
2519 ack.in.lease.lease_state = SMB2_LEASE_NONE;;
2520 status = smb2_lease_break_ack(tree, &ack);
2521 CHECK_STATUS(status, NT_STATUS_OK);
2522 CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
2524 status = smb2_lease_break_ack(tree, &ack);
2525 CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
2527 torture_assert(tctx, req2->cancel.can_cancel,
2528 "req2 can_cancel");
2530 status = smb2_create_recv(req2, tctx, &io2);
2531 CHECK_STATUS(status, NT_STATUS_OK);
2532 h2 = io2.out.file.handle;
2533 CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2534 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2536 CHECK_NO_BREAK(tctx);
2538 /* Get state of the original handle. */
2539 smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
2540 status = smb2_create(tree, mem_ctx, &io1);
2541 CHECK_STATUS(status, NT_STATUS_OK);
2542 CHECK_LEASE(&io1, "", true, LEASE1, 0);
2543 smb2_util_close(tree, io1.out.file.handle);
2545 done:
2546 smb2_util_close(tree, h1a);
2547 smb2_util_close(tree, h1b);
2548 smb2_util_close(tree, h2);
2549 smb2_util_unlink(tree, fname);
2550 talloc_free(mem_ctx);
2551 return ret;
2554 static bool test_lease_breaking3(struct torture_context *tctx,
2555 struct smb2_tree *tree)
2557 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2558 struct smb2_create io1 = {};
2559 struct smb2_create io2 = {};
2560 struct smb2_create io3 = {};
2561 struct smb2_lease ls1 = {};
2562 struct smb2_handle h1a = {};
2563 struct smb2_handle h1b = {};
2564 struct smb2_handle h2 = {};
2565 struct smb2_handle h3 = {};
2566 struct smb2_request *req2 = NULL;
2567 struct smb2_request *req3 = NULL;
2568 struct lease_break_info lease_break_info_tmp = {};
2569 struct smb2_lease_break_ack ack = {};
2570 const char *fname = "lease_breaking3.dat";
2571 bool ret = true;
2572 NTSTATUS status;
2573 uint32_t caps;
2575 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2576 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2578 smb2_util_unlink(tree, fname);
2580 tree->session->transport->lease.handler = torture_lease_handler;
2581 tree->session->transport->lease.private_data = tree;
2582 tree->session->transport->oplock.handler = torture_oplock_handler;
2583 tree->session->transport->oplock.private_data = tree;
2586 * we defer acking the lease break.
2588 torture_reset_lease_break_info(tctx, &lease_break_info);
2589 lease_break_info.lease_skip_ack = true;
2591 smb2_lease_create_share(&io1, &ls1, false, fname,
2592 smb2_util_share_access("RWD"),
2593 LEASE1,
2594 smb2_util_lease_state("RWH"));
2595 status = smb2_create(tree, mem_ctx, &io1);
2596 CHECK_STATUS(status, NT_STATUS_OK);
2597 h1a = io1.out.file.handle;
2598 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2599 CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
2602 * a conflicting open is blocked until we ack the
2603 * lease break
2605 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2606 req2 = smb2_create_send(tree, &io2);
2607 torture_assert(tctx, req2 != NULL, "smb2_create_send");
2610 * we got the lease break, but defer the ack.
2612 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
2614 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2617 * a open using the same lease key is still works,
2618 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2620 status = smb2_create(tree, mem_ctx, &io1);
2621 CHECK_STATUS(status, NT_STATUS_OK);
2622 h1b = io1.out.file.handle;
2623 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2624 CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2625 smb2_util_close(tree, h1b);
2628 * a conflicting open with NTCREATEX_DISP_OVERWRITE
2629 * doesn't trigger an immediate lease break to none.
2631 lease_break_info_tmp = lease_break_info;
2632 torture_reset_lease_break_info(tctx, &lease_break_info);
2633 smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
2634 io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2635 req3 = smb2_create_send(tree, &io3);
2636 torture_assert(tctx, req3 != NULL, "smb2_create_send");
2637 CHECK_NO_BREAK(tctx);
2638 lease_break_info = lease_break_info_tmp;
2640 torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2642 ack.in.lease.lease_key =
2643 lease_break_info.lease_break.current_lease.lease_key;
2644 ack.in.lease.lease_state =
2645 lease_break_info.lease_break.new_lease_state;
2646 torture_reset_lease_break_info(tctx, &lease_break_info);
2649 * a open using the same lease key is still works,
2650 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2652 status = smb2_create(tree, mem_ctx, &io1);
2653 CHECK_STATUS(status, NT_STATUS_OK);
2654 h1b = io1.out.file.handle;
2655 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2656 CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2657 smb2_util_close(tree, h1b);
2659 CHECK_NO_BREAK(tctx);
2662 * We ack the lease break, but defer acking the next break (to "R")
2664 lease_break_info.lease_skip_ack = true;
2665 status = smb2_lease_break_ack(tree, &ack);
2666 CHECK_STATUS(status, NT_STATUS_OK);
2667 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2670 * We got an additional break downgrading to just "R"
2671 * while we defer the ack.
2673 CHECK_BREAK_INFO("RH", "R", LEASE1);
2675 ack.in.lease.lease_key =
2676 lease_break_info.lease_break.current_lease.lease_key;
2677 ack.in.lease.lease_state =
2678 lease_break_info.lease_break.new_lease_state;
2679 torture_reset_lease_break_info(tctx, &lease_break_info);
2682 * a open using the same lease key is still works,
2683 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2685 status = smb2_create(tree, mem_ctx, &io1);
2686 CHECK_STATUS(status, NT_STATUS_OK);
2687 h1b = io1.out.file.handle;
2688 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2689 CHECK_LEASE(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
2690 smb2_util_close(tree, h1b);
2692 CHECK_NO_BREAK(tctx);
2694 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2695 torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2698 * We ack the downgrade to "R" and get an immediate break to none
2700 status = smb2_lease_break_ack(tree, &ack);
2701 CHECK_STATUS(status, NT_STATUS_OK);
2702 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
2705 * We get the downgrade to none.
2707 CHECK_BREAK_INFO("R", "", LEASE1);
2709 torture_assert(tctx, req2->cancel.can_cancel,
2710 "req2 can_cancel");
2711 torture_assert(tctx, req3->cancel.can_cancel,
2712 "req3 can_cancel");
2714 torture_reset_lease_break_info(tctx, &lease_break_info);
2716 status = smb2_create_recv(req2, tctx, &io2);
2717 CHECK_STATUS(status, NT_STATUS_OK);
2718 h2 = io2.out.file.handle;
2719 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2720 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2722 status = smb2_create_recv(req3, tctx, &io3);
2723 CHECK_STATUS(status, NT_STATUS_OK);
2724 h3 = io3.out.file.handle;
2725 CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2726 CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2728 CHECK_NO_BREAK(tctx);
2729 done:
2730 smb2_util_close(tree, h1a);
2731 smb2_util_close(tree, h1b);
2732 smb2_util_close(tree, h2);
2733 smb2_util_close(tree, h3);
2735 smb2_util_unlink(tree, fname);
2736 talloc_free(mem_ctx);
2737 return ret;
2740 static bool test_lease_v2_breaking3(struct torture_context *tctx,
2741 struct smb2_tree *tree)
2743 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2744 struct smb2_create io1 = {};
2745 struct smb2_create io2 = {};
2746 struct smb2_create io3 = {};
2747 struct smb2_lease ls1 = {};
2748 struct smb2_handle h1a = {};
2749 struct smb2_handle h1b = {};
2750 struct smb2_handle h2 = {};
2751 struct smb2_handle h3 = {};
2752 struct smb2_request *req2 = NULL;
2753 struct smb2_request *req3 = NULL;
2754 struct lease_break_info lease_break_info_tmp = {};
2755 struct smb2_lease_break_ack ack = {};
2756 const char *fname = "v2_lease_breaking3.dat";
2757 bool ret = true;
2758 NTSTATUS status;
2759 uint32_t caps;
2760 enum protocol_types protocol;
2762 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2763 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2765 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
2766 if (protocol < PROTOCOL_SMB3_00) {
2767 torture_skip(tctx, "v2 leases are not supported");
2770 smb2_util_unlink(tree, fname);
2772 tree->session->transport->lease.handler = torture_lease_handler;
2773 tree->session->transport->lease.private_data = tree;
2774 tree->session->transport->oplock.handler = torture_oplock_handler;
2775 tree->session->transport->oplock.private_data = tree;
2778 * we defer acking the lease break.
2780 torture_reset_lease_break_info(tctx, &lease_break_info);
2781 lease_break_info.lease_skip_ack = true;
2783 smb2_lease_v2_create_share(&io1, &ls1, false, fname,
2784 smb2_util_share_access("RWD"),
2785 LEASE1, NULL,
2786 smb2_util_lease_state("RHW"),
2787 0x11);
2788 status = smb2_create(tree, mem_ctx, &io1);
2789 CHECK_STATUS(status, NT_STATUS_OK);
2790 h1a = io1.out.file.handle;
2791 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2792 /* Epoch increases on open. */
2793 ls1.lease_epoch += 1;
2794 CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
2797 * a conflicting open is blocked until we ack the
2798 * lease break
2800 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2801 req2 = smb2_create_send(tree, &io2);
2802 torture_assert(tctx, req2 != NULL, "smb2_create_send");
2805 * we got the lease break, but defer the ack.
2807 CHECK_BREAK_INFO_V2(tree->session->transport,
2808 "RWH", "RH", LEASE1, ls1.lease_epoch + 1);
2810 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2812 /* On receiving a lease break, we must sync the new epoch. */
2813 ls1.lease_epoch = lease_break_info.lease_break.new_epoch;
2816 * a open using the same lease key is still works,
2817 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2819 status = smb2_create(tree, mem_ctx, &io1);
2820 CHECK_STATUS(status, NT_STATUS_OK);
2821 h1b = io1.out.file.handle;
2822 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2823 CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2824 smb2_util_close(tree, h1b);
2827 * a conflicting open with NTCREATEX_DISP_OVERWRITE
2828 * doesn't trigger an immediate lease break to none.
2830 lease_break_info_tmp = lease_break_info;
2831 torture_reset_lease_break_info(tctx, &lease_break_info);
2832 smb2_oplock_create(&io3, fname, SMB2_OPLOCK_LEVEL_NONE);
2833 io3.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2834 req3 = smb2_create_send(tree, &io3);
2835 torture_assert(tctx, req3 != NULL, "smb2_create_send");
2836 CHECK_NO_BREAK(tctx);
2837 lease_break_info = lease_break_info_tmp;
2839 torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2841 ack.in.lease.lease_key =
2842 lease_break_info.lease_break.current_lease.lease_key;
2843 ack.in.lease.lease_state =
2844 lease_break_info.lease_break.new_lease_state;
2845 torture_reset_lease_break_info(tctx, &lease_break_info);
2848 * a open using the same lease key is still works,
2849 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2851 status = smb2_create(tree, mem_ctx, &io1);
2852 CHECK_STATUS(status, NT_STATUS_OK);
2853 h1b = io1.out.file.handle;
2854 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2855 CHECK_LEASE_V2(&io1, "RHW", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2856 smb2_util_close(tree, h1b);
2858 CHECK_NO_BREAK(tctx);
2861 * We ack the lease break, but defer acking the next break (to "R")
2863 lease_break_info.lease_skip_ack = true;
2864 status = smb2_lease_break_ack(tree, &ack);
2865 CHECK_STATUS(status, NT_STATUS_OK);
2866 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
2869 * We got an additional break downgrading to just "R"
2870 * while we defer the ack.
2872 CHECK_BREAK_INFO_V2(tree->session->transport,
2873 "RH", "R", LEASE1, ls1.lease_epoch);
2874 /* On receiving a lease break, we must sync the new epoch. */
2875 ls1.lease_epoch = lease_break_info.lease_break.new_epoch;
2877 ack.in.lease.lease_key =
2878 lease_break_info.lease_break.current_lease.lease_key;
2879 ack.in.lease.lease_state =
2880 lease_break_info.lease_break.new_lease_state;
2881 torture_reset_lease_break_info(tctx, &lease_break_info);
2884 * a open using the same lease key is still works,
2885 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
2887 status = smb2_create(tree, mem_ctx, &io1);
2888 CHECK_STATUS(status, NT_STATUS_OK);
2889 h1b = io1.out.file.handle;
2890 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2891 CHECK_LEASE_V2(&io1, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS, 0, ls1.lease_epoch);
2892 smb2_util_close(tree, h1b);
2894 CHECK_NO_BREAK(tctx);
2896 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
2897 torture_assert(tctx, req3->state == SMB2_REQUEST_RECV, "req3 pending");
2900 * We ack the downgrade to "R" and get an immediate break to none
2902 status = smb2_lease_break_ack(tree, &ack);
2903 CHECK_STATUS(status, NT_STATUS_OK);
2904 CHECK_LEASE_BREAK_ACK(&ack, "R", LEASE1);
2907 * We get the downgrade to none.
2909 CHECK_BREAK_INFO_V2(tree->session->transport,
2910 "R", "", LEASE1, ls1.lease_epoch);
2912 torture_assert(tctx, req2->cancel.can_cancel,
2913 "req2 can_cancel");
2914 torture_assert(tctx, req3->cancel.can_cancel,
2915 "req3 can_cancel");
2917 torture_reset_lease_break_info(tctx, &lease_break_info);
2919 status = smb2_create_recv(req2, tctx, &io2);
2920 CHECK_STATUS(status, NT_STATUS_OK);
2921 h2 = io2.out.file.handle;
2922 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
2923 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2925 status = smb2_create_recv(req3, tctx, &io3);
2926 CHECK_STATUS(status, NT_STATUS_OK);
2927 h3 = io3.out.file.handle;
2928 CHECK_CREATED(&io3, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
2929 CHECK_VAL(io3.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
2931 CHECK_NO_BREAK(tctx);
2932 done:
2933 smb2_util_close(tree, h1a);
2934 smb2_util_close(tree, h1b);
2935 smb2_util_close(tree, h2);
2936 smb2_util_close(tree, h3);
2938 smb2_util_unlink(tree, fname);
2939 talloc_free(mem_ctx);
2940 return ret;
2944 static bool test_lease_breaking4(struct torture_context *tctx,
2945 struct smb2_tree *tree)
2947 TALLOC_CTX *mem_ctx = talloc_new(tctx);
2948 struct smb2_create io1 = {};
2949 struct smb2_create io2 = {};
2950 struct smb2_create io3 = {};
2951 struct smb2_lease ls1 = {};
2952 struct smb2_lease ls1t = {};
2953 struct smb2_handle h1 = {};
2954 struct smb2_handle h2 = {};
2955 struct smb2_handle h3 = {};
2956 struct smb2_request *req2 = NULL;
2957 struct lease_break_info lease_break_info_tmp = {};
2958 struct smb2_lease_break_ack ack = {};
2959 const char *fname = "lease_breaking4.dat";
2960 bool ret = true;
2961 NTSTATUS status;
2962 uint32_t caps;
2964 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
2965 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
2967 smb2_util_unlink(tree, fname);
2969 tree->session->transport->lease.handler = torture_lease_handler;
2970 tree->session->transport->lease.private_data = tree;
2971 tree->session->transport->oplock.handler = torture_oplock_handler;
2972 tree->session->transport->oplock.private_data = tree;
2975 * we defer acking the lease break.
2977 torture_reset_lease_break_info(tctx, &lease_break_info);
2978 lease_break_info.lease_skip_ack = true;
2980 smb2_lease_create_share(&io1, &ls1, false, fname,
2981 smb2_util_share_access("RWD"),
2982 LEASE1,
2983 smb2_util_lease_state("RH"));
2984 status = smb2_create(tree, mem_ctx, &io1);
2985 CHECK_STATUS(status, NT_STATUS_OK);
2986 h1 = io1.out.file.handle;
2987 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
2988 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
2990 CHECK_NO_BREAK(tctx);
2993 * a conflicting open is *not* blocked until we ack the
2994 * lease break
2996 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
2997 io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
2998 req2 = smb2_create_send(tree, &io2);
2999 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3002 * We got a break from RH to NONE, we're supported to ack
3003 * this downgrade
3005 CHECK_BREAK_INFO("RH", "", LEASE1);
3007 lease_break_info_tmp = lease_break_info;
3008 torture_reset_lease_break_info(tctx, &lease_break_info);
3009 CHECK_NO_BREAK(tctx);
3011 torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
3013 status = smb2_create_recv(req2, tctx, &io2);
3014 CHECK_STATUS(status, NT_STATUS_OK);
3015 h2 = io2.out.file.handle;
3016 CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
3017 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
3018 smb2_util_close(tree, h2);
3020 CHECK_NO_BREAK(tctx);
3023 * a conflicting open is *not* blocked until we ack the
3024 * lease break, even if the lease is in breaking state.
3026 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
3027 io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
3028 req2 = smb2_create_send(tree, &io2);
3029 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3031 CHECK_NO_BREAK(tctx);
3033 torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
3035 status = smb2_create_recv(req2, tctx, &io2);
3036 CHECK_STATUS(status, NT_STATUS_OK);
3037 h2 = io2.out.file.handle;
3038 CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
3039 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
3040 smb2_util_close(tree, h2);
3042 CHECK_NO_BREAK(tctx);
3045 * We now ask the server about the current lease state
3046 * which should still be "RH", but with
3047 * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS.
3049 smb2_lease_create_share(&io3, &ls1t, false, fname,
3050 smb2_util_share_access("RWD"),
3051 LEASE1,
3052 smb2_util_lease_state(""));
3053 status = smb2_create(tree, mem_ctx, &io3);
3054 CHECK_STATUS(status, NT_STATUS_OK);
3055 h3 = io3.out.file.handle;
3056 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3057 CHECK_LEASE(&io3, "RH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
3060 * We finally ack the lease break...
3062 CHECK_NO_BREAK(tctx);
3063 lease_break_info = lease_break_info_tmp;
3064 ack.in.lease.lease_key =
3065 lease_break_info.lease_break.current_lease.lease_key;
3066 ack.in.lease.lease_state =
3067 lease_break_info.lease_break.new_lease_state;
3068 torture_reset_lease_break_info(tctx, &lease_break_info);
3069 lease_break_info.lease_skip_ack = true;
3071 status = smb2_lease_break_ack(tree, &ack);
3072 CHECK_STATUS(status, NT_STATUS_OK);
3073 CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
3075 CHECK_NO_BREAK(tctx);
3077 done:
3078 smb2_util_close(tree, h1);
3079 smb2_util_close(tree, h2);
3080 smb2_util_close(tree, h3);
3082 smb2_util_unlink(tree, fname);
3083 talloc_free(mem_ctx);
3084 return ret;
3087 static bool test_lease_breaking5(struct torture_context *tctx,
3088 struct smb2_tree *tree)
3090 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3091 struct smb2_create io1 = {};
3092 struct smb2_create io2 = {};
3093 struct smb2_create io3 = {};
3094 struct smb2_lease ls1 = {};
3095 struct smb2_lease ls1t = {};
3096 struct smb2_handle h1 = {};
3097 struct smb2_handle h2 = {};
3098 struct smb2_handle h3 = {};
3099 struct smb2_request *req2 = NULL;
3100 struct lease_break_info lease_break_info_tmp = {};
3101 struct smb2_lease_break_ack ack = {};
3102 const char *fname = "lease_breaking5.dat";
3103 bool ret = true;
3104 NTSTATUS status;
3105 uint32_t caps;
3107 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3108 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3110 smb2_util_unlink(tree, fname);
3112 tree->session->transport->lease.handler = torture_lease_handler;
3113 tree->session->transport->lease.private_data = tree;
3114 tree->session->transport->oplock.handler = torture_oplock_handler;
3115 tree->session->transport->oplock.private_data = tree;
3118 * we defer acking the lease break.
3120 torture_reset_lease_break_info(tctx, &lease_break_info);
3121 lease_break_info.lease_skip_ack = true;
3123 smb2_lease_create_share(&io1, &ls1, false, fname,
3124 smb2_util_share_access("RWD"),
3125 LEASE1,
3126 smb2_util_lease_state("R"));
3127 status = smb2_create(tree, mem_ctx, &io1);
3128 CHECK_STATUS(status, NT_STATUS_OK);
3129 h1 = io1.out.file.handle;
3130 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3131 CHECK_LEASE(&io1, "R", true, LEASE1, 0);
3133 CHECK_NO_BREAK(tctx);
3136 * a conflicting open is *not* blocked until we ack the
3137 * lease break
3139 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
3140 io2.in.create_disposition = NTCREATEX_DISP_OVERWRITE;
3141 req2 = smb2_create_send(tree, &io2);
3142 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3145 * We got a break from RH to NONE, we're supported to ack
3146 * this downgrade
3148 CHECK_BREAK_INFO("R", "", LEASE1);
3150 lease_break_info_tmp = lease_break_info;
3151 torture_reset_lease_break_info(tctx, &lease_break_info);
3152 CHECK_NO_BREAK(tctx);
3154 torture_assert(tctx, req2->state == SMB2_REQUEST_DONE, "req2 done");
3156 status = smb2_create_recv(req2, tctx, &io2);
3157 CHECK_STATUS(status, NT_STATUS_OK);
3158 h2 = io2.out.file.handle;
3159 CHECK_CREATED(&io2, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
3160 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
3162 CHECK_NO_BREAK(tctx);
3165 * We now ask the server about the current lease state
3166 * which should still be "RH", but with
3167 * SMB2_LEASE_FLAG_BREAK_IN_PROGRESS.
3169 smb2_lease_create_share(&io3, &ls1t, false, fname,
3170 smb2_util_share_access("RWD"),
3171 LEASE1,
3172 smb2_util_lease_state(""));
3173 status = smb2_create(tree, mem_ctx, &io3);
3174 CHECK_STATUS(status, NT_STATUS_OK);
3175 h3 = io3.out.file.handle;
3176 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3177 CHECK_LEASE(&io3, "", true, LEASE1, 0);
3180 * We send an ack without without being asked.
3182 CHECK_NO_BREAK(tctx);
3183 lease_break_info = lease_break_info_tmp;
3184 ack.in.lease.lease_key =
3185 lease_break_info.lease_break.current_lease.lease_key;
3186 ack.in.lease.lease_state =
3187 lease_break_info.lease_break.new_lease_state;
3188 torture_reset_lease_break_info(tctx, &lease_break_info);
3189 status = smb2_lease_break_ack(tree, &ack);
3190 CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
3192 CHECK_NO_BREAK(tctx);
3194 done:
3195 smb2_util_close(tree, h1);
3196 smb2_util_close(tree, h2);
3197 smb2_util_close(tree, h3);
3199 smb2_util_unlink(tree, fname);
3200 talloc_free(mem_ctx);
3201 return ret;
3204 static bool test_lease_breaking6(struct torture_context *tctx,
3205 struct smb2_tree *tree)
3207 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3208 struct smb2_create io1 = {};
3209 struct smb2_create io2 = {};
3210 struct smb2_lease ls1 = {};
3211 struct smb2_handle h1a = {};
3212 struct smb2_handle h1b = {};
3213 struct smb2_handle h2 = {};
3214 struct smb2_request *req2 = NULL;
3215 struct smb2_lease_break_ack ack = {};
3216 const char *fname = "lease_breaking6.dat";
3217 bool ret = true;
3218 NTSTATUS status;
3219 uint32_t caps;
3221 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3222 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3224 smb2_util_unlink(tree, fname);
3226 tree->session->transport->lease.handler = torture_lease_handler;
3227 tree->session->transport->lease.private_data = tree;
3228 tree->session->transport->oplock.handler = torture_oplock_handler;
3229 tree->session->transport->oplock.private_data = tree;
3232 * we defer acking the lease break.
3234 torture_reset_lease_break_info(tctx, &lease_break_info);
3235 lease_break_info.lease_skip_ack = true;
3237 smb2_lease_create_share(&io1, &ls1, false, fname,
3238 smb2_util_share_access("RWD"),
3239 LEASE1,
3240 smb2_util_lease_state("RWH"));
3241 status = smb2_create(tree, mem_ctx, &io1);
3242 CHECK_STATUS(status, NT_STATUS_OK);
3243 h1a = io1.out.file.handle;
3244 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3245 CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
3248 * a conflicting open is blocked until we ack the
3249 * lease break
3251 smb2_oplock_create(&io2, fname, SMB2_OPLOCK_LEVEL_NONE);
3252 req2 = smb2_create_send(tree, &io2);
3253 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3256 * we got the lease break, but defer the ack.
3258 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
3260 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
3262 ack.in.lease.lease_key =
3263 lease_break_info.lease_break.current_lease.lease_key;
3264 torture_reset_lease_break_info(tctx, &lease_break_info);
3267 * a open using the same lease key is still works,
3268 * but reports SMB2_LEASE_FLAG_BREAK_IN_PROGRESS
3270 status = smb2_create(tree, mem_ctx, &io1);
3271 CHECK_STATUS(status, NT_STATUS_OK);
3272 h1b = io1.out.file.handle;
3273 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3274 CHECK_LEASE(&io1, "RWH", true, LEASE1, SMB2_LEASE_FLAG_BREAK_IN_PROGRESS);
3275 smb2_util_close(tree, h1b);
3277 CHECK_NO_BREAK(tctx);
3279 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
3282 * We are asked to break to "RH", but we are allowed to
3283 * break to any of "RH", "R" or NONE.
3285 ack.in.lease.lease_state = SMB2_LEASE_NONE;
3286 status = smb2_lease_break_ack(tree, &ack);
3287 CHECK_STATUS(status, NT_STATUS_OK);
3288 CHECK_LEASE_BREAK_ACK(&ack, "", LEASE1);
3290 torture_assert(tctx, req2->cancel.can_cancel,
3291 "req2 can_cancel");
3293 status = smb2_create_recv(req2, tctx, &io2);
3294 CHECK_STATUS(status, NT_STATUS_OK);
3295 h2 = io2.out.file.handle;
3296 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3297 CHECK_VAL(io2.out.oplock_level, SMB2_OPLOCK_LEVEL_NONE);
3299 CHECK_NO_BREAK(tctx);
3300 done:
3301 smb2_util_close(tree, h1a);
3302 smb2_util_close(tree, h1b);
3303 smb2_util_close(tree, h2);
3304 smb2_util_unlink(tree, fname);
3305 talloc_free(mem_ctx);
3306 return ret;
3309 static bool test_lease_lock1(struct torture_context *tctx,
3310 struct smb2_tree *tree1a,
3311 struct smb2_tree *tree2)
3313 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3314 struct smb2_create io1 = {};
3315 struct smb2_create io2 = {};
3316 struct smb2_create io3 = {};
3317 struct smb2_lease ls1 = {};
3318 struct smb2_lease ls2 = {};
3319 struct smb2_lease ls3 = {};
3320 struct smb2_handle h1 = {};
3321 struct smb2_handle h2 = {};
3322 struct smb2_handle h3 = {};
3323 struct smb2_lock lck;
3324 struct smb2_lock_element el[1];
3325 const char *fname = "locktest.dat";
3326 bool ret = true;
3327 NTSTATUS status;
3328 uint32_t caps;
3329 struct smbcli_options options1;
3330 struct smb2_tree *tree1b = NULL;
3332 options1 = tree1a->session->transport->options;
3334 caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3335 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3337 /* Set up handlers. */
3338 tree2->session->transport->lease.handler = torture_lease_handler;
3339 tree2->session->transport->lease.private_data = tree2;
3340 tree2->session->transport->oplock.handler = torture_oplock_handler;
3341 tree2->session->transport->oplock.private_data = tree2;
3343 tree1a->session->transport->lease.handler = torture_lease_handler;
3344 tree1a->session->transport->lease.private_data = tree1a;
3345 tree1a->session->transport->oplock.handler = torture_oplock_handler;
3346 tree1a->session->transport->oplock.private_data = tree1a;
3348 /* create a new connection (same client_guid) */
3349 if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3350 torture_warning(tctx, "couldn't reconnect, bailing\n");
3351 ret = false;
3352 goto done;
3355 tree1b->session->transport->lease.handler = torture_lease_handler;
3356 tree1b->session->transport->lease.private_data = tree1b;
3357 tree1b->session->transport->oplock.handler = torture_oplock_handler;
3358 tree1b->session->transport->oplock.private_data = tree1b;
3360 smb2_util_unlink(tree1a, fname);
3362 torture_reset_lease_break_info(tctx, &lease_break_info);
3363 ZERO_STRUCT(lck);
3365 /* Open a handle on tree1a. */
3366 smb2_lease_create_share(&io1, &ls1, false, fname,
3367 smb2_util_share_access("RWD"),
3368 LEASE1,
3369 smb2_util_lease_state("RWH"));
3370 status = smb2_create(tree1a, mem_ctx, &io1);
3371 CHECK_STATUS(status, NT_STATUS_OK);
3372 h1 = io1.out.file.handle;
3373 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3374 CHECK_LEASE(&io1, "RWH", true, LEASE1, 0);
3376 /* Open a second handle on tree1b. */
3377 smb2_lease_create_share(&io2, &ls2, false, fname,
3378 smb2_util_share_access("RWD"),
3379 LEASE2,
3380 smb2_util_lease_state("RWH"));
3381 status = smb2_create(tree1b, mem_ctx, &io2);
3382 CHECK_STATUS(status, NT_STATUS_OK);
3383 h2 = io2.out.file.handle;
3384 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3385 CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
3386 /* And LEASE1 got broken to RH. */
3387 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
3388 torture_reset_lease_break_info(tctx, &lease_break_info);
3390 /* Now open a lease on a different client guid. */
3391 smb2_lease_create_share(&io3, &ls3, false, fname,
3392 smb2_util_share_access("RWD"),
3393 LEASE3,
3394 smb2_util_lease_state("RWH"));
3395 status = smb2_create(tree2, mem_ctx, &io3);
3396 CHECK_STATUS(status, NT_STATUS_OK);
3397 h3 = io3.out.file.handle;
3398 CHECK_CREATED(&io3, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3399 CHECK_LEASE(&io3, "RH", true, LEASE3, 0);
3400 /* Doesn't break. */
3401 CHECK_NO_BREAK(tctx);
3403 lck.in.locks = el;
3405 * Try and get get an exclusive byte
3406 * range lock on H1 (LEASE1).
3409 lck.in.lock_count = 1;
3410 lck.in.lock_sequence = 1;
3411 lck.in.file.handle = h1;
3412 el[0].offset = 0;
3413 el[0].length = 1;
3414 el[0].reserved = 0;
3415 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
3416 status = smb2_lock(tree1a, &lck);
3417 CHECK_STATUS(status, NT_STATUS_OK);
3419 /* LEASE2 and LEASE3 should get broken to NONE. */
3420 torture_wait_for_lease_break(tctx);
3421 torture_wait_for_lease_break(tctx);
3422 torture_wait_for_lease_break(tctx);
3423 torture_wait_for_lease_break(tctx);
3425 CHECK_VAL(lease_break_info.failures, 0); \
3426 CHECK_VAL(lease_break_info.count, 2); \
3428 /* Get state of the H1 (LEASE1) */
3429 smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
3430 status = smb2_create(tree1a, mem_ctx, &io1);
3431 CHECK_STATUS(status, NT_STATUS_OK);
3432 /* Should still be RH. */
3433 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
3434 smb2_util_close(tree1a, io1.out.file.handle);
3436 /* Get state of the H2 (LEASE2) */
3437 smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state(""));
3438 status = smb2_create(tree1b, mem_ctx, &io2);
3439 CHECK_STATUS(status, NT_STATUS_OK);
3440 CHECK_LEASE(&io2, "", true, LEASE2, 0);
3441 smb2_util_close(tree1b, io2.out.file.handle);
3443 /* Get state of the H3 (LEASE3) */
3444 smb2_lease_create(&io3, &ls3, false, fname, LEASE3, smb2_util_lease_state(""));
3445 status = smb2_create(tree2, mem_ctx, &io3);
3446 CHECK_STATUS(status, NT_STATUS_OK);
3447 CHECK_LEASE(&io3, "", true, LEASE3, 0);
3448 smb2_util_close(tree2, io3.out.file.handle);
3450 torture_reset_lease_break_info(tctx, &lease_break_info);
3453 * Try and get get an exclusive byte
3454 * range lock on H3 (LEASE3).
3456 lck.in.lock_count = 1;
3457 lck.in.lock_sequence = 2;
3458 lck.in.file.handle = h3;
3459 el[0].offset = 100;
3460 el[0].length = 1;
3461 el[0].reserved = 0;
3462 el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE;
3463 status = smb2_lock(tree2, &lck);
3464 CHECK_STATUS(status, NT_STATUS_OK);
3465 /* LEASE1 got broken to NONE. */
3466 CHECK_BREAK_INFO("RH", "", LEASE1);
3467 torture_reset_lease_break_info(tctx, &lease_break_info);
3469 done:
3470 smb2_util_close(tree1a, h1);
3471 smb2_util_close(tree1b, h2);
3472 smb2_util_close(tree2, h3);
3474 smb2_util_unlink(tree1a, fname);
3475 talloc_free(mem_ctx);
3476 return ret;
3479 static bool test_lease_complex1(struct torture_context *tctx,
3480 struct smb2_tree *tree1a)
3482 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3483 struct smb2_create io1;
3484 struct smb2_create io2;
3485 struct smb2_lease ls1;
3486 struct smb2_lease ls2;
3487 struct smb2_handle h = {};
3488 struct smb2_handle h2 = {};
3489 struct smb2_handle h3 = {};
3490 struct smb2_write w;
3491 NTSTATUS status;
3492 const char *fname = "lease_complex1.dat";
3493 bool ret = true;
3494 uint32_t caps;
3495 struct smb2_tree *tree1b = NULL;
3496 struct smbcli_options options1;
3498 options1 = tree1a->session->transport->options;
3500 caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3501 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3503 tree1a->session->transport->lease.handler = torture_lease_handler;
3504 tree1a->session->transport->lease.private_data = tree1a;
3505 tree1a->session->transport->oplock.handler = torture_oplock_handler;
3506 tree1a->session->transport->oplock.private_data = tree1a;
3508 /* create a new connection (same client_guid) */
3509 if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3510 torture_warning(tctx, "couldn't reconnect, bailing\n");
3511 ret = false;
3512 goto done;
3515 tree1b->session->transport->lease.handler = torture_lease_handler;
3516 tree1b->session->transport->lease.private_data = tree1b;
3517 tree1b->session->transport->oplock.handler = torture_oplock_handler;
3518 tree1b->session->transport->oplock.private_data = tree1b;
3520 smb2_util_unlink(tree1a, fname);
3522 torture_reset_lease_break_info(tctx, &lease_break_info);
3524 /* Grab R lease over connection 1a */
3525 smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
3526 status = smb2_create(tree1a, mem_ctx, &io1);
3527 CHECK_STATUS(status, NT_STATUS_OK);
3528 h = io1.out.file.handle;
3529 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3530 CHECK_LEASE(&io1, "R", true, LEASE1, 0);
3532 /* Upgrade to RWH over connection 1b */
3533 ls1.lease_state = smb2_util_lease_state("RWH");
3534 status = smb2_create(tree1b, mem_ctx, &io1);
3535 CHECK_STATUS(status, NT_STATUS_OK);
3536 h2 = io1.out.file.handle;
3537 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3538 CHECK_LEASE(&io1, "RHW", true, LEASE1, 0);
3540 /* close over connection 1b */
3541 status = smb2_util_close(tree1b, h2);
3542 CHECK_STATUS(status, NT_STATUS_OK);
3544 /* Contend with LEASE2. */
3545 smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R"));
3546 status = smb2_create(tree1b, mem_ctx, &io2);
3547 CHECK_STATUS(status, NT_STATUS_OK);
3548 h3 = io2.out.file.handle;
3549 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3550 CHECK_LEASE(&io2, "R", true, LEASE2, 0);
3552 /* Verify that we were only sent one break. */
3553 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
3555 /* again RH over connection 1b doesn't change the epoch */
3556 ls1.lease_state = smb2_util_lease_state("RH");
3557 status = smb2_create(tree1b, mem_ctx, &io1);
3558 CHECK_STATUS(status, NT_STATUS_OK);
3559 h2 = io1.out.file.handle;
3560 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3561 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
3563 /* close over connection 1b */
3564 status = smb2_util_close(tree1b, h2);
3565 CHECK_STATUS(status, NT_STATUS_OK);
3567 torture_reset_lease_break_info(tctx, &lease_break_info);
3569 ZERO_STRUCT(w);
3570 w.in.file.handle = h;
3571 w.in.offset = 0;
3572 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3573 memset(w.in.data.data, 'o', w.in.data.length);
3574 status = smb2_write(tree1a, &w);
3575 CHECK_STATUS(status, NT_STATUS_OK);
3577 ls2.lease_epoch += 1;
3578 CHECK_BREAK_INFO("R", "", LEASE2);
3580 torture_reset_lease_break_info(tctx, &lease_break_info);
3582 ZERO_STRUCT(w);
3583 w.in.file.handle = h3;
3584 w.in.offset = 0;
3585 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3586 memset(w.in.data.data, 'o', w.in.data.length);
3587 status = smb2_write(tree1b, &w);
3588 CHECK_STATUS(status, NT_STATUS_OK);
3590 ls1.lease_epoch += 1;
3591 CHECK_BREAK_INFO("RH", "", LEASE1);
3593 done:
3594 smb2_util_close(tree1a, h);
3595 smb2_util_close(tree1b, h2);
3596 smb2_util_close(tree1b, h3);
3598 smb2_util_unlink(tree1a, fname);
3600 talloc_free(mem_ctx);
3602 return ret;
3605 static bool test_lease_v2_complex1(struct torture_context *tctx,
3606 struct smb2_tree *tree1a)
3608 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3609 struct smb2_create io1;
3610 struct smb2_create io2;
3611 struct smb2_lease ls1;
3612 struct smb2_lease ls2;
3613 struct smb2_handle h = {};
3614 struct smb2_handle h2 = {};
3615 struct smb2_handle h3 = {};
3616 struct smb2_write w;
3617 NTSTATUS status;
3618 const char *fname = "lease_v2_complex1.dat";
3619 bool ret = true;
3620 uint32_t caps;
3621 enum protocol_types protocol;
3622 struct smb2_tree *tree1b = NULL;
3623 struct smbcli_options options1;
3625 options1 = tree1a->session->transport->options;
3627 caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3628 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3630 protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
3631 if (protocol < PROTOCOL_SMB3_00) {
3632 torture_skip(tctx, "v2 leases are not supported");
3635 tree1a->session->transport->lease.handler = torture_lease_handler;
3636 tree1a->session->transport->lease.private_data = tree1a;
3637 tree1a->session->transport->oplock.handler = torture_oplock_handler;
3638 tree1a->session->transport->oplock.private_data = tree1a;
3640 /* create a new connection (same client_guid) */
3641 if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3642 torture_warning(tctx, "couldn't reconnect, bailing\n");
3643 ret = false;
3644 goto done;
3647 tree1b->session->transport->lease.handler = torture_lease_handler;
3648 tree1b->session->transport->lease.private_data = tree1b;
3649 tree1b->session->transport->oplock.handler = torture_oplock_handler;
3650 tree1b->session->transport->oplock.private_data = tree1b;
3652 smb2_util_unlink(tree1a, fname);
3654 torture_reset_lease_break_info(tctx, &lease_break_info);
3656 /* Grab R lease over connection 1a */
3657 smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
3658 smb2_util_lease_state("R"), 0x4711);
3659 status = smb2_create(tree1a, mem_ctx, &io1);
3660 CHECK_STATUS(status, NT_STATUS_OK);
3661 h = io1.out.file.handle;
3662 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3663 ls1.lease_epoch += 1;
3664 CHECK_LEASE_V2(&io1, "R", true, LEASE1,
3665 0, 0, ls1.lease_epoch);
3667 /* Upgrade to RWH over connection 1b */
3668 ls1.lease_state = smb2_util_lease_state("RWH");
3669 status = smb2_create(tree1b, mem_ctx, &io1);
3670 CHECK_STATUS(status, NT_STATUS_OK);
3671 h2 = io1.out.file.handle;
3672 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3673 ls1.lease_epoch += 1;
3674 CHECK_LEASE_V2(&io1, "RHW", true, LEASE1,
3675 0, 0, ls1.lease_epoch);
3677 /* close over connection 1b */
3678 status = smb2_util_close(tree1b, h2);
3679 CHECK_STATUS(status, NT_STATUS_OK);
3681 /* Contend with LEASE2. */
3682 smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
3683 smb2_util_lease_state("R"), 0x11);
3684 status = smb2_create(tree1b, mem_ctx, &io2);
3685 CHECK_STATUS(status, NT_STATUS_OK);
3686 h3 = io2.out.file.handle;
3687 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3688 ls2.lease_epoch += 1;
3689 CHECK_LEASE_V2(&io2, "R", true, LEASE2,
3690 0, 0, ls2.lease_epoch);
3692 /* Verify that we were only sent one break. */
3693 ls1.lease_epoch += 1;
3694 CHECK_BREAK_INFO_V2(tree1a->session->transport,
3695 "RHW", "RH", LEASE1, ls1.lease_epoch);
3697 /* again RH over connection 1b doesn't change the epoch */
3698 ls1.lease_state = smb2_util_lease_state("RH");
3699 status = smb2_create(tree1b, mem_ctx, &io1);
3700 CHECK_STATUS(status, NT_STATUS_OK);
3701 h2 = io1.out.file.handle;
3702 CHECK_CREATED(&io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3703 CHECK_LEASE_V2(&io1, "RH", true, LEASE1,
3704 0, 0, ls1.lease_epoch);
3706 /* close over connection 1b */
3707 status = smb2_util_close(tree1b, h2);
3708 CHECK_STATUS(status, NT_STATUS_OK);
3710 torture_reset_lease_break_info(tctx, &lease_break_info);
3712 ZERO_STRUCT(w);
3713 w.in.file.handle = h;
3714 w.in.offset = 0;
3715 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3716 memset(w.in.data.data, 'o', w.in.data.length);
3717 status = smb2_write(tree1a, &w);
3718 CHECK_STATUS(status, NT_STATUS_OK);
3720 ls2.lease_epoch += 1;
3721 CHECK_BREAK_INFO_V2(tree1a->session->transport,
3722 "R", "", LEASE2, ls2.lease_epoch);
3724 torture_reset_lease_break_info(tctx, &lease_break_info);
3726 ZERO_STRUCT(w);
3727 w.in.file.handle = h3;
3728 w.in.offset = 0;
3729 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3730 memset(w.in.data.data, 'o', w.in.data.length);
3731 status = smb2_write(tree1b, &w);
3732 CHECK_STATUS(status, NT_STATUS_OK);
3734 ls1.lease_epoch += 1;
3735 CHECK_BREAK_INFO_V2(tree1a->session->transport,
3736 "RH", "", LEASE1, ls1.lease_epoch);
3738 done:
3739 smb2_util_close(tree1a, h);
3740 smb2_util_close(tree1b, h2);
3741 smb2_util_close(tree1b, h3);
3743 smb2_util_unlink(tree1a, fname);
3745 talloc_free(mem_ctx);
3747 return ret;
3750 static bool test_lease_v2_complex2(struct torture_context *tctx,
3751 struct smb2_tree *tree1a)
3753 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3754 struct smb2_create io1;
3755 struct smb2_create io2;
3756 struct smb2_lease ls1;
3757 struct smb2_lease ls2;
3758 struct smb2_handle h = {};
3759 struct smb2_handle h2 = {};
3760 struct smb2_request *req2 = NULL;
3761 struct smb2_lease_break_ack ack = {};
3762 NTSTATUS status;
3763 const char *fname = "lease_v2_complex2.dat";
3764 bool ret = true;
3765 uint32_t caps;
3766 enum protocol_types protocol;
3767 struct smb2_tree *tree1b = NULL;
3768 struct smbcli_options options1;
3770 options1 = tree1a->session->transport->options;
3772 caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
3773 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3775 protocol = smbXcli_conn_protocol(tree1a->session->transport->conn);
3776 if (protocol < PROTOCOL_SMB3_00) {
3777 torture_skip(tctx, "v2 leases are not supported");
3780 tree1a->session->transport->lease.handler = torture_lease_handler;
3781 tree1a->session->transport->lease.private_data = tree1a;
3782 tree1a->session->transport->oplock.handler = torture_oplock_handler;
3783 tree1a->session->transport->oplock.private_data = tree1a;
3785 /* create a new connection (same client_guid) */
3786 if (!torture_smb2_connection_ext(tctx, 0, &options1, &tree1b)) {
3787 torture_warning(tctx, "couldn't reconnect, bailing\n");
3788 ret = false;
3789 goto done;
3792 tree1b->session->transport->lease.handler = torture_lease_handler;
3793 tree1b->session->transport->lease.private_data = tree1b;
3794 tree1b->session->transport->oplock.handler = torture_oplock_handler;
3795 tree1b->session->transport->oplock.private_data = tree1b;
3797 smb2_util_unlink(tree1a, fname);
3799 torture_reset_lease_break_info(tctx, &lease_break_info);
3801 /* Grab RWH lease over connection 1a */
3802 smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
3803 smb2_util_lease_state("RWH"), 0x4711);
3804 status = smb2_create(tree1a, mem_ctx, &io1);
3805 CHECK_STATUS(status, NT_STATUS_OK);
3806 h = io1.out.file.handle;
3807 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3808 ls1.lease_epoch += 1;
3809 CHECK_LEASE_V2(&io1, "RWH", true, LEASE1,
3810 0, 0, ls1.lease_epoch);
3813 * we defer acking the lease break.
3815 torture_reset_lease_break_info(tctx, &lease_break_info);
3816 lease_break_info.lease_skip_ack = true;
3818 /* Ask for RWH on connection 1b, different lease. */
3819 smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
3820 smb2_util_lease_state("RWH"), 0x11);
3821 req2 = smb2_create_send(tree1b, &io2);
3822 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3824 ls1.lease_epoch += 1;
3826 CHECK_BREAK_INFO_V2(tree1a->session->transport,
3827 "RWH", "RH", LEASE1, ls1.lease_epoch);
3829 /* Send the break ACK on tree1b. */
3830 ack.in.lease.lease_key =
3831 lease_break_info.lease_break.current_lease.lease_key;
3832 ack.in.lease.lease_state = SMB2_LEASE_HANDLE|SMB2_LEASE_READ;
3834 status = smb2_lease_break_ack(tree1b, &ack);
3835 CHECK_STATUS(status, NT_STATUS_OK);
3836 CHECK_LEASE_BREAK_ACK(&ack, "RH", LEASE1);
3838 torture_reset_lease_break_info(tctx, &lease_break_info);
3840 status = smb2_create_recv(req2, tctx, &io2);
3841 CHECK_STATUS(status, NT_STATUS_OK);
3842 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3843 CHECK_LEASE_V2(&io2, "RH", true, LEASE2,
3844 0, 0, ls2.lease_epoch+1);
3845 h2 = io2.out.file.handle;
3847 done:
3848 smb2_util_close(tree1a, h);
3849 smb2_util_close(tree1b, h2);
3851 smb2_util_unlink(tree1a, fname);
3853 talloc_free(mem_ctx);
3855 return ret;
3859 static bool test_lease_timeout(struct torture_context *tctx,
3860 struct smb2_tree *tree)
3862 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3863 struct smb2_create io;
3864 struct smb2_lease ls1;
3865 struct smb2_lease ls2;
3866 struct smb2_handle h = {};
3867 struct smb2_handle hnew = {};
3868 struct smb2_handle h1b = {};
3869 NTSTATUS status;
3870 const char *fname = "lease_timeout.dat";
3871 bool ret = true;
3872 struct smb2_lease_break_ack ack = {};
3873 struct smb2_request *req2 = NULL;
3874 struct smb2_write w;
3875 uint32_t caps;
3877 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
3878 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
3880 smb2_util_unlink(tree, fname);
3882 /* Grab a RWH lease. */
3883 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
3884 status = smb2_create(tree, mem_ctx, &io);
3885 CHECK_STATUS(status, NT_STATUS_OK);
3886 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
3887 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
3888 h = io.out.file.handle;
3890 tree->session->transport->lease.handler = torture_lease_handler;
3891 tree->session->transport->lease.private_data = tree;
3892 tree->session->transport->oplock.handler = torture_oplock_handler;
3893 tree->session->transport->oplock.private_data = tree;
3896 * Just don't ack the lease break.
3898 torture_reset_lease_break_info(tctx, &lease_break_info);
3899 lease_break_info.lease_skip_ack = true;
3901 /* Break with a RWH request. */
3902 smb2_lease_create(&io, &ls2, false, fname, LEASE2, smb2_util_lease_state("RWH"));
3903 req2 = smb2_create_send(tree, &io);
3904 torture_assert(tctx, req2 != NULL, "smb2_create_send");
3905 torture_assert(tctx, req2->state == SMB2_REQUEST_RECV, "req2 pending");
3907 CHECK_BREAK_INFO("RWH", "RH", LEASE1);
3909 /* Copy the break request. */
3910 ack.in.lease.lease_key =
3911 lease_break_info.lease_break.current_lease.lease_key;
3912 ack.in.lease.lease_state =
3913 lease_break_info.lease_break.new_lease_state;
3915 /* Now wait for the timeout and get the reply. */
3916 status = smb2_create_recv(req2, tctx, &io);
3917 CHECK_STATUS(status, NT_STATUS_OK);
3918 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
3919 CHECK_LEASE(&io, "RH", true, LEASE2, 0);
3920 hnew = io.out.file.handle;
3922 /* Ack the break after the timeout... */
3923 status = smb2_lease_break_ack(tree, &ack);
3924 CHECK_STATUS(status, NT_STATUS_UNSUCCESSFUL);
3926 /* Get state of the original handle. */
3927 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state(""));
3928 status = smb2_create(tree, mem_ctx, &io);
3929 CHECK_STATUS(status, NT_STATUS_OK);
3930 CHECK_LEASE(&io, "", true, LEASE1, 0);
3931 smb2_util_close(tree, io.out.file.handle);
3933 /* Write on the original handle and make sure it's still valid. */
3934 torture_reset_lease_break_info(tctx, &lease_break_info);
3935 ZERO_STRUCT(w);
3936 w.in.file.handle = h;
3937 w.in.offset = 0;
3938 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
3939 memset(w.in.data.data, '1', w.in.data.length);
3940 status = smb2_write(tree, &w);
3941 CHECK_STATUS(status, NT_STATUS_OK);
3943 /* Causes new handle to break to NONE. */
3944 CHECK_BREAK_INFO("RH", "", LEASE2);
3946 /* Write on the new handle. */
3947 torture_reset_lease_break_info(tctx, &lease_break_info);
3948 ZERO_STRUCT(w);
3949 w.in.file.handle = hnew;
3950 w.in.offset = 0;
3951 w.in.data = data_blob_talloc(mem_ctx, NULL, 1024);
3952 memset(w.in.data.data, '2', w.in.data.length);
3953 status = smb2_write(tree, &w);
3954 CHECK_STATUS(status, NT_STATUS_OK);
3955 /* No break - original handle was already NONE. */
3956 CHECK_NO_BREAK(tctx);
3957 smb2_util_close(tree, hnew);
3959 /* Upgrade to R on LEASE1. */
3960 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
3961 status = smb2_create(tree, mem_ctx, &io);
3962 CHECK_STATUS(status, NT_STATUS_OK);
3963 CHECK_LEASE(&io, "R", true, LEASE1, 0);
3964 h1b = io.out.file.handle;
3965 smb2_util_close(tree, h1b);
3967 /* Upgrade to RWH on LEASE1. */
3968 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
3969 status = smb2_create(tree, mem_ctx, &io);
3970 CHECK_STATUS(status, NT_STATUS_OK);
3971 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
3972 h1b = io.out.file.handle;
3973 smb2_util_close(tree, h1b);
3975 done:
3976 smb2_util_close(tree, h);
3977 smb2_util_close(tree, hnew);
3978 smb2_util_close(tree, h1b);
3980 smb2_util_unlink(tree, fname);
3982 talloc_free(mem_ctx);
3984 return ret;
3987 static bool test_lease_rename_wait(struct torture_context *tctx,
3988 struct smb2_tree *tree)
3990 TALLOC_CTX *mem_ctx = talloc_new(tctx);
3991 struct smb2_create io;
3992 struct smb2_lease ls1;
3993 struct smb2_lease ls2;
3994 struct smb2_lease ls3;
3995 struct smb2_handle h1 = {};
3996 struct smb2_handle h2 = {};
3997 struct smb2_handle h3 = {};
3998 union smb_setfileinfo sinfo;
3999 NTSTATUS status;
4000 const char *fname_src = "lease_rename_src.dat";
4001 const char *fname_dst = "lease_rename_dst.dat";
4002 bool ret = true;
4003 struct smb2_lease_break_ack ack = {};
4004 struct smb2_request *rename_req = NULL;
4005 uint32_t caps;
4006 unsigned int i;
4008 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
4009 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4011 smb2_util_unlink(tree, fname_src);
4012 smb2_util_unlink(tree, fname_dst);
4014 /* Short timeout for fails. */
4015 tree->session->transport->options.request_timeout = 15;
4017 /* Grab a RH lease. */
4018 smb2_lease_create(&io,
4019 &ls1,
4020 false,
4021 fname_src,
4022 LEASE1,
4023 smb2_util_lease_state("RH"));
4024 status = smb2_create(tree, mem_ctx, &io);
4025 CHECK_STATUS(status, NT_STATUS_OK);
4026 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4027 CHECK_LEASE(&io, "RH", true, LEASE1, 0);
4028 h1 = io.out.file.handle;
4030 /* Second open with a RH lease. */
4031 smb2_lease_create(&io,
4032 &ls2,
4033 false,
4034 fname_src,
4035 LEASE2,
4036 smb2_util_lease_state("RH"));
4037 io.in.create_disposition = NTCREATEX_DISP_OPEN;
4038 io.in.desired_access = GENERIC_READ_ACCESS;
4039 status = smb2_create(tree, mem_ctx, &io);
4040 CHECK_STATUS(status, NT_STATUS_OK);
4041 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4042 CHECK_LEASE(&io, "RH", true, LEASE2, 0);
4043 h2 = io.out.file.handle;
4046 * Don't ack a lease break.
4048 tree->session->transport->lease.handler = torture_lease_handler;
4049 tree->session->transport->lease.private_data = tree;
4050 torture_reset_lease_break_info(tctx, &lease_break_info);
4051 lease_break_info.lease_skip_ack = true;
4053 /* Break with a rename. */
4054 ZERO_STRUCT(sinfo);
4055 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
4056 sinfo.rename_information.in.file.handle = h1;
4057 sinfo.rename_information.in.overwrite = true;
4058 sinfo.rename_information.in.new_name = fname_dst;
4059 rename_req = smb2_setinfo_file_send(tree, &sinfo);
4061 torture_assert(tctx,
4062 rename_req != NULL,
4063 "smb2_setinfo_file_send");
4064 torture_assert(tctx,
4065 rename_req->state == SMB2_REQUEST_RECV,
4066 "rename pending");
4068 /* Try and open the destination with a RH lease. */
4069 smb2_lease_create(&io,
4070 &ls3,
4071 false,
4072 fname_dst,
4073 LEASE3,
4074 smb2_util_lease_state("RH"));
4075 /* We want to open, not create. */
4076 io.in.create_disposition = NTCREATEX_DISP_OPEN;
4077 io.in.desired_access = GENERIC_READ_ACCESS;
4078 status = smb2_create(tree, mem_ctx, &io);
4079 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
4082 * The smb2_create() I/O should have picked up the break request
4083 * caused by the pending rename.
4086 /* Copy the break request. */
4087 ack.in.lease.lease_key =
4088 lease_break_info.lease_break.current_lease.lease_key;
4089 ack.in.lease.lease_state =
4090 lease_break_info.lease_break.new_lease_state;
4093 * Give the server 3 more chances to have renamed
4094 * the file. Better than doing a sleep.
4096 for (i = 0; i < 3; i++) {
4097 status = smb2_create(tree, mem_ctx, &io);
4098 CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
4101 /* Ack the break. The server is now free to rename. */
4102 status = smb2_lease_break_ack(tree, &ack);
4103 CHECK_STATUS(status, NT_STATUS_OK);
4105 /* Get the rename reply. */
4106 status = smb2_setinfo_recv(rename_req);
4107 CHECK_STATUS(status, NT_STATUS_OK);
4109 /* The target should now exist. */
4110 status = smb2_create(tree, mem_ctx, &io);
4111 CHECK_STATUS(status, NT_STATUS_OK);
4112 h3 = io.out.file.handle;
4114 done:
4115 smb2_util_close(tree, h1);
4116 smb2_util_close(tree, h2);
4117 smb2_util_close(tree, h3);
4119 smb2_util_unlink(tree, fname_src);
4120 smb2_util_unlink(tree, fname_dst);
4122 talloc_free(mem_ctx);
4124 return ret;
4127 static bool test_lease_v2_rename(struct torture_context *tctx,
4128 struct smb2_tree *tree)
4130 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4131 struct smb2_create io;
4132 struct smb2_lease ls1;
4133 struct smb2_lease ls2;
4134 struct smb2_handle h = {};
4135 struct smb2_handle h1 = {};
4136 struct smb2_handle h2 = {};
4137 union smb_setfileinfo sinfo;
4138 const char *fname = "lease_v2_rename_src.dat";
4139 const char *fname_dst = "lease_v2_rename_dst.dat";
4140 bool ret = true;
4141 NTSTATUS status;
4142 uint32_t caps;
4143 enum protocol_types protocol;
4145 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
4146 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4148 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
4149 if (protocol < PROTOCOL_SMB3_00) {
4150 torture_skip(tctx, "v2 leases are not supported");
4153 smb2_util_unlink(tree, fname);
4154 smb2_util_unlink(tree, fname_dst);
4156 tree->session->transport->lease.handler = torture_lease_handler;
4157 tree->session->transport->lease.private_data = tree;
4158 tree->session->transport->oplock.handler = torture_oplock_handler;
4159 tree->session->transport->oplock.private_data = tree;
4161 torture_reset_lease_break_info(tctx, &lease_break_info);
4163 ZERO_STRUCT(io);
4164 smb2_lease_v2_create_share(&io, &ls1, false, fname,
4165 smb2_util_share_access("RWD"),
4166 LEASE1, NULL,
4167 smb2_util_lease_state("RHW"),
4168 0x4711);
4169 status = smb2_create(tree, mem_ctx, &io);
4170 CHECK_STATUS(status, NT_STATUS_OK);
4171 h = io.out.file.handle;
4172 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4173 ls1.lease_epoch += 1;
4174 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
4176 /* Now rename - what happens ? */
4177 ZERO_STRUCT(sinfo);
4178 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
4179 sinfo.rename_information.in.file.handle = h;
4180 sinfo.rename_information.in.overwrite = true;
4181 sinfo.rename_information.in.new_name = fname_dst;
4182 status = smb2_setinfo_file(tree, &sinfo);
4183 CHECK_STATUS(status, NT_STATUS_OK);
4185 /* No lease break. */
4186 CHECK_NO_BREAK(tctx);
4188 /* Check we can open another handle on the new name. */
4189 smb2_lease_v2_create_share(&io, &ls1, false, fname_dst,
4190 smb2_util_share_access("RWD"),
4191 LEASE1, NULL,
4192 smb2_util_lease_state(""),
4193 ls1.lease_epoch);
4194 status = smb2_create(tree, mem_ctx, &io);
4195 CHECK_STATUS(status, NT_STATUS_OK);
4196 h1 = io.out.file.handle;
4197 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4198 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
4199 smb2_util_close(tree, h1);
4201 /* Try another lease key. */
4202 smb2_lease_v2_create_share(&io, &ls2, false, fname_dst,
4203 smb2_util_share_access("RWD"),
4204 LEASE2, NULL,
4205 smb2_util_lease_state("RWH"),
4206 0x44);
4207 status = smb2_create(tree, mem_ctx, &io);
4208 CHECK_STATUS(status, NT_STATUS_OK);
4209 h2 = io.out.file.handle;
4210 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4211 ls2.lease_epoch += 1;
4212 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0, 0, ls2.lease_epoch );
4213 CHECK_BREAK_INFO_V2(tree->session->transport,
4214 "RWH", "RH", LEASE1, ls1.lease_epoch + 1);
4215 ls1.lease_epoch += 1;
4216 torture_reset_lease_break_info(tctx, &lease_break_info);
4218 /* Now rename back. */
4219 ZERO_STRUCT(sinfo);
4220 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
4221 sinfo.rename_information.in.file.handle = h;
4222 sinfo.rename_information.in.overwrite = true;
4223 sinfo.rename_information.in.new_name = fname;
4224 status = smb2_setinfo_file(tree, &sinfo);
4225 CHECK_STATUS(status, NT_STATUS_OK);
4227 /* Breaks to R on LEASE2. */
4228 CHECK_BREAK_INFO_V2(tree->session->transport,
4229 "RH", "R", LEASE2, ls2.lease_epoch + 1);
4230 ls2.lease_epoch += 1;
4232 /* Check we can open another handle on the current name. */
4233 smb2_lease_v2_create_share(&io, &ls1, false, fname,
4234 smb2_util_share_access("RWD"),
4235 LEASE1, NULL,
4236 smb2_util_lease_state(""),
4237 ls1.lease_epoch);
4238 status = smb2_create(tree, mem_ctx, &io);
4239 CHECK_STATUS(status, NT_STATUS_OK);
4240 h1 = io.out.file.handle;
4241 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4242 CHECK_LEASE_V2(&io, "RH", true, LEASE1, 0, 0, ls1.lease_epoch);
4243 smb2_util_close(tree, h1);
4245 done:
4247 smb2_util_close(tree, h);
4248 smb2_util_close(tree, h1);
4249 smb2_util_close(tree, h2);
4251 smb2_util_unlink(tree, fname);
4252 smb2_util_unlink(tree, fname_dst);
4254 smb2_util_unlink(tree, fname);
4255 talloc_free(mem_ctx);
4256 return ret;
4260 * Try doing a rename overwrite where the target file is open
4261 * with a RWH lease.
4264 static bool test_lease_v2_rename_target_overwrite(struct torture_context *tctx,
4265 struct smb2_tree *tree)
4267 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4268 struct smb2_create io;
4269 struct smb2_create io_dst;
4270 struct smb2_lease ls1;
4271 struct smb2_lease ls_dst;
4272 struct smb2_handle h = {};
4273 struct smb2_handle h_dst = {};
4274 union smb_setfileinfo sinfo;
4275 const char *fname = "lease_v2_rename_overwrite_src.dat";
4276 const char *fname_dst = "lease_v2_rename_overwrite_dst.dat";
4277 bool ret = true;
4278 NTSTATUS status;
4279 uint32_t caps;
4280 enum protocol_types protocol;
4281 struct smb2_request *rename_req = NULL;
4283 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
4284 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4286 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
4287 if (protocol < PROTOCOL_SMB3_00) {
4288 torture_skip(tctx, "v2 leases are not supported");
4291 smb2_util_unlink(tree, fname);
4292 smb2_util_unlink(tree, fname_dst);
4294 tree->session->transport->lease.handler = torture_lease_handler;
4295 tree->session->transport->lease.private_data = tree;
4296 tree->session->transport->oplock.handler = torture_oplock_handler;
4297 tree->session->transport->oplock.private_data = tree;
4299 torture_reset_lease_break_info(tctx, &lease_break_info);
4301 ZERO_STRUCT(io);
4302 smb2_lease_v2_create_share(&io, &ls1, false, fname,
4303 smb2_util_share_access("RWD"),
4304 LEASE1, NULL,
4305 smb2_util_lease_state("RHW"),
4306 0x4711);
4307 status = smb2_create(tree, mem_ctx, &io);
4308 CHECK_STATUS(status, NT_STATUS_OK);
4309 h = io.out.file.handle;
4310 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4311 ls1.lease_epoch += 1;
4312 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0, 0, ls1.lease_epoch);
4314 /* Create the target file with a lease and leave open. */
4315 ZERO_STRUCT(io_dst);
4316 smb2_lease_v2_create_share(&io_dst, &ls_dst, false, fname_dst,
4317 smb2_util_share_access("RWD"),
4318 LEASE2, NULL,
4319 smb2_util_lease_state("RHW"),
4320 0x4711);
4321 status = smb2_create(tree, mem_ctx, &io_dst);
4322 CHECK_STATUS(status, NT_STATUS_OK);
4323 h_dst = io_dst.out.file.handle;
4324 CHECK_CREATED(&io_dst, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4325 ls_dst.lease_epoch += 1;
4326 CHECK_LEASE_V2(&io_dst, "RHW", true, LEASE2, 0, 0, ls_dst.lease_epoch);
4329 * Now rename - should break the target lease then return
4330 * ACCESS_DENIED.
4331 * */
4332 ZERO_STRUCT(sinfo);
4333 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
4334 sinfo.rename_information.in.file.handle = h;
4335 sinfo.rename_information.in.overwrite = true;
4336 sinfo.rename_information.in.new_name = fname_dst;
4337 status = smb2_setinfo_file(tree, &sinfo);
4338 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
4340 CHECK_BREAK_INFO_V2(tree->session->transport,
4341 "RWH", "RW", LEASE2, ls_dst.lease_epoch + 1);
4343 torture_reset_lease_break_info(tctx, &lease_break_info);
4344 lease_break_info.lease_skip_ack = true;
4347 * Do the rename again, this time there's no h-lease on the dst anymore,
4348 * so we should get no break and the rename should still fail.
4351 torture_reset_lease_break_info(tctx, &lease_break_info);
4353 status = smb2_setinfo_file(tree, &sinfo);
4354 CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
4356 CHECK_NO_BREAK(tctx);
4359 * Do the rename again, but this time close the handle on the
4360 * destination when receiving the h-lease break.
4363 torture_reset_lease_break_info(tctx, &lease_break_info);
4364 lease_break_info.lease_skip_ack = true;
4366 status = smb2_util_close(tree, h_dst);
4367 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4368 "smb2_util_close failed\n");
4369 ZERO_STRUCT(h_dst);
4371 status = smb2_create(tree, mem_ctx, &io_dst);
4372 CHECK_STATUS(status, NT_STATUS_OK);
4373 h_dst = io_dst.out.file.handle;
4374 ls_dst.lease_epoch += 1;
4376 ZERO_STRUCT(sinfo);
4377 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
4378 sinfo.rename_information.in.file.handle = h;
4379 sinfo.rename_information.in.overwrite = true;
4380 sinfo.rename_information.in.new_name = fname_dst;
4381 rename_req = smb2_setinfo_file_send(tree, &sinfo);
4383 torture_assert(tctx,
4384 rename_req != NULL,
4385 "smb2_setinfo_file_send");
4386 torture_assert(tctx,
4387 rename_req->state == SMB2_REQUEST_RECV,
4388 "rename pending");
4390 CHECK_BREAK_INFO_V2(tree->session->transport,
4391 "RWH", "RW", LEASE2, ls_dst.lease_epoch + 1);
4393 status = smb2_util_close(tree, h_dst);
4394 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4395 "smb2_util_close failed\n");
4396 ZERO_STRUCT(h_dst);
4398 /* Get the rename reply. */
4399 status = smb2_setinfo_recv(rename_req);
4400 CHECK_STATUS(status, NT_STATUS_OK);
4402 done:
4404 smb2_util_close(tree, h);
4405 smb2_util_close(tree, h_dst);
4407 smb2_util_unlink(tree, fname);
4408 smb2_util_unlink(tree, fname_dst);
4410 talloc_free(mem_ctx);
4411 return ret;
4414 static bool test_lease_dynamic_share(struct torture_context *tctx,
4415 struct smb2_tree *tree1a)
4417 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4418 struct smb2_create io;
4419 struct smb2_lease ls1;
4420 struct smb2_handle h = {}, h1 = {}, h2 = {};
4421 struct smb2_write w;
4422 NTSTATUS status;
4423 const char *fname = "dynamic_path.dat";
4424 bool ret = true;
4425 uint32_t caps;
4426 struct smb2_tree *tree_2 = NULL;
4427 struct smb2_tree *tree_3 = NULL;
4428 struct smbcli_options options;
4429 const char *orig_share = NULL;
4431 if (!TARGET_IS_SAMBA3(tctx)) {
4432 torture_skip(tctx, "dynamic shares are not supported");
4433 return true;
4436 options = tree1a->session->transport->options;
4437 options.client_guid = GUID_random();
4439 caps = smb2cli_conn_server_capabilities(tree1a->session->transport->conn);
4440 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4443 * Save off original share name and change it to dynamic_share.
4444 * This must have been pre-created with a dynamic path containing
4445 * %t. It means we'll sleep between the connects in order to
4446 * get a different timestamp for the share path.
4449 orig_share = lpcfg_parm_string(tctx->lp_ctx, NULL, "torture", "share");
4450 orig_share = talloc_strdup(tctx->lp_ctx, orig_share);
4451 if (orig_share == NULL) {
4452 torture_result(tctx, TORTURE_FAIL, __location__ "no memory\n");
4453 ret = false;
4454 goto done;
4456 lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", "dynamic_share");
4458 /* create a new connection (same client_guid) */
4459 sleep(2);
4460 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_2)) {
4461 torture_result(tctx, TORTURE_FAIL,
4462 __location__ "couldn't reconnect "
4463 "max protocol 2.1, bailing\n");
4464 ret = false;
4465 goto done;
4468 tree_2->session->transport->lease.handler = torture_lease_handler;
4469 tree_2->session->transport->lease.private_data = tree_2;
4470 tree_2->session->transport->oplock.handler = torture_oplock_handler;
4471 tree_2->session->transport->oplock.private_data = tree_2;
4473 smb2_util_unlink(tree_2, fname);
4475 /* create a new connection (same client_guid) */
4476 sleep(2);
4477 if (!torture_smb2_connection_ext(tctx, 0, &options, &tree_3)) {
4478 torture_result(tctx, TORTURE_FAIL,
4479 __location__ "couldn't reconnect "
4480 "max protocol 3.0, bailing\n");
4481 ret = false;
4482 goto done;
4485 tree_3->session->transport->lease.handler = torture_lease_handler;
4486 tree_3->session->transport->lease.private_data = tree_3;
4487 tree_3->session->transport->oplock.handler = torture_oplock_handler;
4488 tree_3->session->transport->oplock.private_data = tree_3;
4490 smb2_util_unlink(tree_3, fname);
4492 torture_reset_lease_break_info(tctx, &lease_break_info);
4494 /* Get RWH lease over connection 2 */
4495 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4496 status = smb2_create(tree_2, mem_ctx, &io);
4497 CHECK_STATUS(status, NT_STATUS_OK);
4498 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4499 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4500 h = io.out.file.handle;
4502 /* Write some data into it. */
4503 w.in.file.handle = h;
4504 w.in.offset = 0;
4505 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
4506 memset(w.in.data.data, '1', w.in.data.length);
4507 status = smb2_write(tree_2, &w);
4508 CHECK_STATUS(status, NT_STATUS_OK);
4510 /* Open the same name over connection 3. */
4511 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4512 status = smb2_create(tree_3, mem_ctx, &io);
4513 CHECK_STATUS(status, NT_STATUS_OK);
4514 h1 = io.out.file.handle;
4515 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4517 /* h1 should have replied with NONE. */
4518 CHECK_LEASE(&io, "", true, LEASE1, 0);
4520 /* We should have broken h to NONE. */
4521 CHECK_BREAK_INFO("RWH", "", LEASE1);
4523 /* Try to upgrade to RWH over connection 2 */
4524 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4525 status = smb2_create(tree_2, mem_ctx, &io);
4526 CHECK_STATUS(status, NT_STATUS_OK);
4527 h2 = io.out.file.handle;
4528 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4529 CHECK_VAL(io.out.size, 4096);
4530 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4531 /* Should have been denied. */
4532 CHECK_LEASE(&io, "", true, LEASE1, 0);
4533 smb2_util_close(tree_2, h2);
4535 /* Try to upgrade to RWH over connection 3 */
4536 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4537 status = smb2_create(tree_3, mem_ctx, &io);
4538 CHECK_STATUS(status, NT_STATUS_OK);
4539 h2 = io.out.file.handle;
4540 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4541 CHECK_VAL(io.out.size, 0);
4542 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4543 /* Should have been denied. */
4544 CHECK_LEASE(&io, "", true, LEASE1, 0);
4545 smb2_util_close(tree_3, h2);
4547 /* Write some data into it. */
4548 w.in.file.handle = h1;
4549 w.in.offset = 0;
4550 w.in.data = data_blob_talloc(mem_ctx, NULL, 1024);
4551 memset(w.in.data.data, '2', w.in.data.length);
4552 status = smb2_write(tree_3, &w);
4553 CHECK_STATUS(status, NT_STATUS_OK);
4555 /* Close everything.. */
4556 smb2_util_close(tree_2, h);
4557 smb2_util_close(tree_3, h1);
4559 /* And ensure we can get a lease ! */
4560 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4561 status = smb2_create(tree_2, mem_ctx, &io);
4562 CHECK_STATUS(status, NT_STATUS_OK);
4563 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4564 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4565 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4566 h = io.out.file.handle;
4567 /* And the file is the right size. */
4568 CHECK_VAL(io.out.size, 4096); \
4569 /* Close it. */
4570 smb2_util_close(tree_2, h);
4572 /* And ensure we can get a lease ! */
4573 smb2_lease_create(&io, &ls1, false, fname, LEASE1, smb2_util_lease_state("RWH"));
4574 status = smb2_create(tree_3, mem_ctx, &io);
4575 CHECK_STATUS(status, NT_STATUS_OK);
4576 CHECK_VAL(io.out.create_action, NTCREATEX_ACTION_EXISTED);
4577 CHECK_VAL(io.out.file_attr, FILE_ATTRIBUTE_ARCHIVE);
4578 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4579 h = io.out.file.handle;
4580 /* And the file is the right size. */
4581 CHECK_VAL(io.out.size, 1024); \
4582 /* Close it. */
4583 smb2_util_close(tree_3, h);
4585 done:
4587 if (tree_2 != NULL) {
4588 smb2_util_close(tree_2, h);
4589 smb2_util_unlink(tree_2, fname);
4591 if (tree_3 != NULL) {
4592 smb2_util_close(tree_3, h1);
4593 smb2_util_close(tree_3, h2);
4595 smb2_util_unlink(tree_3, fname);
4598 /* Set sharename back. */
4599 lpcfg_set_cmdline(tctx->lp_ctx, "torture:share", orig_share);
4601 talloc_free(mem_ctx);
4603 return ret;
4607 * Test identifies a bug where the Samba server will not trigger a lease break
4608 * for a handle caching lease held by a client when the underlying file is
4609 * deleted.
4610 * Test:
4611 * Connect session2.
4612 * open file in session1
4613 * session1 should have RWH lease.
4614 * open file in session2
4615 * lease break sent to session1 to downgrade lease to RH
4616 * close file in session 2
4617 * unlink file in session 2
4618 * lease break sent to session1 to downgrade lease to R
4619 * Cleanup
4621 static bool test_lease_unlink(struct torture_context *tctx,
4622 struct smb2_tree *tree1)
4624 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4625 NTSTATUS status;
4626 bool ret = true;
4627 struct smbcli_options transport2_options;
4628 struct smb2_tree *tree2 = NULL;
4629 struct smb2_transport *transport1 = tree1->session->transport;
4630 struct smb2_transport *transport2;
4631 struct smb2_handle h1 = {};
4632 struct smb2_handle h2 = {};
4633 const char *fname = "lease_unlink.dat";
4634 uint32_t caps;
4635 struct smb2_create io1;
4636 struct smb2_create io2;
4637 struct smb2_lease ls1;
4638 struct smb2_lease ls2;
4639 union smb_setfileinfo sfinfo = {};
4641 caps = smb2cli_conn_server_capabilities(
4642 tree1->session->transport->conn);
4643 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4645 /* Connect 2nd connection */
4646 transport2_options = transport1->options;
4647 transport2_options.client_guid = GUID_random();
4648 if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) {
4649 torture_warning(tctx, "couldn't reconnect, bailing\n");
4650 return false;
4652 transport2 = tree2->session->transport;
4654 /* Set lease handlers */
4655 transport1->lease.handler = torture_lease_handler;
4656 transport1->lease.private_data = tree1;
4657 transport2->lease.handler = torture_lease_handler;
4658 transport2->lease.private_data = tree2;
4661 smb2_lease_create(&io1, &ls1, false, fname, LEASE1,
4662 smb2_util_lease_state("RHW"));
4663 smb2_lease_create(&io2, &ls2, false, fname, LEASE2,
4664 smb2_util_lease_state("RHW"));
4666 smb2_util_unlink(tree1, fname);
4668 torture_comment(tctx, "Client opens fname with session 1\n");
4669 torture_reset_lease_break_info(tctx, &lease_break_info);
4670 status = smb2_create(tree1, mem_ctx, &io1);
4671 CHECK_STATUS(status, NT_STATUS_OK);
4672 h1 = io1.out.file.handle;
4673 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4674 CHECK_LEASE(&io1, "RHW", true, LEASE1, 0);
4675 CHECK_VAL(lease_break_info.count, 0);
4677 torture_comment(tctx, "Client opens fname with session 2\n");
4678 torture_reset_lease_break_info(tctx, &lease_break_info);
4679 status = smb2_create(tree2, mem_ctx, &io2);
4680 CHECK_STATUS(status, NT_STATUS_OK);
4681 h2 = io2.out.file.handle;
4682 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4683 CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
4684 CHECK_VAL(lease_break_info.count, 1);
4685 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
4687 torture_comment(tctx,
4688 "Client closes and then unlinks fname with session 2\n");
4689 torture_reset_lease_break_info(tctx, &lease_break_info);
4690 smb2_util_close(tree2, h2);
4691 smb2_util_unlink(tree2, fname);
4692 CHECK_VAL(lease_break_info.count, 1);
4693 CHECK_BREAK_INFO("RH", "R", LEASE1);
4695 smb2_util_close(tree1, h1);
4697 torture_comment(tctx, "Client 1 recreates file with RH lease\n");
4699 torture_reset_lease_break_info(tctx, &lease_break_info);
4701 smb2_lease_create(&io1, &ls1, false, fname, LEASE1,
4702 smb2_util_lease_state("RH"));
4704 status = smb2_create(tree1, mem_ctx, &io1);
4705 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4706 "create failed\n");
4707 h1 = io1.out.file.handle;
4708 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4709 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
4710 CHECK_VAL(lease_break_info.count, 0);
4712 torture_comment(tctx, "Client 2 opens with RH lease\n");
4714 smb2_lease_create(&io2, &ls2, false, fname, LEASE2,
4715 smb2_util_lease_state("RH"));
4716 status = smb2_create(tree2, mem_ctx, &io2);
4717 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4718 "create failed\n");
4720 h2 = io2.out.file.handle;
4721 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
4722 CHECK_LEASE(&io2, "RH", true, LEASE2, 0);
4723 CHECK_VAL(lease_break_info.count, 0);
4725 torture_comment(tctx, "Client 2 sets delete on close, "
4726 "triggering lease break\n");
4728 sfinfo.disposition_info.in.delete_on_close = 1;
4729 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
4730 sfinfo.generic.in.file.handle = h2;
4732 status = smb2_setinfo_file(tree2, &sfinfo);
4733 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4734 "Set DELETE_ON_CLOSE disposition "
4735 "returned un expected status.\n");
4737 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
4738 CHECK_VAL(lease_break_info.count, 1);
4740 status = smb2_util_close(tree2, h2);
4741 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
4742 "smb2_util_close failed\n");
4743 ZERO_STRUCT(h2);
4745 done:
4746 smb2_util_close(tree1, h1);
4747 smb2_util_close(tree2, h2);
4748 smb2_util_unlink(tree1, fname);
4750 return ret;
4753 static bool test_lease_timeout_disconnect(struct torture_context *tctx,
4754 struct smb2_tree *tree1)
4756 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4757 NTSTATUS status;
4758 bool ret = true;
4759 struct smbcli_options transport2_options;
4760 struct smbcli_options transport3_options;
4761 struct smb2_tree *tree2 = NULL;
4762 struct smb2_tree *tree3 = NULL;
4763 struct smb2_transport *transport1 = tree1->session->transport;
4764 struct smb2_transport *transport2;
4765 struct smb2_transport *transport3;
4766 const char *fname = "lease_timeout_logoff.dat" ;
4767 uint32_t caps;
4768 struct smb2_create io1;
4769 struct smb2_create io2;
4770 struct smb2_request *req2 = NULL;
4771 struct smb2_lease ls1;
4773 caps = smb2cli_conn_server_capabilities(
4774 tree1->session->transport->conn);
4775 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4777 smb2_util_unlink(tree1, fname);
4779 /* Connect 2nd connection */
4780 torture_comment(tctx, "connect tree2 with the same client_guid\n");
4781 transport2_options = transport1->options;
4782 if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) {
4783 torture_warning(tctx, "couldn't reconnect, bailing\n");
4784 return false;
4786 transport2 = tree2->session->transport;
4788 /* Connect 3rd connection */
4789 torture_comment(tctx, "connect tree3 with the same client_guid\n");
4790 transport3_options = transport1->options;
4791 if (!torture_smb2_connection_ext(tctx, 0, &transport3_options, &tree3)) {
4792 torture_warning(tctx, "couldn't reconnect, bailing\n");
4793 return false;
4795 transport3 = tree3->session->transport;
4797 /* Set lease handlers */
4798 transport1->lease.handler = torture_lease_handler;
4799 transport1->lease.private_data = tree1;
4800 transport2->lease.handler = torture_lease_handler;
4801 transport2->lease.private_data = tree2;
4802 transport3->lease.handler = torture_lease_handler;
4803 transport3->lease.private_data = tree3;
4805 smb2_lease_create_share(&io1, &ls1, false, fname,
4806 smb2_util_share_access(""),
4807 LEASE1,
4808 smb2_util_lease_state("RH"));
4809 io1.in.durable_open = true;
4810 smb2_generic_create(&io2, NULL, false, fname,
4811 NTCREATEX_DISP_OPEN_IF,
4812 SMB2_OPLOCK_LEVEL_NONE, 0, 0);
4814 torture_comment(tctx, "tree1: create file[%s] with durable RH lease (SHARE NONE)\n", fname);
4815 torture_reset_lease_break_info(tctx, &lease_break_info);
4816 lease_break_info.lease_skip_ack = true;
4817 status = smb2_create(tree1, mem_ctx, &io1);
4818 CHECK_STATUS(status, NT_STATUS_OK);
4819 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4820 CHECK_LEASE(&io1, "RH", true, LEASE1, 0);
4821 CHECK_VAL(lease_break_info.count, 0);
4823 torture_comment(tctx, "tree1: skip lease acks\n");
4824 torture_reset_lease_break_info(tctx, &lease_break_info);
4825 lease_break_info.lease_skip_ack = true;
4826 torture_comment(tctx, "tree2: open file[%s] without lease (SHARE RWD)\n", fname);
4827 req2 = smb2_create_send(tree2, &io2);
4828 torture_assert(tctx, req2 != NULL, "req2 started");
4830 torture_comment(tctx, "tree1: wait for lease break\n");
4831 torture_wait_for_lease_break(tctx);
4832 CHECK_VAL(lease_break_info.count, 1);
4833 CHECK_BREAK_INFO("RH", "R", LEASE1);
4835 torture_comment(tctx, "tree1: reset lease handler\n");
4836 torture_reset_lease_break_info(tctx, &lease_break_info);
4837 lease_break_info.lease_skip_ack = true;
4838 CHECK_VAL(lease_break_info.count, 0);
4840 torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n");
4841 torture_assert_int_equal(tctx, req2->state,
4842 SMB2_REQUEST_RECV,
4843 "SMB2_REQUEST_RECV");
4845 torture_comment(tctx, "sleep 1\n");
4846 smb_msleep(1000);
4848 torture_comment(tctx, "transport1: keepalive\n");
4849 status = smb2_keepalive(transport1);
4850 CHECK_STATUS(status, NT_STATUS_OK);
4852 torture_comment(tctx, "transport2: keepalive\n");
4853 status = smb2_keepalive(transport2);
4854 CHECK_STATUS(status, NT_STATUS_OK);
4856 torture_comment(tctx, "transport3: keepalive\n");
4857 status = smb2_keepalive(transport3);
4858 CHECK_STATUS(status, NT_STATUS_OK);
4860 torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n");
4861 torture_assert_int_equal(tctx, req2->state,
4862 SMB2_REQUEST_RECV,
4863 "SMB2_REQUEST_RECV");
4864 torture_comment(tctx, "tree2: check for STATUS_PENDING\n");
4865 torture_assert(tctx, req2->cancel.can_cancel, "STATUS_PENDING");
4867 torture_comment(tctx, "sleep 1\n");
4868 smb_msleep(1000);
4869 torture_comment(tctx, "transport1: keepalive\n");
4870 status = smb2_keepalive(transport1);
4871 CHECK_STATUS(status, NT_STATUS_OK);
4872 torture_comment(tctx, "transport2: disconnect\n");
4873 TALLOC_FREE(tree2);
4875 torture_comment(tctx, "sleep 1\n");
4876 smb_msleep(1000);
4877 torture_comment(tctx, "transport1: keepalive\n");
4878 status = smb2_keepalive(transport1);
4879 CHECK_STATUS(status, NT_STATUS_OK);
4880 torture_comment(tctx, "transport1: disconnect\n");
4881 TALLOC_FREE(tree1);
4883 torture_comment(tctx, "sleep 1\n");
4884 smb_msleep(1000);
4885 torture_comment(tctx, "transport3: keepalive\n");
4886 status = smb2_keepalive(transport3);
4887 CHECK_STATUS(status, NT_STATUS_OK);
4888 torture_comment(tctx, "transport3: disconnect\n");
4889 TALLOC_FREE(tree3);
4891 done:
4893 return ret;
4896 static bool test_lease_duplicate_create(struct torture_context *tctx,
4897 struct smb2_tree *tree)
4899 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4900 struct smb2_create io;
4901 struct smb2_lease ls;
4902 struct smb2_handle h1 = {};
4903 struct smb2_handle h2 = {};
4904 NTSTATUS status;
4905 const char *fname1 = "duplicate_create1.dat";
4906 const char *fname2 = "duplicate_create2.dat";
4907 bool ret = true;
4908 uint32_t caps;
4910 caps = smb2cli_conn_server_capabilities(
4911 tree->session->transport->conn);
4912 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4914 /* Ensure files don't exist. */
4915 smb2_util_unlink(tree, fname1);
4916 smb2_util_unlink(tree, fname2);
4918 /* Create file1 - LEASE1 key. */
4919 smb2_lease_create(&io, &ls, false, fname1, LEASE1,
4920 smb2_util_lease_state("RWH"));
4921 status = smb2_create(tree, mem_ctx, &io);
4922 CHECK_STATUS(status, NT_STATUS_OK);
4923 h1 = io.out.file.handle;
4924 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4925 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4928 * Create file2 with the same LEASE1 key - this should fail with.
4929 * INVALID_PARAMETER.
4931 smb2_lease_create(&io, &ls, false, fname2, LEASE1,
4932 smb2_util_lease_state("RWH"));
4933 status = smb2_create(tree, mem_ctx, &io);
4934 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
4935 smb2_util_close(tree, h1);
4937 done:
4938 smb2_util_close(tree, h2);
4939 smb2_util_close(tree, h1);
4940 smb2_util_unlink(tree, fname1);
4941 smb2_util_unlink(tree, fname2);
4942 talloc_free(mem_ctx);
4943 return ret;
4946 static bool test_lease_duplicate_open(struct torture_context *tctx,
4947 struct smb2_tree *tree)
4949 TALLOC_CTX *mem_ctx = talloc_new(tctx);
4950 struct smb2_create io;
4951 struct smb2_lease ls;
4952 struct smb2_handle h1 = {};
4953 struct smb2_handle h2 = {};
4954 NTSTATUS status;
4955 const char *fname1 = "duplicate_open1.dat";
4956 const char *fname2 = "duplicate_open2.dat";
4957 bool ret = true;
4958 uint32_t caps;
4960 caps = smb2cli_conn_server_capabilities(
4961 tree->session->transport->conn);
4962 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
4964 /* Ensure files don't exist. */
4965 smb2_util_unlink(tree, fname1);
4966 smb2_util_unlink(tree, fname2);
4968 /* Create file1 - LEASE1 key. */
4969 smb2_lease_create(&io, &ls, false, fname1, LEASE1,
4970 smb2_util_lease_state("RWH"));
4971 status = smb2_create(tree, mem_ctx, &io);
4972 CHECK_STATUS(status, NT_STATUS_OK);
4973 h1 = io.out.file.handle;
4974 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4975 CHECK_LEASE(&io, "RWH", true, LEASE1, 0);
4977 /* Leave file1 open and leased. */
4979 /* Create file2 - no lease. */
4980 smb2_lease_create(&io, NULL, false, fname2, 0,
4981 smb2_util_lease_state("RWH"));
4982 status = smb2_create(tree, mem_ctx, &io);
4983 CHECK_STATUS(status, NT_STATUS_OK);
4984 h2 = io.out.file.handle;
4985 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
4986 /* Close it. */
4987 smb2_util_close(tree, h2);
4990 * Try and open file2 with the same LEASE1 key - this should fail with.
4991 * INVALID_PARAMETER.
4993 smb2_lease_create(&io, &ls, false, fname2, LEASE1,
4994 smb2_util_lease_state("RWH"));
4995 status = smb2_create(tree, mem_ctx, &io);
4996 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
4998 * If we did open this is an error, but save off
4999 * the handle so we close below.
5001 h2 = io.out.file.handle;
5003 done:
5004 smb2_util_close(tree, h2);
5005 smb2_util_close(tree, h1);
5006 smb2_util_unlink(tree, fname1);
5007 smb2_util_unlink(tree, fname2);
5008 talloc_free(mem_ctx);
5009 return ret;
5012 static bool test_lease_v1_bug_15148(struct torture_context *tctx,
5013 struct smb2_tree *tree)
5015 TALLOC_CTX *mem_ctx = talloc_new(tctx);
5016 struct smb2_create io1;
5017 struct smb2_create io2;
5018 struct smb2_lease ls1;
5019 struct smb2_lease ls2;
5020 struct smb2_handle h1 = {};
5021 struct smb2_handle h2 = {};
5022 struct smb2_write w;
5023 NTSTATUS status;
5024 const char *fname = "lease_v1_bug_15148.dat";
5025 bool ret = true;
5026 uint32_t caps;
5028 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
5029 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
5031 tree->session->transport->lease.handler = torture_lease_handler;
5032 tree->session->transport->lease.private_data = tree;
5033 tree->session->transport->oplock.handler = torture_oplock_handler;
5034 tree->session->transport->oplock.private_data = tree;
5036 smb2_util_unlink(tree, fname);
5038 torture_reset_lease_break_info(tctx, &lease_break_info);
5040 /* Grab R lease over connection 1a */
5041 smb2_lease_create(&io1, &ls1, false, fname, LEASE1, smb2_util_lease_state("R"));
5042 status = smb2_create(tree, mem_ctx, &io1);
5043 CHECK_STATUS(status, NT_STATUS_OK);
5044 h1 = io1.out.file.handle;
5045 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
5046 CHECK_LEASE(&io1, "R", true, LEASE1, 0);
5048 CHECK_NO_BREAK(tctx);
5050 /* Contend with LEASE2. */
5051 smb2_lease_create(&io2, &ls2, false, fname, LEASE2, smb2_util_lease_state("R"));
5052 status = smb2_create(tree, mem_ctx, &io2);
5053 CHECK_STATUS(status, NT_STATUS_OK);
5054 h2 = io2.out.file.handle;
5055 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
5056 CHECK_LEASE(&io2, "R", true, LEASE2, 0);
5058 CHECK_NO_BREAK(tctx);
5060 ZERO_STRUCT(w);
5061 w.in.file.handle = h1;
5062 w.in.offset = 0;
5063 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5064 memset(w.in.data.data, 'o', w.in.data.length);
5065 status = smb2_write(tree, &w);
5066 CHECK_STATUS(status, NT_STATUS_OK);
5068 ls2.lease_epoch += 1;
5069 CHECK_BREAK_INFO("R", "", LEASE2);
5071 torture_reset_lease_break_info(tctx, &lease_break_info);
5073 ZERO_STRUCT(w);
5074 w.in.file.handle = h1;
5075 w.in.offset = 0;
5076 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5077 memset(w.in.data.data, 'O', w.in.data.length);
5078 status = smb2_write(tree, &w);
5079 CHECK_STATUS(status, NT_STATUS_OK);
5081 CHECK_NO_BREAK(tctx);
5083 ZERO_STRUCT(w);
5084 w.in.file.handle = h2;
5085 w.in.offset = 0;
5086 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5087 memset(w.in.data.data, 'o', w.in.data.length);
5088 status = smb2_write(tree, &w);
5089 CHECK_STATUS(status, NT_STATUS_OK);
5091 ls1.lease_epoch += 1;
5092 CHECK_BREAK_INFO("R", "", LEASE1);
5094 done:
5095 smb2_util_close(tree, h1);
5096 smb2_util_close(tree, h2);
5098 smb2_util_unlink(tree, fname);
5100 talloc_free(mem_ctx);
5102 return ret;
5105 static bool test_lease_v2_bug_15148(struct torture_context *tctx,
5106 struct smb2_tree *tree)
5108 TALLOC_CTX *mem_ctx = talloc_new(tctx);
5109 struct smb2_create io1;
5110 struct smb2_create io2;
5111 struct smb2_lease ls1;
5112 struct smb2_lease ls2;
5113 struct smb2_handle h1 = {};
5114 struct smb2_handle h2 = {};
5115 struct smb2_write w;
5116 NTSTATUS status;
5117 const char *fname = "lease_v2_bug_15148.dat";
5118 bool ret = true;
5119 uint32_t caps;
5120 enum protocol_types protocol;
5122 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
5123 torture_assert_goto(tctx, caps & SMB2_CAP_LEASING, ret, done, "leases are not supported");
5125 protocol = smbXcli_conn_protocol(tree->session->transport->conn);
5126 if (protocol < PROTOCOL_SMB3_00) {
5127 torture_skip(tctx, "v2 leases are not supported");
5130 tree->session->transport->lease.handler = torture_lease_handler;
5131 tree->session->transport->lease.private_data = tree;
5132 tree->session->transport->oplock.handler = torture_oplock_handler;
5133 tree->session->transport->oplock.private_data = tree;
5135 smb2_util_unlink(tree, fname);
5137 torture_reset_lease_break_info(tctx, &lease_break_info);
5139 /* Grab R lease over connection 1a */
5140 smb2_lease_v2_create(&io1, &ls1, false, fname, LEASE1, NULL,
5141 smb2_util_lease_state("R"), 0x4711);
5142 status = smb2_create(tree, mem_ctx, &io1);
5143 CHECK_STATUS(status, NT_STATUS_OK);
5144 h1 = io1.out.file.handle;
5145 CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE);
5146 ls1.lease_epoch += 1;
5147 CHECK_LEASE_V2(&io1, "R", true, LEASE1,
5148 0, 0, ls1.lease_epoch);
5150 CHECK_NO_BREAK(tctx);
5152 /* Contend with LEASE2. */
5153 smb2_lease_v2_create(&io2, &ls2, false, fname, LEASE2, NULL,
5154 smb2_util_lease_state("R"), 0x11);
5155 status = smb2_create(tree, mem_ctx, &io2);
5156 CHECK_STATUS(status, NT_STATUS_OK);
5157 h2 = io2.out.file.handle;
5158 CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
5159 ls2.lease_epoch += 1;
5160 CHECK_LEASE_V2(&io2, "R", true, LEASE2,
5161 0, 0, ls2.lease_epoch);
5163 CHECK_NO_BREAK(tctx);
5165 ZERO_STRUCT(w);
5166 w.in.file.handle = h1;
5167 w.in.offset = 0;
5168 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5169 memset(w.in.data.data, 'o', w.in.data.length);
5170 status = smb2_write(tree, &w);
5171 CHECK_STATUS(status, NT_STATUS_OK);
5173 ls2.lease_epoch += 1;
5174 CHECK_BREAK_INFO_V2(tree->session->transport,
5175 "R", "", LEASE2, ls2.lease_epoch);
5177 torture_reset_lease_break_info(tctx, &lease_break_info);
5179 ZERO_STRUCT(w);
5180 w.in.file.handle = h1;
5181 w.in.offset = 0;
5182 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5183 memset(w.in.data.data, 'O', w.in.data.length);
5184 status = smb2_write(tree, &w);
5185 CHECK_STATUS(status, NT_STATUS_OK);
5187 CHECK_NO_BREAK(tctx);
5189 ZERO_STRUCT(w);
5190 w.in.file.handle = h2;
5191 w.in.offset = 0;
5192 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
5193 memset(w.in.data.data, 'o', w.in.data.length);
5194 status = smb2_write(tree, &w);
5195 CHECK_STATUS(status, NT_STATUS_OK);
5197 ls1.lease_epoch += 1;
5198 CHECK_BREAK_INFO_V2(tree->session->transport,
5199 "R", "", LEASE1, ls1.lease_epoch);
5201 done:
5202 smb2_util_close(tree, h1);
5203 smb2_util_close(tree, h2);
5205 smb2_util_unlink(tree, fname);
5207 talloc_free(mem_ctx);
5209 return ret;
5212 static bool test_initial_delete_tdis(struct torture_context *tctx,
5213 struct smb2_tree *tree1)
5215 struct smb2_tree *tree2 = NULL;
5216 struct smb2_create c = {};
5217 struct smb2_handle h1 = {};
5218 struct smb2_lease lease1 = {};
5219 const char *fname = "test_initial_delete_tdis.dat";
5220 NTSTATUS status;
5221 bool ret = true;
5223 tree1->session->transport->lease.handler = torture_lease_handler;
5224 tree1->session->transport->lease.private_data = tree1;
5225 torture_reset_lease_break_info(tctx, &lease_break_info);
5227 ret = torture_smb2_connection(tctx, &tree2);
5228 torture_assert_goto(tctx, ret, ret, done, "torture_smb2_connection failed\n");
5230 smb2_util_unlink(tree1, fname);
5232 smb2_lease_v2_create_share(&c, &lease1, false, fname,
5233 smb2_util_share_access("RWD"),
5234 LEASE1,
5235 NULL,
5236 smb2_util_lease_state("RH"),
5238 status = smb2_create(tree1, tree1, &c);
5239 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5240 "smb2_create failed\n");
5241 h1 = c.out.file.handle;
5243 c = (struct smb2_create) {
5244 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5245 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5246 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5247 .in.create_disposition = NTCREATEX_DISP_OPEN,
5248 .in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
5249 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5250 .in.fname = fname,
5253 status = smb2_create(tree2, tree2, &c);
5254 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5255 "smb2_create failed\n");
5257 status = smb2_tdis(tree2);
5258 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5259 "smb2_create failed\n");
5261 CHECK_BREAK_INFO_V2(tree1->session->transport,
5262 "RH", "R",
5263 LEASE1,
5266 status = smb2_util_close(tree1, h1);
5267 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5268 "smb2_util_close failed\n");
5269 ZERO_STRUCT(h1);
5271 c = (struct smb2_create) {
5272 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5273 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5274 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5275 .in.create_disposition = NTCREATEX_DISP_OPEN,
5276 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5277 .in.fname = fname,
5280 status = smb2_create(tree1, tree1, &c);
5281 torture_assert_ntstatus_equal_goto(tctx, status,
5282 NT_STATUS_OBJECT_NAME_NOT_FOUND,
5283 ret, done,
5284 "file still there?\n");
5286 done:
5287 smb2_util_unlink(tree1, fname);
5288 return ret;
5291 static bool test_initial_delete_logoff(struct torture_context *tctx,
5292 struct smb2_tree *tree1)
5294 struct smb2_tree *tree2 = NULL;
5295 struct smb2_create c = {};
5296 struct smb2_handle h1 = {};
5297 struct smb2_lease lease1 = {};
5298 const char *fname = "test_initial_delete_logoff.dat";
5299 NTSTATUS status;
5300 bool ret = true;
5302 tree1->session->transport->lease.handler = torture_lease_handler;
5303 tree1->session->transport->lease.private_data = tree1;
5304 torture_reset_lease_break_info(tctx, &lease_break_info);
5306 ret = torture_smb2_connection(tctx, &tree2);
5307 torture_assert_goto(tctx, ret, ret, done, "torture_smb2_connection failed\n");
5309 smb2_util_unlink(tree2, fname);
5311 smb2_lease_v2_create_share(&c, &lease1, false, fname,
5312 smb2_util_share_access("RWD"),
5313 LEASE1,
5314 NULL,
5315 smb2_util_lease_state("RH"),
5317 status = smb2_create(tree1, tree1, &c);
5318 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5319 "smb2_create failed\n");
5320 h1 = c.out.file.handle;
5322 c = (struct smb2_create) {
5323 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5324 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5325 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5326 .in.create_disposition = NTCREATEX_DISP_OPEN,
5327 .in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
5328 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5329 .in.fname = fname,
5332 status = smb2_create(tree2, tree2, &c);
5333 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5334 "smb2_create failed\n");
5336 status = smb2_logoff(tree2->session);
5337 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5338 "smb2_create failed\n");
5340 CHECK_BREAK_INFO_V2(tree1->session->transport,
5341 "RH", "R",
5342 LEASE1,
5345 status = smb2_util_close(tree1, h1);
5346 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5347 "smb2_util_close failed\n");
5348 ZERO_STRUCT(h1);
5350 c = (struct smb2_create) {
5351 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5352 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5353 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5354 .in.create_disposition = NTCREATEX_DISP_OPEN,
5355 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5356 .in.fname = fname,
5359 status = smb2_create(tree1, tree1, &c);
5360 torture_assert_ntstatus_equal_goto(tctx, status,
5361 NT_STATUS_OBJECT_NAME_NOT_FOUND,
5362 ret, done,
5363 "file still there?\n");
5365 done:
5366 return ret;
5369 static bool test_initial_delete_disconnect(struct torture_context *tctx,
5370 struct smb2_tree *tree1)
5372 struct smb2_tree *tree2 = NULL;
5373 struct smb2_create c = {};
5374 struct smb2_handle h1 = {};
5375 struct smb2_lease lease1 = {};
5376 const char *fname = "test_initial_delete_disconnect.dat";
5377 NTSTATUS status;
5378 bool ret = true;
5380 tree1->session->transport->lease.handler = torture_lease_handler;
5381 tree1->session->transport->lease.private_data = tree1;
5382 torture_reset_lease_break_info(tctx, &lease_break_info);
5384 ret = torture_smb2_connection(tctx, &tree2);
5385 torture_assert_goto(tctx, ret, ret, done, "torture_smb2_connection failed\n");
5387 smb2_util_unlink(tree2, fname);
5389 smb2_lease_v2_create_share(&c, &lease1, false, fname,
5390 smb2_util_share_access("RWD"),
5391 LEASE1,
5392 NULL,
5393 smb2_util_lease_state("RH"),
5395 status = smb2_create(tree1, tree1, &c);
5396 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5397 "smb2_create failed\n");
5398 h1 = c.out.file.handle;
5400 c = (struct smb2_create) {
5401 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5402 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5403 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5404 .in.create_disposition = NTCREATEX_DISP_OPEN,
5405 .in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
5406 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5407 .in.fname = fname,
5410 status = smb2_create(tree2, tree2, &c);
5411 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5412 "smb2_create failed\n");
5414 TALLOC_FREE(tree2);
5416 CHECK_BREAK_INFO_V2(tree1->session->transport,
5417 "RH", "R",
5418 LEASE1,
5421 status = smb2_util_close(tree1, h1);
5422 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5423 "smb2_util_close failed\n");
5424 ZERO_STRUCT(h1);
5426 c = (struct smb2_create) {
5427 .in.desired_access = SEC_RIGHTS_FILE_ALL ,
5428 .in.file_attributes = FILE_ATTRIBUTE_NORMAL,
5429 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
5430 .in.create_disposition = NTCREATEX_DISP_OPEN,
5431 .in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS,
5432 .in.fname = fname,
5435 status = smb2_create(tree1, tree1, &c);
5436 torture_assert_ntstatus_equal_goto(tctx, status,
5437 NT_STATUS_OBJECT_NAME_NOT_FOUND,
5438 ret, done,
5439 "file still there?\n");
5441 done:
5442 return ret;
5445 struct rename_tcase_open {
5446 bool hlease;
5447 bool close_on_break;
5450 struct rename_tcase {
5451 const char *name;
5452 bool disabled;
5453 struct rename_tcase_open o1;
5454 struct rename_tcase_open o2;
5455 bool do_o3;
5456 struct rename_tcase_open o3;
5457 NTSTATUS status;
5460 static bool torture_rename_dir_openfile_do(struct torture_context *tctx,
5461 struct smb2_tree *tree1,
5462 struct smb2_tree *tree2,
5463 struct rename_tcase *t)
5465 struct smb2_create c = {};
5466 union smb_setfileinfo sinfo = {};
5467 struct smb2_handle d1 = {};
5468 struct smb2_handle h1 = {};
5469 struct smb2_handle h2 = {};
5470 struct smb2_handle h3 = {};
5471 struct smb2_handle *h = NULL;
5472 struct smb2_lease *please1 = NULL;
5473 struct smb2_lease *please2 = NULL;
5474 struct smb2_lease *please3 = NULL;
5475 struct smb2_lease lease1 = {};
5476 struct smb2_lease lease2 = {};
5477 struct smb2_lease lease3 = {};
5478 struct smb2_request *req = NULL;
5479 struct smb2_lease_break_ack ack = {};
5480 struct rename_tcase_open *to = NULL;
5481 const char *dname = "torture_rename_dir_openfile_dir";
5482 const char *fname1 = "torture_rename_dir_openfile_dir\\torture_rename_dir_openfile_file1";
5483 const char *fname2 = "torture_rename_dir_openfile_dir\\torture_rename_dir_openfile_file2";
5484 const char *fname3 = "torture_rename_dir_openfile_dir\\torture_rename_dir_openfile_file3";
5485 const char *new_dname = "torture_rename_dir_openfile_dir-renamed";
5486 bool expect_immediate_fail = false;
5487 bool ret = true;
5488 NTSTATUS status;
5490 torture_comment(tctx, "Subtest: %s\n", t->name);
5491 if (t->disabled) {
5492 torture_comment(tctx, "...skipped\n");
5493 return true;
5496 tree2->session->transport->lease.handler = torture_lease_handler;
5497 tree2->session->transport->lease.private_data = tree2;
5498 torture_reset_lease_break_info(tctx, &lease_break_info);
5499 lease_break_info.lease_skip_ack = true;
5501 smb2_deltree(tree1, dname);
5502 smb2_deltree(tree1, new_dname);
5504 torture_comment(tctx, "Creating base directory\n");
5506 smb2_lease_v2_create_share(&c, NULL, true, dname,
5507 smb2_util_share_access("RWD"),
5509 NULL,
5510 smb2_util_lease_state(""),
5512 status = smb2_create(tree1, tree1, &c);
5513 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5514 "smb2_create failed\n");
5515 d1 = c.out.file.handle;
5517 torture_comment(tctx, "Creating test file1\n");
5519 if (t->o1.hlease) {
5520 please1 = &lease1;
5522 smb2_lease_v2_create_share(&c, please1, false, fname1,
5523 smb2_util_share_access("RWD"),
5524 LEASE1,
5525 NULL,
5526 smb2_util_lease_state("RH"),
5528 status = smb2_create(tree2, tree2, &c);
5529 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5530 "smb2_create failed\n");
5531 h1 = c.out.file.handle;
5533 torture_comment(tctx, "Creating test file2\n");
5535 if (t->o2.hlease) {
5536 please2 = &lease2;
5538 smb2_lease_v2_create_share(&c, please2, false, fname2,
5539 smb2_util_share_access("RWD"),
5540 LEASE2,
5541 NULL,
5542 smb2_util_lease_state("RH"),
5544 status = smb2_create(tree2, tree2, &c);
5545 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5546 "smb2_create failed\n");
5547 h2 = c.out.file.handle;
5549 torture_comment(tctx, "Renaming directory\n");
5551 sinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION;
5552 sinfo.rename_information.in.file.handle = d1;
5553 sinfo.rename_information.in.new_name = new_dname;
5555 req = smb2_setinfo_file_send(tree1, &sinfo);
5556 torture_assert_goto(tctx, req != NULL, ret, done,
5557 "smb2_setinfo_file_send");
5559 torture_assert(tctx, req->state == SMB2_REQUEST_RECV, "bad req state");
5561 if (t->o1.hlease || t->o2.hlease) {
5562 /* Get the first break */
5563 torture_wait_for_lease_break(tctx);
5565 if (lease_break_info.count == 0) {
5567 * If one of the two opens was without a h-lease, the
5568 * scan for opens might hit the open without h-lease
5569 * first triggering an immediate STATUS_ACCESS_DENIED
5570 * for the rename without sending out any lease break.
5572 torture_assert_goto(tctx, (!t->o1.hlease || !t->o2.hlease),
5573 ret, done,
5574 "Expected only one hlease when getting no hlease break\n");
5576 status = smb2_setinfo_recv(req);
5577 torture_assert_ntstatus_equal_goto(tctx, status, t->status, ret, done,
5578 "Rename didn't work as expected\n");
5579 goto done;
5582 if (lease_break_info.lease_break.current_lease.lease_key.data[0] == LEASE1 &&
5583 lease_break_info.lease_break.current_lease.lease_key.data[1] == ~LEASE1)
5585 torture_comment(tctx, "Got break for file 1\n");
5586 please1 = &lease1;
5587 h = &h1;
5588 to = &t->o1;
5589 } else {
5590 torture_comment(tctx, "Got break for file 2\n");
5591 please1 = &lease2;
5592 h = &h2;
5593 to = &t->o2;
5595 please1->lease_epoch += 2;
5597 CHECK_BREAK_INFO_V2_NOWAIT(tree2->session->transport,
5598 "RH", "R",
5599 please1->lease_key.data[0],
5600 please1->lease_epoch);
5602 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
5603 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
5604 torture_reset_lease_break_info(tctx, &lease_break_info);
5605 lease_break_info.lease_skip_ack = true;
5607 if (to->close_on_break) {
5608 status = smb2_util_close(tree2, *h);
5609 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5610 "smb2_util_close failed\n");
5611 ZERO_STRUCTP(h);
5612 } else {
5613 status = smb2_lease_break_ack(tree2, &ack);
5614 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5615 "ack failed\n");
5616 expect_immediate_fail = true;
5620 if (t->do_o3) {
5621 torture_comment(tctx, "Doing additional open after first break\n");
5623 if (t->o3.hlease) {
5624 please3 = &lease3;
5626 smb2_lease_v2_create_share(&c, please3, false, fname3,
5627 smb2_util_share_access("RWD"),
5628 LEASE3,
5629 NULL,
5630 smb2_util_lease_state("RH"),
5632 status = smb2_create(tree2, tree2, &c);
5633 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5634 "smb2_create failed\n");
5635 h3 = c.out.file.handle;
5638 if (!expect_immediate_fail && t->o1.hlease && t->o2.hlease) {
5639 /* Get the second break */
5640 torture_wait_for_lease_break(tctx);
5642 if (lease_break_info.lease_break.current_lease.lease_key.data[0] == LEASE1 &&
5643 lease_break_info.lease_break.current_lease.lease_key.data[1] == ~LEASE1)
5645 torture_comment(tctx, "Got break for file 1\n");
5646 please1 = &lease1;
5647 h = &h1;
5648 to = &t->o1;
5649 } else {
5650 torture_comment(tctx, "Got break for file 2\n");
5651 please1 = &lease2;
5652 h = &h2;
5653 to = &t->o2;
5655 please1->lease_epoch += 2;
5657 CHECK_BREAK_INFO_V2_NOWAIT(tree2->session->transport,
5658 "RH", "R",
5659 please1->lease_key.data[0],
5660 please1->lease_epoch);
5662 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
5663 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
5664 torture_reset_lease_break_info(tctx, &lease_break_info);
5665 lease_break_info.lease_skip_ack = true;
5667 if (to->close_on_break) {
5668 status = smb2_util_close(tree2, *h);
5669 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5670 "smb2_util_close failed\n");
5671 ZERO_STRUCTP(h);
5672 } else {
5673 status = smb2_lease_break_ack(tree2, &ack);
5674 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5675 "ack failed\n");
5679 status = smb2_setinfo_recv(req);
5680 torture_assert_ntstatus_equal_goto(tctx, status, t->status, ret, done,
5681 "Rename didn't work as expected\n");
5683 done:
5684 if (!smb2_util_handle_empty(d1)) {
5685 smb2_util_close(tree1, d1);
5687 if (!smb2_util_handle_empty(h1)) {
5688 smb2_util_close(tree2, h1);
5690 if (!smb2_util_handle_empty(h2)) {
5691 smb2_util_close(tree2, h2);
5693 if (!smb2_util_handle_empty(h3)) {
5694 smb2_util_close(tree2, h3);
5696 smb2_deltree(tree1, dname);
5697 smb2_deltree(tree1, new_dname);
5698 return ret;
5701 static bool torture_rename_dir_openfile(struct torture_context *tctx,
5702 struct smb2_tree *tree1,
5703 struct smb2_tree *tree2)
5705 struct rename_tcase tcases[] = {
5707 .name = "two-hleases-two-closes",
5708 .o1 = { .hlease = true, .close_on_break = true },
5709 .o2 = { .hlease = true, .close_on_break = true },
5710 .do_o3 = false,
5711 .status = NT_STATUS_OK,
5714 .name = "no-hleases",
5715 .o1 = { .hlease = false, },
5716 .o2 = { .hlease = false, },
5717 .do_o3 = false,
5718 .status = NT_STATUS_ACCESS_DENIED,
5721 .name = "two-hleases-second-hlease-close",
5722 .o1 = { .hlease = true, .close_on_break = false },
5723 .o2 = { .hlease = true, .close_on_break = true },
5724 .do_o3 = false,
5725 .status = NT_STATUS_ACCESS_DENIED,
5728 .name = "two-hleases-first-hlease-close",
5729 .o1 = { .hlease = true, .close_on_break = true },
5730 .o2 = { .hlease = true, .close_on_break = false },
5731 .do_o3 = false,
5732 .status = NT_STATUS_ACCESS_DENIED,
5735 .name = "first-hlease-close",
5736 .o1 = { .hlease = true, .close_on_break = true },
5737 .o2 = { .hlease = false, },
5738 .do_o3 = false,
5739 .status = NT_STATUS_ACCESS_DENIED,
5742 .name = "second-hlease-close",
5743 .o1 = { .hlease = false, },
5744 .o2 = { .hlease = true, .close_on_break = true },
5745 .do_o3 = false,
5746 .status = NT_STATUS_ACCESS_DENIED,
5749 .name = "two-hleases-two-closes-addopen-w-hlease",
5750 .o1 = { .hlease = true, .close_on_break = true },
5751 .o2 = { .hlease = true, .close_on_break = true },
5752 .do_o3 = true,
5753 .o3 = { .hlease = true, .close_on_break = true },
5754 .status = NT_STATUS_ACCESS_DENIED,
5757 .name = "two-hleases-two-closes-addopen-wo-hlease",
5758 .o1 = { .hlease = true, .close_on_break = true },
5759 .o2 = { .hlease = true, .close_on_break = true },
5760 .do_o3 = true,
5761 .o3 = { .hlease = false, },
5762 .status = NT_STATUS_ACCESS_DENIED,
5765 size_t i;
5766 bool ret;
5768 tree1->session->transport->lease.handler = torture_lease_handler;
5769 tree1->session->transport->lease.private_data = tree2;
5770 torture_reset_lease_break_info(tctx, &lease_break_info);
5772 for (i = 0; i < ARRAY_SIZE(tcases); i++) {
5773 ret = torture_rename_dir_openfile_do(tctx, tree1, tree2, &tcases[i]);
5774 torture_assert_goto(tctx, ret, ret, done, "test failed\n");
5777 done:
5778 return ret;
5781 struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx)
5783 struct torture_suite *suite =
5784 torture_suite_create(ctx, "lease");
5786 torture_suite_add_1smb2_test(suite, "request", test_lease_request);
5787 torture_suite_add_1smb2_test(suite, "break_twice",
5788 test_lease_break_twice);
5789 torture_suite_add_1smb2_test(suite, "nobreakself",
5790 test_lease_nobreakself);
5791 torture_suite_add_1smb2_test(suite, "statopen", test_lease_statopen);
5792 torture_suite_add_1smb2_test(suite, "statopen2", test_lease_statopen2);
5793 torture_suite_add_1smb2_test(suite, "statopen3", test_lease_statopen3);
5794 torture_suite_add_1smb2_test(suite, "statopen4", test_lease_statopen4);
5795 torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
5796 torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
5797 torture_suite_add_1smb2_test(suite, "upgrade3", test_lease_upgrade3);
5798 torture_suite_add_1smb2_test(suite, "break", test_lease_break);
5799 torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
5800 torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
5801 torture_suite_add_1smb2_test(suite, "breaking1", test_lease_breaking1);
5802 torture_suite_add_1smb2_test(suite, "breaking2", test_lease_breaking2);
5803 torture_suite_add_1smb2_test(suite, "breaking3", test_lease_breaking3);
5804 torture_suite_add_1smb2_test(suite, "v2_breaking3", test_lease_v2_breaking3);
5805 torture_suite_add_1smb2_test(suite, "breaking4", test_lease_breaking4);
5806 torture_suite_add_1smb2_test(suite, "breaking5", test_lease_breaking5);
5807 torture_suite_add_1smb2_test(suite, "breaking6", test_lease_breaking6);
5808 torture_suite_add_2smb2_test(suite, "lock1", test_lease_lock1);
5809 torture_suite_add_1smb2_test(suite, "complex1", test_lease_complex1);
5810 torture_suite_add_1smb2_test(suite, "v2_flags_breaking", test_lease_v2_flags_breaking);
5811 torture_suite_add_1smb2_test(suite, "v2_flags_parentkey", test_lease_v2_flags_parentkey);
5812 torture_suite_add_1smb2_test(suite, "v2_epoch1", test_lease_v2_epoch1);
5813 torture_suite_add_1smb2_test(suite, "v2_epoch2", test_lease_v2_epoch2);
5814 torture_suite_add_1smb2_test(suite, "v2_epoch3", test_lease_v2_epoch3);
5815 torture_suite_add_1smb2_test(suite, "v2_complex1", test_lease_v2_complex1);
5816 torture_suite_add_1smb2_test(suite, "v2_complex2", test_lease_v2_complex2);
5817 torture_suite_add_1smb2_test(suite, "v2_rename", test_lease_v2_rename);
5818 torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share);
5819 torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout);
5820 torture_suite_add_1smb2_test(suite, "unlink", test_lease_unlink);
5821 torture_suite_add_1smb2_test(suite, "timeout-disconnect", test_lease_timeout_disconnect);
5822 torture_suite_add_1smb2_test(suite, "rename_wait",
5823 test_lease_rename_wait);
5824 torture_suite_add_1smb2_test(suite, "duplicate_create",
5825 test_lease_duplicate_create);
5826 torture_suite_add_1smb2_test(suite, "duplicate_open",
5827 test_lease_duplicate_open);
5828 torture_suite_add_1smb2_test(suite, "v1_bug15148",
5829 test_lease_v1_bug_15148);
5830 torture_suite_add_1smb2_test(suite, "v2_bug15148",
5831 test_lease_v2_bug_15148);
5832 torture_suite_add_1smb2_test(suite, "v2_rename_target_overwrite",
5833 test_lease_v2_rename_target_overwrite);
5834 torture_suite_add_1smb2_test(suite, "initial_delete_tdis",
5835 test_initial_delete_tdis);
5836 torture_suite_add_1smb2_test(suite, "initial_delete_logoff",
5837 test_initial_delete_logoff);
5838 torture_suite_add_1smb2_test(suite, "initial_delete_disconnect",
5839 test_initial_delete_disconnect);
5840 torture_suite_add_2smb2_test(suite, "rename_dir_openfile",
5841 torture_rename_dir_openfile);
5843 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
5845 return suite;
5848 enum dirlease_test {
5849 DLT_SETEOF,
5850 DLT_SETDOS,
5851 DLT_BTIME,
5852 DLT_MTIME,
5853 DLT_CTIME,
5854 DLT_ATIME,
5857 static void prepare_setinfo(enum dirlease_test t,
5858 union smb_setfileinfo *s,
5859 struct smb2_handle *h)
5861 s->generic.in.file.handle = *h;
5863 switch (t) {
5864 case DLT_SETEOF:
5865 s->end_of_file_info.in.size++;
5866 break;
5867 case DLT_SETDOS:
5868 s->basic_info.in.attrib ^= FILE_ATTRIBUTE_HIDDEN;
5869 if (s->basic_info.in.attrib == 0) {
5870 s->basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL;
5872 break;
5873 case DLT_BTIME:
5874 s->basic_info.in.create_time++;
5875 break;
5876 case DLT_MTIME:
5877 s->basic_info.in.write_time++;
5878 break;
5879 case DLT_CTIME:
5880 s->basic_info.in.change_time++;
5881 break;
5882 case DLT_ATIME:
5883 s->basic_info.in.access_time++;
5884 break;
5885 default:
5886 break;
5890 static bool test_dirlease_setinfo(struct torture_context *tctx,
5891 TALLOC_CTX *mem_ctx,
5892 enum dirlease_test t,
5893 struct smb2_tree *tree,
5894 struct smb2_tree *tree2,
5895 const char *dname,
5896 const char *dnamefname,
5897 struct smb2_handle *dirh,
5898 struct smb2_lease *dirlease,
5899 union smb_setfileinfo *s)
5901 struct smb2_create c;
5902 struct smb2_lease ls1;
5903 struct smb2_handle h1 = {};
5904 NTSTATUS status;
5905 bool ret = true;
5907 /* 1. Same client */
5909 /* 1.1. Handle with correct parent lease key -> no break */
5910 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
5911 smb2_util_share_access("RWD"),
5912 LEASE2, &LEASE1,
5913 smb2_util_lease_state("RHW"), 0);
5914 status = smb2_create(tree, mem_ctx, &c);
5915 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5916 "smb2_create failed\n");
5917 h1 = c.out.file.handle;
5919 prepare_setinfo(t, s, &h1);
5920 status = smb2_setinfo_file(tree, s);
5921 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5922 "smb2_setinfo_filefailed");
5923 CHECK_NO_BREAK(tctx);
5925 smb2_util_close(tree, h1);
5926 ZERO_STRUCT(h1);
5928 /* 1.2. Handle with bad parent lease key -> break */
5929 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
5930 smb2_util_share_access("RWD"),
5931 LEASE2, &LEASE3,
5932 smb2_util_lease_state("RHW"), 0);
5933 status = smb2_create(tree, mem_ctx, &c);
5934 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5935 "smb2_create failed\n");
5936 h1 = c.out.file.handle;
5938 prepare_setinfo(t, s, &h1);
5939 status = smb2_setinfo_file(tree, s);
5940 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5941 "smb2_setinfo_filefailed");
5942 CHECK_BREAK_INFO_V2(tree->session->transport,
5943 "RH", "", LEASE1, ++(dirlease->lease_epoch));
5944 torture_reset_lease_break_info(tctx, &lease_break_info);
5945 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname,
5946 LEASE1, &dirlease->lease_epoch);
5947 torture_assert_goto(tctx, ret == true, ret, done,
5948 "Rearm dirlease failed\n");
5950 smb2_util_close(tree, h1);
5951 ZERO_STRUCT(h1);
5953 /* 1.3. Handle with no parent lease key -> break */
5954 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
5955 smb2_util_share_access("RWD"),
5956 LEASE2, NULL,
5957 smb2_util_lease_state("RHW"), 0);
5958 status = smb2_create(tree, mem_ctx, &c);
5959 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5960 "smb2_create failed\n");
5961 h1 = c.out.file.handle;
5963 prepare_setinfo(t, s, &h1);
5964 status = smb2_setinfo_file(tree, s);
5965 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5966 "smb2_setinfo_filefailed");
5967 CHECK_BREAK_INFO_V2(tree->session->transport,
5968 "RH", "", LEASE1, ++(dirlease->lease_epoch));
5969 torture_reset_lease_break_info(tctx, &lease_break_info);
5970 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname,
5971 LEASE1, &dirlease->lease_epoch);
5972 torture_assert_goto(tctx, ret == true, ret, done,
5973 "Rearm dirlease failed\n");
5975 smb2_util_close(tree, h1);
5976 ZERO_STRUCT(h1);
5978 /* 2. Second client */
5980 /* 2.1. Handle with correct parent lease key -> no break */
5981 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
5982 smb2_util_share_access("RWD"),
5983 LEASE2, &LEASE1,
5984 smb2_util_lease_state("RHW"), 0);
5985 status = smb2_create(tree2, mem_ctx, &c);
5986 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5987 "smb2_create failed\n");
5988 h1 = c.out.file.handle;
5990 prepare_setinfo(t, s, &h1);
5991 status = smb2_setinfo_file(tree2, s);
5992 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
5993 "smb2_setinfo_filefailed");
5994 CHECK_NO_BREAK(tctx);
5996 smb2_util_close(tree2, h1);
5997 ZERO_STRUCT(h1);
5999 /* 2.2. Handle with bad parent lease key -> break */
6000 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
6001 smb2_util_share_access("RWD"),
6002 LEASE2, &LEASE3,
6003 smb2_util_lease_state("RHW"), 0);
6004 status = smb2_create(tree2, mem_ctx, &c);
6005 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6006 "smb2_create failed\n");
6007 h1 = c.out.file.handle;
6009 prepare_setinfo(t, s, &h1);
6010 status = smb2_setinfo_file(tree2, s);
6011 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6012 "smb2_setinfo_filefailed");
6013 CHECK_BREAK_INFO_V2(tree->session->transport,
6014 "RH", "", LEASE1, ++(dirlease->lease_epoch));
6015 torture_reset_lease_break_info(tctx, &lease_break_info);
6016 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname,
6017 LEASE1, &dirlease->lease_epoch);
6018 torture_assert_goto(tctx, ret == true, ret, done,
6019 "Rearm dirlease failed\n");
6021 smb2_util_close(tree2, h1);
6022 ZERO_STRUCT(h1);
6024 /* 2.3. Handle with no parent lease key -> break */
6025 smb2_lease_v2_create_share(&c, &ls1, false, dnamefname,
6026 smb2_util_share_access("RWD"),
6027 LEASE2, NULL,
6028 smb2_util_lease_state("RHW"), 0);
6029 status = smb2_create(tree2, mem_ctx, &c);
6030 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6031 "smb2_create failed\n");
6032 h1 = c.out.file.handle;
6034 prepare_setinfo(t, s, &h1);
6035 status = smb2_setinfo_file(tree2, s);
6036 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6037 "smb2_setinfo_filefailed");
6038 CHECK_BREAK_INFO_V2(tree->session->transport,
6039 "RH", "", LEASE1, ++(dirlease->lease_epoch));
6040 torture_reset_lease_break_info(tctx, &lease_break_info);
6041 ret = test_rearm_dirlease(mem_ctx, tctx, tree, dname,
6042 LEASE1, &dirlease->lease_epoch);
6043 torture_assert_goto(tctx, ret == true, ret, done,
6044 "Rearm dirlease failed\n");
6046 smb2_util_close(tree2, h1);
6047 ZERO_STRUCT(h1);
6049 done:
6050 if (!smb2_util_handle_empty(h1)) {
6051 smb2_util_close(tree2, h1);
6053 return ret;
6056 static bool test_dirlease_seteof(struct torture_context *tctx,
6057 struct smb2_tree *tree,
6058 struct smb2_tree *tree2)
6060 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6061 struct smb2_create c;
6062 struct smb2_lease dirlease;
6063 struct smb2_handle dirh = {};
6064 const char *dname = "test_dirlease_seteof_dir";
6065 const char *dnamefname = "test_dirlease_seteof_dir\\lease.dat";
6066 union smb_setfileinfo sfinfo = {};
6067 NTSTATUS status;
6068 bool ret = true;
6070 smb2_deltree(tree, dname);
6072 tree->session->transport->lease.handler = torture_lease_handler;
6073 tree->session->transport->lease.private_data = tree;
6074 torture_reset_lease_break_info(tctx, &lease_break_info);
6076 /* Get an RH directory lease on the test directory */
6078 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6079 smb2_util_share_access("RWD"),
6080 LEASE1, NULL,
6081 smb2_util_lease_state("RHW"), 0);
6082 status = smb2_create(tree, mem_ctx, &c);
6083 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6084 "smb2_create failed\n");
6085 dirh = c.out.file.handle;
6086 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6089 * TEST: test setting EOF
6091 * Test from same and second client, and test with correct, bad and no
6092 * parent lease key.
6095 sfinfo.generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
6097 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_SETEOF, tree, tree2,
6098 dname, dnamefname,
6099 &dirh, &dirlease, &sfinfo);
6100 torture_assert_goto(tctx, ret, ret, done, "seteof test failed\n");
6102 done:
6103 if (!smb2_util_handle_empty(dirh)) {
6104 smb2_util_close(tree, dirh);
6106 smb2_deltree(tree, dname);
6107 talloc_free(mem_ctx);
6108 return ret;
6111 static bool test_dirlease_setdos(struct torture_context *tctx,
6112 struct smb2_tree *tree,
6113 struct smb2_tree *tree2)
6115 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6116 struct smb2_create c;
6117 struct smb2_lease dirlease;
6118 struct smb2_handle dirh = {};
6119 const char *dname = "test_dirlease_setdos_dir";
6120 const char *dnamefname = "test_dirlease_setdos_dir\\lease.dat";
6121 union smb_setfileinfo sfinfo = {};
6122 NTSTATUS status;
6123 bool ret = true;
6125 smb2_deltree(tree, dname);
6127 tree->session->transport->lease.handler = torture_lease_handler;
6128 tree->session->transport->lease.private_data = tree;
6129 torture_reset_lease_break_info(tctx, &lease_break_info);
6131 /* Get an RH directory lease on the test directory */
6133 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6134 smb2_util_share_access("RWD"),
6135 LEASE1, NULL,
6136 smb2_util_lease_state("RHW"), 0);
6137 status = smb2_create(tree, mem_ctx, &c);
6138 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6139 "smb2_create failed\n");
6140 dirh = c.out.file.handle;
6141 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6144 * TEST: Test setting DOS attributes
6146 * Test from same and second client, and test with correct, bad and no
6147 * parent lease key.
6150 sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_HIDDEN;
6151 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
6153 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_SETDOS, tree, tree2,
6154 dname, dnamefname,
6155 &dirh, &dirlease, &sfinfo);
6156 torture_assert_goto(tctx, ret, ret, done, "setdos test failed\n");
6158 done:
6159 if (!smb2_util_handle_empty(dirh)) {
6160 smb2_util_close(tree, dirh);
6162 smb2_deltree(tree, dname);
6163 talloc_free(mem_ctx);
6164 return ret;
6168 * TEST: Test setting creation date
6170 static bool test_dirlease_setbtime(struct torture_context *tctx,
6171 struct smb2_tree *tree,
6172 struct smb2_tree *tree2)
6174 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6175 struct smb2_create c;
6176 struct smb2_lease dirlease;
6177 struct smb2_handle dirh = {};
6178 const char *dname = "test_dirlease_setbtime_dir";
6179 const char *dnamefname = "test_dirlease_setbtime_dir\\lease.dat";
6180 union smb_setfileinfo sfinfo = {};
6181 NTSTATUS status;
6182 bool ret = true;
6184 smb2_deltree(tree, dname);
6186 tree->session->transport->lease.handler = torture_lease_handler;
6187 tree->session->transport->lease.private_data = tree;
6188 torture_reset_lease_break_info(tctx, &lease_break_info);
6190 /* Get an RH directory lease on the test directory */
6192 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6193 smb2_util_share_access("RWD"),
6194 LEASE1, NULL,
6195 smb2_util_lease_state("RHW"), 0);
6196 status = smb2_create(tree, mem_ctx, &c);
6197 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6198 "smb2_create failed\n");
6199 dirh = c.out.file.handle;
6200 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6202 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
6203 unix_to_nt_time(&sfinfo.basic_info.in.create_time, time(NULL) + 9*30*24*60*60);
6205 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_BTIME, tree, tree2,
6206 dname, dnamefname,
6207 &dirh, &dirlease, &sfinfo);
6208 torture_assert_goto(tctx, ret, ret, done, "setbtime test failed\n");
6210 done:
6211 if (!smb2_util_handle_empty(dirh)) {
6212 smb2_util_close(tree, dirh);
6214 smb2_deltree(tree, dname);
6215 talloc_free(mem_ctx);
6216 return ret;
6220 * TEST: Test setting modification date
6222 static bool test_dirlease_setmtime(struct torture_context *tctx,
6223 struct smb2_tree *tree,
6224 struct smb2_tree *tree2)
6226 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6227 struct smb2_create c;
6228 struct smb2_lease dirlease;
6229 struct smb2_handle dirh = {};
6230 const char *dname = "test_dirlease_setmtime_dir";
6231 const char *dnamefname = "test_dirlease_setmtime_dir\\lease.dat";
6232 union smb_setfileinfo sfinfo = {};
6233 NTSTATUS status;
6234 bool ret = true;
6236 smb2_deltree(tree, dname);
6238 tree->session->transport->lease.handler = torture_lease_handler;
6239 tree->session->transport->lease.private_data = tree;
6240 torture_reset_lease_break_info(tctx, &lease_break_info);
6242 /* Get an RH directory lease on the test directory */
6244 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6245 smb2_util_share_access("RWD"),
6246 LEASE1, NULL,
6247 smb2_util_lease_state("RHW"), 0);
6248 status = smb2_create(tree, mem_ctx, &c);
6249 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6250 "smb2_create failed\n");
6251 dirh = c.out.file.handle;
6252 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6254 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
6255 unix_to_nt_time(&sfinfo.basic_info.in.create_time, time(NULL) + 9*30*24*60*60);
6257 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_MTIME, tree, tree2,
6258 dname, dnamefname,
6259 &dirh, &dirlease, &sfinfo);
6260 torture_assert_goto(tctx, ret, ret, done, "setmtime test failed\n");
6262 done:
6263 if (!smb2_util_handle_empty(dirh)) {
6264 smb2_util_close(tree, dirh);
6266 smb2_deltree(tree, dname);
6267 talloc_free(mem_ctx);
6268 return ret;
6272 * TEST: Test setting inode change date
6274 static bool test_dirlease_setctime(struct torture_context *tctx,
6275 struct smb2_tree *tree,
6276 struct smb2_tree *tree2)
6278 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6279 struct smb2_create c;
6280 struct smb2_lease dirlease;
6281 struct smb2_handle dirh = {};
6282 const char *dname = "test_dirlease_setctime_dir";
6283 const char *dnamefname = "test_dirlease_setctime_dir\\lease.dat";
6284 union smb_setfileinfo sfinfo = {};
6285 NTSTATUS status;
6286 bool ret = true;
6288 smb2_deltree(tree, dname);
6290 tree->session->transport->lease.handler = torture_lease_handler;
6291 tree->session->transport->lease.private_data = tree;
6292 torture_reset_lease_break_info(tctx, &lease_break_info);
6294 /* Get an RH directory lease on the test directory */
6296 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6297 smb2_util_share_access("RWD"),
6298 LEASE1, NULL,
6299 smb2_util_lease_state("RHW"), 0);
6300 status = smb2_create(tree, mem_ctx, &c);
6301 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6302 "smb2_create failed\n");
6303 dirh = c.out.file.handle;
6304 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6306 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
6307 unix_to_nt_time(&sfinfo.basic_info.in.change_time, time(NULL) + 9*30*24*60*60);
6309 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_CTIME, tree, tree2,
6310 dname, dnamefname,
6311 &dirh, &dirlease, &sfinfo);
6312 torture_assert_goto(tctx, ret, ret, done, "setctime test failed\n");
6314 done:
6315 if (!smb2_util_handle_empty(dirh)) {
6316 smb2_util_close(tree, dirh);
6318 smb2_deltree(tree, dname);
6319 talloc_free(mem_ctx);
6320 return ret;
6324 * TEST: Test setting last access date
6326 static bool test_dirlease_setatime(struct torture_context *tctx,
6327 struct smb2_tree *tree,
6328 struct smb2_tree *tree2)
6330 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6331 struct smb2_create c;
6332 struct smb2_lease dirlease;
6333 struct smb2_handle dirh = {};
6334 const char *dname = "test_dirlease_setatime_dir";
6335 const char *dnamefname = "test_dirlease_setatime_dir\\lease.dat";
6336 union smb_setfileinfo sfinfo = {};
6337 NTSTATUS status;
6338 bool ret = true;
6340 smb2_deltree(tree, dname);
6342 tree->session->transport->lease.handler = torture_lease_handler;
6343 tree->session->transport->lease.private_data = tree;
6344 torture_reset_lease_break_info(tctx, &lease_break_info);
6346 /* Get an RH directory lease on the test directory */
6348 smb2_lease_v2_create_share(&c, &dirlease, true, dname,
6349 smb2_util_share_access("RWD"),
6350 LEASE1, NULL,
6351 smb2_util_lease_state("RHW"), 0);
6352 status = smb2_create(tree, mem_ctx, &c);
6353 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6354 "smb2_create failed\n");
6355 dirh = c.out.file.handle;
6356 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease.lease_epoch);
6358 sfinfo.generic.level = RAW_SFILEINFO_BASIC_INFORMATION;
6359 unix_to_nt_time(&sfinfo.basic_info.in.access_time, time(NULL) + 9*30*24*60*60);
6361 ret = test_dirlease_setinfo(tctx, mem_ctx, DLT_ATIME, tree, tree2,
6362 dname, dnamefname,
6363 &dirh, &dirlease, &sfinfo);
6364 torture_assert_goto(tctx, ret, ret, done, "setctime test failed\n");
6366 done:
6367 if (!smb2_util_handle_empty(dirh)) {
6368 smb2_util_close(tree, dirh);
6370 smb2_deltree(tree, dname);
6371 talloc_free(mem_ctx);
6372 return ret;
6375 #define DLEASE1 0x0000000000000001ull
6376 #define DLEASE2 0x0000000000000002ull
6377 #define DLEASE3 0x0000000000000003ull
6379 static struct dlt_rename {
6380 const char *testname;
6381 bool expect_srcdir_break;
6382 bool expect_dstdir_break;
6383 const char *srcdir;
6384 const char *dstdir;
6385 uint64_t srcdir_leasekey;
6386 uint64_t dstdir_leasekey;
6387 uint64_t parent_leasekey;
6388 const char *srcfname;
6389 const char *dstfname;
6390 } dlt_renames[] = {
6392 .testname = "samedir-correct-parent-leaskey",
6393 .expect_srcdir_break = false,
6394 .expect_dstdir_break = false,
6395 .srcdir = "test_dirlease_rename_dir",
6396 .dstdir = "test_dirlease_rename_dir",
6397 .srcdir_leasekey = DLEASE1,
6398 .dstdir_leasekey = DLEASE2,
6399 .parent_leasekey = DLEASE1,
6400 .srcfname = "test_dirlease_rename_dir\\srcfile",
6401 .dstfname = "test_dirlease_rename_dir\\dstfile",
6402 }, {
6403 .testname = "samedir-wrong-parent-leaskey",
6404 .expect_srcdir_break = true,
6405 .expect_dstdir_break = false,
6406 .srcdir = "test_dirlease_rename_dir",
6407 .dstdir = "test_dirlease_rename_dir",
6408 .srcdir_leasekey = DLEASE1,
6409 .dstdir_leasekey = DLEASE2,
6410 .parent_leasekey = DLEASE3,
6411 .srcfname = "test_dirlease_rename_dir\\srcfile",
6412 .dstfname = "test_dirlease_rename_dir\\dstfile",
6413 }, {
6414 .testname = "samedir-no-parent-leaskey",
6415 .expect_srcdir_break = true,
6416 .expect_dstdir_break = false,
6417 .srcdir = "test_dirlease_rename_dir",
6418 .dstdir = "test_dirlease_rename_dir",
6419 .srcdir_leasekey = DLEASE1,
6420 .dstdir_leasekey = DLEASE2,
6421 .parent_leasekey = 0,
6422 .srcfname = "test_dirlease_rename_dir\\srcfile",
6423 .dstfname = "test_dirlease_rename_dir\\dstfile",
6424 }, {
6425 .testname = "otherdir-correct-srcparent-leaskey",
6426 .expect_srcdir_break = false,
6427 .expect_dstdir_break = true,
6428 .srcdir = "test_dirlease_rename_dir",
6429 .dstdir = "test_dirlease_rename_dir2",
6430 .srcdir_leasekey = DLEASE1,
6431 .dstdir_leasekey = DLEASE2,
6432 .parent_leasekey = DLEASE1,
6433 .srcfname = "test_dirlease_rename_dir\\srcfile",
6434 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6435 }, {
6436 .testname = "otherdir-correct-dstparent-leaskey",
6437 .expect_srcdir_break = true,
6438 .expect_dstdir_break = false,
6439 .srcdir = "test_dirlease_rename_dir",
6440 .dstdir = "test_dirlease_rename_dir2",
6441 .srcdir_leasekey = DLEASE1,
6442 .dstdir_leasekey = DLEASE2,
6443 .parent_leasekey = DLEASE2,
6444 .srcfname = "test_dirlease_rename_dir\\srcfile",
6445 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6446 }, {
6447 .testname = "otherdir-wrong-parent-leaskey",
6448 .expect_srcdir_break = true,
6449 .expect_dstdir_break = true,
6450 .srcdir = "test_dirlease_rename_dir",
6451 .dstdir = "test_dirlease_rename_dir2",
6452 .srcdir_leasekey = DLEASE1,
6453 .dstdir_leasekey = DLEASE2,
6454 .parent_leasekey = DLEASE3,
6455 .srcfname = "test_dirlease_rename_dir\\srcfile",
6456 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6457 }, {
6458 .testname = "otherdir-no-parent-leaskey",
6459 .expect_srcdir_break = true,
6460 .expect_dstdir_break = true,
6461 .srcdir = "test_dirlease_rename_dir",
6462 .dstdir = "test_dirlease_rename_dir2",
6463 .srcdir_leasekey = DLEASE1,
6464 .dstdir_leasekey = DLEASE2,
6465 .parent_leasekey = 0,
6466 .srcfname = "test_dirlease_rename_dir\\srcfile",
6467 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6468 }, {
6469 .testname = NULL,
6473 static bool test_rename_one(struct torture_context *tctx,
6474 struct smb2_tree *tree,
6475 struct dlt_rename *t)
6477 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6478 struct smb2_create c;
6479 struct smb2_lease dirlease1;
6480 struct smb2_lease dirlease2;
6481 struct smb2_handle dirh1 = {};
6482 struct smb2_handle dirh2 = {};
6483 struct smb2_lease lease1;
6484 struct smb2_handle h1 = {};
6485 struct smb2_lease_break_ack ack = {};
6486 bool samedir = strequal(t->srcdir, t->dstdir);
6487 union smb_setfileinfo sfinfo = {};
6488 NTSTATUS status;
6489 bool ret = true;
6491 torture_comment(tctx, "\nRename subtest: %s\n"
6492 "==================================\n",
6493 t->testname);
6495 smb2_deltree(tree, t->srcdir);
6496 smb2_deltree(tree, t->dstdir);
6497 torture_reset_lease_break_info(tctx, &lease_break_info);
6500 /* Get an RH directory lease on the src directory */
6501 smb2_lease_v2_create_share(&c, &dirlease1, true, t->srcdir,
6502 smb2_util_share_access("RWD"),
6503 t->srcdir_leasekey, NULL,
6504 smb2_util_lease_state("RHW"), 0);
6505 c.in.desired_access &= ~DELETE_ACCESS;
6506 status = smb2_create(tree, mem_ctx, &c);
6507 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6508 "smb2_create failed\n");
6509 dirh1 = c.out.file.handle;
6510 CHECK_LEASE_V2(&c, "RH", true, t->srcdir_leasekey, 0, 0, ++dirlease1.lease_epoch);
6512 if (!samedir) {
6513 /* Get an RH directory lease on the dst directory */
6514 smb2_lease_v2_create_share(&c, &dirlease2, true, t->dstdir,
6515 smb2_util_share_access("RWD"),
6516 t->dstdir_leasekey, NULL,
6517 smb2_util_lease_state("RHW"), 0);
6518 c.in.desired_access &= ~DELETE_ACCESS;
6519 status = smb2_create(tree, mem_ctx, &c);
6520 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6521 "smb2_create failed\n");
6522 dirh2 = c.out.file.handle;
6523 CHECK_LEASE_V2(&c, "RH", true, t->dstdir_leasekey, 0, 0, ++dirlease2.lease_epoch);
6526 /* Create the to be renamed file */
6527 smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
6528 smb2_util_share_access("RWD"),
6529 LEASE4, &t->srcdir_leasekey,
6530 smb2_util_lease_state("RHW"),
6531 0x33);
6532 status = smb2_create(tree, mem_ctx, &c);
6533 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6534 "smb2_create failed\n");
6535 h1 = c.out.file.handle;
6536 smb2_util_close(tree, h1);
6537 ZERO_STRUCT(h1);
6539 /* Open the testfile, possibly with a bad parent leasekey */
6540 smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
6541 smb2_util_share_access("RWD"),
6542 LEASE4,
6543 t->parent_leasekey != 0 ? &t->parent_leasekey : NULL,
6544 smb2_util_lease_state("RHW"),
6545 0x33);
6546 status = smb2_create(tree, mem_ctx, &c);
6547 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6548 "smb2_create failed\n");
6549 h1 = c.out.file.handle;
6551 sfinfo.generic.level = RAW_SFILEINFO_RENAME_INFORMATION;
6552 sfinfo.generic.in.file.handle = h1;
6553 sfinfo.rename_information.in.new_name = t->dstfname;
6555 status = smb2_setinfo_file(tree, &sfinfo);
6556 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6557 "smb2_setinfo_file failed\n");
6558 if (t->expect_srcdir_break) {
6559 /* Delay the ack in order to be able to possibly process two breaks */
6560 lease_break_info.lease_skip_ack = true;
6561 CHECK_BREAK_INFO_V2(tree->session->transport,
6562 "RH", "",
6563 t->srcdir_leasekey,
6564 ++dirlease1.lease_epoch);
6565 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
6566 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
6567 torture_reset_lease_break_info(tctx, &lease_break_info);
6568 if (!t->expect_dstdir_break) {
6569 CHECK_NO_BREAK(tctx);
6572 if (t->expect_dstdir_break) {
6573 CHECK_BREAK_INFO_V2(tree->session->transport,
6574 "RH", "",
6575 t->dstdir_leasekey,
6576 ++dirlease2.lease_epoch);
6577 torture_reset_lease_break_info(tctx, &lease_break_info);
6578 if (!t->expect_srcdir_break) {
6579 CHECK_NO_BREAK(tctx);
6582 if (!t->expect_srcdir_break && !t->expect_dstdir_break) {
6583 CHECK_NO_BREAK(tctx);
6586 if (t->expect_srcdir_break) {
6587 /* ack the first lease break. */
6588 status = smb2_lease_break_ack(tree, &ack);
6589 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6590 "smb2_lease_break_ack failed\n");
6591 CHECK_LEASE_BREAK_ACK(&ack, "", t->srcdir_leasekey);
6595 done:
6596 if (!smb2_util_handle_empty(h1)) {
6597 smb2_util_close(tree, h1);
6599 if (!smb2_util_handle_empty(dirh1)) {
6600 smb2_util_close(tree, dirh1);
6602 if (!smb2_util_handle_empty(dirh2)) {
6603 smb2_util_close(tree, dirh2);
6605 smb2_deltree(tree, t->srcdir);
6606 smb2_deltree(tree, t->dstdir);
6607 return ret;
6610 static bool test_rename(struct torture_context *tctx,
6611 struct smb2_tree *tree)
6613 struct dlt_rename *t = NULL;
6614 bool ret = true;
6616 tree->session->transport->lease.handler = torture_lease_handler;
6617 tree->session->transport->lease.private_data = tree;
6618 torture_reset_lease_break_info(tctx, &lease_break_info);
6620 for (t = dlt_renames; t->testname != NULL; t++) {
6621 ret = test_rename_one(tctx, tree, t);
6622 torture_assert_goto(tctx, ret, ret, done,
6623 talloc_asprintf(tctx, "%s failed\n",
6624 t->testname));
6627 done:
6628 return ret;
6631 static bool test_overwrite(struct torture_context *tctx,
6632 struct smb2_tree *tree,
6633 struct smb2_tree *tree2)
6635 struct smb2_create c = {};
6636 struct smb2_lease dirlease1 = {};
6637 struct smb2_handle dirh1 = {};
6638 struct smb2_lease lease1 = {};
6639 struct smb2_handle h1 = {};
6640 struct smb2_handle h2 = {};
6641 struct smb2_request *req = NULL;
6642 struct smb2_lease_break_ack ack = {};
6643 struct smb2_lease *expected_lease1 = NULL;
6644 struct smb2_lease *expected_lease2 = NULL;
6645 uint64_t expected_leasekey1;
6646 uint64_t expected_leasekey2;
6647 const char *dname = "test_overwrite_dir";
6648 const char *fname = "test_overwrite_dir\\fname";
6649 int n;
6650 NTSTATUS status;
6651 bool ret = true;
6653 tree->session->transport->lease.handler = torture_lease_handler;
6654 tree->session->transport->lease.private_data = tree;
6655 torture_reset_lease_break_info(tctx, &lease_break_info);
6656 lease_break_info.lease_skip_ack = true;
6658 n = smb2_deltree(tree, dname);
6659 torture_assert_goto(tctx, n != -1, ret, done, "smb2_deltree failed\n");
6661 /* Get an RH directory lease on the directory */
6662 smb2_lease_v2_create_share(&c, &dirlease1, true, dname,
6663 smb2_util_share_access("RWD"),
6664 LEASE1, NULL,
6665 smb2_util_lease_state("RH"), 0);
6666 c.in.desired_access &= ~DELETE_ACCESS;
6667 status = smb2_create(tree, tree, &c);
6668 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6669 "smb2_create failed\n");
6670 dirh1 = c.out.file.handle;
6671 CHECK_LEASE_V2(&c, "RH", true, LEASE1, 0, 0, ++dirlease1.lease_epoch);
6673 /* Create a file with parent leasekey set*/
6674 smb2_lease_v2_create_share(&c, &lease1, false, fname,
6675 smb2_util_share_access("RWD"),
6676 LEASE2, &LEASE1,
6677 smb2_util_lease_state("RH"), 0);
6678 c.in.desired_access &= ~DELETE_ACCESS;
6679 status = smb2_create(tree, tree, &c);
6680 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6681 "smb2_create failed\n");
6682 h1 = c.out.file.handle;
6683 CHECK_LEASE_V2(&c, "RH", true, LEASE2,
6684 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET, LEASE1,
6685 ++lease1.lease_epoch);
6687 CHECK_NO_BREAK(tctx);
6689 /* Second client opens with overwrite disposition */
6690 c = (struct smb2_create) {
6691 .in.desired_access = SEC_FILE_READ_DATA,
6692 .in.share_access = NTCREATEX_SHARE_ACCESS_MASK,
6693 .in.create_disposition = NTCREATEX_DISP_OVERWRITE,
6694 .in.file_attributes = FILE_ATTRIBUTE_ARCHIVE,
6695 .in.fname = fname,
6697 req = smb2_create_send(tree2, &c);
6698 torture_assert(tctx, req != NULL, "smb2_create_send failed\n");
6699 torture_assert(tctx, req->state == SMB2_REQUEST_RECV, "req2 pending");
6701 torture_wait_for_lease_break(tctx);
6704 * Expect two lease breaks (dir and file) and accept the lease breaks in
6705 * any order.
6707 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
6708 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
6709 if (ack.in.lease.lease_key.data[0] == LEASE1) {
6710 expected_leasekey1 = LEASE1;
6711 expected_lease1 = &dirlease1;
6712 expected_leasekey2 = LEASE2;
6713 expected_lease2 = &lease1;
6714 } else {
6715 expected_leasekey1 = LEASE2;
6716 expected_lease1 = &lease1;
6717 expected_leasekey2 = LEASE1;
6718 expected_lease2 = &dirlease1;
6721 /* Break 1 */
6723 CHECK_BREAK_INFO_V2_NOWAIT(tree->session->transport,
6724 "RH", "", expected_leasekey1,
6725 ++(expected_lease1->lease_epoch));
6727 torture_reset_lease_break_info(tctx, &lease_break_info);
6728 lease_break_info.lease_skip_ack = true;
6730 status = smb2_lease_break_ack(tree, &ack);
6731 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6732 "smb2_lease_break_ack failed\n");
6733 CHECK_LEASE_BREAK_ACK(&ack, "", expected_leasekey1);
6735 /* Break 2 */
6737 CHECK_BREAK_INFO_V2(tree->session->transport,
6738 "RH", "", expected_leasekey2,
6739 ++(expected_lease2->lease_epoch));
6740 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
6741 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
6743 torture_reset_lease_break_info(tctx, &lease_break_info);
6744 lease_break_info.lease_skip_ack = true;
6746 status = smb2_lease_break_ack(tree, &ack);
6747 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6748 "smb2_lease_break_ack failed\n");
6749 CHECK_LEASE_BREAK_ACK(&ack, "", expected_leasekey2);
6751 status = smb2_create_recv(req, tctx, &c);
6752 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6753 "smb2_create_recv failed\n");
6754 h2 = c.out.file.handle;
6756 done:
6757 if (!smb2_util_handle_empty(h1)) {
6758 smb2_util_close(tree, h1);
6760 if (!smb2_util_handle_empty(h2)) {
6761 smb2_util_close(tree, h2);
6763 if (!smb2_util_handle_empty(dirh1)) {
6764 smb2_util_close(tree, dirh1);
6767 n = smb2_deltree(tree, dname);
6768 torture_assert(tctx, n != -1, "smb2_deltree failed\n");
6769 return ret;
6772 static struct dlt_hardlink {
6773 const char *testname;
6774 bool expect_srcdir_break;
6775 bool expect_dstdir_break;
6776 const char *srcdir;
6777 const char *dstdir;
6778 uint64_t srcdir_leasekey;
6779 uint64_t dstdir_leasekey;
6780 uint64_t parent_leasekey;
6781 const char *srcfname;
6782 const char *dstfname;
6783 } dlt_hardlinks[] = {
6785 .testname = "samedir-correct-parent-leaskey",
6786 .expect_srcdir_break = false,
6787 .expect_dstdir_break = false,
6788 .srcdir = "test_dirlease_rename_dir",
6789 .dstdir = "test_dirlease_rename_dir",
6790 .srcdir_leasekey = DLEASE1,
6791 .dstdir_leasekey = DLEASE2,
6792 .parent_leasekey = DLEASE1,
6793 .srcfname = "test_dirlease_rename_dir\\srcfile",
6794 .dstfname = "test_dirlease_rename_dir\\dstfile",
6795 }, {
6796 .testname = "samedir-wrong-parent-leaskey",
6797 .expect_srcdir_break = true,
6798 .expect_dstdir_break = false,
6799 .srcdir = "test_dirlease_rename_dir",
6800 .dstdir = "test_dirlease_rename_dir",
6801 .srcdir_leasekey = DLEASE1,
6802 .dstdir_leasekey = DLEASE2,
6803 .parent_leasekey = DLEASE3,
6804 .srcfname = "test_dirlease_rename_dir\\srcfile",
6805 .dstfname = "test_dirlease_rename_dir\\dstfile",
6806 }, {
6807 .testname = "samedir-no-parent-leaskey",
6808 .expect_srcdir_break = true,
6809 .expect_dstdir_break = false,
6810 .srcdir = "test_dirlease_rename_dir",
6811 .dstdir = "test_dirlease_rename_dir",
6812 .srcdir_leasekey = DLEASE1,
6813 .dstdir_leasekey = DLEASE2,
6814 .parent_leasekey = 0,
6815 .srcfname = "test_dirlease_rename_dir\\srcfile",
6816 .dstfname = "test_dirlease_rename_dir\\dstfile",
6817 }, {
6818 .testname = "otherdir-correct-srcparent-leaskey",
6819 .expect_srcdir_break = false,
6820 .expect_dstdir_break = true,
6821 .srcdir = "test_dirlease_rename_dir",
6822 .dstdir = "test_dirlease_rename_dir2",
6823 .srcdir_leasekey = DLEASE1,
6824 .dstdir_leasekey = DLEASE2,
6825 .parent_leasekey = DLEASE1,
6826 .srcfname = "test_dirlease_rename_dir\\srcfile",
6827 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6828 }, {
6829 .testname = "otherdir-correct-dstparent-leaskey",
6830 .expect_srcdir_break = false,
6831 .expect_dstdir_break = false,
6832 .srcdir = "test_dirlease_rename_dir",
6833 .dstdir = "test_dirlease_rename_dir2",
6834 .srcdir_leasekey = DLEASE1,
6835 .dstdir_leasekey = DLEASE2,
6836 .parent_leasekey = DLEASE2,
6837 .srcfname = "test_dirlease_rename_dir\\srcfile",
6838 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6839 }, {
6840 .testname = "otherdir-wrong-parent-leaskey",
6841 .expect_srcdir_break = false,
6842 .expect_dstdir_break = true,
6843 .srcdir = "test_dirlease_rename_dir",
6844 .dstdir = "test_dirlease_rename_dir2",
6845 .srcdir_leasekey = DLEASE1,
6846 .dstdir_leasekey = DLEASE2,
6847 .parent_leasekey = DLEASE3,
6848 .srcfname = "test_dirlease_rename_dir\\srcfile",
6849 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6850 }, {
6851 .testname = "otherdir-no-parent-leaskey",
6852 .expect_srcdir_break = false,
6853 .expect_dstdir_break = true,
6854 .srcdir = "test_dirlease_rename_dir",
6855 .dstdir = "test_dirlease_rename_dir2",
6856 .srcdir_leasekey = DLEASE1,
6857 .dstdir_leasekey = DLEASE2,
6858 .parent_leasekey = 0,
6859 .srcfname = "test_dirlease_rename_dir\\srcfile",
6860 .dstfname = "test_dirlease_rename_dir2\\dstfile",
6861 }, {
6862 .testname = NULL,
6866 static bool test_hardlink_one(struct torture_context *tctx,
6867 struct smb2_tree *tree,
6868 struct dlt_hardlink *t)
6870 TALLOC_CTX *mem_ctx = talloc_new(tctx);
6871 struct smb2_create c;
6872 struct smb2_lease dirlease1;
6873 struct smb2_lease dirlease2;
6874 struct smb2_handle dirh1 = {};
6875 struct smb2_handle dirh2 = {};
6876 struct smb2_lease lease1;
6877 struct smb2_handle h1 = {};
6878 struct smb2_lease_break_ack ack = {};
6879 bool samedir = strequal(t->srcdir, t->dstdir);
6880 union smb_setfileinfo sfinfo = {};
6881 NTSTATUS status;
6882 bool ret = true;
6884 torture_comment(tctx, "\nHardlink subtest: %s\n"
6885 "==================================\n",
6886 t->testname);
6888 smb2_deltree(tree, t->srcdir);
6889 smb2_deltree(tree, t->dstdir);
6890 torture_reset_lease_break_info(tctx, &lease_break_info);
6893 /* Get an RH directory lease on the src directory */
6894 smb2_lease_v2_create_share(&c, &dirlease1, true, t->srcdir,
6895 smb2_util_share_access("RWD"),
6896 t->srcdir_leasekey, NULL,
6897 smb2_util_lease_state("RHW"), 0);
6898 c.in.desired_access &= ~DELETE_ACCESS;
6899 status = smb2_create(tree, mem_ctx, &c);
6900 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6901 "smb2_create failed\n");
6902 dirh1 = c.out.file.handle;
6903 CHECK_LEASE_V2(&c, "RH", true, t->srcdir_leasekey, 0, 0, ++dirlease1.lease_epoch);
6905 if (!samedir) {
6906 /* Get an RH directory lease on the dst directory */
6907 smb2_lease_v2_create_share(&c, &dirlease2, true, t->dstdir,
6908 smb2_util_share_access("RWD"),
6909 t->dstdir_leasekey, NULL,
6910 smb2_util_lease_state("RHW"), 0);
6911 c.in.desired_access &= ~DELETE_ACCESS;
6912 status = smb2_create(tree, mem_ctx, &c);
6913 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6914 "smb2_create failed\n");
6915 dirh2 = c.out.file.handle;
6916 CHECK_LEASE_V2(&c, "RH", true, t->dstdir_leasekey, 0, 0, ++dirlease2.lease_epoch);
6919 /* Create the to be hardlinkd file */
6920 smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
6921 smb2_util_share_access("RWD"),
6922 LEASE4, &t->srcdir_leasekey,
6923 smb2_util_lease_state("RHW"),
6924 0x33);
6925 status = smb2_create(tree, mem_ctx, &c);
6926 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6927 "smb2_create failed\n");
6928 h1 = c.out.file.handle;
6929 smb2_util_close(tree, h1);
6930 ZERO_STRUCT(h1);
6932 /* Open the testfile, possibly with a bad parent leasekey */
6933 smb2_lease_v2_create_share(&c, &lease1, false, t->srcfname,
6934 smb2_util_share_access("RWD"),
6935 LEASE4,
6936 t->parent_leasekey != 0 ? &t->parent_leasekey : NULL,
6937 smb2_util_lease_state("RHW"),
6938 0x33);
6939 status = smb2_create(tree, mem_ctx, &c);
6940 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6941 "smb2_create failed\n");
6942 h1 = c.out.file.handle;
6944 sfinfo.generic.level = RAW_SFILEINFO_LINK_INFORMATION;
6945 sfinfo.generic.in.file.handle = h1;
6946 sfinfo.link_information.in.new_name = t->dstfname;
6948 status = smb2_setinfo_file(tree, &sfinfo);
6949 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6950 "smb2_setinfo_file failed\n");
6951 if (t->expect_srcdir_break) {
6952 /* Delay the ack in order to be able to possibly process two breaks */
6953 lease_break_info.lease_skip_ack = true;
6954 CHECK_BREAK_INFO_V2(tree->session->transport,
6955 "RH", "",
6956 t->srcdir_leasekey,
6957 ++dirlease1.lease_epoch);
6958 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
6959 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
6960 torture_reset_lease_break_info(tctx, &lease_break_info);
6961 if (!t->expect_dstdir_break) {
6962 CHECK_NO_BREAK(tctx);
6965 if (t->expect_dstdir_break) {
6966 CHECK_BREAK_INFO_V2(tree->session->transport,
6967 "RH", "",
6968 t->dstdir_leasekey,
6969 ++dirlease2.lease_epoch);
6970 torture_reset_lease_break_info(tctx, &lease_break_info);
6971 if (!t->expect_srcdir_break) {
6972 CHECK_NO_BREAK(tctx);
6975 if (!t->expect_srcdir_break && !t->expect_dstdir_break) {
6976 CHECK_NO_BREAK(tctx);
6979 if (t->expect_srcdir_break) {
6980 /* ack the first lease break. */
6981 status = smb2_lease_break_ack(tree, &ack);
6982 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
6983 "smb2_lease_break_ack failed\n");
6984 CHECK_LEASE_BREAK_ACK(&ack, "", t->srcdir_leasekey);
6988 done:
6989 if (!smb2_util_handle_empty(h1)) {
6990 smb2_util_close(tree, h1);
6992 if (!smb2_util_handle_empty(dirh1)) {
6993 smb2_util_close(tree, dirh1);
6995 if (!smb2_util_handle_empty(dirh2)) {
6996 smb2_util_close(tree, dirh2);
6998 smb2_deltree(tree, t->srcdir);
6999 smb2_deltree(tree, t->dstdir);
7000 return ret;
7003 static bool test_hardlink(struct torture_context *tctx,
7004 struct smb2_tree *tree)
7006 struct dlt_hardlink *t = NULL;
7007 bool ret = true;
7009 tree->session->transport->lease.handler = torture_lease_handler;
7010 tree->session->transport->lease.private_data = tree;
7011 torture_reset_lease_break_info(tctx, &lease_break_info);
7013 for (t = dlt_hardlinks; t->testname != NULL; t++) {
7014 ret = test_hardlink_one(tctx, tree, t);
7015 torture_assert_goto(tctx, ret, ret, done,
7016 talloc_asprintf(tctx, "%s failed\n",
7017 t->testname));
7020 done:
7021 return ret;
7025 * If the parent key of handle on which delete-on-close was set is the same as
7026 * the parent key of last handle closed, don't break this parent lease but all
7027 * others.
7029 static bool test_unlink_same_set_and_close(struct torture_context *tctx,
7030 struct smb2_tree *tree)
7032 struct smb2_create c = {};
7033 struct smb2_handle d1 = {};
7034 struct smb2_handle d2 = {};
7035 struct smb2_handle h1 = {};
7036 struct smb2_handle h2 = {};
7037 struct smb2_lease dlease1 = {};
7038 struct smb2_lease dlease2 = {};
7039 const uint64_t dlk1 = DLEASE1;
7040 const uint64_t dlk2 = DLEASE2;
7041 struct smb2_lease flease1 = {};
7042 struct smb2_lease flease2 = {};
7043 const char *dname = "test_unlink";
7044 const char *fname = "test_unlink\\test_unlink.dat";
7045 union smb_setfileinfo sfinfo = {};
7046 NTSTATUS status;
7047 bool ret = true;
7049 tree->session->transport->lease.handler = torture_lease_handler;
7050 tree->session->transport->lease.private_data = tree;
7051 torture_reset_lease_break_info(tctx, &lease_break_info);
7053 smb2_deltree(tree, dname);
7054 smb2_util_mkdir(tree, dname);
7055 torture_setup_simple_file(tctx, tree, fname);
7057 torture_comment(tctx, "First open test directory with RH-dirlease\n");
7059 smb2_lease_v2_create(&c, &dlease1, true, dname,
7060 DLEASE1, NULL,
7061 smb2_util_lease_state("RH"), 0);
7062 status = smb2_create(tree, tree, &c);
7063 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7064 "smb2_create failed\n");
7065 d1 = c.out.file.handle;
7066 CHECK_LEASE_V2(&c, "RH", true, DLEASE1, 0, 0, ++dlease1.lease_epoch);
7068 torture_comment(tctx, "Second open test directory with RH-dirlease\n");
7070 smb2_lease_v2_create(&c, &dlease2, true, dname,
7071 DLEASE2, NULL,
7072 smb2_util_lease_state("RH"), 0);
7073 status = smb2_create(tree, tree, &c);
7074 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7075 "smb2_create failed\n");
7076 d2 = c.out.file.handle;
7077 CHECK_LEASE_V2(&c, "RH", true, DLEASE2, 0, 0, ++dlease2.lease_epoch);
7079 torture_comment(tctx, "First open test file\n");
7081 smb2_lease_v2_create(&c, &flease1, false, fname,
7082 LEASE1, &dlk1,
7083 smb2_util_lease_state("R"), 0);
7085 status = smb2_create(tree, tree, &c);
7086 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7087 "smb2_create failed\n");
7088 h1 = c.out.file.handle;
7090 torture_comment(tctx, "Second open test file\n");
7092 smb2_lease_v2_create(&c, &flease2, false, fname,
7093 LEASE2, &dlk2,
7094 smb2_util_lease_state("R"), 0);
7096 status = smb2_create(tree, tree, &c);
7097 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7098 "smb2_create failed\n");
7099 h2 = c.out.file.handle;
7101 torture_comment(tctx, "Sets delete on close on open 2\n");
7103 sfinfo.disposition_info.in.delete_on_close = 1;
7104 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
7105 sfinfo.generic.in.file.handle = h2;
7107 status = smb2_setinfo_file(tree, &sfinfo);
7108 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7109 "smb2_setinfo_file failed\n");
7111 torture_comment(tctx, "Closing first handle that has not set delete-on-close, "
7112 "this should not trigger a break\n");
7114 smb2_util_close(tree, h1);
7115 ZERO_STRUCT(h1);
7116 CHECK_NO_BREAK(tctx);
7118 torture_comment(tctx, "Closing second and last handle will remove the file, "
7119 "trigger a break on first directory with different "
7120 "parent lease key\n");
7122 smb2_util_close(tree, h2);
7123 ZERO_STRUCT(h2);
7125 CHECK_BREAK_INFO_V2(tree->session->transport,
7126 "RH", "", DLEASE1,
7127 ++dlease1.lease_epoch);
7129 done:
7130 if (!smb2_util_handle_empty(h1)) {
7131 smb2_util_close(tree, h1);
7133 if (!smb2_util_handle_empty(h2)) {
7134 smb2_util_close(tree, h2);
7136 if (!smb2_util_handle_empty(d1)) {
7137 smb2_util_close(tree, d1);
7139 if (!smb2_util_handle_empty(d2)) {
7140 smb2_util_close(tree, d2);
7142 return ret;
7146 * When the parent key of handle on which delete-on-close was set differs from
7147 * the parent key of last handle closed, which actually does delete the file,
7148 * all directory leases must be broken.
7150 static bool test_unlink_different_set_and_close(struct torture_context *tctx,
7151 struct smb2_tree *tree)
7153 struct smb2_create c = {};
7154 struct smb2_handle d1 = {};
7155 struct smb2_handle d2 = {};
7156 struct smb2_handle h1 = {};
7157 struct smb2_handle h2 = {};
7158 struct smb2_lease dlease1 = {};
7159 struct smb2_lease dlease2 = {};
7160 const uint64_t dlk1 = DLEASE1;
7161 const uint64_t dlk2 = DLEASE2;
7162 struct smb2_lease flease1 = {};
7163 struct smb2_lease flease2 = {};
7164 const char *dname = "test_unlink";
7165 const char *fname = "test_unlink\\test_unlink.dat";
7166 union smb_setfileinfo sfinfo = {};
7167 struct smb2_lease_break_ack ack = {};
7168 struct smb2_lease *expected_lease1 = NULL;
7169 struct smb2_lease *expected_lease2 = NULL;
7170 uint64_t expected_leasekey1;
7171 uint64_t expected_leasekey2;
7172 NTSTATUS status;
7173 bool ret = true;
7175 tree->session->transport->lease.handler = torture_lease_handler;
7176 tree->session->transport->lease.private_data = tree;
7177 torture_reset_lease_break_info(tctx, &lease_break_info);
7178 lease_break_info.lease_skip_ack = true;
7180 smb2_deltree(tree, dname);
7181 smb2_util_mkdir(tree, dname);
7182 torture_setup_simple_file(tctx, tree, fname);
7184 torture_comment(tctx, "First open test directory with RH-dirlease\n");
7186 smb2_lease_v2_create(&c, &dlease1, true, dname,
7187 DLEASE1, NULL,
7188 smb2_util_lease_state("RH"), 0);
7189 status = smb2_create(tree, tree, &c);
7190 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7191 "smb2_create failed\n");
7192 d1 = c.out.file.handle;
7193 CHECK_LEASE_V2(&c, "RH", true, DLEASE1, 0, 0, ++dlease1.lease_epoch);
7195 torture_comment(tctx, "Second open test directory with RH-dirlease\n");
7197 smb2_lease_v2_create(&c, &dlease2, true, dname,
7198 DLEASE2, NULL,
7199 smb2_util_lease_state("RH"), 0);
7200 status = smb2_create(tree, tree, &c);
7201 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7202 "smb2_create failed\n");
7203 d2 = c.out.file.handle;
7204 CHECK_LEASE_V2(&c, "RH", true, DLEASE2, 0, 0, ++dlease2.lease_epoch);
7206 torture_comment(tctx, "First open test file\n");
7208 smb2_lease_v2_create(&c, &flease1, false, fname,
7209 LEASE1, &dlk1,
7210 smb2_util_lease_state("R"), 0);
7212 status = smb2_create(tree, tree, &c);
7213 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7214 "smb2_create failed\n");
7215 h1 = c.out.file.handle;
7217 torture_comment(tctx, "Second open test file\n");
7219 smb2_lease_v2_create(&c, &flease2, false, fname,
7220 LEASE2, &dlk2,
7221 smb2_util_lease_state("R"), 0);
7223 status = smb2_create(tree, tree, &c);
7224 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7225 "smb2_create failed\n");
7226 h2 = c.out.file.handle;
7228 torture_comment(tctx, "Client 1 sets delete on close\n");
7230 sfinfo.disposition_info.in.delete_on_close = 1;
7231 sfinfo.generic.level = RAW_SFILEINFO_DISPOSITION_INFORMATION;
7232 sfinfo.generic.in.file.handle = h1;
7234 status = smb2_setinfo_file(tree, &sfinfo);
7235 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7236 "smb2_setinfo_file failed\n");
7238 torture_comment(tctx, "Closing first handle that has set delete-on-close, "
7239 "will not delete the file and not trigger a break\n");
7241 smb2_util_close(tree, h1);
7242 ZERO_STRUCT(h1);
7243 CHECK_NO_BREAK(tctx);
7245 torture_comment(tctx, "Closing second and last handle will remove the file, "
7246 "and trigger a break as the parent lease keys don't match\n");
7248 smb2_util_close(tree, h2);
7249 ZERO_STRUCT(h2);
7251 torture_wait_for_lease_break(tctx);
7252 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
7253 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
7255 if (ack.in.lease.lease_key.data[0] == DLEASE1) {
7256 expected_leasekey1 = DLEASE1;
7257 expected_lease1 = &dlease1;
7258 expected_leasekey2 = DLEASE2;
7259 expected_lease2 = &dlease2;
7260 } else {
7261 expected_leasekey1 = DLEASE2;
7262 expected_lease1 = &dlease2;
7263 expected_leasekey2 = DLEASE1;
7264 expected_lease2 = &dlease1;
7267 CHECK_BREAK_INFO_V2_NOWAIT(tree->session->transport,
7268 "RH", "", expected_leasekey1,
7269 ++(expected_lease1->lease_epoch));
7271 torture_reset_lease_break_info(tctx, &lease_break_info);
7272 lease_break_info.lease_skip_ack = true;
7274 status = smb2_lease_break_ack(tree, &ack);
7275 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7276 "smb2_lease_break_ack failed\n");
7277 CHECK_LEASE_BREAK_ACK(&ack, "", expected_leasekey1);
7279 CHECK_BREAK_INFO_V2_NOWAIT(tree->session->transport,
7280 "RH", "", expected_leasekey2,
7281 ++(expected_lease2->lease_epoch));
7283 done:
7284 if (!smb2_util_handle_empty(h1)) {
7285 smb2_util_close(tree, h1);
7287 if (!smb2_util_handle_empty(h2)) {
7288 smb2_util_close(tree, h2);
7290 if (!smb2_util_handle_empty(d1)) {
7291 smb2_util_close(tree, d1);
7293 if (!smb2_util_handle_empty(d2)) {
7294 smb2_util_close(tree, d2);
7296 return ret;
7300 * If the parent key of handle on which initial delete-on-close was requested is
7301 * the same as the parent key of last handle closed, don't break that parent
7302 * lease but all others.
7304 static bool test_unlink_same_initial_and_close(struct torture_context *tctx,
7305 struct smb2_tree *tree)
7307 struct smb2_create c = {};
7308 struct smb2_handle d1 = {};
7309 struct smb2_handle d2 = {};
7310 struct smb2_handle h1 = {};
7311 struct smb2_handle h2 = {};
7312 struct smb2_lease dlease1 = {};
7313 struct smb2_lease dlease2 = {};
7314 const uint64_t dlk1 = DLEASE1;
7315 const uint64_t dlk2 = DLEASE2;
7316 struct smb2_lease flease1 = {};
7317 struct smb2_lease flease2 = {};
7318 const char *dname = "test_unlink";
7319 const char *fname = "test_unlink\\test_unlink.dat";
7320 NTSTATUS status;
7321 bool ret = true;
7323 tree->session->transport->lease.handler = torture_lease_handler;
7324 tree->session->transport->lease.private_data = tree;
7325 torture_reset_lease_break_info(tctx, &lease_break_info);
7326 lease_break_info.lease_skip_ack = true;
7328 smb2_deltree(tree, dname);
7329 smb2_util_mkdir(tree, dname);
7330 torture_setup_simple_file(tctx, tree, fname);
7332 torture_comment(tctx, "First open test directory with RH-dirlease\n");
7334 smb2_lease_v2_create(&c, &dlease1, true, dname,
7335 DLEASE1, NULL,
7336 smb2_util_lease_state("RH"), 0);
7337 status = smb2_create(tree, tree, &c);
7338 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7339 "smb2_create failed\n");
7340 d1 = c.out.file.handle;
7341 CHECK_LEASE_V2(&c, "RH", true, DLEASE1, 0, 0, ++dlease1.lease_epoch);
7343 torture_comment(tctx, "Second open test directory with RH-dirlease\n");
7345 smb2_lease_v2_create(&c, &dlease2, true, dname,
7346 DLEASE2, NULL,
7347 smb2_util_lease_state("RH"), 0);
7348 status = smb2_create(tree, tree, &c);
7349 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7350 "smb2_create failed\n");
7351 d2 = c.out.file.handle;
7352 CHECK_LEASE_V2(&c, "RH", true, DLEASE2, 0, 0, ++dlease2.lease_epoch);
7354 torture_comment(tctx, "First open test file with initial delete-on-close\n");
7356 smb2_lease_v2_create(&c, &flease1, false, fname,
7357 LEASE1, &dlk1,
7358 smb2_util_lease_state("R"), 0);
7359 c.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
7361 status = smb2_create(tree, tree, &c);
7362 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7363 "smb2_create failed\n");
7364 h1 = c.out.file.handle;
7366 torture_comment(tctx, "Second open test file\n");
7368 smb2_lease_v2_create(&c, &flease2, false, fname,
7369 LEASE2, &dlk2,
7370 smb2_util_lease_state("R"), 0);
7372 status = smb2_create(tree, tree, &c);
7373 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7374 "smb2_create failed\n");
7375 h2 = c.out.file.handle;
7377 torture_comment(tctx, "Closing second handle should not trigger a lease break\n");
7379 smb2_util_close(tree, h2);
7380 ZERO_STRUCT(h2);
7382 torture_comment(tctx, "Closing first handle that had initial delete-on-close, "
7383 "must trigger single break on directory handle 2\n");
7385 smb2_util_close(tree, h1);
7386 ZERO_STRUCT(h1);
7388 CHECK_BREAK_INFO_V2(tree->session->transport,
7389 "RH", "", DLEASE2,
7390 ++dlease2.lease_epoch);
7392 done:
7393 if (!smb2_util_handle_empty(h1)) {
7394 smb2_util_close(tree, h1);
7396 if (!smb2_util_handle_empty(h2)) {
7397 smb2_util_close(tree, h2);
7399 if (!smb2_util_handle_empty(d1)) {
7400 smb2_util_close(tree, d1);
7402 if (!smb2_util_handle_empty(d2)) {
7403 smb2_util_close(tree, d2);
7405 return ret;
7409 * When the parent key of handle on which initial delete-on-close was set
7410 * differs from the parent key of last handle closed, which actually does delete
7411 * the file, all directory leases must be broken.
7413 static bool test_unlink_different_initial_and_close(struct torture_context *tctx,
7414 struct smb2_tree *tree)
7416 struct smb2_create c = {};
7417 struct smb2_handle d1 = {};
7418 struct smb2_handle d2 = {};
7419 struct smb2_handle h1 = {};
7420 struct smb2_handle h2 = {};
7421 struct smb2_lease dlease1 = {};
7422 struct smb2_lease dlease2 = {};
7423 const uint64_t dlk1 = DLEASE1;
7424 const uint64_t dlk2 = DLEASE2;
7425 struct smb2_lease flease1 = {};
7426 struct smb2_lease flease2 = {};
7427 const char *dname = "test_unlink";
7428 const char *fname = "test_unlink\\test_unlink.dat";
7429 struct smb2_lease_break_ack ack = {};
7430 struct smb2_lease *expected_lease1 = NULL;
7431 struct smb2_lease *expected_lease2 = NULL;
7432 uint64_t expected_leasekey1;
7433 uint64_t expected_leasekey2;
7434 NTSTATUS status;
7435 bool ret = true;
7437 tree->session->transport->lease.handler = torture_lease_handler;
7438 tree->session->transport->lease.private_data = tree;
7439 torture_reset_lease_break_info(tctx, &lease_break_info);
7440 lease_break_info.lease_skip_ack = true;
7442 smb2_deltree(tree, dname);
7443 smb2_util_mkdir(tree, dname);
7444 torture_setup_simple_file(tctx, tree, fname);
7446 torture_comment(tctx, "First open test directory with RH-dirlease\n");
7448 smb2_lease_v2_create(&c, &dlease1, true, dname,
7449 DLEASE1, NULL,
7450 smb2_util_lease_state("RH"), 0);
7451 status = smb2_create(tree, tree, &c);
7452 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7453 "smb2_create failed\n");
7454 d1 = c.out.file.handle;
7455 CHECK_LEASE_V2(&c, "RH", true, DLEASE1, 0, 0, ++dlease1.lease_epoch);
7457 torture_comment(tctx, "Second open test directory with RH-dirlease\n");
7459 smb2_lease_v2_create(&c, &dlease2, true, dname,
7460 DLEASE2, NULL,
7461 smb2_util_lease_state("RH"), 0);
7462 status = smb2_create(tree, tree, &c);
7463 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7464 "smb2_create failed\n");
7465 d2 = c.out.file.handle;
7466 CHECK_LEASE_V2(&c, "RH", true, DLEASE2, 0, 0, ++dlease2.lease_epoch);
7468 torture_comment(tctx, "First open test file\n");
7470 smb2_lease_v2_create(&c, &flease1, false, fname,
7471 LEASE1, &dlk1,
7472 smb2_util_lease_state("R"), 0);
7473 c.in.create_options = NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
7475 status = smb2_create(tree, tree, &c);
7476 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7477 "smb2_create failed\n");
7478 h1 = c.out.file.handle;
7480 torture_comment(tctx, "Second open test file\n");
7482 smb2_lease_v2_create(&c, &flease2, false, fname,
7483 LEASE2, &dlk2,
7484 smb2_util_lease_state("R"), 0);
7486 status = smb2_create(tree, tree, &c);
7487 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7488 "smb2_create failed\n");
7489 h2 = c.out.file.handle;
7491 torture_comment(tctx, "Closing first handle that requested initial delete-on-close, "
7492 "will not delete the file and not trigger a break\n");
7494 smb2_util_close(tree, h1);
7495 ZERO_STRUCT(h1);
7496 CHECK_NO_BREAK(tctx);
7498 torture_comment(tctx, "Closing second and last handle will remove the file, "
7499 "and trigger a break as the parent lease keys don't match\n");
7501 smb2_util_close(tree, h2);
7502 ZERO_STRUCT(h2);
7504 torture_wait_for_lease_break(tctx);
7505 ack.in.lease.lease_key = lease_break_info.lease_break.current_lease.lease_key;
7506 ack.in.lease.lease_state = lease_break_info.lease_break.new_lease_state;
7508 if (ack.in.lease.lease_key.data[0] == DLEASE1) {
7509 expected_leasekey1 = DLEASE1;
7510 expected_lease1 = &dlease1;
7511 expected_leasekey2 = DLEASE2;
7512 expected_lease2 = &dlease2;
7513 } else {
7514 expected_leasekey1 = DLEASE2;
7515 expected_lease1 = &dlease2;
7516 expected_leasekey2 = DLEASE1;
7517 expected_lease2 = &dlease1;
7520 CHECK_BREAK_INFO_V2_NOWAIT(tree->session->transport,
7521 "RH", "", expected_leasekey1,
7522 ++(expected_lease1->lease_epoch));
7524 torture_reset_lease_break_info(tctx, &lease_break_info);
7525 lease_break_info.lease_skip_ack = true;
7527 status = smb2_lease_break_ack(tree, &ack);
7528 torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
7529 "smb2_lease_break_ack failed\n");
7530 CHECK_LEASE_BREAK_ACK(&ack, "", expected_leasekey1);
7532 CHECK_BREAK_INFO_V2_NOWAIT(tree->session->transport,
7533 "RH", "", expected_leasekey2,
7534 ++(expected_lease2->lease_epoch));
7536 done:
7537 if (!smb2_util_handle_empty(h1)) {
7538 smb2_util_close(tree, h1);
7540 if (!smb2_util_handle_empty(h2)) {
7541 smb2_util_close(tree, h2);
7543 if (!smb2_util_handle_empty(d1)) {
7544 smb2_util_close(tree, d1);
7546 if (!smb2_util_handle_empty(d2)) {
7547 smb2_util_close(tree, d2);
7549 return ret;
7552 struct torture_suite *torture_smb2_dirlease_init(TALLOC_CTX *ctx)
7554 struct torture_suite *suite =
7555 torture_suite_create(ctx, "dirlease");
7557 suite->description = talloc_strdup(suite, "SMB3 Directory Lease tests");
7559 torture_suite_add_1smb2_test(suite, "v2_request_parent", test_lease_v2_request_parent);
7560 torture_suite_add_2smb2_test(suite, "v2_request", test_lease_v2_request);
7561 torture_suite_add_1smb2_test(suite, "leases", test_dirlease_leases);
7562 torture_suite_add_2smb2_test(suite, "seteof", test_dirlease_seteof);
7563 torture_suite_add_2smb2_test(suite, "setdos", test_dirlease_setdos);
7564 torture_suite_add_2smb2_test(suite, "setbtime", test_dirlease_setbtime);
7565 torture_suite_add_2smb2_test(suite, "setmtime", test_dirlease_setmtime);
7566 torture_suite_add_2smb2_test(suite, "setctime", test_dirlease_setctime);
7567 torture_suite_add_2smb2_test(suite, "setatime", test_dirlease_setatime);
7568 torture_suite_add_1smb2_test(suite, "rename", test_rename);
7569 torture_suite_add_2smb2_test(suite, "overwrite", test_overwrite);
7570 torture_suite_add_1smb2_test(suite, "hardlink", test_hardlink);
7571 torture_suite_add_1smb2_test(suite, "unlink_same_set_and_close", test_unlink_same_set_and_close);
7572 torture_suite_add_1smb2_test(suite, "unlink_different_set_and_close", test_unlink_different_set_and_close);
7573 torture_suite_add_1smb2_test(suite, "unlink_same_initial_and_close", test_unlink_same_initial_and_close);
7574 torture_suite_add_1smb2_test(suite, "unlink_different_initial_and_close", test_unlink_different_initial_and_close);
7575 return suite;