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