2 * Copyright 2000, International Business Machines Corporation and others.
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
11 #include <afsconfig.h>
12 #include <afs/param.h>
16 * (3) Define a structure, idused, instead of an
17 * array of long integers, idmap, to count group
18 * memberships. These structures are on a linked
19 * list, with each structure containing IDCOUNT
21 * (4) Add new functions to processs the structure
23 * zeromap(), idcount(), inccount().
24 * (5) Add code, primarily in WalkNextChain():
25 * 1. Test id's, allowing groups within groups.
26 * 2. Count the membership list for supergroups,
27 * and follow the continuation chain for
29 * (6) Add fprintf statements for various error
34 #include <sys/types.h>
37 #include <WINNT/afsevent.h>
41 #include <netinet/in.h>
48 #include <afs/cellconfig.h>
49 #include <afs/afsutil.h>
52 #include <afs/com_err.h>
60 struct prheader cheader;
62 const char *pr_dbaseName;
63 char *whoami = "db_verify";
64 #define UBIK_HEADERSIZE 64
67 printheader(struct prheader *h)
69 printf("Version = %d\n", ntohl(h->version));
70 printf("Header Size = %d\n", ntohl(h->headerSize));
71 printf("Free Ptr = 0x%x\n", ntohl(h->freePtr));
72 printf("EOF Ptr = 0x%x\n", ntohl(h->eofPtr));
73 printf("Max Group ID = %d\n", ntohl(h->maxGroup));
74 printf("Max User ID = %d\n", ntohl(h->maxID));
75 printf("Max Foreign ID = %d\n", ntohl(h->maxForeign));
76 /* printf("Max Sub/Super ID = %d\n", ntohl(h->maxInst)); */
77 printf("Orphaned groups = %d\n", ntohl(h->orphan));
78 printf("User Count = %d\n", ntohl(h->usercount));
79 printf("Group Count = %d\n", ntohl(h->groupcount));
80 /* printf("Foreign Count = %d\n", ntohl(h->foreigncount)); NYI */
81 /* printf("Sub/super Count = %d\n", ntohl(h->instcount)); NYI */
82 printf("Name Hash = %d buckets\n", HASHSIZE);
83 printf("ID Hash = %d buckets\n", HASHSIZE);
88 pr_Read(afs_int32 pos, void *buff, afs_int32 len)
92 code = lseek(fd, UBIK_HEADERSIZE + pos, 0);
96 code = read(fd, buff, len);
106 * Initializes the a transaction on the database and reads the header into
107 * the static variable cheader. If successful it returns a read-locked
108 * transaction. If ubik reports that cached database info should be up to date
109 * the cheader structure is not re-read from the ubik.
117 code = pr_Read(0, (char *)&cheader, sizeof(cheader));
119 afs_com_err(whoami, code, "couldn't read header");
122 /* Check and see if database exists and is approximately OK. */
123 if (ntohl(cheader.headerSize) != sizeof(cheader)
124 || ntohl(cheader.eofPtr) == 0) {
127 afs_com_err(whoami, PRDBBAD, "header is bad");
136 /* returns hash bucket for x */
137 return ((abs(x)) % HASHSIZE);
141 NameHash(register char *aname)
143 /* returns hash bucket for aname */
144 register unsigned int hash = 0;
146 /* stolen directly from the HashString function in the vol package */
147 for (i = strlen(aname), aname += i - 1; i--; aname--)
148 hash = (hash * 31) + (*(unsigned char *)aname - 31);
149 return (hash % HASHSIZE);
152 #define MAP_NAMEHASH 1
154 #define MAP_HASHES (MAP_NAMEHASH | MAP_IDHASH)
157 #define MAP_OWNED 0x10
158 #define MAP_RECREATE 0x20
161 int nEntries; /* number of database entries */
162 int anon; /* found anonymous Id */
163 afs_int32 maxId; /* user */
164 afs_int32 minId; /* group */
165 afs_int32 maxForId; /* foreign user id */
166 #if defined(SUPERGROUPS)
170 afs_int32 idcount[IDCOUNT];
171 struct idused *idnext;
174 int idRange; /* number of ids in map */
175 afs_int32 *idmap; /* map of all id's: midId is origin */
176 #endif /* SUPERGROUPS */
177 int nusers; /* counts of each type */
182 int maxOwnerLength; /* longest owner chain */
183 int maxContLength; /* longest chain of cont. blks */
184 int orphanLength; /* length of orphan list */
185 int freeLength; /* length of free list */
190 FILE *recreate; /* stream for recreate instructions */
193 #if defined(SUPERGROUPS)
194 void zeromap(struct idused *idmap);
195 void inccount(struct idused **idmapp, int id);
196 int idcount(struct idused **idmapp, int id);
200 readUbikHeader(struct misc_data *misc)
203 struct ubik_hdr uheader;
205 offset = lseek(fd, 0, 0);
207 printf("error: lseek to 0 failed: %d %d\n", offset, errno);
211 /* now read the info */
212 r = read(fd, &uheader, sizeof(uheader));
213 if (r != sizeof(uheader)) {
214 printf("error: read of %" AFS_SIZET_FMT " bytes failed: %d %d\n",
215 sizeof(uheader), r, errno);
219 uheader.magic = ntohl(uheader.magic);
220 uheader.size = ntohl(uheader.size);
221 uheader.version.epoch = ntohl(uheader.version.epoch);
222 uheader.version.counter = ntohl(uheader.version.counter);
224 if (misc->listuheader) {
225 printf("Ubik Header\n");
226 printf(" Magic = 0x%x\n", uheader.magic);
227 printf(" Size = %u\n", uheader.size);
228 printf(" Version.epoch = %u\n", uheader.version.epoch);
229 printf(" Version.counter = %u\n", uheader.version.counter);
232 if (uheader.size != UBIK_HEADERSIZE)
233 printf("Ubik header size is %u (should be %u)\n", uheader.size,
235 if (uheader.magic != UBIK_MAGIC)
236 printf("Ubik header magic is 0x%x (should be 0x%x)\n", uheader.magic,
243 ConvertDiskAddress(afs_uint32 ea, int *eiP)
249 if (ea < sizeof(cheader))
251 if (ea >= ntohl(cheader.eofPtr))
253 ea -= sizeof(cheader);
254 i = ea / sizeof(struct prentry);
255 if (i * sizeof(struct prentry) != ea)
257 /* if ((i < 0) || (i >= misc->nEntries)) return PRDBADDR; */
263 PrintEntryError(struct misc_data *misc, afs_int32 ea, struct prentry *e, int indent)
266 pr_PrintEntry(stderr, /*net order */ 0, ea, e, indent);
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 */
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 */
289 for (hi = 0; hi < HASHSIZE; hi++) {
291 next_ea = ntohl(hashtable[hi]);
293 code = ConvertDiskAddress(next_ea, &ei);
295 fprintf(stderr, "Bad chain address %d\n", next_ea);
297 fprintf(stderr, "Last entry in chain:\n");
298 if (PrintEntryError(misc, ea, &e, 2))
301 fprintf(stderr, "Skipping remainder of hash bucket %d\n", hi);
305 code = pr_Read(ea, (char *)&e, sizeof(e));
311 if (((e.flags & htonl((PRGRP | PRINST))) == 0)
312 && (strchr(e.name, '@'))) {
314 if (id > misc->maxForId)
317 if (id == ANONYMOUSID)
319 else if (id > misc->maxId)
321 if (id < misc->minId)
327 next_ea = ntohl(e.nextName);
328 hash = NameHash(e.name);
331 next_ea = ntohl(e.nextID);
335 fprintf(stderr, "unknown hash table type %d\n", hashType);
341 "Entry found twice in hash table: bucket %d\n", hi);
343 fprintf(stderr, "also in wrong bucket: should be in %d\n",
345 if (PrintEntryError(misc, ea, &e, 2))
351 flags = ntohl(e.flags);
352 switch (flags & PRTYPE) {
354 fprintf(stderr, "ENTRY IS FREE");
357 fprintf(stderr, "ENTRY IS CONTINUATION");
365 fprintf(stderr, "ENTRY IS unexpected type (flags=0x%x)\n",
369 fprintf(stderr, "ENTRY IS OF unknown type (flags=0x%x)\n",
375 fprintf(stderr, "entry hashed in bucket %d should be %d\n",
378 if (PrintEntryError(misc, ea, &e, 2))
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)
395 struct prentry c; /* continuation entry */
396 afs_int32 na; /* next thread */
399 int count = 0; /* number of members, set to > 9999 if */
400 /* list ends early */
403 int length; /* length of chain */
404 #if defined(SUPERGROUPS)
405 int sgcount = 0; /* number of sgentrys */
407 #define g (((struct prentryg *)e))
411 head = ntohl(e->next);
414 #if defined(SUPERGROUPS)
415 sghead = ntohl(g->next);
417 for (i = 0; i < PRSIZE; i++) {
418 afs_int32 id = ntohl(e->entries[i]);
424 /* in case the ids are large, convert to pure sign. */
433 #if defined(SUPERGROUPS)
434 if (id_s > 0 && eid_s > 0) {
436 "User can't be member of user in membership list\n");
437 if (PrintEntryError(misc, ea, e, 2))
442 if (id_s * eid_s > 0) { /* sign should be different */
444 "Bad user/group dicotomy in membership list\n");
445 if (PrintEntryError(misc, ea, e, 2))
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);
455 if ((id >= misc->minId) && (id <= misc->maxId)
456 && (id != ANONYMOUSID))
457 misc->idmap[id - misc->minId]++;
458 #endif /* SUPERGROUPS */
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]);
474 "User can't be member of supergroup list\n");
475 if (PrintEntryError(misc, ea, e, 2))
480 inccount(&misc->idmap, id);
484 #endif /* SUPERGROUPS */
486 head = ntohl(cheader.freePtr);
487 #if defined(SUPERGROUPS)
493 #if defined(SUPERGROUPS)
495 for (na = sghead; na; na = ntohl(c.next)) {
496 code = ConvertDiskAddress(na, &ni);
498 fprintf(stderr, "Bad SGcontinuation ptr %d", na);
499 if (PrintEntryError(misc, ea, e, 2))
502 fprintf(stderr, "last block: \n");
503 if (PrintEntryError(misc, na, &c, 4))
508 code = pr_Read(na, (char *)&c, sizeof(c));
514 fprintf(stderr, "Continuation entry reused\n");
515 if (PrintEntryError(misc, ea, e, 2))
517 if (PrintEntryError(misc, na, &c, 4))
523 if ((ntohl(c.id) != eid)) {
524 fprintf(stderr, "Continuation id mismatch\n");
525 if (PrintEntryError(misc, ea, e, 2))
527 if (PrintEntryError(misc, na, &c, 4))
533 /* update membership count */
534 for (i = 0; i < COSIZE; i++) {
535 afs_int32 id = ntohl(c.entries[i]);
541 /* in case the ids are large, convert to pure sign. */
552 "User can't be member of supergroup list\n");
553 if (PrintEntryError(misc, ea, e, 2))
555 if (PrintEntryError(misc, na, &c, 4))
559 /* count each user as a group, and each group a user is in */
560 if ((id != ANONYMOUSID))
561 inccount(&misc->idmap, id);
568 if (length > misc->maxContLength)
569 misc->maxContLength = length;
570 #endif /* SUPERGROUPS */
572 for (na = head; na; na = ntohl(c.next)) {
573 code = ConvertDiskAddress(na, &ni);
575 fprintf(stderr, "Bad continuation ptr %d", na);
577 fprintf(stderr, "walking free list");
578 else if (PrintEntryError(misc, ea, e, 2))
581 fprintf(stderr, "last block: \n");
582 if (PrintEntryError(misc, na, &c, 4))
587 code = pr_Read(na, (char *)&c, sizeof(c));
593 fprintf(stderr, "Continuation entry reused\n");
595 fprintf(stderr, "walking free list");
596 else if (PrintEntryError(misc, ea, e, 2))
598 if (PrintEntryError(misc, na, &c, 4))
604 if (e && (ntohl(c.id) != eid)) {
605 fprintf(stderr, "Continuation id mismatch\n");
607 fprintf(stderr, "walking free list");
608 else if (PrintEntryError(misc, ea, e, 2))
610 if (PrintEntryError(misc, na, &c, 4))
616 /* update membership count */
618 for (i = 0; i < COSIZE; i++) {
619 afs_int32 id = ntohl(c.entries[i]);
625 /* in case the ids are large, convert to pure sign. */
634 #if defined(SUPERGROUPS)
635 if (id_s > 0 && eid_s > 0) {
637 "User can't be member of user in membership list\n");
638 if (PrintEntryError(misc, ea, e, 2))
640 if (PrintEntryError(misc, na, &c, 4))
645 if (id_s * eid_s > 0) { /* sign should be different */
647 "Bad user/group dicotomy in membership list\n");
648 if (PrintEntryError(misc, ea, e, 2))
650 if (PrintEntryError(misc, na, &c, 4))
654 #endif /* SUPERGROUPS */
655 /* count each user as a group, and each group a user is in */
656 #if defined(SUPERGROUPS)
657 if (!(id < 0 && eid < 0) && (id != ANONYMOUSID))
658 inccount(&misc->idmap, id);
660 if ((id >= misc->minId) && (id <= misc->maxId)
661 && (id != ANONYMOUSID))
662 misc->idmap[id - misc->minId]++;
663 #endif /* SUPERGROUPS */
670 if (e && noErrors && (count != ntohl(e->count))) {
671 #if defined(SUPERGROUPS)
673 fprintf(stderr, "Membership list ends early\n");
676 fprintf(stderr, "Membership list ends early\n");
677 #endif /* SUPERGROUPS */
678 fprintf(stderr, "Count was %d should be %d\n", count,
680 if (PrintEntryError(misc, ea, e, 2))
682 #if defined(SUPERGROUPS)
685 if (e && (e->flags & htonl(PRGRP)) && (sgcount != ntohl(g->countsg))) {
686 fprintf(stderr, "SGCount was %d should be %d\n", sgcount,
688 if (PrintEntryError(misc, ea, e, 2))
694 if (length > misc->maxContLength)
695 misc->maxContLength = length;
697 misc->freeLength = length;
700 #if defined(SUPERGROUPS)
706 WalkOwnedChain(char map[], /* one byte per db entry */
707 struct misc_data *misc, /* stuff to keep track of */
708 afs_int32 ea, struct prentry *e)
712 struct prentry c; /* continuation entry */
713 afs_int32 na; /* next thread */
716 int length; /* length of chain */
719 head = ntohl(e->owned);
722 head = ntohl(cheader.orphan);
725 for (na = head; na; na = ntohl(c.nextOwned)) {
726 code = ConvertDiskAddress(na, &ni);
728 fprintf(stderr, "Bad owned list ptr %d", na);
730 fprintf(stderr, "walking orphan list");
731 else if (PrintEntryError(misc, ea, e, 2))
734 fprintf(stderr, "last block: \n");
735 if (PrintEntryError(misc, na, &c, 4))
740 code = pr_Read(na, (char *)&c, sizeof(c));
745 if (map[ni] & MAP_OWNED) {
746 fprintf(stderr, "Entry on multiple owner chains\n");
748 fprintf(stderr, "walking orphan list");
749 else if (PrintEntryError(misc, ea, e, 2))
751 if (PrintEntryError(misc, na, &c, 4))
755 map[ni] |= MAP_OWNED;
756 if ((map[ni] & MAP_HASHES) != MAP_HASHES) {
757 fprintf(stderr, "Owned entry not hashed properly\n");
760 fprintf(stderr, "walking orphan list");
761 else if (PrintEntryError(misc, ea, e, 2))
763 if (PrintEntryError(misc, na, &c, 4))
768 if (ntohl(c.owner) != eid) {
769 fprintf(stderr, "Owner id mismatch\n");
772 } else /* orphan */ if (c.owner) {
773 fprintf(stderr, "Orphan group owner not zero\n");
779 if (length > misc->maxOwnerLength)
780 misc->maxOwnerLength = length;
782 misc->orphanLength = length;
788 WalkChains(char map[], /* one byte per db entry */
789 struct misc_data *misc) /* stuff to keep track of */
793 afs_int32 ea; /* entry's db addr */
798 /* check all entries found in hash table walks */
799 for (ei = 0; ei < misc->nEntries; ei++)
800 if (map[ei] & MAP_HASHES) {
801 ea = ei * sizeof(struct prentry) + sizeof(cheader);
802 code = pr_Read(ea, (char *)&e, sizeof(e));
806 if ((map[ei] & MAP_HASHES) != MAP_HASHES) {
807 fprintf(stderr, "entry not in both hashtables\n");
808 if ((map[ei] & MAP_NAMEHASH) != MAP_NAMEHASH)
809 fprintf(stderr, "--> entry not in Name hashtable\n");
810 if ((map[ei] & MAP_IDHASH) != MAP_IDHASH)
811 fprintf(stderr, "--> entry not in ID hashtable\n");
814 if (PrintEntryError(misc, ea, &e, 2))
821 type = ntohl(e.flags) & PRTYPE;
825 fprintf(stderr, "Group id not negative\n");
828 /* special case sysadmin: it owns itself */
829 if (id == SYSADMINID) {
830 if (ntohl(e.owner) != SYSADMINID) {
832 "System:administrators doesn't own itself\n");
836 code = WalkOwnedChain(map, misc, ea, &e);
839 code = WalkNextChain(map, misc, ea, &e);
846 #if defined(SUPERGROUPS)
847 fprintf(stderr, "User id not positive\n");
849 fprintf(stderr, "User id negative\n");
854 /* Users are owned by sysadmin, but sysadmin doesn't have an owner
855 * chain. Check this then set the owned bit. */
856 if (ntohl(e.owner) != SYSADMINID) {
858 "User not owned by system:administrators\n");
862 fprintf(stderr, "User has owned pointer\n");
865 map[ei] |= MAP_OWNED;
867 code = WalkOwnedChain(map, misc, ea, &e);
870 code = WalkNextChain(map, misc, ea, &e);
873 if (strchr(e.name, '@') == 0) {
874 misc->nusers++; /* Not a foreign user */
876 misc->nforeigns++; /* A foreign user */
886 "ENTRY IS unexpected type [PRFOREIGN] (flags=0x%x)\n",
893 fprintf(stderr, "entry with unexpected type");
902 GC(char map[], struct misc_data *misc)
910 for (ei = 0; ei < misc->nEntries; ei++) {
911 ea = ei * sizeof(struct prentry) + sizeof(cheader);
912 code = pr_Read(ea, (char *)&e, sizeof(e));
917 fprintf(stderr, "Unreferenced entry:");
918 if (PrintEntryError(misc, ea, &e, 2))
921 /* all users and groups should be owned, and their membership counts
923 else if ((m & MAP_HASHES) == MAP_HASHES) {
926 if (!(m & MAP_OWNED)) {
927 fprintf(stderr, "Entry not on any owner chain:\n");
928 if (PrintEntryError(misc, ea, &e, 2))
932 #if defined(SUPERGROUPS)
933 if ((id != ANONYMOUSID)
934 && ((refCount = idcount(&misc->idmap, id)) != ntohl(e.count)))
936 if ((id >= misc->minId) && (id <= misc->maxId)
937 && (id != ANONYMOUSID)
938 && ((refCount = misc->idmap[id - misc->minId]) !=
940 #endif /* SUPERGROUPS */
944 "Entry membership count is inconsistent: %d entries refer to this one\n",
946 if (PrintEntryError(misc, ea, &e, 2))
949 /* get continuation blocks too */
950 for (na = ntohl(e.next); na; na = ntohl(e.next)) {
952 code = ConvertDiskAddress(na, &ni);
955 code = pr_Read(na, (char *)&e, sizeof(e));
958 if (PrintEntryError(misc, na, &e, 4))
971 if (strpbrk(s, " \t")) {
972 qs = (char *)malloc(strlen(s) + 3);
982 DumpRecreate(char map[], struct misc_data *misc)
992 int builtinUsers = 0;
993 int createLow = 0; /* users uncreate from here */
994 #if defined(SUPERGROUPS)
995 struct idused *idmap; /* map of all id's */
997 afs_int32 *idmap; /* map of all id's */
1002 rc = misc->recreate;
1003 idmap = misc->idmap;
1004 #if defined(SUPERGROUPS)
1007 memset(idmap, 0, misc->idRange * sizeof(misc->idmap[0]));
1011 for (ei = createLow; ei < misc->nEntries; ei++) {
1012 if ((map[ei] & MAP_HASHES) && (map[ei] & MAP_RECREATE) == 0) {
1017 ea = ei * sizeof(struct prentry) + sizeof(cheader);
1018 code = pr_Read(ea, (char *)&e, sizeof(e));
1022 if (misc->listentries)
1023 pr_PrintEntry(stdout, 0 /*not in host order */ , ea, &e,
1027 flags = ntohl(e.flags);
1028 owner = ntohl(e.owner);
1029 name = QuoteName(e.name);
1031 if (!strcmp(e.name, "system:administrators")
1032 || !strcmp(e.name, "system:anyuser")
1033 || !strcmp(e.name, "system:authuser")
1034 || !strcmp(e.name, "system:backup")
1035 || !strcmp(e.name, "anonymous")) {
1040 /* check for duplicate id. This may still lead to duplicate
1042 #if defined(SUPERGROUPS)
1043 if (idcount(&idmap, id))
1045 if (idmap[id - misc->minId])
1048 fprintf(stderr, "Skipping entry with duplicate id %di\n",
1053 /* If owner doesn't exist skip for now, unless we're our own
1054 * owner. If so, a special case allows a group to own itself
1055 * if caller is sysadmin. This leaves only owner cycles to
1058 if ((owner < misc->minId) || (owner > misc->maxId)) {
1059 if (owner == ANONYMOUSID)
1061 "Warning: id %di is owned by ANONYMOUS; using sysadmin instead\n",
1065 "Bogus owner (%d) of id %di; using sysadmin instead\n",
1070 fprintf(stderr, "Warning: group %s is self owning\n",
1072 } else if (owner == 0) {
1074 "Warning: orphan group %s will become self owning.\n",
1078 #if defined(SUPERGROUPS)
1079 else if (!idcount(&idmap, owner))
1082 else if (idmap[owner - misc->minId] == 0)
1087 fprintf(rc, "cr %s %d %d\n", name, id, owner);
1089 gq = uq = access = mask = 0;
1090 if (flags & PRACCESS) {
1091 access = (flags >> PRIVATE_SHIFT);
1092 mask |= PR_SF_ALLBITS;
1094 if (flags & PRQUOTA) {
1095 gq = ntohl(e.ngroups);
1096 uq = ntohl(e.nusers);
1097 mask |= PR_SF_NGROUPS | PR_SF_NUSERS;
1100 fprintf(rc, "sf %d %x %x %d %d\n", id, mask, access, gq,
1104 map[ei] |= MAP_RECREATE;
1105 #if defined(SUPERGROUPS)
1106 if (id != ANONYMOUSID)
1107 inccount(&idmap, id);
1109 if (id != ANONYMOUSID)
1110 idmap[id - misc->minId]++;
1114 /* bump low water mark if possible */
1115 if (ei == createLow)
1122 /* Now create the entries with circular owner dependencies and make them
1123 * own themselves. This is the only way to create them with the correct
1125 for (ei = 0; ei < misc->nEntries; ei++)
1126 if (((map[ei] & MAP_HASHES) == MAP_HASHES)
1127 && (map[ei] & MAP_RECREATE) == 0) {
1128 ea = ei * sizeof(struct prentry) + sizeof(cheader);
1129 code = pr_Read(ea, (char *)&e, sizeof(e));
1134 name = QuoteName(e.name);
1135 fprintf(stderr, "Warning: group %s in self owning cycle\n", name);
1137 fprintf(rc, "cr %s %d %d\n", name, id, id);
1138 #if defined(SUPERGROUPS)
1139 inccount(&idmap, id);
1141 idmap[id - misc->minId]++;
1144 for (ei = 0; ei < misc->nEntries; ei++)
1145 if (((map[ei] & MAP_HASHES) == MAP_HASHES)
1146 && (map[ei] & MAP_RECREATE) == 0) {
1147 ea = ei * sizeof(struct prentry) + sizeof(cheader);
1148 code = pr_Read(ea, (char *)&e, sizeof(e));
1152 owner = ntohl(e.owner);
1153 #if defined(SUPERGROUPS)
1154 if (!idcount(&idmap, owner))
1156 if (idmap[owner - misc->minId] == 0)
1160 "Skipping chown of '%s' to non-existant owner %di\n",
1163 fprintf(rc, "ce %d \"\" %d 0\n", ntohl(e.id), e.owner);
1169 /* Reconstruct membership information based on the groups' user lists. */
1170 for (ei = 0; ei < misc->nEntries; ei++) {
1171 if ((map[ei] & MAP_HASHES) == MAP_HASHES) {
1172 ea = ei * sizeof(struct prentry) + sizeof(cheader);
1173 code = pr_Read(ea, (char *)&e, sizeof(e));
1178 flags = ntohl(e.flags);
1180 if ((id < 0) && (flags & PRGRP)) {
1183 #if defined(SUPERGROUPS)
1187 for (i = 0; i < PRSIZE; i++) {
1188 afs_int32 uid = ntohl(e.entries[i]);
1193 #if !defined(SUPERGROUPS)
1196 fprintf(rc, "au %d %d\n", uid, id);
1198 #if !defined(SUPERGROUPS)
1200 fprintf(stderr, "Skipping %di in group %di\n", uid,
1204 #if defined(SUPERGROUPS)
1205 #define g (*((struct prentryg *)&e))
1206 ng = ntohl(g.nextsg);
1207 for (i = 0; i < SGSIZE; i++) {
1208 afs_int32 uid = ntohl(g.supergroup[i]);
1213 fprintf(rc, "au %d %d\n", uid, id);
1218 code = pr_Read(ng, (char *)&c, sizeof(c));
1222 if ((id == ntohl(c.id)) && (c.flags & htonl(PRCONT))) {
1223 for (i = 0; i < COSIZE; i++) {
1224 afs_int32 uid = ntohl(c.entries[i]);
1229 fprintf(rc, "au %d %d\n", uid, id);
1233 fprintf(stderr, "Skipping continuation block at %d\n",
1240 #endif /* SUPERGROUPS */
1244 code = pr_Read(na, (char *)&c, sizeof(c));
1248 if ((id == ntohl(c.id)) && (c.flags & htonl(PRCONT))) {
1249 for (i = 0; i < COSIZE; i++) {
1250 afs_int32 uid = ntohl(c.entries[i]);
1255 #if !defined(SUPERGROUPS)
1258 fprintf(rc, "au %d %d\n", uid, id);
1260 #if !defined(SUPERGROUPS)
1262 fprintf(stderr, "Skipping %di in group %di\n",
1267 fprintf(stderr, "Skipping continuation block at %d\n",
1273 if (count != ntohl(e.count))
1275 "Group membership count problem found %d should be %d\n",
1276 count, ntohl(e.count));
1277 } else if ((id < 0) || (flags & PRGRP)) {
1278 fprintf(stderr, "Skipping group %di\n", id);
1286 CheckPrDatabase(struct misc_data *misc) /* info & statistics */
1291 char *map; /* map of each entry in db */
1293 eof = ntohl(cheader.eofPtr);
1294 eof -= sizeof(cheader);
1295 n = eof / sizeof(struct prentry);
1296 if ((eof < 0) || (n * sizeof(struct prentry) != eof)) {
1298 afs_com_err(whoami, code, "eof ptr no good: eof=%d, sizeof(prentry)=%d",
1299 eof, sizeof(struct prentry));
1304 printf("Database has %d entries\n", n);
1305 map = (char *)malloc(n);
1309 if (misc->verbose) {
1310 printf("\nChecking name hash table\n");
1313 code = WalkHashTable(cheader.nameHash, MAP_NAMEHASH, map, misc);
1315 afs_com_err(whoami, code, "walking name hash");
1318 if (misc->verbose) {
1319 printf("\nChecking id hash table\n");
1322 code = WalkHashTable(cheader.idHash, MAP_IDHASH, map, misc);
1324 afs_com_err(whoami, code, "walking id hash");
1328 /* hash walk calculates min and max id */
1329 #if defined(SUPERGROUPS)
1332 n = ((misc->maxId > misc->maxForId) ? misc->maxId : misc->maxForId);
1333 misc->idRange = n - misc->minId + 1;
1334 misc->idmap = (afs_int32 *) malloc(misc->idRange * sizeof(afs_int32));
1336 afs_com_err(whoami, 0, "Unable to malloc space for max ids of %d",
1341 memset(misc->idmap, 0, misc->idRange * sizeof(misc->idmap[0]));
1342 #endif /* SUPERGROUPS */
1344 if (misc->verbose) {
1345 printf("\nChecking entry chains\n");
1348 code = WalkChains(map, misc);
1350 afs_com_err(whoami, code, "walking chains");
1353 if (misc->verbose) {
1354 printf("\nChecking free list\n");
1357 code = WalkNextChain(map, misc, 0, 0);
1359 afs_com_err(whoami, code, "walking free list");
1362 if (misc->verbose) {
1363 printf("\nChecking orphans list\n");
1366 code = WalkOwnedChain(map, misc, 0, 0);
1368 afs_com_err(whoami, code, "walking orphan list");
1372 if (misc->verbose) {
1373 printf("\nChecking for unreferenced entries\n");
1376 code = GC(map, misc);
1378 afs_com_err(whoami, code, "looking for unreferenced entries");
1382 DumpRecreate(map, misc); /* check for owner cycles */
1384 fclose(misc->recreate);
1386 if (misc->anon != 2) /* once for each hash table */
1387 fprintf(stderr, "Problems with ANON=%d\n", misc->anon);
1388 if (misc->ncells || misc->ninsts)
1389 fprintf(stderr, "Unexpected entry type\n");
1390 if (misc->nusers != ntohl(cheader.usercount)) {
1392 "User count inconsistent: should be %d, header claims: %d\n",
1393 misc->nusers, ntohl(cheader.usercount));
1395 if (misc->ngroups != ntohl(cheader.groupcount)) {
1397 "Group count inconsistent: should be %d, header claims: %d\n",
1398 misc->ngroups, ntohl(cheader.groupcount));
1400 if (misc->maxId > ntohl(cheader.maxID))
1402 "Database's max user Id (%d) is smaller than largest user's Id (%d).\n",
1403 ntohl(cheader.maxID), misc->maxId);
1404 if (misc->minId < ntohl(cheader.maxGroup))
1406 "Database's max group Id (%d) is smaller than largest group's Id (%d).\n",
1407 ntohl(cheader.maxGroup), misc->minId);
1409 if (misc->verbose) {
1410 printf("\nMaxId = %d, MinId = %d, MaxForeignId = %d\n", misc->maxId,
1411 misc->minId, misc->maxForId);
1413 ("Free list is %d entries in length, %d groups on orphan list\n",
1414 misc->freeLength, misc->orphanLength);
1416 ("The longest owner list is %d, the longest continuation block chain is %d\n",
1417 misc->maxOwnerLength, misc->maxContLength);
1418 printf("%d users ; %d foreign users ; and %d groups\n", misc->nusers,
1419 misc->nforeigns, misc->ngroups);
1426 #include "AFS_component_version_number.c"
1429 WorkerBee(struct cmd_syndesc *as, void *arock)
1433 struct misc_data misc; /* info & statistics */
1435 initialize_PT_error_table();
1436 initialize_U_error_table();
1438 pr_dbaseName = AFSDIR_SERVER_PRDB_FILEPATH;
1439 memset(&misc, 0, sizeof(misc));
1441 pr_dbaseName = as->parms[0].items->data; /* -database */
1442 misc.listuheader = (as->parms[1].items ? 1 : 0); /* -uheader */
1443 misc.listpheader = (as->parms[2].items ? 1 : 0); /* -pheader */
1444 misc.listentries = (as->parms[3].items ? 1 : 0); /* -entries */
1445 misc.verbose = (as->parms[4].items ? 1 : 0); /* -verbose */
1446 recreateFile = (as->parms[5].items ? as->parms[5].items->data : NULL); /* -rebuild */
1448 fd = open(pr_dbaseName, O_RDONLY, 0);
1450 afs_com_err(whoami, errno, "Open failed on db %s", pr_dbaseName);
1454 /* Read the ubik header */
1455 if (misc.listuheader) {
1456 readUbikHeader(&misc);
1459 code = ReadHeader();
1462 if (misc.listpheader)
1463 printheader(&cheader);
1466 misc.recreate = fopen(recreateFile, "w");
1467 if (misc.recreate == 0) {
1468 afs_com_err(whoami, errno,
1469 "can't create file for recreation instructions: %s",
1474 code = CheckPrDatabase(&misc);
1476 afs_com_err(whoami, code, "Checking prserver database");
1483 main(int argc, char *argv[])
1485 struct cmd_syndesc *ts;
1489 ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, "PRDB check");
1490 cmd_AddParm(ts, "-database", CMD_SINGLE, CMD_REQUIRED, "ptdb_file");
1491 cmd_AddParm(ts, "-uheader", CMD_FLAG, CMD_OPTIONAL,
1492 "Display UBIK header");
1493 cmd_AddParm(ts, "-pheader", CMD_FLAG, CMD_OPTIONAL,
1494 "Display KADB header");
1495 cmd_AddParm(ts, "-entries", CMD_FLAG, CMD_OPTIONAL, "Display entries");
1496 cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "verbose");
1497 cmd_AddParm(ts, "-rebuild", CMD_SINGLE, CMD_OPTIONAL | CMD_HIDE,
1500 return cmd_Dispatch(argc, argv);
1504 #if defined(SUPERGROUPS)
1506 /* new routines to deal with very large ID numbers */
1509 zeromap(struct idused *idmap)
1512 memset(idmap->idcount, 0, sizeof idmap->idcount);
1513 idmap = idmap->idnext;
1518 inccount(struct idused **idmapp, int id)
1520 struct idused *idmap;
1522 if (IDCOUNT & (IDCOUNT - 1)) {
1523 fprintf(stderr, "IDCOUNT must be power of 2!\n");
1526 while ((idmap = *idmapp) != NULL) {
1527 if (idmap->idstart == (id & ~(IDCOUNT - 1)))
1529 idmapp = &idmap->idnext;
1532 idmap = (struct idused *)malloc(sizeof *idmap);
1537 memset(idmap, 0, sizeof idmap);
1538 idmap->idstart = id & ~(IDCOUNT - 1);
1539 idmap->idnext = *idmapp;
1542 ++idmap->idcount[id & (IDCOUNT - 1)];
1546 idcount(struct idused **idmapp, int id)
1548 struct idused *idmap;
1550 if (IDCOUNT & (IDCOUNT - 1)) {
1551 fprintf(stderr, "IDCOUNT must be power of 2!\n");
1554 while ((idmap = *idmapp) != NULL) {
1555 if (idmap->idstart == (id & ~(IDCOUNT - 1))) {
1556 return idmap->idcount[id & (IDCOUNT - 1)];
1558 idmapp = &idmap->idnext;
1562 #endif /* SUPERGROUPS */