Merge branch 'master' of ssh://jra@git.samba.org/data/git/samba
[amitay/samba.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 #include "libcli/security/security.h"
37 #include "libcli/util/clilsa.h"
38
39
40 /**
41   setup a directory ready for a test
42 */
43 _PUBLIC_ bool torture_setup_dir(struct smbcli_state *cli, const char *dname)
44 {
45         smb_raw_exit(cli->session);
46         if (smbcli_deltree(cli->tree, dname) == -1 ||
47             NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
48                 printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
49                 return false;
50         }
51         return true;
52 }
53
54 /*
55   create a directory, returning a handle to it
56 */
57 NTSTATUS create_directory_handle(struct smbcli_tree *tree, const char *dname, int *fnum)
58 {
59         NTSTATUS status;
60         union smb_open io;
61         TALLOC_CTX *mem_ctx;
62
63         mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
64
65         io.generic.level = RAW_OPEN_NTCREATEX;
66         io.ntcreatex.in.root_fid.fnum = 0;
67         io.ntcreatex.in.flags = 0;
68         io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
69         io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
70         io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
71         io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
72         io.ntcreatex.in.alloc_size = 0;
73         io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
74         io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
75         io.ntcreatex.in.security_flags = 0;
76         io.ntcreatex.in.fname = dname;
77
78         status = smb_raw_open(tree, mem_ctx, &io);
79         talloc_free(mem_ctx);
80
81         if (NT_STATUS_IS_OK(status)) {
82                 *fnum = io.ntcreatex.out.file.fnum;
83         }
84
85         return status;
86 }
87
88
89 /**
90   sometimes we need a fairly complex file to work with, so we can test
91   all possible attributes. 
92 */
93 _PUBLIC_ int create_complex_file(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *fname)
94 {
95         int fnum;
96         char buf[7] = "abc";
97         union smb_setfileinfo setfile;
98         union smb_fileinfo fileinfo;
99         time_t t = (time(NULL) & ~1);
100         NTSTATUS status;
101
102         smbcli_unlink(cli->tree, fname);
103         fnum = smbcli_nt_create_full(cli->tree, fname, 0, 
104                                      SEC_RIGHTS_FILE_ALL,
105                                      FILE_ATTRIBUTE_NORMAL,
106                                      NTCREATEX_SHARE_ACCESS_DELETE|
107                                      NTCREATEX_SHARE_ACCESS_READ|
108                                      NTCREATEX_SHARE_ACCESS_WRITE, 
109                                      NTCREATEX_DISP_OVERWRITE_IF,
110                                      0, 0);
111         if (fnum == -1) return -1;
112
113         smbcli_write(cli->tree, fnum, 0, buf, 0, sizeof(buf));
114
115         if (strchr(fname, ':') == NULL) {
116                 /* setup some EAs */
117                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
118                 setfile.generic.in.file.fnum = fnum;
119                 setfile.ea_set.in.num_eas = 2;  
120                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
121                 setfile.ea_set.in.eas[0].flags = 0;
122                 setfile.ea_set.in.eas[0].name.s = "EAONE";
123                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
124                 setfile.ea_set.in.eas[1].flags = 0;
125                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
126                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
127                 status = smb_raw_setfileinfo(cli->tree, &setfile);
128                 if (!NT_STATUS_IS_OK(status)) {
129                         printf("Failed to setup EAs\n");
130                 }
131         }
132
133         /* make sure all the timestamps aren't the same, and are also 
134            in different DST zones*/
135         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
136         setfile.generic.in.file.fnum = fnum;
137
138         setfile.setattre.in.create_time = t + 9*30*24*60*60;
139         setfile.setattre.in.access_time = t + 6*30*24*60*60;
140         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
141
142         status = smb_raw_setfileinfo(cli->tree, &setfile);
143         if (!NT_STATUS_IS_OK(status)) {
144                 printf("Failed to setup file times - %s\n", nt_errstr(status));
145         }
146
147         /* make sure all the timestamps aren't the same */
148         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
149         fileinfo.generic.in.file.fnum = fnum;
150
151         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
152         if (!NT_STATUS_IS_OK(status)) {
153                 printf("Failed to query file times - %s\n", nt_errstr(status));
154         }
155
156         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
157                 printf("create_time not setup correctly\n");
158         }
159         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
160                 printf("access_time not setup correctly\n");
161         }
162         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
163                 printf("write_time not setup correctly\n");
164         }
165
166         return fnum;
167 }
168
169
170 /*
171   sometimes we need a fairly complex directory to work with, so we can test
172   all possible attributes. 
173 */
174 int create_complex_dir(struct smbcli_state *cli, TALLOC_CTX *mem_ctx, const char *dname)
175 {
176         int fnum;
177         union smb_setfileinfo setfile;
178         union smb_fileinfo fileinfo;
179         time_t t = (time(NULL) & ~1);
180         NTSTATUS status;
181
182         smbcli_deltree(cli->tree, dname);
183         fnum = smbcli_nt_create_full(cli->tree, dname, 0, 
184                                      SEC_RIGHTS_DIR_ALL,
185                                      FILE_ATTRIBUTE_DIRECTORY,
186                                      NTCREATEX_SHARE_ACCESS_READ|
187                                      NTCREATEX_SHARE_ACCESS_WRITE, 
188                                      NTCREATEX_DISP_OPEN_IF,
189                                      NTCREATEX_OPTIONS_DIRECTORY, 0);
190         if (fnum == -1) return -1;
191
192         if (strchr(dname, ':') == NULL) {
193                 /* setup some EAs */
194                 setfile.generic.level = RAW_SFILEINFO_EA_SET;
195                 setfile.generic.in.file.fnum = fnum;
196                 setfile.ea_set.in.num_eas = 2;  
197                 setfile.ea_set.in.eas = talloc_array(mem_ctx, struct ea_struct, 2);
198                 setfile.ea_set.in.eas[0].flags = 0;
199                 setfile.ea_set.in.eas[0].name.s = "EAONE";
200                 setfile.ea_set.in.eas[0].value = data_blob_talloc(mem_ctx, "VALUE1", 6);
201                 setfile.ea_set.in.eas[1].flags = 0;
202                 setfile.ea_set.in.eas[1].name.s = "SECONDEA";
203                 setfile.ea_set.in.eas[1].value = data_blob_talloc(mem_ctx, "ValueTwo", 8);
204                 status = smb_raw_setfileinfo(cli->tree, &setfile);
205                 if (!NT_STATUS_IS_OK(status)) {
206                         printf("Failed to setup EAs\n");
207                 }
208         }
209
210         /* make sure all the timestamps aren't the same, and are also 
211            in different DST zones*/
212         setfile.generic.level = RAW_SFILEINFO_SETATTRE;
213         setfile.generic.in.file.fnum = fnum;
214
215         setfile.setattre.in.create_time = t + 9*30*24*60*60;
216         setfile.setattre.in.access_time = t + 6*30*24*60*60;
217         setfile.setattre.in.write_time  = t + 3*30*24*60*60;
218
219         status = smb_raw_setfileinfo(cli->tree, &setfile);
220         if (!NT_STATUS_IS_OK(status)) {
221                 printf("Failed to setup file times - %s\n", nt_errstr(status));
222         }
223
224         /* make sure all the timestamps aren't the same */
225         fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
226         fileinfo.generic.in.file.fnum = fnum;
227
228         status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
229         if (!NT_STATUS_IS_OK(status)) {
230                 printf("Failed to query file times - %s\n", nt_errstr(status));
231         }
232
233         if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
234                 printf("create_time not setup correctly\n");
235         }
236         if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
237                 printf("access_time not setup correctly\n");
238         }
239         if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
240                 printf("write_time not setup correctly\n");
241         }
242
243         return fnum;
244 }
245
246
247
248 /* return a pointer to a anonymous shared memory segment of size "size"
249    which will persist across fork() but will disappear when all processes
250    exit 
251
252    The memory is not zeroed 
253
254    This function uses system5 shared memory. It takes advantage of a property
255    that the memory is not destroyed if it is attached when the id is removed
256    */
257 void *shm_setup(int size)
258 {
259         int shmid;
260         void *ret;
261
262 #ifdef __QNXNTO__
263         shmid = shm_open("private", O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
264         if (shmid == -1) {
265                 printf("can't get shared memory\n");
266                 exit(1);
267         }
268         shm_unlink("private");
269         if (ftruncate(shmid, size) == -1) {
270                 printf("can't set shared memory size\n");
271                 exit(1);
272         }
273         ret = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, shmid, 0);
274         if (ret == MAP_FAILED) {
275                 printf("can't map shared memory\n");
276                 exit(1);
277         }
278 #else
279         shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
280         if (shmid == -1) {
281                 printf("can't get shared memory\n");
282                 exit(1);
283         }
284         ret = (void *)shmat(shmid, 0, 0);
285         if (!ret || ret == (void *)-1) {
286                 printf("can't attach to shared memory\n");
287                 return NULL;
288         }
289         /* the following releases the ipc, but note that this process
290            and all its children will still have access to the memory, its
291            just that the shmid is no longer valid for other shm calls. This
292            means we don't leave behind lots of shm segments after we exit 
293
294            See Stevens "advanced programming in unix env" for details
295            */
296         shmctl(shmid, IPC_RMID, 0);
297 #endif
298         
299         return ret;
300 }
301
302
303 /**
304   check that a wire string matches the flags specified 
305   not 100% accurate, but close enough for testing
306 */
307 bool wire_bad_flags(struct smb_wire_string *str, int flags, 
308                     struct smbcli_transport *transport)
309 {
310         bool server_unicode;
311         int len;
312         if (!str || !str->s) return true;
313         len = strlen(str->s);
314         if (flags & STR_TERMINATE) len++;
315
316         server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
317         if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) {
318                 server_unicode = false;
319         }
320
321         if ((flags & STR_UNICODE) || server_unicode) {
322                 len *= 2;
323         } else if (flags & STR_TERMINATE_ASCII) {
324                 len++;
325         }
326         if (str->private_length != len) {
327                 printf("Expected wire_length %d but got %d for '%s'\n", 
328                        len, str->private_length, str->s);
329                 return true;
330         }
331         return false;
332 }
333
334 /*
335   dump a all_info QFILEINFO structure
336 */
337 void dump_all_info(TALLOC_CTX *mem_ctx, union smb_fileinfo *finfo)
338 {
339         d_printf("\tcreate_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.create_time));
340         d_printf("\taccess_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.access_time));
341         d_printf("\twrite_time:     %s\n", nt_time_string(mem_ctx, finfo->all_info.out.write_time));
342         d_printf("\tchange_time:    %s\n", nt_time_string(mem_ctx, finfo->all_info.out.change_time));
343         d_printf("\tattrib:         0x%x\n", finfo->all_info.out.attrib);
344         d_printf("\talloc_size:     %llu\n", (long long)finfo->all_info.out.alloc_size);
345         d_printf("\tsize:           %llu\n", (long long)finfo->all_info.out.size);
346         d_printf("\tnlink:          %u\n", finfo->all_info.out.nlink);
347         d_printf("\tdelete_pending: %u\n", finfo->all_info.out.delete_pending);
348         d_printf("\tdirectory:      %u\n", finfo->all_info.out.directory);
349         d_printf("\tea_size:        %u\n", finfo->all_info.out.ea_size);
350         d_printf("\tfname:          '%s'\n", finfo->all_info.out.fname.s);
351 }
352
353 /*
354   dump file infor by name
355 */
356 void torture_all_info(struct smbcli_tree *tree, const char *fname)
357 {
358         TALLOC_CTX *mem_ctx = talloc_named(tree, 0, "%s", fname);
359         union smb_fileinfo finfo;
360         NTSTATUS status;
361
362         finfo.generic.level = RAW_FILEINFO_ALL_INFO;
363         finfo.generic.in.file.path = fname;
364         status = smb_raw_pathinfo(tree, mem_ctx, &finfo);
365         if (!NT_STATUS_IS_OK(status)) {
366                 d_printf("%s - %s\n", fname, nt_errstr(status));
367                 return;
368         }
369
370         d_printf("%s:\n", fname);
371         dump_all_info(mem_ctx, &finfo);
372         talloc_free(mem_ctx);
373 }
374
375
376 /*
377   set a attribute on a file
378 */
379 bool torture_set_file_attribute(struct smbcli_tree *tree, const char *fname, uint16_t attrib)
380 {
381         union smb_setfileinfo sfinfo;
382         NTSTATUS status;
383
384         ZERO_STRUCT(sfinfo.basic_info.in);
385         sfinfo.basic_info.level = RAW_SFILEINFO_BASIC_INFORMATION;
386         sfinfo.basic_info.in.file.path = fname;
387         sfinfo.basic_info.in.attrib = attrib;
388         status = smb_raw_setpathinfo(tree, &sfinfo);
389         return NT_STATUS_IS_OK(status);
390 }
391
392
393 /*
394   set a file descriptor as sparse
395 */
396 NTSTATUS torture_set_sparse(struct smbcli_tree *tree, int fnum)
397 {
398         union smb_ioctl nt;
399         NTSTATUS status;
400         TALLOC_CTX *mem_ctx;
401
402         mem_ctx = talloc_named_const(tree, 0, "torture_set_sparse");
403         if (!mem_ctx) {
404                 return NT_STATUS_NO_MEMORY;
405         }
406
407         nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
408         nt.ntioctl.in.function = FSCTL_SET_SPARSE;
409         nt.ntioctl.in.file.fnum = fnum;
410         nt.ntioctl.in.fsctl = true;
411         nt.ntioctl.in.filter = 0;
412         nt.ntioctl.in.max_data = 0;
413         nt.ntioctl.in.blob = data_blob(NULL, 0);
414
415         status = smb_raw_ioctl(tree, mem_ctx, &nt);
416
417         talloc_free(mem_ctx);
418
419         return status;
420 }
421
422 /*
423   check that an EA has the right value 
424 */
425 NTSTATUS torture_check_ea(struct smbcli_state *cli, 
426                           const char *fname, const char *eaname, const char *value)
427 {
428         union smb_fileinfo info;
429         NTSTATUS status;
430         struct ea_name ea;
431         TALLOC_CTX *mem_ctx = talloc_new(cli);
432
433         info.ea_list.level = RAW_FILEINFO_EA_LIST;
434         info.ea_list.in.file.path = fname;
435         info.ea_list.in.num_names = 1;
436         info.ea_list.in.ea_names = &ea;
437
438         ea.name.s = eaname;
439
440         status = smb_raw_pathinfo(cli->tree, mem_ctx, &info);
441         if (!NT_STATUS_IS_OK(status)) {
442                 talloc_free(mem_ctx);
443                 return status;
444         }
445
446         if (info.ea_list.out.num_eas != 1) {
447                 printf("Expected 1 ea in ea_list\n");
448                 talloc_free(mem_ctx);
449                 return NT_STATUS_EA_CORRUPT_ERROR;
450         }
451
452         if (strcasecmp_m(eaname, info.ea_list.out.eas[0].name.s) != 0) {
453                 printf("Expected ea '%s' not '%s' in ea_list\n",
454                        eaname, info.ea_list.out.eas[0].name.s);
455                 talloc_free(mem_ctx);
456                 return NT_STATUS_EA_CORRUPT_ERROR;
457         }
458
459         if (value == NULL) {
460                 if (info.ea_list.out.eas[0].value.length != 0) {
461                         printf("Expected zero length ea for %s\n", eaname);
462                         talloc_free(mem_ctx);
463                         return NT_STATUS_EA_CORRUPT_ERROR;
464                 }
465                 talloc_free(mem_ctx);
466                 return NT_STATUS_OK;
467         }
468
469         if (strlen(value) == info.ea_list.out.eas[0].value.length &&
470             memcmp(value, info.ea_list.out.eas[0].value.data,
471                    info.ea_list.out.eas[0].value.length) == 0) {
472                 talloc_free(mem_ctx);
473                 return NT_STATUS_OK;
474         }
475
476         printf("Expected value '%s' not '%*.*s' for ea %s\n",
477                value, 
478                (int)info.ea_list.out.eas[0].value.length,
479                (int)info.ea_list.out.eas[0].value.length,
480                info.ea_list.out.eas[0].value.data,
481                eaname);
482
483         talloc_free(mem_ctx);
484
485         return NT_STATUS_EA_CORRUPT_ERROR;
486 }
487
488 _PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
489                                    struct smbcli_state **c, 
490                                    struct torture_context *tctx,
491                                    const char *hostname, 
492                                    const char *sharename,
493                                    struct tevent_context *ev)
494 {
495         NTSTATUS status;
496
497         struct smbcli_options options;
498         struct smbcli_session_options session_options;
499
500         lp_smbcli_options(tctx->lp_ctx, &options);
501         lp_smbcli_session_options(tctx->lp_ctx, &session_options);
502
503         options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true);
504         options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true);
505
506         status = smbcli_full_connection(mem_ctx, c, hostname, 
507                                         lp_smb_ports(tctx->lp_ctx),
508                                         sharename, NULL,
509                                         lp_socket_options(tctx->lp_ctx),
510                                         cmdline_credentials, 
511                                         lp_resolve_context(tctx->lp_ctx),
512                                         ev, &options, &session_options,
513                                         lp_iconv_convenience(tctx->lp_ctx),
514                                         lp_gensec_settings(tctx, tctx->lp_ctx));
515         if (!NT_STATUS_IS_OK(status)) {
516                 printf("Failed to open connection - %s\n", nt_errstr(status));
517                 return false;
518         }
519
520         return true;
521 }
522
523 _PUBLIC_ bool torture_get_conn_index(int conn_index,
524                                      TALLOC_CTX *mem_ctx,
525                                      struct torture_context *tctx,
526                                      char **host, char **share)
527 {
528         char **unc_list = NULL;
529         int num_unc_names = 0;
530         const char *p;
531
532         (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
533         (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
534         
535         p = torture_setting_string(tctx, "unclist", NULL);
536         if (!p) {
537                 return true;
538         }
539
540         unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
541         if (!unc_list || num_unc_names <= 0) {
542                 DEBUG(0,("Failed to load unc names list from '%s'\n", p));
543                 return false;
544         }
545
546         if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
547                               mem_ctx, host, share)) {
548                 DEBUG(0, ("Failed to parse UNC name %s\n",
549                           unc_list[conn_index % num_unc_names]));
550                 return false;
551         }
552
553         talloc_free(unc_list);
554         return true;
555 }
556
557
558
559 _PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
560                                          int conn_index,
561                                          struct torture_context *tctx,
562                                          struct tevent_context *ev)
563 {
564         char *host, *share;
565         bool ret;
566
567         if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
568                 return false;
569         }
570
571         ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
572         talloc_free(host);
573         talloc_free(share);
574
575         return ret;
576 }
577
578 _PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
579 {
580         return torture_open_connection_ev(c, conn_index, tctx, tctx->ev);
581 }
582
583
584
585 _PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
586 {
587         bool ret = true;
588         if (!c) return true;
589         if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
590                 printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
591                 ret = false;
592         }
593         talloc_free(c);
594         return ret;
595 }
596
597
598 /* check if the server produced the expected error code */
599 _PUBLIC_ bool check_error(const char *location, struct smbcli_state *c, 
600                  uint8_t eclass, uint32_t ecode, NTSTATUS nterr)
601 {
602         NTSTATUS status;
603         
604         status = smbcli_nt_error(c->tree);
605         if (NT_STATUS_IS_DOS(status)) {
606                 int classnum, num;
607                 classnum = NT_STATUS_DOS_CLASS(status);
608                 num = NT_STATUS_DOS_CODE(status);
609                 if (eclass != classnum || ecode != num) {
610                         printf("unexpected error code %s\n", nt_errstr(status));
611                         printf(" expected %s or %s (at %s)\n", 
612                                nt_errstr(NT_STATUS_DOS(eclass, ecode)), 
613                                nt_errstr(nterr), location);
614                         return false;
615                 }
616         } else {
617                 if (!NT_STATUS_EQUAL(nterr, status)) {
618                         printf("unexpected error code %s\n", nt_errstr(status));
619                         printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
620                         return false;
621                 }
622         }
623
624         return true;
625 }
626
627 static struct smbcli_state *current_cli;
628 static int procnum; /* records process count number when forking */
629
630 static void sigcont(int sig)
631 {
632 }
633
634 double torture_create_procs(struct torture_context *tctx, 
635                                                         bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
636 {
637         int i, status;
638         volatile pid_t *child_status;
639         volatile bool *child_status_out;
640         int synccount;
641         int tries = 8;
642         int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
643         double start_time_limit = 10 + (torture_nprocs * 1.5);
644         struct timeval tv;
645
646         *result = true;
647
648         synccount = 0;
649
650         signal(SIGCONT, sigcont);
651
652         child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
653         if (!child_status) {
654                 printf("Failed to setup shared memory\n");
655                 return -1;
656         }
657
658         child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
659         if (!child_status_out) {
660                 printf("Failed to setup result status shared memory\n");
661                 return -1;
662         }
663
664         for (i = 0; i < torture_nprocs; i++) {
665                 child_status[i] = 0;
666                 child_status_out[i] = true;
667         }
668
669         tv = timeval_current();
670
671         for (i=0;i<torture_nprocs;i++) {
672                 procnum = i;
673                 if (fork() == 0) {
674                         char *myname;
675
676                         pid_t mypid = getpid();
677                         srandom(((int)mypid) ^ ((int)time(NULL)));
678
679                         if (asprintf(&myname, "CLIENT%d", i) == -1) {
680                                 printf("asprintf failed\n");
681                                 return -1;
682                         }
683                         lp_set_cmdline(tctx->lp_ctx, "netbios name", myname);
684                         free(myname);
685
686
687                         while (1) {
688                                 if (torture_open_connection(&current_cli, tctx, i)) {
689                                         break;
690                                 }
691                                 if (tries-- == 0) {
692                                         printf("pid %d failed to start\n", (int)getpid());
693                                         _exit(1);
694                                 }
695                                 msleep(100);    
696                         }
697
698                         child_status[i] = getpid();
699
700                         pause();
701
702                         if (child_status[i]) {
703                                 printf("Child %d failed to start!\n", i);
704                                 child_status_out[i] = 1;
705                                 _exit(1);
706                         }
707
708                         child_status_out[i] = fn(tctx, current_cli, i);
709                         _exit(0);
710                 }
711         }
712
713         do {
714                 synccount = 0;
715                 for (i=0;i<torture_nprocs;i++) {
716                         if (child_status[i]) synccount++;
717                 }
718                 if (synccount == torture_nprocs) break;
719                 msleep(100);
720         } while (timeval_elapsed(&tv) < start_time_limit);
721
722         if (synccount != torture_nprocs) {
723                 printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
724                 *result = false;
725                 return timeval_elapsed(&tv);
726         }
727
728         printf("Starting %d clients\n", torture_nprocs);
729
730         /* start the client load */
731         tv = timeval_current();
732         for (i=0;i<torture_nprocs;i++) {
733                 child_status[i] = 0;
734         }
735
736         printf("%d clients started\n", torture_nprocs);
737
738         kill(0, SIGCONT);
739
740         for (i=0;i<torture_nprocs;i++) {
741                 int ret;
742                 while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
743                 if (ret == -1 || WEXITSTATUS(status) != 0) {
744                         *result = false;
745                 }
746         }
747
748         printf("\n");
749         
750         for (i=0;i<torture_nprocs;i++) {
751                 if (!child_status_out[i]) {
752                         *result = false;
753                 }
754         }
755         return timeval_elapsed(&tv);
756 }
757
758 static bool wrap_smb_multi_test(struct torture_context *torture,
759                                                                 struct torture_tcase *tcase,
760                                                                 struct torture_test *test)
761 {
762         bool (*fn)(struct torture_context *, struct smbcli_state *, int ) = test->fn;
763         bool result;
764
765         torture_create_procs(torture, fn, &result);
766
767         return result;
768 }
769
770 _PUBLIC_ struct torture_test *torture_suite_add_smb_multi_test(
771                                                                         struct torture_suite *suite,
772                                                                         const char *name,
773                                                                         bool (*run) (struct torture_context *,
774                                                                                                  struct smbcli_state *,
775                                                                                                 int i))
776 {
777         struct torture_test *test; 
778         struct torture_tcase *tcase;
779         
780         tcase = torture_suite_add_tcase(suite, name);
781
782         test = talloc(tcase, struct torture_test);
783
784         test->name = talloc_strdup(test, name);
785         test->description = NULL;
786         test->run = wrap_smb_multi_test;
787         test->fn = run;
788         test->dangerous = false;
789
790         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
791
792         return test;
793
794 }
795
796 static bool wrap_simple_2smb_test(struct torture_context *torture_ctx,
797                                                                         struct torture_tcase *tcase,
798                                                                         struct torture_test *test)
799 {
800         bool (*fn) (struct torture_context *, struct smbcli_state *,
801                                 struct smbcli_state *);
802         bool ret;
803
804         struct smbcli_state *cli1, *cli2;
805
806         if (!torture_open_connection(&cli1, torture_ctx, 0) || 
807                 !torture_open_connection(&cli2, torture_ctx, 1))
808                 return false;
809
810         fn = test->fn;
811
812         ret = fn(torture_ctx, cli1, cli2);
813
814         talloc_free(cli1);
815         talloc_free(cli2);
816
817         return ret;
818 }
819
820
821
822 _PUBLIC_ struct torture_test *torture_suite_add_2smb_test(
823                                                                         struct torture_suite *suite,
824                                                                         const char *name,
825                                                                         bool (*run) (struct torture_context *,
826                                                                                                 struct smbcli_state *,
827                                                                                                 struct smbcli_state *))
828 {
829         struct torture_test *test; 
830         struct torture_tcase *tcase;
831         
832         tcase = torture_suite_add_tcase(suite, name);
833
834         test = talloc(tcase, struct torture_test);
835
836         test->name = talloc_strdup(test, name);
837         test->description = NULL;
838         test->run = wrap_simple_2smb_test;
839         test->fn = run;
840         test->dangerous = false;
841
842         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
843
844         return test;
845
846 }
847
848 static bool wrap_simple_1smb_test(struct torture_context *torture_ctx,
849                                                                         struct torture_tcase *tcase,
850                                                                         struct torture_test *test)
851 {
852         bool (*fn) (struct torture_context *, struct smbcli_state *);
853         bool ret;
854
855         struct smbcli_state *cli1;
856
857         if (!torture_open_connection(&cli1, torture_ctx, 0))
858                 return false;
859
860         fn = test->fn;
861
862         ret = fn(torture_ctx, cli1);
863
864         talloc_free(cli1);
865
866         return ret;
867 }
868
869 _PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
870                                 struct torture_suite *suite,
871                                 const char *name,
872                                 bool (*run) (struct torture_context *, struct smbcli_state *))
873 {
874         struct torture_test *test; 
875         struct torture_tcase *tcase;
876         
877         tcase = torture_suite_add_tcase(suite, name);
878
879         test = talloc(tcase, struct torture_test);
880
881         test->name = talloc_strdup(test, name);
882         test->description = NULL;
883         test->run = wrap_simple_1smb_test;
884         test->fn = run;
885         test->dangerous = false;
886
887         DLIST_ADD_END(tcase->tests, test, struct torture_test *);
888
889         return test;
890 }
891
892
893 NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx,
894                              struct smbcli_session *session,
895                              const char *sharename,
896                              struct smbcli_tree **res)
897 {
898         union smb_tcon tcon;
899         struct smbcli_tree *result;
900         TALLOC_CTX *tmp_ctx;
901         NTSTATUS status;
902
903         if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) {
904                 return NT_STATUS_NO_MEMORY;
905         }
906
907         result = smbcli_tree_init(session, tmp_ctx, false);
908         if (result == NULL) {
909                 talloc_free(tmp_ctx);
910                 return NT_STATUS_NO_MEMORY;
911         }
912
913         tcon.generic.level = RAW_TCON_TCONX;
914         tcon.tconx.in.flags = 0;
915
916         /* Ignore share mode security here */
917         tcon.tconx.in.password = data_blob(NULL, 0);
918         tcon.tconx.in.path = sharename;
919         tcon.tconx.in.device = "?????";
920
921         status = smb_raw_tcon(result, tmp_ctx, &tcon);
922         if (!NT_STATUS_IS_OK(status)) {
923                 talloc_free(tmp_ctx);
924                 return status;
925         }
926
927         result->tid = tcon.tconx.out.tid;
928         *res = talloc_steal(mem_ctx, result);
929         talloc_free(tmp_ctx);
930         return NT_STATUS_OK;
931 }
932
933 /* 
934    a wrapper around smblsa_sid_check_privilege, that tries to take
935    account of the fact that the lsa privileges calls don't expand
936    group memberships, using an explicit check for administrator. There
937    must be a better way ...
938  */
939 NTSTATUS torture_check_privilege(struct smbcli_state *cli, 
940                                  const char *sid_str,
941                                  const char *privilege)
942 {
943         struct dom_sid *sid;
944         TALLOC_CTX *tmp_ctx = talloc_new(cli);
945         uint32_t rid;
946         NTSTATUS status;
947
948         sid = dom_sid_parse_talloc(tmp_ctx, sid_str);
949         if (sid == NULL) {
950                 talloc_free(tmp_ctx);
951                 return NT_STATUS_INVALID_SID;
952         }
953
954         status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
955         NT_STATUS_NOT_OK_RETURN_AND_FREE(status, tmp_ctx);
956
957         if (rid == DOMAIN_RID_ADMINISTRATOR) {
958                 /* assume the administrator has them all */
959                 return NT_STATUS_OK;
960         }
961
962         talloc_free(tmp_ctx);
963
964         return smblsa_sid_check_privilege(cli, sid_str, privilege);
965 }