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"
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_transport *transport)
289 if (!str || !str->s) return True;
290 len = strlen(str->s);
291 if (flags & STR_TERMINATE) len++;
293 server_unicode = (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 _PUBLIC_ 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_get_conn_index(int conn_index,
491 char **host, char **share)
493 char **unc_list = NULL;
494 int num_unc_names = 0;
497 (*host) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "host"));
498 (*share) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "share"));
500 p = lp_parm_string(-1, "torture", "unclist");
505 unc_list = file_lines_load(p, &num_unc_names, NULL);
506 if (!unc_list || num_unc_names <= 0) {
507 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
511 if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
512 mem_ctx, host, share)) {
513 DEBUG(0, ("Failed to parse UNC name %s\n",
514 unc_list[conn_index % num_unc_names]));
518 talloc_free(unc_list);
524 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
526 struct event_context *ev)
531 if (!torture_get_conn_index(conn_index, ev, &host, &share)) {
535 ret = torture_open_connection_share(NULL, c, host, share, ev);
542 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, int conn_index)
544 return torture_open_connection_ev(c, conn_index,
545 cli_credentials_get_event_context(cmdline_credentials));
550 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
554 if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
555 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
563 /* check if the server produced the expected error code */
564 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c,
565 uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
569 status = smbcli_nt_error(c->tree);
570 if (NT_STATUS_IS_DOS(status)) {
572 class = NT_STATUS_DOS_CLASS(status);
573 num = NT_STATUS_DOS_CODE(status);
574 if (eclass != class || ecode != num) {
575 printf("unexpected error code %s\n", nt_errstr(status));
576 printf(" expected %s or %s (at %s)\n",
577 nt_errstr(NT_STATUS_DOS(eclass, ecode)),
578 nt_errstr(nterr), location);
582 if (!NT_STATUS_EQUAL(nterr, status)) {
583 printf("unexpected error code %s\n", nt_errstr(status));
584 printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
592 static struct smbcli_state *current_cli;
593 static int procnum; /* records process count number when forking */
595 static void sigcont(int sig)
599 double torture_create_procs(struct torture_context *tctx,
600 bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
603 volatile pid_t *child_status;
604 volatile bool *child_status_out;
607 int torture_nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
608 double start_time_limit = 10 + (torture_nprocs * 1.5);
615 signal(SIGCONT, sigcont);
617 child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
619 printf("Failed to setup shared memory\n");
623 child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
624 if (!child_status_out) {
625 printf("Failed to setup result status shared memory\n");
629 for (i = 0; i < torture_nprocs; i++) {
631 child_status_out[i] = True;
634 tv = timeval_current();
636 for (i=0;i<torture_nprocs;i++) {
641 pid_t mypid = getpid();
642 srandom(((int)mypid) ^ ((int)time(NULL)));
644 asprintf(&myname, "CLIENT%d", i);
645 lp_set_cmdline("netbios name", myname);
650 if (torture_open_connection(¤t_cli, i)) {
654 printf("pid %d failed to start\n", (int)getpid());
660 child_status[i] = getpid();
664 if (child_status[i]) {
665 printf("Child %d failed to start!\n", i);
666 child_status_out[i] = 1;
670 child_status_out[i] = fn(tctx, current_cli, i);
677 for (i=0;i<torture_nprocs;i++) {
678 if (child_status[i]) synccount++;
680 if (synccount == torture_nprocs) break;
682 } while (timeval_elapsed(&tv) < start_time_limit);
684 if (synccount != torture_nprocs) {
685 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
687 return timeval_elapsed(&tv);
690 printf("Starting %d clients\n", torture_nprocs);
692 /* start the client load */
693 tv = timeval_current();
694 for (i=0;i<torture_nprocs;i++) {
698 printf("%d clients started\n", torture_nprocs);
702 for (i=0;i<torture_nprocs;i++) {
704 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
705 if (ret == -1 || WEXITSTATUS(status) != 0) {
712 for (i=0;i<torture_nprocs;i++) {
713 if (!child_status_out[i]) {
717 return timeval_elapsed(&tv);
720 static bool wrap_smb_multi_test(struct torture_context *torture,
721 struct torture_tcase *tcase,
722 struct torture_test *test)
724 bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
727 torture_create_procs(torture, fn, &result);
732 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
733 struct torture_suite *suite,
735 bool (*run) (struct torture_context *,
736 struct smbcli_state *,
739 struct torture_test *test;
740 struct torture_tcase *tcase;
742 tcase = torture_suite_add_tcase(suite, name);
744 test = talloc(tcase, struct torture_test);
746 test->name = talloc_strdup(test, name);
747 test->description = NULL;
748 test->run = wrap_smb_multi_test;
750 test->dangerous = false;
752 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
758 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
759 struct torture_tcase *tcase,
760 struct torture_test *test)
762 bool (*fn) (struct torture_context *, struct smbcli_state *,
763 struct smbcli_state *);
766 struct smbcli_state *cli1, *cli2;
768 if (!torture_open_connection(&cli1, 0) ||
769 !torture_open_connection(&cli2, 1))
774 ret = fn(torture_ctx, cli1, cli2);
784 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
785 struct torture_suite *suite,
787 bool (*run) (struct torture_context *,
788 struct smbcli_state *,
789 struct smbcli_state *))
791 struct torture_test *test;
792 struct torture_tcase *tcase;
794 tcase = torture_suite_add_tcase(suite, name);
796 test = talloc(tcase, struct torture_test);
798 test->name = talloc_strdup(test, name);
799 test->description = NULL;
800 test->run = wrap_simple_2smb_test;
802 test->dangerous = false;
804 DLIST_ADD_END(tcase->tests, test, struct torture_test *);
810 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
811 struct torture_tcase *tcase,
812 struct torture_test *test)
814 bool (*fn) (struct torture_context *, struct smbcli_state *);
817 struct smbcli_state *cli1;
819 if (!torture_open_connection(&cli1, 0))
824 ret = fn(torture_ctx, cli1);
831 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
832 struct torture_suite *suite,
834 bool (*run) (struct torture_context *,
835 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 *);