2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #include <WINNT/afsevent.h>
20 #include <afs/afsutil.h>
26 /* Read a VLDB file and verify it for correctness */
28 #define VL 0x001 /* good volume entry */
29 #define FR 0x002 /* free volume entry */
30 #define MH 0x004 /* multi-homed entry */
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 */
37 #define MHC 0x100 /* on multihomed chain */
38 #define FRC 0x200 /* on free chain */
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) */
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) */
50 #define MISRWH 0x100000 /* mischained (RW) */
51 #define MISROH 0x200000 /* mischained (RO) */
52 #define MISBKH 0x400000 /* mischained (BK) */
53 #define MISNH 0x800000 /* mischained (name) */
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)
63 #define ADDR(x) ((x)/sizeof(struct nvlentry))
64 #define OFFSET(addr) ((addr) + HDRSIZE)
67 int listentries
, listservers
, listheader
, listuheader
, verbose
, quiet
;
71 /* if quiet, don't send anything to stdout */
73 /* error level. 0 = no error, 1 = warning, 2 = error, 4 = fatal */
81 int serveraddrs
[MAXSERVERID
+ 2];
82 u_char serverxref
[MAXSERVERID
+ 2]; /**< to resolve cross-linked mh entries */
83 int serverref
[MAXSERVERID
+ 2]; /**< which addrs are referenced by vl entries */
86 afs_uint32 addr
; /**< vldb file record */
87 char orphan
[VL_MHSRV_PERBLK
]; /**< unreferenced mh enties */
88 } mhinfo
[VL_MAX_ADDREXTBLKS
];
91 /* Used to control what goes to stdout based on quiet flag */
93 quiet_println(const char *fmt
,...) {
97 vfprintf(stdout
, fmt
, args
);
102 /* Used to set the error level and ship messages to stderr */
104 log_error(int eval
, const char *fmt
, ...)
107 if (error_level
< eval
) error_level
= eval
; /* bump up the severity */
109 vfprintf(stderr
, fmt
, args
);
112 if (error_level
== VLDB_CHECK_FATAL
) exit(VLDB_CHECK_FATAL
);
120 struct ubik_hdr uheader
;
122 offset
= lseek(fd
, 0, 0);
124 log_error(VLDB_CHECK_FATAL
,"error: lseek to 0 failed: %d %d\n", offset
, errno
);
125 return (VLDB_CHECK_FATAL
);
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
,
133 return (VLDB_CHECK_FATAL
);
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
);
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
);
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
,
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
,
160 vldbio(int position
, void *buffer
, int size
, int rdwr
)
164 /* seek to the correct spot. skip ubik stuff */
165 p
= OFFSET(position
);
166 offset
= lseek(fd
, p
, 0);
168 log_error(VLDB_CHECK_FATAL
,"error: lseek to %d failed: %d %d\n", p
, offset
, errno
);
173 r
= write(fd
, buffer
, size
);
175 r
= read(fd
, buffer
, size
);
178 log_error(VLDB_CHECK_FATAL
,"error: %s of %d bytes failed: %d %d\n", rdwr
==1?"write":"read",
202 NameHash(char *volname
)
208 for (vchar
= volname
+ strlen(volname
) - 1; vchar
>= volname
; vchar
--)
209 hash
= (hash
* 63) + (*((unsigned char *)vchar
) - 63);
210 return (hash
% HASHSIZE
);
214 IdHash(afs_uint32 volid
)
216 return (volid
% HASHSIZE
);
219 #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
221 InvalidVolname(char *volname
)
227 slen
= strlen(volname
);
228 if (slen
>= VL_MAXNAMELEN
)
232 return (slen
!= strspn(volname
, map
));
236 validVolumeAddr(afs_uint32 fileOffset
)
238 if (ADDR(fileOffset
) >= maxentries
) {
239 /* Are we in range */
243 * We cannot test whether the offset is aligned
244 * since the vl entries are not in a regular array
250 readheader(struct vlheader
*headerp
)
254 vldbread(0, (char *)headerp
, sizeof(*headerp
));
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]);
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
]);
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
,
301 quiet_println(" server ip addr table: size = %d entries\n",
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",
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
));
316 writeheader(struct vlheader
*headerp
)
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]);
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
]);
345 vldbwrite(0, (char *)headerp
, sizeof(*headerp
));
349 readMH(afs_uint32 addr
, int block
, struct extentaddr
*mhblockP
)
352 struct extentaddr
*e
;
354 vldbread(addr
, (char *)mhblockP
, VL_ADDREXTBLK_SIZE
);
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
);
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
]);
370 for (i
= 1; i
< VL_MHSRV_PERBLK
; i
++) {
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
]);
382 readentry(afs_int32 addr
, struct nvlentry
*vlentryp
, afs_int32
*type
)
386 vldbread(addr
, (char *)vlentryp
, sizeof(*vlentryp
));
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
];
404 if (vlentryp
->flags
== VLCONTBLOCK
) {
406 } else if (vlentryp
->flags
== VLFREE
) {
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");
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");
441 /* all bits not covered by VLF_* and VLOP_* constants */
442 if (vlentryp
->flags
& 0xffff8e0f)
443 quiet_println(" errorflag(0x%x)", vlentryp
->flags
);
445 quiet_println(" LockAfsId = %d\n", vlentryp
->LockAfsId
);
446 quiet_println(" LockTimestamp = %d\n", vlentryp
->LockTimestamp
);
447 quiet_println(" cloneId = %u\n", vlentryp
->cloneId
);
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");
474 writeMH(afs_int32 addr
, int block
, struct extentaddr
*mhblockP
)
477 struct extentaddr
*e
;
480 quiet_println("Writing back MH block % at addr %u\n", block
, addr
);
482 mhblockP
->ex_hdrflags
= htonl(mhblockP
->ex_hdrflags
);
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.
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
]);
493 for (i
= 1; i
< VL_MHSRV_PERBLK
; 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
]);
501 vldbwrite(addr
, (char *)mhblockP
, VL_ADDREXTBLK_SIZE
);
505 writeentry(afs_int32 addr
, struct nvlentry
*vlentryp
)
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
] ;
525 vldbwrite(addr
, (char *)vlentryp
, sizeof(*vlentryp
));
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.
534 ReadAllEntries(struct vlheader
*header
)
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
;
541 afs_uint32 entrysize
= 0;
542 afs_uint32 maxvolid
= 0;
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
) {
548 /* Remember the highest volume id */
549 readentry(addr
, &vlentry
, &type
);
552 for (i
= 0; i
< MAXTYPES
; i
++)
553 if (maxvolid
< vlentry
.volumeId
[i
])
554 maxvolid
= vlentry
.volumeId
[i
];
557 for (j
= 0; j
< NMAXNSERVERS
; j
++) {
558 if (vlentry
.serverNumber
[j
] == 255)
560 if (vlentry
.serverFlags
[j
] & (VLSF_ROVOL
| VLSF_NEWREPSITE
)) {
564 if (vlentry
.serverFlags
[j
] & VLSF_RWVOL
) {
566 if (vlentry
.flags
& VLF_BACKEXISTS
)
570 if (!vlentry
.serverFlags
[j
]) {
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
);
581 (" index %d : serverNumber %d : serverPartition %d : serverFlag %d\n",
582 j
, vlentry
.serverNumber
[j
], vlentry
.serverPartition
[j
],
583 vlentry
.serverFlags
[j
]);
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",
593 record
[rindex
].addr
= addr
;
594 record
[rindex
].type
= type
;
596 /* Determine entrysize and keep count */
598 entrysize
= sizeof(vlentry
);
600 } else if (type
== FR
) {
601 entrysize
= sizeof(vlentry
);
603 } else if (type
== MH
) {
604 entrysize
= VL_ADDREXTBLK_SIZE
;
607 log_error(VLDB_CHECK_ERROR
, "address %u (offset 0x%0x): Unknown entry. Aborting\n", addr
, OFFSET(addr
));
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
,
618 /* Check the maxmimum volume id in the header */
619 if (maxvolid
!= header
->vital_header
.MaxVolumeId
- 1)
621 ("Header's maximum volume id is %u and largest id found in VLDB is %u\n",
622 header
->vital_header
.MaxVolumeId
, maxvolid
);
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.
631 FollowNameHash(struct vlheader
*header
)
633 int count
= 0, longest
= 0, shortest
= -1, chainlength
;
634 struct nvlentry vlentry
;
636 afs_int32 i
, type
, rindex
;
638 /* Now follow the Name Hash Table */
639 if (verbose
) quiet_println("Check Volume Name Hash\n");
640 for (i
= 0; i
< HASHSIZE
; i
++) {
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
]);
649 for (addr
= header
->VolnameHash
[i
]; addr
; addr
= vlentry
.nextNameHash
) {
650 readentry(addr
, &vlentry
, &type
);
652 log_error(VLDB_CHECK_ERROR
,"address %u (offset 0x%0x): Name Hash %d: Not a vlentry\n",
653 addr
, OFFSET(addr
), i
);
660 * we know that the address is valid because we
661 * checked it either above or below
663 if (record
[rindex
].addr
!= addr
&& record
[rindex
].addr
) {
665 (VLDB_CHECK_ERROR
,"INTERNAL VLDB_CHECK_ERROR: addresses %ld and %u use same record slot %d\n",
666 record
[rindex
].addr
, addr
, rindex
);
668 if (record
[rindex
].type
& NH
) {
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
;
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
;
683 record
[rindex
].type
|= NH
;
684 record
[rindex
].type
|= REFN
;
689 /* Hash the name and check if in correct hash table */
690 if (NameHash(vlentry
.name
) != i
) {
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
;
697 if (chainlength
> longest
)
698 longest
= chainlength
;
699 if ((shortest
== -1) || (chainlength
< shortest
))
700 shortest
= chainlength
;
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
));
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.
716 FollowIdHash(struct vlheader
*header
)
718 int count
= 0, longest
= 0, shortest
= -1, chainlength
;
719 struct nvlentry vlentry
;
721 afs_int32 i
, j
, hash
, type
, rindex
, ref
, badref
, badhash
;
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
));
733 for (j
= 0; j
< HASHSIZE
; j
++) {
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
]);
741 for (addr
= header
->VolidHash
[i
][j
]; addr
;
742 addr
= vlentry
.nextIdHash
[i
]) {
743 readentry(addr
, &vlentry
, &type
);
746 (VLDB_CHECK_ERROR
,"address %u (offset 0x%0x): %s Id Hash %d: Not a vlentry\n",
747 addr
, OFFSET(addr
), vtype(i
), j
);
752 if (record
[rindex
].addr
!= addr
&& record
[rindex
].addr
) {
754 (VLDB_CHECK_ERROR
,"INTERNAL VLDB_CHECK_ERROR: addresses %ld and %u use same record slot %d\n",
755 record
[rindex
].addr
, addr
, rindex
);
757 if (record
[rindex
].type
& hash
) {
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
;
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
;
772 record
[rindex
].type
|= hash
;
773 record
[rindex
].type
|= ref
;
778 /* Hash the id and check if in correct hash table */
779 if (IdHash(vlentry
.volumeId
[i
]) != j
) {
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
;
788 if (chainlength
> longest
)
789 longest
= chainlength
;
790 if ((shortest
== -1) || (chainlength
< shortest
))
791 shortest
= chainlength
;
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
));
803 * Follow the free chain.
804 * Record we found it in the free chain within the record array.
807 FollowFreeChain(struct vlheader
*header
)
810 struct nvlentry vlentry
;
812 afs_int32 type
, rindex
;
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
);
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
);
826 rindex
= addr
/ sizeof(vlentry
);
827 if (record
[rindex
].addr
!= addr
&& record
[rindex
].addr
) {
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
);
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",
837 record
[rindex
].type
|= FRC
;
842 quiet_println("%d entries on free chain\n", count
);
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.
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.
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.
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.
863 CheckIpAddrs(struct vlheader
*header
)
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
;
874 memset(&nulluuid
, 0, sizeof(nulluuid
));
877 quiet_println("Check Multihomed blocks\n");
880 /* Read the first MH block and from it, gather the
881 * addresses of all the mh blocks.
883 readMH(header
->SIT
, 0, MHblock
);
884 if (MHblock
->ex_hdrflags
!= VLCONTBLOCK
) {
886 (VLDB_CHECK_ERROR
,"address %u (offset 0x%0x): Multihomed Block 0: Not a multihomed block\n",
887 header
->SIT
, OFFSET(header
->SIT
));
890 for (i
= 0; i
< VL_MAX_ADDREXTBLKS
; i
++) {
891 mhinfo
[i
].addr
= MHblock
->ex_contaddrs
[i
];
894 if (header
->SIT
!= mhinfo
[0].addr
) {
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
);
900 /* Now read each MH block and record it in the record array */
901 for (i
= 0; i
< VL_MAX_ADDREXTBLKS
; i
++) {
905 readMH(mhinfo
[i
].addr
, i
, MHblock
);
906 if (MHblock
->ex_hdrflags
!= VLCONTBLOCK
) {
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
);
912 rindex
= mhinfo
[i
].addr
/ sizeof(vlentry
);
913 if (record
[rindex
].addr
!= mhinfo
[i
].addr
&& record
[rindex
].addr
) {
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
);
918 if (record
[rindex
].type
& FRC
) {
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
);
924 record
[rindex
].type
|= MHC
;
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).
933 for (j
= 1; j
< VL_MHSRV_PERBLK
; j
++) {
934 int first_ipindex
= -1;
935 e
= (struct extentaddr
*)&(MHblock
[j
]);
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
;
946 serverxref
[ipindex
] = first_ipindex
;
950 ipindex
= first_ipindex
;
952 serveraddrs
[ipindex
] = -1;
954 if (memcmp(&e
->ex_hostuuid
, &nulluuid
, sizeof(afsUUID
)) == 0) {
957 (VLDB_CHECK_ERROR
,"Server Addrs index %d references null MH block %d, index %d\n",
959 serveraddrs
[ipindex
] = 0; /* avoids printing 2nd error below */
964 /* Step through each ip address and count the good addresses */
966 for (m
= 0; m
< VL_MAXIPADDRS_PERMH
; m
++) {
971 /* If we found any good ip addresses, mark it in the serveraddrs record */
975 mhinfo
[i
].orphan
[j
] = 1;
977 (VLDB_CHECK_ERROR
,"MH block %d, index %d: Not referenced by server addrs\n",
980 serveraddrs
[ipindex
] = ipaddrs
; /* It is good */
984 if (listservers
&& ipaddrs
) {
985 quiet_println("MH block %d, index %d:", i
, j
);
986 for (m
= 0; m
< VL_MAXIPADDRS_PERMH
; m
++) {
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));
999 * if (mhentries != MHblock->ex_count) {
1000 * quiet_println("MH blocks says it has %d entries (found %d)\n",
1001 * MHblock->ex_count, mhentries);
1007 quiet_println("%d multihomed blocks\n", mhblocks
);
1009 /* Check the server addresses */
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) {
1017 if (((header
->IpMappedAddr
[i
] & 0x00ff0000) >> 16) >
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))
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) {
1032 (VLDB_CHECK_WARNING
,"warning: IP Addr for entry %d: Multihome entry has no ip addresses\n",
1036 if (serverxref
[i
] != BADSERVERID
) {
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),
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
]];
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));
1054 serveraddrs
[i
] = 1; /* It is good */
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));
1066 quiet_println("%d simple entries, %d multihomed entries, Total = %d\n",
1067 regentries
, mhentries
, mhentries
+ regentries
);
1073 nameForAddr(afs_uint32 addr
, int hashtype
, afs_uint32
*hash
, char *buffer
)
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.
1082 struct nvlentry entry
;
1084 /* Distinguished, invalid, hash */
1087 } else if (!validVolumeAddr(addr
)) {
1088 /* Different, invalid, hash */
1092 readentry(addr
, &entry
, &type
);
1097 if (hashtype
>= MAXTYPES
) {
1098 *hash
= NameHash(entry
.name
);
1100 *hash
= IdHash(entry
.volumeId
[hashtype
]);
1102 sprintf(buffer
, "for '%s'", entry
.name
);
1107 reportHashChanges(struct vlheader
*header
, afs_uint32 oldnamehash
[HASHSIZE
], afs_uint32 oldidhash
[MAXTYPES
][HASHSIZE
])
1110 afs_uint32 oldhash
, newhash
;
1111 char oldNameBuffer
[10 + VL_MAXNAMELEN
];
1112 char newNameBuffer
[10 + VL_MAXNAMELEN
];
1113 char *oldname
, *newname
;
1115 * report hash changes
1118 for (i
= 0; i
< HASHSIZE
; i
++) {
1119 if (oldnamehash
[i
] != header
->VolnameHash
[i
]) {
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
);
1127 for (j
= 0; j
< MAXTYPES
; j
++) {
1128 if (oldidhash
[j
][i
] != header
->VolidHash
[j
][i
]) {
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
);
1141 * Remove unreferenced, duplicate multi-home address indices.
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.
1147 * @param[inout] header the vldb header to be updated.
1150 removeCrossLinkedAddresses(struct vlheader
*header
)
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",
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",
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",
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;
1182 WorkerBee(struct cmd_syndesc
*as
, void *arock
)
1186 struct vlheader header
;
1187 struct nvlentry vlentry
, vlentry2
;
1189 afs_uint32 oldnamehash
[HASHSIZE
];
1190 afs_uint32 oldidhash
[MAXTYPES
][HASHSIZE
];
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 */
1203 if (quiet
&& (verbose
|| listuheader
|| listheader
||listservers \
1205 log_error(VLDB_CHECK_FATAL
," -quiet cannot be used other display flags\n");
1206 return VLDB_CHECK_FATAL
;
1210 /* open the vldb database file */
1211 fd
= open(dbfile
, (fix
> 0)?O_RDWR
:O_RDONLY
, 0);
1213 log_error(VLDB_CHECK_FATAL
,"can't open file '%s'. error = %d\n", dbfile
, errno
);
1217 /* read the ubik header and the vldb database header */
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
;
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
;
1234 /* Will fill in the record array of entries it found */
1235 ReadAllEntries(&header
);
1236 listentries
= 0; /* Listed all the entries */
1238 /* Check the multihomed blocks for valid entries as well as
1239 * the IpMappedAddrs array in the header for valid entries.
1241 CheckIpAddrs(&header
);
1243 /* Follow the hash tables */
1244 FollowNameHash(&header
);
1245 FollowIdHash(&header
);
1247 /* Follow the chain of free entries */
1248 FollowFreeChain(&header
);
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).
1255 quiet_println("Verify each volume entry\n");
1256 for (i
= 0; i
< maxentries
; i
++) {
1261 if (record
[i
].type
== 0)
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.
1267 if (record
[i
].type
& VL
) {
1269 int foundbroken
= 0;
1272 readentry(record
[i
].addr
, &vlentry
, &type
);
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]);
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]);
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]);
1286 if (!(record
[i
].type
& NH
)) {
1287 hash
= NameHash(vlentry
.name
);
1293 if (vlentry
.volumeId
[0] && !(record
[i
].type
& RWH
)) {
1294 hash
= IdHash(vlentry
.volumeId
[0]);
1296 sprintf(volidbuf
, "id %u ", vlentry
.volumeId
[0]);
1300 if (vlentry
.volumeId
[1] && !(record
[i
].type
& ROH
)) {
1301 hash
= IdHash(vlentry
.volumeId
[1]);
1303 sprintf(volidbuf
, "id %u ", vlentry
.volumeId
[1]);
1307 if (vlentry
.volumeId
[2] && !(record
[i
].type
& BKH
)) {
1308 hash
= IdHash(vlentry
.volumeId
[2]);
1310 sprintf(volidbuf
, "id %u ", vlentry
.volumeId
[2]);
1314 if (!validVolumeAddr(vlentry
.nextNameHash
) ||
1315 record
[ADDR(vlentry
.nextNameHash
)].type
& MULTN
) {
1316 hash
= NameHash(vlentry
.name
);
1319 if (validVolumeAddr(vlentry
.nextNameHash
)) {
1320 readentry(vlentry
.nextNameHash
, &vlentry2
, &type
);
1321 nexthash
= NameHash(vlentry2
.name
);
1323 nexthash
= 0xFFFFFFFF;
1325 if (hash
!= nexthash
)
1329 if (!validVolumeAddr(vlentry
.nextIdHash
[0]) ||
1330 record
[ADDR(vlentry
.nextIdHash
[0])].type
& MULTRW
) {
1331 hash
= IdHash(vlentry
.volumeId
[0]);
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]);
1338 nexthash
= 0xFFFFFFFF;
1340 if (hash
!= nexthash
)
1344 if (!validVolumeAddr(vlentry
.nextIdHash
[1]) ||
1345 record
[ADDR(vlentry
.nextIdHash
[1])].type
& MULTRO
) {
1346 hash
= IdHash(vlentry
.volumeId
[1]);
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]);
1353 nexthash
= 0xFFFFFFFF;
1355 if (hash
!= nexthash
)
1359 if (!validVolumeAddr(vlentry
.nextIdHash
[2]) ||
1360 record
[ADDR(vlentry
.nextIdHash
[2])].type
& MULTBK
) {
1361 hash
= IdHash(vlentry
.volumeId
[2]);
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]);
1368 nexthash
= 0xFFFFFFFF;
1370 if (hash
!= nexthash
)
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
);
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) {
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
) {
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
]]);
1401 if (record
[i
].type
& 0xffff0f00)
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
);
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
));
1412 if (record
[i
].type
& 0xfffffdf0)
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
);
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
));
1423 if (record
[i
].type
& 0xfffffef0)
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
);
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
);
1436 * If we are fixing we will rebuild the free and hash lists from the ground up.
1438 header
.vital_header
.freePtr
= 0;
1439 memcpy(oldnamehash
, header
.VolnameHash
, sizeof(oldnamehash
));
1440 memset(header
.VolnameHash
, 0, sizeof(header
.VolnameHash
));
1442 memcpy(oldidhash
, header
.VolidHash
, sizeof(oldidhash
));
1443 memset(header
.VolidHash
, 0, sizeof(header
.VolidHash
));
1444 quiet_println("Rebuilding %u entries\n", maxentries
);
1446 quiet_println("Scanning %u entries for possible repairs\n", maxentries
);
1448 for (i
= 0; i
< maxentries
; i
++) {
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
);
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
);
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
);
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
);
1469 afs_uint32 oldhash
, newhash
;
1470 char oldNameBuffer
[10 + VL_MAXNAMELEN
];
1471 char newNameBuffer
[10 + VL_MAXNAMELEN
];
1472 char *oldname
, *newname
;
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
);
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
);
1490 * Put the current hash table contexts into our 'next'
1491 * and our address into the hash table.
1493 hash
= NameHash(vlentry
.name
);
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
]))) {
1502 * That is, only report if we are verbose
1503 * or the hash is changing (and one side wasn't NULL
1505 quiet_println("FIX: Name hash link for '%s' was %s, is now %s\n",
1506 vlentry
.name
, oldname
, newname
);
1510 vlentry
.nextNameHash
= header
.VolnameHash
[hash
];
1511 header
.VolnameHash
[hash
] = record
[i
].addr
;
1513 for (j
= 0; j
< MAXTYPES
; j
++) {
1515 if (0 == vlentry
.volumeId
[j
]) {
1517 * No volume of that type. Continue
1521 hash
= IdHash(vlentry
.volumeId
[j
]);
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
);
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",
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",
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",
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
;
1564 vlentry
.nextIdHash
[j
] = header
.VolidHash
[j
][hash
];
1565 header
.VolidHash
[j
][hash
] = record
[i
].addr
;
1567 writeentry(record
[i
].addr
, &vlentry
);
1570 else if (record
[i
].type
& MH
) {
1572 char mhblock
[VL_ADDREXTBLK_SIZE
];
1573 struct extentaddr
*MHblock
= (struct extentaddr
*)mhblock
;
1576 for (block
= 0; block
< VL_MAX_ADDREXTBLKS
; block
++) {
1577 if (mhinfo
[block
].addr
== record
[i
].addr
)
1580 if (block
== VL_MAX_ADDREXTBLKS
) {
1581 continue; /* skip orphaned extent block */
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",
1588 memset(&(MHblock
[index
]), 0, sizeof(struct extentaddr
));
1591 writeMH(record
[i
].addr
, block
, MHblock
);
1593 } else if (record
[i
].type
& FR
) {
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) {
1600 ("FIX: Putting free entry on the free chain: addr=%lu (offset 0x%0x)\n",
1601 record
[i
].addr
, OFFSET(record
[i
].addr
));
1603 writeentry(record
[i
].addr
, &vlentry
);
1608 reportHashChanges(&header
, oldnamehash
, oldidhash
);
1609 removeCrossLinkedAddresses(&header
);
1610 writeheader(&header
);
1619 main(int argc
, char **argv
)
1621 struct cmd_syndesc
*ts
;
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)");
1638 return cmd_Dispatch(argc
, argv
);