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