s4:torture: send the TCONX_FLAG_EXTENDED_RESPONSE flag
[Samba/gebeck_regimport.git] / source4 / torture / smb2 / lease.c
blob5669c6200ecd9b5a56d9c368f82c5388c587a56d
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 #define CHECK_VAL(v, correct) do { \
29 if ((v) != (correct)) { \
30 torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be 0x%x\n", \
31 __location__, #v, (int)(v), (int)(correct)); \
32 ret = false; \
33 }} while (0)
35 #define CHECK_STATUS(status, correct) do { \
36 if (!NT_STATUS_EQUAL(status, correct)) { \
37 torture_result(tctx, TORTURE_FAIL, __location__": Incorrect status %s - should be %s", \
38 nt_errstr(status), nt_errstr(correct)); \
39 ret = false; \
40 goto done; \
41 }} while (0)
43 #define CHECK_CREATED(__io, __created, __attribute) \
44 do { \
45 CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
46 CHECK_VAL((__io)->out.alloc_size, 0); \
47 CHECK_VAL((__io)->out.size, 0); \
48 CHECK_VAL((__io)->out.file_attr, (__attribute)); \
49 CHECK_VAL((__io)->out.reserved2, 0); \
50 } while(0)
52 #define CHECK_LEASE(__io, __state, __oplevel, __key) \
53 do { \
54 if (__oplevel) { \
55 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_LEASE); \
56 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], (__key)); \
57 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], ~(__key)); \
58 CHECK_VAL((__io)->out.lease_response.lease_state, smb2_util_lease_state(__state)); \
59 } else { \
60 CHECK_VAL((__io)->out.oplock_level, SMB2_OPLOCK_LEVEL_NONE); \
61 CHECK_VAL((__io)->out.lease_response.lease_key.data[0], 0); \
62 CHECK_VAL((__io)->out.lease_response.lease_key.data[1], 0); \
63 CHECK_VAL((__io)->out.lease_response.lease_state, 0); \
64 } \
66 CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \
67 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \
68 } while(0) \
70 static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull;
71 static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull;
72 static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull;
74 #define NREQUEST_RESULTS 8
75 static const char *request_results[NREQUEST_RESULTS][2] = {
76 { "", "" },
77 { "R", "R" },
78 { "H", "" },
79 { "W", "" },
80 { "RH", "RH" },
81 { "RW", "RW" },
82 { "HW", "" },
83 { "RHW", "RHW" },
86 static bool test_lease_request(struct torture_context *tctx,
87 struct smb2_tree *tree)
89 TALLOC_CTX *mem_ctx = talloc_new(tctx);
90 struct smb2_create io;
91 struct smb2_lease ls;
92 struct smb2_handle h1, h2;
93 NTSTATUS status;
94 const char *fname = "lease.dat";
95 const char *fname2 = "lease2.dat";
96 const char *sname = "lease.dat:stream";
97 const char *dname = "lease.dir";
98 bool ret = true;
99 int i;
101 smb2_util_unlink(tree, fname);
102 smb2_util_unlink(tree, fname2);
103 smb2_util_rmdir(tree, dname);
105 /* Win7 is happy to grant RHW leases on files. */
106 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
107 status = smb2_create(tree, mem_ctx, &io);
108 CHECK_STATUS(status, NT_STATUS_OK);
109 h1 = io.out.file.handle;
110 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
111 CHECK_LEASE(&io, "RHW", true, LEASE1);
113 /* But will reject leases on directories. */
114 smb2_lease_create(&io, &ls, true, dname, LEASE2, smb2_util_lease_state("RHW"));
115 status = smb2_create(tree, mem_ctx, &io);
116 CHECK_STATUS(status, NT_STATUS_OK);
117 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_DIRECTORY);
118 CHECK_LEASE(&io, "", false, 0);
119 smb2_util_close(tree, io.out.file.handle);
121 /* Also rejects multiple files leased under the same key. */
122 smb2_lease_create(&io, &ls, true, fname2, LEASE1, smb2_util_lease_state("RHW"));
123 status = smb2_create(tree, mem_ctx, &io);
124 CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER);
126 /* And grants leases on streams (with separate leasekey). */
127 smb2_lease_create(&io, &ls, false, sname, LEASE2, smb2_util_lease_state("RHW"));
128 status = smb2_create(tree, mem_ctx, &io);
129 h2 = io.out.file.handle;
130 CHECK_STATUS(status, NT_STATUS_OK);
131 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
132 CHECK_LEASE(&io, "RHW", true, LEASE2);
133 smb2_util_close(tree, h2);
135 smb2_util_close(tree, h1);
137 /* Now see what combos are actually granted. */
138 for (i = 0; i < NREQUEST_RESULTS; i++) {
139 torture_comment(tctx, "Requesting lease type %s(%x),"
140 " expecting %s(%x)\n",
141 request_results[i][0], smb2_util_lease_state(request_results[i][0]),
142 request_results[i][1], smb2_util_lease_state(request_results[i][1]));
143 smb2_lease_create(&io, &ls, false, fname, LEASE1,
144 smb2_util_lease_state(request_results[i][0]));
145 status = smb2_create(tree, mem_ctx, &io);
146 h2 = io.out.file.handle;
147 CHECK_STATUS(status, NT_STATUS_OK);
148 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
149 CHECK_LEASE(&io, request_results[i][1], true, LEASE1);
150 smb2_util_close(tree, io.out.file.handle);
153 done:
154 smb2_util_close(tree, h1);
155 smb2_util_close(tree, h2);
157 smb2_util_unlink(tree, fname);
158 smb2_util_unlink(tree, fname2);
159 smb2_util_rmdir(tree, dname);
161 talloc_free(mem_ctx);
163 return ret;
166 static bool test_lease_upgrade(struct torture_context *tctx,
167 struct smb2_tree *tree)
169 TALLOC_CTX *mem_ctx = talloc_new(tctx);
170 struct smb2_create io;
171 struct smb2_lease ls;
172 struct smb2_handle h, hnew;
173 NTSTATUS status;
174 const char *fname = "lease.dat";
175 bool ret = true;
177 smb2_util_unlink(tree, fname);
179 /* Grab a RH lease. */
180 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
181 status = smb2_create(tree, mem_ctx, &io);
182 CHECK_STATUS(status, NT_STATUS_OK);
183 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
184 CHECK_LEASE(&io, "RH", true, LEASE1);
185 h = io.out.file.handle;
187 /* Upgrades (sidegrades?) to RW leave us with an RH. */
188 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RW"));
189 status = smb2_create(tree, mem_ctx, &io);
190 CHECK_STATUS(status, NT_STATUS_OK);
191 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
192 CHECK_LEASE(&io, "RH", true, LEASE1);
193 hnew = io.out.file.handle;
195 smb2_util_close(tree, hnew);
197 /* Upgrade to RHW lease. */
198 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
199 status = smb2_create(tree, mem_ctx, &io);
200 CHECK_STATUS(status, NT_STATUS_OK);
201 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
202 CHECK_LEASE(&io, "RHW", true, LEASE1);
203 hnew = io.out.file.handle;
205 smb2_util_close(tree, h);
206 h = hnew;
208 /* Attempt to downgrade - original lease state is maintained. */
209 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
210 status = smb2_create(tree, mem_ctx, &io);
211 CHECK_STATUS(status, NT_STATUS_OK);
212 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
213 CHECK_LEASE(&io, "RHW", true, LEASE1);
214 hnew = io.out.file.handle;
216 smb2_util_close(tree, hnew);
218 done:
219 smb2_util_close(tree, h);
220 smb2_util_close(tree, hnew);
222 smb2_util_unlink(tree, fname);
224 talloc_free(mem_ctx);
226 return ret;
230 * upgrade2 test.
231 * full matrix of lease upgrade combinations
233 struct lease_upgrade2_test {
234 const char *initial;
235 const char *upgrade_to;
236 const char *expected;
239 #define NUM_LEASE_TYPES 5
240 #define NUM_UPGRADE_TESTS ( NUM_LEASE_TYPES * NUM_LEASE_TYPES )
241 struct lease_upgrade2_test lease_upgrade2_tests[NUM_UPGRADE_TESTS] = {
242 { "", "", "" },
243 { "", "R", "R" },
244 { "", "RH", "RH" },
245 { "", "RW", "RW" },
246 { "", "RWH", "RWH" },
248 { "R", "", "R" },
249 { "R", "R", "R" },
250 { "R", "RH", "RH" },
251 { "R", "RW", "RW" },
252 { "R", "RWH", "RWH" },
254 { "RH", "", "RH" },
255 { "RH", "R", "RH" },
256 { "RH", "RH", "RH" },
257 { "RH", "RW", "RH" },
258 { "RH", "RWH", "RWH" },
260 { "RW", "", "RW" },
261 { "RW", "R", "RW" },
262 { "RW", "RH", "RW" },
263 { "RW", "RW", "RW" },
264 { "RW", "RWH", "RWH" },
266 { "RWH", "", "RWH" },
267 { "RWH", "R", "RWH" },
268 { "RWH", "RH", "RWH" },
269 { "RWH", "RW", "RWH" },
270 { "RWH", "RWH", "RWH" },
273 static bool test_lease_upgrade2(struct torture_context *tctx,
274 struct smb2_tree *tree)
276 TALLOC_CTX *mem_ctx = talloc_new(tctx);
277 struct smb2_handle h, hnew;
278 NTSTATUS status;
279 struct smb2_create io;
280 struct smb2_lease ls;
281 const char *fname = "lease.dat";
282 bool ret = true;
283 int i;
285 for (i = 0; i < NUM_UPGRADE_TESTS; i++) {
286 struct lease_upgrade2_test t = lease_upgrade2_tests[i];
288 smb2_util_unlink(tree, fname);
290 /* Grab a lease. */
291 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.initial));
292 status = smb2_create(tree, mem_ctx, &io);
293 CHECK_STATUS(status, NT_STATUS_OK);
294 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
295 CHECK_LEASE(&io, t.initial, true, LEASE1);
296 h = io.out.file.handle;
298 /* Upgrade. */
299 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(t.upgrade_to));
300 status = smb2_create(tree, mem_ctx, &io);
301 CHECK_STATUS(status, NT_STATUS_OK);
302 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
303 CHECK_LEASE(&io, t.expected, true, LEASE1);
304 hnew = io.out.file.handle;
306 smb2_util_close(tree, hnew);
307 smb2_util_close(tree, h);
310 done:
311 smb2_util_close(tree, h);
312 smb2_util_close(tree, hnew);
314 smb2_util_unlink(tree, fname);
316 talloc_free(mem_ctx);
318 return ret;
322 #define CHECK_LEASE_BREAK(__lb, __oldstate, __state, __key) \
323 do { \
324 CHECK_VAL((__lb)->new_lease_state, smb2_util_lease_state(__state)); \
325 CHECK_VAL((__lb)->current_lease.lease_state, smb2_util_lease_state(__oldstate)); \
326 CHECK_VAL((__lb)->current_lease.lease_key.data[0], (__key)); \
327 CHECK_VAL((__lb)->current_lease.lease_key.data[1], ~(__key)); \
328 } while(0)
330 #define CHECK_LEASE_BREAK_ACK(__lba, __state, __key) \
331 do { \
332 CHECK_VAL((__lba)->out.reserved, 0); \
333 CHECK_VAL((__lba)->out.lease.lease_key.data[0], (__key)); \
334 CHECK_VAL((__lba)->out.lease.lease_key.data[1], ~(__key)); \
335 CHECK_VAL((__lba)->out.lease.lease_state, smb2_util_lease_state(__state)); \
336 CHECK_VAL((__lba)->out.lease.lease_flags, 0); \
337 CHECK_VAL((__lba)->out.lease.lease_duration, 0); \
338 } while(0)
340 static struct {
341 struct smb2_lease_break lease_break;
342 struct smb2_lease_break_ack lease_break_ack;
343 int count;
344 int failures;
346 struct smb2_handle oplock_handle;
347 uint8_t held_oplock_level;
348 uint8_t oplock_level;
349 int oplock_count;
350 int oplock_failures;
351 } break_info;
353 #define CHECK_BREAK_INFO(__oldstate, __state, __key) \
354 do { \
355 CHECK_VAL(break_info.failures, 0); \
356 CHECK_VAL(break_info.count, 1); \
357 CHECK_LEASE_BREAK(&break_info.lease_break, (__oldstate), \
358 (__state), (__key)); \
359 if (break_info.lease_break.break_flags & \
360 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) { \
361 CHECK_LEASE_BREAK_ACK(&break_info.lease_break_ack, \
362 (__state), (__key)); \
364 } while(0)
366 static void torture_lease_break_callback(struct smb2_request *req)
368 NTSTATUS status;
370 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack);
371 if (!NT_STATUS_IS_OK(status))
372 break_info.failures++;
374 return;
377 /* a lease break request handler */
378 static bool torture_lease_handler(struct smb2_transport *transport,
379 const struct smb2_lease_break *lb,
380 void *private_data)
382 struct smb2_tree *tree = private_data;
383 struct smb2_lease_break_ack io;
384 struct smb2_request *req;
386 break_info.lease_break = *lb;
387 break_info.count++;
389 if (lb->break_flags & SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED) {
390 ZERO_STRUCT(io);
391 io.in.lease.lease_key = lb->current_lease.lease_key;
392 io.in.lease.lease_state = lb->new_lease_state;
394 req = smb2_lease_break_ack_send(tree, &io);
395 req->async.fn = torture_lease_break_callback;
396 req->async.private_data = NULL;
399 return true;
403 break_results should be read as "held lease, new lease, hold broken to, new
404 grant", i.e. { "RH", "RW", "RH", "R" } means that if key1 holds RH and key2
405 tries for RW, key1 will be broken to RH (in this case, not broken at all)
406 and key2 will be granted R.
408 Note: break_results only includes things that Win7 will actually grant (see
409 request_results above).
411 #define NBREAK_RESULTS 16
412 static const char *break_results[NBREAK_RESULTS][4] = {
413 {"R", "R", "R", "R"},
414 {"R", "RH", "R", "RH"},
415 {"R", "RW", "R", "R"},
416 {"R", "RHW", "R", "RH"},
418 {"RH", "R", "RH", "R"},
419 {"RH", "RH", "RH", "RH"},
420 {"RH", "RW", "RH", "R"},
421 {"RH", "RHW", "RH", "RH"},
423 {"RW", "R", "R", "R"},
424 {"RW", "RH", "R", "RH"},
425 {"RW", "RW", "R", "R"},
426 {"RW", "RHW", "R", "RH"},
428 {"RHW", "R", "RH", "R"},
429 {"RHW", "RH", "RH", "RH"},
430 {"RHW", "RW", "RH", "R"},
431 {"RHW", "RHW", "RH", "RH"},
434 static bool test_lease_break(struct torture_context *tctx,
435 struct smb2_tree *tree)
437 TALLOC_CTX *mem_ctx = talloc_new(tctx);
438 struct smb2_create io;
439 struct smb2_lease ls;
440 struct smb2_handle h, h2, h3;
441 NTSTATUS status;
442 const char *fname = "lease.dat";
443 bool ret = true;
444 int i;
446 tree->session->transport->lease.handler = torture_lease_handler;
447 tree->session->transport->lease.private_data = tree;
449 smb2_util_unlink(tree, fname);
451 for (i = 0; i < NBREAK_RESULTS; i++) {
452 const char *held = break_results[i][0];
453 const char *contend = break_results[i][1];
454 const char *brokento = break_results[i][2];
455 const char *granted = break_results[i][3];
456 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
457 "expecting break to %s(%x) and grant of %s(%x)\n",
458 held, smb2_util_lease_state(held), contend, smb2_util_lease_state(contend),
459 brokento, smb2_util_lease_state(brokento), granted, smb2_util_lease_state(granted));
461 ZERO_STRUCT(break_info);
463 /* Grab lease. */
464 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
465 status = smb2_create(tree, mem_ctx, &io);
466 CHECK_STATUS(status, NT_STATUS_OK);
467 h = io.out.file.handle;
468 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
469 CHECK_LEASE(&io, held, true, LEASE1);
471 /* Possibly contend lease. */
472 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state(contend));
473 status = smb2_create(tree, mem_ctx, &io);
474 CHECK_STATUS(status, NT_STATUS_OK);
475 h2 = io.out.file.handle;
476 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
477 CHECK_LEASE(&io, granted, true, LEASE2);
479 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
480 CHECK_BREAK_INFO(held, brokento, LEASE1);
481 } else {
482 CHECK_VAL(break_info.count, 0);
483 CHECK_VAL(break_info.failures, 0);
486 ZERO_STRUCT(break_info);
489 Now verify that an attempt to upgrade LEASE1 results in no
490 break and no change in LEASE1.
492 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
493 status = smb2_create(tree, mem_ctx, &io);
494 CHECK_STATUS(status, NT_STATUS_OK);
495 h3 = io.out.file.handle;
496 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
497 CHECK_LEASE(&io, brokento, true, LEASE1);
498 CHECK_VAL(break_info.count, 0);
499 CHECK_VAL(break_info.failures, 0);
501 smb2_util_close(tree, h);
502 smb2_util_close(tree, h2);
503 smb2_util_close(tree, h3);
505 status = smb2_util_unlink(tree, fname);
506 CHECK_STATUS(status, NT_STATUS_OK);
509 done:
510 smb2_util_close(tree, h);
511 smb2_util_close(tree, h2);
513 smb2_util_unlink(tree, fname);
515 talloc_free(mem_ctx);
517 return ret;
520 static void torture_oplock_break_callback(struct smb2_request *req)
522 NTSTATUS status;
523 struct smb2_break br;
525 ZERO_STRUCT(br);
526 status = smb2_break_recv(req, &br);
527 if (!NT_STATUS_IS_OK(status))
528 break_info.oplock_failures++;
530 return;
533 /* a oplock break request handler */
534 static bool torture_oplock_handler(struct smb2_transport *transport,
535 const struct smb2_handle *handle,
536 uint8_t level, void *private_data)
538 struct smb2_tree *tree = private_data;
539 struct smb2_request *req;
540 struct smb2_break br;
542 break_info.oplock_handle = *handle;
543 break_info.oplock_level = level;
544 break_info.oplock_count++;
546 ZERO_STRUCT(br);
547 br.in.file.handle = *handle;
548 br.in.oplock_level = level;
550 if (break_info.held_oplock_level > SMB2_OPLOCK_LEVEL_II) {
551 req = smb2_break_send(tree, &br);
552 req->async.fn = torture_oplock_break_callback;
553 req->async.private_data = NULL;
555 break_info.held_oplock_level = level;
557 return true;
560 #define NOPLOCK_RESULTS 12
561 static const char *oplock_results[NOPLOCK_RESULTS][4] = {
562 {"R", "s", "R", "s"},
563 {"R", "x", "R", "s"},
564 {"R", "b", "R", "s"},
566 {"RH", "s", "RH", ""},
567 {"RH", "x", "RH", ""},
568 {"RH", "b", "RH", ""},
570 {"RW", "s", "R", "s"},
571 {"RW", "x", "R", "s"},
572 {"RW", "b", "R", "s"},
574 {"RHW", "s", "RH", ""},
575 {"RHW", "x", "RH", ""},
576 {"RHW", "b", "RH", ""},
579 static const char *oplock_results_2[NOPLOCK_RESULTS][4] = {
580 {"s", "R", "s", "R"},
581 {"s", "RH", "s", "R"},
582 {"s", "RW", "s", "R"},
583 {"s", "RHW", "s", "R"},
585 {"x", "R", "s", "R"},
586 {"x", "RH", "s", "R"},
587 {"x", "RW", "s", "R"},
588 {"x", "RHW", "s", "R"},
590 {"b", "R", "s", "R"},
591 {"b", "RH", "s", "R"},
592 {"b", "RW", "s", "R"},
593 {"b", "RHW", "s", "R"},
596 static bool test_lease_oplock(struct torture_context *tctx,
597 struct smb2_tree *tree)
599 TALLOC_CTX *mem_ctx = talloc_new(tctx);
600 struct smb2_create io;
601 struct smb2_lease ls;
602 struct smb2_handle h, h2;
603 NTSTATUS status;
604 const char *fname = "lease.dat";
605 bool ret = true;
606 int i;
608 tree->session->transport->lease.handler = torture_lease_handler;
609 tree->session->transport->lease.private_data = tree;
610 tree->session->transport->oplock.handler = torture_oplock_handler;
611 tree->session->transport->oplock.private_data = tree;
613 smb2_util_unlink(tree, fname);
615 for (i = 0; i < NOPLOCK_RESULTS; i++) {
616 const char *held = oplock_results[i][0];
617 const char *contend = oplock_results[i][1];
618 const char *brokento = oplock_results[i][2];
619 const char *granted = oplock_results[i][3];
620 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
621 "expecting break to %s(%x) and grant of %s(%x)\n",
622 held, smb2_util_lease_state(held), contend, smb2_util_oplock_level(contend),
623 brokento, smb2_util_lease_state(brokento), granted, smb2_util_oplock_level(granted));
625 ZERO_STRUCT(break_info);
627 /* Grab lease. */
628 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(held));
629 status = smb2_create(tree, mem_ctx, &io);
630 CHECK_STATUS(status, NT_STATUS_OK);
631 h = io.out.file.handle;
632 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
633 CHECK_LEASE(&io, held, true, LEASE1);
635 /* Does an oplock contend the lease? */
636 smb2_oplock_create(&io, fname, smb2_util_oplock_level(contend));
637 status = smb2_create(tree, mem_ctx, &io);
638 CHECK_STATUS(status, NT_STATUS_OK);
639 h2 = io.out.file.handle;
640 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
641 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(granted));
642 break_info.held_oplock_level = io.out.oplock_level;
644 if (smb2_util_lease_state(held) != smb2_util_lease_state(brokento)) {
645 CHECK_BREAK_INFO(held, brokento, LEASE1);
646 } else {
647 CHECK_VAL(break_info.count, 0);
648 CHECK_VAL(break_info.failures, 0);
651 smb2_util_close(tree, h);
652 smb2_util_close(tree, h2);
654 status = smb2_util_unlink(tree, fname);
655 CHECK_STATUS(status, NT_STATUS_OK);
658 for (i = 0; i < NOPLOCK_RESULTS; i++) {
659 const char *held = oplock_results_2[i][0];
660 const char *contend = oplock_results_2[i][1];
661 const char *brokento = oplock_results_2[i][2];
662 const char *granted = oplock_results_2[i][3];
663 torture_comment(tctx, "Hold %s(%x), requesting %s(%x), "
664 "expecting break to %s(%x) and grant of %s(%x)\n",
665 held, smb2_util_oplock_level(held), contend, smb2_util_lease_state(contend),
666 brokento, smb2_util_oplock_level(brokento), granted, smb2_util_lease_state(granted));
668 ZERO_STRUCT(break_info);
670 /* Grab an oplock. */
671 smb2_oplock_create(&io, fname, smb2_util_oplock_level(held));
672 status = smb2_create(tree, mem_ctx, &io);
673 CHECK_STATUS(status, NT_STATUS_OK);
674 h = io.out.file.handle;
675 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
676 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(held));
677 break_info.held_oplock_level = io.out.oplock_level;
679 /* Grab lease. */
680 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state(contend));
681 status = smb2_create(tree, mem_ctx, &io);
682 CHECK_STATUS(status, NT_STATUS_OK);
683 h2 = io.out.file.handle;
684 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
685 CHECK_LEASE(&io, granted, true, LEASE1);
687 if (smb2_util_oplock_level(held) != smb2_util_oplock_level(brokento)) {
688 CHECK_VAL(break_info.oplock_count, 1);
689 CHECK_VAL(break_info.oplock_failures, 0);
690 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(brokento));
691 break_info.held_oplock_level = break_info.oplock_level;
692 } else {
693 CHECK_VAL(break_info.oplock_count, 0);
694 CHECK_VAL(break_info.oplock_failures, 0);
697 smb2_util_close(tree, h);
698 smb2_util_close(tree, h2);
700 status = smb2_util_unlink(tree, fname);
701 CHECK_STATUS(status, NT_STATUS_OK);
704 done:
705 smb2_util_close(tree, h);
706 smb2_util_close(tree, h2);
708 smb2_util_unlink(tree, fname);
710 talloc_free(mem_ctx);
712 return ret;
715 static bool test_lease_multibreak(struct torture_context *tctx,
716 struct smb2_tree *tree)
718 TALLOC_CTX *mem_ctx = talloc_new(tctx);
719 struct smb2_create io;
720 struct smb2_lease ls;
721 struct smb2_handle h, h2, h3;
722 struct smb2_write w;
723 NTSTATUS status;
724 const char *fname = "lease.dat";
725 bool ret = true;
727 tree->session->transport->lease.handler = torture_lease_handler;
728 tree->session->transport->lease.private_data = tree;
729 tree->session->transport->oplock.handler = torture_oplock_handler;
730 tree->session->transport->oplock.private_data = tree;
732 smb2_util_unlink(tree, fname);
734 ZERO_STRUCT(break_info);
736 /* Grab lease, upgrade to RHW .. */
737 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RH"));
738 status = smb2_create(tree, mem_ctx, &io);
739 CHECK_STATUS(status, NT_STATUS_OK);
740 h = io.out.file.handle;
741 CHECK_CREATED(&io, CREATED, FILE_ATTRIBUTE_ARCHIVE);
742 CHECK_LEASE(&io, "RH", true, LEASE1);
744 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("RHW"));
745 status = smb2_create(tree, mem_ctx, &io);
746 CHECK_STATUS(status, NT_STATUS_OK);
747 h2 = io.out.file.handle;
748 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
749 CHECK_LEASE(&io, "RHW", true, LEASE1);
751 /* Contend with LEASE2. */
752 smb2_lease_create(&io, &ls, false, fname, LEASE2, smb2_util_lease_state("RHW"));
753 status = smb2_create(tree, mem_ctx, &io);
754 CHECK_STATUS(status, NT_STATUS_OK);
755 h3 = io.out.file.handle;
756 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
757 CHECK_LEASE(&io, "RH", true, LEASE2);
759 /* Verify that we were only sent one break. */
760 CHECK_BREAK_INFO("RHW", "RH", LEASE1);
762 /* Drop LEASE1 / LEASE2 */
763 status = smb2_util_close(tree, h);
764 CHECK_STATUS(status, NT_STATUS_OK);
765 status = smb2_util_close(tree, h2);
766 CHECK_STATUS(status, NT_STATUS_OK);
767 status = smb2_util_close(tree, h3);
768 CHECK_STATUS(status, NT_STATUS_OK);
770 ZERO_STRUCT(break_info);
772 /* Grab an R lease. */
773 smb2_lease_create(&io, &ls, false, fname, LEASE1, smb2_util_lease_state("R"));
774 status = smb2_create(tree, mem_ctx, &io);
775 CHECK_STATUS(status, NT_STATUS_OK);
776 h = io.out.file.handle;
777 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
778 CHECK_LEASE(&io, "R", true, LEASE1);
780 /* Grab a level-II oplock. */
781 smb2_oplock_create(&io, fname, smb2_util_oplock_level("s"));
782 status = smb2_create(tree, mem_ctx, &io);
783 CHECK_STATUS(status, NT_STATUS_OK);
784 h2 = io.out.file.handle;
785 CHECK_CREATED(&io, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
786 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level("s"));
787 break_info.held_oplock_level = io.out.oplock_level;
789 /* Verify no breaks. */
790 CHECK_VAL(break_info.count, 0);
791 CHECK_VAL(break_info.failures, 0);
793 /* Open for truncate, force a break. */
794 smb2_generic_create(&io, NULL, false, fname,
795 NTCREATEX_DISP_OVERWRITE_IF, smb2_util_oplock_level(""), 0, 0);
796 status = smb2_create(tree, mem_ctx, &io);
797 CHECK_STATUS(status, NT_STATUS_OK);
798 h3 = io.out.file.handle;
799 CHECK_CREATED(&io, TRUNCATED, FILE_ATTRIBUTE_ARCHIVE);
800 CHECK_VAL(io.out.oplock_level, smb2_util_oplock_level(""));
801 break_info.held_oplock_level = io.out.oplock_level;
803 /* Sleep, use a write to clear the recv queue. */
804 smb_msleep(250);
805 ZERO_STRUCT(w);
806 w.in.file.handle = h3;
807 w.in.offset = 0;
808 w.in.data = data_blob_talloc(mem_ctx, NULL, 4096);
809 memset(w.in.data.data, 'o', w.in.data.length);
810 status = smb2_write(tree, &w);
811 CHECK_STATUS(status, NT_STATUS_OK);
813 /* Verify one oplock break, one lease break. */
814 CHECK_VAL(break_info.oplock_count, 1);
815 CHECK_VAL(break_info.oplock_failures, 0);
816 CHECK_VAL(break_info.oplock_level, smb2_util_oplock_level(""));
817 CHECK_BREAK_INFO("R", "", LEASE1);
819 done:
820 smb2_util_close(tree, h);
821 smb2_util_close(tree, h2);
822 smb2_util_close(tree, h3);
824 smb2_util_unlink(tree, fname);
826 talloc_free(mem_ctx);
828 return ret;
831 struct torture_suite *torture_smb2_lease_init(void)
833 struct torture_suite *suite =
834 torture_suite_create(talloc_autofree_context(), "lease");
836 torture_suite_add_1smb2_test(suite, "request", test_lease_request);
837 torture_suite_add_1smb2_test(suite, "upgrade", test_lease_upgrade);
838 torture_suite_add_1smb2_test(suite, "upgrade2", test_lease_upgrade2);
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;