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