s4:torture:smb2: lease.update2: some more reasonable struct names and a comment
[Samba/wip.git] / source4 / torture / smb2 / lease.c
blobfc1f7c7d247be99b2d28b527fedbf6f2edc2b9e3
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 "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/smb2/proto.h"
28 static inline uint32_t lease(const char *ls) {
29 uint32_t val = 0;
30 int i;
32 for (i = 0; i < strlen(ls); i++) {
33 switch (ls[i]) {
34 case 'R':
35 val |= SMB2_LEASE_READ;
36 break;
37 case 'H':
38 val |= SMB2_LEASE_HANDLE;
39 break;
40 case 'W':
41 val |= SMB2_LEASE_WRITE;
42 break;
46 return val;
49 #define CHECK_VAL(v, correct) do { \
50 if ((v) != (correct)) { \
51 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
52 __location__, #v, (int)(v), (int)(correct)); \
53 ret = false; \
54 }} while (0)
56 #define CHECK_STATUS(status, correct) do { \
57 if (!NT_STATUS_EQUAL(status, correct)) { \
58 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
59 nt_errstr(status), nt_errstr(correct)); \
60 ret = false; \
61 goto done; \
62 }} while (0)
64 static void smb2_generic_create(struct smb2_create *io, struct smb2_lease *ls,
65 bool dir, const char *name, uint32_t disposition,
66 uint32_t oplock, uint64_t leasekey,
67 uint32_t leasestate)
69 ZERO_STRUCT(*io);
70 io->in.security_flags = 0x00;
71 io->in.oplock_level = oplock;
72 io->in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
73 io->in.create_flags = 0x00000000;
74 io->in.reserved = 0x00000000;
75 io->in.desired_access = SEC_RIGHTS_FILE_ALL;
76 io->in.file_attributes = FILE_ATTRIBUTE_NORMAL;
77 io->in.share_access = NTCREATEX_SHARE_ACCESS_READ |
78 NTCREATEX_SHARE_ACCESS_WRITE |
79 NTCREATEX_SHARE_ACCESS_DELETE;
80 io->in.create_disposition = disposition;
81 io->in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
82 NTCREATEX_OPTIONS_ASYNC_ALERT |
83 NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
84 0x00200000;
85 io->in.fname = name;
87 if (dir) {
88 io->in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
89 io->in.share_access &= ~NTCREATEX_SHARE_ACCESS_DELETE;
90 io->in.file_attributes = FILE_ATTRIBUTE_DIRECTORY;
91 io->in.create_disposition = NTCREATEX_DISP_CREATE;
94 if (ls) {
95 ZERO_STRUCT(*ls);
96 ls->lease_key.data[0] = leasekey;
97 ls->lease_key.data[1] = ~leasekey;
98 ls->lease_state = leasestate;
99 io->in.lease_request = ls;
103 static void smb2_lease_create(struct smb2_create *io, struct smb2_lease *ls,
104 bool dir, const char *name, uint64_t leasekey,
105 uint32_t leasestate)
107 smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF,
108 SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate);
111 static void smb2_oplock_create(struct smb2_create *io, const char *name,
112 uint32_t oplock)
114 smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF,
115 oplock, 0, 0);
118 #define CHECK_CREATED(__io, __created, __attribute) \
119 do { \
120 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
121 CHECK_VAL((__io)->out.alloc_size, 0); \
122 CHECK_VAL((__io)->out.size, 0); \
123 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
124 CHECK_VAL((__io)->out.reserved2, 0); \
125 } while(0)
127 #define CHECK_LEASE(__io, __state, __oplevel, __key) \
128 do { \
129 if (__oplevel) { \
130 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
131 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
132 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
133 CHECK_VAL((__io)->out.lease_response.lease_state, lease(__state)); \
134 } else { \
135 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
136 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
137 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
138 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
141 CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \
142 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
143 } while(0) \
145 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
146 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
147 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
149 #define NREQUEST_RESULTS 8
150 static const char *request_results[NREQUEST_RESULTS][2] = {
151 { "", "" },
152 { "R", "R" },
153 { "H", "" },
154 { "W", "" },
155 { "RH", "RH" },
156 { "RW", "RW" },
157 { "HW", "" },
158 { "RHW", "RHW" },
161 static bool test_lease_request(struct torture_context *tctx,
162 struct smb2_tree *tree)
164 TALLOC_CTX *mem_ctx = talloc_new(tctx);
165 struct smb2_create io;
166 struct smb2_lease ls;
167 struct smb2_handle h1, h2;
168 NTSTATUS status;
169 const char *fname = "lease.dat";
170 const char *fname2 = "lease2.dat";
171 const char *sname = "lease.dat:stream";
172 const char *dname = "lease.dir";
173 bool ret = true;
174 int i;
176 smb2_util_unlink(tree, fname);
177 smb2_util_unlink(tree, fname2);
178 smb2_util_rmdir(tree, dname);
180 /* Win7 is happy to grant RHW leases on files. */
181 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
182 status = smb2_create(tree, mem_ctx, &io);
183 CHECK_STATUS(status, NT_STATUS_OK);
184 h1 = io.out.file.handle;
185 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
186 CHECK_LEASE(&io, "RHW", true, LEASE1);
188 /* But will reject leases on directories. */
189 smb2_lease_create(&io, &ls, true, dname, LEASE2, lease("RHW"));
190 status = smb2_create(tree, mem_ctx, &io);
191 CHECK_STATUS(status, NT_STATUS_OK);
192 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
193 CHECK_LEASE(&io, "", false, 0);
194 smb2_util_close(tree, io.out.file.handle);
196 /* Also rejects multiple files leased under the same key. */
197 smb2_lease_create(&io, &ls, true, fname2, LEASE1, lease("RHW"));
198 status = smb2_create(tree, mem_ctx, &io);
199 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
201 /* And grants leases on streams (with separate leasekey). */
202 smb2_lease_create(&io, &ls, false, sname, LEASE2, lease("RHW"));
203 status = smb2_create(tree, mem_ctx, &io);
204 h2 = io.out.file.handle;
205 CHECK_STATUS(status, NT_STATUS_OK);
206 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
207 CHECK_LEASE(&io, "RHW", true, LEASE2);
208 smb2_util_close(tree, h2);
210 smb2_util_close(tree, h1);
212 /* Now see what combos are actually granted. */
213 for (i = 0; i < NREQUEST_RESULTS; i++) {
214 torture_comment(tctx, "Requesting lease type %s(%x),"
215 " expecting %s(%x)\n",
216 request_results[i][0], lease(request_results[i][0]),
217 request_results[i][1], lease(request_results[i][1]));
218 smb2_lease_create(&io, &ls, false, fname, LEASE1,
219 lease(request_results[i][0]));
220 status = smb2_create(tree, mem_ctx, &io);
221 h2 = io.out.file.handle;
222 CHECK_STATUS(status, NT_STATUS_OK);
223 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
224 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
225 smb2_util_close(tree, io.out.file.handle);
228 done:
229 smb2_util_close(tree, h1);
230 smb2_util_close(tree, h2);
232 smb2_util_unlink(tree, fname);
233 smb2_util_unlink(tree, fname2);
234 smb2_util_rmdir(tree, dname);
236 talloc_free(mem_ctx);
238 return ret;
241 static bool test_lease_upgrade(struct torture_context *tctx,
242 struct smb2_tree *tree)
244 TALLOC_CTX *mem_ctx = talloc_new(tctx);
245 struct smb2_create io;
246 struct smb2_lease ls;
247 struct smb2_handle h, hnew;
248 NTSTATUS status;
249 const char *fname = "lease.dat";
250 bool ret = true;
252 smb2_util_unlink(tree, fname);
254 /* Grab a RH lease. */
255 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
256 status = smb2_create(tree, mem_ctx, &io);
257 CHECK_STATUS(status, NT_STATUS_OK);
258 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
259 CHECK_LEASE(&io, "RH", true, LEASE1);
260 h = io.out.file.handle;
262 /* Upgrades (sidegrades?) to RW leave us with an RH. */
263 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RW"));
264 status = smb2_create(tree, mem_ctx, &io);
265 CHECK_STATUS(status, NT_STATUS_OK);
266 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
267 CHECK_LEASE(&io, "RH", true, LEASE1);
268 hnew = io.out.file.handle;
270 smb2_util_close(tree, hnew);
272 /* Upgrade to RHW lease. */
273 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
274 status = smb2_create(tree, mem_ctx, &io);
275 CHECK_STATUS(status, NT_STATUS_OK);
276 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
277 CHECK_LEASE(&io, "RHW", true, LEASE1);
278 hnew = io.out.file.handle;
280 smb2_util_close(tree, h);
281 h = hnew;
283 /* Attempt to downgrade - original lease state is maintained. */
284 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
285 status = smb2_create(tree, mem_ctx, &io);
286 CHECK_STATUS(status, NT_STATUS_OK);
287 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
288 CHECK_LEASE(&io, "RHW", true, LEASE1);
289 hnew = io.out.file.handle;
291 smb2_util_close(tree, hnew);
293 done:
294 smb2_util_close(tree, h);
295 smb2_util_close(tree, hnew);
297 smb2_util_unlink(tree, fname);
299 talloc_free(mem_ctx);
301 return ret;
305 * upgrade2 test.
306 * full matrix of lease upgrade combinations
308 struct lease_upgrade2_test {
309 const char *initial;
310 const char *upgrade_to;
311 const char *expected;
314 #define NUM_LEASE_TYPES 5
315 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
316 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
317 { "", "", "" },
318 { "", "R", "R" },
319 { "", "RH", "RH" },
320 { "", "RW", "RW" },
321 { "", "RWH", "RWH" },
323 { "R", "", "R" },
324 { "R", "R", "R" },
325 { "R", "RH", "RH" },
326 { "R", "RW", "RW" },
327 { "R", "RWH", "RWH" },
329 { "RH", "", "RH" },
330 { "RH", "R", "RH" },
331 { "RH", "RH", "RH" },
332 { "RH", "RW", "RH" },
333 { "RH", "RWH", "RWH" },
335 { "RW", "", "RW" },
336 { "RW", "R", "RW" },
337 { "RW", "RH", "RW" },
338 { "RW", "RW", "RW" },
339 { "RW", "RWH", "RWH" },
341 { "RWH", "", "RWH" },
342 { "RWH", "R", "RWH" },
343 { "RWH", "RH", "RWH" },
344 { "RWH", "RW", "RWH" },
345 { "RWH", "RWH", "RWH" },
348 static bool test_lease_upgrade2(struct torture_context *tctx,
349 struct smb2_tree *tree)
351 TALLOC_CTX *mem_ctx = talloc_new(tctx);
352 struct smb2_handle h, hnew;
353 NTSTATUS status;
354 struct smb2_create io;
355 struct smb2_lease ls;
356 const char *fname = "lease.dat";
357 bool ret = true;
358 int i;
360 for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
361 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
363 smb2_util_unlink(tree, fname);
365 /* Grab a lease. */
366 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(t.initial));
367 status = smb2_create(tree, mem_ctx, &io);
368 CHECK_STATUS(status, NT_STATUS_OK);
369 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
370 CHECK_LEASE(&io, t.initial, true, LEASE1);
371 h = io.out.file.handle;
373 /* Upgrade. */
374 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(t.upgrade_to));
375 status = smb2_create(tree, mem_ctx, &io);
376 CHECK_STATUS(status, NT_STATUS_OK);
377 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
378 CHECK_LEASE(&io, t.expected, true, LEASE1);
379 hnew = io.out.file.handle;
381 smb2_util_close(tree, hnew);
382 smb2_util_close(tree, h);
385 done:
386 smb2_util_close(tree, h);
387 smb2_util_close(tree, hnew);
389 smb2_util_unlink(tree, fname);
391 talloc_free(mem_ctx);
393 return ret;
397 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
398 do { \
399 CHECK_VAL((__lb)->new_lease_state, lease(__state)); \
400 CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
401 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
402 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
403 } while(0)
405 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
406 do { \
407 CHECK_VAL((__lba)->out.reserved, 0); \
408 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
409 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
410 CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
411 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
412 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
413 } while(0)
415 static struct {
416 struct smb2_lease_break lease_break;
417 struct smb2_lease_break_ack lease_break_ack;
418 int count;
419 int failures;
421 struct smb2_handle oplock_handle;
422 int held_oplock_level;
423 int oplock_level;
424 int oplock_count;
425 int oplock_failures;
426 } break_info;
428 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
429 do { \
430 CHECK_VAL(break_info.failures, 0); \
431 CHECK_VAL(break_info.count, 1); \
432 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
433 (__state), (__key)); \
434 if (break_info.lease_break.break_flags & \
435 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
436 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
437 (__state), (__key)); \
439 } while(0)
441 static void torture_lease_break_callback(struct smb2_request *req)
443 NTSTATUS status;
445 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
446 if (!NT_STATUS_IS_OK(status))
447 break_info.failures++;
449 return;
452 /* a lease break request handler */
453 static bool torture_lease_handler(struct smb2_transport *transport,
454 const struct smb2_lease_break *lb,
455 void *private_data)
457 struct smb2_tree *tree = private_data;
458 struct smb2_lease_break_ack io;
459 struct smb2_request *req;
461 break_info.lease_break = *lb;
462 break_info.count++;
464 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
465 ZERO_STRUCT(io);
466 io.in.lease.lease_key = lb->current_lease.lease_key;
467 io.in.lease.lease_state = lb->new_lease_state;
469 req = smb2_lease_break_ack_send(tree, &io);
470 req->async.fn = torture_lease_break_callback;
471 req->async.private_data = NULL;
474 return true;
478 break_results should be read as "held lease, new lease, hold broken to, new
479 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
480 tries for RW, key1 will be broken to RH (in this case, not broken at all)
481 and key2 will be granted R.
483 Note: break_results only includes things that Win7 will actually grant (see
484 request_results above).
486 #define NBREAK_RESULTS 16
487 static const char *break_results[NBREAK_RESULTS][4] = {
488 {"R", "R", "R", "R"},
489 {"R", "RH", "R", "RH"},
490 {"R", "RW", "R", "R"},
491 {"R", "RHW", "R", "RH"},
493 {"RH", "R", "RH", "R"},
494 {"RH", "RH", "RH", "RH"},
495 {"RH", "RW", "RH", "R"},
496 {"RH", "RHW", "RH", "RH"},
498 {"RW", "R", "R", "R"},
499 {"RW", "RH", "R", "RH"},
500 {"RW", "RW", "R", "R"},
501 {"RW", "RHW", "R", "RH"},
503 {"RHW", "R", "RH", "R"},
504 {"RHW", "RH", "RH", "RH"},
505 {"RHW", "RW", "RH", "R"},
506 {"RHW", "RHW", "RH", "RH"},
509 static bool test_lease_break(struct torture_context *tctx,
510 struct smb2_tree *tree)
512 TALLOC_CTX *mem_ctx = talloc_new(tctx);
513 struct smb2_create io;
514 struct smb2_lease ls;
515 struct smb2_handle h, h2, h3;
516 NTSTATUS status;
517 const char *fname = "lease.dat";
518 bool ret = true;
519 int i;
521 tree->session->transport->lease.handler = torture_lease_handler;
522 tree->session->transport->lease.private_data = tree;
524 smb2_util_unlink(tree, fname);
526 for (i = 0; i < NBREAK_RESULTS; i++) {
527 const char *held = break_results[i][0];
528 const char *contend = break_results[i][1];
529 const char *brokento = break_results[i][2];
530 const char *granted = break_results[i][3];
531 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
532 "expecting break to %s(%x) and grant of %s(%x)\n",
533 held, lease(held), contend, lease(contend),
534 brokento, lease(brokento), granted, lease(granted));
536 ZERO_STRUCT(break_info);
538 /* Grab lease. */
539 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
540 status = smb2_create(tree, mem_ctx, &io);
541 CHECK_STATUS(status, NT_STATUS_OK);
542 h = io.out.file.handle;
543 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
544 CHECK_LEASE(&io, held, true, LEASE1);
546 /* Possibly contend lease. */
547 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
548 status = smb2_create(tree, mem_ctx, &io);
549 CHECK_STATUS(status, NT_STATUS_OK);
550 h2 = io.out.file.handle;
551 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
552 CHECK_LEASE(&io, granted, true, LEASE2);
554 if (lease(held) != lease(brokento)) {
555 CHECK_BREAK_INFO(held, brokento, LEASE1);
556 } else {
557 CHECK_VAL(break_info.count, 0);
558 CHECK_VAL(break_info.failures, 0);
561 ZERO_STRUCT(break_info);
564 Now verify that an attempt to upgrade LEASE1 results in no
565 break and no change in LEASE1.
567 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
568 status = smb2_create(tree, mem_ctx, &io);
569 CHECK_STATUS(status, NT_STATUS_OK);
570 h3 = io.out.file.handle;
571 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
572 CHECK_LEASE(&io, brokento, true, LEASE1);
573 CHECK_VAL(break_info.count, 0);
574 CHECK_VAL(break_info.failures, 0);
576 smb2_util_close(tree, h);
577 smb2_util_close(tree, h2);
578 smb2_util_close(tree, h3);
580 status = smb2_util_unlink(tree, fname);
581 CHECK_STATUS(status, NT_STATUS_OK);
584 done:
585 smb2_util_close(tree, h);
586 smb2_util_close(tree, h2);
588 smb2_util_unlink(tree, fname);
590 talloc_free(mem_ctx);
592 return ret;
595 static void torture_oplock_break_callback(struct smb2_request *req)
597 NTSTATUS status;
598 struct smb2_break br;
600 ZERO_STRUCT(br);
601 status = smb2_break_recv(req, &br);
602 if (!NT_STATUS_IS_OK(status))
603 break_info.oplock_failures++;
605 return;
608 /* a oplock break request handler */
609 static bool torture_oplock_handler(struct smb2_transport *transport,
610 const struct smb2_handle *handle,
611 uint8_t level, void *private_data)
613 struct smb2_tree *tree = private_data;
614 struct smb2_request *req;
615 struct smb2_break br;
617 break_info.oplock_handle = *handle;
618 break_info.oplock_level = level;
619 break_info.oplock_count++;
621 ZERO_STRUCT(br);
622 br.in.file.handle = *handle;
623 br.in.oplock_level = level;
625 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
626 req = smb2_break_send(tree, &br);
627 req->async.fn = torture_oplock_break_callback;
628 req->async.private_data = NULL;
630 break_info.held_oplock_level = level;
632 return true;
635 static inline uint32_t oplock(const char *op) {
636 uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
637 int i;
639 for (i = 0; i < strlen(op); i++) {
640 switch (op[i]) {
641 case 's':
642 return SMB2_OPLOCK_LEVEL_II;
643 case 'x':
644 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
645 case 'b':
646 return SMB2_OPLOCK_LEVEL_BATCH;
647 default:
648 continue;
652 return val;
655 #define NOPLOCK_RESULTS 12
656 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
657 {"R", "s", "R", "s"},
658 {"R", "x", "R", "s"},
659 {"R", "b", "R", "s"},
661 {"RH", "s", "RH", ""},
662 {"RH", "x", "RH", ""},
663 {"RH", "b", "RH", ""},
665 {"RW", "s", "R", "s"},
666 {"RW", "x", "R", "s"},
667 {"RW", "b", "R", "s"},
669 {"RHW", "s", "RH", ""},
670 {"RHW", "x", "RH", ""},
671 {"RHW", "b", "RH", ""},
674 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
675 {"s", "R", "s", "R"},
676 {"s", "RH", "s", "R"},
677 {"s", "RW", "s", "R"},
678 {"s", "RHW", "s", "R"},
680 {"x", "R", "s", "R"},
681 {"x", "RH", "s", "R"},
682 {"x", "RW", "s", "R"},
683 {"x", "RHW", "s", "R"},
685 {"b", "R", "s", "R"},
686 {"b", "RH", "s", "R"},
687 {"b", "RW", "s", "R"},
688 {"b", "RHW", "s", "R"},
691 static bool test_lease_oplock(struct torture_context *tctx,
692 struct smb2_tree *tree)
694 TALLOC_CTX *mem_ctx = talloc_new(tctx);
695 struct smb2_create io;
696 struct smb2_lease ls;
697 struct smb2_handle h, h2;
698 NTSTATUS status;
699 const char *fname = "lease.dat";
700 bool ret = true;
701 int i;
703 tree->session->transport->lease.handler = torture_lease_handler;
704 tree->session->transport->lease.private_data = tree;
705 tree->session->transport->oplock.handler = torture_oplock_handler;
706 tree->session->transport->oplock.private_data = tree;
708 smb2_util_unlink(tree, fname);
710 for (i = 0; i < NOPLOCK_RESULTS; i++) {
711 const char *held = oplock_results[i][0];
712 const char *contend = oplock_results[i][1];
713 const char *brokento = oplock_results[i][2];
714 const char *granted = oplock_results[i][3];
715 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
716 "expecting break to %s(%x) and grant of %s(%x)\n",
717 held, lease(held), contend, oplock(contend),
718 brokento, lease(brokento), granted, oplock(granted));
720 ZERO_STRUCT(break_info);
722 /* Grab lease. */
723 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
724 status = smb2_create(tree, mem_ctx, &io);
725 CHECK_STATUS(status, NT_STATUS_OK);
726 h = io.out.file.handle;
727 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
728 CHECK_LEASE(&io, held, true, LEASE1);
730 /* Does an oplock contend the lease? */
731 smb2_oplock_create(&io, fname, oplock(contend));
732 status = smb2_create(tree, mem_ctx, &io);
733 CHECK_STATUS(status, NT_STATUS_OK);
734 h2 = io.out.file.handle;
735 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
736 CHECK_VAL(io.out.oplock_level, oplock(granted));
737 break_info.held_oplock_level = io.out.oplock_level;
739 if (lease(held) != lease(brokento)) {
740 CHECK_BREAK_INFO(held, brokento, LEASE1);
741 } else {
742 CHECK_VAL(break_info.count, 0);
743 CHECK_VAL(break_info.failures, 0);
746 smb2_util_close(tree, h);
747 smb2_util_close(tree, h2);
749 status = smb2_util_unlink(tree, fname);
750 CHECK_STATUS(status, NT_STATUS_OK);
753 for (i = 0; i < NOPLOCK_RESULTS; i++) {
754 const char *held = oplock_results_2[i][0];
755 const char *contend = oplock_results_2[i][1];
756 const char *brokento = oplock_results_2[i][2];
757 const char *granted = oplock_results_2[i][3];
758 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
759 "expecting break to %s(%x) and grant of %s(%x)\n",
760 held, oplock(held), contend, lease(contend),
761 brokento, oplock(brokento), granted, lease(granted));
763 ZERO_STRUCT(break_info);
765 /* Grab an oplock. */
766 smb2_oplock_create(&io, fname, oplock(held));
767 status = smb2_create(tree, mem_ctx, &io);
768 CHECK_STATUS(status, NT_STATUS_OK);
769 h = io.out.file.handle;
770 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
771 CHECK_VAL(io.out.oplock_level, oplock(held));
772 break_info.held_oplock_level = io.out.oplock_level;
774 /* Grab lease. */
775 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
776 status = smb2_create(tree, mem_ctx, &io);
777 CHECK_STATUS(status, NT_STATUS_OK);
778 h2 = io.out.file.handle;
779 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
780 CHECK_LEASE(&io, granted, true, LEASE1);
782 if (oplock(held) != oplock(brokento)) {
783 CHECK_VAL(break_info.oplock_count, 1);
784 CHECK_VAL(break_info.oplock_failures, 0);
785 CHECK_VAL(break_info.oplock_level, oplock(brokento));
786 break_info.held_oplock_level = break_info.oplock_level;
787 } else {
788 CHECK_VAL(break_info.oplock_count, 0);
789 CHECK_VAL(break_info.oplock_failures, 0);
792 smb2_util_close(tree, h);
793 smb2_util_close(tree, h2);
795 status = smb2_util_unlink(tree, fname);
796 CHECK_STATUS(status, NT_STATUS_OK);
799 done:
800 smb2_util_close(tree, h);
801 smb2_util_close(tree, h2);
803 smb2_util_unlink(tree, fname);
805 talloc_free(mem_ctx);
807 return ret;
810 static bool test_lease_multibreak(struct torture_context *tctx,
811 struct smb2_tree *tree)
813 TALLOC_CTX *mem_ctx = talloc_new(tctx);
814 struct smb2_create io;
815 struct smb2_lease ls;
816 struct smb2_handle h, h2, h3;
817 struct smb2_write w;
818 NTSTATUS status;
819 const char *fname = "lease.dat";
820 bool ret = true;
822 tree->session->transport->lease.handler = torture_lease_handler;
823 tree->session->transport->lease.private_data = tree;
824 tree->session->transport->oplock.handler = torture_oplock_handler;
825 tree->session->transport->oplock.private_data = tree;
827 smb2_util_unlink(tree, fname);
829 ZERO_STRUCT(break_info);
831 /* Grab lease, upgrade to RHW .. */
832 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
833 status = smb2_create(tree, mem_ctx, &io);
834 CHECK_STATUS(status, NT_STATUS_OK);
835 h = io.out.file.handle;
836 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
837 CHECK_LEASE(&io, "RH", true, LEASE1);
839 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
840 status = smb2_create(tree, mem_ctx, &io);
841 CHECK_STATUS(status, NT_STATUS_OK);
842 h2 = io.out.file.handle;
843 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
844 CHECK_LEASE(&io, "RHW", true, LEASE1);
846 /* Contend with LEASE2. */
847 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
848 status = smb2_create(tree, mem_ctx, &io);
849 CHECK_STATUS(status, NT_STATUS_OK);
850 h3 = io.out.file.handle;
851 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
852 CHECK_LEASE(&io, "RH", true, LEASE2);
854 /* Verify that we were only sent one break. */
855 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
857 /* Drop LEASE1 / LEASE2 */
858 status = smb2_util_close(tree, h);
859 CHECK_STATUS(status, NT_STATUS_OK);
860 status = smb2_util_close(tree, h2);
861 CHECK_STATUS(status, NT_STATUS_OK);
862 status = smb2_util_close(tree, h3);
863 CHECK_STATUS(status, NT_STATUS_OK);
865 ZERO_STRUCT(break_info);
867 /* Grab an R lease. */
868 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
869 status = smb2_create(tree, mem_ctx, &io);
870 CHECK_STATUS(status, NT_STATUS_OK);
871 h = io.out.file.handle;
872 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
873 CHECK_LEASE(&io, "R", true, LEASE1);
875 /* Grab a level-II oplock. */
876 smb2_oplock_create(&io, fname, oplock("s"));
877 status = smb2_create(tree, mem_ctx, &io);
878 CHECK_STATUS(status, NT_STATUS_OK);
879 h2 = io.out.file.handle;
880 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
881 CHECK_VAL(io.out.oplock_level, oplock("s"));
882 break_info.held_oplock_level = io.out.oplock_level;
884 /* Verify no breaks. */
885 CHECK_VAL(break_info.count, 0);
886 CHECK_VAL(break_info.failures, 0);
888 /* Open for truncate, force a break. */
889 smb2_generic_create(&io, NULL, false, fname,
890 NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
891 status = smb2_create(tree, mem_ctx, &io);
892 CHECK_STATUS(status, NT_STATUS_OK);
893 h3 = io.out.file.handle;
894 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
895 CHECK_VAL(io.out.oplock_level, oplock(""));
896 break_info.held_oplock_level = io.out.oplock_level;
898 /* Sleep, use a write to clear the recv queue. */
899 smb_msleep(250);
900 ZERO_STRUCT(w);
901 w.in.file.handle = h3;
902 w.in.offset = 0;
903 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
904 status = smb2_write(tree, &w);
905 CHECK_STATUS(status, NT_STATUS_OK);
907 /* Verify one oplock break, one lease break. */
908 CHECK_VAL(break_info.oplock_count, 1);
909 CHECK_VAL(break_info.oplock_failures, 0);
910 CHECK_VAL(break_info.oplock_level, oplock(""));
911 CHECK_BREAK_INFO("R", "", LEASE1);
913 done:
914 smb2_util_close(tree, h);
915 smb2_util_close(tree, h2);
916 smb2_util_close(tree, h3);
918 smb2_util_unlink(tree, fname);
920 talloc_free(mem_ctx);
922 return ret;
925 struct torture_suite *torture_smb2_lease_init(void)
927 struct torture_suite *suite =
928 torture_suite_create(talloc_autofree_context(), "lease");
930 torture_suite_add_1smb2_test(suite, "request", test_lease_request);
931 torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
932 torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
933 torture_suite_add_1smb2_test(suite, "break", test_lease_break);
934 torture_suite_add_1smb2_test(suite, "oplock", test_lease_oplock);
935 torture_suite_add_1smb2_test(suite, "multibreak", test_lease_multibreak);
937 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
939 return suite;