56df576c3110e07195b962efd16c3f5525ec6a1a
[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 extern int errno;
11
12 #include <afsconfig.h>
13 #include <afs/param.h>
14
15 RCSID("$Header$");
16
17 #include <afs/stds.h>
18 #include <sys/types.h>
19 #ifdef AFS_NT40_ENV
20 #include <winsock2.h>
21 #include <WINNT/afsevent.h>
22 #include <io.h>
23 #else
24 #include <netdb.h>
25 #include <netinet/in.h>
26 #include <sys/file.h>
27 #endif
28 #include <stdio.h>
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #else
32 #ifdef HAVE_STRINGS_H
33 #include <strings.h>
34 #endif
35 #endif
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <afs/cellconfig.h>
39 #include <afs/afsutil.h>
40 #include <ubik.h>
41 #include <afs/cmd.h>
42
43 #include "ptint.h"
44 #include "pterror.h"
45 #include "ptserver.h"
46
47 struct prheader cheader;
48 int    fd;
49 char *pr_dbaseName;
50 char *whoami = "db_verify";
51 #define UBIK_HEADERSIZE 64
52
53
54 afs_int32 printheader(h)
55      struct prheader *h;
56 {
57    printf("Version           = %d\n", ntohl(h->version));
58    printf("Header Size       = %d\n", ntohl(h->headerSize));
59    printf("Free Ptr          = 0x%x\n", ntohl(h->freePtr));
60    printf("EOF  Ptr          = 0x%x\n", ntohl(h->eofPtr));
61    printf("Max Group     ID  = %d\n", ntohl(h->maxGroup));
62    printf("Max User      ID  = %d\n", ntohl(h->maxID));
63    printf("Max Foreign   ID  = %d\n", ntohl(h->maxForeign));
64 /* printf("Max Sub/Super ID  = %d\n", ntohl(h->maxInst)); */
65    printf("Orphaned groups   = %d\n", ntohl(h->orphan));
66    printf("User      Count   = %d\n", ntohl(h->usercount));
67    printf("Group     Count   = %d\n", ntohl(h->groupcount));
68 /* printf("Foreign   Count   = %d\n", ntohl(h->foreigncount)); NYI */
69 /* printf("Sub/super Count   = %d\n", ntohl(h->instcount));    NYI */
70    printf("Name Hash         = %d buckets\n", HASHSIZE);
71    printf("ID   Hash         = %d buckets\n", HASHSIZE);
72 }
73
74 static afs_int32 pr_Read (pos, buff, len)
75   afs_int32 pos;
76   char *buff;
77   afs_int32 len;
78 {
79     afs_int32 code;
80
81     code = lseek(fd,UBIK_HEADERSIZE+pos, 0);
82     if (code == -1) return errno;
83
84     code = read(fd, buff, len);
85     if (code != len) return -1;
86     if (code == -1)  return errno;
87
88     return 0;
89 }
90
91 /* InitDB ()
92  *   Initializes the a transaction on the database and reads the header into
93  * the static variable cheader.  If successful it returns a read-locked
94  * transaction.  If ubik reports that cached database info should be up to date
95  * the cheader structure is not re-read from the ubik.
96  */
97
98 afs_int32 ReadHeader()
99 {
100     afs_int32 code;
101
102     code = pr_Read (0, (char *) &cheader, sizeof(cheader));
103     if (code) {
104         com_err(whoami, code, "couldn't read header");
105         return code;
106     }
107     /* Check and see if database exists and is approximately OK. */
108     if (ntohl(cheader.headerSize) != sizeof(cheader) ||
109         ntohl(cheader.eofPtr) == 0) {
110         if (code) return code;
111         com_err (whoami, PRDBBAD, "header is bad");
112         return PRDBBAD;
113     }
114     return 0;
115 }
116
117 static afs_int32 IDHash(x)
118   afs_int32 x;
119 {
120     /* returns hash bucket for x */
121     return ((abs(x)) % HASHSIZE);
122 }
123
124 static afs_int32 NameHash(aname)
125   register unsigned char *aname;
126 {
127     /* returns hash bucket for aname */
128     register unsigned int hash=0;
129     register int i;
130 /* stolen directly from the HashString function in the vol package */
131     for (i=strlen(aname),aname += i-1;i--;aname--)
132         hash = (hash*31) + (*aname-31);
133     return(hash % HASHSIZE);
134 }
135
136 #define MAP_NAMEHASH 1
137 #define MAP_IDHASH 2
138 #define MAP_HASHES (MAP_NAMEHASH | MAP_IDHASH)
139 #define MAP_CONT 4
140 #define MAP_FREE 8
141 #define MAP_OWNED 0x10
142 #define MAP_RECREATE 0x20
143
144 struct misc_data {
145     int nEntries;                       /* number of database entries */
146     int  anon;                          /* found anonymous Id */
147     afs_int32 maxId;                            /* user */
148     afs_int32 minId;                            /* group */
149     afs_int32 maxForId;                             /* foreign user id */
150     int idRange;                        /* number of ids in map */
151     afs_int32 *idmap;                   /* map of all id's: midId is origin */
152     int  nusers;                        /* counts of each type */
153     int  ngroups;
154     int  nforeigns;
155     int  ninsts;
156     int  ncells;
157     int  maxOwnerLength;                /* longest owner chain */
158     int  maxContLength;                 /* longest chain of cont. blks */
159     int  orphanLength;                  /* length of orphan list */
160     int  freeLength;                    /* length of free list */
161     int  verbose;
162     int  listuheader;
163     int  listpheader;
164     int  listentries;
165     FILE *recreate;                     /* stream for recreate instructions */
166 };
167
168 int readUbikHeader(misc)
169   struct misc_data *misc;
170 {
171   int offset, r;
172   struct ubik_hdr uheader;
173
174   offset = lseek(fd, 0, 0);
175   if (offset != 0) {
176      printf("error: lseek to 0 failed: %d %d\n", offset, errno);
177      return(-1);
178   }
179
180   /* now read the info */
181   r = read(fd, &uheader, sizeof(uheader));
182   if (r != sizeof(uheader)) {
183      printf("error: read of %d bytes failed: %d %d\n", sizeof(uheader), r, errno);
184      return(-1);
185   }
186
187   uheader.magic = ntohl(uheader.magic);
188   uheader.size  = ntohl(uheader.size);
189   uheader.version.epoch   = ntohl(uheader.version.epoch);
190   uheader.version.counter = ntohl(uheader.version.counter);
191
192   if (misc->listuheader) {
193      printf("Ubik Header\n");
194      printf("   Magic           = 0x%x\n", uheader.magic);
195      printf("   Size            = %u\n",   uheader.size);
196      printf("   Version.epoch   = %u\n",   uheader.version.epoch);
197      printf("   Version.counter = %u\n",   uheader.version.counter);
198   }
199
200   if (uheader.size != UBIK_HEADERSIZE)
201      printf("Ubik header size is %u (should be %u)\n", uheader.size, UBIK_HEADERSIZE);
202   if (uheader.magic != UBIK_MAGIC)
203      printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic, UBIK_MAGIC);
204
205   return(0);
206 }
207
208 afs_int32 ConvertDiskAddress (ea, eiP)
209   afs_uint32 ea;
210   int *eiP;
211 {
212     int i;
213
214     *eiP = -1;
215
216     if (ea < sizeof(cheader)) return PRDBADDR;
217     if (ea >= ntohl(cheader.eofPtr)) return PRDBADDR;
218     ea -= sizeof(cheader);
219     i = ea / sizeof(struct prentry);
220     if (i*sizeof(struct prentry) != ea) return PRDBADDR;
221 /*    if ((i < 0) || (i >= misc->nEntries)) return PRDBADDR; */
222     *eiP = i;
223     return 0;
224 }
225
226 int PrintEntryError (misc, ea, e, indent)
227   struct misc_data *misc;
228   afs_int32 ea;
229   struct prentry *e;
230   int indent;
231 {
232
233     pr_PrintEntry (stderr, /*net order*/0, ea, e, indent);
234     return 0;
235 }
236
237 afs_int32 WalkHashTable (hashtable, hashType, map, misc)
238   afs_int32  hashtable[];                       /* hash table to walk */
239   int   hashType;                       /* hash function to use */
240   char  map[];                          /* one byte per db entry */
241   struct misc_data *misc;               /* stuff to keep track of */
242 {
243     afs_int32 code;
244     int  hi;                            /* index in hash table */
245     afs_int32 ea;                               /* entry's db addr */
246     int  ei;                            /* entry's index */
247     char bit;                           /* bits to check for in map */
248     struct prentry e;
249     afs_int32 next_ea;
250     afs_int32 id;
251     afs_int32 flags;
252     afs_int32 hash;
253
254     bit = hashType;
255
256     for (hi=0; hi<HASHSIZE; hi++) {
257         ea = 0;
258         next_ea = ntohl(hashtable[hi]);
259         while (next_ea) {
260             code = ConvertDiskAddress (next_ea, &ei);
261             if (code) {
262                 fprintf (stderr, "Bad chain address %d\n", next_ea);
263                 if (ea) {
264                     fprintf (stderr, "Last entry in chain:\n");
265                     if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
266                 }
267                 fprintf (stderr,
268                          "Skipping remainder of hash bucket %d\n", hi);
269                 break;
270             }
271             ea = next_ea;
272             code = pr_Read (ea, (char *)&e, sizeof(e));
273             if (code) return code;
274
275             id = ntohl(e.id);
276
277             if ( ((ntohl(e.flags) & (PRGRP | PRINST)) == 0) &&
278                  (strchr(e.name,'@')) ) {
279                /* Foreign user */
280                if (id > misc->maxForId) misc->maxForId = id;
281             } else {
282                if (id == ANONYMOUSID) misc->anon++;
283                else if (id > misc->maxId) misc->maxId = id;
284                if      (id < misc->minId) misc->minId = id;
285             }
286
287             switch (hashType) {
288               case MAP_NAMEHASH:
289                 next_ea = ntohl (e.nextName);
290                 hash = NameHash (e.name);
291                 break;
292               case MAP_IDHASH:
293                 next_ea = ntohl (e.nextID);
294                 hash = IDHash (id);
295                 break;
296               default:
297                 fprintf (stderr, "unknown hash table type %d\n", hashType);
298                 return PRBADARG;
299             }
300
301             if (map[ei] & bit) {
302                 fprintf (stderr,
303                          "Entry found twice in hash table: bucket %d\n", hi);
304                 if (hi != hash)
305                     fprintf (stderr,
306                              "also in wrong bucket: should be in %d\n", hash);
307                 if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
308                 break;
309             }
310             map[ei] |= bit;
311
312             flags = ntohl(e.flags);
313             switch (flags & PRTYPE) {
314               case PRFREE:
315                 fprintf (stderr, "ENTRY IS FREE");
316                 goto abort;
317               case PRCONT:
318                 fprintf (stderr, "ENTRY IS CONTINUATION");
319                 goto abort;
320               case PRGRP:
321               case PRUSER:
322                 break;
323               case PRCELL:
324               case PRFOREIGN:
325               case PRINST:
326                 fprintf (stderr,
327                          "ENTRY IS unexpected type (flags=0x%x)\n", flags);
328                 break;
329               default:
330                 fprintf (stderr,
331                          "ENTRY IS OF unknown type (flags=0x%x)\n", flags);
332                 goto abort;
333             }
334
335             if (hash != hi) {
336                 fprintf (stderr, "entry hashed in bucket %d should be %d\n",
337                          hi, hash);
338               abort:
339                 if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
340                 continue;
341             }
342         }
343     }
344     return 0;
345 }
346
347 afs_int32 WalkNextChain (map, misc, ea, e)
348   char  map[];                          /* one byte per db entry */
349   struct misc_data *misc;               /* stuff to keep track of */
350   afs_int32 ea;
351   struct prentry *e;
352 {
353     afs_int32 head;
354     int bit;
355     afs_int32 code;
356     struct prentry c;                   /* continuation entry */
357     afs_int32 na;                               /* next thread */
358     int ni;
359     afs_int32 eid;
360     int count;                          /* number of members */
361     int i;
362     int noErrors = 1;
363     int length;                         /* length of chain */
364
365     if (e) {
366         head = ntohl(e->next);
367         eid = ntohl(e->id);
368         bit = MAP_CONT;
369         count = 0;                      /* set to >9999 if list ends early */
370         for (i=0; i<PRSIZE; i++) {
371             afs_int32 id = ntohl(e->entries[i]);
372             if (id == PRBADID) continue;
373             else if (id) {
374                 int eid_s, id_s;
375                 count++;
376                 /* in case the ids are large, convert to pure sign. */
377                 if (id > 0) id_s = 1; else id_s = -1; 
378                 if (eid > 0) eid_s = 1; else eid_s = -1; 
379                 if (id_s * eid_s > 0) { /* sign should be different */
380                     fprintf (stderr,
381                              "Bad user/group dicotomy in membership list\n");
382                     if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
383                     noErrors = 0;
384                 }
385                 /* count each user as a group, and each group a user is in */
386                 if ((id >= misc->minId) && (id <= misc->maxId) &&
387                     (id != ANONYMOUSID))
388                     misc->idmap[id - misc->minId]++;
389             }
390             else if (head) count=9999;
391             else break;
392         }
393     }
394     else {
395         head = ntohl(cheader.freePtr);
396         bit = MAP_FREE;
397     }
398
399     length = 0;
400     for (na=head; na; na=ntohl(c.next)) {
401         code = ConvertDiskAddress (na, &ni);
402         if (code) {
403             fprintf (stderr, "Bad continuation ptr %d", na);
404             if (e == 0) fprintf (stderr, "walking free list");
405             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
406             if (na != head) {
407                 fprintf (stderr, "last block: \n");
408                 if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
409             }
410             return 0;
411         }
412         code = pr_Read (na, (char *)&c, sizeof(c));
413         if (code) return code;
414         length++;
415
416         if (map[ni]) {
417             fprintf (stderr, "Continuation entry reused\n");
418             if (e == 0) fprintf (stderr, "walking free list");
419             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
420             if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
421             noErrors = 0;
422             break;
423         }
424         map[ni] |= bit;
425         if (e && (ntohl(c.id) != eid)) {
426             fprintf (stderr, "Continuation id mismatch\n");
427             if (e == 0) fprintf (stderr, "walking free list");
428             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
429             if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
430             noErrors = 0;
431             continue;
432         }
433
434         /* update membership count */
435         if (e) for (i=0; i<COSIZE; i++) {
436             afs_int32 id = ntohl(c.entries[i]);
437             if (id == PRBADID) continue;
438             else if (id) {
439                 int eid_s, id_s;
440                 count++;
441                 /* in case the ids are large, convert to pure sign. */
442                 if (id > 0) id_s = 1; else id_s = -1; 
443                 if (eid > 0) eid_s = 1; else eid_s = -1; 
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)) return PRDBBAD;
448                     if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
449                     noErrors = 0;
450                 }
451                 /* count each user as a group, and each group a user is in */
452                 if ((id >= misc->minId) && (id <= misc->maxId) &&
453                     (id != ANONYMOUSID))
454                     misc->idmap[id - misc->minId]++;
455             }
456             else if (c.next) count = 9999;
457             else break;
458         }
459     }
460     if (e && noErrors && (count != ntohl(e->count))) {
461         if (count > 9999) fprintf (stderr, "Membership list ends early\n");
462         fprintf (stderr, "Count was %d should be %d\n",
463                  count, ntohl(e->count));
464         if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
465     }
466
467     if (e) {
468         if (length > misc->maxContLength) misc->maxContLength = length;
469     }
470     else misc->freeLength = length;
471
472     return 0;
473 }
474
475 afs_int32 WalkOwnedChain (map, misc, ea, e)
476   char  map[];                          /* one byte per db entry */
477   struct misc_data *misc;               /* stuff to keep track of */
478   afs_int32 ea;
479   struct prentry *e;
480 {
481     afs_int32 head;
482     afs_int32 code;
483     struct prentry c;                   /* continuation entry */
484     afs_int32 na;                               /* next thread */
485     int ni;
486     afs_int32 eid;
487     int length;                         /* length of chain */
488
489     if (e) {
490         head = ntohl(e->owned);
491         eid = ntohl(e->id);
492     }
493     else head = ntohl(cheader.orphan);
494
495     length = 0;
496     for (na=head; na; na=ntohl(c.nextOwned)) {
497         code = ConvertDiskAddress (na, &ni);
498         if (code) {
499             fprintf (stderr, "Bad owned list ptr %d", na);
500             if (e == 0) fprintf (stderr, "walking orphan list");
501             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
502             if (na != head) {
503                 fprintf (stderr, "last block: \n");
504                 if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
505             }
506             return 0;
507         }
508         code = pr_Read (na, (char *)&c, sizeof(c));
509         if (code) return code;
510         length++;
511
512         if (map[ni] & MAP_OWNED) {
513             fprintf (stderr, "Entry on multiple owner chains\n");
514             if (e == 0) fprintf (stderr, "walking orphan list");
515             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
516             if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
517             break;
518         }
519         map[ni] |= MAP_OWNED;
520         if ((map[ni] & MAP_HASHES) != MAP_HASHES) {
521             fprintf (stderr, "Owned entry not hashed properly\n");
522 abort:
523             if (e == 0) fprintf (stderr, "walking orphan list");
524             else if (PrintEntryError (misc, ea, e, 2)) return PRDBBAD;
525             if (PrintEntryError (misc, na, &c, 4)) return PRDBBAD;
526             continue;
527         }
528         if (e) {
529             if (ntohl(c.owner) != eid) {
530                 fprintf (stderr, "Owner id mismatch\n");
531                 goto abort;
532             }
533         }
534         else /* orphan */ if (c.owner) {
535             fprintf (stderr, "Orphan group owner not zero\n");
536             goto abort;
537         }
538     }
539
540     if (e) {
541         if (length > misc->maxOwnerLength) misc->maxOwnerLength = length;
542     }
543     else misc->orphanLength = length;
544
545     return 0;
546 }
547
548 afs_int32 WalkChains (map, misc)
549   char  map[];                          /* one byte per db entry */
550   struct misc_data *misc;               /* stuff to keep track of */
551 {
552     afs_int32 code;
553     int  ei;
554     afs_int32 ea;                               /* entry's db addr */
555     struct prentry e;
556     afs_int32 id;
557     int  type;
558
559     /* check all entries found in hash table walks */
560     for (ei=0; ei < misc->nEntries; ei++) if (map[ei] & MAP_HASHES) {
561         ea = ei * sizeof(struct prentry) + sizeof(cheader);
562         code = pr_Read (ea, (char *)&e, sizeof(e));
563         if (code) return code;
564
565         if ((map[ei] & MAP_HASHES) != MAP_HASHES) {
566             fprintf (stderr, "entry not in both hashtables\n");
567             if ((map[ei] & MAP_NAMEHASH) != MAP_NAMEHASH)
568                 fprintf (stderr, "--> entry not in Name hashtable\n");
569             if ((map[ei] & MAP_IDHASH) != MAP_IDHASH)
570                 fprintf (stderr, "--> entry not in ID hashtable\n");
571
572           abort:
573             if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
574             continue;
575         }
576         
577         id = ntohl (e.id);
578
579         type = ntohl (e.flags) & PRTYPE;
580         switch (type) {
581           case PRGRP:
582             if (id >= 0) {
583                 fprintf (stderr, "Group id not negative\n");
584                 goto abort;
585             }
586             /* special case sysadmin: it owns itself */
587             if (id == SYSADMINID) {
588                 if (ntohl(e.owner) != SYSADMINID) {
589                     fprintf (stderr,
590                              "System:administrators doesn't own itself\n");
591                     goto abort;
592                 }
593             }
594             code = WalkOwnedChain (map, misc, ea, &e);
595             if (code) return code;
596             code = WalkNextChain (map, misc, ea, &e);
597             if (code) return code;
598             misc->ngroups++;
599             break;
600           case PRUSER:
601             if (id <= 0) {
602                 fprintf (stderr, "User id negative\n");
603                 goto abort;
604             }
605
606             /* Users are owned by sysadmin, but sysadmin doesn't have an owner
607              * chain.  Check this then set the owned bit. */
608             if (ntohl(e.owner) != SYSADMINID) {
609                 fprintf (stderr, "User not owned by system:administrators\n");
610                 goto abort;
611             }
612             if (e.nextOwned) {
613                 fprintf (stderr, "User has owned pointer\n");
614                 goto abort;
615             }
616             map[ei] |= MAP_OWNED;
617
618             code = WalkOwnedChain (map, misc, ea, &e);
619             if (code) return code;
620             code = WalkNextChain (map, misc, ea, &e);
621             if (code) return code;
622             if (strchr(e.name,'@') == 0) {
623                 misc->nusers++;             /* Not a foreign user */
624             } else {
625                 misc->nforeigns++;          /* A foreign user */
626             }
627             break;
628           case PRFREE:
629           case PRCONT:
630           case PRCELL:
631             misc->ncells++;
632             break;
633           case PRFOREIGN:
634              fprintf (stderr, "ENTRY IS unexpected type [PRFOREIGN] (flags=0x%x)\n", e.flags);
635              break;
636           case PRINST:
637             misc->ninsts++;
638             break;
639           default:
640             fprintf (stderr, "entry with unexpected type");
641             goto abort;
642         }
643     }
644
645     return 0;
646 }
647
648 afs_int32 GC (map, misc)
649   char map[];
650   struct misc_data *misc;
651 {
652     afs_int32 code;
653     int ei;
654     afs_int32 ea;
655     struct prentry e;
656     char m;
657
658     for (ei=0; ei<misc->nEntries; ei++) {
659         ea = ei * sizeof(struct prentry) + sizeof(cheader);
660         code = pr_Read (ea, (char *)&e, sizeof(e));
661         if (code) return code;
662         m = map[ei];
663         if (m == 0) {
664             fprintf (stderr, "Unreferenced entry:");
665             if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
666         }
667         /* all users and groups should be owned, and their membership counts
668          * should be okay */
669         else if ((m & MAP_HASHES) == MAP_HASHES) {
670             afs_int32 id;
671             int refCount;
672             if (!(m & MAP_OWNED)) {
673                 fprintf (stderr, "Entry not on any owner chain:\n");
674                 if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
675             }
676             id = ntohl(e.id);
677             if ((id >= misc->minId) && (id <= misc->maxId) &&
678                 (id != ANONYMOUSID) &&
679                 ((refCount = misc->idmap[id - misc->minId]) !=
680                  ntohl(e.count))) {
681                 afs_int32 na;
682                 fprintf (stderr, "Entry membership count is inconsistent: %d entries refer to this one\n", refCount);
683                 if (PrintEntryError (misc, ea, &e, 2)) return PRDBBAD;
684
685                 /* get continuation blocks too */
686                 for (na=ntohl(e.next); na; na=ntohl(e.next)) {
687                     int ni;
688                     code = ConvertDiskAddress (na, &ni);
689                     if (code) return code;
690                     code = pr_Read (na, (char *)&e, sizeof(e));
691                     if (code) return code;
692                     if (PrintEntryError (misc, na, &e, 4)) return PRDBBAD;
693                 }
694             }
695         }
696     }
697     return 0;
698 }
699
700 char *QuoteName (s)
701   char *s;
702 {
703     char *qs;
704     if (strpbrk (s," \t")) {
705         qs = (char *)malloc (strlen(s)+3);
706         strcpy (qs, "\"");
707         strcat (qs, s);
708         strcat (qs, "\"");
709     } else qs = s;
710     return qs;
711 }
712
713 afs_int32 DumpRecreate (map, misc)
714   char map[];
715   struct misc_data *misc;
716 {
717     afs_int32 code;
718     int ei;
719     afs_int32 ea;
720     struct prentry e;
721     afs_int32 id;
722     afs_int32 flags;
723     afs_int32 owner;
724     char *name;
725     int  builtinUsers = 0;
726     int  createLow = 0;                 /* users uncreate from here */
727     afs_int32 *idmap;                   /* map of all id's */
728     int found;    
729     FILE *rc;
730
731     rc = misc->recreate;
732     idmap = misc->idmap;
733     memset(idmap, 0, misc->idRange*sizeof(misc->idmap[0]));
734     do {
735         found = 0;
736         for (ei=createLow; ei<misc->nEntries; ei++) {
737             if ((map[ei] & MAP_HASHES) &&
738                 (map[ei] & MAP_RECREATE) == 0) {
739                 afs_int32 mask;
740                 afs_int32 access;
741                 int gq,uq;
742                 
743                 ea = ei * sizeof(struct prentry) + sizeof(cheader);
744                 code = pr_Read (ea, (char *)&e, sizeof(e));
745                 if (code) return code;
746
747                 if (misc->listentries)
748                    pr_PrintEntry(stdout, 0/*not in host order*/, ea, &e, 0);
749
750                 id = ntohl(e.id);
751                 flags = ntohl(e.flags);
752                 owner = ntohl(e.owner);
753                 name = QuoteName(e.name);
754
755                 if (!strcmp (e.name, "system:administrators") ||
756                     !strcmp (e.name, "system:anyuser") ||
757                     !strcmp (e.name, "system:authuser") ||
758                     !strcmp (e.name, "system:backup") ||
759                     !strcmp (e.name, "anonymous")) {
760                    builtinUsers++;
761                    goto user_done;
762                 }
763
764                 /* check for duplicate id.  This may still lead to duplicate
765                  * names. */
766                 if (idmap[id-misc->minId]) {
767                     fprintf (stderr,
768                              "Skipping entry with duplicate id %di\n", id);
769                     goto user_done;
770                 }
771
772                 /* If owner doesn't exist skip for now, unless we're our own
773                  * owner.  If so, a special case allows a group to own itself
774                  * if caller is sysadmin.  This leaves only owner cycles to
775                  * deal with. */
776                 
777                 if ((owner < misc->minId) || (owner > misc->maxId)) {
778                     if (owner == ANONYMOUSID) fprintf (stderr, "Warning: id %di is owned by ANONYMOUS; using sysadmin instead\n", id);
779                     else fprintf (stderr, "Bogus owner (%d) of id %di; using sysadmin instead\n", owner, id);
780                     owner = SYSADMINID;
781                 }
782                 if (id == owner) {
783                     fprintf (stderr,
784                              "Warning: group %s is self owning\n", name);
785                 }
786                 else if (owner == 0) {
787                     fprintf (stderr, "Warning: orphan group %s will become self owning.\n", name);
788                     owner = id;
789                 }
790                 else if (idmap[owner-misc->minId] == 0) goto user_skip;
791
792                 if (rc) fprintf (rc, "cr %s %d %d\n", name, id, owner);
793                 
794                 gq = uq = access = mask = 0;
795                 if (flags & PRACCESS) {
796                     access = (flags >> PRIVATE_SHIFT);
797                     mask |= PR_SF_ALLBITS;
798                 }
799                 if (flags & PRQUOTA) {
800                     gq = ntohl(e.ngroups);
801                     uq = ntohl(e.nusers);
802                     mask |= PR_SF_NGROUPS | PR_SF_NUSERS;
803                 }
804                 if (mask && rc) {
805                     fprintf (rc, "sf %d %x %x %d %d\n",
806                              id, mask, access, gq, uq);
807                 }
808 user_done:
809                 map[ei] |= MAP_RECREATE;
810                 if (id != ANONYMOUSID) idmap[id-misc->minId]++;
811                 found++;
812             }
813             /* bump low water mark if possible */
814             if (ei == createLow) createLow++;
815 user_skip:;
816         }
817         misc->verbose = 0;
818     } while (found);
819
820     /* Now create the entries with circular owner dependencies and make them
821      * own themselves.  This is the only way to create them with the correct
822      * names. */
823     for (ei=0; ei<misc->nEntries; ei++)
824         if (((map[ei] & MAP_HASHES) == MAP_HASHES) &&
825             (map[ei] & MAP_RECREATE) == 0) {
826             ea = ei * sizeof(struct prentry) + sizeof(cheader);
827             code = pr_Read (ea, (char *)&e, sizeof(e));
828             if (code) return code;
829
830             id = ntohl(e.id);
831             name = QuoteName(e.name);
832             fprintf (stderr, "Warning: group %s in self owning cycle\n", name);
833             if (rc) fprintf (rc, "cr %s %d %d\n", name, id, id);
834             idmap[id-misc->minId]++;
835         }
836     for (ei=0; ei<misc->nEntries; ei++)
837         if (((map[ei] & MAP_HASHES) == MAP_HASHES) &&
838             (map[ei] & MAP_RECREATE) == 0) {
839             ea = ei * sizeof(struct prentry) + sizeof(cheader);
840             code = pr_Read (ea, (char *)&e, sizeof(e));
841             if (code) return code;
842
843             owner = ntohl(e.owner);
844             if (idmap[owner-misc->minId] == 0) {
845                 fprintf (stderr,
846                          "Skipping chown of '%s' to non-existant owner %di\n",
847                          e.name, owner);
848             }
849             else if (rc) fprintf (rc, "ce %d \"\" %d 0\n",
850                                   ntohl(e.id), e.owner);
851         }
852
853     if (rc == 0) return 0;
854
855     /* Reconstruct membership information based on the groups' user lists. */
856     for (ei=0; ei<misc->nEntries; ei++) {
857         if ((map[ei] & MAP_HASHES) == MAP_HASHES) {
858             ea = ei * sizeof(struct prentry) + sizeof(cheader);
859             code = pr_Read (ea, (char *)&e, sizeof(e));
860             if (code) return code;
861
862             id = ntohl(e.id);
863             flags = ntohl(e.flags);
864
865             if ((id < 0) && (flags & PRGRP)) {
866                 int count = 0;
867                 afs_int32 na;
868                 int i;
869                 for (i=0; i<PRSIZE; i++) {
870                     afs_int32 uid = ntohl(e.entries[i]);
871                     if (uid == 0) break;
872                     if (uid == PRBADID) continue;
873                     if (uid > 0) {
874                         fprintf (rc, "au %d %d\n", uid, id);
875                         count++;
876                     } else fprintf (stderr,
877                                     "Skipping %di in group %di\n", uid, id);
878                 }
879                 na = ntohl(e.next);
880                 while (na) {
881                     struct prentry c;
882                     code = pr_Read (na, (char *)&c, sizeof(c));
883                     if (code) return code;
884                     
885                     if ((id == ntohl(c.id)) && (ntohl(c.flags) & PRCONT)) {
886                         for (i=0; i<COSIZE; i++) {
887                             afs_int32 uid = ntohl(c.entries[i]);
888                             if (uid == 0) break;
889                             if (uid == PRBADID) continue;
890                             if (uid > 0) {
891                                 fprintf (rc, "au %d %d\n", uid, id);
892                                 count++;
893                             } else fprintf (stderr,
894                                             "Skipping %di in group %di\n",
895                                             uid, id);
896                         }
897                     } else {
898                         fprintf (stderr,
899                                  "Skipping continuation block at %d\n", na);
900                         break;
901                     }
902                     na = ntohl(c.next);
903                 }
904                 if (count != ntohl(e.count))
905                     fprintf (stderr, "Group membership count problem found %d should be %d\n", count, ntohl(e.count));
906             } else if ((id < 0) || (flags & PRGRP)) {
907                 fprintf (stderr, "Skipping group %di\n", id);
908             }
909         }
910     }
911     return 0;
912 }
913
914 afs_int32 CheckPrDatabase (misc)
915   struct misc_data *misc;               /* info & statistics */
916 {
917     afs_int32 code;
918     afs_int32 eof;
919     int n;
920     char *map;                          /* map of each entry in db */
921
922     eof = ntohl (cheader.eofPtr);
923     eof -= sizeof(cheader);
924     n = eof / sizeof(struct prentry);
925     if ((eof < 0) || (n*sizeof(struct prentry) != eof)) {
926         code = PRDBBAD;
927         com_err (whoami, code,
928                  "eof ptr no good: eof=%d, sizeof(prentry)=%d",
929                  eof, sizeof(struct prentry));
930       abort:
931         return code;
932     }
933     if (misc->verbose)
934        printf ("Database has %d entries\n", n);
935     map = (char *)malloc (n);
936     memset(map, 0, n);
937     misc->nEntries = n;
938
939     if (misc->verbose) {
940        printf ("\nChecking name hash table\n");
941        fflush (stdout);
942     }
943     code = WalkHashTable (cheader.nameHash, MAP_NAMEHASH, map, misc);
944     if (code) {
945         com_err (whoami, code, "walking name hash");
946         goto abort;
947     }
948     if (misc->verbose) {
949        printf ("\nChecking id hash table\n");
950        fflush (stdout);
951     }
952     code = WalkHashTable (cheader.idHash, MAP_IDHASH, map, misc);
953     if (code) {
954         com_err (whoami, code, "walking id hash");
955         goto abort;
956     }
957
958     /* hash walk calculates min and max id */
959     n = ((misc->maxId > misc->maxForId) ? misc->maxId : misc->maxForId);
960     misc->idRange = n - misc->minId + 1;
961     misc->idmap = (afs_int32 *)malloc (misc->idRange * sizeof(afs_int32));
962     if (!misc->idmap) {
963         com_err (whoami, 0, "Unable to malloc space for max ids of %d",
964                  misc->idRange);
965         code = -1;
966         goto abort;
967     }
968     memset(misc->idmap, 0, misc->idRange*sizeof(misc->idmap[0]));
969
970     if (misc->verbose) {
971        printf ("\nChecking entry chains\n");
972        fflush (stdout);
973     }
974     code = WalkChains (map, misc);
975     if (code) {
976         com_err (whoami, code, "walking chains");
977         goto abort;
978     }
979     if (misc->verbose) {
980        printf ("\nChecking free list\n");
981        fflush (stdout);
982     }
983     code = WalkNextChain (map, misc, 0, 0);
984     if (code) {
985         com_err (whoami, code, "walking free list");
986         goto abort;
987     }
988     if (misc->verbose) {
989        printf ("\nChecking orphans list\n");
990        fflush (stdout);
991     }
992     code = WalkOwnedChain (map, misc, 0, 0);
993     if (code) {
994         com_err (whoami, code, "walking orphan list");
995         goto abort;
996     }
997
998     if (misc->verbose) {
999        printf ("\nChecking for unreferenced entries\n");
1000        fflush (stdout);
1001     }
1002     code = GC (map, misc);
1003     if (code) {
1004         com_err (whoami, code, "looking for unreferenced entries");
1005         goto abort;
1006     }
1007
1008     DumpRecreate (map, misc);   /* check for owner cycles */
1009     if (misc->recreate) fclose (misc->recreate);
1010
1011     if (misc->anon != 2)                /* once for each hash table */
1012         fprintf (stderr, "Problems with ANON=%d\n", misc->anon);
1013     if (misc->ncells || misc->ninsts)
1014         fprintf (stderr, "Unexpected entry type\n");
1015     if (misc->nusers != ntohl(cheader.usercount)) {
1016         fprintf (stderr,
1017                  "User count inconsistent: should be %d, header claims: %d\n",
1018                  misc->nusers, ntohl(cheader.usercount));
1019     }
1020     if (misc->ngroups != ntohl(cheader.groupcount)) {
1021         fprintf (stderr,
1022                  "Group count inconsistent: should be %d, header claims: %d\n",
1023                  misc->ngroups, ntohl(cheader.groupcount));
1024     }
1025     if (misc->maxId > ntohl(cheader.maxID))
1026         fprintf (stderr, "Database's max user Id (%d) is smaller than largest user's Id (%d).\n", ntohl(cheader.maxID), misc->maxId);
1027     if (misc->minId < ntohl(cheader.maxGroup))
1028         fprintf (stderr, "Database's max group Id (%d) is smaller than largest group's Id (%d).\n", ntohl(cheader.maxGroup), misc->minId);
1029
1030     if (misc->verbose) {
1031        printf ("\nMaxId = %d, MinId = %d, MaxForeignId = %d\n",
1032                misc->maxId, misc->minId, misc->maxForId);
1033        printf ("Free list is %d entries in length, %d groups on orphan list\n",
1034                misc->freeLength, misc->orphanLength);
1035        printf ("The longest owner list is %d, the longest continuation block chain is %d\n",
1036                misc->maxOwnerLength, misc->maxContLength);
1037        printf ("%d users ; %d foreign users ; and %d groups\n", 
1038                misc->nusers, misc->nforeigns, misc->ngroups);
1039     }
1040
1041     return code;
1042 }
1043
1044 #include "AFS_component_version_number.c"
1045
1046 WorkerBee (as, arock)
1047   struct cmd_syndesc *as;
1048   char *arock;
1049 {
1050     afs_int32 code;
1051     char *recreateFile;
1052     struct misc_data misc;              /* info & statistics */
1053
1054     initialize_PT_error_table();
1055     initialize_U_error_table();
1056     
1057     pr_dbaseName = AFSDIR_SERVER_PRDB_FILEPATH;
1058     memset(&misc, 0, sizeof(misc));
1059
1060     pr_dbaseName     =  as->parms[0].items->data;        /* -database */
1061     misc.listuheader = (as->parms[1].items ? 1 : 0);     /* -uheader  */
1062     misc.listpheader = (as->parms[2].items ? 1 : 0);     /* -pheader  */
1063     misc.listentries = (as->parms[3].items ? 1 : 0);     /* -entries  */
1064     misc.verbose     = (as->parms[4].items ? 1 : 0);     /* -verbose  */
1065     recreateFile     = (as->parms[5].items ? as->parms[5].items->data :
1066                                              NULL); /* -rebuild  */
1067
1068     fd = open (pr_dbaseName, O_RDONLY, 0);
1069     if (fd == -1) {
1070         com_err (whoami, errno, "Open failed on db %s", pr_dbaseName);
1071         exit(2);
1072     }
1073
1074     /* Read the ubik header */
1075     if (misc.listuheader) {
1076        readUbikHeader(&misc);
1077     }
1078
1079     code = ReadHeader();
1080     if (code) return code;
1081     if (misc.listpheader) printheader(&cheader);
1082
1083     if (recreateFile) {
1084         misc.recreate = fopen (recreateFile, "w");
1085         if (misc.recreate == 0) {
1086             com_err (whoami, errno,
1087                      "can't create file for recreation instructions: %s",
1088                      recreateFile);
1089             exit (4);
1090         }
1091     }
1092     code = CheckPrDatabase (&misc);
1093     if (code) {
1094         com_err (whoami, code, "Checking prserver database");
1095         exit (3);
1096     }
1097     exit (0);
1098 }
1099
1100 main (argc, argv)
1101   int   argc;
1102   char *argv[];
1103 {
1104   struct cmd_syndesc *ts;
1105
1106   setlinebuf(stdout);
1107
1108   ts=cmd_CreateSyntax(NULL, WorkerBee, NULL, "PRDB check");
1109   cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "ptdb_file");
1110   cmd_AddParm(ts, "-uheader",  CMD_FLAG,   CMD_OPTIONAL, "Display UBIK header");
1111   cmd_AddParm(ts, "-pheader",  CMD_FLAG,   CMD_OPTIONAL, "Display KADB header");
1112   cmd_AddParm(ts, "-entries",  CMD_FLAG,   CMD_OPTIONAL, "Display entries");
1113   cmd_AddParm(ts, "-verbose",  CMD_FLAG,   CMD_OPTIONAL, "verbose");
1114   cmd_AddParm(ts, "-rebuild",  CMD_SINGLE, CMD_OPTIONAL|CMD_HIDE, "out_file");
1115
1116   return cmd_Dispatch(argc, argv);
1117 }