1/* 2 Unix SMB/CIFS implementation. 3 4 test suite for SMB2 leases 5 6 Copyright (C) Zachary Loafman 2009 7 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. 12 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. 17 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/>. 20*/ 21 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" 29 30static inline uint32_t lease(const char *ls) { 31 uint32_t val = 0; 32 int i; 33 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; 45 } 46 } 47 48 return val; 49} 50 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) 57 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) 65 66static 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) 70{ 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; 88 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; 94 } 95 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; 102 } 103} 104 105static 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) 108{ 109 smb2_generic_create(io, ls, dir, name, NTCREATEX_DISP_OPEN_IF, 110 SMB2_OPLOCK_LEVEL_LEASE, leasekey, leasestate); 111} 112 113static void smb2_oplock_create(struct smb2_create *io, const char *name, 114 uint32_t oplock) 115{ 116 smb2_generic_create(io, NULL, false, name, NTCREATEX_DISP_OPEN_IF, 117 oplock, 0, 0); 118} 119 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) 128 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); \ 141 } \ 142 \ 143 CHECK_VAL((__io)->out.lease_response.lease_flags, 0); \ 144 CHECK_VAL((__io)->out.lease_response.lease_duration, 0); \ 145 } while(0) \ 146 147static const uint64_t LEASE1 = 0xBADC0FFEE0DDF00Dull; 148static const uint64_t LEASE2 = 0xDEADBEEFFEEDBEADull; 149static const uint64_t LEASE3 = 0xDAD0FFEDD00DF00Dull; 150 151#define NREQUEST_RESULTS 8 152static 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" }, 161}; 162 163static bool test_lease_request(struct torture_context *tctx, 164 struct smb2_tree *tree) 165{ 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; 177 178 smb2_util_unlink(tree, fname); 179 smb2_util_unlink(tree, fname2); 180 smb2_util_rmdir(tree, dname); 181 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); 189 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); 197 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); 202 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); 211 212 smb2_util_close(tree, h1); 213 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); 228 } 229 230 done: 231 smb2_util_close(tree, h1); 232 smb2_util_close(tree, h2); 233 234 smb2_util_unlink(tree, fname); 235 smb2_util_unlink(tree, fname2); 236 smb2_util_rmdir(tree, dname); 237 238 talloc_free(mem_ctx); 239 240 return ret; 241} 242 243static bool test_lease_upgrade(struct torture_context *tctx, 244 struct smb2_tree *tree) 245{ 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; 253 254 smb2_util_unlink(tree, fname); 255 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; 263 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; 271 272 smb2_util_close(tree, hnew); 273 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; 281 282 smb2_util_close(tree, h); 283 h = hnew; 284 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; 292 293 smb2_util_close(tree, hnew); 294 295 done: 296 smb2_util_close(tree, h); 297 smb2_util_close(tree, hnew); 298 299 smb2_util_unlink(tree, fname); 300 301 talloc_free(mem_ctx); 302 303 return ret; 304} 305 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) 313 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) 323 324static struct { 325 struct smb2_lease_break lease_break; 326 struct smb2_lease_break_ack lease_break_ack; 327 int count; 328 int failures; 329 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; 336 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)); \ 347 } \ 348 } while(0) 349 350static void torture_lease_break_callback(struct smb2_request *req) 351{ 352 NTSTATUS status; 353 354 status = smb2_lease_break_ack_recv(req, &break_info.lease_break_ack); 355 if (!NT_STATUS_IS_OK(status)) 356 break_info.failures++; 357 358 return; 359} 360 361/* a lease break request handler */ 362static bool torture_lease_handler(struct smb2_transport *transport, 363 const struct smb2_lease_break *lb, 364 void *private_data) 365{ 366 struct smb2_tree *tree = private_data; 367 struct smb2_lease_break_ack io; 368 struct smb2_request *req; 369 370 break_info.lease_break = *lb; 371 break_info.count++; 372 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; 377 378 req = smb2_lease_break_ack_send(tree, &io); 379 req->async.fn = torture_lease_break_callback; 380 req->async.private_data = NULL; 381 } 382 383 return true; 384} 385 386/* 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. 391 392 Note: break_results only includes things that Win7 will actually grant (see 393 request_results above). 394 */ 395#define NBREAK_RESULTS 16 396static 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"}, 401 402 {"RH", "R", "RH", "R"}, 403 {"RH", "RH", "RH", "RH"}, 404 {"RH", "RW", "RH", "R"}, 405 {"RH", "RHW", "RH", "RH"}, 406 407 {"RW", "R", "R", "R"}, 408 {"RW", "RH", "R", "RH"}, 409 {"RW", "RW", "R", "R"}, 410 {"RW", "RHW", "R", "RH"}, 411 412 {"RHW", "R", "RH", "R"}, 413 {"RHW", "RH", "RH", "RH"}, 414 {"RHW", "RW", "RH", "R"}, 415 {"RHW", "RHW", "RH", "RH"}, 416}; 417 418static bool test_lease_break(struct torture_context *tctx, 419 struct smb2_tree *tree) 420{ 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; 429 430 tree->session->transport->lease.handler = torture_lease_handler; 431 tree->session->transport->lease.private_data = tree; 432 433 smb2_util_unlink(tree, fname); 434 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)); 444 445 ZERO_STRUCT(break_info); 446 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); 454 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); 462 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); 468 } 469 470 ZERO_STRUCT(break_info); 471 472 /* 473 Now verify that an attempt to upgrade LEASE1 results in no 474 break and no change in LEASE1. 475 */ 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); 484 485 smb2_util_close(tree, h); 486 smb2_util_close(tree, h2); 487 smb2_util_close(tree, h3); 488 489 status = smb2_util_unlink(tree, fname); 490 CHECK_STATUS(status, NT_STATUS_OK); 491 } 492 493 done: 494 smb2_util_close(tree, h); 495 smb2_util_close(tree, h2); 496 497 smb2_util_unlink(tree, fname); 498 499 talloc_free(mem_ctx); 500 501 return ret; 502} 503 504static void torture_oplock_break_callback(struct smb2_request *req) 505{ 506 NTSTATUS status; 507 struct smb2_break br; 508 509 ZERO_STRUCT(br); 510 status = smb2_break_recv(req, &br); 511 if (!NT_STATUS_IS_OK(status)) 512 break_info.oplock_failures++; 513 514 return; 515} 516 517/* a oplock break request handler */ 518static bool torture_oplock_handler(struct smb2_transport *transport, 519 const struct smb2_handle *handle, 520 uint8_t level, void *private_data) 521{ 522 struct smb2_tree *tree = private_data; 523 struct smb2_request *req; 524 struct smb2_break br; 525 526 break_info.oplock_handle = *handle; 527 break_info.oplock_level = level; 528 break_info.oplock_count++; 529 530 ZERO_STRUCT(br); 531 br.in.file.handle = *handle; 532 br.in.oplock_level = level; 533 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; 538 } 539 break_info.held_oplock_level = level; 540 541 return true; 542} 543 544static inline uint32_t oplock(const char *op) { 545 uint32_t val = SMB2_OPLOCK_LEVEL_NONE; 546 int i; 547 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; 558 } 559 } 560 561 return val; 562} 563 564#define NOPLOCK_RESULTS 12 565static const char *oplock_results[NOPLOCK_RESULTS][4] = { 566 {"R", "s", "R", "s"}, 567 {"R", "x", "R", "s"}, 568 {"R", "b", "R", "s"}, 569 570 {"RH", "s", "RH", ""}, 571 {"RH", "x", "RH", ""}, 572 {"RH", "b", "RH", ""}, 573 574 {"RW", "s", "R", "s"}, 575 {"RW", "x", "R", "s"}, 576 {"RW", "b", "R", "s"}, 577 578 {"RHW", "s", "RH", ""}, 579 {"RHW", "x", "RH", ""}, 580 {"RHW", "b", "RH", ""}, 581}; 582 583static 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"}, 588 589 {"x", "R", "s", "R"}, 590 {"x", "RH", "s", "R"}, 591 {"x", "RW", "s", "R"}, 592 {"x", "RHW", "s", "R"}, 593 594 {"b", "R", "s", "R"}, 595 {"b", "RH", "s", "R"}, 596 {"b", "RW", "s", "R"}, 597 {"b", "RHW", "s", "R"}, 598}; 599 600static bool test_lease_oplock(struct torture_context *tctx, 601 struct smb2_tree *tree) 602{ 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; 611 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; 616 617 smb2_util_unlink(tree, fname); 618 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)); 628 629 ZERO_STRUCT(break_info); 630 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); 638 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; 647 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); 653 } 654 655 smb2_util_close(tree, h); 656 smb2_util_close(tree, h2); 657 658 status = smb2_util_unlink(tree, fname); 659 CHECK_STATUS(status, NT_STATUS_OK); 660 } 661 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)); 671 672 ZERO_STRUCT(break_info); 673 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; 682 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); 690 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); 699 } 700 701 smb2_util_close(tree, h); 702 smb2_util_close(tree, h2); 703 704 status = smb2_util_unlink(tree, fname); 705 CHECK_STATUS(status, NT_STATUS_OK); 706 } 707 708 done: 709 smb2_util_close(tree, h); 710 smb2_util_close(tree, h2); 711 712 smb2_util_unlink(tree, fname); 713 714 talloc_free(mem_ctx); 715 716 return ret; 717} 718 719static bool test_lease_multibreak(struct torture_context *tctx, 720 struct smb2_tree *tree) 721{ 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; 730 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; 735 736 smb2_util_unlink(tree, fname); 737 738 ZERO_STRUCT(break_info); 739 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); 747 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); 754 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); 762 763 /* Verify that we were only sent one break. */ 764 CHECK_BREAK_INFO("RHW", "RH", LEASE1); 765 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); 773 774 ZERO_STRUCT(break_info); 775 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); 783 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; 792 793 /* Verify no breaks. */ 794 CHECK_VAL(break_info.count, 0); 795 CHECK_VAL(break_info.failures, 0); 796 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; 806 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); 815 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); 821 822 done: 823 smb2_util_close(tree, h); 824 smb2_util_close(tree, h2); 825 smb2_util_close(tree, h3); 826 827 smb2_util_unlink(tree, fname); 828 829 talloc_free(mem_ctx); 830 831 return ret; 832} 833 834struct torture_suite *torture_smb2_lease_init(void) 835{ 836 struct torture_suite *suite = 837 torture_suite_create(talloc_autofree_context(), "LEASE"); 838 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); 844 845 suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); 846 847 return suite; 848} 849