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