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