2 Unix SMB/CIFS implementation.
3 SMB torture tester utility functions
4 Copyright (C) Andrew Tridgell 2003
5 Copyright (C) Jelmer Vernooij 2006
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.
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.
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/>.
22 #include "lib/cmdline/popt_common.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "libcli/raw/ioctl.h"
25 #include "libcli/libcli.h"
26 #include "system/filesys.h"
27 #include "system/shmem.h"
28 #include "system/wait.h"
29 #include "system/time.h"
30 #include "torture/ui.h"
31 #include "torture/torture.h"
32 #include "util/dlinklist.h"
33 #include "auth/credentials/credentials.h"
34 #include "param/param.h"
38 setup a directory ready for a test
40 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
42 smb_raw_exit(cli->session);
43 if (smbcli_deltree(cli->tree, dname) == -1 ||
44 NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
45 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
52 create a directory, returning a handle to it
54 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
60 mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
62 io.generic.level = RAW_OPEN_NTCREATEX;
63 io.ntcreatex.in.root_fid = 0;
64 io.ntcreatex.in.flags = 0;
65 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
66 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
67 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
68 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
69 io.ntcreatex.in.alloc_size = 0;
70 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
71 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
72 io.ntcreatex.in.security_flags = 0;
73 io.ntcreatex.in.fname = dname;
75 status = smb_raw_open(tree, mem_ctx, &io);
78 if (NT_STATUS_IS_OK(status)) {
79 *fnum = io.ntcreatex.out.file.fnum;
87 sometimes we need a fairly complex file to work with, so we can test
88 all possible attributes.
90 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
94 union smb_setfileinfo setfile;
95 union smb_fileinfo fileinfo;
96 time_t t = (time(NULL) & ~1);
99 smbcli_unlink(cli->tree, fname);
100 fnum = smbcli_nt_create_full(cli->tree, fname, 0,
102 FILE_ATTRIBUTE_NORMAL,
103 NTCREATEX_SHARE_ACCESS_DELETE|
104 NTCREATEX_SHARE_ACCESS_READ|
105 NTCREATEX_SHARE_ACCESS_WRITE,
106 NTCREATEX_DISP_OVERWRITE_IF,
108 if (fnum == -1) return -1;
110 smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
112 if (strchr(fname, ':') == NULL) {
114 setfile.generic.level = RAW_SFILEINFO_EA_SET;
115 setfile.generic.in.file.fnum = fnum;
116 setfile.ea_set.in.num_eas = 2;
117 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
118 setfile.ea_set.in.eas[0].flags = 0;
119 setfile.ea_set.in.eas[0].name.s = "EAONE";
120 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
121 setfile.ea_set.in.eas[1].flags = 0;
122 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
123 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
124 status = smb_raw_setfileinfo(cli->tree, &setfile);
125 if (!NT_STATUS_IS_OK(status)) {
126 printf("Failed to setup EAs\n");
130 /* make sure all the timestamps aren't the same, and are also
131 in different DST zones*/
132 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
133 setfile.generic.in.file.fnum = fnum;
135 setfile.setattre.in.create_time = t + 9*30*24*60*60;
136 setfile.setattre.in.access_time = t + 6*30*24*60*60;
137 setfile.setattre.in.write_time = t + 3*30*24*60*60;
139 status = smb_raw_setfileinfo(cli->tree, &setfile);
140 if (!NT_STATUS_IS_OK(status)) {
141 printf("Failed to setup file times - %s\n", nt_errstr(status));
144 /* make sure all the timestamps aren't the same */
145 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
146 fileinfo.generic.in.file.fnum = fnum;
148 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
149 if (!NT_STATUS_IS_OK(status)) {
150 printf("Failed to query file times - %s\n", nt_errstr(status));
153 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
154 printf("create_time not setup correctly\n");
156 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
157 printf("access_time not setup correctly\n");
159 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
160 printf("write_time not setup correctly\n");
168 sometimes we need a fairly complex directory to work with, so we can test
169 all possible attributes.
171 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
174 union smb_setfileinfo setfile;
175 union smb_fileinfo fileinfo;
176 time_t t = (time(NULL) & ~1);
179 smbcli_deltree(cli->tree, dname);
180 fnum = smbcli_nt_create_full(cli->tree, dname, 0,
182 FILE_ATTRIBUTE_DIRECTORY,
183 NTCREATEX_SHARE_ACCESS_READ|
184 NTCREATEX_SHARE_ACCESS_WRITE,
185 NTCREATEX_DISP_OPEN_IF,
186 NTCREATEX_OPTIONS_DIRECTORY, 0);
187 if (fnum == -1) return -1;
189 if (strchr(dname, ':') == NULL) {
191 setfile.generic.level = RAW_SFILEINFO_EA_SET;
192 setfile.generic.in.file.fnum = fnum;
193 setfile.ea_set.in.num_eas = 2;
194 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
195 setfile.ea_set.in.eas[0].flags = 0;
196 setfile.ea_set.in.eas[0].name.s = "EAONE";
197 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
198 setfile.ea_set.in.eas[1].flags = 0;
199 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
200 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
201 status = smb_raw_setfileinfo(cli->tree, &setfile);
202 if (!NT_STATUS_IS_OK(status)) {
203 printf("Failed to setup EAs\n");
207 /* make sure all the timestamps aren't the same, and are also
208 in different DST zones*/
209 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
210 setfile.generic.in.file.fnum = fnum;
212 setfile.setattre.in.create_time = t + 9*30*24*60*60;
213 setfile.setattre.in.access_time = t + 6*30*24*60*60;
214 setfile.setattre.in.write_time = t + 3*30*24*60*60;
216 status = smb_raw_setfileinfo(cli->tree, &setfile);
217 if (!NT_STATUS_IS_OK(status)) {
218 printf("Failed to setup file times - %s\n", nt_errstr(status));
221 /* make sure all the timestamps aren't the same */
222 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
223 fileinfo.generic.in.file.fnum = fnum;
225 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
226 if (!NT_STATUS_IS_OK(status)) {
227 printf("Failed to query file times - %s\n", nt_errstr(status));
230 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
231 printf("create_time not setup correctly\n");
233 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
234 printf("access_time not setup correctly\n");
236 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
237 printf("write_time not setup correctly\n");
245 /* return a pointer to a anonymous shared memory segment of size "size"
246 which will persist across fork() but will disappear when all processes
249 The memory is not zeroed
251 This function uses system5 shared memory. It takes advantage of a property
252 that the memory is not destroyed if it is attached when the id is removed
254 void *shm_setup(int size)
259 shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
261 printf("can't get shared memory\n");
264 ret = (void *)shmat(shmid, 0, 0);
265 if (!ret || ret == (void *)-1) {
266 printf("can't attach to shared memory\n");
269 /* the following releases the ipc, but note that this process
270 and all its children will still have access to the memory, its
271 just that the shmid is no longer valid for other shm calls. This
272 means we don't leave behind lots of shm segments after we exit
274 See Stevens "advanced programming in unix env" for details
276 shmctl(shmid, IPC_RMID, 0);
283 check that a wire string matches the flags specified
284 not 100% accurate, but close enough for testing
286 bool wire_bad_flags(struct smb_wire_string *str, int flags,
287 struct smbcli_transport *transport)
291 if (!str || !str->s) return true;
292 len = strlen(str->s);
293 if (flags & STR_TERMINATE) len++;
295 server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
296 if (getenv("CLI_FORCE_ASCII") || !lp_unicode(global_loadparm)) {
297 server_unicode = false;
300 if ((flags & STR_UNICODE) || server_unicode) {
302 } else if (flags & STR_TERMINATE_ASCII) {
305 if (str->private_length != len) {
306 printf("Expected wire_length %d but got %d for '%s'\n",
307 len, str->private_length, str->s);
314 dump a all_info QFILEINFO structure
316 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
318 d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
319 d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
320 d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
321 d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
322 d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib);
323 d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size);
324 d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size);
325 d_printf("\tnlink: %u\n", finfo->all_info.out.nlink);
326 d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
327 d_printf("\tdirectory: %u\n", finfo->all_info.out.directory);
328 d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size);
329 d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s);
333 dump file infor by name
335 void torture_all_info(struct smbcli_tree *tree, const char *fname)
337 TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
338 union smb_fileinfo finfo;
341 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
342 finfo.generic.in.file.path = fname;
343 status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
344 if (!NT_STATUS_IS_OK(status)) {
345 d_printf("%s - %s\n", fname, nt_errstr(status));
349 d_printf("%s:\n", fname);
350 dump_all_info(mem_ctx, &finfo);
351 talloc_free(mem_ctx);
356 set a attribute on a file
358 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
360 union smb_setfileinfo sfinfo;
363 ZERO_STRUCT(sfinfo.basic_info.in);
364 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
365 sfinfo.basic_info.in.file.path = fname;
366 sfinfo.basic_info.in.attrib = attrib;
367 status = smb_raw_setpathinfo(tree, &sfinfo);
368 return NT_STATUS_IS_OK(status);
373 set a file descriptor as sparse
375 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
381 mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
383 return NT_STATUS_NO_MEMORY;
386 nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
387 nt.ntioctl.in.function = FSCTL_SET_SPARSE;
388 nt.ntioctl.in.file.fnum = fnum;
389 nt.ntioctl.in.fsctl = true;
390 nt.ntioctl.in.filter = 0;
391 nt.ntioctl.in.max_data = 0;
392 nt.ntioctl.in.blob = data_blob(NULL, 0);
394 status = smb_raw_ioctl(tree, mem_ctx, &nt);
396 talloc_free(mem_ctx);
402 check that an EA has the right value
404 NTSTATUS torture_check_ea(struct smbcli_state *cli,
405 const char *fname, const char *eaname, const char *value)
407 union smb_fileinfo info;
410 TALLOC_CTX *mem_ctx = talloc_new(cli);
412 info.ea_list.level = RAW_FILEINFO_EA_LIST;
413 info.ea_list.in.file.path = fname;
414 info.ea_list.in.num_names = 1;
415 info.ea_list.in.ea_names = &ea;
419 status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
420 if (!NT_STATUS_IS_OK(status)) {
421 talloc_free(mem_ctx);
425 if (info.ea_list.out.num_eas != 1) {
426 printf("Expected 1 ea in ea_list\n");
427 talloc_free(mem_ctx);
428 return NT_STATUS_EA_CORRUPT_ERROR;
431 if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
432 printf("Expected ea '%s' not '%s' in ea_list\n",
433 eaname, info.ea_list.out.eas[0].name.s);
434 talloc_free(mem_ctx);
435 return NT_STATUS_EA_CORRUPT_ERROR;
439 if (info.ea_list.out.eas[0].value.length != 0) {
440 printf("Expected zero length ea for %s\n", eaname);
441 talloc_free(mem_ctx);
442 return NT_STATUS_EA_CORRUPT_ERROR;
444 talloc_free(mem_ctx);
448 if (strlen(value) == info.ea_list.out.eas[0].value.length &&
449 memcmp(value, info.ea_list.out.eas[0].value.data,
450 info.ea_list.out.eas[0].value.length) == 0) {
451 talloc_free(mem_ctx);
455 printf("Expected value '%s' not '%*.*s' for ea %s\n",
457 (int)info.ea_list.out.eas[0].value.length,
458 (int)info.ea_list.out.eas[0].value.length,
459 info.ea_list.out.eas[0].value.data,
462 talloc_free(mem_ctx);
464 return NT_STATUS_EA_CORRUPT_ERROR;
467 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
468 struct smbcli_state **c,
469 struct torture_context *tctx,
470 const char *hostname,
471 const char *sharename,
472 struct event_context *ev)
476 status = smbcli_full_connection(mem_ctx, c, hostname,
477 lp_smb_ports(tctx->lp_ctx),
479 cmdline_credentials, ev);
480 if (!NT_STATUS_IS_OK(status)) {
481 printf("Failed to open connection - %s\n", nt_errstr(status));
485 (*c)->transport->options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", false);
486 (*c)->transport->options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", false);
491 _PUBLIC_ bool torture_get_conn_index(int conn_index,
493 struct torture_context *tctx,
494 char **host, char **share)
496 char **unc_list = NULL;
497 int num_unc_names = 0;
500 (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
501 (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
503 p = torture_setting_string(tctx, "unclist", NULL);
508 unc_list = file_lines_load(p, &num_unc_names, NULL);
509 if (!unc_list || num_unc_names <= 0) {
510 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
514 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
515 mem_ctx, host, share)) {
516 DEBUG(0, ("Failed to parse UNC name %s\n",
517 unc_list[conn_index % num_unc_names]));
521 talloc_free(unc_list);
527 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
529 struct torture_context *tctx,
530 struct event_context *ev)
535 if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
539 ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
546 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
548 return torture_open_connection_ev(c, conn_index, tctx,
549 cli_credentials_get_event_context(cmdline_credentials));
554 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
558 if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
559 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
567 /* check if the server produced the expected error code */
568 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c,
569 uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
573 status = smbcli_nt_error(c->tree);
574 if (NT_STATUS_IS_DOS(status)) {
576 class = NT_STATUS_DOS_CLASS(status);
577 num = NT_STATUS_DOS_CODE(status);
578 if (eclass != class || ecode != num) {
579 printf("unexpected error code %s\n", nt_errstr(status));
580 printf(" expected %s or %s (at %s)\n",
581 nt_errstr(NT_STATUS_DOS(eclass, ecode)),
582 nt_errstr(nterr), location);
586 if (!NT_STATUS_EQUAL(nterr, status)) {
587 printf("unexpected error code %s\n", nt_errstr(status));
588 printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
596 static struct smbcli_state *current_cli;
597 static int procnum; /* records process count number when forking */
599 static void sigcont(int sig)
603 double torture_create_procs(struct torture_context *tctx,
604 bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
607 volatile pid_t *child_status;
608 volatile bool *child_status_out;
611 int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
612 double start_time_limit = 10 + (torture_nprocs * 1.5);
619 signal(SIGCONT, sigcont);
621 child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
623 printf("Failed to setup shared memory\n");
627 child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
628 if (!child_status_out) {
629 printf("Failed to setup result status shared memory\n");
633 for (i = 0; i < torture_nprocs; i++) {
635 child_status_out[i] = true;
638 tv = timeval_current();
640 for (i=0;i<torture_nprocs;i++) {
645 pid_t mypid = getpid();
646 srandom(((int)mypid) ^ ((int)time(NULL)));
648 asprintf(&myname, "CLIENT%d", i);
649 lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
654 if (torture_open_connection(¤t_cli, tctx, i)) {
658 printf("pid %d failed to start\n", (int)getpid());
664 child_status[i] = getpid();
668 if (child_status[i]) {
669 printf("Child %d failed to start!\n", i);
670 child_status_out[i] = 1;
674 child_status_out[i] = fn(tctx, current_cli, i);
681 for (i=0;i<torture_nprocs;i++) {
682 if (child_status[i]) synccount++;
684 if (synccount == torture_nprocs) break;
686 } while (timeval_elapsed(&tv) < start_time_limit);
688 if (synccount != torture_nprocs) {
689 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
691 return timeval_elapsed(&tv);
694 printf("Starting %d clients\n", torture_nprocs);
696 /* start the client load */
697 tv = timeval_current();
698 for (i=0;i<torture_nprocs;i++) {
702 printf("%d clients started\n", torture_nprocs);
706 for (i=0;i<torture_nprocs;i++) {
708 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
709 if (ret == -1 || WEXITSTATUS(status) != 0) {
716 for (i=0;i<torture_nprocs;i++) {
717 if (!child_status_out[i]) {
721 return timeval_elapsed(&tv);
724 static bool wrap_smb_multi_test(struct torture_context *torture,
725 struct torture_tcase *tcase,
726 struct torture_test *test)
728 bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
731 torture_create_procs(torture, fn, &result);
736 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
737 struct torture_suite *suite,
739 bool (*run) (struct torture_context *,
740 struct smbcli_state *,
743 struct torture_test *test;
744 struct torture_tcase *tcase;
746 tcase = torture_suite_add_tcase(suite, name);
748 test = talloc(tcase, struct torture_test);
750 test->name = talloc_strdup(test, name);
751 test->description = NULL;
752 test->run = wrap_smb_multi_test;
754 test->dangerous = false;
756 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
762 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
763 struct torture_tcase *tcase,
764 struct torture_test *test)
766 bool (*fn) (struct torture_context *, struct smbcli_state *,
767 struct smbcli_state *);
770 struct smbcli_state *cli1, *cli2;
772 if (!torture_open_connection(&cli1, torture_ctx, 0) ||
773 !torture_open_connection(&cli2, torture_ctx, 1))
778 ret = fn(torture_ctx, cli1, cli2);
788 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
789 struct torture_suite *suite,
791 bool (*run) (struct torture_context *,
792 struct smbcli_state *,
793 struct smbcli_state *))
795 struct torture_test *test;
796 struct torture_tcase *tcase;
798 tcase = torture_suite_add_tcase(suite, name);
800 test = talloc(tcase, struct torture_test);
802 test->name = talloc_strdup(test, name);
803 test->description = NULL;
804 test->run = wrap_simple_2smb_test;
806 test->dangerous = false;
808 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
814 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
815 struct torture_tcase *tcase,
816 struct torture_test *test)
818 bool (*fn) (struct torture_context *, struct smbcli_state *);
821 struct smbcli_state *cli1;
823 if (!torture_open_connection(&cli1, torture_ctx, 0))
828 ret = fn(torture_ctx, cli1);
835 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
836 struct torture_suite *suite,
838 bool (*run) (struct torture_context *, struct smbcli_state *))
840 struct torture_test *test;
841 struct torture_tcase *tcase;
843 tcase = torture_suite_add_tcase(suite, name);
845 test = talloc(tcase, struct torture_test);
847 test->name = talloc_strdup(test, name);
848 test->description = NULL;
849 test->run = wrap_simple_1smb_test;
851 test->dangerous = false;
853 DLIST_ADD_END(tcase->tests, test, struct torture_test *);