WHATSNEW: Update release notes.
[Samba/bb.git] / source4 / torture / smb2 / lease.c
blob87526e3f780a171cea301973eacc282af889e667
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 "lib/events/events.h"
24 #include "librpc/gen_ndr/security.h"
25 #include "libcli/smb2/smb2.h"
26 #include "libcli/smb2/smb2_calls.h"
27 #include "torture/torture.h"
28 #include "torture/smb2/proto.h"
30 static inline uint32_t lease(const char *ls) {
31 uint32_t val = 0;
32 int i;
34 for (i = 0; i < strlen(ls); i++) {
35 switch (ls[i]) {
36 case 'R':
37 val |= SMB2_LEASE_READ;
38 break;
39 case 'H':
40 val |= SMB2_LEASE_HANDLE;
41 break;
42 case 'W':
43 val |= SMB2_LEASE_WRITE;
44 break;
48 return val;
51 #define CHECK_VAL(v, correct) do { \
52 if ((v) != (correct)) { \
53 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
54 __location__, #v, (int)(v), (int)(correct)); \
55 ret = false; \
56 }} while (0)
58 #define CHECK_STATUS(status, correct) do { \
59 if (!NT_STATUS_EQUAL(status, correct)) { \
60 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
61 nt_errstr(status), nt_errstr(correct)); \
62 ret = false; \
63 goto done; \
64 }} while (0)
66 static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
67 bool dir, const char *name, uint32_t disposition,
68 uint32_t oplock, uint64_t leasekey,
69 uint32_t leasestate)
71 ZERO_STRUCT(*io);
72 io->in.security_flags = 0x00;
73 io->in.oplock_level = oplock;
74 io->in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
75 io->in.create_flags = 0x00000000;
76 io->in.reserved = 0x00000000;
77 io->in.desired_access = SEC_RIGHTS_FILE_ALL;
78 io->in.file_attributes = FILE_ATTRIBUTE_NORMAL;
79 io->in.share_access = NTCREATEX_SHARE_ACCESS_READ |
80 NTCREATEX_SHARE_ACCESS_WRITE |
81 NTCREATEX_SHARE_ACCESS_DELETE;
82 io->in.create_disposition = disposition;
83 io->in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
84 NTCREATEX_OPTIONS_ASYNC_ALERT |
85 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
86 0x00200000;
87 io->in.fname = name;
89 if (dir) {
90 io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
91 io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
92 io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
93 io->in.create_disposition = NTCREATEX_DISP_CREATE;
96 if (ls) {
97 ZERO_STRUCT(*ls);
98 ls->lease_key.data[0] = leasekey;
99 ls->lease_key.data[1] = ~leasekey;
100 ls->lease_state = leasestate;
101 io->in.lease_request = ls;
105 static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
106 bool dir, const char *name, uint64_t leasekey,
107 uint32_t leasestate)
109 smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
110 SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
113 static void smb2_oplock_create(struct smb2_create *io, const char *name,
114 uint32_t oplock)
116 smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
117 oplock, 0, 0);
120 #define CHECK_CREATED(__io, __created, __attribute) \
121 do { \
122 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
123 CHECK_VAL((__io)->out.alloc_size, 0); \
124 CHECK_VAL((__io)->out.size, 0); \
125 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
126 CHECK_VAL((__io)->out.reserved2, 0); \
127 } while(0)
129 #define CHECK_LEASE(__io, __state, __oplevel, __key) \
130 do { \
131 if (__oplevel) { \
132 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
133 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
134 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
135 CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
136 } else { \
137 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
138 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
139 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
140 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
143 CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \
144 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
145 } while(0) \
147 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
148 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
149 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
151 #define NREQUEST_RESULTS 8
152 static const char *request_results[NREQUEST_RESULTS][2] = {
153 { "", "" },
154 { "R", "R" },
155 { "H", "" },
156 { "W", "" },
157 { "RH", "RH" },
158 { "RW", "RW" },
159 { "HW", "" },
160 { "RHW", "RHW" },
163 static bool test_lease_request(struct torture_context *tctx,
164 struct smb2_tree *tree)
166 TALLOC_CTX *mem_ctx = talloc_new(tctx);
167 struct smb2_create io;
168 struct smb2_lease ls;
169 struct smb2_handle h1, h2;
170 NTSTATUS status;
171 const char *fname = "lease.dat";
172 const char *fname2 = "lease2.dat";
173 const char *sname = "lease.dat:stream";
174 const char *dname = "lease.dir";
175 bool ret = true;
176 int i;
178 smb2_util_unlink(tree, fname);
179 smb2_util_unlink(tree, fname2);
180 smb2_util_rmdir(tree, dname);
182 /* Win7 is happy to grant RHW leases on files. */
183 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
184 status = smb2_create(tree, mem_ctx, &io);
185 CHECK_STATUS(status, NT_STATUS_OK);
186 h1 = io.out.file.handle;
187 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
188 CHECK_LEASE(&io, "RHW", true, LEASE1);
190 /* But will reject leases on directories. */
191 smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
192 status = smb2_create(tree, mem_ctx, &io);
193 CHECK_STATUS(status, NT_STATUS_OK);
194 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
195 CHECK_LEASE(&io, "", false, 0);
196 smb2_util_close(tree, io.out.file.handle);
198 /* Also rejects multiple files leased under the same key. */
199 smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
200 status = smb2_create(tree, mem_ctx, &io);
201 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
203 /* And grants leases on streams (with separate leasekey). */
204 smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
205 status = smb2_create(tree, mem_ctx, &io);
206 h2 = io.out.file.handle;
207 CHECK_STATUS(status, NT_STATUS_OK);
208 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
209 CHECK_LEASE(&io, "RHW", true, LEASE2);
210 smb2_util_close(tree, h2);
212 smb2_util_close(tree, h1);
214 /* Now see what combos are actually granted. */
215 for (i = 0; i < NREQUEST_RESULTS; i++) {
216 torture_comment(tctx, "Requesting lease type %s(%x),"
217 " expecting %s(%x)\n",
218 request_results[i][0], lease(request_results[i][0]),
219 request_results[i][1], lease(request_results[i][1]));
220 smb2_lease_create(&io, &ls, false, fname, LEASE1,
221 lease(request_results[i][0]));
222 status = smb2_create(tree, mem_ctx, &io);
223 h2 = io.out.file.handle;
224 CHECK_STATUS(status, NT_STATUS_OK);
225 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
226 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
227 smb2_util_close(tree, io.out.file.handle);
230 done:
231 smb2_util_close(tree, h1);
232 smb2_util_close(tree, h2);
234 smb2_util_unlink(tree, fname);
235 smb2_util_unlink(tree, fname2);
236 smb2_util_rmdir(tree, dname);
238 talloc_free(mem_ctx);
240 return ret;
243 static bool test_lease_upgrade(struct torture_context *tctx,
244 struct smb2_tree *tree)
246 TALLOC_CTX *mem_ctx = talloc_new(tctx);
247 struct smb2_create io;
248 struct smb2_lease ls;
249 struct smb2_handle h, hnew;
250 NTSTATUS status;
251 const char *fname = "lease.dat";
252 bool ret = true;
254 smb2_util_unlink(tree, fname);
256 /* Grab a RH lease. */
257 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
258 status = smb2_create(tree, mem_ctx, &io);
259 CHECK_STATUS(status, NT_STATUS_OK);
260 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
261 CHECK_LEASE(&io, "RH", true, LEASE1);
262 h = io.out.file.handle;
264 /* Upgrades (sidegrades?) to RW leave us with an RH. */
265 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
266 status = smb2_create(tree, mem_ctx, &io);
267 CHECK_STATUS(status, NT_STATUS_OK);
268 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
269 CHECK_LEASE(&io, "RH", true, LEASE1);
270 hnew = io.out.file.handle;
272 smb2_util_close(tree, hnew);
274 /* Upgrade to RHW lease. */
275 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
276 status = smb2_create(tree, mem_ctx, &io);
277 CHECK_STATUS(status, NT_STATUS_OK);
278 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
279 CHECK_LEASE(&io, "RHW", true, LEASE1);
280 hnew = io.out.file.handle;
282 smb2_util_close(tree, h);
283 h = hnew;
285 /* Attempt to downgrade - original lease state is maintained. */
286 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
287 status = smb2_create(tree, mem_ctx, &io);
288 CHECK_STATUS(status, NT_STATUS_OK);
289 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
290 CHECK_LEASE(&io, "RHW", true, LEASE1);
291 hnew = io.out.file.handle;
293 smb2_util_close(tree, hnew);
295 done:
296 smb2_util_close(tree, h);
297 smb2_util_close(tree, hnew);
299 smb2_util_unlink(tree, fname);
301 talloc_free(mem_ctx);
303 return ret;
306 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
307 do { \
308 CHECK_VAL((__lb)->new_lease_state, lease(__state)); \
309 CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
310 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
311 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
312 } while(0)
314 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
315 do { \
316 CHECK_VAL((__lba)->out.reserved, 0); \
317 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
318 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
319 CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
320 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
321 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
322 } while(0)
324 static struct {
325 struct smb2_lease_break lease_break;
326 struct smb2_lease_break_ack lease_break_ack;
327 int count;
328 int failures;
330 struct smb2_handle oplock_handle;
331 int held_oplock_level;
332 int oplock_level;
333 int oplock_count;
334 int oplock_failures;
335 } break_info;
337 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
338 do { \
339 CHECK_VAL(break_info.failures, 0); \
340 CHECK_VAL(break_info.count, 1); \
341 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
342 (__state), (__key)); \
343 if (break_info.lease_break.break_flags & \
344 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
345 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
346 (__state), (__key)); \
348 } while(0)
350 static void torture_lease_break_callback(struct smb2_request *req)
352 NTSTATUS status;
354 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
355 if (!NT_STATUS_IS_OK(status))
356 break_info.failures++;
358 return;
361 /* a lease break request handler */
362 static bool torture_lease_handler(struct smb2_transport *transport,
363 const struct smb2_lease_break *lb,
364 void *private_data)
366 struct smb2_tree *tree = private_data;
367 struct smb2_lease_break_ack io;
368 struct smb2_request *req;
370 break_info.lease_break = *lb;
371 break_info.count++;
373 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
374 ZERO_STRUCT(io);
375 io.in.lease.lease_key = lb->current_lease.lease_key;
376 io.in.lease.lease_state = lb->new_lease_state;
378 req = smb2_lease_break_ack_send(tree, &io);
379 req->async.fn = torture_lease_break_callback;
380 req->async.private_data = NULL;
383 return true;
387 break_results should be read as "held lease, new lease, hold broken to, new
388 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
389 tries for RW, key1 will be broken to RH (in this case, not broken at all)
390 and key2 will be granted R.
392 Note: break_results only includes things that Win7 will actually grant (see
393 request_results above).
395 #define NBREAK_RESULTS 16
396 static const char *break_results[NBREAK_RESULTS][4] = {
397 {"R", "R", "R", "R"},
398 {"R", "RH", "R", "RH"},
399 {"R", "RW", "R", "R"},
400 {"R", "RHW", "R", "RH"},
402 {"RH", "R", "RH", "R"},
403 {"RH", "RH", "RH", "RH"},
404 {"RH", "RW", "RH", "R"},
405 {"RH", "RHW", "RH", "RH"},
407 {"RW", "R", "R", "R"},
408 {"RW", "RH", "R", "RH"},
409 {"RW", "RW", "R", "R"},
410 {"RW", "RHW", "R", "RH"},
412 {"RHW", "R", "RH", "R"},
413 {"RHW", "RH", "RH", "RH"},
414 {"RHW", "RW", "RH", "R"},
415 {"RHW", "RHW", "RH", "RH"},
418 static bool test_lease_break(struct torture_context *tctx,
419 struct smb2_tree *tree)
421 TALLOC_CTX *mem_ctx = talloc_new(tctx);
422 struct smb2_create io;
423 struct smb2_lease ls;
424 struct smb2_handle h, h2, h3;
425 NTSTATUS status;
426 const char *fname = "lease.dat";
427 bool ret = true;
428 int i;
430 tree->session->transport->lease.handler = torture_lease_handler;
431 tree->session->transport->lease.private_data = tree;
433 smb2_util_unlink(tree, fname);
435 for (i = 0; i < NBREAK_RESULTS; i++) {
436 const char *held = break_results[i][0];
437 const char *contend = break_results[i][1];
438 const char *brokento = break_results[i][2];
439 const char *granted = break_results[i][3];
440 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
441 "expecting break to %s(%x) and grant of %s(%x)\n",
442 held, lease(held), contend, lease(contend),
443 brokento, lease(brokento), granted, lease(granted));
445 ZERO_STRUCT(break_info);
447 /* Grab lease. */
448 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
449 status = smb2_create(tree, mem_ctx, &io);
450 CHECK_STATUS(status, NT_STATUS_OK);
451 h = io.out.file.handle;
452 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
453 CHECK_LEASE(&io, held, true, LEASE1);
455 /* Possibly contend lease. */
456 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
457 status = smb2_create(tree, mem_ctx, &io);
458 CHECK_STATUS(status, NT_STATUS_OK);
459 h2 = io.out.file.handle;
460 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
461 CHECK_LEASE(&io, granted, true, LEASE2);
463 if (lease(held) != lease(brokento)) {
464 CHECK_BREAK_INFO(held, brokento, LEASE1);
465 } else {
466 CHECK_VAL(break_info.count, 0);
467 CHECK_VAL(break_info.failures, 0);
470 ZERO_STRUCT(break_info);
473 Now verify that an attempt to upgrade LEASE1 results in no
474 break and no change in LEASE1.
476 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
477 status = smb2_create(tree, mem_ctx, &io);
478 CHECK_STATUS(status, NT_STATUS_OK);
479 h3 = io.out.file.handle;
480 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
481 CHECK_LEASE(&io, brokento, true, LEASE1);
482 CHECK_VAL(break_info.count, 0);
483 CHECK_VAL(break_info.failures, 0);
485 smb2_util_close(tree, h);
486 smb2_util_close(tree, h2);
487 smb2_util_close(tree, h3);
489 status = smb2_util_unlink(tree, fname);
490 CHECK_STATUS(status, NT_STATUS_OK);
493 done:
494 smb2_util_close(tree, h);
495 smb2_util_close(tree, h2);
497 smb2_util_unlink(tree, fname);
499 talloc_free(mem_ctx);
501 return ret;
504 static void torture_oplock_break_callback(struct smb2_request *req)
506 NTSTATUS status;
507 struct smb2_break br;
509 ZERO_STRUCT(br);
510 status = smb2_break_recv(req, &br);
511 if (!NT_STATUS_IS_OK(status))
512 break_info.oplock_failures++;
514 return;
517 /* a oplock break request handler */
518 static bool torture_oplock_handler(struct smb2_transport *transport,
519 const struct smb2_handle *handle,
520 uint8_t level, void *private_data)
522 struct smb2_tree *tree = private_data;
523 struct smb2_request *req;
524 struct smb2_break br;
526 break_info.oplock_handle = *handle;
527 break_info.oplock_level = level;
528 break_info.oplock_count++;
530 ZERO_STRUCT(br);
531 br.in.file.handle = *handle;
532 br.in.oplock_level = level;
534 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
535 req = smb2_break_send(tree, &br);
536 req->async.fn = torture_oplock_break_callback;
537 req->async.private_data = NULL;
539 break_info.held_oplock_level = level;
541 return true;
544 static inline uint32_t oplock(const char *op) {
545 uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
546 int i;
548 for (i = 0; i < strlen(op); i++) {
549 switch (op[i]) {
550 case 's':
551 return SMB2_OPLOCK_LEVEL_II;
552 case 'x':
553 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
554 case 'b':
555 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
556 default:
557 continue;
561 return val;
564 #define NOPLOCK_RESULTS 12
565 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
566 {"R", "s", "R", "s"},
567 {"R", "x", "R", "s"},
568 {"R", "b", "R", "s"},
570 {"RH", "s", "RH", ""},
571 {"RH", "x", "RH", ""},
572 {"RH", "b", "RH", ""},
574 {"RW", "s", "R", "s"},
575 {"RW", "x", "R", "s"},
576 {"RW", "b", "R", "s"},
578 {"RHW", "s", "RH", ""},
579 {"RHW", "x", "RH", ""},
580 {"RHW", "b", "RH", ""},
583 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
584 {"s", "R", "s", "R"},
585 {"s", "RH", "s", "R"},
586 {"s", "RW", "s", "R"},
587 {"s", "RHW", "s", "R"},
589 {"x", "R", "s", "R"},
590 {"x", "RH", "s", "R"},
591 {"x", "RW", "s", "R"},
592 {"x", "RHW", "s", "R"},
594 {"b", "R", "s", "R"},
595 {"b", "RH", "s", "R"},
596 {"b", "RW", "s", "R"},
597 {"b", "RHW", "s", "R"},
600 static bool test_lease_oplock(struct torture_context *tctx,
601 struct smb2_tree *tree)
603 TALLOC_CTX *mem_ctx = talloc_new(tctx);
604 struct smb2_create io;
605 struct smb2_lease ls;
606 struct smb2_handle h, h2;
607 NTSTATUS status;
608 const char *fname = "lease.dat";
609 bool ret = true;
610 int i;
612 tree->session->transport->lease.handler = torture_lease_handler;
613 tree->session->transport->lease.private_data = tree;
614 tree->session->transport->oplock.handler = torture_oplock_handler;
615 tree->session->transport->oplock.private_data = tree;
617 smb2_util_unlink(tree, fname);
619 for (i = 0; i < NOPLOCK_RESULTS; i++) {
620 const char *held = oplock_results[i][0];
621 const char *contend = oplock_results[i][1];
622 const char *brokento = oplock_results[i][2];
623 const char *granted = oplock_results[i][3];
624 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
625 "expecting break to %s(%x) and grant of %s(%x)\n",
626 held, lease(held), contend, oplock(contend),
627 brokento, lease(brokento), granted, oplock(granted));
629 ZERO_STRUCT(break_info);
631 /* Grab lease. */
632 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
633 status = smb2_create(tree, mem_ctx, &io);
634 CHECK_STATUS(status, NT_STATUS_OK);
635 h = io.out.file.handle;
636 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
637 CHECK_LEASE(&io, held, true, LEASE1);
639 /* Does an oplock contend the lease? */
640 smb2_oplock_create(&io, fname, oplock(contend));
641 status = smb2_create(tree, mem_ctx, &io);
642 CHECK_STATUS(status, NT_STATUS_OK);
643 h2 = io.out.file.handle;
644 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
645 CHECK_VAL(io.out.oplock_level, oplock(granted));
646 break_info.held_oplock_level = io.out.oplock_level;
648 if (lease(held) != lease(brokento)) {
649 CHECK_BREAK_INFO(held, brokento, LEASE1);
650 } else {
651 CHECK_VAL(break_info.count, 0);
652 CHECK_VAL(break_info.failures, 0);
655 smb2_util_close(tree, h);
656 smb2_util_close(tree, h2);
658 status = smb2_util_unlink(tree, fname);
659 CHECK_STATUS(status, NT_STATUS_OK);
662 for (i = 0; i < NOPLOCK_RESULTS; i++) {
663 const char *held = oplock_results_2[i][0];
664 const char *contend = oplock_results_2[i][1];
665 const char *brokento = oplock_results_2[i][2];
666 const char *granted = oplock_results_2[i][3];
667 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
668 "expecting break to %s(%x) and grant of %s(%x)\n",
669 held, oplock(held), contend, lease(contend),
670 brokento, oplock(brokento), granted, lease(granted));
672 ZERO_STRUCT(break_info);
674 /* Grab an oplock. */
675 smb2_oplock_create(&io, fname, oplock(held));
676 status = smb2_create(tree, mem_ctx, &io);
677 CHECK_STATUS(status, NT_STATUS_OK);
678 h = io.out.file.handle;
679 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
680 CHECK_VAL(io.out.oplock_level, oplock(held));
681 break_info.held_oplock_level = io.out.oplock_level;
683 /* Grab lease. */
684 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
685 status = smb2_create(tree, mem_ctx, &io);
686 CHECK_STATUS(status, NT_STATUS_OK);
687 h2 = io.out.file.handle;
688 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
689 CHECK_LEASE(&io, granted, true, LEASE1);
691 if (oplock(held) != oplock(brokento)) {
692 CHECK_VAL(break_info.oplock_count, 1);
693 CHECK_VAL(break_info.oplock_failures, 0);
694 CHECK_VAL(break_info.oplock_level, oplock(brokento));
695 break_info.held_oplock_level = break_info.oplock_level;
696 } else {
697 CHECK_VAL(break_info.oplock_count, 0);
698 CHECK_VAL(break_info.oplock_failures, 0);
701 smb2_util_close(tree, h);
702 smb2_util_close(tree, h2);
704 status = smb2_util_unlink(tree, fname);
705 CHECK_STATUS(status, NT_STATUS_OK);
708 done:
709 smb2_util_close(tree, h);
710 smb2_util_close(tree, h2);
712 smb2_util_unlink(tree, fname);
714 talloc_free(mem_ctx);
716 return ret;
719 static bool test_lease_multibreak(struct torture_context *tctx,
720 struct smb2_tree *tree)
722 TALLOC_CTX *mem_ctx = talloc_new(tctx);
723 struct smb2_create io;
724 struct smb2_lease ls;
725 struct smb2_handle h, h2, h3;
726 struct smb2_write w;
727 NTSTATUS status;
728 const char *fname = "lease.dat";
729 bool ret = true;
731 tree->session->transport->lease.handler = torture_lease_handler;
732 tree->session->transport->lease.private_data = tree;
733 tree->session->transport->oplock.handler = torture_oplock_handler;
734 tree->session->transport->oplock.private_data = tree;
736 smb2_util_unlink(tree, fname);
738 ZERO_STRUCT(break_info);
740 /* Grab lease, upgrade to RHW .. */
741 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
742 status = smb2_create(tree, mem_ctx, &io);
743 CHECK_STATUS(status, NT_STATUS_OK);
744 h = io.out.file.handle;
745 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
746 CHECK_LEASE(&io, "RH", true, LEASE1);
748 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
749 status = smb2_create(tree, mem_ctx, &io);
750 CHECK_STATUS(status, NT_STATUS_OK);
751 h2 = io.out.file.handle;
752 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
753 CHECK_LEASE(&io, "RHW", true, LEASE1);
755 /* Contend with LEASE2. */
756 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
757 status = smb2_create(tree, mem_ctx, &io);
758 CHECK_STATUS(status, NT_STATUS_OK);
759 h3 = io.out.file.handle;
760 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
761 CHECK_LEASE(&io, "RH", true, LEASE2);
763 /* Verify that we were only sent one break. */
764 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
766 /* Drop LEASE1 / LEASE2 */
767 status = smb2_util_close(tree, h);
768 CHECK_STATUS(status, NT_STATUS_OK);
769 status = smb2_util_close(tree, h2);
770 CHECK_STATUS(status, NT_STATUS_OK);
771 status = smb2_util_close(tree, h3);
772 CHECK_STATUS(status, NT_STATUS_OK);
774 ZERO_STRUCT(break_info);
776 /* Grab an R lease. */
777 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
778 status = smb2_create(tree, mem_ctx, &io);
779 CHECK_STATUS(status, NT_STATUS_OK);
780 h = io.out.file.handle;
781 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
782 CHECK_LEASE(&io, "R", true, LEASE1);
784 /* Grab a level-II oplock. */
785 smb2_oplock_create(&io, fname, oplock("s"));
786 status = smb2_create(tree, mem_ctx, &io);
787 CHECK_STATUS(status, NT_STATUS_OK);
788 h2 = io.out.file.handle;
789 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
790 CHECK_VAL(io.out.oplock_level, oplock("s"));
791 break_info.held_oplock_level = io.out.oplock_level;
793 /* Verify no breaks. */
794 CHECK_VAL(break_info.count, 0);
795 CHECK_VAL(break_info.failures, 0);
797 /* Open for truncate, force a break. */
798 smb2_generic_create(&io, NULL, false, fname,
799 NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
800 status = smb2_create(tree, mem_ctx, &io);
801 CHECK_STATUS(status, NT_STATUS_OK);
802 h3 = io.out.file.handle;
803 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
804 CHECK_VAL(io.out.oplock_level, oplock(""));
805 break_info.held_oplock_level = io.out.oplock_level;
807 /* Sleep, use a write to clear the recv queue. */
808 msleep(250);
809 ZERO_STRUCT(w);
810 w.in.file.handle = h3;
811 w.in.offset = 0;
812 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
813 status = smb2_write(tree, &w);
814 CHECK_STATUS(status, NT_STATUS_OK);
816 /* Verify one oplock break, one lease break. */
817 CHECK_VAL(break_info.oplock_count, 1);
818 CHECK_VAL(break_info.oplock_failures, 0);
819 CHECK_VAL(break_info.oplock_level, oplock(""));
820 CHECK_BREAK_INFO("R", "", LEASE1);
822 done:
823 smb2_util_close(tree, h);
824 smb2_util_close(tree, h2);
825 smb2_util_close(tree, h3);
827 smb2_util_unlink(tree, fname);
829 talloc_free(mem_ctx);
831 return ret;
834 struct torture_suite *torture_smb2_lease_init(void)
836 struct torture_suite *suite =
837 torture_suite_create(talloc_autofree_context(), "LEASE");
839 torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request);
840 torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade);
841 torture_suite_add_1smb2_test(suite, "BREAK", test_lease_break);
842 torture_suite_add_1smb2_test(suite, "OPLOCK", test_lease_oplock);
843 torture_suite_add_1smb2_test(suite, "MULTIBREAK", test_lease_multibreak);
845 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
847 return suite;