openafs-string-header-cleanup-20071030
[openafs.git] / src / vlserver / vldb_check.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* Read a VLDB file and verify it for correctness */
11
12 #define VL  0x001               /* good volume entry */
13 #define FR  0x002               /* free volume entry */
14 #define MH  0x004               /* multi-homed entry */
15
16 #define RWH 0x010               /* on rw hash chain */
17 #define ROH 0x020               /* on ro hash chain */
18 #define BKH 0x040               /* on bk hash chain */
19 #define NH  0x080               /* on name hash chain */
20
21 #define MHC 0x100               /* on multihomed chain */
22 #define FRC 0x200               /* on free chain */
23
24 #define REFRW 0x1000            /* linked from something (RW) */
25 #define REFRO 0x2000            /* linked from something (RO) */
26 #define REFBK 0x4000            /* linked from something (BK) */
27 #define REFN  0x8000            /* linked from something (name) */
28
29 #define vldbread(x,y,z) vldbio(x,y,z,0)
30 #define vldbwrite(x,y,z) vldbio(x,y,z,1)
31
32 #include <afsconfig.h>
33 #include <afs/param.h>
34
35 RCSID
36     ("$Header$");
37
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <string.h>
43 #ifdef AFS_NT40_ENV
44 #include <winsock2.h>
45 #include <WINNT/afsevent.h>
46 #include <io.h>
47 #else
48 #include <sys/socket.h>
49 #include <netdb.h>
50 #include <netinet/in.h>
51 #endif
52
53 #include "vlserver.h"
54 #include "vldbint.h"
55 #include <ubik.h>
56 #include <afs/afsutil.h>
57 #include <afs/cmd.h>
58
59 #define ADDR(x) (x/sizeof(struct nvlentry))
60
61 int fd;
62 int listentries, listservers, listheader, listuheader, verbose;
63
64 int fix = 0;
65
66 struct er {
67     long addr;
68     int type;
69 } *record;
70 int serveraddrs[MAXSERVERID + 2];
71
72 #if 0
73 int
74 writeUbikHeader()
75 {
76     /* Bump the version number?? We could cheat and push a new db... */
77 }
78 #endif
79
80 #define HDRSIZE 64
81 int
82 readUbikHeader()
83 {
84     int offset, r;
85     struct ubik_hdr uheader;
86
87     offset = lseek(fd, 0, 0);
88     if (offset != 0) {
89         printf("error: lseek to 0 failed: %d %d\n", offset, errno);
90         return (-1);
91     }
92
93     /* now read the info */
94     r = read(fd, &uheader, sizeof(uheader));
95     if (r != sizeof(uheader)) {
96         printf("error: read of %d bytes failed: %d %d\n", sizeof(uheader), r,
97                errno);
98         return (-1);
99     }
100
101     uheader.magic = ntohl(uheader.magic);
102     uheader.size = ntohl(uheader.size);
103     uheader.version.epoch = ntohl(uheader.version.epoch);
104     uheader.version.counter = ntohl(uheader.version.counter);
105
106     if (listuheader) {
107         printf("Ubik Header\n");
108         printf("   Magic           = 0x%x\n", uheader.magic);
109         printf("   Size            = %u\n", uheader.size);
110         printf("   Version.epoch   = %u\n", uheader.version.epoch);
111         printf("   Version.counter = %u\n", uheader.version.counter);
112     }
113
114     if (uheader.size != HDRSIZE)
115         printf("Ubik header size is %u (should be %u)\n", uheader.size,
116                HDRSIZE);
117     if (uheader.magic != UBIK_MAGIC)
118         printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
119                UBIK_MAGIC);
120
121     return (0);
122 }
123
124 int
125 vldbio(int position, char *buffer, int size, int rdwr)
126 {
127     int offset, r, p;
128
129     /* seek to the correct spot. skip ubik stuff */
130     p = position + HDRSIZE;
131     offset = lseek(fd, p, 0);
132     if (offset != p) {
133         printf("error: lseek to %d failed: %d %d\n", p, offset, errno);
134         return (-1);
135     }
136
137     if (rdwr == 1) 
138         r = write(fd, buffer, size);
139     else 
140         r = read(fd, buffer, size);
141
142     if (r != size) {
143         printf("error: %s of %d bytes failed: %d %d\n", rdwr==1?"write":"read",
144                size, r, errno);
145         return (-1);
146     }
147     return (0);
148 }
149
150 char *
151 vtype(int type)
152 {
153     static char Type[3];
154
155     if (type == 0)
156         strcpy(Type, "rw");
157     else if (type == 1)
158         strcpy(Type, "ro");
159     else if (type == 2)
160         strcpy(Type, "bk");
161     else
162         strcpy(Type, "??");
163     return (Type);
164 }
165
166 afs_int32
167 NameHash(char *volname)
168 {
169     unsigned int hash;
170     char *vchar;
171
172     hash = 0;
173     for (vchar = volname + strlen(volname) - 1; vchar >= volname; vchar--)
174         hash = (hash * 63) + (*((unsigned char *)vchar) - 63);
175     return (hash % HASHSIZE);
176 }
177
178 afs_int32
179 IdHash(afs_int32 volid)
180 {
181     return ((abs(volid)) % HASHSIZE);
182 }
183
184 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
185 int
186 InvalidVolname(char *volname)
187 {
188     char *map;
189     size_t slen;
190
191     map = LEGALCHARS;
192     slen = strlen(volname);
193     if (slen >= VL_MAXNAMELEN)
194         return 1;
195     return (slen != strspn(volname, map));
196 }
197
198 void
199 readheader(struct vlheader *headerp)
200 {
201     int i, j;
202
203     vldbread(0, headerp, sizeof(*headerp));
204
205     headerp->vital_header.vldbversion =
206         ntohl(headerp->vital_header.vldbversion);
207     headerp->vital_header.headersize =
208         ntohl(headerp->vital_header.headersize);
209     headerp->vital_header.freePtr = ntohl(headerp->vital_header.freePtr);
210     headerp->vital_header.eofPtr = ntohl(headerp->vital_header.eofPtr);
211     headerp->vital_header.allocs = ntohl(headerp->vital_header.allocs);
212     headerp->vital_header.frees = ntohl(headerp->vital_header.frees);
213     headerp->vital_header.MaxVolumeId =
214         ntohl(headerp->vital_header.MaxVolumeId);
215     headerp->vital_header.totalEntries[0] =
216         ntohl(headerp->vital_header.totalEntries[0]);
217     for (i = 0; i < MAXTYPES; i++)
218         headerp->vital_header.totalEntries[i] =
219             ntohl(headerp->vital_header.totalEntries[1]);
220
221     headerp->SIT = ntohl(headerp->SIT);
222     for (i = 0; i < MAXSERVERID; i++)
223         headerp->IpMappedAddr[i] = ntohl(headerp->IpMappedAddr[i]);
224     for (i = 0; i < HASHSIZE; i++)
225         headerp->VolnameHash[i] = ntohl(headerp->VolnameHash[i]);
226     for (i = 0; i < MAXTYPES; i++)
227         for (j = 0; j < HASHSIZE; j++)
228             headerp->VolidHash[i][j] = ntohl(headerp->VolidHash[i][j]);
229
230     if (listheader) {
231         printf("vldb header\n");
232         printf("   vldbversion      = %u\n",
233                headerp->vital_header.vldbversion);
234         printf("   headersize       = %u [actual=%u]\n",
235                headerp->vital_header.headersize, sizeof(*headerp));
236         printf("   freePtr          = 0x%x\n", headerp->vital_header.freePtr);
237         printf("   eofPtr           = %u\n", headerp->vital_header.eofPtr);
238         printf("   allocblock calls = %10u\n", headerp->vital_header.allocs);
239         printf("   freeblock  calls = %10u\n", headerp->vital_header.frees);
240         printf("   MaxVolumeId      = %u\n",
241                headerp->vital_header.MaxVolumeId);
242         printf("   rw vol entries   = %u\n",
243                headerp->vital_header.totalEntries[0]);
244         printf("   ro vol entries   = %u\n",
245                headerp->vital_header.totalEntries[1]);
246         printf("   bk vol entries   = %u\n",
247                headerp->vital_header.totalEntries[2]);
248         printf("   multihome info   = 0x%x (%u)\n", headerp->SIT,
249                headerp->SIT);
250         printf("   server ip addr   table: size = %d entries\n",
251                MAXSERVERID + 1);
252         printf("   volume name hash table: size = %d buckets\n", HASHSIZE);
253         printf("   volume id   hash table: %d tables with %d buckets each\n",
254                MAXTYPES, HASHSIZE);
255     }
256
257     /* Check the header size */
258     if (headerp->vital_header.headersize != sizeof(*headerp))
259         printf("Header reports its size as %d (should be %d)\n",
260                headerp->vital_header.headersize, sizeof(*headerp));
261 }
262
263 void
264 writeheader(struct vlheader *headerp)
265 {
266     int i, j;
267
268     headerp->vital_header.vldbversion =
269         htonl(headerp->vital_header.vldbversion);
270     headerp->vital_header.headersize =
271         htonl(headerp->vital_header.headersize);
272     headerp->vital_header.freePtr = htonl(headerp->vital_header.freePtr);
273     headerp->vital_header.eofPtr = htonl(headerp->vital_header.eofPtr);
274     headerp->vital_header.allocs = htonl(headerp->vital_header.allocs);
275     headerp->vital_header.frees = htonl(headerp->vital_header.frees);
276     headerp->vital_header.MaxVolumeId =
277         htonl(headerp->vital_header.MaxVolumeId);
278     headerp->vital_header.totalEntries[0] =
279         htonl(headerp->vital_header.totalEntries[0]);
280     for (i = 0; i < MAXTYPES; i++)
281         headerp->vital_header.totalEntries[i] =
282             htonl(headerp->vital_header.totalEntries[1]);
283
284     headerp->SIT = htonl(headerp->SIT);
285     for (i = 0; i < MAXSERVERID; i++)
286         headerp->IpMappedAddr[i] = htonl(headerp->IpMappedAddr[i]);
287     for (i = 0; i < HASHSIZE; i++)
288         headerp->VolnameHash[i] = htonl(headerp->VolnameHash[i]);
289     for (i = 0; i < MAXTYPES; i++)
290         for (j = 0; j < HASHSIZE; j++)
291             headerp->VolidHash[i][j] = htonl(headerp->VolidHash[i][j]);
292
293     vldbwrite(0, headerp, sizeof(*headerp));
294 }
295
296 void
297 readMH(afs_int32 addr, struct extentaddr *mhblockP)
298 {
299     int i, j;
300     struct extentaddr *e;
301
302     vldbread(addr, mhblockP, VL_ADDREXTBLK_SIZE);
303
304     mhblockP->ex_count = ntohl(mhblockP->ex_count);
305     mhblockP->ex_flags = ntohl(mhblockP->ex_flags);
306     for (i = 0; i < VL_MAX_ADDREXTBLKS; i++)
307         mhblockP->ex_contaddrs[i] = ntohl(mhblockP->ex_contaddrs[i]);
308
309     for (i = 1; i < VL_MHSRV_PERBLK; i++) {
310         e = &(mhblockP[i]);
311
312         /* won't convert hostuuid */
313         e->ex_uniquifier = ntohl(e->ex_uniquifier);
314         for (j = 0; j < VL_MAXIPADDRS_PERMH; j++)
315             e->ex_addrs[j] = ntohl(e->ex_addrs[j]);
316     }
317 }
318
319 void
320 readentry(afs_int32 addr, struct nvlentry *vlentryp, afs_int32 *type)
321 {
322     int i;
323
324     vldbread(addr, vlentryp, sizeof(*vlentryp));
325
326     for (i = 0; i < MAXTYPES; i++)
327         vlentryp->volumeId[i] = ntohl(vlentryp->volumeId[i]);
328     vlentryp->flags = ntohl(vlentryp->flags);
329     vlentryp->LockAfsId = ntohl(vlentryp->LockAfsId);
330     vlentryp->LockTimestamp = ntohl(vlentryp->LockTimestamp);
331     vlentryp->cloneId = ntohl(vlentryp->cloneId);
332     for (i = 0; i < MAXTYPES; i++)
333         vlentryp->nextIdHash[i] = ntohl(vlentryp->nextIdHash[i]);
334     vlentryp->nextNameHash = ntohl(vlentryp->nextNameHash);
335     for (i = 0; i < NMAXNSERVERS; i++) {
336         vlentryp->serverNumber[i] = ntohl(vlentryp->serverNumber[i]);
337         vlentryp->serverPartition[i] = ntohl(vlentryp->serverPartition[i]);
338         vlentryp->serverFlags[i] = ntohl(vlentryp->serverFlags[i]);
339     }
340
341     if (vlentryp->flags == VLCONTBLOCK) {
342         *type = MH;
343     } else if (vlentryp->flags == VLFREE) {
344         *type = FR;
345     } else {
346         *type = VL;
347     }
348
349     if (listentries) {
350         printf("address %u: ", addr);
351         if (vlentryp->flags == VLCONTBLOCK) {
352             printf("mh extension block\n");
353         } else if (vlentryp->flags == VLFREE) {
354             printf("free vlentry\n");
355         } else {
356             printf("vlentry %s\n", vlentryp->name);
357             printf("   rw id = %u ; ro id = %u ; bk id = %u\n",
358                    vlentryp->volumeId[0], vlentryp->volumeId[1],
359                    vlentryp->volumeId[2]);
360             printf("   flags         =");
361             if (vlentryp->flags & VLF_RWEXISTS)
362                 printf(" rw");
363             if (vlentryp->flags & VLF_ROEXISTS)
364                 printf(" ro");
365             if (vlentryp->flags & VLF_BACKEXISTS)
366                 printf(" bk");
367             if (vlentryp->flags & 0xffff8fff)
368                 printf(" errorflag(0x%x)", vlentryp->flags);
369             printf("\n");
370             printf("   LockAfsId     = %d\n", vlentryp->LockAfsId);
371             printf("   LockTimestamp = %d\n", vlentryp->LockTimestamp);
372             printf("   cloneId       = %u\n", vlentryp->cloneId);
373             printf
374                 ("   next hash for rw = %u ; ro = %u ; bk = %u ; name = %u\n",
375                  vlentryp->nextIdHash[0], vlentryp->nextIdHash[1],
376                  vlentryp->nextIdHash[2], vlentryp->nextNameHash);
377             for (i = 0; i < NMAXNSERVERS; i++) {
378                 if (vlentryp->serverNumber[i] != 255) {
379                     printf("   server %d ; partition %d ; flags =",
380                            vlentryp->serverNumber[i],
381                            vlentryp->serverPartition[i]);
382                     if (vlentryp->serverFlags[i] & VLSF_RWVOL)
383                         printf(" rw");
384                     if (vlentryp->serverFlags[i] & VLSF_ROVOL)
385                         printf(" ro");
386                     if (vlentryp->serverFlags[i] & VLSF_BACKVOL)
387                         printf(" bk");
388                     if (vlentryp->serverFlags[i] & VLSF_NEWREPSITE)
389                         printf(" newro");
390                     printf("\n");
391                 }
392             }
393         }
394     }
395 }
396
397 void
398 writeentry(afs_int32 addr, struct nvlentry *vlentryp)
399 {
400     int i;
401
402     if (verbose)
403         printf("Writing back entry at addr %u\n", addr);
404
405     for (i = 0; i < MAXTYPES; i++)
406         vlentryp->volumeId[i] = htonl(vlentryp->volumeId[i]);
407     vlentryp->flags = htonl(vlentryp->flags);
408     vlentryp->LockAfsId = htonl(vlentryp->LockAfsId);
409     vlentryp->LockTimestamp = htonl(vlentryp->LockTimestamp);
410     vlentryp->cloneId = htonl(vlentryp->cloneId);
411     for (i = 0; i < MAXTYPES; i++)
412         vlentryp->nextIdHash[i] = htonl(vlentryp->nextIdHash[i]);
413     vlentryp->nextNameHash = htonl(vlentryp->nextNameHash);
414     for (i = 0; i < NMAXNSERVERS; i++) {
415         vlentryp->serverNumber[i] = htonl(vlentryp->serverNumber[i]);
416         vlentryp->serverPartition[i] = htonl(vlentryp->serverPartition[i]);
417         vlentryp->serverFlags[i] = htonl(vlentryp->serverFlags[i]);
418     }
419     vldbwrite(addr, vlentryp, sizeof(*vlentryp));
420 }
421
422 void
423 readSIT(int base, int addr)
424 {
425     int i, j, a;
426     char sitbuf[VL_ADDREXTBLK_SIZE];
427     struct extentaddr *extent;
428
429     if (!addr)
430         return;
431     vldbread(addr, sitbuf, VL_ADDREXTBLK_SIZE);
432     extent = (struct extentaddr *)sitbuf;
433
434     printf("multihome info block: base %d\n", base);
435     if (base == 0) {
436         printf("   count = %u\n", ntohl(extent->ex_count));
437         printf("   flags = %u\n", ntohl(extent->ex_flags));
438         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
439             printf("   contaddrs[%d] = %u\n", i,
440                    ntohl(extent->ex_contaddrs[i]));
441         }
442     }
443     for (i = 1; i < VL_MHSRV_PERBLK; i++) {
444         /* should we skip this entry */
445         for (j = 0; j < VL_MAX_ADDREXTBLKS; j++) {
446             if (extent[i].ex_addrs[j])
447                 break;
448         }
449         if (j >= VL_MAX_ADDREXTBLKS)
450             continue;
451
452         printf("   base %d index %d:\n", base, i);
453
454         printf("       afsuuid    = (%x %x %x /%d/%d/ /%x/%x/%x/%x/%x/%x/)\n",
455                ntohl(extent[i].ex_hostuuid.time_low),
456                ntohl(extent[i].ex_hostuuid.time_mid),
457                ntohl(extent[i].ex_hostuuid.time_hi_and_version),
458                ntohl(extent[i].ex_hostuuid.clock_seq_hi_and_reserved),
459                ntohl(extent[i].ex_hostuuid.clock_seq_low),
460                ntohl(extent[i].ex_hostuuid.node[0]),
461                ntohl(extent[i].ex_hostuuid.node[1]),
462                ntohl(extent[i].ex_hostuuid.node[2]),
463                ntohl(extent[i].ex_hostuuid.node[3]),
464                ntohl(extent[i].ex_hostuuid.node[4]),
465                ntohl(extent[i].ex_hostuuid.node[5]));
466         printf("       uniquifier = %u\n", ntohl(extent[i].ex_uniquifier));
467         for (j = 0; j < VL_MAXIPADDRS_PERMH; j++) {
468             a = ntohl(extent[i].ex_addrs[j]);
469             if (a) {
470                 printf("       %d.%d.%d.%d\n", (a >> 24) & 0xff,
471                        (a >> 16) & 0xff, (a >> 8) & 0xff, (a) & 0xff);
472             }
473         }
474     }
475 }
476
477 /*
478  * Read each entry in the database:
479  * Record what type of entry it is and its address in the record array.
480  * Remember what the maximum volume id we found is and check against the header.
481  */
482 void
483 ReadAllEntries(struct vlheader *header)
484 {
485     afs_int32 type, rindex, i, j, e;
486     int freecount = 0, mhcount = 0, vlcount = 0;
487     int rwcount = 0, rocount = 0, bkcount = 0;
488     struct nvlentry vlentry;
489     afs_uint32 addr, entrysize, maxvolid = 0;
490
491     if (verbose)
492         printf("Read each entry in the database\n");
493     for (addr = header->vital_header.headersize;
494          addr < header->vital_header.eofPtr; addr += entrysize) {
495
496         /* Remember the highest volume id */
497         readentry(addr, &vlentry, &type);
498         if (type == VL) {
499             if (!(vlentry.flags & VLF_RWEXISTS))
500                 printf("WARNING: VLDB entry '%s' has no RW volume\n",
501                        vlentry.name);
502
503             for (i = 0; i < MAXTYPES; i++)
504                 if (maxvolid < vlentry.volumeId[i])
505                     maxvolid = vlentry.volumeId[i];
506
507             e = 1;
508             for (j = 0; j < NMAXNSERVERS; j++) {
509                 if (vlentry.serverNumber[j] == 255)
510                     continue;
511                 if (vlentry.serverFlags[j] & (VLSF_ROVOL | VLSF_NEWREPSITE)) {
512                     rocount++;
513                     continue;
514                 }
515                 if (vlentry.serverFlags[j] & VLSF_RWVOL) {
516                     rwcount++;
517                     if (vlentry.flags & VLF_BACKEXISTS)
518                         bkcount++;
519                     continue;
520                 }
521                 if (!vlentry.serverFlags[j]) {
522                     /*e = 0;*/
523                     continue;
524                 }
525                 if (e) {
526                     printf
527                         ("VLDB entry '%s' contains an unknown RW/RO index serverFlag\n",
528                          vlentry.name);
529                     e = 0;
530                 }
531                 printf
532                     ("   index %d : serverNumber %d : serverPartition %d : serverFlag %d\n",
533                      j, vlentry.serverNumber[j], vlentry.serverPartition[j],
534                      vlentry.serverFlags[j]);
535             }
536         }
537
538         rindex = addr / sizeof(vlentry);
539         if (record[rindex].type) {
540             printf("INTERNAL ERROR: record holder %d already in use\n",
541                    rindex);
542             return;
543         }
544         record[rindex].addr = addr;
545         record[rindex].type = type;
546
547         /* Determine entrysize and keep count */
548         if (type == VL) {
549             entrysize = sizeof(vlentry);
550             vlcount++;
551         } else if (type == FR) {
552             entrysize = sizeof(vlentry);
553             freecount++;
554         } else if (type == MH) {
555             entrysize = VL_ADDREXTBLK_SIZE;
556             mhcount++;
557         } else {
558             printf("Unknown entry at %u\n", addr);
559         }
560     }
561     if (verbose) {
562         printf("Found %d entries, %d free entries, %d multihomed blocks\n",
563                vlcount, freecount, mhcount);
564         printf("Found %d RW volumes, %d BK volumes, %d RO volumes\n", rwcount,
565                bkcount, rocount);
566     }
567
568     /* Check the maxmimum volume id in the header */
569     if (maxvolid != header->vital_header.MaxVolumeId - 1)
570         printf
571             ("Header's maximum volume id is %u and largest id found in VLDB is %u\n",
572              header->vital_header.MaxVolumeId, maxvolid);
573 }
574
575
576 void
577 SetHashEnd(long addr, int type, long new)
578 {
579     struct nvlentry vlentry;
580     afs_int32 i, rindex, type2, next = -1;
581
582     for (; addr; addr = next) {
583         readentry(addr, &vlentry, &type2);
584         switch(type & 0xf0) {
585         case RWH:
586             next = vlentry.nextIdHash[0];
587             break;
588         case ROH:
589             next = vlentry.nextIdHash[1];
590             break;
591         case BKH:
592             next = vlentry.nextIdHash[2];
593             break;
594         case NH:
595             next = vlentry.nextNameHash;
596             break;
597         default:
598             next = -1;
599         }
600
601         if (next < 1) {
602             switch(type & 0xf0) {
603             case RWH:
604               if (vlentry.nextIdHash[0] != 0) {printf("bwoop\n");}
605                 vlentry.nextIdHash[0] = new;
606                 break;
607             case ROH:
608               if (vlentry.nextIdHash[1] != 0) {printf("bwoop\n");}
609                 vlentry.nextIdHash[1] = new;
610                 break;
611             case BKH:
612               if (vlentry.nextIdHash[2] != 0) {printf("bwoop\n");}
613                 vlentry.nextIdHash[2] = new;
614                 break;
615             case NH:
616               if (vlentry.nextNameHash != 0) {printf("bwoop\n");}
617                 vlentry.nextNameHash = new;
618                 break;
619             }
620             writeentry(addr, &vlentry);
621             return;
622         }
623     }
624 }
625
626 /*
627  * Follow each Name hash bucket marking it as read in the record array.
628  * Record we found it in the name hash within the record array.
629  * Check that the name is hashed correctly.
630  */
631 void
632 FollowNameHash(struct vlheader *header)
633 {
634     int count = 0, longest = 0, shortest = -1, chainlength;
635     struct nvlentry vlentry;
636     afs_uint32 addr;
637     afs_int32 i, type, rindex;
638
639     /* Now follow the Name Hash Table */
640     if (verbose)
641         printf("Check Volume Name Hash\n");
642     for (i = 0; i < HASHSIZE; i++) {
643         chainlength = 0;
644         for (addr = header->VolnameHash[i]; addr; addr = vlentry.nextNameHash) {
645             readentry(addr, &vlentry, &type);
646             if (type != VL) {
647                 printf("Name Hash %d: Bad entry at %u: Not a valid vlentry\n",
648                        i, addr);
649                 continue;
650             }
651
652             rindex = addr / sizeof(vlentry);
653
654             if (record[rindex].addr != addr && record[rindex].addr) {
655                 printf
656                     ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
657                      record[rindex].addr, addr, rindex);
658             }
659             if (record[rindex].type & NH) {
660                 printf
661                     ("Name Hash %d: Bad entry '%s': Already in the name hash\n",
662                      i, vlentry.name);
663                 break;
664             }
665             record[rindex].type |= NH;
666
667             record[rindex].type |= REFN;
668
669             chainlength++;
670             count++;
671
672             /* Hash the name and check if in correct hash table */
673             if (NameHash(vlentry.name) != i) {
674                 printf
675                     ("Name Hash %d: Bad entry '%s': Incorrect name hash chain (should be in %d)\n",
676                      i, vlentry.name, NameHash(vlentry.name));
677             }
678         }
679         if (chainlength > longest)
680             longest = chainlength;
681         if ((shortest == -1) || (chainlength < shortest))
682             shortest = chainlength;
683     }
684     if (verbose) {
685         printf
686             ("%d entries in name hash, longest is %d, shortest is %d, average length is %f\n",
687              count, longest, shortest, ((float)count / (float)HASHSIZE));
688     }
689 }
690
691 /*
692  * Follow the ID hash chains for the RW, RO, and BK hash tables.
693  * Record we found it in the id hash within the record array.
694  * Check that the ID is hashed correctly.
695  */
696 void
697 FollowIdHash(struct vlheader *header)
698 {
699     int count = 0, longest = 0, shortest = -1, chainlength;
700     struct nvlentry vlentry;
701     afs_uint32 addr;
702     afs_int32 i, j, hash, type, rindex, ref;
703
704     /* Now follow the RW, RO, and BK Hash Tables */
705     if (verbose)
706         printf("Check RW, RO, and BK id Hashes\n");
707     for (i = 0; i < MAXTYPES; i++) {
708         hash = ((i == 0) ? RWH : ((i == 1) ? ROH : BKH));
709         ref = ((i == 0) ? REFRW : ((i == 1) ? REFRO : REFBK));
710         count = longest = 0;
711         shortest = -1;
712
713         for (j = 0; j < HASHSIZE; j++) {
714             chainlength = 0;
715             for (addr = header->VolidHash[i][j]; addr;
716                  addr = vlentry.nextIdHash[i]) {
717                 readentry(addr, &vlentry, &type);
718                 if (type != VL) {
719                     printf
720                         ("%s Id Hash %d: Bad entry at %u: Not a valid vlentry\n",
721                          vtype(i), j, addr);
722                     continue;
723                 }
724
725                 rindex = addr / sizeof(vlentry);
726                 if (record[rindex].addr != addr && record[rindex].addr) {
727                     printf
728                         ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
729                          record[rindex].addr, addr, rindex);
730                 }
731                 if (record[rindex].type & hash) {
732                     printf
733                         ("%s Id Hash %d: Bad entry '%s': Already in the the hash table\n",
734                          vtype(i), j, vlentry.name);
735                     break;
736                 }
737                 record[rindex].type |= hash;
738                 record[rindex].type |= ref;
739
740                 chainlength++;
741                 count++;
742
743                 /* Hash the id and check if in correct hash table */
744                 if (IdHash(vlentry.volumeId[i]) != j) {
745                     printf
746                         ("%s Id Hash %d: Bad entry '%s': Incorrect Id hash chain (should be in %d)\n",
747                          vtype(i), j, vlentry.name,
748                          IdHash(vlentry.volumeId[i]));
749                 }
750             }
751
752             if (chainlength > longest)
753                 longest = chainlength;
754             if ((shortest == -1) || (chainlength < shortest))
755                 shortest = chainlength;
756         }
757         if (verbose) {
758             printf
759                 ("%d entries in %s hash, longest is %d, shortest is %d, average length is %f\n",
760                  count, vtype(i), longest, shortest,
761                  ((float)count / (float)HASHSIZE));
762         }
763     }
764 }
765
766 /*
767  * Follow the free chain.
768  * Record we found it in the free chain within the record array.
769  */
770 void
771 FollowFreeChain(struct vlheader *header)
772 {
773     afs_int32 count = 0;
774     struct nvlentry vlentry;
775     afs_uint32 addr;
776     afs_int32 type, rindex;
777
778     /* Now follow the Free Chain */
779     if (verbose)
780         printf("Check Volume Free Chain\n");
781     for (addr = header->vital_header.freePtr; addr;
782          addr = vlentry.nextIdHash[0]) {
783         readentry(addr, &vlentry, &type);
784         if (type != FR) {
785             printf
786                 ("Free Chain %d: Bad entry at %u: Not a valid free vlentry (0x%x)\n",
787                  count, addr, type);
788             continue;
789         }
790
791         rindex = addr / sizeof(vlentry);
792         if (record[rindex].addr != addr && record[rindex].addr) {
793             printf
794                 ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
795                  record[rindex].addr, addr, rindex);
796         }
797         if (record[rindex].type & FRC) {
798             printf("Free Chain: Bad entry at %u: Already in the free chain\n",
799                    addr);
800             break;
801         }
802         record[rindex].type |= FRC;
803
804         count++;
805     }
806     if (verbose)
807         printf("%d entries on free chain\n", count);
808 }
809
810 /*
811  * Read each multihomed block and mark it as found in the record.
812  * Read each entry in each multihomed block and mark the serveraddrs
813  * array with the number of ip addresses found for this entry.
814  * 
815  * Then read the IpMappedAddr array in the header.
816  * Verify that multihomed entries base and index are valid and points to
817  * a good multhomed entry.
818  * Mark the serveraddrs array with 1 ip address for regular entries.
819  * 
820  * By the end, the severaddrs array will have a 0 if the entry has no 
821  * IP addresses in it or the count of the number of IP addresses.
822  *
823  * The code does not verify if there are duplicate IP addresses in the 
824  * list. The vlserver does this when a fileserver registeres itself.
825  */
826 void
827 CheckIpAddrs(struct vlheader *header)
828 {
829     int mhblocks = 0;
830     afs_int32 i, j, m, rindex;
831     afs_int32 mhentries, regentries;
832     afs_int32 caddrs[VL_MAX_ADDREXTBLKS];
833     char mhblock[VL_ADDREXTBLK_SIZE];
834     struct extentaddr *MHblock = (struct extentaddr *)mhblock;
835     struct extentaddr *e;
836     int ipindex, ipaddrs;
837     afsUUID nulluuid;
838
839     memset(&nulluuid, 0, sizeof(nulluuid));
840
841     if (verbose)
842         printf("Check Multihomed blocks\n");
843
844     if (header->SIT) {
845         /* Read the first MH block and from it, gather the 
846          * addresses of all the mh blocks.
847          */
848         readMH(header->SIT, MHblock);
849         if (MHblock->ex_flags != VLCONTBLOCK) {
850             printf
851                 ("Multihomed Block 0: Bad entry at %u: Not a valid multihomed block\n",
852                  header->SIT);
853         }
854
855         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
856             caddrs[i] = MHblock->ex_contaddrs[i];
857         }
858
859         if (header->SIT != caddrs[0]) {
860             printf
861                 ("MH block does not point to self %u in header, %u in block\n",
862                  header->SIT, caddrs[0]);
863         }
864
865         /* Now read each MH block and record it in the record array */
866         for (i = 0; i < VL_MAX_ADDREXTBLKS; i++) {
867             if (!caddrs[i])
868                 continue;
869
870             readMH(caddrs[i], MHblock);
871             if (MHblock->ex_flags != VLCONTBLOCK) {
872                 printf
873                     ("Multihomed Block 0: Bad entry at %u: Not a valid multihomed block\n",
874                      header->SIT);
875             }
876
877             rindex = caddrs[i] / sizeof(vlentry);
878             if (record[rindex].addr != caddrs[i] && record[rindex].addr) {
879                 printf
880                     ("INTERNAL ERROR: addresses %u and %u use same record slot %d\n",
881                      record[rindex].addr, caddrs[i], rindex);
882             }
883             if (record[rindex].type & FRC) {
884                 printf
885                     ("MH Blocks Chain %d: Bad entry at %u: Already a MH block\n",
886                      i, record[rindex].addr);
887                 break;
888             }
889             record[rindex].type |= MHC;
890
891             mhblocks++;
892
893             /* Read each entry in a multihomed block. 
894              * Find the pointer to the entry in the IpMappedAddr array and
895              * verify that the entry is good (has IP addresses in it).
896              */
897             mhentries = 0;
898             for (j = 1; j < VL_MHSRV_PERBLK; j++) {
899                 e = (struct extentaddr *)&(MHblock[j]);
900
901                 /* Search the IpMappedAddr array for the reference to this entry */
902                 for (ipindex = 0; ipindex < MAXSERVERID; ipindex++) {
903                     if (((header->IpMappedAddr[ipindex] & 0xff000000) ==
904                          0xff000000)
905                         &&
906                         (((header->
907                            IpMappedAddr[ipindex] & 0x00ff0000) >> 16) == i)
908                         && ((header->IpMappedAddr[ipindex] & 0x0000ffff) ==
909                             j)) {
910                         break;
911                     }
912                 }
913                 if (ipindex >= MAXSERVERID)
914                     ipindex = -1;
915                 else
916                     serveraddrs[ipindex] = -1;
917
918                 if (memcmp(&e->ex_hostuuid, &nulluuid, sizeof(afsUUID)) == 0) {
919                     if (ipindex != -1) {
920                         printf
921                             ("Server Addrs index %d references null MH block %d, index %d\n",
922                              ipindex, i, j);
923                         serveraddrs[ipindex] = 0;       /* avoids printing 2nd error below */
924                     }
925                     continue;
926                 }
927
928                 /* Step through each ip address and count the good addresses */
929                 ipaddrs = 0;
930                 for (m = 0; m < VL_MAXIPADDRS_PERMH; m++) {
931                     if (e->ex_addrs[m])
932                         ipaddrs++;
933                 }
934
935                 /* If we found any good ip addresses, mark it in the serveraddrs record */
936                 if (ipaddrs) {
937                     mhentries++;
938                     if (ipindex == -1) {
939                         printf
940                             ("MH block %d, index %d: Not referenced by server addrs\n",
941                              i, j);
942                     } else {
943                         serveraddrs[ipindex] = ipaddrs; /* It is good */
944                     }
945                 }
946
947                 if (listservers && ipaddrs) {
948                     printf("MH block %d, index %d:", i, j);
949                     for (m = 0; m < VL_MAXIPADDRS_PERMH; m++) {
950                         if (!e->ex_addrs[m])
951                             continue;
952                         printf(" %d.%d.%d.%d",
953                                (e->ex_addrs[m] & 0xff000000) >> 24,
954                                (e->ex_addrs[m] & 0x00ff0000) >> 16,
955                                (e->ex_addrs[m] & 0x0000ff00) >> 8,
956                                (e->ex_addrs[m] & 0x000000ff));
957                     }
958                     printf("\n");
959                 }
960             }
961 /*
962  *      if (mhentries != MHblock->ex_count) {
963  *         printf("MH blocks says it has %d entries (found %d)\n",
964  *                MHblock->ex_count, mhentries);
965  *      }
966  */
967         }
968     }
969     if (verbose)
970         printf("%d multihomed blocks\n", mhblocks);
971
972     /* Check the server addresses */
973     if (verbose)
974         printf("Check server addresses\n");
975     mhentries = regentries = 0;
976     for (i = 0; i <= MAXSERVERID; i++) {
977         if (header->IpMappedAddr[i]) {
978             if ((header->IpMappedAddr[i] & 0xff000000) == 0xff000000) {
979                 mhentries++;
980                 if (((header->IpMappedAddr[i] & 0x00ff0000) >> 16) >
981                     VL_MAX_ADDREXTBLKS)
982                     printf
983                         ("IP Addr for entry %d: Multihome block is bad (%d)\n",
984                          i, ((header->IpMappedAddr[i] & 0x00ff0000) >> 16));
985                 if (((header->IpMappedAddr[i] & 0x0000ffff) > VL_MHSRV_PERBLK)
986                     || ((header->IpMappedAddr[i] & 0x0000ffff) < 1))
987                     printf
988                         ("IP Addr for entry %d: Multihome index is bad (%d)\n",
989                          i, (header->IpMappedAddr[i] & 0x0000ffff));
990                 if (serveraddrs[i] == -1) {
991                     printf
992                         ("warning: IP Addr for entry %d: Multihome entry has no ip addresses\n",
993                          i);
994                     serveraddrs[i] = 0;
995                 }
996                 if (listservers) {
997                     printf("   Server ip addr %d = MH block %d, index %d\n",
998                            i, (header->IpMappedAddr[i] & 0x00ff0000) >> 16,
999                            (header->IpMappedAddr[i] & 0x0000ffff));
1000                 }
1001             } else {
1002                 regentries++;
1003                 serveraddrs[i] = 1;     /* It is good */
1004                 if (listservers) {
1005                     printf("   Server ip addr %d = %d.%d.%d.%d\n", i,
1006                            (header->IpMappedAddr[i] & 0xff000000) >> 24,
1007                            (header->IpMappedAddr[i] & 0x00ff0000) >> 16,
1008                            (header->IpMappedAddr[i] & 0x0000ff00) >> 8,
1009                            (header->IpMappedAddr[i] & 0x000000ff));
1010                 }
1011             }
1012         }
1013     }
1014     if (verbose) {
1015         printf("%d simple entries, %d multihomed entries, Total = %d\n",
1016                regentries, mhentries, mhentries + regentries);
1017     }
1018
1019 }
1020
1021 void 
1022 FixBad(afs_uint32 idx, afs_uint32 addr, afs_uint32 type, afs_uint32 tmp, 
1023        struct nvlentry *vlentry) {
1024     SetHashEnd(addr, type, tmp);
1025     printf("linked unlinked chain %u (index %d) to end of chain\n", 
1026            tmp, ADDR(tmp));
1027 }
1028
1029 int
1030 WorkerBee(struct cmd_syndesc *as, char *arock)
1031 {
1032     char *dbfile;
1033     afs_int32 maxentries, type, tmp;
1034     struct vlheader header;
1035     struct nvlentry vlentry;
1036     int i, j, help = 0;
1037
1038     dbfile = as->parms[0].items->data;  /* -database */
1039     listuheader = (as->parms[1].items ? 1 : 0); /* -uheader  */
1040     listheader = (as->parms[2].items ? 1 : 0);  /* -vheader  */
1041     listservers = (as->parms[3].items ? 1 : 0); /* -servers  */
1042     listentries = (as->parms[4].items ? 1 : 0); /* -entries  */
1043     verbose = (as->parms[5].items ? 1 : 0);     /* -verbose  */
1044     fix = (as->parms[6].items ? 1 : 0); /* -fix  */
1045
1046     /* open the vldb database file */
1047     fd = open(dbfile, (fix > 0)?O_RDWR:O_RDONLY, 0);
1048     if (fd < 0) {
1049         printf("can't open file '%s'. error = %d\n", dbfile, errno);
1050         return 0;
1051     }
1052
1053     /* read the ubik header and the vldb database header */
1054     readUbikHeader();
1055     readheader(&header);
1056     if (header.vital_header.vldbversion < 3) {
1057         printf("does not support vldb with version less than 3\n");
1058         return 0;
1059     }
1060
1061     maxentries = (header.vital_header.eofPtr / sizeof(vlentry)) + 1;
1062     record = (struct er *)malloc(maxentries * sizeof(struct er));
1063     memset((char *)record, 0, (maxentries * sizeof(struct er)));
1064     memset((char *)serveraddrs, 0, sizeof(serveraddrs));
1065
1066     /* Will fill in the record array of entries it found */
1067     ReadAllEntries(&header);
1068     listentries = 0;            /* Listed all the entries */
1069
1070     /* Check the multihomed blocks for valid entries as well as
1071      * the IpMappedAddrs array in the header for valid entries.
1072      */
1073     CheckIpAddrs(&header);
1074
1075     /* Follow the hash tables */
1076     FollowNameHash(&header);
1077     FollowIdHash(&header);
1078
1079     /* Follow the chain of free entries */
1080     FollowFreeChain(&header);
1081
1082     /* Now check the record we have been keeping for inconsistancies
1083      * For valid vlentries, also check that the server we point to is 
1084      * valid (the serveraddrs array).
1085      */
1086     if (verbose)
1087         printf("Verify each volume entry\n");
1088     for (i = 0; i < maxentries; i++) {
1089         int nextp;
1090         int reft;
1091         int hash;
1092         int *nextpp;
1093         char *which;
1094
1095         if (record[i].type == 0)
1096             continue;
1097
1098         /* If a vlentry, verify that its name is valid, its name and ids are
1099          * on the hash chains, and its server numbers are good.
1100          */
1101         if (record[i].type & VL) {
1102             int foundbad = 0;
1103             char volidbuf[256];
1104
1105             readentry(record[i].addr, &vlentry, &type);
1106
1107             if (InvalidVolname(vlentry.name))
1108                 printf("Volume '%s' at addr %u has an invalid name\n",
1109                        vlentry.name, record[i].addr);
1110
1111             if (!(record[i].type & NH)) {
1112                 nextp = ADDR(vlentry.nextNameHash);
1113                 reft = REFN;
1114                 hash = NameHash(vlentry.name);
1115                 nextpp = &vlentry.nextNameHash;
1116                 which = "name";
1117                 sprintf(volidbuf, "");
1118                 foundbad = 1;
1119             }
1120
1121             if (vlentry.volumeId[0] && !(record[i].type & RWH)) {
1122                 nextp = ADDR(vlentry.nextIdHash[0]);
1123                 reft = REFRW;
1124                 hash = IdHash(vlentry.volumeId[0]);
1125                 nextpp = &(vlentry.nextIdHash[0]);
1126                 which = "RW";
1127                 sprintf(volidbuf, "id %u ", vlentry.volumeId[0]);
1128                 foundbad = 1;
1129             }
1130
1131             if (vlentry.volumeId[1] && !(record[i].type & ROH)) {
1132                 nextp = ADDR(vlentry.nextIdHash[1]);
1133                 reft = REFRO;
1134                 hash = IdHash(vlentry.volumeId[1]);
1135                 nextpp = &(vlentry.nextIdHash[1]);
1136                 which = "RO";
1137                 sprintf(volidbuf, "id %u ", vlentry.volumeId[1]);
1138                 foundbad = 1;
1139             }
1140
1141             if (vlentry.volumeId[2] && !(record[i].type & BKH)) {
1142                 nextp = ADDR(vlentry.nextIdHash[2]);
1143                 reft = REFBK;
1144                 hash = IdHash(vlentry.volumeId[2]);
1145                 nextpp = &(vlentry.nextIdHash[2]);
1146                 which = "BK";
1147                 sprintf(volidbuf, "id %u ", vlentry.volumeId[2]);
1148                 foundbad = 1;
1149             }
1150
1151             if (foundbad) {
1152                 printf("%d: Volume '%s' %snot found in %s hash %d", i, 
1153                        vlentry.name, volidbuf, which, hash);
1154                 if (nextp) {
1155                     printf(" (next %d", nextp);
1156                     if (!(record[nextp].type & reft)) {
1157                         printf(" not in chain ");
1158                         record[nextp].type |= reft;
1159                     } else if (nextp != 0) {
1160                         printf(" next in chain");
1161                         if (fix) {
1162                             printf(", unchaining");
1163                             *nextpp = 0;
1164                             writeentry(record[i].addr, &vlentry);
1165                         }
1166                     }
1167                     printf(")");
1168                 }
1169                 printf("\n");
1170             }
1171         
1172             for (j = 0; j < NMAXNSERVERS; j++) {
1173                 if ((vlentry.serverNumber[j] != 255)
1174                     && (serveraddrs[vlentry.serverNumber[j]] == 0)) {
1175                     printf
1176                         ("Volume '%s', index %d points to empty server entry %d\n",
1177                          vlentry.name, j, vlentry.serverNumber[j]);
1178                 }
1179             }
1180         
1181             if (record[i].type & 0xffff0f00)
1182                 printf
1183                     ("Volume '%s' id %u also found on other chains (0x%x)\n",
1184                      vlentry.name, vlentry.volumeId[0], record[i].type);
1185             
1186             /* A free entry */
1187         } else if (record[i].type & FR) {
1188             if (!(record[i].type & FRC))
1189                 printf("Free vlentry at %u not on free chain\n",
1190                        record[i].addr);
1191             
1192             if (record[i].type & 0xfffffdf0)
1193                 printf
1194                     ("Free vlentry at %u also found on other chains (0x%x)\n",
1195                      record[i].addr, record[i].type);
1196             
1197             /* A multihomed entry */
1198         } else if (record[i].type & MH) {
1199             if (!(record[i].type & MHC))
1200                 printf("Multihomed block at %u is orphaned\n",
1201                        record[i].addr);
1202             
1203             if (record[i].type & 0xfffffef0)
1204                 printf
1205                     ("Multihomed block at %u also found on other chains (0x%x)\n",
1206                      record[i].addr, record[i].type);
1207             
1208         } else {
1209             printf("Unknown entry type at %u (0x%x)\n", record[i].addr,
1210                    record[i].type);
1211         }
1212     }
1213
1214     /* By the time we get here, unchained entries are really unchained */
1215     printf("Scanning %u entries for possible repairs\n", maxentries);
1216     for (i = 0; i < maxentries; i++) {
1217         if (record[i].type & VL) {
1218             readentry(record[i].addr, &vlentry, &type);
1219             if (!(record[i].type & REFN) && (strlen(vlentry.name)>0)) {
1220                 printf("%d: Record %u (type 0x%x) not in a name chain\n", i, 
1221                        record[i].addr, record[i].type);
1222                 if (fix) {
1223                     if (header.VolnameHash[NameHash(vlentry.name)] == 0)
1224                         header.VolnameHash[NameHash(vlentry.name)] = record[i].addr;
1225                     else
1226                         FixBad(i, header.VolnameHash[NameHash(vlentry.name)], NH, record[i].addr, &vlentry);
1227                 }
1228             }
1229             if (vlentry.volumeId[0] && !(record[i].type & REFRW)) {
1230                 printf("%d: Record %u (type 0x%x) not in a RW chain\n", i,
1231                        record[i].addr, record[i].type);
1232                 if (fix) {
1233                     if (header.VolidHash[0][IdHash(vlentry.volumeId[0])] == 0)
1234                         header.VolidHash[0][IdHash(vlentry.volumeId[0])] = record[i].addr;
1235                     else
1236                         FixBad(i, header.VolidHash[0][IdHash(vlentry.volumeId[0])], RWH, record[i].addr, &vlentry);
1237                 }
1238             }
1239             if (vlentry.volumeId[1] && !(record[i].type & REFRO)) {
1240                 printf("%d: Record %u (type 0x%x) not in a RO chain\n", i, 
1241                        record[i].addr, record[i].type);
1242                 if (fix) {
1243                     if (header.VolidHash[1][IdHash(vlentry.volumeId[1])] == 0)
1244                         header.VolidHash[1][IdHash(vlentry.volumeId[1])] = record[i].addr;
1245                     else
1246                         FixBad(i, header.VolidHash[1][IdHash(vlentry.volumeId[1])], ROH, record[i].addr, &vlentry);
1247                 }
1248             }
1249             if (vlentry.volumeId[2] && !(record[i].type & REFBK)) {
1250                 printf("%d: Record %u (type 0x%x) not in a BK chain\n", i, 
1251                        record[i].addr, record[i].type);
1252                 if (fix) {
1253                     if (header.VolidHash[2][IdHash(vlentry.volumeId[2])] == 0)
1254                         header.VolidHash[2][IdHash(vlentry.volumeId[2])] = record[i].addr;
1255                     else
1256                         FixBad(i, header.VolidHash[2][IdHash(vlentry.volumeId[2])], BKH, record[i].addr, &vlentry);
1257                 }
1258
1259             }
1260         }
1261     }
1262     if (fix) 
1263         writeheader(&header);
1264
1265     return 0;
1266 }
1267
1268 int
1269 main(int argc, char **argv)
1270 {
1271     struct cmd_syndesc *ts;
1272
1273     setlinebuf(stdout);
1274
1275     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "vldb check");
1276     cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "vldb_file");
1277     cmd_AddParm(ts, "-uheader", CMD_FLAG, CMD_OPTIONAL,
1278                 "Display UBIK header");
1279     cmd_AddParm(ts, "-vheader", CMD_FLAG, CMD_OPTIONAL,
1280                 "Display VLDB header");
1281     cmd_AddParm(ts, "-servers", CMD_FLAG, CMD_OPTIONAL,
1282                 "Display server list");
1283     cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
1284     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
1285     cmd_AddParm(ts, "-fix", CMD_FLAG, CMD_OPTIONAL, "attempt to patch the database (potentially dangerous)");
1286
1287     return cmd_Dispatch(argc, argv);
1288 }