s4-smbtorture: add more paranoid checks for REG_DWORD SetPrinterDataEx tests.
[Samba/kamenim.git] / source4 / torture / smb2 / lease.c
blob63285d40e02c2b83f53386115138220a6a6887d0
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;
304 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
305 do { \
306 CHECK_VAL((__lb)->new_lease_state, lease(__state)); \
307 CHECK_VAL((__lb)->current_lease.lease_state, lease(__oldstate)); \
308 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
309 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
310 } while(0)
312 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
313 do { \
314 CHECK_VAL((__lba)->out.reserved, 0); \
315 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
316 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
317 CHECK_VAL((__lba)->out.lease.lease_state, lease(__state)); \
318 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
319 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
320 } while(0)
322 static struct {
323 struct smb2_lease_break lease_break;
324 struct smb2_lease_break_ack lease_break_ack;
325 int count;
326 int failures;
328 struct smb2_handle oplock_handle;
329 int held_oplock_level;
330 int oplock_level;
331 int oplock_count;
332 int oplock_failures;
333 } break_info;
335 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
336 do { \
337 CHECK_VAL(break_info.failures, 0); \
338 CHECK_VAL(break_info.count, 1); \
339 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
340 (__state), (__key)); \
341 if (break_info.lease_break.break_flags & \
342 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
343 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
344 (__state), (__key)); \
346 } while(0)
348 static void torture_lease_break_callback(struct smb2_request *req)
350 NTSTATUS status;
352 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
353 if (!NT_STATUS_IS_OK(status))
354 break_info.failures++;
356 return;
359 /* a lease break request handler */
360 static bool torture_lease_handler(struct smb2_transport *transport,
361 const struct smb2_lease_break *lb,
362 void *private_data)
364 struct smb2_tree *tree = private_data;
365 struct smb2_lease_break_ack io;
366 struct smb2_request *req;
368 break_info.lease_break = *lb;
369 break_info.count++;
371 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
372 ZERO_STRUCT(io);
373 io.in.lease.lease_key = lb->current_lease.lease_key;
374 io.in.lease.lease_state = lb->new_lease_state;
376 req = smb2_lease_break_ack_send(tree, &io);
377 req->async.fn = torture_lease_break_callback;
378 req->async.private_data = NULL;
381 return true;
385 break_results should be read as "held lease, new lease, hold broken to, new
386 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
387 tries for RW, key1 will be broken to RH (in this case, not broken at all)
388 and key2 will be granted R.
390 Note: break_results only includes things that Win7 will actually grant (see
391 request_results above).
393 #define NBREAK_RESULTS 16
394 static const char *break_results[NBREAK_RESULTS][4] = {
395 {"R", "R", "R", "R"},
396 {"R", "RH", "R", "RH"},
397 {"R", "RW", "R", "R"},
398 {"R", "RHW", "R", "RH"},
400 {"RH", "R", "RH", "R"},
401 {"RH", "RH", "RH", "RH"},
402 {"RH", "RW", "RH", "R"},
403 {"RH", "RHW", "RH", "RH"},
405 {"RW", "R", "R", "R"},
406 {"RW", "RH", "R", "RH"},
407 {"RW", "RW", "R", "R"},
408 {"RW", "RHW", "R", "RH"},
410 {"RHW", "R", "RH", "R"},
411 {"RHW", "RH", "RH", "RH"},
412 {"RHW", "RW", "RH", "R"},
413 {"RHW", "RHW", "RH", "RH"},
416 static bool test_lease_break(struct torture_context *tctx,
417 struct smb2_tree *tree)
419 TALLOC_CTX *mem_ctx = talloc_new(tctx);
420 struct smb2_create io;
421 struct smb2_lease ls;
422 struct smb2_handle h, h2, h3;
423 NTSTATUS status;
424 const char *fname = "lease.dat";
425 bool ret = true;
426 int i;
428 tree->session->transport->lease.handler = torture_lease_handler;
429 tree->session->transport->lease.private_data = tree;
431 smb2_util_unlink(tree, fname);
433 for (i = 0; i < NBREAK_RESULTS; i++) {
434 const char *held = break_results[i][0];
435 const char *contend = break_results[i][1];
436 const char *brokento = break_results[i][2];
437 const char *granted = break_results[i][3];
438 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
439 "expecting break to %s(%x) and grant of %s(%x)\n",
440 held, lease(held), contend, lease(contend),
441 brokento, lease(brokento), granted, lease(granted));
443 ZERO_STRUCT(break_info);
445 /* Grab lease. */
446 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
447 status = smb2_create(tree, mem_ctx, &io);
448 CHECK_STATUS(status, NT_STATUS_OK);
449 h = io.out.file.handle;
450 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
451 CHECK_LEASE(&io, held, true, LEASE1);
453 /* Possibly contend lease. */
454 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease(contend));
455 status = smb2_create(tree, mem_ctx, &io);
456 CHECK_STATUS(status, NT_STATUS_OK);
457 h2 = io.out.file.handle;
458 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
459 CHECK_LEASE(&io, granted, true, LEASE2);
461 if (lease(held) != lease(brokento)) {
462 CHECK_BREAK_INFO(held, brokento, LEASE1);
463 } else {
464 CHECK_VAL(break_info.count, 0);
465 CHECK_VAL(break_info.failures, 0);
468 ZERO_STRUCT(break_info);
471 Now verify that an attempt to upgrade LEASE1 results in no
472 break and no change in LEASE1.
474 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
475 status = smb2_create(tree, mem_ctx, &io);
476 CHECK_STATUS(status, NT_STATUS_OK);
477 h3 = io.out.file.handle;
478 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
479 CHECK_LEASE(&io, brokento, true, LEASE1);
480 CHECK_VAL(break_info.count, 0);
481 CHECK_VAL(break_info.failures, 0);
483 smb2_util_close(tree, h);
484 smb2_util_close(tree, h2);
485 smb2_util_close(tree, h3);
487 status = smb2_util_unlink(tree, fname);
488 CHECK_STATUS(status, NT_STATUS_OK);
491 done:
492 smb2_util_close(tree, h);
493 smb2_util_close(tree, h2);
495 smb2_util_unlink(tree, fname);
497 talloc_free(mem_ctx);
499 return ret;
502 static void torture_oplock_break_callback(struct smb2_request *req)
504 NTSTATUS status;
505 struct smb2_break br;
507 ZERO_STRUCT(br);
508 status = smb2_break_recv(req, &br);
509 if (!NT_STATUS_IS_OK(status))
510 break_info.oplock_failures++;
512 return;
515 /* a oplock break request handler */
516 static bool torture_oplock_handler(struct smb2_transport *transport,
517 const struct smb2_handle *handle,
518 uint8_t level, void *private_data)
520 struct smb2_tree *tree = private_data;
521 struct smb2_request *req;
522 struct smb2_break br;
524 break_info.oplock_handle = *handle;
525 break_info.oplock_level = level;
526 break_info.oplock_count++;
528 ZERO_STRUCT(br);
529 br.in.file.handle = *handle;
530 br.in.oplock_level = level;
532 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
533 req = smb2_break_send(tree, &br);
534 req->async.fn = torture_oplock_break_callback;
535 req->async.private_data = NULL;
537 break_info.held_oplock_level = level;
539 return true;
542 static inline uint32_t oplock(const char *op) {
543 uint32_t val = SMB2_OPLOCK_LEVEL_NONE;
544 int i;
546 for (i = 0; i < strlen(op); i++) {
547 switch (op[i]) {
548 case 's':
549 return SMB2_OPLOCK_LEVEL_II;
550 case 'x':
551 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
552 case 'b':
553 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
554 default:
555 continue;
559 return val;
562 #define NOPLOCK_RESULTS 12
563 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
564 {"R", "s", "R", "s"},
565 {"R", "x", "R", "s"},
566 {"R", "b", "R", "s"},
568 {"RH", "s", "RH", ""},
569 {"RH", "x", "RH", ""},
570 {"RH", "b", "RH", ""},
572 {"RW", "s", "R", "s"},
573 {"RW", "x", "R", "s"},
574 {"RW", "b", "R", "s"},
576 {"RHW", "s", "RH", ""},
577 {"RHW", "x", "RH", ""},
578 {"RHW", "b", "RH", ""},
581 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
582 {"s", "R", "s", "R"},
583 {"s", "RH", "s", "R"},
584 {"s", "RW", "s", "R"},
585 {"s", "RHW", "s", "R"},
587 {"x", "R", "s", "R"},
588 {"x", "RH", "s", "R"},
589 {"x", "RW", "s", "R"},
590 {"x", "RHW", "s", "R"},
592 {"b", "R", "s", "R"},
593 {"b", "RH", "s", "R"},
594 {"b", "RW", "s", "R"},
595 {"b", "RHW", "s", "R"},
598 static bool test_lease_oplock(struct torture_context *tctx,
599 struct smb2_tree *tree)
601 TALLOC_CTX *mem_ctx = talloc_new(tctx);
602 struct smb2_create io;
603 struct smb2_lease ls;
604 struct smb2_handle h, h2;
605 NTSTATUS status;
606 const char *fname = "lease.dat";
607 bool ret = true;
608 int i;
610 tree->session->transport->lease.handler = torture_lease_handler;
611 tree->session->transport->lease.private_data = tree;
612 tree->session->transport->oplock.handler = torture_oplock_handler;
613 tree->session->transport->oplock.private_data = tree;
615 smb2_util_unlink(tree, fname);
617 for (i = 0; i < NOPLOCK_RESULTS; i++) {
618 const char *held = oplock_results[i][0];
619 const char *contend = oplock_results[i][1];
620 const char *brokento = oplock_results[i][2];
621 const char *granted = oplock_results[i][3];
622 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
623 "expecting break to %s(%x) and grant of %s(%x)\n",
624 held, lease(held), contend, oplock(contend),
625 brokento, lease(brokento), granted, oplock(granted));
627 ZERO_STRUCT(break_info);
629 /* Grab lease. */
630 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(held));
631 status = smb2_create(tree, mem_ctx, &io);
632 CHECK_STATUS(status, NT_STATUS_OK);
633 h = io.out.file.handle;
634 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
635 CHECK_LEASE(&io, held, true, LEASE1);
637 /* Does an oplock contend the lease? */
638 smb2_oplock_create(&io, fname, oplock(contend));
639 status = smb2_create(tree, mem_ctx, &io);
640 CHECK_STATUS(status, NT_STATUS_OK);
641 h2 = io.out.file.handle;
642 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
643 CHECK_VAL(io.out.oplock_level, oplock(granted));
644 break_info.held_oplock_level = io.out.oplock_level;
646 if (lease(held) != lease(brokento)) {
647 CHECK_BREAK_INFO(held, brokento, LEASE1);
648 } else {
649 CHECK_VAL(break_info.count, 0);
650 CHECK_VAL(break_info.failures, 0);
653 smb2_util_close(tree, h);
654 smb2_util_close(tree, h2);
656 status = smb2_util_unlink(tree, fname);
657 CHECK_STATUS(status, NT_STATUS_OK);
660 for (i = 0; i < NOPLOCK_RESULTS; i++) {
661 const char *held = oplock_results_2[i][0];
662 const char *contend = oplock_results_2[i][1];
663 const char *brokento = oplock_results_2[i][2];
664 const char *granted = oplock_results_2[i][3];
665 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
666 "expecting break to %s(%x) and grant of %s(%x)\n",
667 held, oplock(held), contend, lease(contend),
668 brokento, oplock(brokento), granted, lease(granted));
670 ZERO_STRUCT(break_info);
672 /* Grab an oplock. */
673 smb2_oplock_create(&io, fname, oplock(held));
674 status = smb2_create(tree, mem_ctx, &io);
675 CHECK_STATUS(status, NT_STATUS_OK);
676 h = io.out.file.handle;
677 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
678 CHECK_VAL(io.out.oplock_level, oplock(held));
679 break_info.held_oplock_level = io.out.oplock_level;
681 /* Grab lease. */
682 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease(contend));
683 status = smb2_create(tree, mem_ctx, &io);
684 CHECK_STATUS(status, NT_STATUS_OK);
685 h2 = io.out.file.handle;
686 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
687 CHECK_LEASE(&io, granted, true, LEASE1);
689 if (oplock(held) != oplock(brokento)) {
690 CHECK_VAL(break_info.oplock_count, 1);
691 CHECK_VAL(break_info.oplock_failures, 0);
692 CHECK_VAL(break_info.oplock_level, oplock(brokento));
693 break_info.held_oplock_level = break_info.oplock_level;
694 } else {
695 CHECK_VAL(break_info.oplock_count, 0);
696 CHECK_VAL(break_info.oplock_failures, 0);
699 smb2_util_close(tree, h);
700 smb2_util_close(tree, h2);
702 status = smb2_util_unlink(tree, fname);
703 CHECK_STATUS(status, NT_STATUS_OK);
706 done:
707 smb2_util_close(tree, h);
708 smb2_util_close(tree, h2);
710 smb2_util_unlink(tree, fname);
712 talloc_free(mem_ctx);
714 return ret;
717 static bool test_lease_multibreak(struct torture_context *tctx,
718 struct smb2_tree *tree)
720 TALLOC_CTX *mem_ctx = talloc_new(tctx);
721 struct smb2_create io;
722 struct smb2_lease ls;
723 struct smb2_handle h, h2, h3;
724 struct smb2_write w;
725 NTSTATUS status;
726 const char *fname = "lease.dat";
727 bool ret = true;
729 tree->session->transport->lease.handler = torture_lease_handler;
730 tree->session->transport->lease.private_data = tree;
731 tree->session->transport->oplock.handler = torture_oplock_handler;
732 tree->session->transport->oplock.private_data = tree;
734 smb2_util_unlink(tree, fname);
736 ZERO_STRUCT(break_info);
738 /* Grab lease, upgrade to RHW .. */
739 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RH"));
740 status = smb2_create(tree, mem_ctx, &io);
741 CHECK_STATUS(status, NT_STATUS_OK);
742 h = io.out.file.handle;
743 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
744 CHECK_LEASE(&io, "RH", true, LEASE1);
746 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("RHW"));
747 status = smb2_create(tree, mem_ctx, &io);
748 CHECK_STATUS(status, NT_STATUS_OK);
749 h2 = io.out.file.handle;
750 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
751 CHECK_LEASE(&io, "RHW", true, LEASE1);
753 /* Contend with LEASE2. */
754 smb2_lease_create(&io, &ls, false, fname, LEASE2, lease("RHW"));
755 status = smb2_create(tree, mem_ctx, &io);
756 CHECK_STATUS(status, NT_STATUS_OK);
757 h3 = io.out.file.handle;
758 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
759 CHECK_LEASE(&io, "RH", true, LEASE2);
761 /* Verify that we were only sent one break. */
762 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
764 /* Drop LEASE1 / LEASE2 */
765 status = smb2_util_close(tree, h);
766 CHECK_STATUS(status, NT_STATUS_OK);
767 status = smb2_util_close(tree, h2);
768 CHECK_STATUS(status, NT_STATUS_OK);
769 status = smb2_util_close(tree, h3);
770 CHECK_STATUS(status, NT_STATUS_OK);
772 ZERO_STRUCT(break_info);
774 /* Grab an R lease. */
775 smb2_lease_create(&io, &ls, false, fname, LEASE1, lease("R"));
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, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
780 CHECK_LEASE(&io, "R", true, LEASE1);
782 /* Grab a level-II oplock. */
783 smb2_oplock_create(&io, fname, oplock("s"));
784 status = smb2_create(tree, mem_ctx, &io);
785 CHECK_STATUS(status, NT_STATUS_OK);
786 h2 = io.out.file.handle;
787 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
788 CHECK_VAL(io.out.oplock_level, oplock("s"));
789 break_info.held_oplock_level = io.out.oplock_level;
791 /* Verify no breaks. */
792 CHECK_VAL(break_info.count, 0);
793 CHECK_VAL(break_info.failures, 0);
795 /* Open for truncate, force a break. */
796 smb2_generic_create(&io, NULL, false, fname,
797 NTCREATEX_DISP_OVERWRITE_IF, oplock(""), 0, 0);
798 status = smb2_create(tree, mem_ctx, &io);
799 CHECK_STATUS(status, NT_STATUS_OK);
800 h3 = io.out.file.handle;
801 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
802 CHECK_VAL(io.out.oplock_level, oplock(""));
803 break_info.held_oplock_level = io.out.oplock_level;
805 /* Sleep, use a write to clear the recv queue. */
806 msleep(250);
807 ZERO_STRUCT(w);
808 w.in.file.handle = h3;
809 w.in.offset = 0;
810 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
811 status = smb2_write(tree, &w);
812 CHECK_STATUS(status, NT_STATUS_OK);
814 /* Verify one oplock break, one lease break. */
815 CHECK_VAL(break_info.oplock_count, 1);
816 CHECK_VAL(break_info.oplock_failures, 0);
817 CHECK_VAL(break_info.oplock_level, oplock(""));
818 CHECK_BREAK_INFO("R", "", LEASE1);
820 done:
821 smb2_util_close(tree, h);
822 smb2_util_close(tree, h2);
823 smb2_util_close(tree, h3);
825 smb2_util_unlink(tree, fname);
827 talloc_free(mem_ctx);
829 return ret;
832 struct torture_suite *torture_smb2_lease_init(void)
834 struct torture_suite *suite =
835 torture_suite_create(talloc_autofree_context(), "LEASE");
837 torture_suite_add_1smb2_test(suite, "REQUEST", test_lease_request);
838 torture_suite_add_1smb2_test(suite, "UPGRADE", test_lease_upgrade);
839 torture_suite_add_1smb2_test(suite, "BREAK", test_lease_break);
840 torture_suite_add_1smb2_test(suite, "OPLOCK", test_lease_oplock);
841 torture_suite_add_1smb2_test(suite, "MULTIBREAK", test_lease_multibreak);
843 suite->description = talloc_strdup(suite, "SMB2-LEASE tests");
845 return suite;