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