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, struct smbcli_transport *transport)
290 if (!str || !str->s) return True;
291 len = strlen(str->s);
292 if (flags & STR_TERMINATE) len++;
294 server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?True:False;
295 if (getenv("CLI_FORCE_ASCII") || !lp_unicode(global_loadparm)) {
296 server_unicode = False;
299 if ((flags & STR_UNICODE) || server_unicode) {
301 } else if (flags & STR_TERMINATE_ASCII) {
304 if (str->private_length != len) {
305 printf("Expected wire_length %d but got %d for '%s'\n",
306 len, str->private_length, str->s);
313 dump a all_info QFILEINFO structure
315 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
317 d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
318 d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
319 d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
320 d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
321 d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib);
322 d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size);
323 d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size);
324 d_printf("\tnlink: %u\n", finfo->all_info.out.nlink);
325 d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
326 d_printf("\tdirectory: %u\n", finfo->all_info.out.directory);
327 d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size);
328 d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s);
332 dump file infor by name
334 void torture_all_info(struct smbcli_tree *tree, const char *fname)
336 TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
337 union smb_fileinfo finfo;
340 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
341 finfo.generic.in.file.path = fname;
342 status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
343 if (!NT_STATUS_IS_OK(status)) {
344 d_printf("%s - %s\n", fname, nt_errstr(status));
348 d_printf("%s:\n", fname);
349 dump_all_info(mem_ctx, &finfo);
350 talloc_free(mem_ctx);
355 set a attribute on a file
357 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
359 union smb_setfileinfo sfinfo;
362 ZERO_STRUCT(sfinfo.basic_info.in);
363 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
364 sfinfo.basic_info.in.file.path = fname;
365 sfinfo.basic_info.in.attrib = attrib;
366 status = smb_raw_setpathinfo(tree, &sfinfo);
367 return NT_STATUS_IS_OK(status);
372 set a file descriptor as sparse
374 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
380 mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
382 return NT_STATUS_NO_MEMORY;
385 nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
386 nt.ntioctl.in.function = FSCTL_SET_SPARSE;
387 nt.ntioctl.in.file.fnum = fnum;
388 nt.ntioctl.in.fsctl = True;
389 nt.ntioctl.in.filter = 0;
390 nt.ntioctl.in.max_data = 0;
391 nt.ntioctl.in.blob = data_blob(NULL, 0);
393 status = smb_raw_ioctl(tree, mem_ctx, &nt);
395 talloc_free(mem_ctx);
401 check that an EA has the right value
403 NTSTATUS torture_check_ea(struct smbcli_state *cli,
404 const char *fname, const char *eaname, const char *value)
406 union smb_fileinfo info;
409 TALLOC_CTX *mem_ctx = talloc_new(cli);
411 info.ea_list.level = RAW_FILEINFO_EA_LIST;
412 info.ea_list.in.file.path = fname;
413 info.ea_list.in.num_names = 1;
414 info.ea_list.in.ea_names = &ea;
418 status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
419 if (!NT_STATUS_IS_OK(status)) {
420 talloc_free(mem_ctx);
424 if (info.ea_list.out.num_eas != 1) {
425 printf("Expected 1 ea in ea_list\n");
426 talloc_free(mem_ctx);
427 return NT_STATUS_EA_CORRUPT_ERROR;
430 if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
431 printf("Expected ea '%s' not '%s' in ea_list\n",
432 eaname, info.ea_list.out.eas[0].name.s);
433 talloc_free(mem_ctx);
434 return NT_STATUS_EA_CORRUPT_ERROR;
438 if (info.ea_list.out.eas[0].value.length != 0) {
439 printf("Expected zero length ea for %s\n", eaname);
440 talloc_free(mem_ctx);
441 return NT_STATUS_EA_CORRUPT_ERROR;
443 talloc_free(mem_ctx);
447 if (strlen(value) == info.ea_list.out.eas[0].value.length &&
448 memcmp(value, info.ea_list.out.eas[0].value.data,
449 info.ea_list.out.eas[0].value.length) == 0) {
450 talloc_free(mem_ctx);
454 printf("Expected value '%s' not '%*.*s' for ea %s\n",
456 (int)info.ea_list.out.eas[0].value.length,
457 (int)info.ea_list.out.eas[0].value.length,
458 info.ea_list.out.eas[0].value.data,
461 talloc_free(mem_ctx);
463 return NT_STATUS_EA_CORRUPT_ERROR;
466 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
467 struct smbcli_state **c,
468 const char *hostname,
469 const char *sharename,
470 struct event_context *ev)
474 status = smbcli_full_connection(mem_ctx, c, hostname,
476 cmdline_credentials, ev);
477 if (!NT_STATUS_IS_OK(status)) {
478 printf("Failed to open connection - %s\n", nt_errstr(status));
482 (*c)->transport->options.use_oplocks = lp_parm_bool(global_loadparm, NULL, "torture",
483 "use_oplocks", false);
484 (*c)->transport->options.use_level2_oplocks = lp_parm_bool(global_loadparm, NULL, "torture",
485 "use_level2_oplocks", false);
490 _PUBLIC_ bool torture_get_conn_index(int conn_index,
492 char **host, char **share)
494 char **unc_list = NULL;
495 int num_unc_names = 0;
498 (*host) = talloc_strdup(mem_ctx, lp_parm_string(global_loadparm, NULL, "torture", "host"));
499 (*share) = talloc_strdup(mem_ctx, lp_parm_string(global_loadparm, NULL, "torture", "share"));
501 p = lp_parm_string(global_loadparm, NULL, "torture", "unclist");
506 unc_list = file_lines_load(p, &num_unc_names, NULL);
507 if (!unc_list || num_unc_names <= 0) {
508 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
512 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
513 mem_ctx, host, share)) {
514 DEBUG(0, ("Failed to parse UNC name %s\n",
515 unc_list[conn_index % num_unc_names]));
519 talloc_free(unc_list);
525 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
527 struct event_context *ev)
532 if (!torture_get_conn_index(conn_index, ev, &host, &share)) {
536 ret = torture_open_connection_share(NULL, c, host, share, ev);
543 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, int conn_index)
545 return torture_open_connection_ev(c, conn_index,
546 cli_credentials_get_event_context(cmdline_credentials));
551 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
555 if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
556 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
564 /* check if the server produced the expected error code */
565 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c,
566 uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
570 status = smbcli_nt_error(c->tree);
571 if (NT_STATUS_IS_DOS(status)) {
573 class = NT_STATUS_DOS_CLASS(status);
574 num = NT_STATUS_DOS_CODE(status);
575 if (eclass != class || ecode != num) {
576 printf("unexpected error code %s\n", nt_errstr(status));
577 printf(" expected %s or %s (at %s)\n",
578 nt_errstr(NT_STATUS_DOS(eclass, ecode)),
579 nt_errstr(nterr), location);
583 if (!NT_STATUS_EQUAL(nterr, status)) {
584 printf("unexpected error code %s\n", nt_errstr(status));
585 printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
593 static struct smbcli_state *current_cli;
594 static int procnum; /* records process count number when forking */
596 static void sigcont(int sig)
600 double torture_create_procs(struct torture_context *tctx,
601 bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
604 volatile pid_t *child_status;
605 volatile bool *child_status_out;
608 int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
609 double start_time_limit = 10 + (torture_nprocs * 1.5);
616 signal(SIGCONT, sigcont);
618 child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
620 printf("Failed to setup shared memory\n");
624 child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
625 if (!child_status_out) {
626 printf("Failed to setup result status shared memory\n");
630 for (i = 0; i < torture_nprocs; i++) {
632 child_status_out[i] = True;
635 tv = timeval_current();
637 for (i=0;i<torture_nprocs;i++) {
642 pid_t mypid = getpid();
643 srandom(((int)mypid) ^ ((int)time(NULL)));
645 asprintf(&myname, "CLIENT%d", i);
646 lp_set_cmdline(global_loadparm, "netbios name", myname);
651 if (torture_open_connection(¤t_cli, i)) {
655 printf("pid %d failed to start\n", (int)getpid());
661 child_status[i] = getpid();
665 if (child_status[i]) {
666 printf("Child %d failed to start!\n", i);
667 child_status_out[i] = 1;
671 child_status_out[i] = fn(tctx, current_cli, i);
678 for (i=0;i<torture_nprocs;i++) {
679 if (child_status[i]) synccount++;
681 if (synccount == torture_nprocs) break;
683 } while (timeval_elapsed(&tv) < start_time_limit);
685 if (synccount != torture_nprocs) {
686 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
688 return timeval_elapsed(&tv);
691 printf("Starting %d clients\n", torture_nprocs);
693 /* start the client load */
694 tv = timeval_current();
695 for (i=0;i<torture_nprocs;i++) {
699 printf("%d clients started\n", torture_nprocs);
703 for (i=0;i<torture_nprocs;i++) {
705 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
706 if (ret == -1 || WEXITSTATUS(status) != 0) {
713 for (i=0;i<torture_nprocs;i++) {
714 if (!child_status_out[i]) {
718 return timeval_elapsed(&tv);
721 static bool wrap_smb_multi_test(struct torture_context *torture,
722 struct torture_tcase *tcase,
723 struct torture_test *test)
725 bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
728 torture_create_procs(torture, fn, &result);
733 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
734 struct torture_suite *suite,
736 bool (*run) (struct torture_context *,
737 struct smbcli_state *,
740 struct torture_test *test;
741 struct torture_tcase *tcase;
743 tcase = torture_suite_add_tcase(suite, name);
745 test = talloc(tcase, struct torture_test);
747 test->name = talloc_strdup(test, name);
748 test->description = NULL;
749 test->run = wrap_smb_multi_test;
751 test->dangerous = false;
753 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
759 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
760 struct torture_tcase *tcase,
761 struct torture_test *test)
763 bool (*fn) (struct torture_context *, struct smbcli_state *,
764 struct smbcli_state *);
767 struct smbcli_state *cli1, *cli2;
769 if (!torture_open_connection(&cli1, 0) ||
770 !torture_open_connection(&cli2, 1))
775 ret = fn(torture_ctx, cli1, cli2);
785 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
786 struct torture_suite *suite,
788 bool (*run) (struct torture_context *,
789 struct smbcli_state *,
790 struct smbcli_state *))
792 struct torture_test *test;
793 struct torture_tcase *tcase;
795 tcase = torture_suite_add_tcase(suite, name);
797 test = talloc(tcase, struct torture_test);
799 test->name = talloc_strdup(test, name);
800 test->description = NULL;
801 test->run = wrap_simple_2smb_test;
803 test->dangerous = false;
805 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
811 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
812 struct torture_tcase *tcase,
813 struct torture_test *test)
815 bool (*fn) (struct torture_context *, struct smbcli_state *);
818 struct smbcli_state *cli1;
820 if (!torture_open_connection(&cli1, 0))
825 ret = fn(torture_ctx, cli1);
832 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
833 struct torture_suite *suite,
835 bool (*run) (struct torture_context *, struct smbcli_state *))
837 struct torture_test *test;
838 struct torture_tcase *tcase;
840 tcase = torture_suite_add_tcase(suite, name);
842 test = talloc(tcase, struct torture_test);
844 test->name = talloc_strdup(test, name);
845 test->description = NULL;
846 test->run = wrap_simple_1smb_test;
848 test->dangerous = false;
850 DLIST_ADD_END(tcase->tests, test, struct torture_test *);