1/* 2 Unix SMB/CIFS implementation. 3 randomised byte range lock tester 4 Copyright (C) Andrew Tridgell 1999 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19*/ 20 21#include "includes.h" 22 23static fstring password[2]; 24static fstring username[2]; 25static int got_user; 26static int got_pass; 27static BOOL use_kerberos; 28static int numops = 1000; 29static BOOL showall; 30static BOOL analyze; 31static BOOL hide_unlock_fails; 32static BOOL use_oplocks; 33static unsigned lock_range = 100; 34static unsigned lock_base = 0; 35static unsigned min_length = 0; 36static BOOL exact_error_codes; 37static BOOL zero_zero; 38 39extern char *optarg; 40extern int optind; 41 42#define FILENAME "\\locktest.dat" 43 44#define READ_PCT 50 45#define LOCK_PCT 45 46#define UNLOCK_PCT 70 47#define RANGE_MULTIPLE 1 48#define NSERVERS 2 49#define NCONNECTIONS 2 50#define NFILES 2 51#define LOCK_TIMEOUT 0 52 53#define NASTY_POSIX_LOCK_HACK 0 54 55enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN}; 56 57static const char *lock_op_type(int op) 58{ 59 if (op == WRITE_LOCK) return "write"; 60 else if (op == READ_LOCK) return "read"; 61 else return "other"; 62} 63 64static const char *lock_op_name(enum lock_op op) 65{ 66 if (op == OP_LOCK) return "lock"; 67 else if (op == OP_UNLOCK) return "unlock"; 68 else return "reopen"; 69} 70 71struct record { 72 enum lock_op lock_op; 73 enum brl_type lock_type; 74 char conn, f; 75 SMB_BIG_UINT start, len; 76 char needed; 77}; 78 79#define PRESETS 0 80 81#if PRESETS 82static struct record preset[] = { 83{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, 84{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 85{OP_LOCK, WRITE_LOCK, 0, 0, 3, 0, 1}, 86{OP_UNLOCK, 0 , 0, 0, 2, 0, 1}, 87{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 88 89{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, 90{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1}, 91{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 92{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 93 94{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, 95{OP_LOCK, WRITE_LOCK, 0, 0, 3, 1, 1}, 96{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 97{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 98 99{OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1}, 100{OP_LOCK, WRITE_LOCK, 0, 0, 1, 1, 1}, 101{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 102{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 103 104{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, 105{OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1}, 106{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 107{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 108 109{OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1}, 110{OP_LOCK, READ_LOCK, 0, 0, 3, 1, 1}, 111{OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1}, 112{OP_REOPEN, 0, 0, 0, 0, 0, 1}, 113 114}; 115#endif 116 117static struct record *recorded; 118 119static void print_brl(SMB_DEV_T dev, 120 SMB_INO_T ino, 121 struct process_id pid, 122 enum brl_type lock_type, 123 enum brl_flavour lock_flav, 124 br_off start, 125 br_off size) 126{ 127#if NASTY_POSIX_LOCK_HACK 128 { 129 pstring cmd; 130 static SMB_INO_T lastino; 131 132 if (lastino != ino) { 133 slprintf(cmd, sizeof(cmd), 134 "egrep POSIX.*%u /proc/locks", (int)ino); 135 system(cmd); 136 } 137 lastino = ino; 138 } 139#endif 140 141 printf("%s %05x:%05x %s %.0f:%.0f(%.0f)\n", 142 procid_str_static(&pid), (int)dev, (int)ino, 143 lock_type==READ_LOCK?"R":"W", 144 (double)start, (double)start+size-1,(double)size); 145 146} 147 148 149static void show_locks(void) 150{ 151 brl_forall(print_brl); 152 /* system("cat /proc/locks"); */ 153} 154 155 156/***************************************************** 157return a connection to a server 158*******************************************************/ 159static struct cli_state *connect_one(char *share, int snum) 160{ 161 struct cli_state *c; 162 struct nmb_name called, calling; 163 char *server_n; 164 fstring server; 165 struct in_addr ip; 166 fstring myname; 167 static int count; 168 169 fstrcpy(server,share+2); 170 share = strchr_m(server,'\\'); 171 if (!share) return NULL; 172 *share = 0; 173 share++; 174 175 server_n = server; 176 177 zero_ip(&ip); 178 179 slprintf(myname,sizeof(myname), "lock-%lu-%u", (unsigned long)getpid(), count++); 180 181 make_nmb_name(&calling, myname, 0x0); 182 make_nmb_name(&called , server, 0x20); 183 184 again: 185 zero_ip(&ip); 186 187 /* have to open a new connection */ 188 if (!(c=cli_initialise()) || !cli_connect(c, server_n, &ip)) { 189 DEBUG(0,("Connection to %s failed\n", server_n)); 190 return NULL; 191 } 192 193 c->use_kerberos = use_kerberos; 194 195 if (!cli_session_request(c, &calling, &called)) { 196 DEBUG(0,("session request to %s failed\n", called.name)); 197 cli_shutdown(c); 198 if (strcmp(called.name, "*SMBSERVER")) { 199 make_nmb_name(&called , "*SMBSERVER", 0x20); 200 goto again; 201 } 202 return NULL; 203 } 204 205 DEBUG(4,(" session request ok\n")); 206 207 if (!cli_negprot(c)) { 208 DEBUG(0,("protocol negotiation failed\n")); 209 cli_shutdown(c); 210 return NULL; 211 } 212 213 if (!got_pass) { 214 char *pass = getpass("Password: "); 215 if (pass) { 216 fstrcpy(password[0], pass); 217 fstrcpy(password[1], pass); 218 } 219 } 220 221 if (got_pass == 1) { 222 fstrcpy(password[1], password[0]); 223 fstrcpy(username[1], username[0]); 224 } 225 226 if (!NT_STATUS_IS_OK(cli_session_setup(c, username[snum], 227 password[snum], 228 strlen(password[snum]), 229 password[snum], 230 strlen(password[snum]), 231 lp_workgroup()))) { 232 DEBUG(0,("session setup failed: %s\n", cli_errstr(c))); 233 return NULL; 234 } 235 236 /* 237 * These next two lines are needed to emulate 238 * old client behaviour for people who have 239 * scripts based on client output. 240 * QUESTION ? Do we want to have a 'client compatibility 241 * mode to turn these on/off ? JRA. 242 */ 243 244 if (*c->server_domain || *c->server_os || *c->server_type) 245 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n", 246 c->server_domain,c->server_os,c->server_type)); 247 248 DEBUG(4,(" session setup ok\n")); 249 250 if (!cli_send_tconX(c, share, "?????", 251 password[snum], strlen(password[snum])+1)) { 252 DEBUG(0,("tree connect failed: %s\n", cli_errstr(c))); 253 cli_shutdown(c); 254 return NULL; 255 } 256 257 DEBUG(4,(" tconx ok\n")); 258 259 c->use_oplocks = use_oplocks; 260 261 return c; 262} 263 264 265static void reconnect(struct cli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES], 266 char *share[NSERVERS]) 267{ 268 int server, conn, f; 269 270 for (server=0;server<NSERVERS;server++) 271 for (conn=0;conn<NCONNECTIONS;conn++) { 272 if (cli[server][conn]) { 273 for (f=0;f<NFILES;f++) { 274 if (fnum[server][conn][f] != -1) { 275 cli_close(cli[server][conn], fnum[server][conn][f]); 276 fnum[server][conn][f] = -1; 277 } 278 } 279 cli_ulogoff(cli[server][conn]); 280 cli_shutdown(cli[server][conn]); 281 } 282 cli[server][conn] = connect_one(share[server], server); 283 if (!cli[server][conn]) { 284 DEBUG(0,("Failed to connect to %s\n", share[server])); 285 exit(1); 286 } 287 } 288} 289 290 291 292static BOOL test_one(struct cli_state *cli[NSERVERS][NCONNECTIONS], 293 int fnum[NSERVERS][NCONNECTIONS][NFILES], 294 struct record *rec) 295{ 296 unsigned conn = rec->conn; 297 unsigned f = rec->f; 298 SMB_BIG_UINT start = rec->start; 299 SMB_BIG_UINT len = rec->len; 300 enum brl_type op = rec->lock_type; 301 int server; 302 BOOL ret[NSERVERS]; 303 NTSTATUS status[NSERVERS]; 304 305 switch (rec->lock_op) { 306 case OP_LOCK: 307 /* set a lock */ 308 for (server=0;server<NSERVERS;server++) { 309 ret[server] = cli_lock64(cli[server][conn], 310 fnum[server][conn][f], 311 start, len, LOCK_TIMEOUT, op); 312 status[server] = cli_nt_error(cli[server][conn]); 313 if (!exact_error_codes && 314 NT_STATUS_EQUAL(status[server], 315 NT_STATUS_FILE_LOCK_CONFLICT)) { 316 status[server] = NT_STATUS_LOCK_NOT_GRANTED; 317 } 318 } 319 if (showall || !NT_STATUS_EQUAL(status[0],status[1])) { 320 printf("lock conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n", 321 conn, f, 322 (double)start, (double)len, 323 op==READ_LOCK?"READ_LOCK":"WRITE_LOCK", 324 nt_errstr(status[0]), nt_errstr(status[1])); 325 } 326 if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks(); 327 if (!NT_STATUS_EQUAL(status[0],status[1])) return False; 328 break; 329 330 case OP_UNLOCK: 331 /* unset a lock */ 332 for (server=0;server<NSERVERS;server++) { 333 ret[server] = cli_unlock64(cli[server][conn], 334 fnum[server][conn][f], 335 start, len); 336 status[server] = cli_nt_error(cli[server][conn]); 337 } 338 if (showall || 339 (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) { 340 printf("unlock conn=%u f=%u range=%.0f(%.0f) -> %s:%s\n", 341 conn, f, 342 (double)start, (double)len, 343 nt_errstr(status[0]), nt_errstr(status[1])); 344 } 345 if (showall || !NT_STATUS_EQUAL(status[0],status[1])) show_locks(); 346 if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) 347 return False; 348 break; 349 350 case OP_REOPEN: 351 /* reopen the file */ 352 for (server=0;server<NSERVERS;server++) { 353 cli_close(cli[server][conn], fnum[server][conn][f]); 354 fnum[server][conn][f] = -1; 355 } 356 for (server=0;server<NSERVERS;server++) { 357 fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME, 358 O_RDWR|O_CREAT, 359 DENY_NONE); 360 if (fnum[server][conn][f] == -1) { 361 printf("failed to reopen on share%d\n", server); 362 return False; 363 } 364 } 365 if (showall) { 366 printf("reopen conn=%u f=%u\n", 367 conn, f); 368 show_locks(); 369 } 370 break; 371 } 372 373 return True; 374} 375 376static void close_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 377 int fnum[NSERVERS][NCONNECTIONS][NFILES]) 378{ 379 int server, conn, f; 380 381 for (server=0;server<NSERVERS;server++) 382 for (conn=0;conn<NCONNECTIONS;conn++) 383 for (f=0;f<NFILES;f++) { 384 if (fnum[server][conn][f] != -1) { 385 cli_close(cli[server][conn], fnum[server][conn][f]); 386 fnum[server][conn][f] = -1; 387 } 388 } 389 for (server=0;server<NSERVERS;server++) { 390 cli_unlink(cli[server][0], FILENAME); 391 } 392} 393 394static void open_files(struct cli_state *cli[NSERVERS][NCONNECTIONS], 395 int fnum[NSERVERS][NCONNECTIONS][NFILES]) 396{ 397 int server, conn, f; 398 399 for (server=0;server<NSERVERS;server++) 400 for (conn=0;conn<NCONNECTIONS;conn++) 401 for (f=0;f<NFILES;f++) { 402 fnum[server][conn][f] = cli_open(cli[server][conn], FILENAME, 403 O_RDWR|O_CREAT, 404 DENY_NONE); 405 if (fnum[server][conn][f] == -1) { 406 fprintf(stderr,"Failed to open fnum[%u][%u][%u]\n", 407 server, conn, f); 408 exit(1); 409 } 410 } 411} 412 413 414static int retest(struct cli_state *cli[NSERVERS][NCONNECTIONS], 415 int fnum[NSERVERS][NCONNECTIONS][NFILES], 416 int n) 417{ 418 int i; 419 printf("testing %u ...\n", n); 420 for (i=0; i<n; i++) { 421 if (i && i % 100 == 0) { 422 printf("%u\n", i); 423 } 424 425 if (recorded[i].needed && 426 !test_one(cli, fnum, &recorded[i])) return i; 427 } 428 return n; 429} 430 431 432/* each server has two connections open to it. Each connection has two file 433 descriptors open on the file - 8 file descriptors in total 434 435 we then do random locking ops in tamdem on the 4 fnums from each 436 server and ensure that the results match 437 */ 438static void test_locks(char *share[NSERVERS]) 439{ 440 struct cli_state *cli[NSERVERS][NCONNECTIONS]; 441 int fnum[NSERVERS][NCONNECTIONS][NFILES]; 442 int n, i, n1, skip, r1, r2; 443 444 ZERO_STRUCT(fnum); 445 ZERO_STRUCT(cli); 446 447 recorded = SMB_MALLOC_ARRAY(struct record, numops); 448 449 for (n=0; n<numops; n++) { 450#if PRESETS 451 if (n < sizeof(preset) / sizeof(preset[0])) { 452 recorded[n] = preset[n]; 453 } else { 454#endif 455 recorded[n].conn = random() % NCONNECTIONS; 456 recorded[n].f = random() % NFILES; 457 recorded[n].start = lock_base + ((unsigned)random() % (lock_range-1)); 458 recorded[n].len = min_length + 459 random() % (lock_range-(recorded[n].start-lock_base)); 460 recorded[n].start *= RANGE_MULTIPLE; 461 recorded[n].len *= RANGE_MULTIPLE; 462 r1 = random() % 100; 463 r2 = random() % 100; 464 if (r1 < READ_PCT) { 465 recorded[n].lock_type = READ_LOCK; 466 } else { 467 recorded[n].lock_type = WRITE_LOCK; 468 } 469 if (r2 < LOCK_PCT) { 470 recorded[n].lock_op = OP_LOCK; 471 } else if (r2 < UNLOCK_PCT) { 472 recorded[n].lock_op = OP_UNLOCK; 473 } else { 474 recorded[n].lock_op = OP_REOPEN; 475 } 476 recorded[n].needed = True; 477 if (!zero_zero && recorded[n].start==0 && recorded[n].len==0) { 478 recorded[n].len = 1; 479 } 480#if PRESETS 481 } 482#endif 483 } 484 485 reconnect(cli, fnum, share); 486 open_files(cli, fnum); 487 n = retest(cli, fnum, numops); 488 489 if (n == numops || !analyze) return; 490 n++; 491 492 skip = n/2; 493 494 while (1) { 495 n1 = n; 496 497 close_files(cli, fnum); 498 reconnect(cli, fnum, share); 499 open_files(cli, fnum); 500 501 for (i=0;i<n-skip;i+=skip) { 502 int m, j; 503 printf("excluding %d-%d\n", i, i+skip-1); 504 for (j=i;j<i+skip;j++) { 505 recorded[j].needed = False; 506 } 507 508 close_files(cli, fnum); 509 open_files(cli, fnum); 510 511 m = retest(cli, fnum, n); 512 if (m == n) { 513 for (j=i;j<i+skip;j++) { 514 recorded[j].needed = True; 515 } 516 } else { 517 if (i+(skip-1) < m) { 518 memmove(&recorded[i], &recorded[i+skip], 519 (m-(i+skip-1))*sizeof(recorded[0])); 520 } 521 n = m-(skip-1); 522 i--; 523 } 524 } 525 526 if (skip > 1) { 527 skip = skip/2; 528 printf("skip=%d\n", skip); 529 continue; 530 } 531 532 if (n1 == n) break; 533 } 534 535 close_files(cli, fnum); 536 reconnect(cli, fnum, share); 537 open_files(cli, fnum); 538 showall = True; 539 n1 = retest(cli, fnum, n); 540 if (n1 != n-1) { 541 printf("ERROR - inconsistent result (%u %u)\n", n1, n); 542 } 543 close_files(cli, fnum); 544 545 for (i=0;i<n;i++) { 546 printf("{%s, %s, conn = %u, file = %u, start = %.0f, len = %.0f, %u},\n", 547 lock_op_name(recorded[i].lock_op), 548 lock_op_type(recorded[i].lock_type), 549 recorded[i].conn, 550 recorded[i].f, 551 (double)recorded[i].start, 552 (double)recorded[i].len, 553 recorded[i].needed); 554 } 555} 556 557 558 559static void usage(void) 560{ 561 printf( 562"Usage:\n\ 563 locktest //server1/share1 //server2/share2 [options..]\n\ 564 options:\n\ 565 -U user%%pass (may be specified twice)\n\ 566 -k use kerberos\n\ 567 -s seed\n\ 568 -o numops\n\ 569 -u hide unlock fails\n\ 570 -a (show all ops)\n\ 571 -A analyse for minimal ops\n\ 572 -O use oplocks\n\ 573 -E enable exact error code checking\n\ 574 -Z enable the zero/zero lock\n\ 575 -R range set lock range\n\ 576 -B base set lock base\n\ 577 -M min set min lock length\n\ 578"); 579} 580 581/**************************************************************************** 582 main program 583****************************************************************************/ 584 int main(int argc,char *argv[]) 585{ 586 char *share[NSERVERS]; 587 int opt; 588 char *p; 589 int seed, server; 590 591 setlinebuf(stdout); 592 593 load_case_tables(); 594 595 dbf = x_stderr; 596 597 if (argc < 3 || argv[1][0] == '-') { 598 usage(); 599 exit(1); 600 } 601 602 setup_logging(argv[0],True); 603 604 for (server=0;server<NSERVERS;server++) { 605 share[server] = argv[1+server]; 606 all_string_sub(share[server],"/","\\",0); 607 } 608 609 argc -= NSERVERS; 610 argv += NSERVERS; 611 612 lp_load(dyn_CONFIGFILE,True,False,False,True); 613 load_interfaces(); 614 615 if (getenv("USER")) { 616 fstrcpy(username[0],getenv("USER")); 617 fstrcpy(username[1],getenv("USER")); 618 } 619 620 seed = time(NULL); 621 622 while ((opt = getopt(argc, argv, "U:s:ho:aAW:OkR:B:M:EZ")) != EOF) { 623 switch (opt) { 624 case 'k': 625#ifdef HAVE_KRB5 626 use_kerberos = True; 627#else 628 d_printf("No kerberos support compiled in\n"); 629 exit(1); 630#endif 631 break; 632 case 'U': 633 got_user = 1; 634 if (got_pass == 2) { 635 d_printf("Max of 2 usernames\n"); 636 exit(1); 637 } 638 fstrcpy(username[got_pass],optarg); 639 p = strchr_m(username[got_pass],'%'); 640 if (p) { 641 *p = 0; 642 fstrcpy(password[got_pass], p+1); 643 got_pass++; 644 } 645 break; 646 case 'R': 647 lock_range = strtol(optarg, NULL, 0); 648 break; 649 case 'B': 650 lock_base = strtol(optarg, NULL, 0); 651 break; 652 case 'M': 653 min_length = strtol(optarg, NULL, 0); 654 break; 655 case 's': 656 seed = atoi(optarg); 657 break; 658 case 'u': 659 hide_unlock_fails = True; 660 break; 661 case 'o': 662 numops = atoi(optarg); 663 break; 664 case 'O': 665 use_oplocks = True; 666 break; 667 case 'a': 668 showall = True; 669 break; 670 case 'A': 671 analyze = True; 672 break; 673 case 'Z': 674 zero_zero = True; 675 break; 676 case 'E': 677 exact_error_codes = True; 678 break; 679 case 'h': 680 usage(); 681 exit(1); 682 default: 683 printf("Unknown option %c (%d)\n", (char)opt, opt); 684 exit(1); 685 } 686 } 687 688 if(use_kerberos && !got_user) got_pass = True; 689 690 argc -= optind; 691 argv += optind; 692 693 DEBUG(0,("seed=%u\n", seed)); 694 srandom(seed); 695 696 test_locks(share); 697 698 return(0); 699} 700