s4:torture:smb2: add comment explaining lease upgrade in the non-contended case
[Samba/wip.git] / source4 / torture / smb2 / lease.c
blob651fe26b153098a9629a981d8d29e71b4bef60e5
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 "libcli/smb/smbXcli_base.h"
30 #define CHECK_VAL(v, correct) do { \
31 if ((v) != (correct)) { \
32 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
33 __location__, #v, (int)(v), (int)(correct)); \
34 ret = false; \
35 }} while (0)
37 #define CHECK_STATUS(status, correct) do { \
38 if (!NT_STATUS_EQUAL(status, correct)) { \
39 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
40 nt_errstr(status), nt_errstr(correct)); \
41 ret = false; \
42 goto done; \
43 }} while (0)
45 #define CHECK_CREATED(__io, __created, __attribute) \
46 do { \
47 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
48 CHECK_VAL((__io)->out.alloc_size, 0); \
49 CHECK_VAL((__io)->out.size, 0); \
50 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
51 CHECK_VAL((__io)->out.reserved2, 0); \
52 } while(0)
54 #define CHECK_LEASE(__io, __state, __oplevel, __key) \
55 do { \
56 if (__oplevel) { \
57 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
58 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
59 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
60 CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
61 } else { \
62 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
63 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
64 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
65 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
66 } \
68 CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \
69 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
70 } while(0)
72 #define CHECK_LEASE_V2(__io, __state, __oplevel, __key, __flags) \
73 do { \
74 if (__oplevel) { \
75 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
76 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], (__key)); \
77 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], ~(__key)); \
78 CHECK_VAL((__io)->out.lease_response_v2.lease_state, smb2_util_lease_state(__state)); \
79 } else { \
80 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
81 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[0], 0); \
82 CHECK_VAL((__io)->out.lease_response_v2.lease_key.data[1], 0); \
83 CHECK_VAL((__io)->out.lease_response_v2.lease_state, 0); \
84 } \
86 CHECK_VAL((__io)->out.lease_response_v2.lease_flags, __flags); \
87 CHECK_VAL((__io)->out.lease_response_v2.lease_duration, 0); \
88 } while(0)
90 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
91 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
92 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
93 static const uint64_t LEASE4 = 0xBAD0FFEDD00DF00Dull;
95 #define NREQUEST_RESULTS 8
96 static const char *request_results[NREQUEST_RESULTS][2] = {
97 { "", "" },
98 { "R", "R" },
99 { "H", "" },
100 { "W", "" },
101 { "RH", "RH" },
102 { "RW", "RW" },
103 { "HW", "" },
104 { "RHW", "RHW" },
107 static bool test_lease_request(struct torture_context *tctx,
108 struct smb2_tree *tree)
110 TALLOC_CTX *mem_ctx = talloc_new(tctx);
111 struct smb2_create io;
112 struct smb2_lease ls;
113 struct smb2_handle h1, h2;
114 NTSTATUS status;
115 const char *fname = "lease.dat";
116 const char *fname2 = "lease2.dat";
117 const char *sname = "lease.dat:stream";
118 const char *dname = "lease.dir";
119 bool ret = true;
120 int i;
121 uint32_t caps;
123 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
124 if (!(caps & SMB2_CAP_LEASING)) {
125 torture_skip(tctx, "leases are not supported");
128 smb2_util_unlink(tree, fname);
129 smb2_util_unlink(tree, fname2);
130 smb2_util_rmdir(tree, dname);
132 /* Win7 is happy to grant RHW leases on files. */
133 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
134 status = smb2_create(tree, mem_ctx, &io);
135 CHECK_STATUS(status, NT_STATUS_OK);
136 h1 = io.out.file.handle;
137 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
138 CHECK_LEASE(&io, "RHW", true, LEASE1);
140 /* But will reject leases on directories. */
141 smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
142 status = smb2_create(tree, mem_ctx, &io);
143 CHECK_STATUS(status, NT_STATUS_OK);
144 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
145 CHECK_LEASE(&io, "", false, 0);
146 smb2_util_close(tree, io.out.file.handle);
148 /* Also rejects multiple files leased under the same key. */
149 smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
150 status = smb2_create(tree, mem_ctx, &io);
151 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
153 /* And grants leases on streams (with separate leasekey). */
154 smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW"));
155 status = smb2_create(tree, mem_ctx, &io);
156 h2 = io.out.file.handle;
157 CHECK_STATUS(status, NT_STATUS_OK);
158 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
159 CHECK_LEASE(&io, "RHW", true, LEASE2);
160 smb2_util_close(tree, h2);
162 smb2_util_close(tree, h1);
164 /* Now see what combos are actually granted. */
165 for (i = 0; i < NREQUEST_RESULTS; i++) {
166 torture_comment(tctx, "Requesting lease type %s(%x),"
167 " expecting %s(%x)\n",
168 request_results[i][0], smb2_util_lease_state(request_results[i][0]),
169 request_results[i][1], smb2_util_lease_state(request_results[i][1]));
170 smb2_lease_create(&io, &ls, false, fname, LEASE1,
171 smb2_util_lease_state(request_results[i][0]));
172 status = smb2_create(tree, mem_ctx, &io);
173 h2 = io.out.file.handle;
174 CHECK_STATUS(status, NT_STATUS_OK);
175 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
176 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
177 smb2_util_close(tree, io.out.file.handle);
180 done:
181 smb2_util_close(tree, h1);
182 smb2_util_close(tree, h2);
184 smb2_util_unlink(tree, fname);
185 smb2_util_unlink(tree, fname2);
186 smb2_util_rmdir(tree, dname);
188 talloc_free(mem_ctx);
190 return ret;
193 static bool test_lease_upgrade(struct torture_context *tctx,
194 struct smb2_tree *tree)
196 TALLOC_CTX *mem_ctx = talloc_new(tctx);
197 struct smb2_create io;
198 struct smb2_lease ls;
199 struct smb2_handle h, hnew;
200 NTSTATUS status;
201 const char *fname = "lease.dat";
202 bool ret = true;
203 uint32_t caps;
205 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
206 if (!(caps & SMB2_CAP_LEASING)) {
207 torture_skip(tctx, "leases are not supported");
210 smb2_util_unlink(tree, fname);
212 /* Grab a RH lease. */
213 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
214 status = smb2_create(tree, mem_ctx, &io);
215 CHECK_STATUS(status, NT_STATUS_OK);
216 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
217 CHECK_LEASE(&io, "RH", true, LEASE1);
218 h = io.out.file.handle;
220 /* Upgrades (sidegrades?) to RW leave us with an RH. */
221 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW"));
222 status = smb2_create(tree, mem_ctx, &io);
223 CHECK_STATUS(status, NT_STATUS_OK);
224 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
225 CHECK_LEASE(&io, "RH", true, LEASE1);
226 hnew = io.out.file.handle;
228 smb2_util_close(tree, hnew);
230 /* Upgrade to RHW lease. */
231 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
232 status = smb2_create(tree, mem_ctx, &io);
233 CHECK_STATUS(status, NT_STATUS_OK);
234 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
235 CHECK_LEASE(&io, "RHW", true, LEASE1);
236 hnew = io.out.file.handle;
238 smb2_util_close(tree, h);
239 h = hnew;
241 /* Attempt to downgrade - original lease state is maintained. */
242 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
243 status = smb2_create(tree, mem_ctx, &io);
244 CHECK_STATUS(status, NT_STATUS_OK);
245 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
246 CHECK_LEASE(&io, "RHW", true, LEASE1);
247 hnew = io.out.file.handle;
249 smb2_util_close(tree, hnew);
251 done:
252 smb2_util_close(tree, h);
253 smb2_util_close(tree, hnew);
255 smb2_util_unlink(tree, fname);
257 talloc_free(mem_ctx);
259 return ret;
263 * upgrade2 test.
264 * full matrix of lease upgrade combinations
265 * (non-contended case)
267 * The summary of the behaviour is this:
268 * -------------------------------------
269 * An uncontended lease upgrade results in a change
270 * if and only if the requested lease state is
271 * - valid, and
272 * - strictly a superset of the lease state already held.
274 * In that case the resulting lease state is the one
275 * requested in the upgrade.
277 struct lease_upgrade2_test {
278 const char *initial;
279 const char *upgrade_to;
280 const char *expected;
283 #define NUM_LEASE_TYPES 5
284 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
285 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
286 { "", "", "" },
287 { "", "R", "R" },
288 { "", "RH", "RH" },
289 { "", "RW", "RW" },
290 { "", "RWH", "RWH" },
292 { "R", "", "R" },
293 { "R", "R", "R" },
294 { "R", "RH", "RH" },
295 { "R", "RW", "RW" },
296 { "R", "RWH", "RWH" },
298 { "RH", "", "RH" },
299 { "RH", "R", "RH" },
300 { "RH", "RH", "RH" },
301 { "RH", "RW", "RH" },
302 { "RH", "RWH", "RWH" },
304 { "RW", "", "RW" },
305 { "RW", "R", "RW" },
306 { "RW", "RH", "RW" },
307 { "RW", "RW", "RW" },
308 { "RW", "RWH", "RWH" },
310 { "RWH", "", "RWH" },
311 { "RWH", "R", "RWH" },
312 { "RWH", "RH", "RWH" },
313 { "RWH", "RW", "RWH" },
314 { "RWH", "RWH", "RWH" },
317 static bool test_lease_upgrade2(struct torture_context *tctx,
318 struct smb2_tree *tree)
320 TALLOC_CTX *mem_ctx = talloc_new(tctx);
321 struct smb2_handle h, hnew;
322 NTSTATUS status;
323 struct smb2_create io;
324 struct smb2_lease ls;
325 const char *fname = "lease.dat";
326 bool ret = true;
327 int i;
328 uint32_t caps;
330 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
331 if (!(caps & SMB2_CAP_LEASING)) {
332 torture_skip(tctx, "leases are not supported");
335 for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
336 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
338 smb2_util_unlink(tree, fname);
340 /* Grab a lease. */
341 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
342 status = smb2_create(tree, mem_ctx, &io);
343 CHECK_STATUS(status, NT_STATUS_OK);
344 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
345 CHECK_LEASE(&io, t.initial, true, LEASE1);
346 h = io.out.file.handle;
348 /* Upgrade. */
349 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
350 status = smb2_create(tree, mem_ctx, &io);
351 CHECK_STATUS(status, NT_STATUS_OK);
352 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
353 CHECK_LEASE(&io, t.expected, true, LEASE1);
354 hnew = io.out.file.handle;
356 smb2_util_close(tree, hnew);
357 smb2_util_close(tree, h);
360 done:
361 smb2_util_close(tree, h);
362 smb2_util_close(tree, hnew);
364 smb2_util_unlink(tree, fname);
366 talloc_free(mem_ctx);
368 return ret;
372 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
373 do { \
374 CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state)); \
375 CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \
376 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
377 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
378 } while(0)
380 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
381 do { \
382 CHECK_VAL((__lba)->out.reserved, 0); \
383 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
384 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
385 CHECK_VAL((__lba)->out.lease.lease_state, smb2_util_lease_state(__state)); \
386 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
387 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
388 } while(0)
390 static struct {
391 struct smb2_lease_break lease_break;
392 struct smb2_lease_break_ack lease_break_ack;
393 int count;
394 int failures;
396 struct smb2_handle oplock_handle;
397 uint8_t held_oplock_level;
398 uint8_t oplock_level;
399 int oplock_count;
400 int oplock_failures;
401 } break_info;
403 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
404 do { \
405 CHECK_VAL(break_info.failures, 0); \
406 CHECK_VAL(break_info.count, 1); \
407 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
408 (__state), (__key)); \
409 if (break_info.lease_break.break_flags & \
410 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
411 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
412 (__state), (__key)); \
414 } while(0)
416 static void torture_lease_break_callback(struct smb2_request *req)
418 NTSTATUS status;
420 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
421 if (!NT_STATUS_IS_OK(status))
422 break_info.failures++;
424 return;
427 /* a lease break request handler */
428 static bool torture_lease_handler(struct smb2_transport *transport,
429 const struct smb2_lease_break *lb,
430 void *private_data)
432 struct smb2_tree *tree = private_data;
433 struct smb2_lease_break_ack io;
434 struct smb2_request *req;
436 break_info.lease_break = *lb;
437 break_info.count++;
439 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
440 ZERO_STRUCT(io);
441 io.in.lease.lease_key = lb->current_lease.lease_key;
442 io.in.lease.lease_state = lb->new_lease_state;
444 req = smb2_lease_break_ack_send(tree, &io);
445 req->async.fn = torture_lease_break_callback;
446 req->async.private_data = NULL;
449 return true;
453 Timer handler function notifies the registering function that time is up
455 static void timeout_cb(struct tevent_context *ev,
456 struct tevent_timer *te,
457 struct timeval current_time,
458 void *private_data)
460 bool *timesup = (bool *)private_data;
461 *timesup = true;
462 return;
466 Wait a short period of time to receive a single oplock break request
468 static void torture_wait_for_lease_break(struct torture_context *tctx)
470 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
471 struct tevent_timer *te = NULL;
472 struct timeval ne;
473 bool timesup = false;
474 int old_count = break_info.count;
476 /* Wait .1 seconds for an lease break */
477 ne = tevent_timeval_current_ofs(0, 100000);
479 te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
480 if (te == NULL) {
481 torture_comment(tctx, "Failed to wait for an oplock break. "
482 "test results may not be accurate.");
483 goto done;
486 while (!timesup && break_info.count < old_count + 1) {
487 if (tevent_loop_once(tctx->ev) != 0) {
488 torture_comment(tctx, "Failed to wait for an oplock "
489 "break. test results may not be "
490 "accurate.");
491 goto done;
495 done:
496 /* We don't know if the timed event fired and was freed, we received
497 * our oplock break, or some other event triggered the loop. Thus,
498 * we create a tmp_ctx to be able to safely free/remove the timed
499 * event in all 3 cases. */
500 talloc_free(tmp_ctx);
502 return;
506 break_results should be read as "held lease, new lease, hold broken to, new
507 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
508 tries for RW, key1 will be broken to RH (in this case, not broken at all)
509 and key2 will be granted R.
511 Note: break_results only includes things that Win7 will actually grant (see
512 request_results above).
514 #define NBREAK_RESULTS 16
515 static const char *break_results[NBREAK_RESULTS][4] = {
516 {"R", "R", "R", "R"},
517 {"R", "RH", "R", "RH"},
518 {"R", "RW", "R", "R"},
519 {"R", "RHW", "R", "RH"},
521 {"RH", "R", "RH", "R"},
522 {"RH", "RH", "RH", "RH"},
523 {"RH", "RW", "RH", "R"},
524 {"RH", "RHW", "RH", "RH"},
526 {"RW", "R", "R", "R"},
527 {"RW", "RH", "R", "RH"},
528 {"RW", "RW", "R", "R"},
529 {"RW", "RHW", "R", "RH"},
531 {"RHW", "R", "RH", "R"},
532 {"RHW", "RH", "RH", "RH"},
533 {"RHW", "RW", "RH", "R"},
534 {"RHW", "RHW", "RH", "RH"},
537 static bool test_lease_break(struct torture_context *tctx,
538 struct smb2_tree *tree)
540 TALLOC_CTX *mem_ctx = talloc_new(tctx);
541 struct smb2_create io;
542 struct smb2_lease ls;
543 struct smb2_handle h, h2, h3;
544 NTSTATUS status;
545 const char *fname = "lease.dat";
546 bool ret = true;
547 int i;
548 uint32_t caps;
550 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
551 if (!(caps & SMB2_CAP_LEASING)) {
552 torture_skip(tctx, "leases are not supported");
555 tree->session->transport->lease.handler = torture_lease_handler;
556 tree->session->transport->lease.private_data = tree;
558 smb2_util_unlink(tree, fname);
560 for (i = 0; i < NBREAK_RESULTS; i++) {
561 const char *held = break_results[i][0];
562 const char *contend = break_results[i][1];
563 const char *brokento = break_results[i][2];
564 const char *granted = break_results[i][3];
565 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
566 "expecting break to %s(%x) and grant of %s(%x)\n",
567 held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
568 brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
570 ZERO_STRUCT(break_info);
572 /* Grab lease. */
573 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
574 status = smb2_create(tree, mem_ctx, &io);
575 CHECK_STATUS(status, NT_STATUS_OK);
576 h = io.out.file.handle;
577 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
578 CHECK_LEASE(&io, held, true, LEASE1);
580 /* Possibly contend lease. */
581 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
582 status = smb2_create(tree, mem_ctx, &io);
583 CHECK_STATUS(status, NT_STATUS_OK);
584 h2 = io.out.file.handle;
585 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
586 CHECK_LEASE(&io, granted, true, LEASE2);
588 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
589 CHECK_BREAK_INFO(held, brokento, LEASE1);
590 } else {
591 CHECK_VAL(break_info.count, 0);
592 CHECK_VAL(break_info.failures, 0);
595 ZERO_STRUCT(break_info);
598 Now verify that an attempt to upgrade LEASE1 results in no
599 break and no change in LEASE1.
601 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
602 status = smb2_create(tree, mem_ctx, &io);
603 CHECK_STATUS(status, NT_STATUS_OK);
604 h3 = io.out.file.handle;
605 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
606 CHECK_LEASE(&io, brokento, true, LEASE1);
607 CHECK_VAL(break_info.count, 0);
608 CHECK_VAL(break_info.failures, 0);
610 smb2_util_close(tree, h);
611 smb2_util_close(tree, h2);
612 smb2_util_close(tree, h3);
614 status = smb2_util_unlink(tree, fname);
615 CHECK_STATUS(status, NT_STATUS_OK);
618 done:
619 smb2_util_close(tree, h);
620 smb2_util_close(tree, h2);
622 smb2_util_unlink(tree, fname);
624 talloc_free(mem_ctx);
626 return ret;
629 static void torture_oplock_break_callback(struct smb2_request *req)
631 NTSTATUS status;
632 struct smb2_break br;
634 ZERO_STRUCT(br);
635 status = smb2_break_recv(req, &br);
636 if (!NT_STATUS_IS_OK(status))
637 break_info.oplock_failures++;
639 return;
642 /* a oplock break request handler */
643 static bool torture_oplock_handler(struct smb2_transport *transport,
644 const struct smb2_handle *handle,
645 uint8_t level, void *private_data)
647 struct smb2_tree *tree = private_data;
648 struct smb2_request *req;
649 struct smb2_break br;
651 break_info.oplock_handle = *handle;
652 break_info.oplock_level = level;
653 break_info.oplock_count++;
655 ZERO_STRUCT(br);
656 br.in.file.handle = *handle;
657 br.in.oplock_level = level;
659 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
660 req = smb2_break_send(tree, &br);
661 req->async.fn = torture_oplock_break_callback;
662 req->async.private_data = NULL;
664 break_info.held_oplock_level = level;
666 return true;
669 #define NOPLOCK_RESULTS 12
670 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
671 {"R", "s", "R", "s"},
672 {"R", "x", "R", "s"},
673 {"R", "b", "R", "s"},
675 {"RH", "s", "RH", ""},
676 {"RH", "x", "RH", ""},
677 {"RH", "b", "RH", ""},
679 {"RW", "s", "R", "s"},
680 {"RW", "x", "R", "s"},
681 {"RW", "b", "R", "s"},
683 {"RHW", "s", "RH", ""},
684 {"RHW", "x", "RH", ""},
685 {"RHW", "b", "RH", ""},
688 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
689 {"s", "R", "s", "R"},
690 {"s", "RH", "s", "R"},
691 {"s", "RW", "s", "R"},
692 {"s", "RHW", "s", "R"},
694 {"x", "R", "s", "R"},
695 {"x", "RH", "s", "R"},
696 {"x", "RW", "s", "R"},
697 {"x", "RHW", "s", "R"},
699 {"b", "R", "s", "R"},
700 {"b", "RH", "s", "R"},
701 {"b", "RW", "s", "R"},
702 {"b", "RHW", "s", "R"},
705 static bool test_lease_oplock(struct torture_context *tctx,
706 struct smb2_tree *tree)
708 TALLOC_CTX *mem_ctx = talloc_new(tctx);
709 struct smb2_create io;
710 struct smb2_lease ls;
711 struct smb2_handle h, h2;
712 NTSTATUS status;
713 const char *fname = "lease.dat";
714 bool ret = true;
715 int i;
716 uint32_t caps;
718 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
719 if (!(caps & SMB2_CAP_LEASING)) {
720 torture_skip(tctx, "leases are not supported");
723 tree->session->transport->lease.handler = torture_lease_handler;
724 tree->session->transport->lease.private_data = tree;
725 tree->session->transport->oplock.handler = torture_oplock_handler;
726 tree->session->transport->oplock.private_data = tree;
728 smb2_util_unlink(tree, fname);
730 for (i = 0; i < NOPLOCK_RESULTS; i++) {
731 const char *held = oplock_results[i][0];
732 const char *contend = oplock_results[i][1];
733 const char *brokento = oplock_results[i][2];
734 const char *granted = oplock_results[i][3];
735 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
736 "expecting break to %s(%x) and grant of %s(%x)\n",
737 held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
738 brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
740 ZERO_STRUCT(break_info);
742 /* Grab lease. */
743 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
744 status = smb2_create(tree, mem_ctx, &io);
745 CHECK_STATUS(status, NT_STATUS_OK);
746 h = io.out.file.handle;
747 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
748 CHECK_LEASE(&io, held, true, LEASE1);
750 /* Does an oplock contend the lease? */
751 smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
752 status = smb2_create(tree, mem_ctx, &io);
753 CHECK_STATUS(status, NT_STATUS_OK);
754 h2 = io.out.file.handle;
755 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
756 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
757 break_info.held_oplock_level = io.out.oplock_level;
759 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
760 CHECK_BREAK_INFO(held, brokento, LEASE1);
761 } else {
762 CHECK_VAL(break_info.count, 0);
763 CHECK_VAL(break_info.failures, 0);
766 smb2_util_close(tree, h);
767 smb2_util_close(tree, h2);
769 status = smb2_util_unlink(tree, fname);
770 CHECK_STATUS(status, NT_STATUS_OK);
773 for (i = 0; i < NOPLOCK_RESULTS; i++) {
774 const char *held = oplock_results_2[i][0];
775 const char *contend = oplock_results_2[i][1];
776 const char *brokento = oplock_results_2[i][2];
777 const char *granted = oplock_results_2[i][3];
778 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
779 "expecting break to %s(%x) and grant of %s(%x)\n",
780 held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
781 brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
783 ZERO_STRUCT(break_info);
785 /* Grab an oplock. */
786 smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
787 status = smb2_create(tree, mem_ctx, &io);
788 CHECK_STATUS(status, NT_STATUS_OK);
789 h = io.out.file.handle;
790 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
791 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
792 break_info.held_oplock_level = io.out.oplock_level;
794 /* Grab lease. */
795 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
796 status = smb2_create(tree, mem_ctx, &io);
797 CHECK_STATUS(status, NT_STATUS_OK);
798 h2 = io.out.file.handle;
799 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
800 CHECK_LEASE(&io, granted, true, LEASE1);
802 if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
803 CHECK_VAL(break_info.oplock_count, 1);
804 CHECK_VAL(break_info.oplock_failures, 0);
805 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento));
806 break_info.held_oplock_level = break_info.oplock_level;
807 } else {
808 CHECK_VAL(break_info.oplock_count, 0);
809 CHECK_VAL(break_info.oplock_failures, 0);
812 smb2_util_close(tree, h);
813 smb2_util_close(tree, h2);
815 status = smb2_util_unlink(tree, fname);
816 CHECK_STATUS(status, NT_STATUS_OK);
819 done:
820 smb2_util_close(tree, h);
821 smb2_util_close(tree, h2);
823 smb2_util_unlink(tree, fname);
825 talloc_free(mem_ctx);
827 return ret;
830 static bool test_lease_multibreak(struct torture_context *tctx,
831 struct smb2_tree *tree)
833 TALLOC_CTX *mem_ctx = talloc_new(tctx);
834 struct smb2_create io;
835 struct smb2_lease ls;
836 struct smb2_handle h, h2, h3;
837 struct smb2_write w;
838 NTSTATUS status;
839 const char *fname = "lease.dat";
840 bool ret = true;
841 uint32_t caps;
843 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
844 if (!(caps & SMB2_CAP_LEASING)) {
845 torture_skip(tctx, "leases are not supported");
848 tree->session->transport->lease.handler = torture_lease_handler;
849 tree->session->transport->lease.private_data = tree;
850 tree->session->transport->oplock.handler = torture_oplock_handler;
851 tree->session->transport->oplock.private_data = tree;
853 smb2_util_unlink(tree, fname);
855 ZERO_STRUCT(break_info);
857 /* Grab lease, upgrade to RHW .. */
858 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
859 status = smb2_create(tree, mem_ctx, &io);
860 CHECK_STATUS(status, NT_STATUS_OK);
861 h = io.out.file.handle;
862 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
863 CHECK_LEASE(&io, "RH", true, LEASE1);
865 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
866 status = smb2_create(tree, mem_ctx, &io);
867 CHECK_STATUS(status, NT_STATUS_OK);
868 h2 = io.out.file.handle;
869 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
870 CHECK_LEASE(&io, "RHW", true, LEASE1);
872 /* Contend with LEASE2. */
873 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
874 status = smb2_create(tree, mem_ctx, &io);
875 CHECK_STATUS(status, NT_STATUS_OK);
876 h3 = io.out.file.handle;
877 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
878 CHECK_LEASE(&io, "RH", true, LEASE2);
880 /* Verify that we were only sent one break. */
881 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
883 /* Drop LEASE1 / LEASE2 */
884 status = smb2_util_close(tree, h);
885 CHECK_STATUS(status, NT_STATUS_OK);
886 status = smb2_util_close(tree, h2);
887 CHECK_STATUS(status, NT_STATUS_OK);
888 status = smb2_util_close(tree, h3);
889 CHECK_STATUS(status, NT_STATUS_OK);
891 ZERO_STRUCT(break_info);
893 /* Grab an R lease. */
894 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
895 status = smb2_create(tree, mem_ctx, &io);
896 CHECK_STATUS(status, NT_STATUS_OK);
897 h = io.out.file.handle;
898 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
899 CHECK_LEASE(&io, "R", true, LEASE1);
901 /* Grab a level-II oplock. */
902 smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
903 status = smb2_create(tree, mem_ctx, &io);
904 CHECK_STATUS(status, NT_STATUS_OK);
905 h2 = io.out.file.handle;
906 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
907 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
908 break_info.held_oplock_level = io.out.oplock_level;
910 /* Verify no breaks. */
911 CHECK_VAL(break_info.count, 0);
912 CHECK_VAL(break_info.failures, 0);
914 /* Open for truncate, force a break. */
915 smb2_generic_create(&io, NULL, false, fname,
916 NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
917 status = smb2_create(tree, mem_ctx, &io);
918 CHECK_STATUS(status, NT_STATUS_OK);
919 h3 = io.out.file.handle;
920 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
921 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
922 break_info.held_oplock_level = io.out.oplock_level;
924 /* Sleep, use a write to clear the recv queue. */
925 smb_msleep(250);
926 ZERO_STRUCT(w);
927 w.in.file.handle = h3;
928 w.in.offset = 0;
929 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
930 memset(w.in.data.data, 'o', w.in.data.length);
931 status = smb2_write(tree, &w);
932 CHECK_STATUS(status, NT_STATUS_OK);
934 /* Verify one oplock break, one lease break. */
935 CHECK_VAL(break_info.oplock_count, 1);
936 CHECK_VAL(break_info.oplock_failures, 0);
937 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(""));
938 CHECK_BREAK_INFO("R", "", LEASE1);
940 done:
941 smb2_util_close(tree, h);
942 smb2_util_close(tree, h2);
943 smb2_util_close(tree, h3);
945 smb2_util_unlink(tree, fname);
947 talloc_free(mem_ctx);
949 return ret;
952 static bool test_lease_v2_request(struct torture_context *tctx,
953 struct smb2_tree *tree)
955 TALLOC_CTX *mem_ctx = talloc_new(tctx);
956 struct smb2_create io;
957 struct smb2_lease ls;
958 struct smb2_handle h1, h2, h3, h4, h5;
959 struct smb2_write w;
960 NTSTATUS status;
961 const char *fname = "lease.dat";
962 const char *dname = "lease.dir";
963 const char *dnamefname = "lease.dir\\lease.dat";
964 const char *dnamefname2 = "lease.dir\\lease2.dat";
965 bool ret = true;
967 smb2_util_unlink(tree, fname);
968 smb2_deltree(tree, dname);
970 tree->session->transport->lease.handler = torture_lease_handler;
971 tree->session->transport->lease.private_data = tree;
972 tree->session->transport->oplock.handler = torture_oplock_handler;
973 tree->session->transport->oplock.private_data = tree;
975 ZERO_STRUCT(break_info);
977 ZERO_STRUCT(io);
978 smb2_lease_v2_create_share(&io, &ls, false, fname,
979 smb2_util_share_access("RWD"),
980 LEASE1, NULL,
981 smb2_util_lease_state("RHW"),
984 status = smb2_create(tree, mem_ctx, &io);
985 CHECK_STATUS(status, NT_STATUS_OK);
986 h1 = io.out.file.handle;
987 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
988 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0);
990 ZERO_STRUCT(io);
991 smb2_lease_v2_create_share(&io, &ls, true, dname,
992 smb2_util_share_access("RWD"),
993 LEASE2, NULL,
994 smb2_util_lease_state("RHW"),
996 status = smb2_create(tree, mem_ctx, &io);
997 CHECK_STATUS(status, NT_STATUS_OK);
998 h2 = io.out.file.handle;
999 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
1000 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
1002 ZERO_STRUCT(io);
1003 smb2_lease_v2_create_share(&io, &ls, false, dnamefname,
1004 smb2_util_share_access("RWD"),
1005 LEASE3, &LEASE2,
1006 smb2_util_lease_state("RHW"),
1008 status = smb2_create(tree, mem_ctx, &io);
1009 CHECK_STATUS(status, NT_STATUS_OK);
1010 h3 = io.out.file.handle;
1011 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1012 CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
1013 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET);
1015 torture_wait_for_lease_break(tctx);
1016 CHECK_VAL(break_info.count, 0);
1017 CHECK_VAL(break_info.failures, 0);
1019 ZERO_STRUCT(io);
1020 smb2_lease_v2_create_share(&io, &ls, false, dnamefname2,
1021 smb2_util_share_access("RWD"),
1022 LEASE4, NULL,
1023 smb2_util_lease_state("RHW"),
1025 status = smb2_create(tree, mem_ctx, &io);
1026 CHECK_STATUS(status, NT_STATUS_OK);
1027 h4 = io.out.file.handle;
1028 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1029 CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0);
1031 torture_wait_for_lease_break(tctx);
1032 torture_wait_for_lease_break(tctx);
1033 CHECK_BREAK_INFO("RH", "", LEASE2);
1034 torture_wait_for_lease_break(tctx);
1036 ZERO_STRUCT(break_info);
1038 ZERO_STRUCT(io);
1039 smb2_lease_v2_create_share(&io, &ls, true, dname,
1040 smb2_util_share_access("RWD"),
1041 LEASE2, NULL,
1042 smb2_util_lease_state("RHW"),
1044 io.in.create_disposition = NTCREATEX_DISP_OPEN;
1045 status = smb2_create(tree, mem_ctx, &io);
1046 CHECK_STATUS(status, NT_STATUS_OK);
1047 h5 = io.out.file.handle;
1048 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY);
1049 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
1050 smb2_util_close(tree, h5);
1052 ZERO_STRUCT(w);
1053 w.in.file.handle = h4;
1054 w.in.offset = 0;
1055 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1056 memset(w.in.data.data, 'o', w.in.data.length);
1057 status = smb2_write(tree, &w);
1058 CHECK_STATUS(status, NT_STATUS_OK);
1060 smb_msleep(2000);
1061 torture_wait_for_lease_break(tctx);
1062 CHECK_VAL(break_info.count, 0);
1063 CHECK_VAL(break_info.failures, 0);
1065 smb2_util_close(tree, h4);
1066 torture_wait_for_lease_break(tctx);
1067 torture_wait_for_lease_break(tctx);
1068 CHECK_BREAK_INFO("RH", "", LEASE2);
1069 torture_wait_for_lease_break(tctx);
1071 done:
1072 smb2_util_close(tree, h1);
1073 smb2_util_close(tree, h2);
1074 smb2_util_close(tree, h3);
1075 smb2_util_close(tree, h4);
1076 smb2_util_close(tree, h5);
1078 smb2_util_unlink(tree, fname);
1079 smb2_deltree(tree, dname);
1081 talloc_free(mem_ctx);
1083 return ret;
1086 struct torture_suite *torture_smb2_lease_init(void)
1088 struct torture_suite *suite =
1089 torture_suite_create(talloc_autofree_context(), "lease");
1091 torture_suite_add_1smb2_test(suite, "request", test_lease_request);
1092 torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
1093 torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
1094 torture_suite_add_1smb2_test(suite, "break", test_lease_break);
1095 torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
1096 torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
1097 torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
1099 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
1101 return suite;