r5929: Use cli_credentials for the SMB functions as well.
[Samba.git] / source4 / torture / gentest.c
blob92d6da9fef0e48e8be5995cc600e91d0f5c0f5b5
1 /*
2 Unix SMB/CIFS implementation.
3 generic testing tool
4 Copyright (C) Andrew Tridgell 2003
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 2 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
22 #include "dynconfig.h"
23 #include "system/time.h"
24 #include "system/filesys.h"
25 #include "request.h"
26 #include "libcli/raw/libcliraw.h"
27 #include "librpc/gen_ndr/ndr_security.h"
29 #define NSERVERS 2
30 #define NINSTANCES 2
32 /* global options */
33 static struct gentest_options {
34 BOOL showall;
35 BOOL analyze;
36 BOOL analyze_always;
37 BOOL analyze_continuous;
38 uint_t max_open_handles;
39 uint_t seed;
40 uint_t numops;
41 BOOL use_oplocks;
42 char **ignore_patterns;
43 const char *seeds_file;
44 BOOL use_preset_seeds;
45 BOOL fast_reconnect;
46 } options;
48 /* mapping between open handles on the server and local handles */
49 static struct {
50 BOOL active;
51 uint_t instance;
52 uint_t server_fnum[NSERVERS];
53 const char *name;
54 } *open_handles;
55 static uint_t num_open_handles;
57 /* state information for the servers. We open NINSTANCES connections to
58 each server */
59 static struct {
60 struct smbcli_state *cli[NINSTANCES];
61 char *server_name;
62 char *share_name;
63 struct cli_credentials credentials;
64 } servers[NSERVERS];
66 /* the seeds and flags for each operation */
67 static struct {
68 uint_t seed;
69 BOOL disabled;
70 } *op_parms;
73 /* oplock break info */
74 static struct {
75 BOOL got_break;
76 uint16_t fnum;
77 uint16_t handle;
78 uint8_t level;
79 BOOL do_close;
80 } oplocks[NSERVERS][NINSTANCES];
82 /* change notify reply info */
83 static struct {
84 int notify_count;
85 NTSTATUS status;
86 struct smb_notify notify;
87 } notifies[NSERVERS][NINSTANCES];
89 /* info relevant to the current operation */
90 static struct {
91 const char *name;
92 uint_t seed;
93 NTSTATUS status;
94 uint_t opnum;
95 TALLOC_CTX *mem_ctx;
96 } current_op;
100 #define BAD_HANDLE 0xFFFE
102 static BOOL oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private);
103 static void idle_func(struct smbcli_transport *transport, void *private);
106 check if a string should be ignored. This is used as the basis
107 for all error ignore settings
109 static BOOL ignore_pattern(const char *str)
111 int i;
112 if (!options.ignore_patterns) return False;
114 for (i=0;options.ignore_patterns[i];i++) {
115 if (strcmp(options.ignore_patterns[i], str) == 0 ||
116 gen_fnmatch(options.ignore_patterns[i], str) == 0) {
117 DEBUG(2,("Ignoring '%s'\n", str));
118 return True;
121 return False;
124 /*****************************************************
125 connect to the servers
126 *******************************************************/
127 static BOOL connect_servers_fast(void)
129 int h, i;
131 /* close all open files */
132 for (h=0;h<options.max_open_handles;h++) {
133 if (!open_handles[h].active) continue;
134 for (i=0;i<NSERVERS;i++) {
135 if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree,
136 open_handles[h].server_fnum[i])))) {
137 return False;
139 open_handles[h].active = False;
143 return True;
149 /*****************************************************
150 connect to the servers
151 *******************************************************/
152 static BOOL connect_servers(void)
154 int i, j;
156 if (options.fast_reconnect && servers[0].cli[0]) {
157 if (connect_servers_fast()) {
158 return True;
162 /* close any existing connections */
163 for (i=0;i<NSERVERS;i++) {
164 for (j=0;j<NINSTANCES;j++) {
165 if (servers[i].cli[j]) {
166 smbcli_tdis(servers[i].cli[j]);
167 smbcli_shutdown(servers[i].cli[j]);
168 servers[i].cli[j] = NULL;
173 for (i=0;i<NSERVERS;i++) {
174 for (j=0;j<NINSTANCES;j++) {
175 NTSTATUS status;
176 printf("Connecting to \\\\%s\\%s as %s - instance %d\n",
177 servers[i].server_name, servers[i].share_name,
178 servers[i].credentials.username, j);
180 status = smbcli_full_connection(NULL, &servers[i].cli[j],
181 "gentest",
182 servers[i].server_name,
183 servers[i].share_name, NULL,
184 &servers[i].credentials);
185 if (!NT_STATUS_IS_OK(status)) {
186 printf("Failed to connect to \\\\%s\\%s - %s\n",
187 servers[i].server_name, servers[i].share_name,
188 nt_errstr(status));
189 return False;
192 smbcli_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL);
193 smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL);
197 return True;
201 work out the time skew between the servers - be conservative
203 static uint_t time_skew(void)
205 uint_t ret;
206 ret = ABS(servers[0].cli[0]->transport->negotiate.server_time -
207 servers[1].cli[0]->transport->negotiate.server_time);
208 return ret + 300;
212 turn an fnum for an instance into a handle
214 static uint_t fnum_to_handle(int server, int instance, uint16_t fnum)
216 uint_t i;
217 for (i=0;i<options.max_open_handles;i++) {
218 if (!open_handles[i].active ||
219 instance != open_handles[i].instance) continue;
220 if (open_handles[i].server_fnum[server] == fnum) {
221 return i;
224 printf("Invalid fnum %d in fnum_to_handle on server %d instance %d\n",
225 fnum, server, instance);
226 return BAD_HANDLE;
230 add some newly opened handles
232 static void gen_add_handle(int instance, const char *name, uint16_t fnums[NSERVERS])
234 int i, h;
235 for (h=0;h<options.max_open_handles;h++) {
236 if (!open_handles[h].active) break;
238 if (h == options.max_open_handles) {
239 /* we have to force close a random handle */
240 h = random() % options.max_open_handles;
241 for (i=0;i<NSERVERS;i++) {
242 if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree,
243 open_handles[h].server_fnum[i])))) {
244 printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n",
245 smbcli_errstr(servers[i].cli[open_handles[h].instance]->tree));
248 printf("Recovered handle %d\n", h);
249 num_open_handles--;
251 for (i=0;i<NSERVERS;i++) {
252 open_handles[h].server_fnum[i] = fnums[i];
253 open_handles[h].instance = instance;
254 open_handles[h].active = True;
255 open_handles[h].name = name;
257 num_open_handles++;
259 printf("OPEN num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n",
260 num_open_handles, h,
261 open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
262 name);
266 remove a closed handle
268 static void gen_remove_handle(int instance, uint16_t fnums[NSERVERS])
270 int h;
271 for (h=0;h<options.max_open_handles;h++) {
272 if (instance == open_handles[h].instance &&
273 open_handles[h].server_fnum[0] == fnums[0]) {
274 open_handles[h].active = False;
275 num_open_handles--;
276 printf("CLOSE num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n",
277 num_open_handles, h,
278 open_handles[h].server_fnum[0], open_handles[h].server_fnum[1],
279 open_handles[h].name);
280 return;
283 printf("Removing invalid handle!?\n");
284 exit(1);
288 return True with 'chance' probability as a percentage
290 static BOOL gen_chance(uint_t chance)
292 return ((random() % 100) <= chance);
296 map an internal handle number to a server fnum
298 static uint16_t gen_lookup_fnum(int server, uint16_t handle)
300 if (handle == BAD_HANDLE) return handle;
301 return open_handles[handle].server_fnum[server];
305 return a file handle
307 static uint16_t gen_fnum(int instance)
309 uint16_t h;
310 int count = 0;
312 if (gen_chance(20)) return BAD_HANDLE;
314 while (num_open_handles > 0 && count++ < 10*options.max_open_handles) {
315 h = random() % options.max_open_handles;
316 if (open_handles[h].active &&
317 open_handles[h].instance == instance) {
318 return h;
321 return BAD_HANDLE;
325 return a file handle, but skewed so we don't close the last
326 couple of handles too readily
328 static uint16_t gen_fnum_close(int instance)
330 if (num_open_handles < 3) {
331 if (gen_chance(80)) return BAD_HANDLE;
334 return gen_fnum(instance);
338 generate an integer in a specified range
340 static int gen_int_range(uint_t min, uint_t max)
342 uint_t r = random();
343 return min + (r % (1+max-min));
347 return a fnum for use as a root fid
348 be careful to call GEN_SET_FNUM() when you use this!
350 static uint16_t gen_root_fid(int instance)
352 if (gen_chance(5)) return gen_fnum(instance);
353 return 0;
357 generate a file offset
359 static int gen_offset(void)
361 if (gen_chance(20)) return 0;
362 return gen_int_range(0, 1024*1024);
366 generate a io count
368 static int gen_io_count(void)
370 if (gen_chance(20)) return 0;
371 return gen_int_range(0, 4096);
375 generate a filename
377 static const char *gen_fname(void)
379 const char *names[] = {"\\gentest\\gentest.dat",
380 "\\gentest\\foo",
381 "\\gentest\\foo2.sym",
382 "\\gentest\\foo3.dll",
383 "\\gentest\\foo4",
384 "\\gentest\\foo4:teststream1",
385 "\\gentest\\foo4:teststream2",
386 "\\gentest\\foo5.exe",
387 "\\gentest\\foo5.exe:teststream3",
388 "\\gentest\\foo5.exe:teststream4",
389 "\\gentest\\foo6.com",
390 "\\gentest\\blah",
391 "\\gentest\\blah\\blergh.txt",
392 "\\gentest\\blah\\blergh2",
393 "\\gentest\\blah\\blergh3.txt",
394 "\\gentest\\blah\\blergh4",
395 "\\gentest\\blah\\blergh5.txt",
396 "\\gentest\\blah\\blergh5",
397 "\\gentest\\blah\\.",
398 #if 0
399 /* this causes problem with w2k3 */
400 "\\gentest\\blah\\..",
401 #endif
402 "\\gentest\\a_very_long_name.bin",
403 "\\gentest\\x.y",
404 "\\gentest\\blah"};
405 int i;
407 do {
408 i = gen_int_range(0, ARRAY_SIZE(names)-1);
409 } while (ignore_pattern(names[i]));
411 return names[i];
415 generate a filename with a higher chance of choosing an already
416 open file
418 static const char *gen_fname_open(int instance)
420 uint16_t h;
421 h = gen_fnum(instance);
422 if (h == BAD_HANDLE) {
423 return gen_fname();
425 return open_handles[h].name;
429 generate a wildcard pattern
431 static const char *gen_pattern(void)
433 int i;
434 const char *names[] = {"\\gentest\\*.dat",
435 "\\gentest\\*",
436 "\\gentest\\*.*",
437 "\\gentest\\blah\\*.*",
438 "\\gentest\\blah\\*",
439 "\\gentest\\?"};
441 if (gen_chance(50)) return gen_fname();
443 do {
444 i = gen_int_range(0, ARRAY_SIZE(names)-1);
445 } while (ignore_pattern(names[i]));
447 return names[i];
451 generate a bitmask
453 static uint32_t gen_bits_mask(uint_t mask)
455 uint_t ret = random();
456 return ret & mask;
460 generate a bitmask with high probability of the first mask
461 and low of the second
463 static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2)
465 if (gen_chance(10)) return gen_bits_mask(mask2);
466 return gen_bits_mask(mask1);
470 generate a boolean
472 static BOOL gen_bool(void)
474 return gen_bits_mask2(0x1, 0xFF);
478 generate ntrename flags
480 static uint16_t gen_rename_flags(void)
482 if (gen_chance(30)) return RENAME_FLAG_RENAME;
483 if (gen_chance(30)) return RENAME_FLAG_HARD_LINK;
484 if (gen_chance(30)) return RENAME_FLAG_COPY;
485 return gen_bits_mask(0xFFFF);
490 return a lockingx lock mode
492 static uint16_t gen_lock_mode(void)
494 if (gen_chance(5)) return gen_bits_mask(0xFFFF);
495 if (gen_chance(20)) return gen_bits_mask(0x1F);
496 return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES);
500 generate a pid
502 static uint16_t gen_pid(void)
504 if (gen_chance(10)) return gen_bits_mask(0xFFFF);
505 return getpid();
509 generate a lock count
511 static off_t gen_lock_count(void)
513 return gen_int_range(0, 3);
517 generate a ntcreatex flags field
519 static uint32_t gen_ntcreatex_flags(void)
521 if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED;
522 return gen_bits_mask2(0x1F, 0xFFFFFFFF);
526 generate a NT access mask
528 static uint32_t gen_access_mask(void)
530 if (gen_chance(50)) return SEC_FLAG_MAXIMUM_ALLOWED;
531 if (gen_chance(20)) return SEC_FILE_ALL;
532 return gen_bits_mask(0xFFFFFFFF);
536 generate a ntcreatex create options bitfield
538 static uint32_t gen_create_options(void)
540 if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
541 if (gen_chance(50)) return 0;
542 return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY);
546 generate a ntcreatex open disposition
548 static uint32_t gen_open_disp(void)
550 if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF);
551 return gen_int_range(0, 5);
555 generate an openx open mode
557 static uint16_t gen_openx_mode(void)
559 if (gen_chance(20)) return gen_bits_mask(0xFFFF);
560 if (gen_chance(20)) return gen_bits_mask(0xFF);
561 return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3);
565 generate an openx flags field
567 static uint16_t gen_openx_flags(void)
569 if (gen_chance(20)) return gen_bits_mask(0xFFFF);
570 return gen_bits_mask(0x7);
574 generate an openx open function
576 static uint16_t gen_openx_func(void)
578 if (gen_chance(20)) return gen_bits_mask(0xFFFF);
579 return gen_bits_mask(0x13);
583 generate a file attrib combination
585 static uint32_t gen_attrib(void)
587 if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF);
588 return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY);
592 generate a unix timestamp
594 static time_t gen_timet(void)
596 if (gen_chance(30)) return 0;
597 return (time_t)random();
601 generate a unix timestamp
603 static NTTIME gen_nttime(void)
605 NTTIME ret;
606 unix_to_nt_time(&ret, gen_timet());
607 return ret;
611 generate a milliseconds protocol timeout
613 static uint32_t gen_timeout(void)
615 if (gen_chance(98)) return 0;
616 return random() % 50;
620 generate a file allocation size
622 static uint_t gen_alloc_size(void)
624 uint_t ret;
626 if (gen_chance(30)) return 0;
628 ret = random() % 4*1024*1024;
629 /* give a high chance of a round number */
630 if (gen_chance(60)) {
631 ret &= ~(1024*1024 - 1);
633 return ret;
637 generate an ea_struct
639 static struct ea_struct gen_ea_struct(void)
641 struct ea_struct ea;
642 const char *names[] = {"EAONE",
643 "",
644 "FOO!",
645 " WITH SPACES ",
646 ".",
647 "AVERYLONGATTRIBUTENAME"};
648 const char *values[] = {"VALUE1",
649 "",
650 "NOT MUCH FOO",
651 " LEADING SPACES ",
652 ":",
653 "ASOMEWHATLONGERATTRIBUTEVALUE"};
654 int i;
656 do {
657 i = gen_int_range(0, ARRAY_SIZE(names)-1);
658 } while (ignore_pattern(names[i]));
660 ea.name.s = names[i];
662 do {
663 i = gen_int_range(0, ARRAY_SIZE(values)-1);
664 } while (ignore_pattern(values[i]));
666 ea.value = data_blob(values[i], strlen(values[i]));
668 if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF);
669 ea.flags = 0;
671 return ea;
676 this is called when a change notify reply comes in
678 static void async_notify(struct smbcli_request *req)
680 struct smb_notify notify;
681 NTSTATUS status;
682 int i, j;
683 uint16_t tid;
684 struct smbcli_transport *transport = req->transport;
686 tid = SVAL(req->in.hdr, HDR_TID);
688 status = smb_raw_changenotify_recv(req, current_op.mem_ctx, &notify);
689 if (NT_STATUS_IS_OK(status)) {
690 printf("notify tid=%d num_changes=%d action=%d name=%s\n",
691 tid,
692 notify.out.num_changes,
693 notify.out.changes[0].action,
694 notify.out.changes[0].name.s);
697 for (i=0;i<NSERVERS;i++) {
698 for (j=0;j<NINSTANCES;j++) {
699 if (transport == servers[i].cli[j]->transport &&
700 tid == servers[i].cli[j]->tree->tid) {
701 notifies[i][j].notify_count++;
702 notifies[i][j].status = status;
703 notifies[i][j].notify = notify;
710 the oplock handler will either ack the break or close the file
712 static BOOL oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private)
714 union smb_close io;
715 NTSTATUS status;
716 int i, j;
717 BOOL do_close;
718 struct smbcli_tree *tree = NULL;
720 srandom(current_op.seed);
721 do_close = gen_chance(50);
723 for (i=0;i<NSERVERS;i++) {
724 for (j=0;j<NINSTANCES;j++) {
725 if (transport == servers[i].cli[j]->transport &&
726 tid == servers[i].cli[j]->tree->tid) {
727 oplocks[i][j].got_break = True;
728 oplocks[i][j].fnum = fnum;
729 oplocks[i][j].handle = fnum_to_handle(i, j, fnum);
730 oplocks[i][j].level = level;
731 oplocks[i][j].do_close = do_close;
732 tree = servers[i].cli[j]->tree;
737 if (!tree) {
738 printf("Oplock break not for one of our trees!?\n");
739 return False;
742 if (!do_close) {
743 printf("oplock ack fnum=%d\n", fnum);
744 return smbcli_oplock_ack(tree, fnum, level);
747 printf("oplock close fnum=%d\n", fnum);
749 io.close.level = RAW_CLOSE_CLOSE;
750 io.close.in.fnum = fnum;
751 io.close.in.write_time = 0;
752 status = smb_raw_close(tree, &io);
754 if (!NT_STATUS_IS_OK(status)) {
755 printf("WARNING: close failed in oplock_handler_close - %s\n", nt_errstr(status));
757 return True;
762 the idle function tries to cope with getting an oplock break on a connection, and
763 an operation on another connection blocking until that break is acked
764 we check for operations on all transports in the idle function
766 static void idle_func(struct smbcli_transport *transport, void *private)
768 int i, j;
769 for (i=0;i<NSERVERS;i++) {
770 for (j=0;j<NINSTANCES;j++) {
771 if (servers[i].cli[j] &&
772 transport != servers[i].cli[j]->transport) {
773 smbcli_transport_process(servers[i].cli[j]->transport);
782 compare NTSTATUS, using checking ignored patterns
784 static BOOL compare_status(NTSTATUS status1, NTSTATUS status2)
786 if (NT_STATUS_EQUAL(status1, status2)) return True;
788 /* one code being an error and the other OK is always an error */
789 if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return False;
791 /* if we are ignoring one of the status codes then consider this a match */
792 if (ignore_pattern(nt_errstr(status1)) ||
793 ignore_pattern(nt_errstr(status2))) {
794 return True;
796 return False;
801 check for pending packets on all connections
803 static void check_pending(void)
805 int i, j;
807 msleep(20);
809 for (j=0;j<NINSTANCES;j++) {
810 for (i=0;i<NSERVERS;i++) {
811 smbcli_transport_process(servers[i].cli[j]->transport);
817 check that the same oplock breaks have been received by all instances
819 static BOOL check_oplocks(const char *call)
821 int i, j;
822 int tries = 0;
824 again:
825 check_pending();
827 for (j=0;j<NINSTANCES;j++) {
828 for (i=1;i<NSERVERS;i++) {
829 if (oplocks[0][j].got_break != oplocks[i][j].got_break ||
830 oplocks[0][j].handle != oplocks[i][j].handle ||
831 oplocks[0][j].level != oplocks[i][j].level) {
832 if (tries++ < 10) goto again;
833 printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n",
834 oplocks[0][j].got_break,
835 oplocks[0][j].handle,
836 oplocks[0][j].level,
837 oplocks[i][j].got_break,
838 oplocks[i][j].handle,
839 oplocks[i][j].level);
840 return False;
845 /* if we got a break and closed then remove the handle */
846 for (j=0;j<NINSTANCES;j++) {
847 if (oplocks[0][j].got_break &&
848 oplocks[0][j].do_close) {
849 uint16_t fnums[NSERVERS];
850 for (i=0;i<NSERVERS;i++) {
851 fnums[i] = oplocks[i][j].fnum;
853 gen_remove_handle(j, fnums);
854 break;
857 return True;
862 check that the same change notify info has been received by all instances
864 static BOOL check_notifies(const char *call)
866 int i, j;
867 int tries = 0;
869 again:
870 check_pending();
872 for (j=0;j<NINSTANCES;j++) {
873 for (i=1;i<NSERVERS;i++) {
874 int n;
875 struct smb_notify not1, not2;
877 if (notifies[0][j].notify_count != notifies[i][j].notify_count) {
878 if (tries++ < 10) goto again;
879 printf("Notify count inconsistent %d %d\n",
880 notifies[0][j].notify_count,
881 notifies[i][j].notify_count);
882 return False;
885 if (notifies[0][j].notify_count == 0) continue;
887 if (!NT_STATUS_EQUAL(notifies[0][j].status,
888 notifies[i][j].status)) {
889 printf("Notify status mismatch - %s - %s\n",
890 nt_errstr(notifies[0][j].status),
891 nt_errstr(notifies[i][j].status));
892 return False;
895 if (!NT_STATUS_IS_OK(notifies[0][j].status)) {
896 continue;
899 not1 = notifies[0][j].notify;
900 not2 = notifies[i][j].notify;
902 for (n=0;n<not1.out.num_changes;n++) {
903 if (not1.out.changes[n].action !=
904 not2.out.changes[n].action) {
905 printf("Notify action %d inconsistent %d %d\n", n,
906 not1.out.changes[n].action,
907 not2.out.changes[n].action);
908 return False;
910 if (strcmp(not1.out.changes[n].name.s,
911 not2.out.changes[n].name.s)) {
912 printf("Notify name %d inconsistent %s %s\n", n,
913 not1.out.changes[n].name.s,
914 not2.out.changes[n].name.s);
915 return False;
917 if (not1.out.changes[n].name.private_length !=
918 not2.out.changes[n].name.private_length) {
919 printf("Notify name length %d inconsistent %d %d\n", n,
920 not1.out.changes[n].name.private_length,
921 not2.out.changes[n].name.private_length);
922 return False;
928 ZERO_STRUCT(notifies);
930 return True;
934 #define GEN_COPY_PARM do { \
935 int i; \
936 for (i=1;i<NSERVERS;i++) { \
937 parm[i] = parm[0]; \
939 } while (0)
941 #define GEN_CALL(call) do { \
942 int i; \
943 ZERO_STRUCT(oplocks); \
944 ZERO_STRUCT(notifies); \
945 for (i=0;i<NSERVERS;i++) { \
946 struct smbcli_tree *tree = servers[i].cli[instance]->tree; \
947 status[i] = call; \
949 current_op.status = status[0]; \
950 for (i=1;i<NSERVERS;i++) { \
951 if (!compare_status(status[i], status[0])) { \
952 printf("status different in %s - %s %s\n", #call, \
953 nt_errstr(status[0]), nt_errstr(status[i])); \
954 return False; \
957 if (!check_oplocks(#call)) return False; \
958 if (!check_notifies(#call)) return False; \
959 if (!NT_STATUS_IS_OK(status[0])) { \
960 return True; \
962 } while(0)
964 #define ADD_HANDLE(name, field) do { \
965 uint16_t fnums[NSERVERS]; \
966 int i; \
967 for (i=0;i<NSERVERS;i++) { \
968 fnums[i] = parm[i].field; \
970 gen_add_handle(instance, name, fnums); \
971 } while(0)
973 #define REMOVE_HANDLE(field) do { \
974 uint16_t fnums[NSERVERS]; \
975 int i; \
976 for (i=0;i<NSERVERS;i++) { \
977 fnums[i] = parm[i].field; \
979 gen_remove_handle(instance, fnums); \
980 } while(0)
982 #define GEN_SET_FNUM(field) do { \
983 int i; \
984 for (i=0;i<NSERVERS;i++) { \
985 parm[i].field = gen_lookup_fnum(i, parm[i].field); \
987 } while(0)
989 #define CHECK_EQUAL(field) do { \
990 if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \
991 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
992 (int)parm[0].field, (int)parm[1].field); \
993 return False; \
995 } while(0)
997 #define CHECK_WSTR_EQUAL(field) do { \
998 if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \
999 printf("%s is NULL!\n", #field); \
1000 return False; \
1002 if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \
1003 printf("Mismatch in %s - %s %s\n", #field, \
1004 parm[0].field.s, parm[1].field.s); \
1005 return False; \
1007 CHECK_EQUAL(field.private_length); \
1008 } while(0)
1010 #define CHECK_BLOB_EQUAL(field) do { \
1011 if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \
1012 printf("Mismatch in %s\n", #field); \
1013 return False; \
1015 CHECK_EQUAL(field.length); \
1016 } while(0)
1018 #define CHECK_TIMES_EQUAL(field) do { \
1019 if (ABS(parm[0].field - parm[1].field) > time_skew() && \
1020 !ignore_pattern(#field)) { \
1021 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
1022 (int)parm[0].field, (int)parm[1].field); \
1023 return False; \
1025 } while(0)
1027 #define CHECK_NTTIMES_EQUAL(field) do { \
1028 if (ABS(nt_time_to_unix(parm[0].field) - \
1029 nt_time_to_unix(parm[1].field)) > time_skew() && \
1030 !ignore_pattern(#field)) { \
1031 printf("Mismatch in %s - 0x%x 0x%x\n", #field, \
1032 (int)nt_time_to_unix(parm[0].field), \
1033 (int)nt_time_to_unix(parm[1].field)); \
1034 return False; \
1036 } while(0)
1039 generate openx operations
1041 static BOOL handler_openx(int instance)
1043 union smb_open parm[NSERVERS];
1044 NTSTATUS status[NSERVERS];
1046 parm[0].openx.level = RAW_OPEN_OPENX;
1047 parm[0].openx.in.flags = gen_openx_flags();
1048 parm[0].openx.in.open_mode = gen_openx_mode();
1049 parm[0].openx.in.search_attrs = gen_attrib();
1050 parm[0].openx.in.file_attrs = gen_attrib();
1051 parm[0].openx.in.write_time = gen_timet();
1052 parm[0].openx.in.open_func = gen_openx_func();
1053 parm[0].openx.in.size = gen_io_count();
1054 parm[0].openx.in.timeout = gen_timeout();
1055 parm[0].openx.in.fname = gen_fname_open(instance);
1057 if (!options.use_oplocks) {
1058 /* mask out oplocks */
1059 parm[0].openx.in.flags &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
1060 OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
1063 GEN_COPY_PARM;
1064 GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1066 CHECK_EQUAL(openx.out.attrib);
1067 CHECK_EQUAL(openx.out.size);
1068 CHECK_EQUAL(openx.out.access);
1069 CHECK_EQUAL(openx.out.ftype);
1070 CHECK_EQUAL(openx.out.devstate);
1071 CHECK_EQUAL(openx.out.action);
1072 CHECK_EQUAL(openx.out.access_mask);
1073 CHECK_EQUAL(openx.out.unknown);
1074 CHECK_TIMES_EQUAL(openx.out.write_time);
1076 /* open creates a new file handle */
1077 ADD_HANDLE(parm[0].openx.in.fname, openx.out.fnum);
1079 return True;
1084 generate open operations
1086 static BOOL handler_open(int instance)
1088 union smb_open parm[NSERVERS];
1089 NTSTATUS status[NSERVERS];
1091 parm[0].openold.level = RAW_OPEN_OPEN;
1092 parm[0].openold.in.open_mode = gen_bits_mask2(0xF, 0xFFFF);
1093 parm[0].openold.in.search_attrs = gen_attrib();
1094 parm[0].openold.in.fname = gen_fname_open(instance);
1096 if (!options.use_oplocks) {
1097 /* mask out oplocks */
1098 parm[0].openold.in.open_mode &= ~(OPENX_FLAGS_REQUEST_OPLOCK|
1099 OPENX_FLAGS_REQUEST_BATCH_OPLOCK);
1102 GEN_COPY_PARM;
1103 GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1105 CHECK_EQUAL(openold.out.attrib);
1106 CHECK_TIMES_EQUAL(openold.out.write_time);
1107 CHECK_EQUAL(openold.out.size);
1108 CHECK_EQUAL(openold.out.rmode);
1110 /* open creates a new file handle */
1111 ADD_HANDLE(parm[0].openold.in.fname, openold.out.fnum);
1113 return True;
1118 generate ntcreatex operations
1120 static BOOL handler_ntcreatex(int instance)
1122 union smb_open parm[NSERVERS];
1123 NTSTATUS status[NSERVERS];
1125 parm[0].ntcreatex.level = RAW_OPEN_NTCREATEX;
1126 parm[0].ntcreatex.in.flags = gen_ntcreatex_flags();
1127 parm[0].ntcreatex.in.root_fid = gen_root_fid(instance);
1128 parm[0].ntcreatex.in.access_mask = gen_access_mask();
1129 parm[0].ntcreatex.in.alloc_size = gen_alloc_size();
1130 parm[0].ntcreatex.in.file_attr = gen_attrib();
1131 parm[0].ntcreatex.in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF);
1132 parm[0].ntcreatex.in.open_disposition = gen_open_disp();
1133 parm[0].ntcreatex.in.create_options = gen_create_options();
1134 parm[0].ntcreatex.in.impersonation = gen_bits_mask2(0, 0xFFFFFFFF);
1135 parm[0].ntcreatex.in.security_flags = gen_bits_mask2(0, 0xFF);
1136 parm[0].ntcreatex.in.fname = gen_fname_open(instance);
1138 if (!options.use_oplocks) {
1139 /* mask out oplocks */
1140 parm[0].ntcreatex.in.flags &= ~(NTCREATEX_FLAGS_REQUEST_OPLOCK|
1141 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK);
1144 GEN_COPY_PARM;
1145 if (parm[0].ntcreatex.in.root_fid != 0) {
1146 GEN_SET_FNUM(ntcreatex.in.root_fid);
1148 GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i]));
1150 CHECK_EQUAL(ntcreatex.out.oplock_level);
1151 CHECK_EQUAL(ntcreatex.out.create_action);
1152 CHECK_NTTIMES_EQUAL(ntcreatex.out.create_time);
1153 CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time);
1154 CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time);
1155 CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time);
1156 CHECK_EQUAL(ntcreatex.out.attrib);
1157 CHECK_EQUAL(ntcreatex.out.alloc_size);
1158 CHECK_EQUAL(ntcreatex.out.size);
1159 CHECK_EQUAL(ntcreatex.out.file_type);
1160 CHECK_EQUAL(ntcreatex.out.ipc_state);
1161 CHECK_EQUAL(ntcreatex.out.is_directory);
1163 /* ntcreatex creates a new file handle */
1164 ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.fnum);
1166 return True;
1170 generate close operations
1172 static BOOL handler_close(int instance)
1174 union smb_close parm[NSERVERS];
1175 NTSTATUS status[NSERVERS];
1177 parm[0].close.level = RAW_CLOSE_CLOSE;
1178 parm[0].close.in.fnum = gen_fnum_close(instance);
1179 parm[0].close.in.write_time = gen_timet();
1181 GEN_COPY_PARM;
1182 GEN_SET_FNUM(close.in.fnum);
1183 GEN_CALL(smb_raw_close(tree, &parm[i]));
1185 REMOVE_HANDLE(close.in.fnum);
1187 return True;
1191 generate unlink operations
1193 static BOOL handler_unlink(int instance)
1195 struct smb_unlink parm[NSERVERS];
1196 NTSTATUS status[NSERVERS];
1198 parm[0].in.pattern = gen_pattern();
1199 parm[0].in.attrib = gen_attrib();
1201 GEN_COPY_PARM;
1202 GEN_CALL(smb_raw_unlink(tree, &parm[i]));
1204 return True;
1208 generate chkpath operations
1210 static BOOL handler_chkpath(int instance)
1212 struct smb_chkpath parm[NSERVERS];
1213 NTSTATUS status[NSERVERS];
1215 parm[0].in.path = gen_fname_open(instance);
1217 GEN_COPY_PARM;
1218 GEN_CALL(smb_raw_chkpath(tree, &parm[i]));
1220 return True;
1224 generate mkdir operations
1226 static BOOL handler_mkdir(int instance)
1228 union smb_mkdir parm[NSERVERS];
1229 NTSTATUS status[NSERVERS];
1231 parm[0].mkdir.level = RAW_MKDIR_MKDIR;
1232 parm[0].mkdir.in.path = gen_fname_open(instance);
1234 GEN_COPY_PARM;
1235 GEN_CALL(smb_raw_mkdir(tree, &parm[i]));
1237 return True;
1241 generate rmdir operations
1243 static BOOL handler_rmdir(int instance)
1245 struct smb_rmdir parm[NSERVERS];
1246 NTSTATUS status[NSERVERS];
1248 parm[0].in.path = gen_fname_open(instance);
1250 GEN_COPY_PARM;
1251 GEN_CALL(smb_raw_rmdir(tree, &parm[i]));
1253 return True;
1257 generate rename operations
1259 static BOOL handler_rename(int instance)
1261 union smb_rename parm[NSERVERS];
1262 NTSTATUS status[NSERVERS];
1264 parm[0].generic.level = RAW_RENAME_RENAME;
1265 parm[0].rename.in.pattern1 = gen_pattern();
1266 parm[0].rename.in.pattern2 = gen_pattern();
1267 parm[0].rename.in.attrib = gen_attrib();
1269 GEN_COPY_PARM;
1270 GEN_CALL(smb_raw_rename(tree, &parm[i]));
1272 return True;
1276 generate ntrename operations
1278 static BOOL handler_ntrename(int instance)
1280 union smb_rename parm[NSERVERS];
1281 NTSTATUS status[NSERVERS];
1283 parm[0].generic.level = RAW_RENAME_NTRENAME;
1284 parm[0].ntrename.in.old_name = gen_fname();
1285 parm[0].ntrename.in.new_name = gen_fname();
1286 parm[0].ntrename.in.attrib = gen_attrib();
1287 parm[0].ntrename.in.cluster_size = gen_bits_mask2(0, 0xFFFFFFF);
1288 parm[0].ntrename.in.flags = gen_rename_flags();
1290 GEN_COPY_PARM;
1291 GEN_CALL(smb_raw_rename(tree, &parm[i]));
1293 return True;
1298 generate seek operations
1300 static BOOL handler_seek(int instance)
1302 struct smb_seek parm[NSERVERS];
1303 NTSTATUS status[NSERVERS];
1305 parm[0].in.fnum = gen_fnum(instance);
1306 parm[0].in.mode = gen_bits_mask2(0x3, 0xFFFF);
1307 parm[0].in.offset = gen_offset();
1309 GEN_COPY_PARM;
1310 GEN_SET_FNUM(in.fnum);
1311 GEN_CALL(smb_raw_seek(tree, &parm[i]));
1313 CHECK_EQUAL(out.offset);
1315 return True;
1320 generate readx operations
1322 static BOOL handler_readx(int instance)
1324 union smb_read parm[NSERVERS];
1325 NTSTATUS status[NSERVERS];
1327 parm[0].readx.level = RAW_READ_READX;
1328 parm[0].readx.in.fnum = gen_fnum(instance);
1329 parm[0].readx.in.offset = gen_offset();
1330 parm[0].readx.in.mincnt = gen_io_count();
1331 parm[0].readx.in.maxcnt = gen_io_count();
1332 parm[0].readx.in.remaining = gen_io_count();
1333 parm[0].readx.out.data = talloc_size(current_op.mem_ctx,
1334 MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt));
1336 GEN_COPY_PARM;
1337 GEN_SET_FNUM(readx.in.fnum);
1338 GEN_CALL(smb_raw_read(tree, &parm[i]));
1340 CHECK_EQUAL(readx.out.remaining);
1341 CHECK_EQUAL(readx.out.compaction_mode);
1342 CHECK_EQUAL(readx.out.nread);
1344 return True;
1348 generate writex operations
1350 static BOOL handler_writex(int instance)
1352 union smb_write parm[NSERVERS];
1353 NTSTATUS status[NSERVERS];
1355 parm[0].writex.level = RAW_WRITE_WRITEX;
1356 parm[0].writex.in.fnum = gen_fnum(instance);
1357 parm[0].writex.in.offset = gen_offset();
1358 parm[0].writex.in.wmode = gen_bits_mask(0xFFFF);
1359 parm[0].writex.in.remaining = gen_io_count();
1360 parm[0].writex.in.count = gen_io_count();
1361 parm[0].writex.in.data = talloc_zero_size(current_op.mem_ctx, parm[0].writex.in.count);
1363 GEN_COPY_PARM;
1364 GEN_SET_FNUM(writex.in.fnum);
1365 GEN_CALL(smb_raw_write(tree, &parm[i]));
1367 CHECK_EQUAL(writex.out.nwritten);
1368 CHECK_EQUAL(writex.out.remaining);
1370 return True;
1374 generate lockingx operations
1376 static BOOL handler_lockingx(int instance)
1378 union smb_lock parm[NSERVERS];
1379 NTSTATUS status[NSERVERS];
1380 int n, nlocks;
1382 parm[0].lockx.level = RAW_LOCK_LOCKX;
1383 parm[0].lockx.in.fnum = gen_fnum(instance);
1384 parm[0].lockx.in.mode = gen_lock_mode();
1385 parm[0].lockx.in.timeout = gen_timeout();
1386 do {
1387 /* make sure we don't accidentially generate an oplock
1388 break ack - otherwise the server can just block forever */
1389 parm[0].lockx.in.ulock_cnt = gen_lock_count();
1390 parm[0].lockx.in.lock_cnt = gen_lock_count();
1391 nlocks = parm[0].lockx.in.ulock_cnt + parm[0].lockx.in.lock_cnt;
1392 } while (nlocks == 0);
1394 if (nlocks > 0) {
1395 parm[0].lockx.in.locks = talloc_array(current_op.mem_ctx,
1396 struct smb_lock_entry,
1397 nlocks);
1398 for (n=0;n<nlocks;n++) {
1399 parm[0].lockx.in.locks[n].pid = gen_pid();
1400 parm[0].lockx.in.locks[n].offset = gen_offset();
1401 parm[0].lockx.in.locks[n].count = gen_io_count();
1405 GEN_COPY_PARM;
1406 GEN_SET_FNUM(lockx.in.fnum);
1407 GEN_CALL(smb_raw_lock(tree, &parm[i]));
1409 return True;
1413 generate a fileinfo query structure
1415 static void gen_fileinfo(int instance, union smb_fileinfo *info)
1417 int i;
1418 #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v}
1419 struct {
1420 enum smb_fileinfo_level level;
1421 const char *name;
1422 } levels[] = {
1423 LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD),
1424 LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID),
1425 LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO),
1426 LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO),
1427 LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION),
1428 LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION),
1429 LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION),
1430 LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION),
1431 LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION),
1432 LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION)
1434 do {
1435 i = gen_int_range(0, ARRAY_SIZE(levels)-1);
1436 } while (ignore_pattern(levels[i].name));
1438 info->generic.level = levels[i].level;
1442 compare returned fileinfo structures
1444 static BOOL cmp_fileinfo(int instance,
1445 union smb_fileinfo parm[NSERVERS],
1446 NTSTATUS status[NSERVERS])
1448 int i;
1450 switch (parm[0].generic.level) {
1451 case RAW_FILEINFO_GENERIC:
1452 return False;
1454 case RAW_FILEINFO_GETATTR:
1455 CHECK_EQUAL(getattr.out.attrib);
1456 CHECK_EQUAL(getattr.out.size);
1457 CHECK_TIMES_EQUAL(getattr.out.write_time);
1458 break;
1460 case RAW_FILEINFO_GETATTRE:
1461 CHECK_TIMES_EQUAL(getattre.out.create_time);
1462 CHECK_TIMES_EQUAL(getattre.out.access_time);
1463 CHECK_TIMES_EQUAL(getattre.out.write_time);
1464 CHECK_EQUAL(getattre.out.size);
1465 CHECK_EQUAL(getattre.out.alloc_size);
1466 CHECK_EQUAL(getattre.out.attrib);
1467 break;
1469 case RAW_FILEINFO_STANDARD:
1470 CHECK_TIMES_EQUAL(standard.out.create_time);
1471 CHECK_TIMES_EQUAL(standard.out.access_time);
1472 CHECK_TIMES_EQUAL(standard.out.write_time);
1473 CHECK_EQUAL(standard.out.size);
1474 CHECK_EQUAL(standard.out.alloc_size);
1475 CHECK_EQUAL(standard.out.attrib);
1476 break;
1478 case RAW_FILEINFO_EA_SIZE:
1479 CHECK_TIMES_EQUAL(ea_size.out.create_time);
1480 CHECK_TIMES_EQUAL(ea_size.out.access_time);
1481 CHECK_TIMES_EQUAL(ea_size.out.write_time);
1482 CHECK_EQUAL(ea_size.out.size);
1483 CHECK_EQUAL(ea_size.out.alloc_size);
1484 CHECK_EQUAL(ea_size.out.attrib);
1485 CHECK_EQUAL(ea_size.out.ea_size);
1486 break;
1488 case RAW_FILEINFO_ALL_EAS:
1489 CHECK_EQUAL(all_eas.out.num_eas);
1490 for (i=0;i<parm[0].all_eas.out.num_eas;i++) {
1491 CHECK_EQUAL(all_eas.out.eas[i].flags);
1492 CHECK_WSTR_EQUAL(all_eas.out.eas[i].name);
1493 CHECK_BLOB_EQUAL(all_eas.out.eas[i].value);
1495 break;
1497 case RAW_FILEINFO_IS_NAME_VALID:
1498 break;
1500 case RAW_FILEINFO_BASIC_INFO:
1501 case RAW_FILEINFO_BASIC_INFORMATION:
1502 CHECK_NTTIMES_EQUAL(basic_info.out.create_time);
1503 CHECK_NTTIMES_EQUAL(basic_info.out.access_time);
1504 CHECK_NTTIMES_EQUAL(basic_info.out.write_time);
1505 CHECK_NTTIMES_EQUAL(basic_info.out.change_time);
1506 CHECK_EQUAL(basic_info.out.attrib);
1507 break;
1509 case RAW_FILEINFO_STANDARD_INFO:
1510 case RAW_FILEINFO_STANDARD_INFORMATION:
1511 CHECK_EQUAL(standard_info.out.alloc_size);
1512 CHECK_EQUAL(standard_info.out.size);
1513 CHECK_EQUAL(standard_info.out.nlink);
1514 CHECK_EQUAL(standard_info.out.delete_pending);
1515 CHECK_EQUAL(standard_info.out.directory);
1516 break;
1518 case RAW_FILEINFO_EA_INFO:
1519 case RAW_FILEINFO_EA_INFORMATION:
1520 CHECK_EQUAL(ea_info.out.ea_size);
1521 break;
1523 case RAW_FILEINFO_NAME_INFO:
1524 case RAW_FILEINFO_NAME_INFORMATION:
1525 CHECK_WSTR_EQUAL(name_info.out.fname);
1526 break;
1528 case RAW_FILEINFO_ALL_INFO:
1529 case RAW_FILEINFO_ALL_INFORMATION:
1530 CHECK_NTTIMES_EQUAL(all_info.out.create_time);
1531 CHECK_NTTIMES_EQUAL(all_info.out.access_time);
1532 CHECK_NTTIMES_EQUAL(all_info.out.write_time);
1533 CHECK_NTTIMES_EQUAL(all_info.out.change_time);
1534 CHECK_EQUAL(all_info.out.attrib);
1535 CHECK_EQUAL(all_info.out.alloc_size);
1536 CHECK_EQUAL(all_info.out.size);
1537 CHECK_EQUAL(all_info.out.nlink);
1538 CHECK_EQUAL(all_info.out.delete_pending);
1539 CHECK_EQUAL(all_info.out.directory);
1540 CHECK_EQUAL(all_info.out.ea_size);
1541 CHECK_WSTR_EQUAL(all_info.out.fname);
1542 break;
1544 case RAW_FILEINFO_ALT_NAME_INFO:
1545 case RAW_FILEINFO_ALT_NAME_INFORMATION:
1546 CHECK_WSTR_EQUAL(alt_name_info.out.fname);
1547 break;
1549 case RAW_FILEINFO_STREAM_INFO:
1550 case RAW_FILEINFO_STREAM_INFORMATION:
1551 CHECK_EQUAL(stream_info.out.num_streams);
1552 for (i=0;i<parm[0].stream_info.out.num_streams;i++) {
1553 CHECK_EQUAL(stream_info.out.streams[i].size);
1554 CHECK_EQUAL(stream_info.out.streams[i].alloc_size);
1555 CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name);
1557 break;
1559 case RAW_FILEINFO_COMPRESSION_INFO:
1560 case RAW_FILEINFO_COMPRESSION_INFORMATION:
1561 CHECK_EQUAL(compression_info.out.compressed_size);
1562 CHECK_EQUAL(compression_info.out.format);
1563 CHECK_EQUAL(compression_info.out.unit_shift);
1564 CHECK_EQUAL(compression_info.out.chunk_shift);
1565 CHECK_EQUAL(compression_info.out.cluster_shift);
1566 break;
1568 case RAW_FILEINFO_INTERNAL_INFORMATION:
1569 CHECK_EQUAL(internal_information.out.file_id);
1570 break;
1572 case RAW_FILEINFO_ACCESS_INFORMATION:
1573 CHECK_EQUAL(access_information.out.access_flags);
1574 break;
1576 case RAW_FILEINFO_POSITION_INFORMATION:
1577 CHECK_EQUAL(position_information.out.position);
1578 break;
1580 case RAW_FILEINFO_MODE_INFORMATION:
1581 CHECK_EQUAL(mode_information.out.mode);
1582 break;
1584 case RAW_FILEINFO_ALIGNMENT_INFORMATION:
1585 CHECK_EQUAL(alignment_information.out.alignment_requirement);
1586 break;
1588 case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
1589 CHECK_NTTIMES_EQUAL(network_open_information.out.create_time);
1590 CHECK_NTTIMES_EQUAL(network_open_information.out.access_time);
1591 CHECK_NTTIMES_EQUAL(network_open_information.out.write_time);
1592 CHECK_NTTIMES_EQUAL(network_open_information.out.change_time);
1593 CHECK_EQUAL(network_open_information.out.alloc_size);
1594 CHECK_EQUAL(network_open_information.out.size);
1595 CHECK_EQUAL(network_open_information.out.attrib);
1596 break;
1598 case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
1599 CHECK_EQUAL(attribute_tag_information.out.attrib);
1600 CHECK_EQUAL(attribute_tag_information.out.reparse_tag);
1601 break;
1604 return True;
1608 generate qpathinfo operations
1610 static BOOL handler_qpathinfo(int instance)
1612 union smb_fileinfo parm[NSERVERS];
1613 NTSTATUS status[NSERVERS];
1615 parm[0].generic.in.fname = gen_fname_open(instance);
1617 gen_fileinfo(instance, &parm[0]);
1619 GEN_COPY_PARM;
1620 GEN_CALL(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i]));
1622 return cmp_fileinfo(instance, parm, status);
1626 generate qfileinfo operations
1628 static BOOL handler_qfileinfo(int instance)
1630 union smb_fileinfo parm[NSERVERS];
1631 NTSTATUS status[NSERVERS];
1633 parm[0].generic.in.fnum = gen_fnum(instance);
1635 gen_fileinfo(instance, &parm[0]);
1637 GEN_COPY_PARM;
1638 GEN_SET_FNUM(generic.in.fnum);
1639 GEN_CALL(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i]));
1641 return cmp_fileinfo(instance, parm, status);
1646 generate a fileinfo query structure
1648 static void gen_setfileinfo(int instance, union smb_setfileinfo *info)
1650 int i;
1651 #undef LVL
1652 #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v}
1653 struct {
1654 enum smb_setfileinfo_level level;
1655 const char *name;
1656 } levels[] = {
1657 #if 0
1658 /* disabled until win2003 can handle them ... */
1659 LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO),
1660 LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO),
1661 #endif
1662 LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION),
1663 LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION),
1664 LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION),
1665 LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION),
1666 LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040)
1668 do {
1669 i = gen_int_range(0, ARRAY_SIZE(levels)-1);
1670 } while (ignore_pattern(levels[i].name));
1672 info->generic.level = levels[i].level;
1674 switch (info->generic.level) {
1675 case RAW_SFILEINFO_SETATTR:
1676 info->setattr.in.attrib = gen_attrib();
1677 info->setattr.in.write_time = gen_timet();
1678 break;
1679 case RAW_SFILEINFO_SETATTRE:
1680 info->setattre.in.create_time = gen_timet();
1681 info->setattre.in.access_time = gen_timet();
1682 info->setattre.in.write_time = gen_timet();
1683 break;
1684 case RAW_SFILEINFO_STANDARD:
1685 info->standard.in.create_time = gen_timet();
1686 info->standard.in.access_time = gen_timet();
1687 info->standard.in.write_time = gen_timet();
1688 break;
1689 case RAW_SFILEINFO_EA_SET: {
1690 static struct ea_struct ea;
1691 info->ea_set.in.num_eas = 1;
1692 info->ea_set.in.eas = &ea;
1693 info->ea_set.in.eas[0] = gen_ea_struct();
1695 break;
1696 case RAW_SFILEINFO_BASIC_INFO:
1697 case RAW_SFILEINFO_BASIC_INFORMATION:
1698 info->basic_info.in.create_time = gen_nttime();
1699 info->basic_info.in.access_time = gen_nttime();
1700 info->basic_info.in.write_time = gen_nttime();
1701 info->basic_info.in.change_time = gen_nttime();
1702 info->basic_info.in.attrib = gen_attrib();
1703 break;
1704 case RAW_SFILEINFO_DISPOSITION_INFO:
1705 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
1706 info->disposition_info.in.delete_on_close = gen_bool();
1707 break;
1708 case RAW_SFILEINFO_ALLOCATION_INFO:
1709 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
1710 info->allocation_info.in.alloc_size = gen_alloc_size();
1711 break;
1712 case RAW_SFILEINFO_END_OF_FILE_INFO:
1713 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
1714 info->end_of_file_info.in.size = gen_offset();
1715 break;
1716 case RAW_SFILEINFO_RENAME_INFORMATION:
1717 info->rename_information.in.overwrite = gen_bool();
1718 info->rename_information.in.root_fid = gen_root_fid(instance);
1719 info->rename_information.in.new_name = gen_fname_open(instance);
1720 break;
1721 case RAW_SFILEINFO_POSITION_INFORMATION:
1722 info->position_information.in.position = gen_offset();
1723 break;
1724 case RAW_SFILEINFO_MODE_INFORMATION:
1725 info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF);
1726 break;
1731 generate setpathinfo operations
1733 static BOOL handler_spathinfo(int instance)
1735 union smb_setfileinfo parm[NSERVERS];
1736 NTSTATUS status[NSERVERS];
1738 parm[0].generic.file.fname = gen_fname_open(instance);
1740 gen_setfileinfo(instance, &parm[0]);
1742 GEN_COPY_PARM;
1744 /* a special case for the fid in a RENAME */
1745 if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION &&
1746 parm[0].rename_information.in.root_fid != 0) {
1747 GEN_SET_FNUM(rename_information.in.root_fid);
1750 GEN_CALL(smb_raw_setpathinfo(tree, &parm[i]));
1752 return True;
1757 generate setfileinfo operations
1759 static BOOL handler_sfileinfo(int instance)
1761 union smb_setfileinfo parm[NSERVERS];
1762 NTSTATUS status[NSERVERS];
1764 parm[0].generic.file.fnum = gen_fnum(instance);
1766 gen_setfileinfo(instance, &parm[0]);
1768 GEN_COPY_PARM;
1769 GEN_SET_FNUM(generic.file.fnum);
1770 GEN_CALL(smb_raw_setfileinfo(tree, &parm[i]));
1772 return True;
1777 generate change notify operations
1779 static BOOL handler_notify(int instance)
1781 struct smb_notify parm[NSERVERS];
1782 int n;
1784 parm[0].in.buffer_size = gen_io_count();
1785 parm[0].in.completion_filter = gen_bits_mask(0xFF);
1786 parm[0].in.fnum = gen_fnum(instance);
1787 parm[0].in.recursive = gen_bool();
1789 GEN_COPY_PARM;
1790 GEN_SET_FNUM(in.fnum);
1792 for (n=0;n<NSERVERS;n++) {
1793 struct smbcli_request *req;
1794 req = smb_raw_changenotify_send(servers[n].cli[instance]->tree, &parm[n]);
1795 req->async.fn = async_notify;
1798 return True;
1802 wipe any relevant files
1804 static void wipe_files(void)
1806 int i;
1807 for (i=0;i<NSERVERS;i++) {
1808 int n = smbcli_deltree(servers[i].cli[0]->tree, "\\gentest");
1809 if (n == -1) {
1810 printf("Failed to wipe tree on server %d\n", i);
1811 exit(1);
1813 if (NT_STATUS_IS_ERR(smbcli_mkdir(servers[i].cli[0]->tree, "\\gentest"))) {
1814 printf("Failed to create \\gentest - %s\n",
1815 smbcli_errstr(servers[i].cli[0]->tree));
1816 exit(1);
1818 if (n > 0) {
1819 printf("Deleted %d files on server %d\n", n, i);
1825 dump the current seeds - useful for continuing a backtrack
1827 static void dump_seeds(void)
1829 int i;
1830 FILE *f;
1832 if (!options.seeds_file) {
1833 return;
1835 f = fopen("seeds.tmp", "w");
1836 if (!f) return;
1838 for (i=0;i<options.numops;i++) {
1839 fprintf(f, "%u\n", op_parms[i].seed);
1841 fclose(f);
1842 rename("seeds.tmp", options.seeds_file);
1848 the list of top-level operations that we will generate
1850 static struct {
1851 const char *name;
1852 BOOL (*handler)(int instance);
1853 int count, success_count;
1854 } gen_ops[] = {
1855 {"OPEN", handler_open},
1856 {"OPENX", handler_openx},
1857 {"NTCREATEX", handler_ntcreatex},
1858 {"CLOSE", handler_close},
1859 {"UNLINK", handler_unlink},
1860 {"MKDIR", handler_mkdir},
1861 {"RMDIR", handler_rmdir},
1862 {"RENAME", handler_rename},
1863 {"NTRENAME", handler_ntrename},
1864 {"READX", handler_readx},
1865 {"WRITEX", handler_writex},
1866 {"CHKPATH", handler_chkpath},
1867 {"LOCKINGX", handler_lockingx},
1868 {"QPATHINFO", handler_qpathinfo},
1869 {"QFILEINFO", handler_qfileinfo},
1870 {"SPATHINFO", handler_spathinfo},
1871 {"SFILEINFO", handler_sfileinfo},
1872 {"NOTIFY", handler_notify},
1873 {"SEEK", handler_seek},
1878 run the test with the current set of op_parms parameters
1879 return the number of operations that completed successfully
1881 static int run_test(void)
1883 int op, i;
1885 if (!connect_servers()) {
1886 printf("Failed to connect to servers\n");
1887 exit(1);
1890 dump_seeds();
1892 /* wipe any leftover files from old runs */
1893 wipe_files();
1895 /* reset the open handles array */
1896 memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0]));
1897 num_open_handles = 0;
1899 for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
1900 gen_ops[i].count = 0;
1901 gen_ops[i].success_count = 0;
1904 for (op=0; op<options.numops; op++) {
1905 int instance, which_op;
1906 BOOL ret;
1908 if (op_parms[op].disabled) continue;
1910 srandom(op_parms[op].seed);
1912 instance = gen_int_range(0, NINSTANCES-1);
1914 /* generate a non-ignored operation */
1915 do {
1916 which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1);
1917 } while (ignore_pattern(gen_ops[which_op].name));
1919 DEBUG(3,("Generating op %s on instance %d\n",
1920 gen_ops[which_op].name, instance));
1922 current_op.seed = op_parms[op].seed;
1923 current_op.opnum = op;
1924 current_op.name = gen_ops[which_op].name;
1925 current_op.status = NT_STATUS_OK;
1926 current_op.mem_ctx = talloc_init("%s", current_op.name);
1928 ret = gen_ops[which_op].handler(instance);
1930 talloc_free(current_op.mem_ctx);
1932 gen_ops[which_op].count++;
1933 if (NT_STATUS_IS_OK(current_op.status)) {
1934 gen_ops[which_op].success_count++;
1937 if (!ret) {
1938 printf("Failed at operation %d - %s\n",
1939 op, gen_ops[which_op].name);
1940 return op;
1943 if (op % 100 == 0) {
1944 printf("%d\n", op);
1948 for (i=0;i<ARRAY_SIZE(gen_ops);i++) {
1949 printf("Op %-10s got %d/%d success\n",
1950 gen_ops[i].name,
1951 gen_ops[i].success_count,
1952 gen_ops[i].count);
1955 return op;
1959 perform a backtracking analysis of the minimal set of operations
1960 to generate an error
1962 static void backtrack_analyze(void)
1964 int chunk, ret;
1966 chunk = options.numops / 2;
1968 do {
1969 int base;
1970 for (base=0;
1971 chunk > 0 && base+chunk < options.numops && options.numops > 1; ) {
1972 int i, max;
1974 chunk = MIN(chunk, options.numops / 2);
1976 /* mark this range as disabled */
1977 max = MIN(options.numops, base+chunk);
1978 for (i=base;i<max; i++) {
1979 op_parms[i].disabled = True;
1981 printf("Testing %d ops with %d-%d disabled\n",
1982 options.numops, base, max-1);
1983 ret = run_test();
1984 printf("Completed %d of %d ops\n", ret, options.numops);
1985 for (i=base;i<max; i++) {
1986 op_parms[i].disabled = False;
1988 if (ret == options.numops) {
1989 /* this chunk is needed */
1990 base += chunk;
1991 } else if (ret < base) {
1992 printf("damn - inconsistent errors! found early error\n");
1993 options.numops = ret+1;
1994 base = 0;
1995 } else {
1996 /* it failed - this chunk isn't needed for a failure */
1997 memmove(&op_parms[base], &op_parms[max],
1998 sizeof(op_parms[0]) * (options.numops - max));
1999 options.numops = (ret+1) - (max - base);
2003 if (chunk == 2) {
2004 chunk = 1;
2005 } else {
2006 chunk *= 0.4;
2009 if (options.analyze_continuous && chunk == 0 && options.numops != 1) {
2010 chunk = 1;
2012 } while (chunk > 0);
2014 printf("Reduced to %d ops\n", options.numops);
2015 ret = run_test();
2016 if (ret != options.numops - 1) {
2017 printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops);
2022 start the main gentest process
2024 static BOOL start_gentest(void)
2026 int op;
2027 int ret;
2029 /* allocate the open_handles array */
2030 open_handles = calloc(options.max_open_handles, sizeof(open_handles[0]));
2032 srandom(options.seed);
2033 op_parms = calloc(options.numops, sizeof(op_parms[0]));
2035 /* generate the seeds - after this everything is deterministic */
2036 if (options.use_preset_seeds) {
2037 int numops;
2038 char **preset = file_lines_load(options.seeds_file, &numops);
2039 if (!preset) {
2040 printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno));
2041 exit(1);
2043 if (numops < options.numops) {
2044 options.numops = numops;
2046 for (op=0;op<options.numops;op++) {
2047 if (!preset[op]) {
2048 printf("Not enough seeds in %s\n", options.seeds_file);
2049 exit(1);
2051 op_parms[op].seed = atoi(preset[op]);
2053 printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file);
2054 } else {
2055 for (op=0; op<options.numops; op++) {
2056 op_parms[op].seed = random();
2060 ret = run_test();
2062 if (ret != options.numops && options.analyze) {
2063 options.numops = ret+1;
2064 backtrack_analyze();
2065 } else if (options.analyze_always) {
2066 backtrack_analyze();
2067 } else if (options.analyze_continuous) {
2068 while (run_test() == options.numops) ;
2071 return ret == options.numops;
2075 static void usage(void)
2077 printf(
2078 "Usage:\n\
2079 gentest2 //server1/share1 //server2/share2 [options..]\n\
2080 options:\n\
2081 -U user%%pass (can be specified twice)\n\
2082 -s seed\n\
2083 -o numops\n\
2084 -a (show all ops)\n\
2085 -A backtrack to find minimal ops\n\
2086 -i FILE add a list of wildcard exclusions\n\
2087 -O enable oplocks\n\
2088 -S FILE set preset seeds file\n\
2089 -L use preset seeds\n\
2090 -F fast reconnect (just close files)\n\
2091 -C continuous analysis mode\n\
2092 -X analyse even when test OK\n\
2096 /****************************************************************************
2097 main program
2098 ****************************************************************************/
2099 int main(int argc, char *argv[])
2101 int opt;
2102 int i;
2103 BOOL ret;
2105 setlinebuf(stdout);
2107 setup_logging("gentest", DEBUG_STDOUT);
2109 if (argc < 3 || argv[1][0] == '-') {
2110 usage();
2111 exit(1);
2114 setup_logging(argv[0], DEBUG_STDOUT);
2116 for (i=0;i<NSERVERS;i++) {
2117 const char *share = argv[1+i];
2118 if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) {
2119 printf("Invalid share name '%s'\n", share);
2120 return -1;
2124 argc -= NSERVERS;
2125 argv += NSERVERS;
2127 lp_load(dyn_CONFIGFILE,True,False,False);
2128 load_interfaces();
2130 options.seed = time(NULL);
2131 options.numops = 1000;
2132 options.max_open_handles = 20;
2133 options.seeds_file = "gentest_seeds.dat";
2135 while ((opt = getopt(argc, argv, "U:s:o:ad:i:AOhS:LFXC")) != EOF) {
2136 switch (opt) {
2137 case 'U':
2138 i = servers[0].credentials.username?1:0;
2139 cli_credentials_parse_string(&servers[0].credentials, optarg, CRED_SPECIFIED);
2140 break;
2141 case 'd':
2142 DEBUGLEVEL = atoi(optarg);
2143 setup_logging(NULL, DEBUG_STDOUT);
2144 break;
2145 case 's':
2146 options.seed = atoi(optarg);
2147 break;
2148 case 'S':
2149 options.seeds_file = optarg;
2150 break;
2151 case 'L':
2152 options.use_preset_seeds = True;
2153 break;
2154 case 'F':
2155 options.fast_reconnect = True;
2156 break;
2157 case 'o':
2158 options.numops = atoi(optarg);
2159 break;
2160 case 'O':
2161 options.use_oplocks = True;
2162 break;
2163 case 'a':
2164 options.showall = True;
2165 break;
2166 case 'A':
2167 options.analyze = True;
2168 break;
2169 case 'X':
2170 options.analyze_always = True;
2171 break;
2172 case 'C':
2173 options.analyze_continuous = True;
2174 break;
2175 case 'i':
2176 options.ignore_patterns = file_lines_load(optarg, NULL);
2177 break;
2178 case 'h':
2179 usage();
2180 exit(1);
2181 default:
2182 printf("Unknown option %c (%d)\n", (char)opt, opt);
2183 exit(1);
2187 gentest_init_subsystems;
2189 if (!servers[0].credentials.username) {
2190 usage();
2191 return -1;
2193 if (!servers[1].credentials.username) {
2194 servers[1].credentials.username = servers[0].credentials.username;
2195 servers[1].credentials.password = servers[0].credentials.password;
2198 printf("seed=%u\n", options.seed);
2200 ret = start_gentest();
2202 if (ret) {
2203 printf("gentest completed - no errors\n");
2204 } else {
2205 printf("gentest failed\n");
2208 return ret?0:-1;