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