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