s4-smb: declare root_fid as a file handle
[ira/wip.git] / source4 / torture / util_smb.c
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 */
55 NTSTATUS 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.fnum = 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 */
172 int 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    */
255 void *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 */
305 bool 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 */
335 void 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 */
354 void 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 */
377 bool 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 */
394 NTSTATUS 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 */
423 NTSTATUS 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
625 static struct smbcli_state *current_cli;
626 static int procnum; /* records process count number when forking */
627
628 static void sigcont(int sig)
629 {
630 }
631
632 double 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(&current_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
756 static 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
794 static 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
846 static 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
891 NTSTATUS 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 }