Commit | Line | Data |
---|---|---|
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 | ||
66 | int fd; | |
67 | int listentries, listservers, listheader, listuheader, verbose, quiet; | |
68 | ||
69 | int fix = 0; | |
70 | int passes = 0; | |
71 | /* if quiet, don't send anything to stdout */ | |
72 | int quiet = 0; | |
73 | /* error level. 0 = no error, 1 = warning, 2 = error, 4 = fatal */ | |
74 | int error_level = 0; | |
75 | ||
76 | struct er { | |
77 | long addr; | |
78 | int type; | |
79 | } *record; | |
80 | afs_int32 maxentries; | |
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 */ | |
84 | ||
85 | struct 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 */ | |
92 | void | |
93 | quiet_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 */ | |
103 | void | |
104 | log_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 | ||
116 | int | |
117 | readUbikHeader(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 | ||
159 | int | |
160 | vldbio(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 | ||
185 | char * | |
186 | vtype(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 | ||
201 | afs_int32 | |
202 | NameHash(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 | ||
213 | afs_int32 | |
214 | IdHash(afs_uint32 volid) | |
215 | { | |
216 | return (volid % HASHSIZE); | |
217 | } | |
218 | ||
219 | #define LEGALCHARS ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" | |
220 | int | |
221 | InvalidVolname(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 | ||
235 | int | |
236 | validVolumeAddr(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 | ||
249 | void | |
250 | readheader(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 | ||
315 | void | |
316 | writeheader(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 | ||
348 | void | |
349 | readMH(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 | ||
381 | void | |
382 | readentry(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 | ||
473 | void | |
474 | writeMH(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 | ||
504 | void | |
505 | writeentry(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 | */ | |
533 | void | |
534 | ReadAllEntries(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 | */ | |
630 | void | |
631 | FollowNameHash(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 | */ | |
715 | void | |
716 | FollowIdHash(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 | */ | |
806 | void | |
807 | FollowFreeChain(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 | */ | |
862 | void | |
863 | CheckIpAddrs(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 | ||
1072 | char * | |
1073 | nameForAddr(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 | ||
1106 | void | |
1107 | reportHashChanges(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 | */ | |
1149 | void | |
1150 | removeCrossLinkedAddresses(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 | ||
1181 | int | |
1182 | WorkerBee(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 | ||
1618 | int | |
1619 | main(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 | } |