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