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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "lib/cmdline/popt_common.h"
24 #include "libcli/raw/libcliraw.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/ui.h"
32 #include "torture/torture.h"
33 #include "util/dlinklist.h"
37 setup a directory ready for a test
39 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
41 smb_raw_exit(cli->session);
42 if (smbcli_deltree(cli->tree, dname) == -1 ||
43 NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
44 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
51 create a directory, returning a handle to it
53 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
59 mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
61 io.generic.level = RAW_OPEN_NTCREATEX;
62 io.ntcreatex.in.root_fid = 0;
63 io.ntcreatex.in.flags = 0;
64 io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
65 io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
66 io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
67 io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
68 io.ntcreatex.in.alloc_size = 0;
69 io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
70 io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
71 io.ntcreatex.in.security_flags = 0;
72 io.ntcreatex.in.fname = dname;
74 status = smb_raw_open(tree, mem_ctx, &io);
77 if (NT_STATUS_IS_OK(status)) {
78 *fnum = io.ntcreatex.out.file.fnum;
86 sometimes we need a fairly complex file to work with, so we can test
87 all possible attributes.
89 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
93 union smb_setfileinfo setfile;
94 union smb_fileinfo fileinfo;
95 time_t t = (time(NULL) & ~1);
98 smbcli_unlink(cli->tree, fname);
99 fnum = smbcli_nt_create_full(cli->tree, fname, 0,
101 FILE_ATTRIBUTE_NORMAL,
102 NTCREATEX_SHARE_ACCESS_DELETE|
103 NTCREATEX_SHARE_ACCESS_READ|
104 NTCREATEX_SHARE_ACCESS_WRITE,
105 NTCREATEX_DISP_OVERWRITE_IF,
107 if (fnum == -1) return -1;
109 smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
111 if (strchr(fname, ':') == NULL) {
113 setfile.generic.level = RAW_SFILEINFO_EA_SET;
114 setfile.generic.in.file.fnum = fnum;
115 setfile.ea_set.in.num_eas = 2;
116 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
117 setfile.ea_set.in.eas[0].flags = 0;
118 setfile.ea_set.in.eas[0].name.s = "EAONE";
119 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
120 setfile.ea_set.in.eas[1].flags = 0;
121 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
122 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
123 status = smb_raw_setfileinfo(cli->tree, &setfile);
124 if (!NT_STATUS_IS_OK(status)) {
125 printf("Failed to setup EAs\n");
129 /* make sure all the timestamps aren't the same, and are also
130 in different DST zones*/
131 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
132 setfile.generic.in.file.fnum = fnum;
134 setfile.setattre.in.create_time = t + 9*30*24*60*60;
135 setfile.setattre.in.access_time = t + 6*30*24*60*60;
136 setfile.setattre.in.write_time = t + 3*30*24*60*60;
138 status = smb_raw_setfileinfo(cli->tree, &setfile);
139 if (!NT_STATUS_IS_OK(status)) {
140 printf("Failed to setup file times - %s\n", nt_errstr(status));
143 /* make sure all the timestamps aren't the same */
144 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
145 fileinfo.generic.in.file.fnum = fnum;
147 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
148 if (!NT_STATUS_IS_OK(status)) {
149 printf("Failed to query file times - %s\n", nt_errstr(status));
152 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
153 printf("create_time not setup correctly\n");
155 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
156 printf("access_time not setup correctly\n");
158 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
159 printf("write_time not setup correctly\n");
167 sometimes we need a fairly complex directory to work with, so we can test
168 all possible attributes.
170 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
173 union smb_setfileinfo setfile;
174 union smb_fileinfo fileinfo;
175 time_t t = (time(NULL) & ~1);
178 smbcli_deltree(cli->tree, dname);
179 fnum = smbcli_nt_create_full(cli->tree, dname, 0,
181 FILE_ATTRIBUTE_DIRECTORY,
182 NTCREATEX_SHARE_ACCESS_READ|
183 NTCREATEX_SHARE_ACCESS_WRITE,
184 NTCREATEX_DISP_OPEN_IF,
185 NTCREATEX_OPTIONS_DIRECTORY, 0);
186 if (fnum == -1) return -1;
188 if (strchr(dname, ':') == NULL) {
190 setfile.generic.level = RAW_SFILEINFO_EA_SET;
191 setfile.generic.in.file.fnum = fnum;
192 setfile.ea_set.in.num_eas = 2;
193 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
194 setfile.ea_set.in.eas[0].flags = 0;
195 setfile.ea_set.in.eas[0].name.s = "EAONE";
196 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
197 setfile.ea_set.in.eas[1].flags = 0;
198 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
199 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
200 status = smb_raw_setfileinfo(cli->tree, &setfile);
201 if (!NT_STATUS_IS_OK(status)) {
202 printf("Failed to setup EAs\n");
206 /* make sure all the timestamps aren't the same, and are also
207 in different DST zones*/
208 setfile.generic.level = RAW_SFILEINFO_SETATTRE;
209 setfile.generic.in.file.fnum = fnum;
211 setfile.setattre.in.create_time = t + 9*30*24*60*60;
212 setfile.setattre.in.access_time = t + 6*30*24*60*60;
213 setfile.setattre.in.write_time = t + 3*30*24*60*60;
215 status = smb_raw_setfileinfo(cli->tree, &setfile);
216 if (!NT_STATUS_IS_OK(status)) {
217 printf("Failed to setup file times - %s\n", nt_errstr(status));
220 /* make sure all the timestamps aren't the same */
221 fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
222 fileinfo.generic.in.file.fnum = fnum;
224 status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
225 if (!NT_STATUS_IS_OK(status)) {
226 printf("Failed to query file times - %s\n", nt_errstr(status));
229 if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
230 printf("create_time not setup correctly\n");
232 if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
233 printf("access_time not setup correctly\n");
235 if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
236 printf("write_time not setup correctly\n");
244 /* return a pointer to a anonymous shared memory segment of size "size"
245 which will persist across fork() but will disappear when all processes
248 The memory is not zeroed
250 This function uses system5 shared memory. It takes advantage of a property
251 that the memory is not destroyed if it is attached when the id is removed
253 void *shm_setup(int size)
258 shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
260 printf("can't get shared memory\n");
263 ret = (void *)shmat(shmid, 0, 0);
264 if (!ret || ret == (void *)-1) {
265 printf("can't attach to shared memory\n");
268 /* the following releases the ipc, but note that this process
269 and all its children will still have access to the memory, its
270 just that the shmid is no longer valid for other shm calls. This
271 means we don't leave behind lots of shm segments after we exit
273 See Stevens "advanced programming in unix env" for details
275 shmctl(shmid, IPC_RMID, 0);
282 check that a wire string matches the flags specified
283 not 100% accurate, but close enough for testing
285 bool wire_bad_flags(struct smb_wire_string *str, int flags, struct smbcli_state *cli)
289 if (!str || !str->s) return True;
290 len = strlen(str->s);
291 if (flags & STR_TERMINATE) len++;
293 server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
294 if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
295 server_unicode = False;
298 if ((flags & STR_UNICODE) || server_unicode) {
300 } else if (flags & STR_TERMINATE_ASCII) {
303 if (str->private_length != len) {
304 printf("Expected wire_length %d but got %d for '%s'\n",
305 len, str->private_length, str->s);
312 dump a all_info QFILEINFO structure
314 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
316 d_printf("\tcreate_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
317 d_printf("\taccess_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
318 d_printf("\twrite_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
319 d_printf("\tchange_time: %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
320 d_printf("\tattrib: 0x%x\n", finfo->all_info.out.attrib);
321 d_printf("\talloc_size: %llu\n", (long long)finfo->all_info.out.alloc_size);
322 d_printf("\tsize: %llu\n", (long long)finfo->all_info.out.size);
323 d_printf("\tnlink: %u\n", finfo->all_info.out.nlink);
324 d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
325 d_printf("\tdirectory: %u\n", finfo->all_info.out.directory);
326 d_printf("\tea_size: %u\n", finfo->all_info.out.ea_size);
327 d_printf("\tfname: '%s'\n", finfo->all_info.out.fname.s);
331 dump file infor by name
333 void torture_all_info(struct smbcli_tree *tree, const char *fname)
335 TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
336 union smb_fileinfo finfo;
339 finfo.generic.level = RAW_FILEINFO_ALL_INFO;
340 finfo.generic.in.file.path = fname;
341 status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
342 if (!NT_STATUS_IS_OK(status)) {
343 d_printf("%s - %s\n", fname, nt_errstr(status));
347 d_printf("%s:\n", fname);
348 dump_all_info(mem_ctx, &finfo);
349 talloc_free(mem_ctx);
354 set a attribute on a file
356 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
358 union smb_setfileinfo sfinfo;
361 ZERO_STRUCT(sfinfo.basic_info.in);
362 sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
363 sfinfo.basic_info.in.file.path = fname;
364 sfinfo.basic_info.in.attrib = attrib;
365 status = smb_raw_setpathinfo(tree, &sfinfo);
366 return NT_STATUS_IS_OK(status);
371 set a file descriptor as sparse
373 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
379 mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
381 return NT_STATUS_NO_MEMORY;
384 nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
385 nt.ntioctl.in.function = FSCTL_SET_SPARSE;
386 nt.ntioctl.in.file.fnum = fnum;
387 nt.ntioctl.in.fsctl = True;
388 nt.ntioctl.in.filter = 0;
389 nt.ntioctl.in.max_data = 0;
390 nt.ntioctl.in.blob = data_blob(NULL, 0);
392 status = smb_raw_ioctl(tree, mem_ctx, &nt);
394 talloc_free(mem_ctx);
400 check that an EA has the right value
402 NTSTATUS torture_check_ea(struct smbcli_state *cli,
403 const char *fname, const char *eaname, const char *value)
405 union smb_fileinfo info;
408 TALLOC_CTX *mem_ctx = talloc_new(cli);
410 info.ea_list.level = RAW_FILEINFO_EA_LIST;
411 info.ea_list.in.file.path = fname;
412 info.ea_list.in.num_names = 1;
413 info.ea_list.in.ea_names = &ea;
417 status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
418 if (!NT_STATUS_IS_OK(status)) {
419 talloc_free(mem_ctx);
423 if (info.ea_list.out.num_eas != 1) {
424 printf("Expected 1 ea in ea_list\n");
425 talloc_free(mem_ctx);
426 return NT_STATUS_EA_CORRUPT_ERROR;
429 if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
430 printf("Expected ea '%s' not '%s' in ea_list\n",
431 eaname, info.ea_list.out.eas[0].name.s);
432 talloc_free(mem_ctx);
433 return NT_STATUS_EA_CORRUPT_ERROR;
437 if (info.ea_list.out.eas[0].value.length != 0) {
438 printf("Expected zero length ea for %s\n", eaname);
439 talloc_free(mem_ctx);
440 return NT_STATUS_EA_CORRUPT_ERROR;
442 talloc_free(mem_ctx);
446 if (strlen(value) == info.ea_list.out.eas[0].value.length &&
447 memcmp(value, info.ea_list.out.eas[0].value.data,
448 info.ea_list.out.eas[0].value.length) == 0) {
449 talloc_free(mem_ctx);
453 printf("Expected value '%s' not '%*.*s' for ea %s\n",
455 (int)info.ea_list.out.eas[0].value.length,
456 (int)info.ea_list.out.eas[0].value.length,
457 info.ea_list.out.eas[0].value.data,
460 talloc_free(mem_ctx);
462 return NT_STATUS_EA_CORRUPT_ERROR;
465 bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
466 struct smbcli_state **c,
467 const char *hostname,
468 const char *sharename,
469 struct event_context *ev)
473 status = smbcli_full_connection(mem_ctx, c, hostname,
475 cmdline_credentials, ev);
476 if (!NT_STATUS_IS_OK(status)) {
477 printf("Failed to open connection - %s\n", nt_errstr(status));
481 (*c)->transport->options.use_oplocks = lp_parm_bool(-1, "torture",
482 "use_oplocks", False);
483 (*c)->transport->options.use_level2_oplocks = lp_parm_bool(-1, "torture",
484 "use_level2_oplocks", False);
489 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, int conn_index)
491 const char *host = lp_parm_string(-1, "torture", "host");
492 const char *share = lp_parm_string(-1, "torture", "share");
493 char **unc_list = NULL;
494 int num_unc_names = 0;
497 p = lp_parm_string(-1, "torture", "unclist");
500 unc_list = file_lines_load(p, &num_unc_names, NULL);
501 if (!unc_list || num_unc_names <= 0) {
502 printf("Failed to load unc names list from '%s'\n", p);
506 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
508 printf("Failed to parse UNC name %s\n",
509 unc_list[conn_index % num_unc_names]);
516 return torture_open_connection_share(NULL, c, host, share, NULL);
519 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
521 struct event_context *ev)
523 const char *host = lp_parm_string(-1, "torture", "host");
524 const char *share = lp_parm_string(-1, "torture", "share");
525 char **unc_list = NULL;
526 int num_unc_names = 0;
529 p = lp_parm_string(-1, "torture", "unclist");
532 unc_list = file_lines_load(p, &num_unc_names, NULL);
533 if (!unc_list || num_unc_names <= 0) {
534 printf("Failed to load unc names list from '%s'\n", p);
538 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
540 printf("Failed to parse UNC name %s\n",
541 unc_list[conn_index % num_unc_names]);
549 return torture_open_connection_share(NULL, c, host, share, ev);
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 double start_time_limit = 10 + (torture_nprocs * 1.5);
618 signal(SIGCONT, sigcont);
620 child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
622 printf("Failed to setup shared memory\n");
626 child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
627 if (!child_status_out) {
628 printf("Failed to setup result status shared memory\n");
632 for (i = 0; i < torture_nprocs; i++) {
634 child_status_out[i] = True;
637 tv = timeval_current();
639 for (i=0;i<torture_nprocs;i++) {
644 pid_t mypid = getpid();
645 srandom(((int)mypid) ^ ((int)time(NULL)));
647 asprintf(&myname, "CLIENT%d", i);
648 lp_set_cmdline("netbios name", myname);
653 if (torture_open_connection(¤t_cli, i)) {
657 printf("pid %d failed to start\n", (int)getpid());
663 child_status[i] = getpid();
667 if (child_status[i]) {
668 printf("Child %d failed to start!\n", i);
669 child_status_out[i] = 1;
673 child_status_out[i] = fn(tctx, current_cli, i);
680 for (i=0;i<torture_nprocs;i++) {
681 if (child_status[i]) synccount++;
683 if (synccount == torture_nprocs) break;
685 } while (timeval_elapsed(&tv) < start_time_limit);
687 if (synccount != torture_nprocs) {
688 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
690 return timeval_elapsed(&tv);
693 printf("Starting %d clients\n", torture_nprocs);
695 /* start the client load */
696 tv = timeval_current();
697 for (i=0;i<torture_nprocs;i++) {
701 printf("%d clients started\n", torture_nprocs);
705 for (i=0;i<torture_nprocs;i++) {
707 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
708 if (ret == -1 || WEXITSTATUS(status) != 0) {
715 for (i=0;i<torture_nprocs;i++) {
716 if (!child_status_out[i]) {
720 return timeval_elapsed(&tv);
723 static bool wrap_smb_multi_test(struct torture_context *torture,
724 struct torture_tcase *tcase,
725 struct torture_test *test)
727 bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
730 torture_create_procs(torture, fn, &result);
735 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
736 struct torture_suite *suite,
738 bool (*run) (struct torture_context *,
739 struct smbcli_state *,
742 struct torture_test *test;
743 struct torture_tcase *tcase;
745 tcase = torture_suite_add_tcase(suite, name);
747 test = talloc(tcase, struct torture_test);
749 test->name = talloc_strdup(test, name);
750 test->description = NULL;
751 test->run = wrap_smb_multi_test;
753 test->dangerous = false;
755 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
761 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
762 struct torture_tcase *tcase,
763 struct torture_test *test)
765 bool (*fn) (struct torture_context *, struct smbcli_state *,
766 struct smbcli_state *);
769 struct smbcli_state *cli1, *cli2;
771 if (!torture_open_connection(&cli1, 0) ||
772 !torture_open_connection(&cli2, 1))
777 ret = fn(torture_ctx, cli1, cli2);
787 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
788 struct torture_suite *suite,
790 bool (*run) (struct torture_context *,
791 struct smbcli_state *,
792 struct smbcli_state *))
794 struct torture_test *test;
795 struct torture_tcase *tcase;
797 tcase = torture_suite_add_tcase(suite, name);
799 test = talloc(tcase, struct torture_test);
801 test->name = talloc_strdup(test, name);
802 test->description = NULL;
803 test->run = wrap_simple_2smb_test;
805 test->dangerous = false;
807 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
813 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
814 struct torture_tcase *tcase,
815 struct torture_test *test)
817 bool (*fn) (struct torture_context *, struct smbcli_state *);
820 struct smbcli_state *cli1;
822 if (!torture_open_connection(&cli1, 0))
827 ret = fn(torture_ctx, cli1);
834 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
835 struct torture_suite *suite,
837 bool (*run) (struct torture_context *,
838 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 *);