r25398: Parse loadparm context to all lp_*() functions.
[bbaumbach/samba-autobuild/.git] / source4 / torture / gentest.c
1 /* 
2    Unix SMB/CIFS implementation.
3    generic testing tool
4    Copyright (C) Andrew Tridgell 2003
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "system/time.h"
22 #include "system/filesys.h"
23 #include "libcli/raw/request.h"
24 #include "libcli/libcli.h"
25 #include "libcli/raw/libcliraw.h"
26 #include "librpc/gen_ndr/security.h"
27 #include "auth/credentials/credentials.h"
28 #include "auth/gensec/gensec.h"
29 #include "param/param.h"
30 #include "dynconfig.h"
31
32 #define NSERVERS 2
33 #define NINSTANCES 2
34
35 /* global options */
36 static struct gentest_options {
37         BOOL showall;
38         BOOL analyze;
39         BOOL analyze_always;
40         BOOL analyze_continuous;
41         uint_t max_open_handles;
42         uint_t seed;
43         uint_t numops;
44         BOOL use_oplocks;
45         char **ignore_patterns;
46         const char *seeds_file;
47         BOOL use_preset_seeds;
48         BOOL fast_reconnect;
49 } options;
50
51 /* mapping between open handles on the server and local handles */
52 static struct {
53         BOOL active;
54         uint_t instance;
55         uint_t server_fnum[NSERVERS];
56         const char *name;
57 } *open_handles;
58 static uint_t num_open_handles;
59
60 /* state information for the servers. We open NINSTANCES connections to
61    each server */
62 static struct {
63         struct smbcli_state *cli[NINSTANCES];
64         char *server_name;
65         char *share_name;
66         struct cli_credentials *credentials;
67 } servers[NSERVERS];
68
69 /* the seeds and flags for each operation */
70 static struct {
71         uint_t seed;
72         BOOL disabled;
73 } *op_parms;
74
75
76 /* oplock break info */
77 static struct {
78         BOOL got_break;
79         uint16_t fnum;
80         uint16_t handle;
81         uint8_t level;
82         BOOL do_close;
83 } oplocks[NSERVERS][NINSTANCES];
84
85 /* change notify reply info */
86 static struct {
87         int notify_count;
88         NTSTATUS status;
89         union smb_notify notify;
90 } notifies[NSERVERS][NINSTANCES];
91
92 /* info relevant to the current operation */
93 static struct {
94         const char *name;
95         uint_t seed;
96         NTSTATUS status;
97         uint_t opnum;
98         TALLOC_CTX *mem_ctx;
99 } current_op;
100
101
102
103 #define BAD_HANDLE 0xFFFE
104
105 static BOOL oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private);
106 static void idle_func(struct smbcli_transport *transport, void *private);
107
108 /*
109   check if a string should be ignored. This is used as the basis
110   for all error ignore settings
111 */
112 static BOOL ignore_pattern(const char *str)
113 {
114         int i;
115         if (!options.ignore_patterns) return False;
116
117         for (i=0;options.ignore_patterns[i];i++) {
118                 if (strcmp(options.ignore_patterns[i], str) == 0 ||
119                     gen_fnmatch(options.ignore_patterns[i], str) == 0) {
120                         DEBUG(2,("Ignoring '%s'\n", str));
121                         return True;
122                 }
123         }
124         return False;
125 }
126
127 /***************************************************** 
128 connect to the servers
129 *******************************************************/
130 static BOOL connect_servers_fast(void)
131 {
132         int h, i;
133
134         /* close all open files */
135         for (h=0;h<options.max_open_handles;h++) {
136                 if (!open_handles[h].active) continue;
137                 for (i=0;i<NSERVERS;i++) {
138                         if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree,
139                                        open_handles[h].server_fnum[i])))) {
140                                 return False;
141                         }
142                         open_handles[h].active = False;
143                 }
144         }
145
146         return True;
147 }
148
149
150
151
152 /***************************************************** 
153 connect to the servers
154 *******************************************************/
155 static BOOL connect_servers(void)
156 {
157         int i, j;
158
159         if (options.fast_reconnect && servers[0].cli[0]) {
160                 if (connect_servers_fast()) {
161                         return True;
162                 }
163         }
164
165         /* close any existing connections */
166         for (i=0;i<NSERVERS;i++) {
167                 for (j=0;j<NINSTANCES;j++) {
168                         if (servers[i].cli[j]) {
169                                 smbcli_tdis(servers[i].cli[j]);
170                                 talloc_free(servers[i].cli[j]);
171                                 servers[i].cli[j] = NULL;
172                         }
173                 }
174         }
175
176         for (i=0;i<NSERVERS;i++) {
177                 for (j=0;j<NINSTANCES;j++) {
178                         NTSTATUS status;
179                         printf("Connecting to \\\\%s\\%s as %s - instance %d\n",
180                                servers[i].server_name, servers[i].share_name, 
181                                servers[i].credentials->username, j);
182
183                         cli_credentials_set_workstation(servers[i].credentials, 
184                                                         "gentest", CRED_SPECIFIED);
185
186                         status = smbcli_full_connection(NULL, &servers[i].cli[j],
187                                                         servers[i].server_name, 
188                                                         servers[i].share_name, NULL, 
189                                                         servers[i].credentials, NULL);
190                         if (!NT_STATUS_IS_OK(status)) {
191                                 printf("Failed to connect to \\\\%s\\%s - %s\n",
192                                        servers[i].server_name, servers[i].share_name,
193                                        nt_errstr(status));
194                                 return False;
195                         }
196
197                         smbcli_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL);
198                         smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL);
199                 }
200         }
201
202         return True;
203 }
204
205 /*
206   work out the time skew between the servers - be conservative
207 */
208 static uint_t time_skew(void)
209 {
210         uint_t ret;
211         ret = labs(servers[0].cli[0]->transport->negotiate.server_time -
212                   servers[1].cli[0]->transport->negotiate.server_time);
213         return ret + 300;
214 }
215
216 /*
217   turn an fnum for an instance into a handle
218 */
219 static uint_t fnum_to_handle(int server, int instance, uint16_t fnum)
220 {
221         uint_t i;
222         for (i=0;i<options.max_open_handles;i++) {
223                 if (!open_handles[i].active ||
224                     instance != open_handles[i].instance) continue;
225                 if (open_handles[i].server_fnum[server] == fnum) {
226                         return i;
227                 }
228         }
229         printf("Invalid fnum %d in fnum_to_handle on server %d instance %d\n", 
230                fnum, server, instance);
231         return BAD_HANDLE;
232 }
233
234 /*
235   add some newly opened handles
236 */
237 static void gen_add_handle(int instance, const char *name, uint16_t fnums[NSERVERS])
238 {
239         int i, h;
240         for (h=0;h<options.max_open_handles;h++) {
241                 if (!open_handles[h].active) break;
242         }
243         if (h == options.max_open_handles) {
244                 /* we have to force close a random handle */
245                 h = random() % options.max_open_handles;
246                 for (i=0;i<NSERVERS;i++) {
247                         if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree, 
248                                        open_handles[h].server_fnum[i])))) {
249                                 printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
250                                        smbcli_errstr(servers[i].cli[open_handles[h].instance]->tree));
251                         }
252                 }
253                 printf("Recovered handle %d\n", h);
254                 num_open_handles--;
255         }
256         for (i=0;i<NSERVERS;i++) {
257                 open_handles[h].server_fnum[i] = fnums[i];
258                 open_handles[h].instance = instance;
259                 open_handles[h].active = True;
260                 open_handles[h].name = name;
261         }
262         num_open_handles++;
263
264         printf("OPEN num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", 
265                num_open_handles, h, 
266                open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
267                name);
268 }
269
270 /*
271   remove a closed handle
272 */
273 static void gen_remove_handle(int instance, uint16_t fnums[NSERVERS])
274 {
275         int h;
276         for (h=0;h<options.max_open_handles;h++) {
277                 if (instance == open_handles[h].instance &&
278                     open_handles[h].server_fnum[0] == fnums[0]) {
279                         open_handles[h].active = False;                 
280                         num_open_handles--;
281                         printf("CLOSE num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", 
282                                num_open_handles, h, 
283                                open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
284                                open_handles[h].name);
285                         return;
286                 }
287         }
288         printf("Removing invalid handle!?\n");
289         exit(1);
290 }
291
292 /*
293   return True with 'chance' probability as a percentage
294 */
295 static BOOL gen_chance(uint_t chance)
296 {
297         return ((random() % 100) <= chance);
298 }
299
300 /*
301   map an internal handle number to a server fnum
302 */
303 static uint16_t gen_lookup_fnum(int server, uint16_t handle)
304 {
305         if (handle == BAD_HANDLE) return handle;
306         return open_handles[handle].server_fnum[server];
307 }
308
309 /*
310   return a file handle
311 */
312 static uint16_t gen_fnum(int instance)
313 {
314         uint16_t h;
315         int count = 0;
316
317         if (gen_chance(20)) return BAD_HANDLE;
318
319         while (num_open_handles > 0 && count++ < 10*options.max_open_handles) {
320                 h = random() % options.max_open_handles;
321                 if (open_handles[h].active && 
322                     open_handles[h].instance == instance) {
323                         return h;
324                 }
325         }
326         return BAD_HANDLE;
327 }
328
329 /*
330   return a file handle, but skewed so we don't close the last
331   couple of handles too readily
332 */
333 static uint16_t gen_fnum_close(int instance)
334 {
335         if (num_open_handles < 3) {
336                 if (gen_chance(80)) return BAD_HANDLE;
337         }
338
339         return gen_fnum(instance);
340 }
341
342 /*
343   generate an integer in a specified range
344 */
345 static int gen_int_range(uint_t min, uint_t max)
346 {
347         uint_t r = random();
348         return min + (r % (1+max-min));
349 }
350
351 /*
352   return a fnum for use as a root fid
353   be careful to call GEN_SET_FNUM() when you use this!
354 */
355 static uint16_t gen_root_fid(int instance)
356 {
357         if (gen_chance(5)) return gen_fnum(instance);
358         return 0;
359 }
360
361 /*
362   generate a file offset
363 */
364 static int gen_offset(void)
365 {
366         if (gen_chance(20)) return 0;
367         return gen_int_range(0, 1024*1024);
368 }
369
370 /*
371   generate a io count
372 */
373 static int gen_io_count(void)
374 {
375         if (gen_chance(20)) return 0;
376         return gen_int_range(0, 4096);
377 }
378
379 /*
380   generate a filename
381 */
382 static const char *gen_fname(void)
383 {
384         const char *names[] = {"\\gentest\\gentest.dat", 
385                                "\\gentest\\foo", 
386                                "\\gentest\\foo2.sym", 
387                                "\\gentest\\foo3.dll", 
388                                "\\gentest\\foo4", 
389                                "\\gentest\\foo4:teststream1", 
390                                "\\gentest\\foo4:teststream2", 
391                                "\\gentest\\foo5.exe", 
392                                "\\gentest\\foo5.exe:teststream3", 
393                                "\\gentest\\foo5.exe:teststream4", 
394                                "\\gentest\\foo6.com", 
395                                "\\gentest\\blah", 
396                                "\\gentest\\blah\\blergh.txt", 
397                                "\\gentest\\blah\\blergh2", 
398                                "\\gentest\\blah\\blergh3.txt", 
399                                "\\gentest\\blah\\blergh4", 
400                                "\\gentest\\blah\\blergh5.txt", 
401                                "\\gentest\\blah\\blergh5", 
402                                "\\gentest\\blah\\.", 
403 #if 0
404                                /* this causes problem with w2k3 */
405                                "\\gentest\\blah\\..", 
406 #endif
407                                "\\gentest\\a_very_long_name.bin", 
408                                "\\gentest\\x.y", 
409                                "\\gentest\\blah"};
410         int i;
411
412         do {
413                 i = gen_int_range(0, ARRAY_SIZE(names)-1);
414         } while (ignore_pattern(names[i]));
415
416         return names[i];
417 }
418
419 /*
420   generate a filename with a higher chance of choosing an already 
421   open file
422 */
423 static const char *gen_fname_open(int instance)
424 {
425         uint16_t h;
426         h = gen_fnum(instance);
427         if (h == BAD_HANDLE) {
428                 return gen_fname();
429         }
430         return open_handles[h].name;
431 }
432
433 /*
434   generate a wildcard pattern
435 */
436 static const char *gen_pattern(void)
437 {
438         int i;
439         const char *names[] = {"\\gentest\\*.dat", 
440                                "\\gentest\\*", 
441                                "\\gentest\\*.*", 
442                                "\\gentest\\blah\\*.*", 
443                                "\\gentest\\blah\\*", 
444                                "\\gentest\\?"};
445
446         if (gen_chance(50)) return gen_fname();
447
448         do {
449                 i = gen_int_range(0, ARRAY_SIZE(names)-1);
450         } while (ignore_pattern(names[i]));
451
452         return names[i];
453 }
454
455 /*
456   generate a bitmask
457 */
458 static uint32_t gen_bits_mask(uint_t mask)
459 {
460         uint_t ret = random();
461         return ret & mask;
462 }
463
464 /*
465   generate a bitmask with high probability of the first mask
466   and low of the second
467 */
468 static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2)
469 {
470         if (gen_chance(10)) return gen_bits_mask(mask2);
471         return gen_bits_mask(mask1);
472 }
473
474 /*
475   generate a boolean
476 */
477 static BOOL gen_bool(void)
478 {
479         return gen_bits_mask2(0x1, 0xFF);
480 }
481
482 /*
483   generate ntrename flags
484 */
485 static uint16_t gen_rename_flags(void)
486 {
487         if (gen_chance(30)) return RENAME_FLAG_RENAME;
488         if (gen_chance(30)) return RENAME_FLAG_HARD_LINK;
489         if (gen_chance(30)) return RENAME_FLAG_COPY;
490         return gen_bits_mask(0xFFFF);
491 }
492
493
494 /*
495   return a lockingx lock mode
496 */
497 static uint16_t gen_lock_mode(void)
498 {
499         if (gen_chance(5))  return gen_bits_mask(0xFFFF);
500         if (gen_chance(20)) return gen_bits_mask(0x1F);
501         return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES);
502 }
503
504 /*
505   generate a pid 
506 */
507 static uint16_t gen_pid(void)
508 {
509         if (gen_chance(10)) return gen_bits_mask(0xFFFF);
510         return getpid();
511 }
512
513 /*
514   generate a lock count
515 */
516 static off_t gen_lock_count(void)
517 {
518         return gen_int_range(0, 3);
519 }
520
521 /*
522   generate a ntcreatex flags field
523 */
524 static uint32_t gen_ntcreatex_flags(void)
525 {
526         if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED;
527         return gen_bits_mask2(0x1F, 0xFFFFFFFF);
528 }
529
530 /*
531   generate a NT access mask
532 */
533 static uint32_t gen_access_mask(void)
534 {
535         if (gen_chance(50)) return SEC_FLAG_MAXIMUM_ALLOWED;
536         if (gen_chance(20)) return SEC_FILE_ALL;
537         return gen_bits_mask(0xFFFFFFFF);
538 }
539
540 /*
541   generate a ntcreatex create options bitfield
542 */
543 static uint32_t gen_create_options(void)
544 {
545         if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
546         if (gen_chance(50)) return 0;
547         return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY);
548 }
549
550 /*
551   generate a ntcreatex open disposition
552 */
553 static uint32_t gen_open_disp(void)
554 {
555         if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF);
556         return gen_int_range(0, 5);
557 }
558
559 /*
560   generate an openx open mode
561 */
562 static uint16_t gen_openx_mode(void)
563 {
564         if (gen_chance(20)) return gen_bits_mask(0xFFFF);
565         if (gen_chance(20)) return gen_bits_mask(0xFF);
566         return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3);
567 }
568
569 /*
570   generate an openx flags field
571 */
572 static uint16_t gen_openx_flags(void)
573 {
574         if (gen_chance(20)) return gen_bits_mask(0xFFFF);
575         return gen_bits_mask(0x7);
576 }
577
578 /*
579   generate an openx open function
580 */
581 static uint16_t gen_openx_func(void)
582 {
583         if (gen_chance(20)) return gen_bits_mask(0xFFFF);
584         return gen_bits_mask(0x13);
585 }
586
587 /*
588   generate a file attrib combination
589 */
590 static uint32_t gen_attrib(void)
591 {
592         if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
593         return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
594 }
595
596 /*
597   generate a unix timestamp
598 */
599 static time_t gen_timet(void)
600 {
601         if (gen_chance(30)) return 0;
602         return (time_t)random();
603 }
604
605 /*
606   generate a unix timestamp
607 */
608 static NTTIME gen_nttime(void)
609 {
610         NTTIME ret;
611         unix_to_nt_time(&ret, gen_timet());
612         return ret;
613 }
614
615 /*
616   generate a milliseconds protocol timeout
617 */
618 static uint32_t gen_timeout(void)
619 {
620         if (gen_chance(98)) return 0;
621         return random() % 50;
622 }
623
624 /*
625   generate a file allocation size
626 */
627 static uint_t gen_alloc_size(void)
628 {
629         uint_t ret;
630
631         if (gen_chance(30)) return 0;
632
633         ret = random() % 4*1024*1024;
634         /* give a high chance of a round number */
635         if (gen_chance(60)) {
636                 ret &= ~(1024*1024 - 1);
637         }
638         return ret;
639 }
640
641 /*
642   generate an ea_struct
643 */
644 static struct ea_struct gen_ea_struct(void)
645 {
646         struct ea_struct ea;
647         const char *names[] = {"EAONE", 
648                                "", 
649                                "FOO!", 
650                                " WITH SPACES ", 
651                                ".", 
652                                "AVERYLONGATTRIBUTENAME"};
653         const char *values[] = {"VALUE1", 
654                                "", 
655                                "NOT MUCH FOO", 
656                                " LEADING SPACES ", 
657                                ":", 
658                                "ASOMEWHATLONGERATTRIBUTEVALUE"};
659         int i;
660
661         ZERO_STRUCT(ea);
662
663         do {
664                 i = gen_int_range(0, ARRAY_SIZE(names)-1);
665         } while (ignore_pattern(names[i]));
666
667         ea.name.s = names[i];
668
669         do {
670                 i = gen_int_range(0, ARRAY_SIZE(values)-1);
671         } while (ignore_pattern(values[i]));
672
673         ea.value = data_blob(values[i], strlen(values[i]));
674
675         if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF);
676         ea.flags = 0;
677
678         return ea;
679 }
680
681
682 /*
683   this is called when a change notify reply comes in
684 */
685 static void async_notify(struct smbcli_request *req)
686 {
687         union smb_notify notify;
688         NTSTATUS status;
689         int i, j;
690         uint16_t tid;
691         struct smbcli_transport *transport = req->transport;
692
693         tid = SVAL(req->in.hdr, HDR_TID);
694
695         notify.nttrans.level = RAW_NOTIFY_NTTRANS;
696         status = smb_raw_changenotify_recv(req, current_op.mem_ctx, &notify);
697         if (NT_STATUS_IS_OK(status)) {
698                 printf("notify tid=%d num_changes=%d action=%d name=%s\n", 
699                        tid, 
700                        notify.nttrans.out.num_changes,
701                        notify.nttrans.out.changes[0].action,
702                        notify.nttrans.out.changes[0].name.s);
703         }
704
705         for (i=0;i<NSERVERS;i++) {
706                 for (j=0;j<NINSTANCES;j++) {
707                         if (transport == servers[i].cli[j]->transport &&
708                             tid == servers[i].cli[j]->tree->tid) {
709                                 notifies[i][j].notify_count++;
710                                 notifies[i][j].status = status;
711                                 notifies[i][j].notify = notify;
712                         }
713                 }
714         }
715 }
716
717 static void oplock_handler_close_recv(struct smbcli_request *req)
718 {
719         NTSTATUS status;
720         status = smbcli_request_simple_recv(req);
721         if (!NT_STATUS_IS_OK(status)) {
722                 printf("close failed in oplock_handler\n");
723                 smb_panic("close failed in oplock_handler");
724         }
725 }
726
727 /*
728   the oplock handler will either ack the break or close the file
729 */
730 static BOOL oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
731 {
732         union smb_close io;
733         int i, j;
734         BOOL do_close;
735         struct smbcli_tree *tree = NULL;
736         struct smbcli_request *req;
737
738         srandom(current_op.seed);
739         do_close = gen_chance(50);
740
741         for (i=0;i<NSERVERS;i++) {
742                 for (j=0;j<NINSTANCES;j++) {
743                         if (transport == servers[i].cli[j]->transport &&
744                             tid == servers[i].cli[j]->tree->tid) {
745                                 oplocks[i][j].got_break = True;
746                                 oplocks[i][j].fnum = fnum;
747                                 oplocks[i][j].handle = fnum_to_handle(i, j, fnum);
748                                 oplocks[i][j].level = level;
749                                 oplocks[i][j].do_close = do_close;
750                                 tree = servers[i].cli[j]->tree;
751                         }
752                 }
753         }
754
755         if (!tree) {
756                 printf("Oplock break not for one of our trees!?\n");
757                 return False;
758         }
759
760         if (!do_close) {
761                 printf("oplock ack fnum=%d\n", fnum);
762                 return smbcli_oplock_ack(tree, fnum, level);
763         }
764
765         printf("oplock close fnum=%d\n", fnum);
766
767         io.close.level = RAW_CLOSE_CLOSE;
768         io.close.in.file.fnum = fnum;
769         io.close.in.write_time = 0;
770         req = smb_raw_close_send(tree, &io);
771
772         if (req == NULL) {
773                 printf("WARNING: close failed in oplock_handler_close\n");
774                 return False;
775         }
776
777         req->async.fn = oplock_handler_close_recv;
778         req->async.private = NULL;
779
780         return True;
781 }
782
783
784 /*
785   the idle function tries to cope with getting an oplock break on a connection, and
786   an operation on another connection blocking until that break is acked
787   we check for operations on all transports in the idle function
788 */
789 static void idle_func(struct smbcli_transport *transport, void *private)
790 {
791         int i, j;
792         for (i=0;i<NSERVERS;i++) {
793                 for (j=0;j<NINSTANCES;j++) {
794                         if (servers[i].cli[j] &&
795                             transport != servers[i].cli[j]->transport) {
796                                 smbcli_transport_process(servers[i].cli[j]->transport);
797                         }
798                 }
799         }
800
801 }
802
803
804 /*
805   compare NTSTATUS, using checking ignored patterns
806 */
807 static BOOL compare_status(NTSTATUS status1, NTSTATUS status2)
808 {
809         if (NT_STATUS_EQUAL(status1, status2)) return True;
810
811         /* one code being an error and the other OK is always an error */
812         if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return False;
813
814         /* if we are ignoring one of the status codes then consider this a match */
815         if (ignore_pattern(nt_errstr(status1)) ||
816             ignore_pattern(nt_errstr(status2))) {
817                 return True;
818         }
819         return False;
820 }
821
822
823 /*
824   check for pending packets on all connections
825 */
826 static void check_pending(void)
827 {
828         int i, j;
829
830         msleep(20);
831
832         for (j=0;j<NINSTANCES;j++) {
833                 for (i=0;i<NSERVERS;i++) {
834                         smbcli_transport_process(servers[i].cli[j]->transport);
835                 }
836         }       
837 }
838
839 /*
840   check that the same oplock breaks have been received by all instances
841 */
842 static BOOL check_oplocks(const char *call)
843 {
844         int i, j;
845         int tries = 0;
846
847 again:
848         check_pending();
849
850         for (j=0;j<NINSTANCES;j++) {
851                 for (i=1;i<NSERVERS;i++) {
852                         if (oplocks[0][j].got_break != oplocks[i][j].got_break ||
853                             oplocks[0][j].handle != oplocks[i][j].handle ||
854                             oplocks[0][j].level != oplocks[i][j].level) {
855                                 if (tries++ < 10) goto again;
856                                 printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n",
857                                        oplocks[0][j].got_break, 
858                                        oplocks[0][j].handle, 
859                                        oplocks[0][j].level, 
860                                        oplocks[i][j].got_break, 
861                                        oplocks[i][j].handle, 
862                                        oplocks[i][j].level);
863                                 return False;
864                         }
865                 }
866         }
867
868         /* if we got a break and closed then remove the handle */
869         for (j=0;j<NINSTANCES;j++) {
870                 if (oplocks[0][j].got_break &&
871                     oplocks[0][j].do_close) {
872                         uint16_t fnums[NSERVERS];
873                         for (i=0;i<NSERVERS;i++) {
874                                 fnums[i] = oplocks[i][j].fnum;
875                         }
876                         gen_remove_handle(j, fnums);
877                         break;
878                 }
879         }       
880         return True;
881 }
882
883
884 /*
885   check that the same change notify info has been received by all instances
886 */
887 static BOOL check_notifies(const char *call)
888 {
889         int i, j;
890         int tries = 0;
891
892 again:
893         check_pending();
894
895         for (j=0;j<NINSTANCES;j++) {
896                 for (i=1;i<NSERVERS;i++) {
897                         int n;
898                         union smb_notify not1, not2;
899
900                         if (notifies[0][j].notify_count != notifies[i][j].notify_count) {
901                                 if (tries++ < 10) goto again;
902                                 printf("Notify count inconsistent %d %d\n",
903                                        notifies[0][j].notify_count,
904                                        notifies[i][j].notify_count);
905                                 return False;
906                         }
907
908                         if (notifies[0][j].notify_count == 0) continue;
909
910                         if (!NT_STATUS_EQUAL(notifies[0][j].status,
911                                              notifies[i][j].status)) {
912                                 printf("Notify status mismatch - %s - %s\n",
913                                        nt_errstr(notifies[0][j].status),
914                                        nt_errstr(notifies[i][j].status));
915                                 return False;
916                         }
917
918                         if (!NT_STATUS_IS_OK(notifies[0][j].status)) {
919                                 continue;
920                         }
921
922                         not1 = notifies[0][j].notify;
923                         not2 = notifies[i][j].notify;
924
925                         for (n=0;n<not1.nttrans.out.num_changes;n++) {
926                                 if (not1.nttrans.out.changes[n].action != 
927                                     not2.nttrans.out.changes[n].action) {
928                                         printf("Notify action %d inconsistent %d %d\n", n,
929                                                not1.nttrans.out.changes[n].action,
930                                                not2.nttrans.out.changes[n].action);
931                                         return False;
932                                 }
933                                 if (strcmp(not1.nttrans.out.changes[n].name.s,
934                                            not2.nttrans.out.changes[n].name.s)) {
935                                         printf("Notify name %d inconsistent %s %s\n", n,
936                                                not1.nttrans.out.changes[n].name.s,
937                                                not2.nttrans.out.changes[n].name.s);
938                                         return False;
939                                 }
940                                 if (not1.nttrans.out.changes[n].name.private_length !=
941                                     not2.nttrans.out.changes[n].name.private_length) {
942                                         printf("Notify name length %d inconsistent %d %d\n", n,
943                                                not1.nttrans.out.changes[n].name.private_length,
944                                                not2.nttrans.out.changes[n].name.private_length);
945                                         return False;
946                                 }
947                         }
948                 }
949         }
950
951         ZERO_STRUCT(notifies);
952
953         return True;
954 }
955
956
957 #define GEN_COPY_PARM do { \
958         int i; \
959         for (i=1;i<NSERVERS;i++) { \
960                 parm[i] = parm[0]; \
961         } \
962 } while (0)
963
964 #define GEN_CALL(call) do { \
965         int i; \
966         ZERO_STRUCT(oplocks); \
967         ZERO_STRUCT(notifies); \
968         for (i=0;i<NSERVERS;i++) { \
969                 struct smbcli_tree *tree = servers[i].cli[instance]->tree; \
970                 status[i] = call; \
971         } \
972         current_op.status = status[0]; \
973         for (i=1;i<NSERVERS;i++) { \
974                 if (!compare_status(status[i], status[0])) { \
975                         printf("status different in %s - %s %s\n", #call, \
976                                nt_errstr(status[0]), nt_errstr(status[i])); \
977                         return False; \
978                 } \
979         } \
980         if (!check_oplocks(#call)) return False; \
981         if (!check_notifies(#call)) return False; \
982         if (!NT_STATUS_IS_OK(status[0])) { \
983                 return True; \
984         } \
985 } while(0)
986
987 #define ADD_HANDLE(name, field) do { \
988         uint16_t fnums[NSERVERS]; \
989         int i; \
990         for (i=0;i<NSERVERS;i++) { \
991                 fnums[i] = parm[i].field; \
992         } \
993         gen_add_handle(instance, name, fnums); \
994 } while(0)
995
996 #define REMOVE_HANDLE(field) do { \
997         uint16_t fnums[NSERVERS]; \
998         int i; \
999         for (i=0;i<NSERVERS;i++) { \
1000                 fnums[i] = parm[i].field; \
1001         } \
1002         gen_remove_handle(instance, fnums); \
1003 } while(0)
1004
1005 #define GEN_SET_FNUM(field) do { \
1006         int i; \
1007         for (i=0;i<NSERVERS;i++) { \
1008                 parm[i].field = gen_lookup_fnum(i, parm[i].field); \
1009         } \
1010 } while(0)
1011
1012 #define CHECK_EQUAL(field) do { \
1013         if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \
1014                 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
1015                        (int)parm[0].field, (int)parm[1].field); \
1016                 return False; \
1017         } \
1018 } while(0)
1019
1020 #define CHECK_WSTR_EQUAL(field) do { \
1021         if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \
1022                 printf("%s is NULL!\n", #field); \
1023                 return False; \
1024         } \
1025         if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \
1026                 printf("Mismatch in %s - %s %s\n", #field, \
1027                        parm[0].field.s, parm[1].field.s); \
1028                 return False; \
1029         } \
1030         CHECK_EQUAL(field.private_length); \
1031 } while(0)
1032
1033 #define CHECK_BLOB_EQUAL(field) do { \
1034         if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \
1035                 printf("Mismatch in %s\n", #field); \
1036                 return False; \
1037         } \
1038         CHECK_EQUAL(field.length); \
1039 } while(0)
1040
1041 #define CHECK_TIMES_EQUAL(field) do { \
1042         if (labs(parm[0].field - parm[1].field) > time_skew() && \
1043             !ignore_pattern(#field)) { \
1044                 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
1045                        (int)parm[0].field, (int)parm[1].field); \
1046                 return False; \
1047         } \
1048 } while(0)
1049
1050 #define CHECK_NTTIMES_EQUAL(field) do { \
1051         if (labs(nt_time_to_unix(parm[0].field) - \
1052                 nt_time_to_unix(parm[1].field)) > time_skew() && \
1053             !ignore_pattern(#field)) { \
1054                 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
1055                        (int)nt_time_to_unix(parm[0].field), \
1056                        (int)nt_time_to_unix(parm[1].field)); \
1057                 return False; \
1058         } \
1059 } while(0)
1060
1061 /*
1062   generate openx operations
1063 */
1064 static BOOL handler_openx(int instance)
1065 {
1066         union smb_open parm[NSERVERS];
1067         NTSTATUS status[NSERVERS];
1068
1069         parm[0].openx.level = RAW_OPEN_OPENX;
1070         parm[0].openx.in.flags = gen_openx_flags();
1071         parm[0].openx.in.open_mode = gen_openx_mode();
1072         parm[0].openx.in.search_attrs = gen_attrib();
1073         parm[0].openx.in.file_attrs = gen_attrib();
1074         parm[0].openx.in.write_time = gen_timet();
1075         parm[0].openx.in.open_func = gen_openx_func();
1076         parm[0].openx.in.size = gen_io_count();
1077         parm[0].openx.in.timeout = gen_timeout();
1078         parm[0].openx.in.fname = gen_fname_open(instance);
1079
1080         if (!options.use_oplocks) {
1081                 /* mask out oplocks */
1082                 parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
1083                                             OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
1084         }
1085         
1086         GEN_COPY_PARM;
1087         GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1088
1089         CHECK_EQUAL(openx.out.attrib);
1090         CHECK_EQUAL(openx.out.size);
1091         CHECK_EQUAL(openx.out.access);
1092         CHECK_EQUAL(openx.out.ftype);
1093         CHECK_EQUAL(openx.out.devstate);
1094         CHECK_EQUAL(openx.out.action);
1095         CHECK_EQUAL(openx.out.access_mask);
1096         CHECK_EQUAL(openx.out.unknown);
1097         CHECK_TIMES_EQUAL(openx.out.write_time);
1098
1099         /* open creates a new file handle */
1100         ADD_HANDLE(parm[0].openx.in.fname, openx.out.file.fnum);
1101
1102         return True;
1103 }
1104
1105
1106 /*
1107   generate open operations
1108 */
1109 static BOOL handler_open(int instance)
1110 {
1111         union smb_open parm[NSERVERS];
1112         NTSTATUS status[NSERVERS];
1113
1114         parm[0].openold.level = RAW_OPEN_OPEN;
1115         parm[0].openold.in.open_mode = gen_bits_mask2(0xF, 0xFFFF);
1116         parm[0].openold.in.search_attrs = gen_attrib();
1117         parm[0].openold.in.fname = gen_fname_open(instance);
1118
1119         if (!options.use_oplocks) {
1120                 /* mask out oplocks */
1121                 parm[0].openold.in.open_mode &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
1122                                                   OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
1123         }
1124         
1125         GEN_COPY_PARM;
1126         GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1127
1128         CHECK_EQUAL(openold.out.attrib);
1129         CHECK_TIMES_EQUAL(openold.out.write_time);
1130         CHECK_EQUAL(openold.out.size);
1131         CHECK_EQUAL(openold.out.rmode);
1132
1133         /* open creates a new file handle */
1134         ADD_HANDLE(parm[0].openold.in.fname, openold.out.file.fnum);
1135
1136         return True;
1137 }
1138
1139
1140 /*
1141   generate ntcreatex operations
1142 */
1143 static BOOL handler_ntcreatex(int instance)
1144 {
1145         union smb_open parm[NSERVERS];
1146         NTSTATUS status[NSERVERS];
1147
1148         parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX;
1149         parm[0].ntcreatex.in.flags = gen_ntcreatex_flags();
1150         parm[0].ntcreatex.in.root_fid = gen_root_fid(instance);
1151         parm[0].ntcreatex.in.access_mask = gen_access_mask();
1152         parm[0].ntcreatex.in.alloc_size = gen_alloc_size();
1153         parm[0].ntcreatex.in.file_attr = gen_attrib();
1154         parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF);
1155         parm[0].ntcreatex.in.open_disposition = gen_open_disp();
1156         parm[0].ntcreatex.in.create_options = gen_create_options();
1157         parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF);
1158         parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF);
1159         parm[0].ntcreatex.in.fname = gen_fname_open(instance);
1160
1161         if (!options.use_oplocks) {
1162                 /* mask out oplocks */
1163                 parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK|
1164                                                 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK);
1165         }
1166         
1167         GEN_COPY_PARM;
1168         if (parm[0].ntcreatex.in.root_fid != 0) {
1169                 GEN_SET_FNUM(ntcreatex.in.root_fid);
1170         }
1171         GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1172
1173         CHECK_EQUAL(ntcreatex.out.oplock_level);
1174         CHECK_EQUAL(ntcreatex.out.create_action);
1175         CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time);
1176         CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time);
1177         CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time);
1178         CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time);
1179         CHECK_EQUAL(ntcreatex.out.attrib);
1180         CHECK_EQUAL(ntcreatex.out.alloc_size);
1181         CHECK_EQUAL(ntcreatex.out.size);
1182         CHECK_EQUAL(ntcreatex.out.file_type);
1183         CHECK_EQUAL(ntcreatex.out.ipc_state);
1184         CHECK_EQUAL(ntcreatex.out.is_directory);
1185
1186         /* ntcreatex creates a new file handle */
1187         ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.file.fnum);
1188
1189         return True;
1190 }
1191
1192 /*
1193   generate close operations
1194 */
1195 static BOOL handler_close(int instance)
1196 {
1197         union smb_close parm[NSERVERS];
1198         NTSTATUS status[NSERVERS];
1199
1200         parm[0].close.level = RAW_CLOSE_CLOSE;
1201         parm[0].close.in.file.fnum = gen_fnum_close(instance);
1202         parm[0].close.in.write_time = gen_timet();
1203
1204         GEN_COPY_PARM;
1205         GEN_SET_FNUM(close.in.file.fnum);
1206         GEN_CALL(smb_raw_close(tree, &parm[i]));
1207
1208         REMOVE_HANDLE(close.in.file.fnum);
1209
1210         return True;
1211 }
1212
1213 /*
1214   generate unlink operations
1215 */
1216 static BOOL handler_unlink(int instance)
1217 {
1218         union smb_unlink parm[NSERVERS];
1219         NTSTATUS status[NSERVERS];
1220
1221         parm[0].unlink.in.pattern = gen_pattern();
1222         parm[0].unlink.in.attrib = gen_attrib();
1223
1224         GEN_COPY_PARM;
1225         GEN_CALL(smb_raw_unlink(tree, &parm[i]));
1226
1227         return True;
1228 }
1229
1230 /*
1231   generate chkpath operations
1232 */
1233 static BOOL handler_chkpath(int instance)
1234 {
1235         union smb_chkpath parm[NSERVERS];
1236         NTSTATUS status[NSERVERS];
1237
1238         parm[0].chkpath.in.path = gen_fname_open(instance);
1239
1240         GEN_COPY_PARM;
1241         GEN_CALL(smb_raw_chkpath(tree, &parm[i]));
1242
1243         return True;
1244 }
1245
1246 /*
1247   generate mkdir operations
1248 */
1249 static BOOL handler_mkdir(int instance)
1250 {
1251         union smb_mkdir parm[NSERVERS];
1252         NTSTATUS status[NSERVERS];
1253
1254         parm[0].mkdir.level = RAW_MKDIR_MKDIR;
1255         parm[0].mkdir.in.path = gen_fname_open(instance);
1256
1257         GEN_COPY_PARM;
1258         GEN_CALL(smb_raw_mkdir(tree, &parm[i]));
1259
1260         return True;
1261 }
1262
1263 /*
1264   generate rmdir operations
1265 */
1266 static BOOL handler_rmdir(int instance)
1267 {
1268         struct smb_rmdir parm[NSERVERS];
1269         NTSTATUS status[NSERVERS];
1270
1271         parm[0].in.path = gen_fname_open(instance);
1272
1273         GEN_COPY_PARM;
1274         GEN_CALL(smb_raw_rmdir(tree, &parm[i]));
1275
1276         return True;
1277 }
1278
1279 /*
1280   generate rename operations
1281 */
1282 static BOOL handler_rename(int instance)
1283 {
1284         union smb_rename parm[NSERVERS];
1285         NTSTATUS status[NSERVERS];
1286
1287         parm[0].generic.level = RAW_RENAME_RENAME;
1288         parm[0].rename.in.pattern1 = gen_pattern();
1289         parm[0].rename.in.pattern2 = gen_pattern();
1290         parm[0].rename.in.attrib = gen_attrib();
1291
1292         GEN_COPY_PARM;
1293         GEN_CALL(smb_raw_rename(tree, &parm[i]));
1294
1295         return True;
1296 }
1297
1298 /*
1299   generate ntrename operations
1300 */
1301 static BOOL handler_ntrename(int instance)
1302 {
1303         union smb_rename parm[NSERVERS];
1304         NTSTATUS status[NSERVERS];
1305
1306         parm[0].generic.level = RAW_RENAME_NTRENAME;
1307         parm[0].ntrename.in.old_name = gen_fname();
1308         parm[0].ntrename.in.new_name = gen_fname();
1309         parm[0].ntrename.in.attrib = gen_attrib();
1310         parm[0].ntrename.in.cluster_size = gen_bits_mask2(0, 0xFFFFFFF);
1311         parm[0].ntrename.in.flags = gen_rename_flags();
1312
1313         GEN_COPY_PARM;
1314         GEN_CALL(smb_raw_rename(tree, &parm[i]));
1315
1316         return True;
1317 }
1318
1319
1320 /*
1321   generate seek operations
1322 */
1323 static BOOL handler_seek(int instance)
1324 {
1325         union smb_seek parm[NSERVERS];
1326         NTSTATUS status[NSERVERS];
1327
1328         parm[0].lseek.in.file.fnum = gen_fnum(instance);
1329         parm[0].lseek.in.mode = gen_bits_mask2(0x3, 0xFFFF);
1330         parm[0].lseek.in.offset = gen_offset();
1331
1332         GEN_COPY_PARM;
1333         GEN_SET_FNUM(lseek.in.file.fnum);
1334         GEN_CALL(smb_raw_seek(tree, &parm[i]));
1335
1336         CHECK_EQUAL(lseek.out.offset);
1337
1338         return True;
1339 }
1340
1341
1342 /*
1343   generate readx operations
1344 */
1345 static BOOL handler_readx(int instance)
1346 {
1347         union smb_read parm[NSERVERS];
1348         NTSTATUS status[NSERVERS];
1349
1350         parm[0].readx.level = RAW_READ_READX;
1351         parm[0].readx.in.file.fnum = gen_fnum(instance);
1352         parm[0].readx.in.offset = gen_offset();
1353         parm[0].readx.in.mincnt = gen_io_count();
1354         parm[0].readx.in.maxcnt = gen_io_count();
1355         parm[0].readx.in.remaining = gen_io_count();
1356         parm[0].readx.in.read_for_execute = gen_bool();
1357         parm[0].readx.out.data = talloc_array(current_op.mem_ctx, uint8_t,
1358                                              MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt));
1359
1360         GEN_COPY_PARM;
1361         GEN_SET_FNUM(readx.in.file.fnum);
1362         GEN_CALL(smb_raw_read(tree, &parm[i]));
1363
1364         CHECK_EQUAL(readx.out.remaining);
1365         CHECK_EQUAL(readx.out.compaction_mode);
1366         CHECK_EQUAL(readx.out.nread);
1367
1368         return True;
1369 }
1370
1371 /*
1372   generate writex operations
1373 */
1374 static BOOL handler_writex(int instance)
1375 {
1376         union smb_write parm[NSERVERS];
1377         NTSTATUS status[NSERVERS];
1378
1379         parm[0].writex.level = RAW_WRITE_WRITEX;
1380         parm[0].writex.in.file.fnum = gen_fnum(instance);
1381         parm[0].writex.in.offset = gen_offset();
1382         parm[0].writex.in.wmode = gen_bits_mask(0xFFFF);
1383         parm[0].writex.in.remaining = gen_io_count();
1384         parm[0].writex.in.count = gen_io_count();
1385         parm[0].writex.in.data = talloc_zero_array(current_op.mem_ctx, uint8_t, parm[0].writex.in.count);
1386
1387         GEN_COPY_PARM;
1388         GEN_SET_FNUM(writex.in.file.fnum);
1389         GEN_CALL(smb_raw_write(tree, &parm[i]));
1390
1391         CHECK_EQUAL(writex.out.nwritten);
1392         CHECK_EQUAL(writex.out.remaining);
1393
1394         return True;
1395 }
1396
1397 /*
1398   generate lockingx operations
1399 */
1400 static BOOL handler_lockingx(int instance)
1401 {
1402         union smb_lock parm[NSERVERS];
1403         NTSTATUS status[NSERVERS];
1404         int n, nlocks;
1405
1406         parm[0].lockx.level = RAW_LOCK_LOCKX;
1407         parm[0].lockx.in.file.fnum = gen_fnum(instance);
1408         parm[0].lockx.in.mode = gen_lock_mode();
1409         parm[0].lockx.in.timeout = gen_timeout();
1410         do {
1411                 /* make sure we don't accidentially generate an oplock
1412                    break ack - otherwise the server can just block forever */
1413                 parm[0].lockx.in.ulock_cnt = gen_lock_count();
1414                 parm[0].lockx.in.lock_cnt = gen_lock_count();
1415                 nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt;
1416         } while (nlocks == 0);
1417
1418         if (nlocks > 0) {
1419                 parm[0].lockx.in.locks = talloc_array(current_op.mem_ctx,
1420                                                         struct smb_lock_entry,
1421                                                         nlocks);
1422                 for (n=0;n<nlocks;n++) {
1423                         parm[0].lockx.in.locks[n].pid = gen_pid();
1424                         parm[0].lockx.in.locks[n].offset = gen_offset();
1425                         parm[0].lockx.in.locks[n].count = gen_io_count();
1426                 }
1427         }
1428
1429         GEN_COPY_PARM;
1430         GEN_SET_FNUM(lockx.in.file.fnum);
1431         GEN_CALL(smb_raw_lock(tree, &parm[i]));
1432
1433         return True;
1434 }
1435
1436 /*
1437   generate a fileinfo query structure
1438 */
1439 static void gen_fileinfo(int instance, union smb_fileinfo *info)
1440 {
1441         int i;
1442         #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v}
1443         struct {
1444                 enum smb_fileinfo_level level;
1445                 const char *name;
1446         }  levels[] = {
1447                 LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD),
1448                 LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID),
1449                 LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO),
1450                 LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO),
1451                 LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION),
1452                 LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION),
1453                 LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION),
1454                 LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION),
1455                 LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION),
1456                 LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION)
1457         };
1458         do {
1459                 i = gen_int_range(0, ARRAY_SIZE(levels)-1);
1460         } while (ignore_pattern(levels[i].name));
1461
1462         info->generic.level = levels[i].level;
1463 }
1464
1465 /*
1466   compare returned fileinfo structures
1467 */
1468 static BOOL cmp_fileinfo(int instance, 
1469                          union smb_fileinfo parm[NSERVERS],
1470                          NTSTATUS status[NSERVERS])
1471 {
1472         int i;
1473
1474         switch (parm[0].generic.level) {
1475         case RAW_FILEINFO_GENERIC:
1476                 return False;
1477
1478         case RAW_FILEINFO_GETATTR:
1479                 CHECK_EQUAL(getattr.out.attrib);
1480                 CHECK_EQUAL(getattr.out.size);
1481                 CHECK_TIMES_EQUAL(getattr.out.write_time);
1482                 break;
1483
1484         case RAW_FILEINFO_GETATTRE:
1485                 CHECK_TIMES_EQUAL(getattre.out.create_time);
1486                 CHECK_TIMES_EQUAL(getattre.out.access_time);
1487                 CHECK_TIMES_EQUAL(getattre.out.write_time);
1488                 CHECK_EQUAL(getattre.out.size);
1489                 CHECK_EQUAL(getattre.out.alloc_size);
1490                 CHECK_EQUAL(getattre.out.attrib);
1491                 break;
1492
1493         case RAW_FILEINFO_STANDARD:
1494                 CHECK_TIMES_EQUAL(standard.out.create_time);
1495                 CHECK_TIMES_EQUAL(standard.out.access_time);
1496                 CHECK_TIMES_EQUAL(standard.out.write_time);
1497                 CHECK_EQUAL(standard.out.size);
1498                 CHECK_EQUAL(standard.out.alloc_size);
1499                 CHECK_EQUAL(standard.out.attrib);
1500                 break;
1501
1502         case RAW_FILEINFO_EA_SIZE:
1503                 CHECK_TIMES_EQUAL(ea_size.out.create_time);
1504                 CHECK_TIMES_EQUAL(ea_size.out.access_time);
1505                 CHECK_TIMES_EQUAL(ea_size.out.write_time);
1506                 CHECK_EQUAL(ea_size.out.size);
1507                 CHECK_EQUAL(ea_size.out.alloc_size);
1508                 CHECK_EQUAL(ea_size.out.attrib);
1509                 CHECK_EQUAL(ea_size.out.ea_size);
1510                 break;
1511
1512         case RAW_FILEINFO_ALL_EAS:
1513                 CHECK_EQUAL(all_eas.out.num_eas);
1514                 for (i=0;i<parm[0].all_eas.out.num_eas;i++) {
1515                         CHECK_EQUAL(all_eas.out.eas[i].flags);
1516                         CHECK_WSTR_EQUAL(all_eas.out.eas[i].name);
1517                         CHECK_BLOB_EQUAL(all_eas.out.eas[i].value);
1518                 }
1519                 break;
1520
1521         case RAW_FILEINFO_IS_NAME_VALID:
1522                 break;
1523                 
1524         case RAW_FILEINFO_BASIC_INFO:
1525         case RAW_FILEINFO_BASIC_INFORMATION:
1526                 CHECK_NTTIMES_EQUAL(basic_info.out.create_time);
1527                 CHECK_NTTIMES_EQUAL(basic_info.out.access_time);
1528                 CHECK_NTTIMES_EQUAL(basic_info.out.write_time);
1529                 CHECK_NTTIMES_EQUAL(basic_info.out.change_time);
1530                 CHECK_EQUAL(basic_info.out.attrib);
1531                 break;
1532
1533         case RAW_FILEINFO_STANDARD_INFO:
1534         case RAW_FILEINFO_STANDARD_INFORMATION:
1535                 CHECK_EQUAL(standard_info.out.alloc_size);
1536                 CHECK_EQUAL(standard_info.out.size);
1537                 CHECK_EQUAL(standard_info.out.nlink);
1538                 CHECK_EQUAL(standard_info.out.delete_pending);
1539                 CHECK_EQUAL(standard_info.out.directory);
1540                 break;
1541
1542         case RAW_FILEINFO_EA_INFO:
1543         case RAW_FILEINFO_EA_INFORMATION:
1544                 CHECK_EQUAL(ea_info.out.ea_size);
1545                 break;
1546
1547         case RAW_FILEINFO_NAME_INFO:
1548         case RAW_FILEINFO_NAME_INFORMATION:
1549                 CHECK_WSTR_EQUAL(name_info.out.fname);
1550                 break;
1551
1552         case RAW_FILEINFO_ALL_INFO:
1553         case RAW_FILEINFO_ALL_INFORMATION:
1554                 CHECK_NTTIMES_EQUAL(all_info.out.create_time);
1555                 CHECK_NTTIMES_EQUAL(all_info.out.access_time);
1556                 CHECK_NTTIMES_EQUAL(all_info.out.write_time);
1557                 CHECK_NTTIMES_EQUAL(all_info.out.change_time);
1558                 CHECK_EQUAL(all_info.out.attrib);
1559                 CHECK_EQUAL(all_info.out.alloc_size);
1560                 CHECK_EQUAL(all_info.out.size);
1561                 CHECK_EQUAL(all_info.out.nlink);
1562                 CHECK_EQUAL(all_info.out.delete_pending);
1563                 CHECK_EQUAL(all_info.out.directory);
1564                 CHECK_EQUAL(all_info.out.ea_size);
1565                 CHECK_WSTR_EQUAL(all_info.out.fname);
1566                 break;
1567
1568         case RAW_FILEINFO_ALT_NAME_INFO:
1569         case RAW_FILEINFO_ALT_NAME_INFORMATION:
1570                 CHECK_WSTR_EQUAL(alt_name_info.out.fname);
1571                 break;
1572
1573         case RAW_FILEINFO_STREAM_INFO:
1574         case RAW_FILEINFO_STREAM_INFORMATION:
1575                 CHECK_EQUAL(stream_info.out.num_streams);
1576                 for (i=0;i<parm[0].stream_info.out.num_streams;i++) {
1577                         CHECK_EQUAL(stream_info.out.streams[i].size);
1578                         CHECK_EQUAL(stream_info.out.streams[i].alloc_size);
1579                         CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name);
1580                 }
1581                 break;
1582
1583         case RAW_FILEINFO_COMPRESSION_INFO:
1584         case RAW_FILEINFO_COMPRESSION_INFORMATION:
1585                 CHECK_EQUAL(compression_info.out.compressed_size);
1586                 CHECK_EQUAL(compression_info.out.format);
1587                 CHECK_EQUAL(compression_info.out.unit_shift);
1588                 CHECK_EQUAL(compression_info.out.chunk_shift);
1589                 CHECK_EQUAL(compression_info.out.cluster_shift);
1590                 break;
1591
1592         case RAW_FILEINFO_INTERNAL_INFORMATION:
1593                 CHECK_EQUAL(internal_information.out.file_id);
1594                 break;
1595
1596         case RAW_FILEINFO_ACCESS_INFORMATION:
1597                 CHECK_EQUAL(access_information.out.access_flags);
1598                 break;
1599
1600         case RAW_FILEINFO_POSITION_INFORMATION:
1601                 CHECK_EQUAL(position_information.out.position);
1602                 break;
1603
1604         case RAW_FILEINFO_MODE_INFORMATION:
1605                 CHECK_EQUAL(mode_information.out.mode);
1606                 break;
1607
1608         case RAW_FILEINFO_ALIGNMENT_INFORMATION:
1609                 CHECK_EQUAL(alignment_information.out.alignment_requirement);
1610                 break;
1611
1612         case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
1613                 CHECK_NTTIMES_EQUAL(network_open_information.out.create_time);
1614                 CHECK_NTTIMES_EQUAL(network_open_information.out.access_time);
1615                 CHECK_NTTIMES_EQUAL(network_open_information.out.write_time);
1616                 CHECK_NTTIMES_EQUAL(network_open_information.out.change_time);
1617                 CHECK_EQUAL(network_open_information.out.alloc_size);
1618                 CHECK_EQUAL(network_open_information.out.size);
1619                 CHECK_EQUAL(network_open_information.out.attrib);
1620                 break;
1621
1622         case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
1623                 CHECK_EQUAL(attribute_tag_information.out.attrib);
1624                 CHECK_EQUAL(attribute_tag_information.out.reparse_tag);
1625                 break;
1626
1627                 /* Unhandled levels */
1628
1629         case RAW_FILEINFO_SEC_DESC:
1630         case RAW_FILEINFO_EA_LIST:
1631         case RAW_FILEINFO_UNIX_BASIC:
1632         case RAW_FILEINFO_UNIX_LINK:
1633         case RAW_FILEINFO_SMB2_ALL_EAS:
1634         case RAW_FILEINFO_SMB2_ALL_INFORMATION: 
1635                 break;
1636         }
1637
1638         return True;
1639 }
1640
1641 /*
1642   generate qpathinfo operations
1643 */
1644 static BOOL handler_qpathinfo(int instance)
1645 {
1646         union smb_fileinfo parm[NSERVERS];
1647         NTSTATUS status[NSERVERS];
1648
1649         parm[0].generic.in.file.path = gen_fname_open(instance);
1650
1651         gen_fileinfo(instance, &parm[0]);
1652
1653         GEN_COPY_PARM;
1654         GEN_CALL(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i]));
1655
1656         return cmp_fileinfo(instance, parm, status);
1657 }
1658
1659 /*
1660   generate qfileinfo operations
1661 */
1662 static BOOL handler_qfileinfo(int instance)
1663 {
1664         union smb_fileinfo parm[NSERVERS];
1665         NTSTATUS status[NSERVERS];
1666
1667         parm[0].generic.in.file.fnum = gen_fnum(instance);
1668
1669         gen_fileinfo(instance, &parm[0]);
1670
1671         GEN_COPY_PARM;
1672         GEN_SET_FNUM(generic.in.file.fnum);
1673         GEN_CALL(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i]));
1674
1675         return cmp_fileinfo(instance, parm, status);
1676 }
1677
1678
1679 /*
1680   generate a fileinfo query structure
1681 */
1682 static void gen_setfileinfo(int instance, union smb_setfileinfo *info)
1683 {
1684         int i;
1685         #undef LVL
1686         #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v}
1687         struct {
1688                 enum smb_setfileinfo_level level;
1689                 const char *name;
1690         }  levels[] = {
1691 #if 0
1692                 /* disabled until win2003 can handle them ... */
1693                 LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), 
1694                 LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), 
1695 #endif
1696                 LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION),
1697                 LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), 
1698                 LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION),
1699                 LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), 
1700                 LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040)
1701         };
1702         do {
1703                 i = gen_int_range(0, ARRAY_SIZE(levels)-1);
1704         } while (ignore_pattern(levels[i].name));
1705
1706         info->generic.level = levels[i].level;
1707
1708         switch (info->generic.level) {
1709         case RAW_SFILEINFO_SETATTR:
1710                 info->setattr.in.attrib = gen_attrib();
1711                 info->setattr.in.write_time = gen_timet();
1712                 break;
1713         case RAW_SFILEINFO_SETATTRE:
1714                 info->setattre.in.create_time = gen_timet();
1715                 info->setattre.in.access_time = gen_timet();
1716                 info->setattre.in.write_time = gen_timet();
1717                 break;
1718         case RAW_SFILEINFO_STANDARD:
1719                 info->standard.in.create_time = gen_timet();
1720                 info->standard.in.access_time = gen_timet();
1721                 info->standard.in.write_time = gen_timet();
1722                 break;
1723         case RAW_SFILEINFO_EA_SET: {
1724                 static struct ea_struct ea;
1725                 info->ea_set.in.num_eas = 1;
1726                 info->ea_set.in.eas = &ea;
1727                 info->ea_set.in.eas[0] = gen_ea_struct();
1728         }
1729                 break;
1730         case RAW_SFILEINFO_BASIC_INFO:
1731         case RAW_SFILEINFO_BASIC_INFORMATION:
1732                 info->basic_info.in.create_time = gen_nttime();
1733                 info->basic_info.in.access_time = gen_nttime();
1734                 info->basic_info.in.write_time = gen_nttime();
1735                 info->basic_info.in.change_time = gen_nttime();
1736                 info->basic_info.in.attrib = gen_attrib();
1737                 break;
1738         case RAW_SFILEINFO_DISPOSITION_INFO:
1739         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
1740                 info->disposition_info.in.delete_on_close = gen_bool();
1741                 break;
1742         case RAW_SFILEINFO_ALLOCATION_INFO:
1743         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
1744                 info->allocation_info.in.alloc_size = gen_alloc_size();
1745                 break;
1746         case RAW_SFILEINFO_END_OF_FILE_INFO:
1747         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
1748                 info->end_of_file_info.in.size = gen_offset();
1749                 break;
1750         case RAW_SFILEINFO_RENAME_INFORMATION:
1751                 info->rename_information.in.overwrite = gen_bool();
1752                 info->rename_information.in.root_fid = gen_root_fid(instance);
1753                 info->rename_information.in.new_name = gen_fname_open(instance);
1754                 break;
1755         case RAW_SFILEINFO_POSITION_INFORMATION:
1756                 info->position_information.in.position = gen_offset();
1757                 break;
1758         case RAW_SFILEINFO_MODE_INFORMATION:
1759                 info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF);
1760                 break;
1761         case RAW_SFILEINFO_GENERIC:
1762         case RAW_SFILEINFO_SEC_DESC:
1763         case RAW_SFILEINFO_UNIX_BASIC:
1764         case RAW_SFILEINFO_UNIX_LINK:
1765         case RAW_SFILEINFO_UNIX_HLINK:
1766         case RAW_SFILEINFO_1023:
1767         case RAW_SFILEINFO_1025:
1768         case RAW_SFILEINFO_1029:
1769         case RAW_SFILEINFO_1032:
1770         case RAW_SFILEINFO_1039:
1771         case RAW_SFILEINFO_1040:
1772                 /* Untested */
1773                 break;
1774         }
1775 }
1776
1777 /*
1778   generate setpathinfo operations
1779 */
1780 static BOOL handler_spathinfo(int instance)
1781 {
1782         union smb_setfileinfo parm[NSERVERS];
1783         NTSTATUS status[NSERVERS];
1784
1785         parm[0].generic.in.file.path = gen_fname_open(instance);
1786
1787         gen_setfileinfo(instance, &parm[0]);
1788
1789         GEN_COPY_PARM;
1790
1791         /* a special case for the fid in a RENAME */
1792         if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION &&
1793             parm[0].rename_information.in.root_fid != 0) {
1794                 GEN_SET_FNUM(rename_information.in.root_fid);
1795         }
1796
1797         GEN_CALL(smb_raw_setpathinfo(tree, &parm[i]));
1798
1799         return True;
1800 }
1801
1802
1803 /*
1804   generate setfileinfo operations
1805 */
1806 static BOOL handler_sfileinfo(int instance)
1807 {
1808         union smb_setfileinfo parm[NSERVERS];
1809         NTSTATUS status[NSERVERS];
1810
1811         parm[0].generic.in.file.fnum = gen_fnum(instance);
1812
1813         gen_setfileinfo(instance, &parm[0]);
1814
1815         GEN_COPY_PARM;
1816         GEN_SET_FNUM(generic.in.file.fnum);
1817         GEN_CALL(smb_raw_setfileinfo(tree, &parm[i]));
1818
1819         return True;
1820 }
1821
1822
1823 /*
1824   generate change notify operations
1825 */
1826 static BOOL handler_notify(int instance)
1827 {
1828         union smb_notify parm[NSERVERS];
1829         int n;
1830
1831         ZERO_STRUCT(parm[0]);
1832         parm[0].nttrans.level                   = RAW_NOTIFY_NTTRANS;
1833         parm[0].nttrans.in.buffer_size          = gen_io_count();
1834         parm[0].nttrans.in.completion_filter    = gen_bits_mask(0xFF);
1835         parm[0].nttrans.in.file.fnum            = gen_fnum(instance);
1836         parm[0].nttrans.in.recursive            = gen_bool();
1837
1838         GEN_COPY_PARM;
1839         GEN_SET_FNUM(nttrans.in.file.fnum);
1840
1841         for (n=0;n<NSERVERS;n++) {
1842                 struct smbcli_request *req;
1843                 req = smb_raw_changenotify_send(servers[n].cli[instance]->tree, &parm[n]);
1844                 req->async.fn = async_notify;
1845         }
1846
1847         return True;
1848 }
1849
1850 /*
1851   wipe any relevant files
1852 */
1853 static void wipe_files(void)
1854 {
1855         int i;
1856         for (i=0;i<NSERVERS;i++) {
1857                 int n = smbcli_deltree(servers[i].cli[0]->tree, "\\gentest");
1858                 if (n == -1) {
1859                         printf("Failed to wipe tree on server %d\n", i);
1860                         exit(1);
1861                 }
1862                 if (NT_STATUS_IS_ERR(smbcli_mkdir(servers[i].cli[0]->tree, "\\gentest"))) {
1863                         printf("Failed to create \\gentest - %s\n",
1864                                smbcli_errstr(servers[i].cli[0]->tree));
1865                         exit(1);
1866                 }
1867                 if (n > 0) {
1868                         printf("Deleted %d files on server %d\n", n, i);
1869                 }
1870         }
1871 }
1872
1873 /*
1874   dump the current seeds - useful for continuing a backtrack
1875 */
1876 static void dump_seeds(void)
1877 {
1878         int i;
1879         FILE *f;
1880
1881         if (!options.seeds_file) {
1882                 return;
1883         }
1884         f = fopen("seeds.tmp", "w");
1885         if (!f) return;
1886
1887         for (i=0;i<options.numops;i++) {
1888                 fprintf(f, "%u\n", op_parms[i].seed);
1889         }
1890         fclose(f);
1891         rename("seeds.tmp", options.seeds_file);
1892 }
1893
1894
1895
1896 /*
1897   the list of top-level operations that we will generate
1898 */
1899 static struct {
1900         const char *name;
1901         BOOL (*handler)(int instance);
1902         int count, success_count;
1903 } gen_ops[] = {
1904         {"OPEN",       handler_open},
1905         {"OPENX",      handler_openx},
1906         {"NTCREATEX",  handler_ntcreatex},
1907         {"CLOSE",      handler_close},
1908         {"UNLINK",     handler_unlink},
1909         {"MKDIR",      handler_mkdir},
1910         {"RMDIR",      handler_rmdir},
1911         {"RENAME",     handler_rename},
1912         {"NTRENAME",   handler_ntrename},
1913         {"READX",      handler_readx},
1914         {"WRITEX",     handler_writex},
1915         {"CHKPATH",    handler_chkpath},
1916         {"LOCKINGX",   handler_lockingx},
1917         {"QPATHINFO",  handler_qpathinfo},
1918         {"QFILEINFO",  handler_qfileinfo},
1919         {"SPATHINFO",  handler_spathinfo},
1920         {"SFILEINFO",  handler_sfileinfo},
1921         {"NOTIFY",     handler_notify},
1922         {"SEEK",       handler_seek},
1923 };
1924
1925
1926 /*
1927   run the test with the current set of op_parms parameters
1928   return the number of operations that completed successfully
1929 */
1930 static int run_test(void)
1931 {
1932         int op, i;
1933
1934         if (!connect_servers()) {
1935                 printf("Failed to connect to servers\n");
1936                 exit(1);
1937         }
1938
1939         dump_seeds();
1940
1941         /* wipe any leftover files from old runs */
1942         wipe_files();
1943
1944         /* reset the open handles array */
1945         memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0]));
1946         num_open_handles = 0;
1947
1948         for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
1949                 gen_ops[i].count = 0;
1950                 gen_ops[i].success_count = 0;
1951         }
1952
1953         for (op=0; op<options.numops; op++) {
1954                 int instance, which_op;
1955                 BOOL ret;
1956
1957                 if (op_parms[op].disabled) continue;
1958
1959                 srandom(op_parms[op].seed);
1960
1961                 instance = gen_int_range(0, NINSTANCES-1);
1962
1963                 /* generate a non-ignored operation */
1964                 do {
1965                         which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1);
1966                 } while (ignore_pattern(gen_ops[which_op].name));
1967
1968                 DEBUG(3,("Generating op %s on instance %d\n",
1969                          gen_ops[which_op].name, instance));
1970
1971                 current_op.seed = op_parms[op].seed;
1972                 current_op.opnum = op;
1973                 current_op.name = gen_ops[which_op].name;
1974                 current_op.status = NT_STATUS_OK;
1975                 current_op.mem_ctx = talloc_named(NULL, 0, "%s", current_op.name);
1976
1977                 ret = gen_ops[which_op].handler(instance);
1978
1979                 talloc_free(current_op.mem_ctx);
1980
1981                 gen_ops[which_op].count++;
1982                 if (NT_STATUS_IS_OK(current_op.status)) {
1983                         gen_ops[which_op].success_count++;                      
1984                 }
1985
1986                 if (!ret) {
1987                         printf("Failed at operation %d - %s\n",
1988                                op, gen_ops[which_op].name);
1989                         return op;
1990                 }
1991
1992                 if (op % 100 == 0) {
1993                         printf("%d\n", op);
1994                 }
1995         }
1996
1997         for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
1998                 printf("Op %-10s got %d/%d success\n", 
1999                        gen_ops[i].name,
2000                        gen_ops[i].success_count,
2001                        gen_ops[i].count);
2002         }
2003
2004         return op;
2005 }
2006
2007 /* 
2008    perform a backtracking analysis of the minimal set of operations
2009    to generate an error
2010 */
2011 static void backtrack_analyze(void)
2012 {
2013         int chunk, ret;
2014
2015         chunk = options.numops / 2;
2016
2017         do {
2018                 int base;
2019                 for (base=0; 
2020                      chunk > 0 && base+chunk < options.numops && options.numops > 1; ) {
2021                         int i, max;
2022
2023                         chunk = MIN(chunk, options.numops / 2);
2024
2025                         /* mark this range as disabled */
2026                         max = MIN(options.numops, base+chunk);
2027                         for (i=base;i<max; i++) {
2028                                 op_parms[i].disabled = True;
2029                         }
2030                         printf("Testing %d ops with %d-%d disabled\n", 
2031                                options.numops, base, max-1);
2032                         ret = run_test();
2033                         printf("Completed %d of %d ops\n", ret, options.numops);
2034                         for (i=base;i<max; i++) {
2035                                 op_parms[i].disabled = False;
2036                         }
2037                         if (ret == options.numops) {
2038                                 /* this chunk is needed */
2039                                 base += chunk;
2040                         } else if (ret < base) {
2041                                 printf("damn - inconsistent errors! found early error\n");
2042                                 options.numops = ret+1;
2043                                 base = 0;
2044                         } else {
2045                                 /* it failed - this chunk isn't needed for a failure */
2046                                 memmove(&op_parms[base], &op_parms[max], 
2047                                         sizeof(op_parms[0]) * (options.numops - max));
2048                                 options.numops = (ret+1) - (max - base);
2049                         }
2050                 }
2051
2052                 if (chunk == 2) {
2053                         chunk = 1;
2054                 } else {
2055                         chunk *= 0.4;
2056                 }
2057
2058                 if (options.analyze_continuous && chunk == 0 && options.numops != 1) {
2059                         chunk = 1;
2060                 }
2061         } while (chunk > 0);
2062
2063         printf("Reduced to %d ops\n", options.numops);
2064         ret = run_test();
2065         if (ret != options.numops - 1) {
2066                 printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops);
2067         }
2068 }
2069
2070 /* 
2071    start the main gentest process
2072 */
2073 static BOOL start_gentest(void)
2074 {
2075         int op;
2076         int ret;
2077
2078         /* allocate the open_handles array */
2079         open_handles = calloc(options.max_open_handles, sizeof(open_handles[0]));
2080
2081         srandom(options.seed);
2082         op_parms = calloc(options.numops, sizeof(op_parms[0]));
2083
2084         /* generate the seeds - after this everything is deterministic */
2085         if (options.use_preset_seeds) {
2086                 int numops;
2087                 char **preset = file_lines_load(options.seeds_file, &numops, NULL);
2088                 if (!preset) {
2089                         printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno));
2090                         exit(1);
2091                 }
2092                 if (numops < options.numops) {
2093                         options.numops = numops;
2094                 }
2095                 for (op=0;op<options.numops;op++) {
2096                         if (!preset[op]) {
2097                                 printf("Not enough seeds in %s\n", options.seeds_file);
2098                                 exit(1);
2099                         }
2100                         op_parms[op].seed = atoi(preset[op]);
2101                 }
2102                 printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file);
2103         } else {
2104                 for (op=0; op<options.numops; op++) {
2105                         op_parms[op].seed = random();
2106                 }
2107         }
2108
2109         ret = run_test();
2110
2111         if (ret != options.numops && options.analyze) {
2112                 options.numops = ret+1;
2113                 backtrack_analyze();
2114         } else if (options.analyze_always) {
2115                 backtrack_analyze();
2116         } else if (options.analyze_continuous) {
2117                 while (run_test() == options.numops) ;
2118         }
2119
2120         return ret == options.numops;
2121 }
2122
2123
2124 static void usage(void)
2125 {
2126         printf(
2127 "Usage:\n\
2128   gentest2 //server1/share1 //server2/share2 [options..]\n\
2129   options:\n\
2130         -U user%%pass        (can be specified twice)\n\
2131         -s seed\n\
2132         -o numops\n\
2133         -a            (show all ops)\n\
2134         -A            backtrack to find minimal ops\n\
2135         -i FILE       add a list of wildcard exclusions\n\
2136         -O            enable oplocks\n\
2137         -S FILE       set preset seeds file\n\
2138         -L            use preset seeds\n\
2139         -F            fast reconnect (just close files)\n\
2140         -C            continuous analysis mode\n\
2141         -X            analyse even when test OK\n\
2142 ");
2143 }
2144
2145 /**
2146   split a UNC name into server and share names
2147 */
2148 static BOOL split_unc_name(const char *unc, char **server, char **share)
2149 {
2150         char *p = strdup(unc);
2151         if (!p) return False;
2152         all_string_sub(p, "\\", "/", 0);
2153         if (strncmp(p, "//", 2) != 0) return False;
2154
2155         (*server) = p+2;
2156         p = strchr(*server, '/');
2157         if (!p) return False;
2158
2159         *p = 0;
2160         (*share) = p+1;
2161         
2162         return True;
2163 }
2164
2165
2166
2167 /****************************************************************************
2168   main program
2169 ****************************************************************************/
2170  int main(int argc, char *argv[])
2171 {
2172         int opt;
2173         int i, username_count=0;
2174         BOOL ret;
2175
2176         setlinebuf(stdout);
2177
2178         setup_logging("gentest", DEBUG_STDOUT);
2179
2180         if (argc < 3 || argv[1][0] == '-') {
2181                 usage();
2182                 exit(1);
2183         }
2184
2185         setup_logging(argv[0], DEBUG_STDOUT);
2186
2187         for (i=0;i<NSERVERS;i++) {
2188                 const char *share = argv[1+i];
2189                 servers[i].credentials = cli_credentials_init(NULL);
2190                 if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) {
2191                         printf("Invalid share name '%s'\n", share);
2192                         return -1;
2193                 }
2194         }
2195
2196         argc -= NSERVERS;
2197         argv += NSERVERS;
2198
2199         lp_load(dyn_CONFIGFILE);
2200
2201         servers[0].credentials = cli_credentials_init(talloc_autofree_context());
2202         servers[1].credentials = cli_credentials_init(talloc_autofree_context());
2203         cli_credentials_guess(servers[0].credentials);
2204         cli_credentials_guess(servers[1].credentials);
2205
2206         options.seed = time(NULL);
2207         options.numops = 1000;
2208         options.max_open_handles = 20;
2209         options.seeds_file = "gentest_seeds.dat";
2210
2211         while ((opt = getopt(argc, argv, "U:s:o:ad:i:AOhS:LFXC")) != EOF) {
2212                 switch (opt) {
2213                 case 'U':
2214                         if (username_count == 2) {
2215                                 usage();
2216                                 exit(1);
2217                         }
2218                         cli_credentials_parse_string(servers[username_count].credentials, 
2219                                                      optarg, CRED_SPECIFIED);
2220                         username_count++;
2221                         break;
2222                 case 'd':
2223                         DEBUGLEVEL = atoi(optarg);
2224                         setup_logging(NULL, DEBUG_STDOUT);
2225                         break;
2226                 case 's':
2227                         options.seed = atoi(optarg);
2228                         break;
2229                 case 'S':
2230                         options.seeds_file = optarg;
2231                         break;
2232                 case 'L':
2233                         options.use_preset_seeds = True;
2234                         break;
2235                 case 'F':
2236                         options.fast_reconnect = True;
2237                         break;
2238                 case 'o':
2239                         options.numops = atoi(optarg);
2240                         break;
2241                 case 'O':
2242                         options.use_oplocks = True;
2243                         break;
2244                 case 'a':
2245                         options.showall = True;
2246                         break;
2247                 case 'A':
2248                         options.analyze = True;
2249                         break;
2250                 case 'X':
2251                         options.analyze_always = True;
2252                         break;
2253                 case 'C':
2254                         options.analyze_continuous = True;
2255                         break;
2256                 case 'i':
2257                         options.ignore_patterns = file_lines_load(optarg, NULL, NULL);
2258                         break;
2259                 case 'h':
2260                         usage();
2261                         exit(1);
2262                 default:
2263                         printf("Unknown option %c (%d)\n", (char)opt, opt);
2264                         exit(1);
2265                 }
2266         }
2267
2268         gensec_init();
2269
2270         if (username_count == 0) {
2271                 usage();
2272                 return -1;
2273         }
2274         if (username_count == 1) {
2275                 servers[1].credentials = servers[0].credentials;
2276         }
2277
2278         printf("seed=%u\n", options.seed);
2279
2280         ret = start_gentest();
2281
2282         if (ret) {
2283                 printf("gentest completed - no errors\n");
2284         } else {
2285                 printf("gentest failed\n");
2286         }
2287
2288         return ret?0:-1;
2289 }