VERSION: Disable git snapshots for the 4.1.0rc1 release.
[Samba.git] / source4 / torture / smb2 / lease.c
blob992c21b2406ba393fa04aa8a08ea1c87cccd225e
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
266 struct lease_upgrade2_test {
267 const char *initial;
268 const char *upgrade_to;
269 const char *expected;
272 #define NUM_LEASE_TYPES 5
273 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
274 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
275 { "", "", "" },
276 { "", "R", "R" },
277 { "", "RH", "RH" },
278 { "", "RW", "RW" },
279 { "", "RWH", "RWH" },
281 { "R", "", "R" },
282 { "R", "R", "R" },
283 { "R", "RH", "RH" },
284 { "R", "RW", "RW" },
285 { "R", "RWH", "RWH" },
287 { "RH", "", "RH" },
288 { "RH", "R", "RH" },
289 { "RH", "RH", "RH" },
290 { "RH", "RW", "RH" },
291 { "RH", "RWH", "RWH" },
293 { "RW", "", "RW" },
294 { "RW", "R", "RW" },
295 { "RW", "RH", "RW" },
296 { "RW", "RW", "RW" },
297 { "RW", "RWH", "RWH" },
299 { "RWH", "", "RWH" },
300 { "RWH", "R", "RWH" },
301 { "RWH", "RH", "RWH" },
302 { "RWH", "RW", "RWH" },
303 { "RWH", "RWH", "RWH" },
306 static bool test_lease_upgrade2(struct torture_context *tctx,
307 struct smb2_tree *tree)
309 TALLOC_CTX *mem_ctx = talloc_new(tctx);
310 struct smb2_handle h, hnew;
311 NTSTATUS status;
312 struct smb2_create io;
313 struct smb2_lease ls;
314 const char *fname = "lease.dat";
315 bool ret = true;
316 int i;
317 uint32_t caps;
319 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
320 if (!(caps & SMB2_CAP_LEASING)) {
321 torture_skip(tctx, "leases are not supported");
324 for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
325 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
327 smb2_util_unlink(tree, fname);
329 /* Grab a lease. */
330 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
331 status = smb2_create(tree, mem_ctx, &io);
332 CHECK_STATUS(status, NT_STATUS_OK);
333 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
334 CHECK_LEASE(&io, t.initial, true, LEASE1);
335 h = io.out.file.handle;
337 /* Upgrade. */
338 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
339 status = smb2_create(tree, mem_ctx, &io);
340 CHECK_STATUS(status, NT_STATUS_OK);
341 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
342 CHECK_LEASE(&io, t.expected, true, LEASE1);
343 hnew = io.out.file.handle;
345 smb2_util_close(tree, hnew);
346 smb2_util_close(tree, h);
349 done:
350 smb2_util_close(tree, h);
351 smb2_util_close(tree, hnew);
353 smb2_util_unlink(tree, fname);
355 talloc_free(mem_ctx);
357 return ret;
361 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
362 do { \
363 CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state)); \
364 CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \
365 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
366 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
367 } while(0)
369 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
370 do { \
371 CHECK_VAL((__lba)->out.reserved, 0); \
372 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
373 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
374 CHECK_VAL((__lba)->out.lease.lease_state, smb2_util_lease_state(__state)); \
375 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
376 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
377 } while(0)
379 static struct {
380 struct smb2_lease_break lease_break;
381 struct smb2_lease_break_ack lease_break_ack;
382 int count;
383 int failures;
385 struct smb2_handle oplock_handle;
386 uint8_t held_oplock_level;
387 uint8_t oplock_level;
388 int oplock_count;
389 int oplock_failures;
390 } break_info;
392 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
393 do { \
394 CHECK_VAL(break_info.failures, 0); \
395 CHECK_VAL(break_info.count, 1); \
396 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
397 (__state), (__key)); \
398 if (break_info.lease_break.break_flags & \
399 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
400 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
401 (__state), (__key)); \
403 } while(0)
405 static void torture_lease_break_callback(struct smb2_request *req)
407 NTSTATUS status;
409 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
410 if (!NT_STATUS_IS_OK(status))
411 break_info.failures++;
413 return;
416 /* a lease break request handler */
417 static bool torture_lease_handler(struct smb2_transport *transport,
418 const struct smb2_lease_break *lb,
419 void *private_data)
421 struct smb2_tree *tree = private_data;
422 struct smb2_lease_break_ack io;
423 struct smb2_request *req;
425 break_info.lease_break = *lb;
426 break_info.count++;
428 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
429 ZERO_STRUCT(io);
430 io.in.lease.lease_key = lb->current_lease.lease_key;
431 io.in.lease.lease_state = lb->new_lease_state;
433 req = smb2_lease_break_ack_send(tree, &io);
434 req->async.fn = torture_lease_break_callback;
435 req->async.private_data = NULL;
438 return true;
442 Timer handler function notifies the registering function that time is up
444 static void timeout_cb(struct tevent_context *ev,
445 struct tevent_timer *te,
446 struct timeval current_time,
447 void *private_data)
449 bool *timesup = (bool *)private_data;
450 *timesup = true;
451 return;
455 Wait a short period of time to receive a single oplock break request
457 static void torture_wait_for_lease_break(struct torture_context *tctx)
459 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
460 struct tevent_timer *te = NULL;
461 struct timeval ne;
462 bool timesup = false;
463 int old_count = break_info.count;
465 /* Wait .1 seconds for an lease break */
466 ne = tevent_timeval_current_ofs(0, 100000);
468 te = tevent_add_timer(tctx->ev, tmp_ctx, ne, timeout_cb, &timesup);
469 if (te == NULL) {
470 torture_comment(tctx, "Failed to wait for an oplock break. "
471 "test results may not be accurate.");
472 goto done;
475 while (!timesup && break_info.count < old_count + 1) {
476 if (tevent_loop_once(tctx->ev) != 0) {
477 torture_comment(tctx, "Failed to wait for an oplock "
478 "break. test results may not be "
479 "accurate.");
480 goto done;
484 done:
485 /* We don't know if the timed event fired and was freed, we received
486 * our oplock break, or some other event triggered the loop. Thus,
487 * we create a tmp_ctx to be able to safely free/remove the timed
488 * event in all 3 cases. */
489 talloc_free(tmp_ctx);
491 return;
495 break_results should be read as "held lease, new lease, hold broken to, new
496 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
497 tries for RW, key1 will be broken to RH (in this case, not broken at all)
498 and key2 will be granted R.
500 Note: break_results only includes things that Win7 will actually grant (see
501 request_results above).
503 #define NBREAK_RESULTS 16
504 static const char *break_results[NBREAK_RESULTS][4] = {
505 {"R", "R", "R", "R"},
506 {"R", "RH", "R", "RH"},
507 {"R", "RW", "R", "R"},
508 {"R", "RHW", "R", "RH"},
510 {"RH", "R", "RH", "R"},
511 {"RH", "RH", "RH", "RH"},
512 {"RH", "RW", "RH", "R"},
513 {"RH", "RHW", "RH", "RH"},
515 {"RW", "R", "R", "R"},
516 {"RW", "RH", "R", "RH"},
517 {"RW", "RW", "R", "R"},
518 {"RW", "RHW", "R", "RH"},
520 {"RHW", "R", "RH", "R"},
521 {"RHW", "RH", "RH", "RH"},
522 {"RHW", "RW", "RH", "R"},
523 {"RHW", "RHW", "RH", "RH"},
526 static bool test_lease_break(struct torture_context *tctx,
527 struct smb2_tree *tree)
529 TALLOC_CTX *mem_ctx = talloc_new(tctx);
530 struct smb2_create io;
531 struct smb2_lease ls;
532 struct smb2_handle h, h2, h3;
533 NTSTATUS status;
534 const char *fname = "lease.dat";
535 bool ret = true;
536 int i;
537 uint32_t caps;
539 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
540 if (!(caps & SMB2_CAP_LEASING)) {
541 torture_skip(tctx, "leases are not supported");
544 tree->session->transport->lease.handler = torture_lease_handler;
545 tree->session->transport->lease.private_data = tree;
547 smb2_util_unlink(tree, fname);
549 for (i = 0; i < NBREAK_RESULTS; i++) {
550 const char *held = break_results[i][0];
551 const char *contend = break_results[i][1];
552 const char *brokento = break_results[i][2];
553 const char *granted = break_results[i][3];
554 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
555 "expecting break to %s(%x) and grant of %s(%x)\n",
556 held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
557 brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
559 ZERO_STRUCT(break_info);
561 /* Grab lease. */
562 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
563 status = smb2_create(tree, mem_ctx, &io);
564 CHECK_STATUS(status, NT_STATUS_OK);
565 h = io.out.file.handle;
566 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
567 CHECK_LEASE(&io, held, true, LEASE1);
569 /* Possibly contend lease. */
570 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
571 status = smb2_create(tree, mem_ctx, &io);
572 CHECK_STATUS(status, NT_STATUS_OK);
573 h2 = io.out.file.handle;
574 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
575 CHECK_LEASE(&io, granted, true, LEASE2);
577 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
578 CHECK_BREAK_INFO(held, brokento, LEASE1);
579 } else {
580 CHECK_VAL(break_info.count, 0);
581 CHECK_VAL(break_info.failures, 0);
584 ZERO_STRUCT(break_info);
587 Now verify that an attempt to upgrade LEASE1 results in no
588 break and no change in LEASE1.
590 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
591 status = smb2_create(tree, mem_ctx, &io);
592 CHECK_STATUS(status, NT_STATUS_OK);
593 h3 = io.out.file.handle;
594 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
595 CHECK_LEASE(&io, brokento, true, LEASE1);
596 CHECK_VAL(break_info.count, 0);
597 CHECK_VAL(break_info.failures, 0);
599 smb2_util_close(tree, h);
600 smb2_util_close(tree, h2);
601 smb2_util_close(tree, h3);
603 status = smb2_util_unlink(tree, fname);
604 CHECK_STATUS(status, NT_STATUS_OK);
607 done:
608 smb2_util_close(tree, h);
609 smb2_util_close(tree, h2);
611 smb2_util_unlink(tree, fname);
613 talloc_free(mem_ctx);
615 return ret;
618 static void torture_oplock_break_callback(struct smb2_request *req)
620 NTSTATUS status;
621 struct smb2_break br;
623 ZERO_STRUCT(br);
624 status = smb2_break_recv(req, &br);
625 if (!NT_STATUS_IS_OK(status))
626 break_info.oplock_failures++;
628 return;
631 /* a oplock break request handler */
632 static bool torture_oplock_handler(struct smb2_transport *transport,
633 const struct smb2_handle *handle,
634 uint8_t level, void *private_data)
636 struct smb2_tree *tree = private_data;
637 struct smb2_request *req;
638 struct smb2_break br;
640 break_info.oplock_handle = *handle;
641 break_info.oplock_level = level;
642 break_info.oplock_count++;
644 ZERO_STRUCT(br);
645 br.in.file.handle = *handle;
646 br.in.oplock_level = level;
648 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
649 req = smb2_break_send(tree, &br);
650 req->async.fn = torture_oplock_break_callback;
651 req->async.private_data = NULL;
653 break_info.held_oplock_level = level;
655 return true;
658 #define NOPLOCK_RESULTS 12
659 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
660 {"R", "s", "R", "s"},
661 {"R", "x", "R", "s"},
662 {"R", "b", "R", "s"},
664 {"RH", "s", "RH", ""},
665 {"RH", "x", "RH", ""},
666 {"RH", "b", "RH", ""},
668 {"RW", "s", "R", "s"},
669 {"RW", "x", "R", "s"},
670 {"RW", "b", "R", "s"},
672 {"RHW", "s", "RH", ""},
673 {"RHW", "x", "RH", ""},
674 {"RHW", "b", "RH", ""},
677 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
678 {"s", "R", "s", "R"},
679 {"s", "RH", "s", "R"},
680 {"s", "RW", "s", "R"},
681 {"s", "RHW", "s", "R"},
683 {"x", "R", "s", "R"},
684 {"x", "RH", "s", "R"},
685 {"x", "RW", "s", "R"},
686 {"x", "RHW", "s", "R"},
688 {"b", "R", "s", "R"},
689 {"b", "RH", "s", "R"},
690 {"b", "RW", "s", "R"},
691 {"b", "RHW", "s", "R"},
694 static bool test_lease_oplock(struct torture_context *tctx,
695 struct smb2_tree *tree)
697 TALLOC_CTX *mem_ctx = talloc_new(tctx);
698 struct smb2_create io;
699 struct smb2_lease ls;
700 struct smb2_handle h, h2;
701 NTSTATUS status;
702 const char *fname = "lease.dat";
703 bool ret = true;
704 int i;
705 uint32_t caps;
707 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
708 if (!(caps & SMB2_CAP_LEASING)) {
709 torture_skip(tctx, "leases are not supported");
712 tree->session->transport->lease.handler = torture_lease_handler;
713 tree->session->transport->lease.private_data = tree;
714 tree->session->transport->oplock.handler = torture_oplock_handler;
715 tree->session->transport->oplock.private_data = tree;
717 smb2_util_unlink(tree, fname);
719 for (i = 0; i < NOPLOCK_RESULTS; i++) {
720 const char *held = oplock_results[i][0];
721 const char *contend = oplock_results[i][1];
722 const char *brokento = oplock_results[i][2];
723 const char *granted = oplock_results[i][3];
724 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
725 "expecting break to %s(%x) and grant of %s(%x)\n",
726 held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
727 brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
729 ZERO_STRUCT(break_info);
731 /* Grab lease. */
732 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
733 status = smb2_create(tree, mem_ctx, &io);
734 CHECK_STATUS(status, NT_STATUS_OK);
735 h = io.out.file.handle;
736 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
737 CHECK_LEASE(&io, held, true, LEASE1);
739 /* Does an oplock contend the lease? */
740 smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
741 status = smb2_create(tree, mem_ctx, &io);
742 CHECK_STATUS(status, NT_STATUS_OK);
743 h2 = io.out.file.handle;
744 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
745 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
746 break_info.held_oplock_level = io.out.oplock_level;
748 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
749 CHECK_BREAK_INFO(held, brokento, LEASE1);
750 } else {
751 CHECK_VAL(break_info.count, 0);
752 CHECK_VAL(break_info.failures, 0);
755 smb2_util_close(tree, h);
756 smb2_util_close(tree, h2);
758 status = smb2_util_unlink(tree, fname);
759 CHECK_STATUS(status, NT_STATUS_OK);
762 for (i = 0; i < NOPLOCK_RESULTS; i++) {
763 const char *held = oplock_results_2[i][0];
764 const char *contend = oplock_results_2[i][1];
765 const char *brokento = oplock_results_2[i][2];
766 const char *granted = oplock_results_2[i][3];
767 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
768 "expecting break to %s(%x) and grant of %s(%x)\n",
769 held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
770 brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
772 ZERO_STRUCT(break_info);
774 /* Grab an oplock. */
775 smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
776 status = smb2_create(tree, mem_ctx, &io);
777 CHECK_STATUS(status, NT_STATUS_OK);
778 h = io.out.file.handle;
779 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
780 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
781 break_info.held_oplock_level = io.out.oplock_level;
783 /* Grab lease. */
784 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
785 status = smb2_create(tree, mem_ctx, &io);
786 CHECK_STATUS(status, NT_STATUS_OK);
787 h2 = io.out.file.handle;
788 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
789 CHECK_LEASE(&io, granted, true, LEASE1);
791 if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
792 CHECK_VAL(break_info.oplock_count, 1);
793 CHECK_VAL(break_info.oplock_failures, 0);
794 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento));
795 break_info.held_oplock_level = break_info.oplock_level;
796 } else {
797 CHECK_VAL(break_info.oplock_count, 0);
798 CHECK_VAL(break_info.oplock_failures, 0);
801 smb2_util_close(tree, h);
802 smb2_util_close(tree, h2);
804 status = smb2_util_unlink(tree, fname);
805 CHECK_STATUS(status, NT_STATUS_OK);
808 done:
809 smb2_util_close(tree, h);
810 smb2_util_close(tree, h2);
812 smb2_util_unlink(tree, fname);
814 talloc_free(mem_ctx);
816 return ret;
819 static bool test_lease_multibreak(struct torture_context *tctx,
820 struct smb2_tree *tree)
822 TALLOC_CTX *mem_ctx = talloc_new(tctx);
823 struct smb2_create io;
824 struct smb2_lease ls;
825 struct smb2_handle h, h2, h3;
826 struct smb2_write w;
827 NTSTATUS status;
828 const char *fname = "lease.dat";
829 bool ret = true;
830 uint32_t caps;
832 caps = smb2cli_conn_server_capabilities(tree->session->transport->conn);
833 if (!(caps & SMB2_CAP_LEASING)) {
834 torture_skip(tctx, "leases are not supported");
837 tree->session->transport->lease.handler = torture_lease_handler;
838 tree->session->transport->lease.private_data = tree;
839 tree->session->transport->oplock.handler = torture_oplock_handler;
840 tree->session->transport->oplock.private_data = tree;
842 smb2_util_unlink(tree, fname);
844 ZERO_STRUCT(break_info);
846 /* Grab lease, upgrade to RHW .. */
847 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
848 status = smb2_create(tree, mem_ctx, &io);
849 CHECK_STATUS(status, NT_STATUS_OK);
850 h = io.out.file.handle;
851 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
852 CHECK_LEASE(&io, "RH", true, LEASE1);
854 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
855 status = smb2_create(tree, mem_ctx, &io);
856 CHECK_STATUS(status, NT_STATUS_OK);
857 h2 = io.out.file.handle;
858 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
859 CHECK_LEASE(&io, "RHW", true, LEASE1);
861 /* Contend with LEASE2. */
862 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
863 status = smb2_create(tree, mem_ctx, &io);
864 CHECK_STATUS(status, NT_STATUS_OK);
865 h3 = io.out.file.handle;
866 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
867 CHECK_LEASE(&io, "RH", true, LEASE2);
869 /* Verify that we were only sent one break. */
870 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
872 /* Drop LEASE1 / LEASE2 */
873 status = smb2_util_close(tree, h);
874 CHECK_STATUS(status, NT_STATUS_OK);
875 status = smb2_util_close(tree, h2);
876 CHECK_STATUS(status, NT_STATUS_OK);
877 status = smb2_util_close(tree, h3);
878 CHECK_STATUS(status, NT_STATUS_OK);
880 ZERO_STRUCT(break_info);
882 /* Grab an R lease. */
883 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
884 status = smb2_create(tree, mem_ctx, &io);
885 CHECK_STATUS(status, NT_STATUS_OK);
886 h = io.out.file.handle;
887 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
888 CHECK_LEASE(&io, "R", true, LEASE1);
890 /* Grab a level-II oplock. */
891 smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
892 status = smb2_create(tree, mem_ctx, &io);
893 CHECK_STATUS(status, NT_STATUS_OK);
894 h2 = io.out.file.handle;
895 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
896 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
897 break_info.held_oplock_level = io.out.oplock_level;
899 /* Verify no breaks. */
900 CHECK_VAL(break_info.count, 0);
901 CHECK_VAL(break_info.failures, 0);
903 /* Open for truncate, force a break. */
904 smb2_generic_create(&io, NULL, false, fname,
905 NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
906 status = smb2_create(tree, mem_ctx, &io);
907 CHECK_STATUS(status, NT_STATUS_OK);
908 h3 = io.out.file.handle;
909 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
910 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
911 break_info.held_oplock_level = io.out.oplock_level;
913 /* Sleep, use a write to clear the recv queue. */
914 smb_msleep(250);
915 ZERO_STRUCT(w);
916 w.in.file.handle = h3;
917 w.in.offset = 0;
918 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
919 memset(w.in.data.data, 'o', w.in.data.length);
920 status = smb2_write(tree, &w);
921 CHECK_STATUS(status, NT_STATUS_OK);
923 /* Verify one oplock break, one lease break. */
924 CHECK_VAL(break_info.oplock_count, 1);
925 CHECK_VAL(break_info.oplock_failures, 0);
926 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(""));
927 CHECK_BREAK_INFO("R", "", LEASE1);
929 done:
930 smb2_util_close(tree, h);
931 smb2_util_close(tree, h2);
932 smb2_util_close(tree, h3);
934 smb2_util_unlink(tree, fname);
936 talloc_free(mem_ctx);
938 return ret;
941 static bool test_lease_v2_request(struct torture_context *tctx,
942 struct smb2_tree *tree)
944 TALLOC_CTX *mem_ctx = talloc_new(tctx);
945 struct smb2_create io;
946 struct smb2_lease ls;
947 struct smb2_handle h1, h2, h3, h4, h5;
948 struct smb2_write w;
949 NTSTATUS status;
950 const char *fname = "lease.dat";
951 const char *dname = "lease.dir";
952 const char *dnamefname = "lease.dir\\lease.dat";
953 const char *dnamefname2 = "lease.dir\\lease2.dat";
954 bool ret = true;
956 smb2_util_unlink(tree, fname);
957 smb2_deltree(tree, dname);
959 tree->session->transport->lease.handler = torture_lease_handler;
960 tree->session->transport->lease.private_data = tree;
961 tree->session->transport->oplock.handler = torture_oplock_handler;
962 tree->session->transport->oplock.private_data = tree;
964 ZERO_STRUCT(break_info);
966 ZERO_STRUCT(io);
967 smb2_lease_v2_create_share(&io, &ls, false, fname,
968 smb2_util_share_access("RWD"),
969 LEASE1, NULL,
970 smb2_util_lease_state("RHW"),
973 status = smb2_create(tree, mem_ctx, &io);
974 CHECK_STATUS(status, NT_STATUS_OK);
975 h1 = io.out.file.handle;
976 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
977 CHECK_LEASE_V2(&io, "RHW", true, LEASE1, 0);
979 ZERO_STRUCT(io);
980 smb2_lease_v2_create_share(&io, &ls, true, dname,
981 smb2_util_share_access("RWD"),
982 LEASE2, NULL,
983 smb2_util_lease_state("RHW"),
985 status = smb2_create(tree, mem_ctx, &io);
986 CHECK_STATUS(status, NT_STATUS_OK);
987 h2 = io.out.file.handle;
988 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
989 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
991 ZERO_STRUCT(io);
992 smb2_lease_v2_create_share(&io, &ls, false, dnamefname,
993 smb2_util_share_access("RWD"),
994 LEASE3, &LEASE2,
995 smb2_util_lease_state("RHW"),
997 status = smb2_create(tree, mem_ctx, &io);
998 CHECK_STATUS(status, NT_STATUS_OK);
999 h3 = io.out.file.handle;
1000 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1001 CHECK_LEASE_V2(&io, "RHW", true, LEASE3,
1002 SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET);
1004 torture_wait_for_lease_break(tctx);
1005 CHECK_VAL(break_info.count, 0);
1006 CHECK_VAL(break_info.failures, 0);
1008 ZERO_STRUCT(io);
1009 smb2_lease_v2_create_share(&io, &ls, false, dnamefname2,
1010 smb2_util_share_access("RWD"),
1011 LEASE4, NULL,
1012 smb2_util_lease_state("RHW"),
1014 status = smb2_create(tree, mem_ctx, &io);
1015 CHECK_STATUS(status, NT_STATUS_OK);
1016 h4 = io.out.file.handle;
1017 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
1018 CHECK_LEASE_V2(&io, "RHW", true, LEASE4, 0);
1020 torture_wait_for_lease_break(tctx);
1021 torture_wait_for_lease_break(tctx);
1022 CHECK_BREAK_INFO("RH", "", LEASE2);
1023 torture_wait_for_lease_break(tctx);
1025 ZERO_STRUCT(break_info);
1027 ZERO_STRUCT(io);
1028 smb2_lease_v2_create_share(&io, &ls, true, dname,
1029 smb2_util_share_access("RWD"),
1030 LEASE2, NULL,
1031 smb2_util_lease_state("RHW"),
1033 io.in.create_disposition = NTCREATEX_DISP_OPEN;
1034 status = smb2_create(tree, mem_ctx, &io);
1035 CHECK_STATUS(status, NT_STATUS_OK);
1036 h5 = io.out.file.handle;
1037 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_DIRECTORY);
1038 CHECK_LEASE_V2(&io, "RH", true, LEASE2, 0);
1039 smb2_util_close(tree, h5);
1041 ZERO_STRUCT(w);
1042 w.in.file.handle = h4;
1043 w.in.offset = 0;
1044 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
1045 memset(w.in.data.data, 'o', w.in.data.length);
1046 status = smb2_write(tree, &w);
1047 CHECK_STATUS(status, NT_STATUS_OK);
1049 smb_msleep(2000);
1050 torture_wait_for_lease_break(tctx);
1051 CHECK_VAL(break_info.count, 0);
1052 CHECK_VAL(break_info.failures, 0);
1054 smb2_util_close(tree, h4);
1055 torture_wait_for_lease_break(tctx);
1056 torture_wait_for_lease_break(tctx);
1057 CHECK_BREAK_INFO("RH", "", LEASE2);
1058 torture_wait_for_lease_break(tctx);
1060 done:
1061 smb2_util_close(tree, h1);
1062 smb2_util_close(tree, h2);
1063 smb2_util_close(tree, h3);
1064 smb2_util_close(tree, h4);
1065 smb2_util_close(tree, h5);
1067 smb2_util_unlink(tree, fname);
1068 smb2_deltree(tree, dname);
1070 talloc_free(mem_ctx);
1072 return ret;
1075 struct torture_suite *torture_smb2_lease_init(void)
1077 struct torture_suite *suite =
1078 torture_suite_create(talloc_autofree_context(), "lease");
1080 torture_suite_add_1smb2_test(suite, "request", test_lease_request);
1081 torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
1082 torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
1083 torture_suite_add_1smb2_test(suite, "break", test_lease_break);
1084 torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
1085 torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
1086 torture_suite_add_1smb2_test(suite, "v2_request", test_lease_v2_request);
1088 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
1090 return suite;