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