1/* 2 Unix SMB/CIFS implementation. 3 SMB torture tester utility functions 4 Copyright (C) Andrew Tridgell 2003 5 Copyright (C) Jelmer Vernooij 2006 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include "includes.h" 22#include "lib/cmdline/popt_common.h" 23#include "libcli/raw/libcliraw.h" 24#include "libcli/raw/raw_proto.h" 25#include "libcli/raw/ioctl.h" 26#include "libcli/libcli.h" 27#include "system/filesys.h" 28#include "system/shmem.h" 29#include "system/wait.h" 30#include "system/time.h" 31#include "torture/torture.h" 32#include "../lib/util/dlinklist.h" 33#include "auth/credentials/credentials.h" 34#include "libcli/resolve/resolve.h" 35#include "param/param.h" 36 37 38/** 39 setup a directory ready for a test 40*/ 41_PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname) 42{ 43 smb_raw_exit(cli->session); 44 if (smbcli_deltree(cli->tree, dname) == -1 || 45 NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) { 46 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree)); 47 return false; 48 } 49 return true; 50} 51 52/* 53 create a directory, returning a handle to it 54*/ 55NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum) 56{ 57 NTSTATUS status; 58 union smb_open io; 59 TALLOC_CTX *mem_ctx; 60 61 mem_ctx = talloc_named_const(tree, 0, "create_directory_handle"); 62 63 io.generic.level = RAW_OPEN_NTCREATEX; 64 io.ntcreatex.in.root_fid = 0; 65 io.ntcreatex.in.flags = 0; 66 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL; 67 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; 68 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL; 69 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; 70 io.ntcreatex.in.alloc_size = 0; 71 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF; 72 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS; 73 io.ntcreatex.in.security_flags = 0; 74 io.ntcreatex.in.fname = dname; 75 76 status = smb_raw_open(tree, mem_ctx, &io); 77 talloc_free(mem_ctx); 78 79 if (NT_STATUS_IS_OK(status)) { 80 *fnum = io.ntcreatex.out.file.fnum; 81 } 82 83 return status; 84} 85 86 87/** 88 sometimes we need a fairly complex file to work with, so we can test 89 all possible attributes. 90*/ 91_PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname) 92{ 93 int fnum; 94 char buf[7] = "abc"; 95 union smb_setfileinfo setfile; 96 union smb_fileinfo fileinfo; 97 time_t t = (time(NULL) & ~1); 98 NTSTATUS status; 99 100 smbcli_unlink(cli->tree, fname); 101 fnum = smbcli_nt_create_full(cli->tree, fname, 0, 102 SEC_RIGHTS_FILE_ALL, 103 FILE_ATTRIBUTE_NORMAL, 104 NTCREATEX_SHARE_ACCESS_DELETE| 105 NTCREATEX_SHARE_ACCESS_READ| 106 NTCREATEX_SHARE_ACCESS_WRITE, 107 NTCREATEX_DISP_OVERWRITE_IF, 108 0, 0); 109 if (fnum == -1) return -1; 110 111 smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf)); 112 113 if (strchr(fname, ':') == NULL) { 114 /* setup some EAs */ 115 setfile.generic.level = RAW_SFILEINFO_EA_SET; 116 setfile.generic.in.file.fnum = fnum; 117 setfile.ea_set.in.num_eas = 2; 118 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); 119 setfile.ea_set.in.eas[0].flags = 0; 120 setfile.ea_set.in.eas[0].name.s = "EAONE"; 121 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); 122 setfile.ea_set.in.eas[1].flags = 0; 123 setfile.ea_set.in.eas[1].name.s = "SECONDEA"; 124 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); 125 status = smb_raw_setfileinfo(cli->tree, &setfile); 126 if (!NT_STATUS_IS_OK(status)) { 127 printf("Failed to setup EAs\n"); 128 } 129 } 130 131 /* make sure all the timestamps aren't the same, and are also 132 in different DST zones*/ 133 setfile.generic.level = RAW_SFILEINFO_SETATTRE; 134 setfile.generic.in.file.fnum = fnum; 135 136 setfile.setattre.in.create_time = t + 9*30*24*60*60; 137 setfile.setattre.in.access_time = t + 6*30*24*60*60; 138 setfile.setattre.in.write_time = t + 3*30*24*60*60; 139 140 status = smb_raw_setfileinfo(cli->tree, &setfile); 141 if (!NT_STATUS_IS_OK(status)) { 142 printf("Failed to setup file times - %s\n", nt_errstr(status)); 143 } 144 145 /* make sure all the timestamps aren't the same */ 146 fileinfo.generic.level = RAW_FILEINFO_GETATTRE; 147 fileinfo.generic.in.file.fnum = fnum; 148 149 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); 150 if (!NT_STATUS_IS_OK(status)) { 151 printf("Failed to query file times - %s\n", nt_errstr(status)); 152 } 153 154 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) { 155 printf("create_time not setup correctly\n"); 156 } 157 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) { 158 printf("access_time not setup correctly\n"); 159 } 160 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) { 161 printf("write_time not setup correctly\n"); 162 } 163 164 return fnum; 165} 166 167 168/* 169 sometimes we need a fairly complex directory to work with, so we can test 170 all possible attributes. 171*/ 172int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname) 173{ 174 int fnum; 175 union smb_setfileinfo setfile; 176 union smb_fileinfo fileinfo; 177 time_t t = (time(NULL) & ~1); 178 NTSTATUS status; 179 180 smbcli_deltree(cli->tree, dname); 181 fnum = smbcli_nt_create_full(cli->tree, dname, 0, 182 SEC_RIGHTS_DIR_ALL, 183 FILE_ATTRIBUTE_DIRECTORY, 184 NTCREATEX_SHARE_ACCESS_READ| 185 NTCREATEX_SHARE_ACCESS_WRITE, 186 NTCREATEX_DISP_OPEN_IF, 187 NTCREATEX_OPTIONS_DIRECTORY, 0); 188 if (fnum == -1) return -1; 189 190 if (strchr(dname, ':') == NULL) { 191 /* setup some EAs */ 192 setfile.generic.level = RAW_SFILEINFO_EA_SET; 193 setfile.generic.in.file.fnum = fnum; 194 setfile.ea_set.in.num_eas = 2; 195 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2); 196 setfile.ea_set.in.eas[0].flags = 0; 197 setfile.ea_set.in.eas[0].name.s = "EAONE"; 198 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6); 199 setfile.ea_set.in.eas[1].flags = 0; 200 setfile.ea_set.in.eas[1].name.s = "SECONDEA"; 201 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8); 202 status = smb_raw_setfileinfo(cli->tree, &setfile); 203 if (!NT_STATUS_IS_OK(status)) { 204 printf("Failed to setup EAs\n"); 205 } 206 } 207 208 /* make sure all the timestamps aren't the same, and are also 209 in different DST zones*/ 210 setfile.generic.level = RAW_SFILEINFO_SETATTRE; 211 setfile.generic.in.file.fnum = fnum; 212 213 setfile.setattre.in.create_time = t + 9*30*24*60*60; 214 setfile.setattre.in.access_time = t + 6*30*24*60*60; 215 setfile.setattre.in.write_time = t + 3*30*24*60*60; 216 217 status = smb_raw_setfileinfo(cli->tree, &setfile); 218 if (!NT_STATUS_IS_OK(status)) { 219 printf("Failed to setup file times - %s\n", nt_errstr(status)); 220 } 221 222 /* make sure all the timestamps aren't the same */ 223 fileinfo.generic.level = RAW_FILEINFO_GETATTRE; 224 fileinfo.generic.in.file.fnum = fnum; 225 226 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo); 227 if (!NT_STATUS_IS_OK(status)) { 228 printf("Failed to query file times - %s\n", nt_errstr(status)); 229 } 230 231 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) { 232 printf("create_time not setup correctly\n"); 233 } 234 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) { 235 printf("access_time not setup correctly\n"); 236 } 237 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) { 238 printf("write_time not setup correctly\n"); 239 } 240 241 return fnum; 242} 243 244 245 246/* return a pointer to a anonymous shared memory segment of size "size" 247 which will persist across fork() but will disappear when all processes 248 exit 249 250 The memory is not zeroed 251 252 This function uses system5 shared memory. It takes advantage of a property 253 that the memory is not destroyed if it is attached when the id is removed 254 */ 255void *shm_setup(int size) 256{ 257 int shmid; 258 void *ret; 259 260#ifdef __QNXNTO__ 261 shmid = shm_open("private", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); 262 if (shmid == -1) { 263 printf("can't get shared memory\n"); 264 exit(1); 265 } 266 shm_unlink("private"); 267 if (ftruncate(shmid, size) == -1) { 268 printf("can't set shared memory size\n"); 269 exit(1); 270 } 271 ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0); 272 if (ret == MAP_FAILED) { 273 printf("can't map shared memory\n"); 274 exit(1); 275 } 276#else 277 shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W); 278 if (shmid == -1) { 279 printf("can't get shared memory\n"); 280 exit(1); 281 } 282 ret = (void *)shmat(shmid, 0, 0); 283 if (!ret || ret == (void *)-1) { 284 printf("can't attach to shared memory\n"); 285 return NULL; 286 } 287 /* the following releases the ipc, but note that this process 288 and all its children will still have access to the memory, its 289 just that the shmid is no longer valid for other shm calls. This 290 means we don't leave behind lots of shm segments after we exit 291 292 See Stevens "advanced programming in unix env" for details 293 */ 294 shmctl(shmid, IPC_RMID, 0); 295#endif 296 297 return ret; 298} 299 300 301/** 302 check that a wire string matches the flags specified 303 not 100% accurate, but close enough for testing 304*/ 305bool wire_bad_flags(struct smb_wire_string *str, int flags, 306 struct smbcli_transport *transport) 307{ 308 bool server_unicode; 309 int len; 310 if (!str || !str->s) return true; 311 len = strlen(str->s); 312 if (flags & STR_TERMINATE) len++; 313 314 server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false; 315 if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) { 316 server_unicode = false; 317 } 318 319 if ((flags & STR_UNICODE) || server_unicode) { 320 len *= 2; 321 } else if (flags & STR_TERMINATE_ASCII) { 322 len++; 323 } 324 if (str->private_length != len) { 325 printf("Expected wire_length %d but got %d for '%s'\n", 326 len, str->private_length, str->s); 327 return true; 328 } 329 return false; 330} 331 332/* 333 dump a all_info QFILEINFO structure 334*/ 335void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo) 336{ 337 d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time)); 338 d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time)); 339 d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time)); 340 d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time)); 341 d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib); 342 d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size); 343 d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size); 344 d_printf("\tnlink: %u\n", finfo->all_info.out.nlink); 345 d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending); 346 d_printf("\tdirectory: %u\n", finfo->all_info.out.directory); 347 d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size); 348 d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s); 349} 350 351/* 352 dump file infor by name 353*/ 354void torture_all_info(struct smbcli_tree *tree, const char *fname) 355{ 356 TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname); 357 union smb_fileinfo finfo; 358 NTSTATUS status; 359 360 finfo.generic.level = RAW_FILEINFO_ALL_INFO; 361 finfo.generic.in.file.path = fname; 362 status = smb_raw_pathinfo(tree, mem_ctx, &finfo); 363 if (!NT_STATUS_IS_OK(status)) { 364 d_printf("%s - %s\n", fname, nt_errstr(status)); 365 return; 366 } 367 368 d_printf("%s:\n", fname); 369 dump_all_info(mem_ctx, &finfo); 370 talloc_free(mem_ctx); 371} 372 373 374/* 375 set a attribute on a file 376*/ 377bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib) 378{ 379 union smb_setfileinfo sfinfo; 380 NTSTATUS status; 381 382 ZERO_STRUCT(sfinfo.basic_info.in); 383 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION; 384 sfinfo.basic_info.in.file.path = fname; 385 sfinfo.basic_info.in.attrib = attrib; 386 status = smb_raw_setpathinfo(tree, &sfinfo); 387 return NT_STATUS_IS_OK(status); 388} 389 390 391/* 392 set a file descriptor as sparse 393*/ 394NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum) 395{ 396 union smb_ioctl nt; 397 NTSTATUS status; 398 TALLOC_CTX *mem_ctx; 399 400 mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse"); 401 if (!mem_ctx) { 402 return NT_STATUS_NO_MEMORY; 403 } 404 405 nt.ntioctl.level = RAW_IOCTL_NTIOCTL; 406 nt.ntioctl.in.function = FSCTL_SET_SPARSE; 407 nt.ntioctl.in.file.fnum = fnum; 408 nt.ntioctl.in.fsctl = true; 409 nt.ntioctl.in.filter = 0; 410 nt.ntioctl.in.max_data = 0; 411 nt.ntioctl.in.blob = data_blob(NULL, 0); 412 413 status = smb_raw_ioctl(tree, mem_ctx, &nt); 414 415 talloc_free(mem_ctx); 416 417 return status; 418} 419 420/* 421 check that an EA has the right value 422*/ 423NTSTATUS torture_check_ea(struct smbcli_state *cli, 424 const char *fname, const char *eaname, const char *value) 425{ 426 union smb_fileinfo info; 427 NTSTATUS status; 428 struct ea_name ea; 429 TALLOC_CTX *mem_ctx = talloc_new(cli); 430 431 info.ea_list.level = RAW_FILEINFO_EA_LIST; 432 info.ea_list.in.file.path = fname; 433 info.ea_list.in.num_names = 1; 434 info.ea_list.in.ea_names = &ea; 435 436 ea.name.s = eaname; 437 438 status = smb_raw_pathinfo(cli->tree, mem_ctx, &info); 439 if (!NT_STATUS_IS_OK(status)) { 440 talloc_free(mem_ctx); 441 return status; 442 } 443 444 if (info.ea_list.out.num_eas != 1) { 445 printf("Expected 1 ea in ea_list\n"); 446 talloc_free(mem_ctx); 447 return NT_STATUS_EA_CORRUPT_ERROR; 448 } 449 450 if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) { 451 printf("Expected ea '%s' not '%s' in ea_list\n", 452 eaname, info.ea_list.out.eas[0].name.s); 453 talloc_free(mem_ctx); 454 return NT_STATUS_EA_CORRUPT_ERROR; 455 } 456 457 if (value == NULL) { 458 if (info.ea_list.out.eas[0].value.length != 0) { 459 printf("Expected zero length ea for %s\n", eaname); 460 talloc_free(mem_ctx); 461 return NT_STATUS_EA_CORRUPT_ERROR; 462 } 463 talloc_free(mem_ctx); 464 return NT_STATUS_OK; 465 } 466 467 if (strlen(value) == info.ea_list.out.eas[0].value.length && 468 memcmp(value, info.ea_list.out.eas[0].value.data, 469 info.ea_list.out.eas[0].value.length) == 0) { 470 talloc_free(mem_ctx); 471 return NT_STATUS_OK; 472 } 473 474 printf("Expected value '%s' not '%*.*s' for ea %s\n", 475 value, 476 (int)info.ea_list.out.eas[0].value.length, 477 (int)info.ea_list.out.eas[0].value.length, 478 info.ea_list.out.eas[0].value.data, 479 eaname); 480 481 talloc_free(mem_ctx); 482 483 return NT_STATUS_EA_CORRUPT_ERROR; 484} 485 486_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx, 487 struct smbcli_state **c, 488 struct torture_context *tctx, 489 const char *hostname, 490 const char *sharename, 491 struct tevent_context *ev) 492{ 493 NTSTATUS status; 494 495 struct smbcli_options options; 496 struct smbcli_session_options session_options; 497 498 lp_smbcli_options(tctx->lp_ctx, &options); 499 lp_smbcli_session_options(tctx->lp_ctx, &session_options); 500 501 options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true); 502 options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true); 503 504 status = smbcli_full_connection(mem_ctx, c, hostname, 505 lp_smb_ports(tctx->lp_ctx), 506 sharename, NULL, 507 lp_socket_options(tctx->lp_ctx), 508 cmdline_credentials, 509 lp_resolve_context(tctx->lp_ctx), 510 ev, &options, &session_options, 511 lp_iconv_convenience(tctx->lp_ctx), 512 lp_gensec_settings(tctx, tctx->lp_ctx)); 513 if (!NT_STATUS_IS_OK(status)) { 514 printf("Failed to open connection - %s\n", nt_errstr(status)); 515 return false; 516 } 517 518 return true; 519} 520 521_PUBLIC_ bool torture_get_conn_index(int conn_index, 522 TALLOC_CTX *mem_ctx, 523 struct torture_context *tctx, 524 char **host, char **share) 525{ 526 char **unc_list = NULL; 527 int num_unc_names = 0; 528 const char *p; 529 530 (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL)); 531 (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL)); 532 533 p = torture_setting_string(tctx, "unclist", NULL); 534 if (!p) { 535 return true; 536 } 537 538 unc_list = file_lines_load(p, &num_unc_names, 0, NULL); 539 if (!unc_list || num_unc_names <= 0) { 540 DEBUG(0,("Failed to load unc names list from '%s'\n", p)); 541 return false; 542 } 543 544 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names], 545 mem_ctx, host, share)) { 546 DEBUG(0, ("Failed to parse UNC name %s\n", 547 unc_list[conn_index % num_unc_names])); 548 return false; 549 } 550 551 talloc_free(unc_list); 552 return true; 553} 554 555 556 557_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c, 558 int conn_index, 559 struct torture_context *tctx, 560 struct tevent_context *ev) 561{ 562 char *host, *share; 563 bool ret; 564 565 if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) { 566 return false; 567 } 568 569 ret = torture_open_connection_share(NULL, c, tctx, host, share, ev); 570 talloc_free(host); 571 talloc_free(share); 572 573 return ret; 574} 575 576_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index) 577{ 578 return torture_open_connection_ev(c, conn_index, tctx, tctx->ev); 579} 580 581 582 583_PUBLIC_ bool torture_close_connection(struct smbcli_state *c) 584{ 585 bool ret = true; 586 if (!c) return true; 587 if (NT_STATUS_IS_ERR(smbcli_tdis(c))) { 588 printf("tdis failed (%s)\n", smbcli_errstr(c->tree)); 589 ret = false; 590 } 591 talloc_free(c); 592 return ret; 593} 594 595 596/* check if the server produced the expected error code */ 597_PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 598 uint8_t eclass, uint32_t ecode, NTSTATUS nterr) 599{ 600 NTSTATUS status; 601 602 status = smbcli_nt_error(c->tree); 603 if (NT_STATUS_IS_DOS(status)) { 604 int classnum, num; 605 classnum = NT_STATUS_DOS_CLASS(status); 606 num = NT_STATUS_DOS_CODE(status); 607 if (eclass != classnum || ecode != num) { 608 printf("unexpected error code %s\n", nt_errstr(status)); 609 printf(" expected %s or %s (at %s)\n", 610 nt_errstr(NT_STATUS_DOS(eclass, ecode)), 611 nt_errstr(nterr), location); 612 return false; 613 } 614 } else { 615 if (!NT_STATUS_EQUAL(nterr, status)) { 616 printf("unexpected error code %s\n", nt_errstr(status)); 617 printf(" expected %s (at %s)\n", nt_errstr(nterr), location); 618 return false; 619 } 620 } 621 622 return true; 623} 624 625static struct smbcli_state *current_cli; 626static int procnum; /* records process count number when forking */ 627 628static void sigcont(int sig) 629{ 630} 631 632double torture_create_procs(struct torture_context *tctx, 633 bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result) 634{ 635 int i, status; 636 volatile pid_t *child_status; 637 volatile bool *child_status_out; 638 int synccount; 639 int tries = 8; 640 int torture_nprocs = torture_setting_int(tctx, "nprocs", 4); 641 double start_time_limit = 10 + (torture_nprocs * 1.5); 642 struct timeval tv; 643 644 *result = true; 645 646 synccount = 0; 647 648 signal(SIGCONT, sigcont); 649 650 child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs); 651 if (!child_status) { 652 printf("Failed to setup shared memory\n"); 653 return -1; 654 } 655 656 child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs); 657 if (!child_status_out) { 658 printf("Failed to setup result status shared memory\n"); 659 return -1; 660 } 661 662 for (i = 0; i < torture_nprocs; i++) { 663 child_status[i] = 0; 664 child_status_out[i] = true; 665 } 666 667 tv = timeval_current(); 668 669 for (i=0;i<torture_nprocs;i++) { 670 procnum = i; 671 if (fork() == 0) { 672 char *myname; 673 674 pid_t mypid = getpid(); 675 srandom(((int)mypid) ^ ((int)time(NULL))); 676 677 if (asprintf(&myname, "CLIENT%d", i) == -1) { 678 printf("asprintf failed\n"); 679 return -1; 680 } 681 lp_set_cmdline(tctx->lp_ctx, "netbios name", myname); 682 free(myname); 683 684 685 while (1) { 686 if (torture_open_connection(¤t_cli, tctx, i)) { 687 break; 688 } 689 if (tries-- == 0) { 690 printf("pid %d failed to start\n", (int)getpid()); 691 _exit(1); 692 } 693 msleep(100); 694 } 695 696 child_status[i] = getpid(); 697 698 pause(); 699 700 if (child_status[i]) { 701 printf("Child %d failed to start!\n", i); 702 child_status_out[i] = 1; 703 _exit(1); 704 } 705 706 child_status_out[i] = fn(tctx, current_cli, i); 707 _exit(0); 708 } 709 } 710 711 do { 712 synccount = 0; 713 for (i=0;i<torture_nprocs;i++) { 714 if (child_status[i]) synccount++; 715 } 716 if (synccount == torture_nprocs) break; 717 msleep(100); 718 } while (timeval_elapsed(&tv) < start_time_limit); 719 720 if (synccount != torture_nprocs) { 721 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount); 722 *result = false; 723 return timeval_elapsed(&tv); 724 } 725 726 printf("Starting %d clients\n", torture_nprocs); 727 728 /* start the client load */ 729 tv = timeval_current(); 730 for (i=0;i<torture_nprocs;i++) { 731 child_status[i] = 0; 732 } 733 734 printf("%d clients started\n", torture_nprocs); 735 736 kill(0, SIGCONT); 737 738 for (i=0;i<torture_nprocs;i++) { 739 int ret; 740 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ; 741 if (ret == -1 || WEXITSTATUS(status) != 0) { 742 *result = false; 743 } 744 } 745 746 printf("\n"); 747 748 for (i=0;i<torture_nprocs;i++) { 749 if (!child_status_out[i]) { 750 *result = false; 751 } 752 } 753 return timeval_elapsed(&tv); 754} 755 756static bool wrap_smb_multi_test(struct torture_context *torture, 757 struct torture_tcase *tcase, 758 struct torture_test *test) 759{ 760 bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn; 761 bool result; 762 763 torture_create_procs(torture, fn, &result); 764 765 return result; 766} 767 768_PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test( 769 struct torture_suite *suite, 770 const char *name, 771 bool (*run) (struct torture_context *, 772 struct smbcli_state *, 773 int i)) 774{ 775 struct torture_test *test; 776 struct torture_tcase *tcase; 777 778 tcase = torture_suite_add_tcase(suite, name); 779 780 test = talloc(tcase, struct torture_test); 781 782 test->name = talloc_strdup(test, name); 783 test->description = NULL; 784 test->run = wrap_smb_multi_test; 785 test->fn = run; 786 test->dangerous = false; 787 788 DLIST_ADD_END(tcase->tests, test, struct torture_test *); 789 790 return test; 791 792} 793 794static bool wrap_simple_2smb_test(struct torture_context *torture_ctx, 795 struct torture_tcase *tcase, 796 struct torture_test *test) 797{ 798 bool (*fn) (struct torture_context *, struct smbcli_state *, 799 struct smbcli_state *); 800 bool ret; 801 802 struct smbcli_state *cli1, *cli2; 803 804 if (!torture_open_connection(&cli1, torture_ctx, 0) || 805 !torture_open_connection(&cli2, torture_ctx, 1)) 806 return false; 807 808 fn = test->fn; 809 810 ret = fn(torture_ctx, cli1, cli2); 811 812 talloc_free(cli1); 813 talloc_free(cli2); 814 815 return ret; 816} 817 818 819 820_PUBLIC_ struct torture_test *torture_suite_add_2smb_test( 821 struct torture_suite *suite, 822 const char *name, 823 bool (*run) (struct torture_context *, 824 struct smbcli_state *, 825 struct smbcli_state *)) 826{ 827 struct torture_test *test; 828 struct torture_tcase *tcase; 829 830 tcase = torture_suite_add_tcase(suite, name); 831 832 test = talloc(tcase, struct torture_test); 833 834 test->name = talloc_strdup(test, name); 835 test->description = NULL; 836 test->run = wrap_simple_2smb_test; 837 test->fn = run; 838 test->dangerous = false; 839 840 DLIST_ADD_END(tcase->tests, test, struct torture_test *); 841 842 return test; 843 844} 845 846static bool wrap_simple_1smb_test(struct torture_context *torture_ctx, 847 struct torture_tcase *tcase, 848 struct torture_test *test) 849{ 850 bool (*fn) (struct torture_context *, struct smbcli_state *); 851 bool ret; 852 853 struct smbcli_state *cli1; 854 855 if (!torture_open_connection(&cli1, torture_ctx, 0)) 856 return false; 857 858 fn = test->fn; 859 860 ret = fn(torture_ctx, cli1); 861 862 talloc_free(cli1); 863 864 return ret; 865} 866 867_PUBLIC_ struct torture_test *torture_suite_add_1smb_test( 868 struct torture_suite *suite, 869 const char *name, 870 bool (*run) (struct torture_context *, struct smbcli_state *)) 871{ 872 struct torture_test *test; 873 struct torture_tcase *tcase; 874 875 tcase = torture_suite_add_tcase(suite, name); 876 877 test = talloc(tcase, struct torture_test); 878 879 test->name = talloc_strdup(test, name); 880 test->description = NULL; 881 test->run = wrap_simple_1smb_test; 882 test->fn = run; 883 test->dangerous = false; 884 885 DLIST_ADD_END(tcase->tests, test, struct torture_test *); 886 887 return test; 888} 889 890 891NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx, 892 struct smbcli_session *session, 893 const char *sharename, 894 struct smbcli_tree **res) 895{ 896 union smb_tcon tcon; 897 struct smbcli_tree *result; 898 TALLOC_CTX *tmp_ctx; 899 NTSTATUS status; 900 901 if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) { 902 return NT_STATUS_NO_MEMORY; 903 } 904 905 result = smbcli_tree_init(session, tmp_ctx, false); 906 if (result == NULL) { 907 talloc_free(tmp_ctx); 908 return NT_STATUS_NO_MEMORY; 909 } 910 911 tcon.generic.level = RAW_TCON_TCONX; 912 tcon.tconx.in.flags = 0; 913 914 /* Ignore share mode security here */ 915 tcon.tconx.in.password = data_blob(NULL, 0); 916 tcon.tconx.in.path = sharename; 917 tcon.tconx.in.device = "?????"; 918 919 status = smb_raw_tcon(result, tmp_ctx, &tcon); 920 if (!NT_STATUS_IS_OK(status)) { 921 talloc_free(tmp_ctx); 922 return status; 923 } 924 925 result->tid = tcon.tconx.out.tid; 926 *res = talloc_steal(mem_ctx, result); 927 talloc_free(tmp_ctx); 928 return NT_STATUS_OK; 929} 930