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