4554f52e4113000366c1e08cf2a09aab251eb8e9
[sfrench/samba-autobuild/.git] / source4 / torture / locktest.c
1 /* 
2    Unix SMB/CIFS implementation.
3    randomised byte range lock tester
4    Copyright (C) Andrew Tridgell 1999
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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "system/time.h"
24 #include "pstring.h"
25 #include "auth/gensec/gensec.h"
26 #include "libcli/libcli.h"
27
28 static int numops = 1000;
29 static BOOL showall;
30 static BOOL analyze;
31 static BOOL hide_unlock_fails;
32 static BOOL use_oplocks;
33 static uint_t lock_range = 100;
34 static uint_t lock_base = 0;
35 static uint_t min_length = 0;
36 static BOOL exact_error_codes;
37 static BOOL zero_zero;
38
39 #define FILENAME "\\locktest.dat"
40
41 #define READ_PCT 50
42 #define LOCK_PCT 45
43 #define UNLOCK_PCT 70
44 #define RANGE_MULTIPLE 1
45 #define NSERVERS 2
46 #define NCONNECTIONS 2
47 #define NFILES 2
48 #define LOCK_TIMEOUT 0
49
50 static struct cli_credentials *servers[NSERVERS];
51
52 enum lock_op {OP_LOCK, OP_UNLOCK, OP_REOPEN};
53
54 struct record {
55         enum lock_op lock_op;
56         enum brl_type lock_type;
57         char conn, f;
58         uint64_t start, len;
59         char needed;
60 };
61
62 #define PRESETS 0
63
64 #if PRESETS
65 static struct record preset[] = {
66 {OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
67 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
68 {OP_LOCK, WRITE_LOCK, 0, 0, 3, 0, 1},
69 {OP_UNLOCK, 0       , 0, 0, 2, 0, 1},
70 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
71
72 {OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
73 {OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
74 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
75 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
76
77 {OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
78 {OP_LOCK, WRITE_LOCK, 0, 0, 3, 1, 1},
79 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
80 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
81
82 {OP_LOCK, READ_LOCK, 0, 0, 2, 0, 1},
83 {OP_LOCK, WRITE_LOCK, 0, 0, 1, 1, 1},
84 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
85 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
86
87 {OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
88 {OP_LOCK, READ_LOCK, 0, 0, 1, 1, 1},
89 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
90 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
91
92 {OP_LOCK, WRITE_LOCK, 0, 0, 2, 0, 1},
93 {OP_LOCK, READ_LOCK, 0, 0, 3, 1, 1},
94 {OP_LOCK, WRITE_LOCK, 0, 0, 0, 0, 1},
95 {OP_REOPEN, 0, 0, 0, 0, 0, 1},
96
97 };
98 #endif
99
100 static struct record *recorded;
101
102 /***************************************************** 
103 return a connection to a server
104 *******************************************************/
105 static struct smbcli_state *connect_one(char *share, int snum, int conn)
106 {
107         struct smbcli_state *c;
108         fstring server, myname;
109         NTSTATUS status;
110         int retries = 10;
111
112         printf("connect_one(%s, %d, %d)\n", share, snum, conn);
113
114         fstrcpy(server,share+2);
115         share = strchr_m(server,'\\');
116         if (!share) return NULL;
117         *share = 0;
118         share++;
119
120         if (snum == 0) {
121                 char **unc_list = NULL;
122                 int num_unc_names;
123                 const char *p;
124                 p = lp_parm_string(-1, "torture", "unclist");
125                 if (p) {
126                         char *h, *s;
127                         unc_list = file_lines_load(p, &num_unc_names, NULL);
128                         if (!unc_list || num_unc_names <= 0) {
129                                 printf("Failed to load unc names list from '%s'\n", p);
130                                 exit(1);
131                         }
132
133                         if (!smbcli_parse_unc(unc_list[conn % num_unc_names],
134                                               NULL, &h, &s)) {
135                                 printf("Failed to parse UNC name %s\n",
136                                        unc_list[conn % num_unc_names]);
137                                 exit(1);
138                         }
139                         fstrcpy(server, h);
140                         fstrcpy(share, s);
141                 }
142         }
143
144
145         slprintf(myname,sizeof(myname), "lock-%u-%u", getpid(), snum);
146         cli_credentials_set_workstation(servers[snum], myname, CRED_SPECIFIED);
147
148         do {
149                 printf("\\\\%s\\%s\n", server, share);
150                 status = smbcli_full_connection(NULL, &c, 
151                                                 server, 
152                                                 share, NULL,
153                                                 servers[snum], NULL);
154                 if (!NT_STATUS_IS_OK(status)) {
155                         sleep(2);
156                 }
157         } while (!NT_STATUS_IS_OK(status) && retries--);
158
159         if (!NT_STATUS_IS_OK(status)) {
160                 return NULL;
161         }
162
163         return c;
164 }
165
166
167 static void reconnect(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], int fnum[NSERVERS][NCONNECTIONS][NFILES],
168                       char *share[NSERVERS])
169 {
170         int server, conn, f;
171
172         for (server=0;server<NSERVERS;server++)
173         for (conn=0;conn<NCONNECTIONS;conn++) {
174                 if (cli[server][conn]) {
175                         for (f=0;f<NFILES;f++) {
176                                 if (fnum[server][conn][f] != -1) {
177                                         smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]);
178                                         fnum[server][conn][f] = -1;
179                                 }
180                         }
181                         talloc_free(cli[server][conn]);
182                 }
183                 cli[server][conn] = connect_one(share[server], server, conn);
184                 if (!cli[server][conn]) {
185                         DEBUG(0,("Failed to connect to %s\n", share[server]));
186                         exit(1);
187                 }
188         }
189 }
190
191
192
193 static BOOL test_one(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], 
194                      int fnum[NSERVERS][NCONNECTIONS][NFILES],
195                      struct record *rec)
196 {
197         uint_t conn = rec->conn;
198         uint_t f = rec->f;
199         uint64_t start = rec->start;
200         uint64_t len = rec->len;
201         enum brl_type op = rec->lock_type;
202         int server;
203         BOOL ret[NSERVERS];
204         NTSTATUS status[NSERVERS];
205
206         switch (rec->lock_op) {
207         case OP_LOCK:
208                 /* set a lock */
209                 for (server=0;server<NSERVERS;server++) {
210                         ret[server] = NT_STATUS_IS_OK(smbcli_lock64(cli[server][conn]->tree, 
211                                                  fnum[server][conn][f],
212                                                  start, len, LOCK_TIMEOUT, op));
213                         status[server] = smbcli_nt_error(cli[server][conn]->tree);
214                         if (!exact_error_codes && 
215                             NT_STATUS_EQUAL(status[server], 
216                                             NT_STATUS_FILE_LOCK_CONFLICT)) {
217                                 status[server] = NT_STATUS_LOCK_NOT_GRANTED;
218                         }
219                 }
220                 if (showall || !NT_STATUS_EQUAL(status[0],status[1])) {
221                         printf("lock   conn=%u f=%u range=%.0f(%.0f) op=%s -> %s:%s\n",
222                                conn, f, 
223                                (double)start, (double)len,
224                                op==READ_LOCK?"READ_LOCK":"WRITE_LOCK",
225                                nt_errstr(status[0]), nt_errstr(status[1]));
226                 }
227                 if (!NT_STATUS_EQUAL(status[0],status[1])) return False;
228                 break;
229                 
230         case OP_UNLOCK:
231                 /* unset a lock */
232                 for (server=0;server<NSERVERS;server++) {
233                         ret[server] = NT_STATUS_IS_OK(smbcli_unlock64(cli[server][conn]->tree, 
234                                                    fnum[server][conn][f],
235                                                    start, len));
236                         status[server] = smbcli_nt_error(cli[server][conn]->tree);
237                 }
238                 if (showall || 
239                     (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1]))) {
240                         printf("unlock conn=%u f=%u range=%.0f(%.0f)       -> %s:%s\n",
241                                conn, f, 
242                                (double)start, (double)len,
243                                nt_errstr(status[0]), nt_errstr(status[1]));
244                 }
245                 if (!hide_unlock_fails && !NT_STATUS_EQUAL(status[0],status[1])) 
246                         return False;
247                 break;
248
249         case OP_REOPEN:
250                 /* reopen the file */
251                 for (server=0;server<NSERVERS;server++) {
252                         smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]);
253                         fnum[server][conn][f] = -1;
254                 }
255                 for (server=0;server<NSERVERS;server++) {
256                         fnum[server][conn][f] = smbcli_open(cli[server][conn]->tree, FILENAME,
257                                                          O_RDWR|O_CREAT,
258                                                          DENY_NONE);
259                         if (fnum[server][conn][f] == -1) {
260                                 printf("failed to reopen on share%d\n", server);
261                                 return False;
262                         }
263                 }
264                 if (showall) {
265                         printf("reopen conn=%u f=%u\n",
266                                conn, f);
267                 }
268                 break;
269         }
270
271         return True;
272 }
273
274 static void close_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], 
275                         int fnum[NSERVERS][NCONNECTIONS][NFILES])
276 {
277         int server, conn, f; 
278
279         for (server=0;server<NSERVERS;server++)
280         for (conn=0;conn<NCONNECTIONS;conn++)
281         for (f=0;f<NFILES;f++) {
282                 if (fnum[server][conn][f] != -1) {
283                         smbcli_close(cli[server][conn]->tree, fnum[server][conn][f]);
284                         fnum[server][conn][f] = -1;
285                 }
286         }
287         for (server=0;server<NSERVERS;server++) {
288                 smbcli_unlink(cli[server][0]->tree, FILENAME);
289         }
290 }
291
292 static void open_files(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], 
293                        int fnum[NSERVERS][NCONNECTIONS][NFILES])
294 {
295         int server, conn, f; 
296
297         for (server=0;server<NSERVERS;server++)
298         for (conn=0;conn<NCONNECTIONS;conn++)
299         for (f=0;f<NFILES;f++) {
300                 fnum[server][conn][f] = smbcli_open(cli[server][conn]->tree, FILENAME,
301                                                  O_RDWR|O_CREAT,
302                                                  DENY_NONE);
303                 if (fnum[server][conn][f] == -1) {
304                         fprintf(stderr,"Failed to open fnum[%u][%u][%u]\n",
305                                 server, conn, f);
306                         exit(1);
307                 }
308         }
309 }
310
311
312 static int retest(struct smbcli_state *cli[NSERVERS][NCONNECTIONS], 
313                    int fnum[NSERVERS][NCONNECTIONS][NFILES],
314                    int n)
315 {
316         int i;
317         printf("testing %u ...\n", n);
318         for (i=0; i<n; i++) {
319                 if (i && i % 100 == 0) {
320                         printf("%u\n", i);
321                 }
322
323                 if (recorded[i].needed &&
324                     !test_one(cli, fnum, &recorded[i])) return i;
325         }
326         return n;
327 }
328
329
330 /* each server has two connections open to it. Each connection has two file
331    descriptors open on the file - 8 file descriptors in total 
332
333    we then do random locking ops in tamdem on the 4 fnums from each
334    server and ensure that the results match
335  */
336 static void test_locks(char *share[NSERVERS])
337 {
338         struct smbcli_state *cli[NSERVERS][NCONNECTIONS];
339         int fnum[NSERVERS][NCONNECTIONS][NFILES];
340         int n, i, n1, skip, r1, r2; 
341
342         ZERO_STRUCT(fnum);
343         ZERO_STRUCT(cli);
344
345         recorded = malloc_array_p(struct record, numops);
346
347         for (n=0; n<numops; n++) {
348 #if PRESETS
349                 if (n < sizeof(preset) / sizeof(preset[0])) {
350                         recorded[n] = preset[n];
351                 } else {
352 #endif
353                         recorded[n].conn = random() % NCONNECTIONS;
354                         recorded[n].f = random() % NFILES;
355                         recorded[n].start = lock_base + ((uint_t)random() % (lock_range-1));
356                         recorded[n].len =  min_length +
357                                 random() % (lock_range-(recorded[n].start-lock_base));
358                         recorded[n].start *= RANGE_MULTIPLE;
359                         recorded[n].len *= RANGE_MULTIPLE;
360                         r1 = random() % 100;
361                         r2 = random() % 100;
362                         if (r1 < READ_PCT) {
363                                 recorded[n].lock_type = READ_LOCK;
364                         } else {
365                                 recorded[n].lock_type = WRITE_LOCK;
366                         }
367                         if (r2 < LOCK_PCT) {
368                                 recorded[n].lock_op = OP_LOCK;
369                         } else if (r2 < UNLOCK_PCT) {
370                                 recorded[n].lock_op = OP_UNLOCK;
371                         } else {
372                                 recorded[n].lock_op = OP_REOPEN;
373                         }
374                         recorded[n].needed = True;
375                         if (!zero_zero && recorded[n].start==0 && recorded[n].len==0) {
376                                 recorded[n].len = 1;
377                         }
378 #if PRESETS
379                 }
380 #endif
381         }
382
383         reconnect(cli, fnum, share);
384         open_files(cli, fnum);
385         n = retest(cli, fnum, numops);
386
387         if (n == numops || !analyze) return;
388         n++;
389
390         skip = n/2;
391
392         while (1) {
393                 n1 = n;
394
395                 close_files(cli, fnum);
396                 reconnect(cli, fnum, share);
397                 open_files(cli, fnum);
398
399                 for (i=0;i<n-skip;i+=skip) {
400                         int m, j;
401                         printf("excluding %d-%d\n", i, i+skip-1);
402                         for (j=i;j<i+skip;j++) {
403                                 recorded[j].needed = False;
404                         }
405
406                         close_files(cli, fnum);
407                         open_files(cli, fnum);
408
409                         m = retest(cli, fnum, n);
410                         if (m == n) {
411                                 for (j=i;j<i+skip;j++) {
412                                         recorded[j].needed = True;
413                                 }
414                         } else {
415                                 if (i+(skip-1) < m) {
416                                         memmove(&recorded[i], &recorded[i+skip],
417                                                 (m-(i+skip-1))*sizeof(recorded[0]));
418                                 }
419                                 n = m-(skip-1);
420                                 i--;
421                         }
422                 }
423
424                 if (skip > 1) {
425                         skip = skip/2;
426                         printf("skip=%d\n", skip);
427                         continue;
428                 }
429
430                 if (n1 == n) break;
431         }
432
433         close_files(cli, fnum);
434         reconnect(cli, fnum, share);
435         open_files(cli, fnum);
436         showall = True;
437         n1 = retest(cli, fnum, n);
438         if (n1 != n-1) {
439                 printf("ERROR - inconsistent result (%u %u)\n", n1, n);
440         }
441         close_files(cli, fnum);
442
443         for (i=0;i<n;i++) {
444                 printf("{%d, %d, %u, %u, %.0f, %.0f, %u},\n",
445                        recorded[i].lock_op,
446                        recorded[i].lock_type,
447                        recorded[i].conn,
448                        recorded[i].f,
449                        (double)recorded[i].start,
450                        (double)recorded[i].len,
451                        recorded[i].needed);
452         }       
453 }
454
455
456
457 static void usage(void)
458 {
459         printf(
460 "Usage:\n\
461   locktest //server1/share1 //server2/share2 [options..]\n\
462   options:\n\
463         -U user%%pass        (may be specified twice)\n\
464         -s seed\n\
465         -o numops\n\
466         -u          hide unlock fails\n\
467         -a          (show all ops)\n\
468         -A          analyse for minimal ops\n\
469         -O          use oplocks\n\
470         -E          enable exact error code checking\n\
471         -Z          enable the zero/zero lock\n\
472         -R range    set lock range\n\
473         -B base     set lock base\n\
474         -M min      set min lock length\n\
475         -l filename unclist file\n\
476 ");
477 }
478
479 /****************************************************************************
480   main program
481 ****************************************************************************/
482  int main(int argc,char *argv[])
483 {
484         char *share[NSERVERS];
485         int opt;
486         int seed, server;
487         int username_count=0;
488
489         setlinebuf(stdout);
490
491         setup_logging("locktest", DEBUG_STDOUT);
492
493         if (argc < 3 || argv[1][0] == '-') {
494                 usage();
495                 exit(1);
496         }
497
498         setup_logging(argv[0], DEBUG_STDOUT);
499
500         for (server=0;server<NSERVERS;server++) {
501                 share[server] = argv[1+server];
502                 all_string_sub(share[server],"/","\\",0);
503         }
504
505         argc -= NSERVERS;
506         argv += NSERVERS;
507
508         lp_load();
509
510         servers[0] = cli_credentials_init(talloc_autofree_context());
511         servers[1] = cli_credentials_init(talloc_autofree_context());
512         cli_credentials_guess(servers[0]);
513         cli_credentials_guess(servers[1]);
514
515         seed = time(NULL);
516
517         while ((opt = getopt(argc, argv, "U:s:ho:aAW:OR:B:M:EZW:l:")) != EOF) {
518                 switch (opt) {
519                 case 'U':
520                         if (username_count == 2) {
521                                 usage();
522                                 exit(1);
523                         }
524                         cli_credentials_parse_string(servers[username_count], 
525                                                      optarg, CRED_SPECIFIED);
526                         username_count++;
527                         break;
528                 case 'R':
529                         lock_range = strtol(optarg, NULL, 0);
530                         break;
531                 case 'B':
532                         lock_base = strtol(optarg, NULL, 0);
533                         break;
534                 case 'M':
535                         min_length = strtol(optarg, NULL, 0);
536                         break;
537                 case 's':
538                         seed = atoi(optarg);
539                         break;
540                 case 'u':
541                         hide_unlock_fails = True;
542                         break;
543                 case 'o':
544                         numops = atoi(optarg);
545                         break;
546                 case 'O':
547                         use_oplocks = True;
548                         break;
549                 case 'a':
550                         showall = True;
551                         break;
552                 case 'A':
553                         analyze = True;
554                         break;
555                 case 'Z':
556                         zero_zero = True;
557                         break;
558                 case 'E':
559                         exact_error_codes = True;
560                         break;
561                 case 'l':
562                         lp_set_cmdline("torture:unclist", optarg);
563                         break;
564                 case 'W':
565                         lp_set_cmdline("workgroup", optarg);
566                         break;
567                 case 'h':
568                         usage();
569                         exit(1);
570                 default:
571                         printf("Unknown option %c (%d)\n", (char)opt, opt);
572                         exit(1);
573                 }
574         }
575
576         if (username_count == 0) {
577                 usage();
578                 return -1;
579         }
580         if (username_count == 1) {
581                 servers[1] = servers[0];
582         }
583
584         gensec_init();
585
586         argc -= optind;
587         argv += optind;
588
589         DEBUG(0,("seed=%u base=%d range=%d min_length=%d\n", 
590                  seed, lock_base, lock_range, min_length));
591         srandom(seed);
592
593         test_locks(share);
594
595         return(0);
596 }
597