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