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