2ea33f933c14ba420e46902b8085a22ac30c8c74
[openafs.git] / src / ptserver / db_verify.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10
11 #include <afsconfig.h>
12 #include <afs/param.h>
13
14
15 /*
16  *                      (3) Define a structure, idused, instead of an
17  *                          array of long integers, idmap, to count group
18  *                          memberships. These structures are on a linked
19  *                          list, with each structure containing IDCOUNT
20  *                          slots for id's.
21  *                      (4) Add new functions to processs the structure
22  *                          described above:
23  *                             zeromap(), idcount(), inccount().
24  *                      (5) Add code, primarily in WalkNextChain():
25  *                           1. Test id's, allowing groups within groups.
26  *                           2. Count the membership list for supergroups,
27  *                              and follow the continuation chain for
28  *                              supergroups.
29  *                      (6) Add fprintf statements for various error
30  *                          conditions.
31  */
32
33 #include <afs/stds.h>
34 #include <sys/types.h>
35 #ifdef AFS_NT40_ENV
36 #include <winsock2.h>
37 #include <WINNT/afsevent.h>
38 #include <io.h>
39 #else
40 #include <netdb.h>
41 #include <netinet/in.h>
42 #include <sys/file.h>
43 #endif
44 #include <stdio.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <afs/cellconfig.h>
49 #include <afs/afsutil.h>
50 #include <ubik.h>
51 #include <afs/cmd.h>
52 #include <afs/com_err.h>
53
54 #include "ptint.h"
55 #include "pterror.h"
56 #include "ptserver.h"
57 #include "ptuser.h"
58 #include "display.h"
59
60 struct prheader cheader;
61 int fd;
62 const char *pr_dbaseName;
63 char *whoami = "db_verify";
64 #define UBIK_HEADERSIZE 64
65
66 afs_int32
67 printheader(struct prheader *h)
68 {
69     printf("Version           = %d\n", ntohl(h->version));
70     printf("Header Size       = %d\n", ntohl(h->headerSize));
71     printf("Free Ptr          = 0x%x\n", ntohl(h->freePtr));
72     printf("EOF  Ptr          = 0x%x\n", ntohl(h->eofPtr));
73     printf("Max Group     ID  = %d\n", ntohl(h->maxGroup));
74     printf("Max User      ID  = %d\n", ntohl(h->maxID));
75     printf("Max Foreign   ID  = %d\n", ntohl(h->maxForeign));
76 /* printf("Max Sub/Super ID  = %d\n", ntohl(h->maxInst)); */
77     printf("Orphaned groups   = %d\n", ntohl(h->orphan));
78     printf("User      Count   = %d\n", ntohl(h->usercount));
79     printf("Group     Count   = %d\n", ntohl(h->groupcount));
80 /* printf("Foreign   Count   = %d\n", ntohl(h->foreigncount)); NYI */
81 /* printf("Sub/super Count   = %d\n", ntohl(h->instcount));    NYI */
82     printf("Name Hash         = %d buckets\n", HASHSIZE);
83     printf("ID   Hash         = %d buckets\n", HASHSIZE);
84     return 0;
85 }
86
87 static afs_int32
88 pr_Read(afs_int32 pos, void *buff, afs_int32 len)
89 {
90     afs_int32 code;
91
92     code = lseek(fd, UBIK_HEADERSIZE + pos, 0);
93     if (code == -1)
94         return errno;
95
96     code = read(fd, buff, len);
97     if (code != len)
98         return -1;
99     if (code == -1)
100         return errno;
101
102     return 0;
103 }
104
105 /* InitDB ()
106  *   Initializes the a transaction on the database and reads the header into
107  * the static variable cheader.  If successful it returns a read-locked
108  * transaction.  If ubik reports that cached database info should be up to date
109  * the cheader structure is not re-read from the ubik.
110  */
111
112 afs_int32
113 ReadHeader(void)
114 {
115     afs_int32 code;
116
117     code = pr_Read(0, (char *)&cheader, sizeof(cheader));
118     if (code) {
119         afs_com_err(whoami, code, "couldn't read header");
120         return code;
121     }
122     /* Check and see if database exists and is approximately OK. */
123     if (ntohl(cheader.headerSize) != sizeof(cheader)
124         || ntohl(cheader.eofPtr) == 0) {
125         if (code)
126             return code;
127         afs_com_err(whoami, PRDBBAD, "header is bad");
128         return PRDBBAD;
129     }
130     return 0;
131 }
132
133 static afs_int32
134 IDHash(afs_int32 x)
135 {
136     /* returns hash bucket for x */
137     return ((abs(x)) % HASHSIZE);
138 }
139
140 static afs_int32
141 NameHash(register char *aname)
142 {
143     /* returns hash bucket for aname */
144     register unsigned int hash = 0;
145     register int i;
146 /* stolen directly from the HashString function in the vol package */
147     for (i = strlen(aname), aname += i - 1; i--; aname--)
148         hash = (hash * 31) + (*(unsigned char *)aname - 31);
149     return (hash % HASHSIZE);
150 }
151
152 #define MAP_NAMEHASH 1
153 #define MAP_IDHASH 2
154 #define MAP_HASHES (MAP_NAMEHASH | MAP_IDHASH)
155 #define MAP_CONT 4
156 #define MAP_FREE 8
157 #define MAP_OWNED 0x10
158 #define MAP_RECREATE 0x20
159
160 struct misc_data {
161     int nEntries;               /* number of database entries */
162     int anon;                   /* found anonymous Id */
163     afs_int32 maxId;            /* user */
164     afs_int32 minId;            /* group */
165     afs_int32 maxForId;         /* foreign user id */
166 #if defined(SUPERGROUPS)
167 #define IDCOUNT 512
168     struct idused {
169         int idstart;
170         afs_int32 idcount[IDCOUNT];
171         struct idused *idnext;
172     } *idmap;
173 #else
174     int idRange;                /* number of ids in map */
175     afs_int32 *idmap;           /* map of all id's: midId is origin */
176 #endif                          /* SUPERGROUPS */
177     int nusers;                 /* counts of each type */
178     int ngroups;
179     int nforeigns;
180     int ninsts;
181     int ncells;
182     int maxOwnerLength;         /* longest owner chain */
183     int maxContLength;          /* longest chain of cont. blks */
184     int orphanLength;           /* length of orphan list */
185     int freeLength;             /* length of free list */
186     int verbose;
187     int listuheader;
188     int listpheader;
189     int listentries;
190     FILE *recreate;             /* stream for recreate instructions */
191 };
192
193 #if defined(SUPERGROUPS)
194 void zeromap(struct idused *idmap);
195 void inccount(struct idused **idmapp, int id);
196 int idcount(struct idused **idmapp, int id);
197 #endif
198
199 int
200 readUbikHeader(struct misc_data *misc)
201 {
202     int offset, r;
203     struct ubik_hdr uheader;
204
205     offset = lseek(fd, 0, 0);
206     if (offset != 0) {
207         printf("error: lseek to 0 failed: %d %d\n", offset, errno);
208         return (-1);
209     }
210
211     /* now read the info */
212     r = read(fd, &uheader, sizeof(uheader));
213     if (r != sizeof(uheader)) {
214         printf("error: read of %" AFS_SIZET_FMT " bytes failed: %d %d\n",
215                sizeof(uheader), r, errno);
216         return (-1);
217     }
218
219     uheader.magic = ntohl(uheader.magic);
220     uheader.size = ntohl(uheader.size);
221     uheader.version.epoch = ntohl(uheader.version.epoch);
222     uheader.version.counter = ntohl(uheader.version.counter);
223
224     if (misc->listuheader) {
225         printf("Ubik Header\n");
226         printf("   Magic           = 0x%x\n", uheader.magic);
227         printf("   Size            = %u\n", uheader.size);
228         printf("   Version.epoch   = %u\n", uheader.version.epoch);
229         printf("   Version.counter = %u\n", uheader.version.counter);
230     }
231
232     if (uheader.size != UBIK_HEADERSIZE)
233         printf("Ubik header size is %u (should be %u)\n", uheader.size,
234                UBIK_HEADERSIZE);
235     if (uheader.magic != UBIK_MAGIC)
236         printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
237                UBIK_MAGIC);
238
239     return (0);
240 }
241
242 afs_int32
243 ConvertDiskAddress(afs_uint32 ea, int *eiP)
244 {
245     int i;
246
247     *eiP = -1;
248
249     if (ea < sizeof(cheader))
250         return PRDBADDR;
251     if (ea >= ntohl(cheader.eofPtr))
252         return PRDBADDR;
253     ea -= sizeof(cheader);
254     i = ea / sizeof(struct prentry);
255     if (i * sizeof(struct prentry) != ea)
256         return PRDBADDR;
257 /*    if ((i < 0) || (i >= misc->nEntries)) return PRDBADDR; */
258     *eiP = i;
259     return 0;
260 }
261
262 int
263 PrintEntryError(struct misc_data *misc, afs_int32 ea, struct prentry *e, int indent)
264 {
265
266     pr_PrintEntry(stderr, /*net order */ 0, ea, e, indent);
267     return 0;
268 }
269
270 afs_int32
271 WalkHashTable(afs_int32 hashtable[],    /* hash table to walk */
272               int hashType,             /* hash function to use */
273               char map[],               /* one byte per db entry */
274               struct misc_data *misc)   /* stuff to keep track of */
275 {
276     afs_int32 code;
277     int hi;                     /* index in hash table */
278     afs_int32 ea;               /* entry's db addr */
279     int ei;                     /* entry's index */
280     char bit;                   /* bits to check for in map */
281     struct prentry e;
282     afs_int32 next_ea;
283     afs_int32 id;
284     afs_int32 flags;
285     afs_int32 hash;
286
287     bit = hashType;
288
289     for (hi = 0; hi < HASHSIZE; hi++) {
290         ea = 0;
291         next_ea = ntohl(hashtable[hi]);
292         while (next_ea) {
293             code = ConvertDiskAddress(next_ea, &ei);
294             if (code) {
295                 fprintf(stderr, "Bad chain address %d\n", next_ea);
296                 if (ea) {
297                     fprintf(stderr, "Last entry in chain:\n");
298                     if (PrintEntryError(misc, ea, &e, 2))
299                         return PRDBBAD;
300                 }
301                 fprintf(stderr, "Skipping remainder of hash bucket %d\n", hi);
302                 break;
303             }
304             ea = next_ea;
305             code = pr_Read(ea, (char *)&e, sizeof(e));
306             if (code)
307                 return code;
308
309             id = ntohl(e.id);
310
311             if (((e.flags & htonl((PRGRP | PRINST))) == 0)
312                 && (strchr(e.name, '@'))) {
313                 /* Foreign user */
314                 if (id > misc->maxForId)
315                     misc->maxForId = id;
316             } else {
317                 if (id == ANONYMOUSID)
318                     misc->anon++;
319                 else if (id > misc->maxId)
320                     misc->maxId = id;
321                 if (id < misc->minId)
322                     misc->minId = id;
323             }
324
325             switch (hashType) {
326             case MAP_NAMEHASH:
327                 next_ea = ntohl(e.nextName);
328                 hash = NameHash(e.name);
329                 break;
330             case MAP_IDHASH:
331                 next_ea = ntohl(e.nextID);
332                 hash = IDHash(id);
333                 break;
334             default:
335                 fprintf(stderr, "unknown hash table type %d\n", hashType);
336                 return PRBADARG;
337             }
338
339             if (map[ei] & bit) {
340                 fprintf(stderr,
341                         "Entry found twice in hash table: bucket %d\n", hi);
342                 if (hi != hash)
343                     fprintf(stderr, "also in wrong bucket: should be in %d\n",
344                             hash);
345                 if (PrintEntryError(misc, ea, &e, 2))
346                     return PRDBBAD;
347                 break;
348             }
349             map[ei] |= bit;
350
351             flags = ntohl(e.flags);
352             switch (flags & PRTYPE) {
353             case PRFREE:
354                 fprintf(stderr, "ENTRY IS FREE");
355                 goto abort;
356             case PRCONT:
357                 fprintf(stderr, "ENTRY IS CONTINUATION");
358                 goto abort;
359             case PRGRP:
360             case PRUSER:
361                 break;
362             case PRCELL:
363             case PRFOREIGN:
364             case PRINST:
365                 fprintf(stderr, "ENTRY IS unexpected type (flags=0x%x)\n",
366                         flags);
367                 break;
368             default:
369                 fprintf(stderr, "ENTRY IS OF unknown type (flags=0x%x)\n",
370                         flags);
371                 goto abort;
372             }
373
374             if (hash != hi) {
375                 fprintf(stderr, "entry hashed in bucket %d should be %d\n",
376                         hi, hash);
377               abort:
378                 if (PrintEntryError(misc, ea, &e, 2))
379                     return PRDBBAD;
380                 continue;
381             }
382         }
383     }
384     return 0;
385 }
386
387 afs_int32
388 WalkNextChain(char map[],               /* one byte per db entry */
389               struct misc_data *misc,   /* stuff to keep track of */
390               afs_int32 ea, struct prentry *e)
391 {
392     afs_int32 head;
393     int bit;
394     afs_int32 code;
395     struct prentry c;           /* continuation entry */
396     afs_int32 na;               /* next thread */
397     int ni;
398     afs_int32 eid = 0;
399     int count = 0;              /* number of members, set to > 9999 if */
400                                 /* list ends early */
401     int i;
402     int noErrors = 1;
403     int length;                 /* length of chain */
404 #if defined(SUPERGROUPS)
405     int sgcount = 0;            /* number of sgentrys */
406     afs_int32 sghead;
407 #define g (((struct prentryg *)e))
408 #endif
409
410     if (e) {
411         head = ntohl(e->next);
412         eid = ntohl(e->id);
413         bit = MAP_CONT;
414 #if defined(SUPERGROUPS)
415         sghead = ntohl(g->next);
416 #endif
417         for (i = 0; i < PRSIZE; i++) {
418             afs_int32 id = ntohl(e->entries[i]);
419             if (id == PRBADID)
420                 continue;
421             else if (id) {
422                 int eid_s, id_s;
423                 count++;
424                 /* in case the ids are large, convert to pure sign. */
425                 if (id > 0)
426                     id_s = 1;
427                 else
428                     id_s = -1;
429                 if (eid > 0)
430                     eid_s = 1;
431                 else
432                     eid_s = -1;
433 #if defined(SUPERGROUPS)
434                 if (id_s > 0 && eid_s > 0) {
435                     fprintf(stderr,
436                             "User can't be member of user in membership list\n");
437                     if (PrintEntryError(misc, ea, e, 2))
438                         return PRDBBAD;
439                     noErrors = 0;
440                 }
441 #else
442                 if (id_s * eid_s > 0) { /* sign should be different */
443                     fprintf(stderr,
444                             "Bad user/group dicotomy in membership list\n");
445                     if (PrintEntryError(misc, ea, e, 2))
446                         return PRDBBAD;
447                     noErrors = 0;
448                 }
449 #endif /* SUPERGROUPS */
450                 /* count each user as a group, and each group a user is in */
451 #if defined(SUPERGROUPS)
452                 if (!(id < 0 && eid < 0) && (id != ANONYMOUSID))
453                     inccount(&misc->idmap, id);
454 #else
455                 if ((id >= misc->minId) && (id <= misc->maxId)
456                     && (id != ANONYMOUSID))
457                     misc->idmap[id - misc->minId]++;
458 #endif /* SUPERGROUPS */
459             } else if (head)
460                 count = 9999;
461             else
462                 break;
463         }
464 #if defined(SUPERGROUPS)
465         sghead = ntohl(g->nextsg);
466         if ((e->flags & htonl(PRGRP))) {
467             for (i = 0; i < SGSIZE; ++i) {
468                 afs_int32 id = ntohl(g->supergroup[i]);
469                 if (id == PRBADID)
470                     continue;
471                 else if (id) {
472                     if (id > 0) {
473                         fprintf(stderr,
474                                 "User can't be member of supergroup list\n");
475                         if (PrintEntryError(misc, ea, e, 2))
476                             return PRDBBAD;
477                         noErrors = 0;
478                     }
479                     sgcount++;
480                     inccount(&misc->idmap, id);
481                 }
482             }
483         }
484 #endif /* SUPERGROUPS */
485     } else {
486         head = ntohl(cheader.freePtr);
487 #if defined(SUPERGROUPS)
488         sghead = 0;
489 #endif
490         bit = MAP_FREE;
491     }
492
493 #if defined(SUPERGROUPS)
494     length = 0;
495     for (na = sghead; na; na = ntohl(c.next)) {
496         code = ConvertDiskAddress(na, &ni);
497         if (code) {
498             fprintf(stderr, "Bad SGcontinuation ptr %d", na);
499             if (PrintEntryError(misc, ea, e, 2))
500                 return PRDBBAD;
501             if (na != sghead) {
502                 fprintf(stderr, "last block: \n");
503                 if (PrintEntryError(misc, na, &c, 4))
504                     return PRDBBAD;
505             }
506             return 0;
507         }
508         code = pr_Read(na, (char *)&c, sizeof(c));
509         if (code)
510             return code;
511         length++;
512
513         if (map[ni]) {
514             fprintf(stderr, "Continuation entry reused\n");
515             if (PrintEntryError(misc, ea, e, 2))
516                 return PRDBBAD;
517             if (PrintEntryError(misc, na, &c, 4))
518                 return PRDBBAD;
519             noErrors = 0;
520             break;
521         }
522         map[ni] |= bit;
523         if ((ntohl(c.id) != eid)) {
524             fprintf(stderr, "Continuation id mismatch\n");
525             if (PrintEntryError(misc, ea, e, 2))
526                 return PRDBBAD;
527             if (PrintEntryError(misc, na, &c, 4))
528                 return PRDBBAD;
529             noErrors = 0;
530             continue;
531         }
532
533         /* update membership count */
534         for (i = 0; i < COSIZE; i++) {
535             afs_int32 id = ntohl(c.entries[i]);
536             if (id == PRBADID)
537                 continue;
538             else if (id) {
539                 int eid_s, id_s;
540                 sgcount++;
541                 /* in case the ids are large, convert to pure sign. */
542                 if (id > 0)
543                     id_s = 1;
544                 else
545                     id_s = -1;
546                 if (eid > 0)
547                     eid_s = 1;
548                 else
549                     eid_s = -1;
550                 if (id_s > 0) {
551                     fprintf(stderr,
552                             "User can't be member of supergroup list\n");
553                     if (PrintEntryError(misc, ea, e, 2))
554                         return PRDBBAD;
555                     if (PrintEntryError(misc, na, &c, 4))
556                         return PRDBBAD;
557                     noErrors = 0;
558                 }
559                 /* count each user as a group, and each group a user is in */
560                 if ((id != ANONYMOUSID))
561                     inccount(&misc->idmap, id);
562             } else if (c.next)
563                 count = 9999;
564             else
565                 break;
566         }
567     }
568     if (length > misc->maxContLength)
569         misc->maxContLength = length;
570 #endif /* SUPERGROUPS */
571     length = 0;
572     for (na = head; na; na = ntohl(c.next)) {
573         code = ConvertDiskAddress(na, &ni);
574         if (code) {
575             fprintf(stderr, "Bad continuation ptr %d", na);
576             if (e == 0)
577                 fprintf(stderr, "walking free list");
578             else if (PrintEntryError(misc, ea, e, 2))
579                 return PRDBBAD;
580             if (na != head) {
581                 fprintf(stderr, "last block: \n");
582                 if (PrintEntryError(misc, na, &c, 4))
583                     return PRDBBAD;
584             }
585             return 0;
586         }
587         code = pr_Read(na, (char *)&c, sizeof(c));
588         if (code)
589             return code;
590         length++;
591
592         if (map[ni]) {
593             fprintf(stderr, "Continuation entry reused\n");
594             if (e == 0)
595                 fprintf(stderr, "walking free list");
596             else if (PrintEntryError(misc, ea, e, 2))
597                 return PRDBBAD;
598             if (PrintEntryError(misc, na, &c, 4))
599                 return PRDBBAD;
600             noErrors = 0;
601             break;
602         }
603         map[ni] |= bit;
604         if (e && (ntohl(c.id) != eid)) {
605             fprintf(stderr, "Continuation id mismatch\n");
606             if (e == 0)
607                 fprintf(stderr, "walking free list");
608             else if (PrintEntryError(misc, ea, e, 2))
609                 return PRDBBAD;
610             if (PrintEntryError(misc, na, &c, 4))
611                 return PRDBBAD;
612             noErrors = 0;
613             continue;
614         }
615
616         /* update membership count */
617         if (e)
618             for (i = 0; i < COSIZE; i++) {
619                 afs_int32 id = ntohl(c.entries[i]);
620                 if (id == PRBADID)
621                     continue;
622                 else if (id) {
623                     int eid_s, id_s;
624                     count++;
625                     /* in case the ids are large, convert to pure sign. */
626                     if (id > 0)
627                         id_s = 1;
628                     else
629                         id_s = -1;
630                     if (eid > 0)
631                         eid_s = 1;
632                     else
633                         eid_s = -1;
634 #if defined(SUPERGROUPS)
635                     if (id_s > 0 && eid_s > 0) {
636                         fprintf(stderr,
637                                 "User can't be member of user in membership list\n");
638                         if (PrintEntryError(misc, ea, e, 2))
639                             return PRDBBAD;
640                         if (PrintEntryError(misc, na, &c, 4))
641                             return PRDBBAD;
642                         noErrors = 0;
643                     }
644 #else
645                     if (id_s * eid_s > 0) {     /* sign should be different */
646                         fprintf(stderr,
647                                 "Bad user/group dicotomy in membership list\n");
648                         if (PrintEntryError(misc, ea, e, 2))
649                             return PRDBBAD;
650                         if (PrintEntryError(misc, na, &c, 4))
651                             return PRDBBAD;
652                         noErrors = 0;
653                     }
654 #endif /* SUPERGROUPS */
655                     /* count each user as a group, and each group a user is in */
656 #if defined(SUPERGROUPS)
657                     if (!(id < 0 && eid < 0) && (id != ANONYMOUSID))
658                         inccount(&misc->idmap, id);
659 #else
660                     if ((id >= misc->minId) && (id <= misc->maxId)
661                         && (id != ANONYMOUSID))
662                         misc->idmap[id - misc->minId]++;
663 #endif /* SUPERGROUPS */
664                 } else if (c.next)
665                     count = 9999;
666                 else
667                     break;
668             }
669     }
670     if (e && noErrors && (count != ntohl(e->count))) {
671 #if defined(SUPERGROUPS)
672         if (count >= 9999)
673             fprintf(stderr, "Membership list ends early\n");
674 #else
675         if (count > 9999)
676             fprintf(stderr, "Membership list ends early\n");
677 #endif /* SUPERGROUPS */
678         fprintf(stderr, "Count was %d should be %d\n", count,
679                 ntohl(e->count));
680         if (PrintEntryError(misc, ea, e, 2))
681             return PRDBBAD;
682 #if defined(SUPERGROUPS)
683         noErrors = 0;
684     }
685     if (e && (e->flags & htonl(PRGRP)) && (sgcount != ntohl(g->countsg))) {
686         fprintf(stderr, "SGCount was %d should be %d\n", sgcount,
687                 ntohl(g->countsg));
688         if (PrintEntryError(misc, ea, e, 2))
689             return PRDBBAD;
690 #endif
691     }
692
693     if (e) {
694         if (length > misc->maxContLength)
695             misc->maxContLength = length;
696     } else
697         misc->freeLength = length;
698
699     return 0;
700 #if defined(SUPERGROUPS)
701 #undef g
702 #endif
703 }
704
705 afs_int32
706 WalkOwnedChain(char map[],              /* one byte per db entry */
707                struct misc_data *misc,  /* stuff to keep track of */
708                afs_int32 ea, struct prentry *e)
709 {
710     afs_int32 head;
711     afs_int32 code;
712     struct prentry c;           /* continuation entry */
713     afs_int32 na;               /* next thread */
714     int ni;
715     afs_int32 eid = 0;
716     int length;                 /* length of chain */
717
718     if (e) {
719         head = ntohl(e->owned);
720         eid = ntohl(e->id);
721     } else
722         head = ntohl(cheader.orphan);
723
724     length = 0;
725     for (na = head; na; na = ntohl(c.nextOwned)) {
726         code = ConvertDiskAddress(na, &ni);
727         if (code) {
728             fprintf(stderr, "Bad owned list ptr %d", na);
729             if (e == 0)
730                 fprintf(stderr, "walking orphan list");
731             else if (PrintEntryError(misc, ea, e, 2))
732                 return PRDBBAD;
733             if (na != head) {
734                 fprintf(stderr, "last block: \n");
735                 if (PrintEntryError(misc, na, &c, 4))
736                     return PRDBBAD;
737             }
738             return 0;
739         }
740         code = pr_Read(na, (char *)&c, sizeof(c));
741         if (code)
742             return code;
743         length++;
744
745         if (map[ni] & MAP_OWNED) {
746             fprintf(stderr, "Entry on multiple owner chains\n");
747             if (e == 0)
748                 fprintf(stderr, "walking orphan list");
749             else if (PrintEntryError(misc, ea, e, 2))
750                 return PRDBBAD;
751             if (PrintEntryError(misc, na, &c, 4))
752                 return PRDBBAD;
753             break;
754         }
755         map[ni] |= MAP_OWNED;
756         if ((map[ni] & MAP_HASHES) != MAP_HASHES) {
757             fprintf(stderr, "Owned entry not hashed properly\n");
758           abort:
759             if (e == 0)
760                 fprintf(stderr, "walking orphan list");
761             else if (PrintEntryError(misc, ea, e, 2))
762                 return PRDBBAD;
763             if (PrintEntryError(misc, na, &c, 4))
764                 return PRDBBAD;
765             continue;
766         }
767         if (e) {
768             if (ntohl(c.owner) != eid) {
769                 fprintf(stderr, "Owner id mismatch\n");
770                 goto abort;
771             }
772         } else /* orphan */ if (c.owner) {
773             fprintf(stderr, "Orphan group owner not zero\n");
774             goto abort;
775         }
776     }
777
778     if (e) {
779         if (length > misc->maxOwnerLength)
780             misc->maxOwnerLength = length;
781     } else
782         misc->orphanLength = length;
783
784     return 0;
785 }
786
787 afs_int32
788 WalkChains(char map[],          /* one byte per db entry */
789            struct misc_data *misc)      /* stuff to keep track of */
790 {
791     afs_int32 code;
792     int ei;
793     afs_int32 ea;               /* entry's db addr */
794     struct prentry e;
795     afs_int32 id;
796     int type;
797
798     /* check all entries found in hash table walks */
799     for (ei = 0; ei < misc->nEntries; ei++)
800         if (map[ei] & MAP_HASHES) {
801             ea = ei * sizeof(struct prentry) + sizeof(cheader);
802             code = pr_Read(ea, (char *)&e, sizeof(e));
803             if (code)
804                 return code;
805
806             if ((map[ei] & MAP_HASHES) != MAP_HASHES) {
807                 fprintf(stderr, "entry not in both hashtables\n");
808                 if ((map[ei] & MAP_NAMEHASH) != MAP_NAMEHASH)
809                     fprintf(stderr, "--> entry not in Name hashtable\n");
810                 if ((map[ei] & MAP_IDHASH) != MAP_IDHASH)
811                     fprintf(stderr, "--> entry not in ID hashtable\n");
812
813               abort:
814                 if (PrintEntryError(misc, ea, &e, 2))
815                     return PRDBBAD;
816                 continue;
817             }
818
819             id = ntohl(e.id);
820
821             type = ntohl(e.flags) & PRTYPE;
822             switch (type) {
823             case PRGRP:
824                 if (id >= 0) {
825                     fprintf(stderr, "Group id not negative\n");
826                     goto abort;
827                 }
828                 /* special case sysadmin: it owns itself */
829                 if (id == SYSADMINID) {
830                     if (ntohl(e.owner) != SYSADMINID) {
831                         fprintf(stderr,
832                                 "System:administrators doesn't own itself\n");
833                         goto abort;
834                     }
835                 }
836                 code = WalkOwnedChain(map, misc, ea, &e);
837                 if (code)
838                     return code;
839                 code = WalkNextChain(map, misc, ea, &e);
840                 if (code)
841                     return code;
842                 misc->ngroups++;
843                 break;
844             case PRUSER:
845                 if (id <= 0) {
846 #if defined(SUPERGROUPS)
847                     fprintf(stderr, "User id not positive\n");
848 #else
849                     fprintf(stderr, "User id negative\n");
850 #endif
851                     goto abort;
852                 }
853
854                 /* Users are owned by sysadmin, but sysadmin doesn't have an owner
855                  * chain.  Check this then set the owned bit. */
856                 if (ntohl(e.owner) != SYSADMINID) {
857                     fprintf(stderr,
858                             "User not owned by system:administrators\n");
859                     goto abort;
860                 }
861                 if (e.nextOwned) {
862                     fprintf(stderr, "User has owned pointer\n");
863                     goto abort;
864                 }
865                 map[ei] |= MAP_OWNED;
866
867                 code = WalkOwnedChain(map, misc, ea, &e);
868                 if (code)
869                     return code;
870                 code = WalkNextChain(map, misc, ea, &e);
871                 if (code)
872                     return code;
873                 if (strchr(e.name, '@') == 0) {
874                     misc->nusers++;     /* Not a foreign user */
875                 } else {
876                     misc->nforeigns++;  /* A foreign user */
877                 }
878                 break;
879             case PRFREE:
880             case PRCONT:
881             case PRCELL:
882                 misc->ncells++;
883                 break;
884             case PRFOREIGN:
885                 fprintf(stderr,
886                         "ENTRY IS unexpected type [PRFOREIGN] (flags=0x%x)\n",
887                         ntohl(e.flags));
888                 break;
889             case PRINST:
890                 misc->ninsts++;
891                 break;
892             default:
893                 fprintf(stderr, "entry with unexpected type");
894                 goto abort;
895             }
896         }
897
898     return 0;
899 }
900
901 afs_int32
902 GC(char map[], struct misc_data *misc)
903 {
904     afs_int32 code;
905     int ei;
906     afs_int32 ea;
907     struct prentry e;
908     char m;
909
910     for (ei = 0; ei < misc->nEntries; ei++) {
911         ea = ei * sizeof(struct prentry) + sizeof(cheader);
912         code = pr_Read(ea, (char *)&e, sizeof(e));
913         if (code)
914             return code;
915         m = map[ei];
916         if (m == 0) {
917             fprintf(stderr, "Unreferenced entry:");
918             if (PrintEntryError(misc, ea, &e, 2))
919                 return PRDBBAD;
920         }
921         /* all users and groups should be owned, and their membership counts
922          * should be okay */
923         else if ((m & MAP_HASHES) == MAP_HASHES) {
924             afs_int32 id;
925             int refCount;
926             if (!(m & MAP_OWNED)) {
927                 fprintf(stderr, "Entry not on any owner chain:\n");
928                 if (PrintEntryError(misc, ea, &e, 2))
929                     return PRDBBAD;
930             }
931             id = ntohl(e.id);
932 #if defined(SUPERGROUPS)
933             if ((id != ANONYMOUSID)
934                 && ((refCount = idcount(&misc->idmap, id)) != ntohl(e.count))) 
935 #else
936             if ((id >= misc->minId) && (id <= misc->maxId)
937                 && (id != ANONYMOUSID)
938                 && ((refCount = misc->idmap[id - misc->minId]) !=
939                     ntohl(e.count))) 
940 #endif /* SUPERGROUPS */
941               {
942                 afs_int32 na;
943                 fprintf(stderr,
944                         "Entry membership count is inconsistent: %d entries refer to this one\n",
945                         refCount);
946                 if (PrintEntryError(misc, ea, &e, 2))
947                     return PRDBBAD;
948
949                 /* get continuation blocks too */
950                 for (na = ntohl(e.next); na; na = ntohl(e.next)) {
951                     int ni;
952                     code = ConvertDiskAddress(na, &ni);
953                     if (code)
954                         return code;
955                     code = pr_Read(na, (char *)&e, sizeof(e));
956                     if (code)
957                         return code;
958                     if (PrintEntryError(misc, na, &e, 4))
959                         return PRDBBAD;
960                 }
961             }
962         }
963     }
964     return 0;
965 }
966
967 char *
968 QuoteName(char *s)
969 {
970     char *qs;
971     if (strpbrk(s, " \t")) {
972         qs = (char *)malloc(strlen(s) + 3);
973         strcpy(qs, "\"");
974         strcat(qs, s);
975         strcat(qs, "\"");
976     } else
977         qs = s;
978     return qs;
979 }
980
981 afs_int32
982 DumpRecreate(char map[], struct misc_data *misc)
983 {
984     afs_int32 code;
985     int ei;
986     afs_int32 ea;
987     struct prentry e;
988     afs_int32 id;
989     afs_int32 flags;
990     afs_int32 owner;
991     char *name;
992     int builtinUsers = 0;
993     int createLow = 0;          /* users uncreate from here */
994 #if defined(SUPERGROUPS)
995     struct idused *idmap;       /* map of all id's */
996 #else
997     afs_int32 *idmap;           /* map of all id's */
998 #endif
999     int found;
1000     FILE *rc;
1001
1002     rc = misc->recreate;
1003     idmap = misc->idmap;
1004 #if defined(SUPERGROUPS)
1005     zeromap(idmap);
1006 #else
1007     memset(idmap, 0, misc->idRange * sizeof(misc->idmap[0]));
1008 #endif
1009     do {
1010         found = 0;
1011         for (ei = createLow; ei < misc->nEntries; ei++) {
1012             if ((map[ei] & MAP_HASHES) && (map[ei] & MAP_RECREATE) == 0) {
1013                 afs_int32 mask;
1014                 afs_int32 access;
1015                 int gq, uq;
1016
1017                 ea = ei * sizeof(struct prentry) + sizeof(cheader);
1018                 code = pr_Read(ea, (char *)&e, sizeof(e));
1019                 if (code)
1020                     return code;
1021
1022                 if (misc->listentries)
1023                     pr_PrintEntry(stdout, 0 /*not in host order */ , ea, &e,
1024                                   0);
1025
1026                 id = ntohl(e.id);
1027                 flags = ntohl(e.flags);
1028                 owner = ntohl(e.owner);
1029                 name = QuoteName(e.name);
1030
1031                 if (!strcmp(e.name, "system:administrators")
1032                     || !strcmp(e.name, "system:anyuser")
1033                     || !strcmp(e.name, "system:authuser")
1034                     || !strcmp(e.name, "system:backup")
1035                     || !strcmp(e.name, "anonymous")) {
1036                     builtinUsers++;
1037                     goto user_done;
1038                 }
1039
1040                 /* check for duplicate id.  This may still lead to duplicate
1041                  * names. */
1042 #if defined(SUPERGROUPS)
1043                 if (idcount(&idmap, id)) 
1044 #else
1045                 if (idmap[id - misc->minId]) 
1046 #endif
1047                   {
1048                     fprintf(stderr, "Skipping entry with duplicate id %di\n",
1049                             id);
1050                     goto user_done;
1051                 }
1052
1053                 /* If owner doesn't exist skip for now, unless we're our own
1054                  * owner.  If so, a special case allows a group to own itself
1055                  * if caller is sysadmin.  This leaves only owner cycles to
1056                  * deal with. */
1057
1058                 if ((owner < misc->minId) || (owner > misc->maxId)) {
1059                     if (owner == ANONYMOUSID)
1060                         fprintf(stderr,
1061                                 "Warning: id %di is owned by ANONYMOUS; using sysadmin instead\n",
1062                                 id);
1063                     else
1064                         fprintf(stderr,
1065                                 "Bogus owner (%d) of id %di; using sysadmin instead\n",
1066                                 owner, id);
1067                     owner = SYSADMINID;
1068                 }
1069                 if (id == owner) {
1070                     fprintf(stderr, "Warning: group %s is self owning\n",
1071                             name);
1072                 } else if (owner == 0) {
1073                     fprintf(stderr,
1074                             "Warning: orphan group %s will become self owning.\n",
1075                             name);
1076                     owner = id;
1077                 }
1078 #if defined(SUPERGROUPS)
1079                 else if (!idcount(&idmap, owner))
1080                     goto user_skip;
1081 #else
1082                 else if (idmap[owner - misc->minId] == 0)
1083                     goto user_skip;
1084 #endif
1085
1086                 if (rc)
1087                     fprintf(rc, "cr %s %d %d\n", name, id, owner);
1088
1089                 gq = uq = access = mask = 0;
1090                 if (flags & PRACCESS) {
1091                     access = (flags >> PRIVATE_SHIFT);
1092                     mask |= PR_SF_ALLBITS;
1093                 }
1094                 if (flags & PRQUOTA) {
1095                     gq = ntohl(e.ngroups);
1096                     uq = ntohl(e.nusers);
1097                     mask |= PR_SF_NGROUPS | PR_SF_NUSERS;
1098                 }
1099                 if (mask && rc) {
1100                     fprintf(rc, "sf %d %x %x %d %d\n", id, mask, access, gq,
1101                             uq);
1102                 }
1103               user_done:
1104                 map[ei] |= MAP_RECREATE;
1105 #if defined(SUPERGROUPS)
1106                 if (id != ANONYMOUSID)
1107                     inccount(&idmap, id);
1108 #else
1109                 if (id != ANONYMOUSID)
1110                     idmap[id - misc->minId]++;
1111 #endif
1112                 found++;
1113             }
1114             /* bump low water mark if possible */
1115             if (ei == createLow)
1116                 createLow++;
1117           user_skip:;
1118         }
1119         misc->verbose = 0;
1120     } while (found);
1121
1122     /* Now create the entries with circular owner dependencies and make them
1123      * own themselves.  This is the only way to create them with the correct
1124      * names. */
1125     for (ei = 0; ei < misc->nEntries; ei++)
1126         if (((map[ei] & MAP_HASHES) == MAP_HASHES)
1127             && (map[ei] & MAP_RECREATE) == 0) {
1128             ea = ei * sizeof(struct prentry) + sizeof(cheader);
1129             code = pr_Read(ea, (char *)&e, sizeof(e));
1130             if (code)
1131                 return code;
1132
1133             id = ntohl(e.id);
1134             name = QuoteName(e.name);
1135             fprintf(stderr, "Warning: group %s in self owning cycle\n", name);
1136             if (rc)
1137                 fprintf(rc, "cr %s %d %d\n", name, id, id);
1138 #if defined(SUPERGROUPS)
1139             inccount(&idmap, id);
1140 #else
1141             idmap[id - misc->minId]++;
1142 #endif
1143         }
1144     for (ei = 0; ei < misc->nEntries; ei++)
1145         if (((map[ei] & MAP_HASHES) == MAP_HASHES)
1146             && (map[ei] & MAP_RECREATE) == 0) {
1147             ea = ei * sizeof(struct prentry) + sizeof(cheader);
1148             code = pr_Read(ea, (char *)&e, sizeof(e));
1149             if (code)
1150                 return code;
1151
1152             owner = ntohl(e.owner);
1153 #if defined(SUPERGROUPS)
1154             if (!idcount(&idmap, owner)) 
1155 #else
1156             if (idmap[owner - misc->minId] == 0) 
1157 #endif
1158               {
1159                 fprintf(stderr,
1160                         "Skipping chown of '%s' to non-existant owner %di\n",
1161                         e.name, owner);
1162             } else if (rc)
1163                 fprintf(rc, "ce %d \"\" %d 0\n", ntohl(e.id), e.owner);
1164         }
1165
1166     if (rc == 0)
1167         return 0;
1168
1169     /* Reconstruct membership information based on the groups' user lists. */
1170     for (ei = 0; ei < misc->nEntries; ei++) {
1171         if ((map[ei] & MAP_HASHES) == MAP_HASHES) {
1172             ea = ei * sizeof(struct prentry) + sizeof(cheader);
1173             code = pr_Read(ea, (char *)&e, sizeof(e));
1174             if (code)
1175                 return code;
1176
1177             id = ntohl(e.id);
1178             flags = ntohl(e.flags);
1179
1180             if ((id < 0) && (flags & PRGRP)) {
1181                 int count = 0;
1182                 afs_int32 na;
1183 #if defined(SUPERGROUPS)
1184                 afs_int32 ng;
1185 #endif
1186                 int i;
1187                 for (i = 0; i < PRSIZE; i++) {
1188                     afs_int32 uid = ntohl(e.entries[i]);
1189                     if (uid == 0)
1190                         break;
1191                     if (uid == PRBADID)
1192                         continue;
1193 #if !defined(SUPERGROUPS)
1194                     if (uid > 0) {
1195 #endif
1196                         fprintf(rc, "au %d %d\n", uid, id);
1197                         count++;
1198 #if !defined(SUPERGROUPS)
1199                     } else
1200                         fprintf(stderr, "Skipping %di in group %di\n", uid,
1201                                 id);
1202 #endif
1203                 }
1204 #if defined(SUPERGROUPS)
1205 #define g       (*((struct prentryg *)&e))
1206                 ng = ntohl(g.nextsg);
1207                 for (i = 0; i < SGSIZE; i++) {
1208                     afs_int32 uid = ntohl(g.supergroup[i]);
1209                     if (uid == 0)
1210                         break;
1211                     if (uid == PRBADID)
1212                         continue;
1213                     fprintf(rc, "au %d %d\n", uid, id);
1214                     count++;
1215                 }
1216                 while (ng) {
1217                     struct prentry c;
1218                     code = pr_Read(ng, (char *)&c, sizeof(c));
1219                     if (code)
1220                         return code;
1221
1222                     if ((id == ntohl(c.id)) && (c.flags & htonl(PRCONT))) {
1223                         for (i = 0; i < COSIZE; i++) {
1224                             afs_int32 uid = ntohl(c.entries[i]);
1225                             if (uid == 0)
1226                                 break;
1227                             if (uid == PRBADID)
1228                                 continue;
1229                             fprintf(rc, "au %d %d\n", uid, id);
1230                             count++;
1231                         }
1232                     } else {
1233                         fprintf(stderr, "Skipping continuation block at %d\n",
1234                                 ng);
1235                         break;
1236                     }
1237                     ng = ntohl(c.next);
1238                 }
1239 #undef g
1240 #endif /* SUPERGROUPS */
1241                 na = ntohl(e.next);
1242                 while (na) {
1243                     struct prentry c;
1244                     code = pr_Read(na, (char *)&c, sizeof(c));
1245                     if (code)
1246                         return code;
1247
1248                     if ((id == ntohl(c.id)) && (c.flags & htonl(PRCONT))) {
1249                         for (i = 0; i < COSIZE; i++) {
1250                             afs_int32 uid = ntohl(c.entries[i]);
1251                             if (uid == 0)
1252                                 break;
1253                             if (uid == PRBADID)
1254                                 continue;
1255 #if !defined(SUPERGROUPS)
1256                             if (uid > 0) {
1257 #endif
1258                                 fprintf(rc, "au %d %d\n", uid, id);
1259                                 count++;
1260 #if !defined(SUPERGROUPS)
1261                             } else
1262                                 fprintf(stderr, "Skipping %di in group %di\n",
1263                                         uid, id);
1264 #endif
1265                         }
1266                     } else {
1267                         fprintf(stderr, "Skipping continuation block at %d\n",
1268                                 na);
1269                         break;
1270                     }
1271                     na = ntohl(c.next);
1272                 }
1273                 if (count != ntohl(e.count))
1274                     fprintf(stderr,
1275                             "Group membership count problem found %d should be %d\n",
1276                             count, ntohl(e.count));
1277             } else if ((id < 0) || (flags & PRGRP)) {
1278                 fprintf(stderr, "Skipping group %di\n", id);
1279             }
1280         }
1281     }
1282     return 0;
1283 }
1284
1285 afs_int32
1286 CheckPrDatabase(struct misc_data *misc) /* info & statistics */
1287 {
1288     afs_int32 code;
1289     afs_int32 eof;
1290     int n;
1291     char *map;                  /* map of each entry in db */
1292
1293     eof = ntohl(cheader.eofPtr);
1294     eof -= sizeof(cheader);
1295     n = eof / sizeof(struct prentry);
1296     if ((eof < 0) || (n * sizeof(struct prentry) != eof)) {
1297         code = PRDBBAD;
1298         afs_com_err(whoami, code, "eof ptr no good: eof=%d, sizeof(prentry)=%d",
1299                 eof, sizeof(struct prentry));
1300       abort:
1301         return code;
1302     }
1303     if (misc->verbose)
1304         printf("Database has %d entries\n", n);
1305     map = (char *)malloc(n);
1306     memset(map, 0, n);
1307     misc->nEntries = n;
1308
1309     if (misc->verbose) {
1310         printf("\nChecking name hash table\n");
1311         fflush(stdout);
1312     }
1313     code = WalkHashTable(cheader.nameHash, MAP_NAMEHASH, map, misc);
1314     if (code) {
1315         afs_com_err(whoami, code, "walking name hash");
1316         goto abort;
1317     }
1318     if (misc->verbose) {
1319         printf("\nChecking id hash table\n");
1320         fflush(stdout);
1321     }
1322     code = WalkHashTable(cheader.idHash, MAP_IDHASH, map, misc);
1323     if (code) {
1324         afs_com_err(whoami, code, "walking id hash");
1325         goto abort;
1326     }
1327
1328     /* hash walk calculates min and max id */
1329 #if defined(SUPERGROUPS)
1330     misc->idmap = 0;
1331 #else
1332     n = ((misc->maxId > misc->maxForId) ? misc->maxId : misc->maxForId);
1333     misc->idRange = n - misc->minId + 1;
1334     misc->idmap = (afs_int32 *) malloc(misc->idRange * sizeof(afs_int32));
1335     if (!misc->idmap) {
1336         afs_com_err(whoami, 0, "Unable to malloc space for max ids of %d",
1337                 misc->idRange);
1338         code = -1;
1339         goto abort;
1340     }
1341     memset(misc->idmap, 0, misc->idRange * sizeof(misc->idmap[0]));
1342 #endif /* SUPERGROUPS */
1343
1344     if (misc->verbose) {
1345         printf("\nChecking entry chains\n");
1346         fflush(stdout);
1347     }
1348     code = WalkChains(map, misc);
1349     if (code) {
1350         afs_com_err(whoami, code, "walking chains");
1351         goto abort;
1352     }
1353     if (misc->verbose) {
1354         printf("\nChecking free list\n");
1355         fflush(stdout);
1356     }
1357     code = WalkNextChain(map, misc, 0, 0);
1358     if (code) {
1359         afs_com_err(whoami, code, "walking free list");
1360         goto abort;
1361     }
1362     if (misc->verbose) {
1363         printf("\nChecking orphans list\n");
1364         fflush(stdout);
1365     }
1366     code = WalkOwnedChain(map, misc, 0, 0);
1367     if (code) {
1368         afs_com_err(whoami, code, "walking orphan list");
1369         goto abort;
1370     }
1371
1372     if (misc->verbose) {
1373         printf("\nChecking for unreferenced entries\n");
1374         fflush(stdout);
1375     }
1376     code = GC(map, misc);
1377     if (code) {
1378         afs_com_err(whoami, code, "looking for unreferenced entries");
1379         goto abort;
1380     }
1381
1382     DumpRecreate(map, misc);    /* check for owner cycles */
1383     if (misc->recreate)
1384         fclose(misc->recreate);
1385
1386     if (misc->anon != 2)        /* once for each hash table */
1387         fprintf(stderr, "Problems with ANON=%d\n", misc->anon);
1388     if (misc->ncells || misc->ninsts)
1389         fprintf(stderr, "Unexpected entry type\n");
1390     if (misc->nusers != ntohl(cheader.usercount)) {
1391         fprintf(stderr,
1392                 "User count inconsistent: should be %d, header claims: %d\n",
1393                 misc->nusers, ntohl(cheader.usercount));
1394     }
1395     if (misc->ngroups != ntohl(cheader.groupcount)) {
1396         fprintf(stderr,
1397                 "Group count inconsistent: should be %d, header claims: %d\n",
1398                 misc->ngroups, ntohl(cheader.groupcount));
1399     }
1400     if (misc->maxId > ntohl(cheader.maxID))
1401         fprintf(stderr,
1402                 "Database's max user Id (%d) is smaller than largest user's Id (%d).\n",
1403                 ntohl(cheader.maxID), misc->maxId);
1404     if (misc->minId < ntohl(cheader.maxGroup))
1405         fprintf(stderr,
1406                 "Database's max group Id (%d) is smaller than largest group's Id (%d).\n",
1407                 ntohl(cheader.maxGroup), misc->minId);
1408
1409     if (misc->verbose) {
1410         printf("\nMaxId = %d, MinId = %d, MaxForeignId = %d\n", misc->maxId,
1411                misc->minId, misc->maxForId);
1412         printf
1413             ("Free list is %d entries in length, %d groups on orphan list\n",
1414              misc->freeLength, misc->orphanLength);
1415         printf
1416             ("The longest owner list is %d, the longest continuation block chain is %d\n",
1417              misc->maxOwnerLength, misc->maxContLength);
1418         printf("%d users ; %d foreign users ; and %d groups\n", misc->nusers,
1419                misc->nforeigns, misc->ngroups);
1420     }
1421
1422     free(map);
1423     return code;
1424 }
1425     
1426 #include "AFS_component_version_number.c"
1427
1428 int
1429 WorkerBee(struct cmd_syndesc *as, void *arock)
1430 {
1431     afs_int32 code;
1432     char *recreateFile;
1433     struct misc_data misc;      /* info & statistics */
1434
1435     initialize_PT_error_table();
1436     initialize_U_error_table();
1437
1438     pr_dbaseName = AFSDIR_SERVER_PRDB_FILEPATH;
1439     memset(&misc, 0, sizeof(misc));
1440
1441     pr_dbaseName = as->parms[0].items->data;    /* -database */
1442     misc.listuheader = (as->parms[1].items ? 1 : 0);    /* -uheader  */
1443     misc.listpheader = (as->parms[2].items ? 1 : 0);    /* -pheader  */
1444     misc.listentries = (as->parms[3].items ? 1 : 0);    /* -entries  */
1445     misc.verbose = (as->parms[4].items ? 1 : 0);        /* -verbose  */
1446     recreateFile = (as->parms[5].items ? as->parms[5].items->data : NULL);      /* -rebuild  */
1447
1448     fd = open(pr_dbaseName, O_RDONLY, 0);
1449     if (fd == -1) {
1450         afs_com_err(whoami, errno, "Open failed on db %s", pr_dbaseName);
1451         exit(2);
1452     }
1453
1454     /* Read the ubik header */
1455     if (misc.listuheader) {
1456         readUbikHeader(&misc);
1457     }
1458
1459     code = ReadHeader();
1460     if (code)
1461         return code;
1462     if (misc.listpheader)
1463         printheader(&cheader);
1464
1465     if (recreateFile) {
1466         misc.recreate = fopen(recreateFile, "w");
1467         if (misc.recreate == 0) {
1468             afs_com_err(whoami, errno,
1469                     "can't create file for recreation instructions: %s",
1470                     recreateFile);
1471             exit(4);
1472         }
1473     }
1474     code = CheckPrDatabase(&misc);
1475     if (code) {
1476         afs_com_err(whoami, code, "Checking prserver database");
1477         exit(3);
1478     }
1479     exit(0);
1480 }
1481
1482 int
1483 main(int argc, char *argv[])
1484 {
1485     struct cmd_syndesc *ts;
1486
1487     setlinebuf(stdout);
1488
1489     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "PRDB check");
1490     cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "ptdb_file");
1491     cmd_AddParm(ts, "-uheader", CMD_FLAG, CMD_OPTIONAL,
1492                 "Display UBIK header");
1493     cmd_AddParm(ts, "-pheader", CMD_FLAG, CMD_OPTIONAL,
1494                 "Display KADB header");
1495     cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
1496     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
1497     cmd_AddParm(ts, "-rebuild", CMD_SINGLE, CMD_OPTIONAL | CMD_HIDE,
1498                 "out_file");
1499
1500     return cmd_Dispatch(argc, argv);
1501 }
1502
1503
1504 #if defined(SUPERGROUPS)
1505
1506 /* new routines to deal with very large ID numbers */
1507
1508 void
1509 zeromap(struct idused *idmap)
1510 {
1511     while (idmap) {
1512         memset(idmap->idcount, 0, sizeof idmap->idcount);
1513         idmap = idmap->idnext;
1514     }
1515 }
1516
1517 void
1518 inccount(struct idused **idmapp, int id)
1519 {
1520     struct idused *idmap;
1521
1522     if (IDCOUNT & (IDCOUNT - 1)) {
1523         fprintf(stderr, "IDCOUNT must be power of 2!\n");
1524         exit(1);
1525     }
1526     while ((idmap = *idmapp) != NULL) {
1527         if (idmap->idstart == (id & ~(IDCOUNT - 1)))
1528             break;
1529         idmapp = &idmap->idnext;
1530     }
1531     if (!idmap) {
1532         idmap = (struct idused *)malloc(sizeof *idmap);
1533         if (!idmap) {
1534             perror("idmap");
1535             exit(1);
1536         }
1537         memset(idmap, 0, sizeof idmap);
1538         idmap->idstart = id & ~(IDCOUNT - 1);
1539         idmap->idnext = *idmapp;
1540         *idmapp = idmap;
1541     }
1542     ++idmap->idcount[id & (IDCOUNT - 1)];
1543 }
1544
1545 int
1546 idcount(struct idused **idmapp, int id)
1547 {
1548     struct idused *idmap;
1549
1550     if (IDCOUNT & (IDCOUNT - 1)) {
1551         fprintf(stderr, "IDCOUNT must be power of 2!\n");
1552         exit(1);
1553     }
1554     while ((idmap = *idmapp) != NULL) {
1555         if (idmap->idstart == (id & ~(IDCOUNT - 1))) {
1556             return idmap->idcount[id & (IDCOUNT - 1)];
1557         }
1558         idmapp = &idmap->idnext;
1559     }
1560     return 0;
1561 }
1562 #endif /* SUPERGROUPS */