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