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