ptserver: Tidy header includes
[openafs.git] / src / ptserver / ptutils.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 /*
11  *                      (5) Add functions to process supergroups:
12  *                          ChangeIDEntry(), RemoveFromSGEntry(),
13  *                          AddToSGEntry(), GetListSG2().
14  *                      (6) Add code to existing functions to process
15  *                          supergroups.
16  *      2/1/98 jjm      Add mdw's changes for bit mapping for supergroups
17  *
18  *      09/26/02 kwc    Move depthsg definition here from ptserver.c since
19  *                      pt_util needs it defined also, and this file is
20  *                      common between the two programs.
21  */
22
23 #include <afsconfig.h>
24 #include <afs/param.h>
25 #include <afs/stds.h>
26
27 #include <roken.h>
28
29 #include <lock.h>
30 #include <ubik.h>
31 #include <rx/xdr.h>
32 #include <afs/com_err.h>
33 #include <afs/cellconfig.h>
34
35 #include "ptserver.h"
36 #include "pterror.h"
37 #include "ptprototypes.h"
38
39 /* Foreign cells are represented by the group system:authuser@cell*/
40 #define AUTHUSER_GROUP "system:authuser"
41
42 extern int restricted;
43 extern struct ubik_dbase *dbase;
44 extern struct afsconf_dir *prdir;
45 extern int pr_noAuth;
46
47 static int inRange(struct prentry *cellEntry, afs_int32 aid);
48 static afs_int32 allocNextId(struct ubik_trans *, struct prentry *);
49 static int AddAuthGroup(struct prentry *tentry, prlist *alist, afs_int32 *size);
50
51 static char *whoami = "ptserver";
52
53 int prp_user_default = PRP_USER_DEFAULT;
54 int prp_group_default = PRP_GROUP_DEFAULT;
55
56 #if defined(SUPERGROUPS)
57
58 #include "map.h"
59
60 afs_int32 depthsg = 5;          /* Maximum iterations used during IsAMemberOF */
61 afs_int32 GetListSG2(struct ubik_trans *at, afs_int32 gid, prlist * alist,
62                      afs_int32 * sizeP, afs_int32 depth);
63
64 struct map *sg_flagged;
65 struct map *sg_found;
66
67 #define NIL_MAP ((struct map *) 0)
68
69 /* pt_mywrite hooks into the logic that writes ubik records to disk
70  *  at a very low level.  It invalidates mappings in sg_flagged/sg_found.
71  *  By hoooking in at this level, we ensure that a ubik file reload
72  *  invalidates our incore cache.
73  *
74  * We assert records, cheaders and everything else are 0 mod 64.
75  *  So, we can always see the first 64 bytes of any record written.
76  *  The stuff we're interested in (flags, id) are in the first 8 bytes.
77  *  so we can always tell if we're writing a group record.
78  */
79
80 int (*pt_save_dbase_write)(struct ubik_dbase *, afs_int32, void *, afs_int32,
81                            afs_int32);
82
83 int
84 pt_mywrite(struct ubik_dbase *tdb, afs_int32 fno, void *bp, afs_int32 pos, afs_int32 count)
85 {
86     afs_uint32 headersize = ntohl(cheader.headerSize);
87
88     if (fno == 0 && pos + count > headersize) {
89         afs_int32 p, l, c, o;
90         char *cp;
91         p = pos - headersize;
92         cp = bp;
93         l = count;
94         if (p < 0) {
95             l += p;
96             p = 0;
97         }
98         while (l > 0) {
99             o = p % ENTRYSIZE;
100             c = ENTRYSIZE - o;
101             if (c > l)
102                 c = l;
103 #define xPT(p,x) ((((struct prentry *)(p))->flags & htonl(PRTYPE)) == htonl(x))
104 #if DEBUG_SG_MAP
105             if (o)
106                 fprintf(stderr, "Writing %d bytes of entry @ %#lx(+%d)\n", c,
107                         p - o, o);
108             else if (c == ENTRYSIZE)
109                 fprintf(stderr,
110                         "Writing %d bytes of entry @ %#lx (%d<%s>,%d)\n",
111                         c, p, ntohl(((struct prentry *)cp)->flags),
112                         xPT(cp,PRUSER) ? "user" : xPT(cp,PRFREE) ? "free" :
113                         xPT(cp,PRGRP) ? "group" : xPT(cp,PRCONT) ? "cont" :
114                         xPT(cp,PRCELL) ? "cell" : xPT(cp,PRFOREIGN) ? "foreign" :
115                         xPT(cp,PRINST) ? "sub/super instance" : "?",
116                         ntohl(((struct prentry *)cp)->id));
117             else if (c >= 8)
118                 fprintf(stderr,
119                         "Writing first %d bytes of entry @ %#lx (%d<%s>,%d)\n",
120                         c, p, ntohl(((struct prentry *)cp)->flags),
121                         xPT(cp,PRUSER) ? "user" : xPT(cp,PRFREE) ? "free" :
122                         xPT(cp,PRGRP) ? "group" : xPT(cp,PRCONT) ? "cont" :
123                         xPT(cp,PRCELL) ? "cell" : xPT(cp,PRFOREIGN) ? "foreign" :
124                         xPT(cp,PRINST) ? "sub/super instance" : "?",
125                         ntohl(((struct prentry *)cp)->id));
126             else
127                 fprintf(stderr, "Writing %d bytes of entry @ %#lx\n", c, p);
128 #endif
129             if (!o && c >= 8 && xPT(cp,PRGRP)) {
130 #if DEBUG_SG_MAP
131                 if (in_map(sg_found, -ntohl(((struct prentry *)cp)->id)))
132                     fprintf(stderr, "Unfound: Removing group %d\n",
133                             ntohl(((struct prentry *)cp)->id));
134                 if (in_map(sg_flagged, -ntohl(((struct prentry *)cp)->id)))
135                     fprintf(stderr, "Unflag: Removing group %d\n",
136                             ntohl(((struct prentry *)cp)->id));
137 #endif
138                 sg_found =
139                     bic_map(sg_found,
140                         add_map(NIL_MAP, -ntohl(((struct prentry *)cp)->id)));
141                 sg_flagged =
142                     bic_map(sg_flagged,
143                         add_map(NIL_MAP, -ntohl(((struct prentry *)cp)->id)));
144             }
145             cp += c;
146             p += c;
147             l -= c;
148 #undef xPT
149         }
150     }
151     return (*pt_save_dbase_write) (tdb, fno, bp, pos, count);
152 }
153
154 /*
155  * this function attaches pt_mywrite.  It's called once,
156  *  just after ubik_ServerInit.
157  */
158
159 void
160 pt_hook_write(void)
161 {
162     extern struct ubik_dbase *ubik_dbase;
163     if (ubik_dbase->write != pt_mywrite) {
164         pt_save_dbase_write = ubik_dbase->write;
165         ubik_dbase->write = pt_mywrite;
166     }
167 }
168
169 #endif /* SUPERGROUPS */
170
171 /* CorrectUserName - Check to make sure a user name is OK.  It must not include
172  *   either a colon (or it would look like a group) or a newline (which can
173  *   confuse some ptdb code, depending on the format we're reading from).
174  *   This is a predicate, so it return one if name is OK and zero if name is
175  *   bogus. */
176
177 static int
178 CorrectUserName(char *name)
179 {
180     /* We accept foreign names, so we will deal with '@' later */
181     if (strchr(name, ':') || strchr(name, '\n'))
182         return 0;
183     if (strlen(name) >= PR_MAXNAMELEN)
184         return 0;
185     return 1;
186 }
187
188 /* CorrectGroupName - Like the above but handles more complicated cases caused
189  * by including the ownership in the name.  The interface works by calculating
190  * the correct name based on a given name and owner.  This allows easy use by
191  * rename, which then compares the correct name with the requested new name. */
192
193 static afs_int32
194 CorrectGroupName(struct ubik_trans *ut, char aname[PR_MAXNAMELEN],      /* name for group */
195                  afs_int32 cid,         /* caller id */
196                  afs_int32 oid,         /* owner of group */
197                  char cname[PR_MAXNAMELEN])     /* correct name for group */
198 {
199     afs_int32 code;
200     int admin;
201     char *prefix;               /* ptr to group owner part */
202     char *suffix;               /* ptr to group name part */
203     char name[PR_MAXNAMELEN];   /* correct name for group */
204     struct prentry tentry;
205
206     if (strlen(aname) >= PR_MAXNAMELEN)
207         return PRBADNAM;
208     admin = pr_noAuth || IsAMemberOf(ut, cid, SYSADMINID);
209
210     if (oid == 0)
211         oid = cid;
212
213     /* Determine the correct prefix for the name. */
214     if (oid == SYSADMINID)
215         prefix = "system";
216     else {
217         afs_int32 loc = FindByID(ut, oid);
218         if (loc == 0) {
219             /* let admin create groups owned by non-existent ids (probably
220              * setting a group to own itself).  Check that they look like
221              * groups (with a colon) or otherwise are good user names. */
222             if (admin) {
223                 strcpy(cname, aname);
224                 goto done;
225             }
226             return PRNOENT;
227         }
228         code = pr_Read(ut, 0, loc, &tentry, sizeof(tentry));
229         if (code)
230             return code;
231         if (ntohl(tentry.flags) & PRGRP) {
232             if ((tentry.count == 0) && !admin)
233                 return PRGROUPEMPTY;
234             /* terminate prefix at colon if there is one */
235             if ((prefix = strchr(tentry.name, ':')))
236                 *prefix = 0;
237         }
238         prefix = tentry.name;
239     }
240     /* only sysadmin allow to use 'system:' prefix */
241     if ((strcmp(prefix, "system") == 0) && !admin)
242         return PRPERM;
243
244     strcpy(name, aname);        /* in case aname & cname are same */
245     suffix = strchr(name, ':');
246     if (suffix == 0) {
247         /* sysadmin can make groups w/o ':', but they must still look like
248          * legal user names. */
249         if (!admin)
250             return PRBADNAM;
251         strcpy(cname, name);
252     } else {
253         if (strlen(prefix) + strlen(suffix) >= PR_MAXNAMELEN)
254             return PRBADNAM;
255         strcpy(cname, prefix);
256         strcat(cname, suffix);
257     }
258   done:
259     /* check for legal name with either group rules or user rules */
260     if ((suffix = strchr(cname, ':'))) {
261         /* check for confusing characters */
262         if (strchr(cname, '\n') ||      /* restrict so recreate can work */
263             strchr(suffix + 1, ':'))    /* avoid multiple colons */
264             return PRBADNAM;
265     } else {
266         if (!CorrectUserName(cname))
267             return PRBADNAM;
268     }
269     return 0;
270 }
271
272 int
273 AccessOK(struct ubik_trans *ut, afs_int32 cid,          /* caller id */
274          struct prentry *tentry,        /* object being accessed */
275          int mem,                       /* check membership in aid, if group */
276          int any)                       /* if set return true */
277 {
278     afs_int32 flags;
279     afs_int32 oid;
280     afs_int32 aid;
281
282     if (pr_noAuth)
283         return 1;
284     if (cid == SYSADMINID)
285         return 1;               /* special case fileserver */
286     if (restricted && ((mem == PRP_ADD_MEM) || (mem == PRP_REMOVE_MEM)) && (any == 0))
287         return 0;
288     if (tentry) {
289         flags = tentry->flags;
290         oid = tentry->owner;
291         aid = tentry->id;
292     } else {
293         flags = oid = aid = 0;
294     }
295     if (!(flags & PRACCESS)) {  /* provide default access */
296         if (flags & PRGRP)
297             flags |= prp_group_default;
298         else
299             flags |= prp_user_default;
300     }
301
302     if (flags & any)
303         return 1;
304     if (oid) {
305         if ((cid == oid) || IsAMemberOf(ut, cid, oid))
306             return 1;
307     }
308     if (aid > 0) {              /* checking on a user */
309         if (aid == cid)
310             return 1;
311     } else if (aid < 0) {       /* checking on group */
312         if ((flags & mem) && IsAMemberOf(ut, cid, aid))
313             return 1;
314     }
315     /* Allow members of SYSVIEWERID to get membership and status only */
316     if (((mem == PRP_STATUS_MEM) || (mem == PRP_MEMBER_MEM)
317          || (any == PRP_OWNED_ANY)) && (IsAMemberOf(ut, cid, SYSVIEWERID)))
318         return 1;
319     if (IsAMemberOf(ut, cid, SYSADMINID))
320         return 1;
321     return 0;                   /* no access */
322 }
323
324 afs_int32
325 CreateEntry(struct ubik_trans *at, char aname[PR_MAXNAMELEN], afs_int32 *aid, afs_int32 idflag, afs_int32 flag, afs_int32 oid, afs_int32 creator)
326 {
327     /* get and init a new entry */
328     afs_int32 code;
329     afs_int32 newEntry;
330     struct prentry tentry, tent;
331     char *atsign;
332
333     memset(&tentry, 0, sizeof(tentry));
334
335     if ((oid == 0) || (oid == ANONYMOUSID))
336         oid = creator;
337
338     if (flag & PRGRP) {
339         code = CorrectGroupName(at, aname, creator, oid, tentry.name);
340         if (code)
341             return code;
342         if (strcmp(aname, tentry.name) != 0)
343             return PRBADNAM;
344     } else {                    /* non-group must not have colon */
345         if (!CorrectUserName(aname))
346             return PRBADNAM;
347         strcpy(tentry.name, aname);
348     }
349
350     if (FindByName(at, aname, &tent))
351         return PREXIST;
352
353     newEntry = AllocBlock(at);
354     if (!newEntry)
355         return PRDBFAIL;
356 #ifdef PR_REMEMBER_TIMES
357     tentry.createTime = time(0);
358 #endif
359
360     if (flag & PRGRP) {
361         tentry.flags = PRGRP;
362         tentry.owner = oid;
363     } else if (flag == 0) {
364         tentry.flags = 0;
365         tentry.owner = SYSADMINID;
366     } else {
367         return PRBADARG;
368     }
369
370     atsign = strchr(aname, '@');
371     if (!atsign) {
372         /* A normal user or group. Pick an id for it */
373         if (idflag)
374             tentry.id = *aid;
375         else {
376             code = AllocID(at, flag, &tentry.id);
377             if (code != PRSUCCESS)
378                 return code;
379         }
380     } else if (flag & PRGRP) {
381         /* A foreign group. Its format must be AUTHUSER_GROUP@cellname
382          * Then pick an id for the group.
383          */
384         int badFormat;
385
386         *atsign = '\0';
387         badFormat = strcmp(AUTHUSER_GROUP, aname);
388         *atsign = '@';
389         if (badFormat)
390             return PRBADNAM;
391
392         if (idflag)
393             tentry.id = *aid;
394         else {
395             code = AllocID(at, flag, &tentry.id);
396             if (code != PRSUCCESS)
397                 return code;
398         }
399     } else {
400         /* A foreign user: <name>@<cell>. The foreign user is added to
401          * its representing group. It is
402          */
403         char *cellGroup;
404         afs_int32 pos, n;
405         struct prentry centry;
406
407         /* To create the user <name>@<cell> the group AUTHUSER_GROUP@<cell>
408          * must exist.
409          */
410         cellGroup =
411             (char *)malloc(strlen(AUTHUSER_GROUP) + strlen(atsign) + 1);
412         strcpy(cellGroup, AUTHUSER_GROUP);
413         strcat(cellGroup, atsign);
414         pos = FindByName(at, cellGroup, &centry);
415         free(cellGroup);
416         if (!pos)
417             return PRBADNAM;
418         code = pr_Read(at, 0, pos, &centry, sizeof(centry));
419         if (code)
420             return code;
421
422         /* cellid is the id of the group representing the cell */
423         tentry.cellid = ntohl(centry.id);
424
425         if (idflag) {
426             /* Check if id is good */
427             if (!inRange(&centry, *aid))
428                 return PRBADARG;
429             tentry.id = *aid;
430         } else {
431             /* Allocate an ID special for this foreign user. It is based
432              * on the representing group's id and nusers count.
433              */
434             tentry.id = allocNextId(at, &centry);
435             if (!tentry.id)
436                 return PRNOIDS;
437         }
438
439         /* The foreign user will be added to the representing foreign
440          * group. The group can hold up to 30 entries.
441          */
442         if (!(ntohl(centry.flags) & PRQUOTA)) {
443             centry.flags = htonl(ntohl(centry.flags) | PRQUOTA);
444             centry.ngroups = htonl(30);
445         }
446         n = ntohl(centry.ngroups);
447         if ((n <= 0) && !pr_noAuth)
448             return PRNOMORE;
449         centry.ngroups = htonl(n - 1);
450
451         /* write updated entry for group */
452         code = pr_Write(at, 0, pos, &centry, sizeof(centry));
453
454         /* Now add the new user entry to the database */
455         tentry.creator = creator;
456         *aid = tentry.id;
457         code = pr_WriteEntry(at, 0, newEntry, &tentry);
458         if (code)
459             return PRDBFAIL;
460         code = AddToIDHash(at, *aid, newEntry);
461         if (code != PRSUCCESS)
462             return code;
463         code = AddToNameHash(at, aname, newEntry);
464         if (code != PRSUCCESS)
465             return code;
466         if (inc_header_word(at, foreigncount, 1))
467             return PRDBFAIL;
468
469         /* Now add the entry to the authuser group for this cell.
470          * We will reread the entries for the user and the group
471          * instead of modifying them before writing them in the
472          * previous steps. Although not very efficient, much simpler
473          */
474         pos = FindByID(at, tentry.cellid);
475         if (!pos)
476             return PRBADNAM;
477         code = pr_ReadEntry(at, 0, pos, &centry);
478         if (code)
479             return code;
480         code = AddToEntry(at, &centry, pos, *aid);
481         if (code)
482             return code;
483         /* and now the user entry */
484         pos = FindByID(at, *aid);
485         if (!pos)
486             return PRBADNAM;
487         code = pr_ReadEntry(at, 0, pos, &tentry);
488         if (code)
489             return code;
490         code = AddToEntry(at, &tentry, pos, tentry.cellid);
491         if (code)
492             return code;
493
494         return PRSUCCESS;
495     }
496
497     /* Remember the largest group id or largest user id */
498     if (flag & PRGRP) {
499         /* group ids are negative */
500         if (tentry.id < (afs_int32) ntohl(cheader.maxGroup)) {
501             code = set_header_word(at, maxGroup, htonl(tentry.id));
502             if (code)
503                 return PRDBFAIL;
504         }
505     } else {
506         if (tentry.id > (afs_int32) ntohl(cheader.maxID)) {
507             code = set_header_word(at, maxID, htonl(tentry.id));
508             if (code)
509                 return PRDBFAIL;
510         }
511     }
512
513     /* Charge the creator for this group */
514     if (flag & PRGRP) {
515         afs_int32 loc = FindByID(at, creator);
516         struct prentry centry;
517         int admin;
518
519         if (loc) {              /* this should only fail during initialization */
520             code = pr_Read(at, 0, loc, &centry, sizeof(centry));
521             if (code)
522                 return code;
523
524             /* If quota is uninitialized, do it */
525             if (!(ntohl(centry.flags) & PRQUOTA)) {
526                 centry.flags = htonl(ntohl(centry.flags) | PRQUOTA);
527                 centry.ngroups = centry.nusers = htonl(20);
528             }
529
530             /* Admins don't get charged for creating a group.
531              * If in noAuth mode, you get changed for it but you
532              * are still allowed to create as many groups as you want.
533              */
534             admin = ((creator == SYSADMINID)
535                      || IsAMemberOf(at, creator, SYSADMINID));
536             if (!admin) {
537                 if (ntohl(centry.ngroups) <= 0) {
538                     if (!pr_noAuth)
539                         return PRNOMORE;
540                 } else {
541                     centry.ngroups = htonl(ntohl(centry.ngroups) - 1);
542                 }
543             }
544
545             code = pr_Write(at, 0, loc, &centry, sizeof(centry));
546             if (code)
547                 return code;
548         }                       /* if (loc) */
549     } else {
550         /* Initialize the quota for the user. Groups don't have their
551          * quota initialized.
552          */
553         tentry.flags |= PRQUOTA;
554         tentry.ngroups = tentry.nusers = 20;
555     }
556
557     tentry.creator = creator;
558     *aid = tentry.id;
559     code = pr_WriteEntry(at, 0, newEntry, &tentry);
560     if (code)
561         return PRDBFAIL;
562     code = AddToIDHash(at, *aid, newEntry);
563     if (code != PRSUCCESS)
564         return code;
565     code = AddToNameHash(at, aname, newEntry);
566     if (code != PRSUCCESS)
567         return code;
568     if (tentry.flags & PRGRP) {
569         code = AddToOwnerChain(at, tentry.id, oid);
570         if (code)
571             return code;
572     }
573     if (tentry.flags & PRGRP) {
574         if (inc_header_word(at, groupcount, 1))
575             return PRDBFAIL;
576     } else if (tentry.flags & PRINST) {
577         if (inc_header_word(at, instcount, 1))
578             return PRDBFAIL;
579     } else {
580         if (inc_header_word(at, usercount, 1))
581             return PRDBFAIL;
582     }
583     return PRSUCCESS;
584 }
585
586
587 /* RemoveFromEntry - remove aid from bid's entries list, freeing a continuation
588  * entry if appropriate */
589
590 afs_int32
591 RemoveFromEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 bid)
592 {
593     afs_int32 code;
594     struct prentry tentry;
595     struct contentry centry;
596     struct contentry hentry;
597     afs_int32 temp;
598     afs_int32 i, j;
599     afs_int32 nptr;
600     afs_int32 hloc;
601
602     if (aid == bid)
603         return PRINCONSISTENT;
604     memset(&hentry, 0, sizeof(hentry));
605     temp = FindByID(at, bid);
606     if (temp == 0)
607         return PRNOENT;
608     code = pr_ReadEntry(at, 0, temp, &tentry);
609     if (code != 0)
610         return code;
611 #ifdef PR_REMEMBER_TIMES
612     tentry.removeTime = time(0);
613 #endif
614     for (i = 0; i < PRSIZE; i++) {
615         if (tentry.entries[i] == aid) {
616             tentry.entries[i] = PRBADID;
617             tentry.count--;
618             code = pr_WriteEntry(at, 0, temp, &tentry);
619             if (code != 0)
620                 return code;
621             return PRSUCCESS;
622         }
623         if (tentry.entries[i] == 0)     /* found end of list */
624             return PRNOENT;
625     }
626     hloc = 0;
627     nptr = tentry.next;
628     while (nptr != 0) {
629         code = pr_ReadCoEntry(at, 0, nptr, &centry);
630         if (code != 0)
631             return code;
632         if ((centry.id != bid) || !(centry.flags & PRCONT))
633             return PRDBBAD;
634         for (i = 0; i < COSIZE; i++) {
635             if (centry.entries[i] == aid) {
636                 centry.entries[i] = PRBADID;
637                 for (j = 0; j < COSIZE; j++)
638                     if (centry.entries[j] != PRBADID
639                         && centry.entries[j] != 0)
640                         break;
641                 if (j == COSIZE) {      /* can free this block */
642                     if (hloc == 0) {
643                         tentry.next = centry.next;
644                     } else {
645                         hentry.next = centry.next;
646                         code = pr_WriteCoEntry(at, 0, hloc, &hentry);
647                         if (code != 0)
648                             return code;
649                     }
650                     code = FreeBlock(at, nptr);
651                     if (code)
652                         return code;
653                 } else {        /* can't free it yet */
654                     code = pr_WriteCoEntry(at, 0, nptr, &centry);
655                     if (code != 0)
656                         return code;
657                 }
658                 tentry.count--;
659                 code = pr_WriteEntry(at, 0, temp, &tentry);
660                 if (code)
661                     return PRDBFAIL;
662                 return 0;
663             }
664             if (centry.entries[i] == 0)
665                 return PRNOENT;
666         }                       /* for all coentry slots */
667         hloc = nptr;
668         nptr = centry.next;
669         memcpy(&hentry, &centry, sizeof(centry));
670     }                           /* while there are coentries */
671     return PRNOENT;
672 }
673
674 #if defined(SUPERGROUPS)
675 /* ChangeIDEntry - remove aid from bid's entries list, freeing a continuation
676  * entry if appropriate */
677
678 afs_int32
679 ChangeIDEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 newid, afs_int32 bid)
680 {
681     afs_int32 code;
682     struct prentry tentry;
683     struct contentry centry;
684     afs_int32 temp;
685     afs_int32 i, j;
686     afs_int32 nptr;
687
688     if (aid == bid)
689         return PRINCONSISTENT;
690     temp = FindByID(at, bid);
691     if (temp == 0) {
692         return PRNOENT;
693     }
694     code = pr_ReadEntry(at, 0, temp, &tentry);
695     if (code != 0)
696         return code;
697     for (i = 0; i < PRSIZE; i++) {
698         if (tentry.entries[i] == aid) {
699             tentry.entries[i] = newid;
700             code = pr_WriteEntry(at, 0, temp, &tentry);
701             if (code != 0)
702                 return code;
703             return PRSUCCESS;
704         }
705         if (tentry.entries[i] == 0) {   /* found end of list */
706             return PRNOENT;
707         }
708     }
709
710     nptr = tentry.next;
711     while (nptr) {
712         code = pr_ReadCoEntry(at, 0, nptr, &centry);
713         if (code != 0)
714             return code;
715         if ((centry.id != bid) || !(centry.flags & PRCONT)) {
716             fprintf(stderr,
717                     "ChangeIDEntry: bad database bid=%d centry.id=%d .flags=%d\n",
718                     bid, centry.id, centry.flags);
719             return PRDBBAD;
720         }
721         for (i = 0; i < COSIZE; i++) {
722             if (centry.entries[i] == aid) {
723                 centry.entries[i] = newid;
724                 for (j = 0; j < COSIZE; j++)
725                     if (centry.entries[j] != PRBADID
726                         && centry.entries[j] != 0)
727                         break;
728                 code = pr_WriteCoEntry(at, 0, nptr, &centry);
729                 if (code != 0)
730                     return code;
731                 return 0;
732             }
733             if (centry.entries[i] == 0) {
734                 return PRNOENT;
735             }
736         }                       /* for all coentry slots */
737         nptr = centry.next;
738     }                           /* while there are coentries */
739     return PRNOENT;
740 }
741
742 /*  #ifdef SUPERGROUPS */
743 /* RemoveFromSGEntry - remove aid from bid's supergroups list, freeing a
744  * continuation entry if appropriate */
745
746 afs_int32
747 RemoveFromSGEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 bid)
748 {
749     afs_int32 code;
750     struct prentry tentry;
751     struct prentryg *tentryg;
752     struct contentry centry;
753     struct contentry hentry;
754     afs_int32 temp;
755     afs_int32 i, j;
756     afs_int32 nptr;
757     afs_int32 hloc;
758
759     if (aid == bid)
760         return PRINCONSISTENT;
761     memset(&hentry, 0, sizeof(hentry));
762     temp = FindByID(at, bid);
763     if (temp == 0) {
764         return PRNOENT;
765     }
766     code = pr_ReadEntry(at, 0, temp, &tentry);
767     if (code != 0)
768         return code;
769 #ifdef PR_REMEMBER_TIMES
770     tentry.removeTime = time(NULL);
771 #endif
772     tentryg = (struct prentryg *)&tentry;
773     for (i = 0; i < SGSIZE; i++) {
774         if (tentryg->supergroup[i] == aid) {
775             tentryg->supergroup[i] = PRBADID;
776             tentryg->countsg--;
777             code = pr_WriteEntry(at, 0, temp, &tentry);
778             if (code != 0)
779                 return code;
780             return PRSUCCESS;
781         }
782         if (tentryg->supergroup[i] == 0) {      /* found end of list */
783             return PRNOENT;
784         }
785     }
786     hloc = 0;
787     nptr = tentryg->nextsg;
788     while (nptr) {
789         code = pr_ReadCoEntry(at, 0, nptr, &centry);
790         if (code != 0)
791             return code;
792         if ((centry.id != bid) || !(centry.flags & PRCONT)) {
793             fprintf(stderr,
794                     "ChangeIDEntry: bad database bid=%d centry.id=%d .flags=%d\n",
795                     bid, centry.id, centry.flags);
796             return PRDBBAD;
797         }
798         for (i = 0; i < COSIZE; i++) {
799             if (centry.entries[i] == aid) {
800                 centry.entries[i] = PRBADID;
801                 for (j = 0; j < COSIZE; j++)
802                     if (centry.entries[j] != PRBADID
803                         && centry.entries[j] != 0)
804                         break;
805                 if (j == COSIZE) {      /* can free this block */
806                     if (hloc == 0) {
807                         tentryg->nextsg = centry.next;
808                     } else {
809                         hentry.next = centry.next;
810                         code = pr_WriteCoEntry(at, 0, hloc, &hentry);
811                         if (code != 0)
812                             return code;
813                     }
814                     code = FreeBlock(at, nptr);
815                     if (code)
816                         return code;
817                 } else {        /* can't free it yet */
818                     code = pr_WriteCoEntry(at, 0, nptr, &centry);
819                     if (code != 0)
820                         return code;
821                 }
822                 tentryg->countsg--;
823                 code = pr_WriteEntry(at, 0, temp, &tentry);
824                 if (code)
825                     return PRDBFAIL;
826                 return 0;
827             }
828             if (centry.entries[i] == 0) {
829                 return PRNOENT;
830             }
831         }                       /* for all coentry slots */
832         hloc = nptr;
833         nptr = centry.next;
834         memcpy((char *)&centry, (char *)&hentry, sizeof(centry));
835     }                           /* while there are coentries */
836     return PRNOENT;
837 }
838
839 #endif /* SUPERGROUPS */
840
841 /* DeleteEntry - delete the entry in tentry at loc, removing it from all
842  * groups, putting groups owned by it on orphan chain, and freeing the space */
843
844 afs_int32
845 DeleteEntry(struct ubik_trans *at, struct prentry *tentry, afs_int32 loc)
846 {
847     afs_int32 code;
848     struct contentry centry;
849     afs_int32 i;
850     afs_int32 nptr;
851
852     if (strchr(tentry->name, '@')) {
853         if (tentry->flags & PRGRP) {
854             /* If there are still foreign user accounts from that cell
855              * don't delete the group */
856             if (tentry->count)
857                 return PRBADARG;
858         } else {
859             /* adjust quota */
860
861             afs_int32 loc = FindByID(at, tentry->cellid);
862             struct prentry centry;
863             if (loc) {
864                 code = pr_Read(at, 0, loc, &centry, sizeof(centry));
865                 if (code)
866                     return code;
867                 if (ntohl(centry.flags) & PRQUOTA) {
868                     centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
869                 }
870                 code = pr_Write(at, 0, loc, &centry, sizeof(centry));
871                 if (code)
872                     return code;
873             }
874         }
875     }
876     /* First remove the entire membership list */
877     for (i = 0; i < PRSIZE; i++) {
878         if (tentry->entries[i] == PRBADID)
879             continue;
880         if (tentry->entries[i] == 0)
881             break;
882 #if defined(SUPERGROUPS)
883         if ((tentry->flags & PRGRP) && tentry->entries[i] < 0)  /* Supergroup */
884             code = RemoveFromSGEntry(at, tentry->id, tentry->entries[i]);
885         else
886 #endif
887             code = RemoveFromEntry(at, tentry->id, tentry->entries[i]);
888         if (code)
889             return code;
890     }
891 #if defined(SUPERGROUPS)
892     {
893         struct prentryg *tentryg = (struct prentryg *)tentry;
894
895         /* Then remove the entire supergroup list */
896         for (i = 0; i < SGSIZE; i++) {
897             if (tentryg->supergroup[i] == PRBADID)
898                 continue;
899             if (tentryg->supergroup[i] == 0)
900                 break;
901             code = RemoveFromEntry(at, tentry->id, tentryg->supergroup[i]);
902             if (code)
903                 return code;
904         }
905     }
906 #endif /* SUPERGROUPS */
907     nptr = tentry->next;
908     while (nptr != 0) {
909         code = pr_ReadCoEntry(at, 0, nptr, &centry);
910         if (code != 0)
911             return PRDBFAIL;
912         for (i = 0; i < COSIZE; i++) {
913             if (centry.entries[i] == PRBADID)
914                 continue;
915             if (centry.entries[i] == 0)
916                 break;
917             code = RemoveFromEntry(at, tentry->id, centry.entries[i]);
918             if (code)
919                 return code;
920         }
921         code = FreeBlock(at, nptr);     /* free continuation block */
922         if (code)
923             return code;
924         nptr = centry.next;
925     }
926
927     /* Remove us from other's owned chain.  Note that this will zero our owned
928      * field (on disk) so this step must follow the above step in case we are
929      * on our own owned list. */
930     if (tentry->flags & PRGRP) {
931         if (tentry->owner) {
932             code = RemoveFromOwnerChain(at, tentry->id, tentry->owner);
933             if (code)
934                 return code;
935         } else {
936             code = RemoveFromOrphan(at, tentry->id);
937             if (code)
938                 return code;
939         }
940     }
941
942     code = RemoveFromIDHash(at, tentry->id, &loc);
943     if (code != PRSUCCESS)
944         return code;
945     code = RemoveFromNameHash(at, tentry->name, &loc);
946     if (code != PRSUCCESS)
947         return code;
948
949     if (tentry->flags & PRGRP) {
950         afs_int32 loc = FindByID(at, tentry->creator);
951         struct prentry centry;
952         int admin;
953
954         if (loc) {
955             code = pr_Read(at, 0, loc, &centry, sizeof(centry));
956             if (code)
957                 return code;
958             admin = ((tentry->creator == SYSADMINID)
959                      || IsAMemberOf(at, tentry->creator, SYSADMINID));
960             if (ntohl(centry.flags) & PRQUOTA) {
961                 if (!(admin && (ntohl(centry.ngroups) >= 20))) {
962                     centry.ngroups = htonl(ntohl(centry.ngroups) + 1);
963                 }
964             }
965             code = pr_Write(at, 0, loc, &centry, sizeof(centry));
966             if (code)
967                 return code;
968         }
969     }
970
971     if (tentry->flags & PRGRP) {
972         if (inc_header_word(at, groupcount, -1))
973             return PRDBFAIL;
974     } else if (tentry->flags & PRINST) {
975         if (inc_header_word(at, instcount, -1))
976             return PRDBFAIL;
977     } else {
978         if (strchr(tentry->name, '@')) {
979             if (inc_header_word(at, foreigncount, -1))
980                 return PRDBFAIL;
981         } else {
982             if (inc_header_word(at, usercount, -1))
983                 return PRDBFAIL;
984         }
985     }
986     code = FreeBlock(at, loc);
987     return code;
988 }
989
990 /* AddToEntry - add aid to entry's entries list, alloc'ing a continuation block
991  * if needed.
992  *
993  * Note the entry is written out by this routine. */
994
995 afs_int32
996 AddToEntry(struct ubik_trans *tt, struct prentry *entry, afs_int32 loc, afs_int32 aid)
997 {
998     afs_int32 code;
999     afs_int32 i;
1000     struct contentry nentry;
1001     struct contentry aentry;
1002     afs_int32 nptr;
1003     afs_int32 last;             /* addr of last cont. block */
1004     afs_int32 first = 0;
1005     afs_int32 cloc = 0;
1006     afs_int32 slot = -1;
1007
1008     if (entry->id == aid)
1009         return PRINCONSISTENT;
1010 #ifdef PR_REMEMBER_TIMES
1011     entry->addTime = time(0);
1012 #endif
1013     for (i = 0; i < PRSIZE; i++) {
1014         if (entry->entries[i] == aid)
1015             return PRIDEXIST;
1016         if (entry->entries[i] == PRBADID) {     /* remember this spot */
1017             first = 1;
1018             slot = i;
1019         } else if (entry->entries[i] == 0) {    /* end of the line */
1020             if (slot == -1) {
1021                 first = 1;
1022                 slot = i;
1023             }
1024             break;
1025         }
1026     }
1027     last = 0;
1028     nptr = entry->next;
1029     while (nptr != 0) {
1030         code = pr_ReadCoEntry(tt, 0, nptr, &nentry);
1031         if (code != 0)
1032             return code;
1033         last = nptr;
1034         if (!(nentry.flags & PRCONT))
1035             return PRDBFAIL;
1036         for (i = 0; i < COSIZE; i++) {
1037             if (nentry.entries[i] == aid)
1038                 return PRIDEXIST;
1039             if (nentry.entries[i] == PRBADID) {
1040                 if (slot == -1) {
1041                     slot = i;
1042                     cloc = nptr;
1043                 }
1044             } else if (nentry.entries[i] == 0) {
1045                 if (slot == -1) {
1046                     slot = i;
1047                     cloc = nptr;
1048                 }
1049                 break;
1050             }
1051         }
1052         nptr = nentry.next;
1053     }
1054     if (slot != -1) {           /* we found a place */
1055         entry->count++;
1056         if (first) {            /* place is in first block */
1057             entry->entries[slot] = aid;
1058             code = pr_WriteEntry(tt, 0, loc, entry);
1059             if (code != 0)
1060                 return code;
1061             return PRSUCCESS;
1062         }
1063         code = pr_WriteEntry(tt, 0, loc, entry);
1064         if (code)
1065             return code;
1066         code = pr_ReadCoEntry(tt, 0, cloc, &aentry);
1067         if (code != 0)
1068             return code;
1069         aentry.entries[slot] = aid;
1070         code = pr_WriteCoEntry(tt, 0, cloc, &aentry);
1071         if (code != 0)
1072             return code;
1073         return PRSUCCESS;
1074     }
1075     /* have to allocate a continuation block if we got here */
1076     nptr = AllocBlock(tt);
1077     if (last) {
1078         /* then we should tack new block after last block in cont. chain */
1079         nentry.next = nptr;
1080         code = pr_WriteCoEntry(tt, 0, last, &nentry);
1081         if (code != 0)
1082             return code;
1083     } else {
1084         entry->next = nptr;
1085     }
1086     memset(&aentry, 0, sizeof(aentry));
1087     aentry.flags |= PRCONT;
1088     aentry.id = entry->id;
1089     aentry.next = 0;
1090     aentry.entries[0] = aid;
1091     code = pr_WriteCoEntry(tt, 0, nptr, &aentry);
1092     if (code != 0)
1093         return code;
1094     /* don't forget to update count, here! */
1095     entry->count++;
1096     code = pr_WriteEntry(tt, 0, loc, entry);
1097     return code;
1098
1099 }
1100
1101 #if defined(SUPERGROUPS)
1102
1103 /* AddToSGEntry - add aid to entry's supergroup list, alloc'ing a
1104  * continuation block if needed.
1105  *
1106  * Note the entry is written out by this routine. */
1107
1108 afs_int32
1109 AddToSGEntry(struct ubik_trans *tt, struct prentry *entry, afs_int32 loc, afs_int32 aid)
1110 {
1111     afs_int32 code;
1112     afs_int32 i;
1113     struct contentry nentry;
1114     struct contentry aentry;
1115     struct prentryg *entryg;
1116     afs_int32 nptr;
1117     afs_int32 last;             /* addr of last cont. block */
1118     afs_int32 first = 0;
1119     afs_int32 cloc = 0;
1120     afs_int32 slot = -1;
1121
1122     if (entry->id == aid)
1123         return PRINCONSISTENT;
1124 #ifdef PR_REMEMBER_TIMES
1125     entry->addTime = time(NULL);
1126 #endif
1127     entryg = (struct prentryg *)entry;
1128     for (i = 0; i < SGSIZE; i++) {
1129         if (entryg->supergroup[i] == aid)
1130             return PRIDEXIST;
1131         if (entryg->supergroup[i] == PRBADID) { /* remember this spot */
1132             first = 1;
1133             slot = i;
1134         } else if (entryg->supergroup[i] == 0) {        /* end of the line */
1135             if (slot == -1) {
1136                 first = 1;
1137                 slot = i;
1138             }
1139             break;
1140         }
1141     }
1142     last = 0;
1143     nptr = entryg->nextsg;
1144     while (nptr) {
1145         code = pr_ReadCoEntry(tt, 0, nptr, &nentry);
1146         if (code != 0)
1147             return code;
1148         last = nptr;
1149         if (!(nentry.flags & PRCONT))
1150             return PRDBFAIL;
1151         for (i = 0; i < COSIZE; i++) {
1152             if (nentry.entries[i] == aid)
1153                 return PRIDEXIST;
1154             if (nentry.entries[i] == PRBADID) {
1155                 if (slot == -1) {
1156                     slot = i;
1157                     cloc = nptr;
1158                 }
1159             } else if (nentry.entries[i] == 0) {
1160                 if (slot == -1) {
1161                     slot = i;
1162                     cloc = nptr;
1163                 }
1164                 break;
1165             }
1166         }
1167         nptr = nentry.next;
1168     }
1169     if (slot != -1) {           /* we found a place */
1170         entryg->countsg++;
1171         if (first) {            /* place is in first block */
1172             entryg->supergroup[slot] = aid;
1173             code = pr_WriteEntry(tt, 0, loc, entry);
1174             if (code != 0)
1175                 return code;
1176             return PRSUCCESS;
1177         }
1178         code = pr_WriteEntry(tt, 0, loc, entry);
1179         if (code)
1180             return code;
1181         code = pr_ReadCoEntry(tt, 0, cloc, &aentry);
1182         if (code != 0)
1183             return code;
1184         aentry.entries[slot] = aid;
1185         code = pr_WriteCoEntry(tt, 0, cloc, &aentry);
1186         if (code != 0)
1187             return code;
1188         return PRSUCCESS;
1189     }
1190     /* have to allocate a continuation block if we got here */
1191     nptr = AllocBlock(tt);
1192     if (last) {
1193         /* then we should tack new block after last block in cont. chain */
1194         nentry.next = nptr;
1195         code = pr_WriteCoEntry(tt, 0, last, &nentry);
1196         if (code != 0)
1197             return code;
1198     } else {
1199         entryg->nextsg = nptr;
1200     }
1201     memset(&aentry, 0, sizeof(aentry));
1202     aentry.flags |= PRCONT;
1203     aentry.id = entry->id;
1204     aentry.next = 0;
1205     aentry.entries[0] = aid;
1206     code = pr_WriteCoEntry(tt, 0, nptr, &aentry);
1207     if (code != 0)
1208         return code;
1209     /* don't forget to update count, here! */
1210     entryg->countsg++;
1211     code = pr_WriteEntry(tt, 0, loc, entry);
1212     return code;
1213
1214 }
1215 #endif /* SUPERGROUPS */
1216
1217 afs_int32
1218 AddToPRList(prlist *alist, int *sizeP, afs_int32 id)
1219 {
1220     char *tmp;
1221     int count;
1222
1223     if (alist->prlist_len >= *sizeP) {
1224         count = alist->prlist_len + 100;
1225         if (alist->prlist_val) {
1226             tmp =
1227                 (char *)realloc(alist->prlist_val, count * sizeof(afs_int32));
1228         } else {
1229             tmp = (char *)malloc(count * sizeof(afs_int32));
1230         }
1231         if (!tmp)
1232             return (PRNOMEM);
1233         alist->prlist_val = (afs_int32 *) tmp;
1234         *sizeP = count;
1235     }
1236     alist->prlist_val[alist->prlist_len++] = id;
1237     return 0;
1238 }
1239
1240 afs_int32
1241 GetList(struct ubik_trans *at, struct prentry *tentry, prlist *alist, afs_int32 add)
1242 {
1243     afs_int32 code;
1244     afs_int32 i;
1245     struct contentry centry;
1246     afs_int32 nptr;
1247     int size;
1248     int count = 0;
1249
1250     size = 0;
1251     alist->prlist_val = 0;
1252     alist->prlist_len = 0;
1253
1254     for (i = 0; i < PRSIZE; i++) {
1255         if (tentry->entries[i] == PRBADID)
1256             continue;
1257         if (tentry->entries[i] == 0)
1258             break;
1259         code = AddToPRList(alist, &size, tentry->entries[i]);
1260         if (code)
1261             return code;
1262 #if defined(SUPERGROUPS)
1263         if (!add)
1264             continue;
1265         code = GetListSG2(at, tentry->entries[i], alist, &size, depthsg);
1266         if (code)
1267             return code;
1268 #endif
1269     }
1270
1271     for (nptr = tentry->next; nptr != 0; nptr = centry.next) {
1272         /* look through cont entries */
1273         code = pr_ReadCoEntry(at, 0, nptr, &centry);
1274         if (code != 0)
1275             return code;
1276         for (i = 0; i < COSIZE; i++) {
1277             if (centry.entries[i] == PRBADID)
1278                 continue;
1279             if (centry.entries[i] == 0)
1280                 break;
1281             code = AddToPRList(alist, &size, centry.entries[i]);
1282             if (code)
1283                 return code;
1284 #if defined(SUPERGROUPS)
1285             if (!add)
1286                 continue;
1287             code = GetListSG2(at, centry.entries[i], alist, &size, depthsg);
1288             if (code)
1289                 return code;
1290 #endif
1291         }
1292         if (count++ > 50) {
1293 #ifndef AFS_PTHREAD_ENV
1294             IOMGR_Poll();
1295 #endif
1296             count = 0;
1297         }
1298     }
1299
1300     if (add) {                  /* this is for a CPS, so tack on appropriate stuff */
1301         if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) {
1302             if ((code = AddToPRList(alist, &size, ANYUSERID))
1303                 || (code = AddAuthGroup(tentry, alist, &size))
1304                 || (code = AddToPRList(alist, &size, tentry->id)))
1305                 return code;
1306         } else {
1307             if ((code = AddToPRList(alist, &size, ANYUSERID))
1308                 || (code = AddToPRList(alist, &size, tentry->id)))
1309                 return code;
1310         }
1311     }
1312 #ifndef AFS_PTHREAD_ENV
1313     if (alist->prlist_len > 100)
1314         IOMGR_Poll();
1315 #endif
1316     qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp);
1317     return PRSUCCESS;
1318 }
1319
1320
1321 afs_int32
1322 GetList2(struct ubik_trans *at, struct prentry *tentry, struct prentry *tentry2, prlist *alist, afs_int32 add)
1323 {
1324     afs_int32 code = 0;
1325     afs_int32 i;
1326     struct contentry centry;
1327     afs_int32 nptr;
1328     afs_int32 size;
1329     int count = 0;
1330
1331     size = 0;
1332     alist->prlist_val = 0;
1333     alist->prlist_len = 0;
1334     for (i = 0; i < PRSIZE; i++) {
1335         if (tentry->entries[i] == PRBADID)
1336             continue;
1337         if (tentry->entries[i] == 0)
1338             break;
1339         code = AddToPRList(alist, &size, tentry->entries[i]);
1340         if (code)
1341             return code;
1342 #if defined(SUPERGROUPS)
1343         if (!add)
1344             continue;
1345         code = GetListSG2(at, tentry->entries[i], alist, &size, depthsg);
1346         if (code)
1347             return code;
1348 #endif
1349     }
1350
1351     nptr = tentry->next;
1352     while (nptr != 0) {
1353         /* look through cont entries */
1354         code = pr_ReadCoEntry(at, 0, nptr, &centry);
1355         if (code != 0)
1356             return code;
1357         for (i = 0; i < COSIZE; i++) {
1358             if (centry.entries[i] == PRBADID)
1359                 continue;
1360             if (centry.entries[i] == 0)
1361                 break;
1362             code = AddToPRList(alist, &size, centry.entries[i]);
1363             if (code)
1364                 return code;
1365 #if defined(SUPERGROUPS)
1366             if (!add)
1367                 continue;
1368             code = GetListSG2(at, centry.entries[i], alist, &size, depthsg);
1369             if (code)
1370                 return code;
1371 #endif
1372         }
1373         nptr = centry.next;
1374         if (count++ > 50) {
1375 #ifndef AFS_PTHREAD_ENV
1376             IOMGR_Poll();
1377 #endif
1378             count = 0;
1379         }
1380     }
1381
1382     for (i = 0; i < PRSIZE; i++) {
1383         if (tentry2->entries[i] == PRBADID)
1384             continue;
1385         if (tentry2->entries[i] == 0)
1386             break;
1387         code = AddToPRList(alist, &size, tentry2->entries[i]);
1388         if (code)
1389             break;
1390     }
1391
1392     if (!code) {
1393         nptr = tentry2->next;
1394         while (nptr != 0) {
1395             /* look through cont entries */
1396             code = pr_ReadCoEntry(at, 0, nptr, &centry);
1397             if (code != 0)
1398                 break;
1399             for (i = 0; i < COSIZE; i++) {
1400                 if (centry.entries[i] == PRBADID)
1401                     continue;
1402                 if (centry.entries[i] == 0)
1403                     break;
1404                 code = AddToPRList(alist, &size, centry.entries[i]);
1405                 if (code)
1406                     break;
1407             }
1408             nptr = centry.next;
1409             if (count++ > 50) {
1410 #ifndef AFS_PTHREAD_ENV
1411                 IOMGR_Poll();
1412 #endif
1413                 count = 0;
1414             }
1415         }
1416     }
1417     if (add) {                  /* this is for a CPS, so tack on appropriate stuff */
1418         if (tentry->id != ANONYMOUSID && tentry->id != ANYUSERID) {
1419             if ((code = AddToPRList(alist, &size, ANYUSERID))
1420                 || (code = AddToPRList(alist, &size, AUTHUSERID))
1421                 || (code = AddToPRList(alist, &size, tentry->id)))
1422                 return code;
1423         } else {
1424             if ((code = AddToPRList(alist, &size, ANYUSERID))
1425                 || (code = AddToPRList(alist, &size, tentry->id)))
1426                 return code;
1427         }
1428     }
1429 #ifndef AFS_PTHREAD_ENV
1430     if (alist->prlist_len > 100)
1431         IOMGR_Poll();
1432 #endif
1433     qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp);
1434     return PRSUCCESS;
1435 }
1436
1437 #if defined(SUPERGROUPS)
1438
1439 afs_int32
1440 GetListSG2(struct ubik_trans *at, afs_int32 gid, prlist *alist, afs_int32 *sizeP, afs_int32 depth)
1441 {
1442     afs_int32 code;
1443     struct prentry tentry;
1444     struct prentryg *tentryg = (struct prentryg *)&tentry;
1445     afs_int32 i;
1446     struct contentry centry;
1447     afs_int32 nptr;
1448     int count = 0;
1449     afs_int32 temp;
1450     int didsomething;
1451 #if DEBUG_SG_MAP
1452     int predictfound, predictflagged;
1453 #endif
1454
1455 #if DEBUG_SG_MAP
1456     predictfound = 0;
1457     if (!in_map(sg_flagged, -gid)) {
1458         predictflagged = 0;
1459         fprintf(stderr, "GetListSG2: I have not yet searched for gid=%d\n",
1460                 gid);
1461     } else if (predictflagged = 1, in_map(sg_found, -gid)) {
1462         predictfound = 1;
1463         fprintf(stderr,
1464                 "GetListSG2: I have already searched for gid=%d, and predict success.\n",
1465                 gid);
1466     }
1467 #endif
1468
1469     if (in_map(sg_flagged, -gid) && !in_map(sg_found, -gid)) {
1470 #if DEBUG_SG_MAP
1471         fprintf(stderr,
1472                 "GetListSG2: I have already searched for gid=%d, and predict failure.\n",
1473                 gid);
1474 #else
1475         return 0;
1476 #endif
1477     }
1478
1479     if (depth < 1)
1480         return 0;
1481     temp = FindByID(at, gid);
1482     if (!temp) {
1483         code = PRNOENT;
1484         return code;
1485     }
1486     code = pr_ReadEntry(at, 0, temp, &tentry);
1487     if (code)
1488         return code;
1489 #if DEBUG_SG_MAP
1490     fprintf(stderr, "GetListSG2: lookup for gid=%d [\n", gid);
1491 #endif
1492     didsomething = 0;
1493
1494     for (i = 0; i < SGSIZE; i++) {
1495         if (tentryg->supergroup[i] == PRBADID)
1496             continue;
1497         if (tentryg->supergroup[i] == 0)
1498             break;
1499         didsomething = 1;
1500 #if DEBUG_SG_MAP
1501         fprintf(stderr, "via gid=%d, added %d\n", gid,
1502                 e.tentryg.supergroup[i]);
1503 #endif
1504         code = AddToPRList(alist, sizeP, tentryg->supergroup[i]);
1505         if (code)
1506             return code;
1507         code =
1508             GetListSG2(at, tentryg->supergroup[i], alist, sizeP, depth - 1);
1509         if (code)
1510             return code;
1511     }
1512
1513     nptr = tentryg->nextsg;
1514     while (nptr) {
1515         didsomething = 1;
1516         /* look through cont entries */
1517         code = pr_ReadCoEntry(at, 0, nptr, &centry);
1518         if (code != 0)
1519             return code;
1520         for (i = 0; i < COSIZE; i++) {
1521             if (centry.entries[i] == PRBADID)
1522                 continue;
1523             if (centry.entries[i] == 0)
1524                 break;
1525 #if DEBUG_SG_MAP
1526             fprintf(stderr, "via gid=%d, added %d\n", gid,
1527                     e.centry.entries[i]);
1528 #endif
1529             code = AddToPRList(alist, sizeP, centry.entries[i]);
1530             if (code)
1531                 return code;
1532             code = GetListSG2(at, centry.entries[i], alist, sizeP, depth - 1);
1533             if (code)
1534                 return code;
1535         }
1536         nptr = centry.next;
1537         if (count++ > 50) {
1538 #ifndef AFS_PTHREAD_ENV
1539             IOMGR_Poll();
1540 #endif
1541             count = 0;
1542         }
1543     }
1544 #if DEBUG_SG_MAP
1545     fprintf(stderr, "] for gid %d, done [flag=%s]\n", gid,
1546             didsomething ? "TRUE" : "FALSE");
1547     if (predictflagged && didsomething != predictfound)
1548         fprintf(stderr, "**** for gid=%d, didsomething=%d predictfound=%d\n",
1549                 didsomething, predictfound);
1550 #endif
1551     if (didsomething)
1552         sg_found = add_map(sg_found, -gid);
1553     else
1554         sg_found = bic_map(sg_found, add_map(NIL_MAP, -gid));
1555     sg_flagged = add_map(sg_flagged, -gid);
1556     return 0;
1557 }
1558
1559 afs_int32
1560 GetSGList(struct ubik_trans *at, struct prentry *tentry, prlist *alist)
1561 {
1562     afs_int32 code;
1563     afs_int32 i;
1564     struct contentry centry;
1565     struct prentryg *tentryg;
1566     afs_int32 nptr;
1567     int size;
1568     int count = 0;
1569
1570     size = 0;
1571     alist->prlist_val = 0;
1572     alist->prlist_len = 0;
1573
1574     tentryg = (struct prentryg *)tentry;
1575     for (i = 0; i < SGSIZE; i++) {
1576         if (tentryg->supergroup[i] == PRBADID)
1577             continue;
1578         if (tentryg->supergroup[i] == 0)
1579             break;
1580         code = AddToPRList(alist, &size, tentryg->supergroup[i]);
1581         if (code)
1582             return code;
1583     }
1584
1585     nptr = tentryg->nextsg;
1586     while (nptr) {
1587         /* look through cont entries */
1588         code = pr_ReadCoEntry(at, 0, nptr, &centry);
1589         if (code != 0)
1590             return code;
1591         for (i = 0; i < COSIZE; i++) {
1592             if (centry.entries[i] == PRBADID)
1593                 continue;
1594             if (centry.entries[i] == 0)
1595                 break;
1596             code = AddToPRList(alist, &size, centry.entries[i]);
1597             if (code)
1598                 return code;
1599         }
1600         nptr = centry.next;
1601         if (count++ > 50) {
1602 #ifndef AFS_PTHREAD_ENV
1603             IOMGR_Poll();
1604 #endif
1605             count = 0;
1606         }
1607     }
1608
1609 #ifndef AFS_PTHREAD_ENV
1610     if (alist->prlist_len > 100)
1611         IOMGR_Poll();
1612 #endif
1613     qsort((char *)alist->prlist_val, (int)alist->prlist_len,
1614           sizeof(afs_int32), IDCmp);
1615     return PRSUCCESS;
1616 }
1617 #endif /* SUPERGROUPS */
1618
1619 afs_int32
1620 GetOwnedChain(struct ubik_trans *ut, afs_int32 *next, prlist *alist)
1621 {
1622     afs_int32 code;
1623     struct prentry tentry;
1624     int size;
1625     int count = 0;
1626
1627     size = 0;
1628     alist->prlist_val = 0;
1629     alist->prlist_len = 0;
1630
1631     for (; *next; *next = ntohl(tentry.nextOwned)) {
1632         code = pr_Read(ut, 0, *next, &tentry, sizeof(tentry));
1633         if (code)
1634             return code;
1635         code = AddToPRList(alist, &size, ntohl(tentry.id));
1636         if (alist->prlist_len >= PR_MAXGROUPS) {
1637             return PRTOOMANY;
1638         }
1639         if (code)
1640             return code;
1641         if (count++ > 50) {
1642 #ifndef AFS_PTHREAD_ENV
1643             IOMGR_Poll();
1644 #endif
1645             count = 0;
1646         }
1647     }
1648 #ifndef AFS_PTHREAD_ENV
1649     if (alist->prlist_len > 100)
1650         IOMGR_Poll();
1651 #endif
1652     qsort(alist->prlist_val, alist->prlist_len, sizeof(afs_int32), IDCmp);
1653     return PRSUCCESS;
1654 }
1655
1656 afs_int32
1657 GetMax(struct ubik_trans *at, afs_int32 *uid, afs_int32 *gid)
1658 {
1659     *uid = ntohl(cheader.maxID);
1660     *gid = ntohl(cheader.maxGroup);
1661     return PRSUCCESS;
1662 }
1663
1664 afs_int32
1665 SetMax(struct ubik_trans *at, afs_int32 id, afs_int32 flag)
1666 {
1667     afs_int32 code;
1668     if (flag & PRGRP) {
1669         cheader.maxGroup = htonl(id);
1670         code =
1671             pr_Write(at, 0, 16, (char *)&cheader.maxGroup,
1672                      sizeof(cheader.maxGroup));
1673         if (code != 0)
1674             return code;
1675     } else {
1676         cheader.maxID = htonl(id);
1677         code =
1678             pr_Write(at, 0, 20, (char *)&cheader.maxID,
1679                      sizeof(cheader.maxID));
1680         if (code != 0)
1681             return code;
1682     }
1683     return PRSUCCESS;
1684 }
1685
1686 static afs_int32
1687 UpdateCache(struct ubik_trans *tt, void *rock)
1688 {
1689     afs_int32 code;
1690
1691     code = pr_Read(tt, 0, 0, (char *)&cheader, sizeof(cheader));
1692     if (code != 0) {
1693         afs_com_err(whoami, code, "Couldn't read header");
1694     }
1695     return code;
1696 }
1697
1698 afs_int32
1699 read_DbHeader(struct ubik_trans *tt)
1700 {
1701     return ubik_CheckCache(tt, UpdateCache, NULL);
1702 }
1703
1704 int pr_noAuth;
1705
1706 /**
1707  * reads in db cache from ubik.
1708  *
1709  * @param[in] ut ubik transaction
1710  * @param[out] rock  opaque pointer to an int*, which on success will be set
1711  *                   to 1 if we need to build the database, or 0 if we do not
1712  *
1713  * @return operation status
1714  *   @retval 0 success
1715  */
1716 static afs_int32
1717 Initdb_check(struct ubik_trans *tt, void *rock)
1718 {
1719     int *build_rock = rock;
1720     afs_int32 code;
1721     afs_int32 len;
1722
1723     len = sizeof(cheader);
1724     code = pr_Read(tt, 0, 0, (char *)&cheader, len);
1725     if (code != 0) {
1726         afs_com_err(whoami, code, "couldn't read header");
1727         return code;
1728     }
1729     if ((ntohl(cheader.version) == PRDBVERSION)
1730         && ntohl(cheader.headerSize) == sizeof(cheader)
1731         && ntohl(cheader.eofPtr) != 0
1732         && FindByID(tt, ANONYMOUSID) != 0) {
1733         /* database exists, so we don't have to build it */
1734         *build_rock = 0;
1735         return 0;
1736     }
1737
1738     /* else we need to build a database */
1739     *build_rock = 1;
1740     return 0;
1741 }
1742
1743 afs_int32
1744 Initdb(void)
1745 {
1746     struct ubik_trans *tt;
1747     int build = 0;
1748     afs_int32 code;
1749
1750     /* init the database.  We'll try reading it, but if we're starting
1751      * from scratch, we'll have to do a write transaction. */
1752
1753     pr_noAuth = afsconf_GetNoAuthFlag(prdir);
1754
1755     code = ubik_BeginTransReadAny(dbase, UBIK_READTRANS, &tt);
1756     if (code)
1757         return code;
1758     code = ubik_SetLock(tt, 1, 1, LOCKREAD);
1759     if (code) {
1760         ubik_AbortTrans(tt);
1761         return code;
1762     }
1763
1764     code = ubik_CheckCache(tt, Initdb_check, &build);
1765     if (code) {
1766         ubik_AbortTrans(tt);
1767         return code;
1768     }
1769
1770     if (build) {
1771         /* Only rebuild database if the db was deleted (the header is zero) and we
1772          * are running noAuth. */
1773         char *bp = (char *)&cheader;
1774         int i;
1775         for (i = 0; i < sizeof(cheader); i++) {
1776             if (bp[i]) {
1777                 code = PRDBBAD;
1778                 afs_com_err(whoami, code,
1779                         "Can't rebuild database because it is not empty");
1780                 break;
1781             }
1782         }
1783         if (!pr_noAuth) {
1784             code = PRDBBAD;
1785             afs_com_err(whoami, code,
1786                         "Can't rebuild database because not running NoAuth");
1787         }
1788     }
1789
1790     if (code) {
1791         ubik_EndTrans(tt);
1792     } else {
1793         code = ubik_EndTrans(tt);
1794     }
1795     if (code || !build) {
1796         /* either we encountered an error, or we don't need to build the db */
1797         return code;
1798     }
1799
1800     code = ubik_BeginTrans(dbase, UBIK_WRITETRANS, &tt);
1801     if (code)
1802         return code;
1803
1804     code = ubik_SetLock(tt, 1, 1, LOCKWRITE);
1805     if (code) {
1806         ubik_AbortTrans(tt);
1807         return code;
1808     }
1809
1810     /* before doing a rebuild, check again that the dbase looks bad, because
1811      * the previous check was only under a ReadAny transaction, and there could
1812      * actually have been a good database out there.  Now that we have a
1813      * real write transaction, make sure things are still bad.
1814      */
1815     code = pr_Read(tt, 0, 0, (char *)&cheader, sizeof(cheader));
1816     if (code != 0) {
1817         afs_com_err(whoami, code, "couldn't read header");
1818         ubik_AbortTrans(tt);
1819         return code;
1820     }
1821     if ((ntohl(cheader.version) == PRDBVERSION)
1822         && ntohl(cheader.headerSize) == sizeof(cheader)
1823         && ntohl(cheader.eofPtr) != 0
1824         && FindByID(tt, ANONYMOUSID) != 0) {
1825         /* database exists, so we don't have to build it */
1826         code = ubik_EndTrans(tt);
1827         if (code)
1828             return code;
1829         return PRSUCCESS;
1830     }
1831
1832     /* Initialize the database header */
1833     if ((code = set_header_word(tt, version, htonl(PRDBVERSION)))
1834         || (code = set_header_word(tt, headerSize, htonl(sizeof(cheader))))
1835         || (code = set_header_word(tt, eofPtr, cheader.headerSize))) {
1836         afs_com_err(whoami, code, "couldn't write header words");
1837         ubik_AbortTrans(tt);
1838         return code;
1839     }
1840 #define InitialGroup(id,name) do {    \
1841     afs_int32 temp = (id);                    \
1842     afs_int32 flag = (id) < 0 ? PRGRP : 0; \
1843     code = CreateEntry                \
1844         (tt, (name), &temp, /*idflag*/1, flag, SYSADMINID, SYSADMINID); \
1845     if (code) {                       \
1846         afs_com_err (whoami, code, "couldn't create %s with id %di.",   \
1847                  (name), (id));       \
1848         ubik_AbortTrans(tt);          \
1849         return code;                  \
1850     }                                 \
1851 } while (0)
1852
1853     InitialGroup(SYSADMINID, "system:administrators");
1854     InitialGroup(SYSBACKUPID, "system:backup");
1855     InitialGroup(ANYUSERID, "system:anyuser");
1856     InitialGroup(AUTHUSERID, "system:authuser");
1857     InitialGroup(SYSVIEWERID, "system:ptsviewers");
1858     InitialGroup(ANONYMOUSID, "anonymous");
1859
1860     /* Well, we don't really want the max id set to anonymousid, so we'll set
1861      * it back to 0 */
1862     code = set_header_word(tt, maxID, 0);       /* correct in any byte order */
1863     if (code) {
1864         afs_com_err(whoami, code, "couldn't reset max id");
1865         ubik_AbortTrans(tt);
1866         return code;
1867     }
1868
1869     code = ubik_EndTrans(tt);
1870     if (code)
1871         return code;
1872     return PRSUCCESS;
1873 }
1874
1875 afs_int32
1876 ChangeEntry(struct ubik_trans *at, afs_int32 aid, afs_int32 cid, char *name, afs_int32 oid, afs_int32 newid)
1877 {
1878     afs_int32 code;
1879     afs_int32 i, pos;
1880 #if defined(SUPERGROUPS)
1881     afs_int32 nextpos;
1882 #else
1883     afs_int32 nptr;
1884     struct contentry centry;
1885 #endif
1886     struct prentry tentry, tent;
1887     afs_int32 loc;
1888     afs_int32 oldowner;
1889     char holder[PR_MAXNAMELEN];
1890     char temp[PR_MAXNAMELEN];
1891     char oldname[PR_MAXNAMELEN];
1892     char *atsign;
1893
1894     memset(holder, 0, PR_MAXNAMELEN);
1895     memset(temp, 0, PR_MAXNAMELEN);
1896     loc = FindByID(at, aid);
1897     if (!loc)
1898         return PRNOENT;
1899     code = pr_ReadEntry(at, 0, loc, &tentry);
1900     if (code)
1901         return PRDBFAIL;
1902     if (restricted && !IsAMemberOf(at, cid, SYSADMINID))
1903         return PRPERM;
1904     if (tentry.owner != cid && !IsAMemberOf(at, cid, SYSADMINID)
1905         && !IsAMemberOf(at, cid, tentry.owner) && !pr_noAuth)
1906         return PRPERM;
1907 #ifdef PR_REMEMBER_TIMES
1908     tentry.changeTime = time(0);
1909 #endif
1910
1911     /* we're actually trying to change the id */
1912     if (newid && (newid != aid)) {
1913         if (!IsAMemberOf(at, cid, SYSADMINID) && !pr_noAuth)
1914             return PRPERM;
1915
1916         pos = FindByID(at, newid);
1917         if (pos)
1918             return PRIDEXIST;   /* new id already in use! */
1919         if ((aid < 0 && newid > 0) || (aid > 0 && newid < 0))
1920             return PRPERM;
1921
1922         /* Should check that foreign users id to change to is good: inRange() */
1923
1924         /* if new id is not in use, rehash things */
1925         code = RemoveFromIDHash(at, aid, &loc);
1926         if (code != PRSUCCESS)
1927             return code;
1928         tentry.id = newid;
1929         code = pr_WriteEntry(at, 0, loc, &tentry);
1930         if (code)
1931             return code;
1932         code = AddToIDHash(at, tentry.id, loc);
1933         if (code)
1934             return code;
1935
1936         /* get current data */
1937         code = pr_ReadEntry(at, 0, loc, &tentry);
1938         if (code)
1939             return PRDBFAIL;
1940
1941 #if defined(SUPERGROUPS)
1942         if (tentry.id > (afs_int32) ntohl(cheader.maxID))
1943             code = set_header_word(at, maxID, htonl(tentry.id));
1944         if (code)
1945             return PRDBFAIL;
1946
1947         /* need to fix up: membership
1948          * (supergroups?)
1949          * ownership
1950          */
1951
1952         for (i = 0; i < PRSIZE; i++) {
1953             if (tentry.entries[i] == PRBADID)
1954                 continue;
1955             if (tentry.entries[i] == 0)
1956                 break;
1957             if ((tentry.flags & PRGRP) && tentry.entries[i] < 0) {      /* Supergroup */
1958                 return 5;       /* not yet, in short. */
1959             } else {
1960                 code = ChangeIDEntry(at, aid, newid, tentry.entries[i]);
1961             }
1962             if (code)
1963                 return code;
1964         }
1965         for (pos = ntohl(tentry.owned); pos; pos = nextpos) {
1966             code = pr_ReadEntry(at, 0, pos, &tent);
1967             if (code)
1968                 break;
1969             tent.owner = newid;
1970             nextpos = tent.nextOwned;
1971             code = pr_WriteEntry(at, 0, pos, &tent);
1972             if (code)
1973                 break;
1974         }
1975         pos = tentry.next;
1976         while (pos) {
1977 #define centry  (*(struct contentry*)&tent)
1978             code = pr_ReadCoEntry(at, 0, pos, &centry);
1979             if ((centry.id != aid)
1980                 || !(centry.flags & PRCONT)) {
1981                 fprintf(stderr,
1982                         "ChangeEntry: bad database aid=%d centry.id=%d .flags=%d\n",
1983                         aid, centry.id, centry.flags);
1984                 return PRDBBAD;
1985             }
1986             centry.id = newid;
1987             for (i = 0; i < COSIZE; i++) {
1988                 if (centry.entries[i] == PRBADID)
1989                     continue;
1990                 if (centry.entries[i] == 0)
1991                     break;
1992                 if ((centry.flags & PRGRP) && centry.entries[i] < 0) {  /* Supergroup */
1993                     return 5;   /* not yet, in short. */
1994                 } else {
1995                     code = ChangeIDEntry(at, aid, newid, centry.entries[i]);
1996                 }
1997                 if (code)
1998                     return code;
1999             }
2000             code = pr_WriteCoEntry(at, 0, pos, &centry);
2001             pos = centry.next;
2002 #undef centry
2003         }
2004         if (code)
2005             return code;
2006
2007 #else /* SUPERGROUPS */
2008
2009
2010         /* Also change the references from the membership list */
2011         for (i = 0; i < PRSIZE; i++) {
2012             if (tentry.entries[i] == PRBADID)
2013                 continue;
2014             if (tentry.entries[i] == 0)
2015                 break;
2016             pos = FindByID(at, tentry.entries[i]);
2017             if (!pos)
2018                 return (PRDBFAIL);
2019             code = RemoveFromEntry(at, aid, tentry.entries[i]);
2020             if (code)
2021                 return code;
2022             code = pr_ReadEntry(at, 0, pos, &tent);
2023             if (code)
2024                 return code;
2025             code = AddToEntry(at, &tent, pos, newid);
2026             if (code)
2027                 return code;
2028         }
2029         /* Look through cont entries too. This needs to be broken into
2030          * seperate transaction so that no one transaction becomes too
2031          * large to complete.
2032          */
2033         for (nptr = tentry.next; nptr; nptr = centry.next) {
2034             code = pr_ReadCoEntry(at, 0, nptr, &centry);
2035             if (code)
2036                 return code;
2037             for (i = 0; i < COSIZE; i++) {
2038                 if (centry.entries[i] == PRBADID)
2039                     continue;
2040                 if (centry.entries[i] == 0)
2041                     break;
2042                 pos = FindByID(at, centry.entries[i]);
2043                 if (!pos)
2044                     return (PRDBFAIL);
2045                 code = RemoveFromEntry(at, aid, centry.entries[i]);
2046                 if (code)
2047                     return code;
2048                 code = pr_ReadEntry(at, 0, pos, &tent);
2049                 if (code)
2050                     return code;
2051                 code = AddToEntry(at, &tent, pos, newid);
2052                 if (code)
2053                     return code;
2054             }
2055         }
2056 #endif /* SUPERGROUPS */
2057     }
2058
2059     atsign = strchr(tentry.name, '@');  /* check for foreign entry */
2060
2061     /* Change the owner */
2062     if (oid && (oid != tentry.owner)) {
2063         /* only groups can have their owner's changed */
2064         if (!(tentry.flags & PRGRP))
2065             return PRPERM;
2066         if (atsign != NULL)
2067             return PRPERM;
2068         oldowner = tentry.owner;
2069         tentry.owner = oid;
2070         /* The entry must be written through first so Remove and Add routines
2071          * can operate on disk data */
2072         code = pr_WriteEntry(at, 0, loc, &tentry);
2073         if (code)
2074             return PRDBFAIL;
2075
2076         /* switch owner chains */
2077         if (oldowner)           /* if it has an owner */
2078             code = RemoveFromOwnerChain(at, tentry.id, oldowner);
2079         else                    /* must be an orphan */
2080             code = RemoveFromOrphan(at, tentry.id);
2081         if (code)
2082             return code;
2083         code = AddToOwnerChain(at, tentry.id, tentry.owner);
2084         if (code)
2085             return code;
2086
2087         /* fix up the name */
2088         if (strlen(name) == 0)
2089             name = tentry.name;
2090         /* get current data */
2091         code = pr_ReadEntry(at, 0, loc, &tentry);
2092         if (code)
2093             return PRDBFAIL;
2094     }
2095
2096     /* Change the name, if name is a ptr to tentry.name then this name change
2097      * is due to a chown, otherwise caller has specified a new name */
2098     if ((name == tentry.name) || (*name && (strcmp(tentry.name, name) != 0))) {
2099         strncpy(oldname, tentry.name, PR_MAXNAMELEN);
2100         if (tentry.flags & PRGRP) {
2101             /* don't let foreign cell groups change name */
2102             if (atsign != NULL)
2103                 return PRPERM;
2104             code = CorrectGroupName(at, name, cid, tentry.owner, tentry.name);
2105             if (code)
2106                 return code;
2107
2108             if (name == tentry.name) {  /* owner fixup */
2109                 if (strcmp(oldname, tentry.name) == 0)
2110                     goto nameOK;
2111             } else {            /* new name, caller must be correct */
2112                 if (strcmp(name, tentry.name) != 0)
2113                     return PRBADNAM;
2114             }
2115         } else
2116             /* Allow a foreign name change only if the cellname part is
2117              * the same */
2118         {
2119             char *newatsign;
2120
2121             newatsign = strchr(name, '@');
2122             if (newatsign != atsign) {  /* if they are the same no problem */
2123                 /*if the pointers are not equal the strings better be */
2124                 if ((atsign == NULL) || (newatsign == NULL)
2125                     || strcmp(atsign, newatsign))
2126                     return PRPERM;
2127             }
2128             if (!CorrectUserName(name))
2129                 return PRBADNAM;
2130         }
2131
2132         pos = FindByName(at, name, &tent);
2133         if (pos)
2134             return PREXIST;
2135         code = RemoveFromNameHash(at, oldname, &loc);
2136         if (code != PRSUCCESS)
2137             return code;
2138         strncpy(tentry.name, name, PR_MAXNAMELEN);
2139         code = pr_WriteEntry(at, 0, loc, &tentry);
2140         if (code)
2141             return PRDBFAIL;
2142         code = AddToNameHash(at, tentry.name, loc);
2143         if (code != PRSUCCESS)
2144             return code;
2145       nameOK:;
2146     }
2147     return PRSUCCESS;
2148 }
2149
2150
2151 static afs_int32
2152 allocNextId(struct ubik_trans * at, struct prentry * cellEntry)
2153 {
2154     /* Id's for foreign cell entries are constructed as follows:
2155      * The 16 low order bits are the group id of the cell and the
2156      * top 16 bits identify the particular users in that cell */
2157
2158     afs_int32 id;
2159     afs_int32 cellid = ((ntohl(cellEntry->id)) & 0x0000ffff);
2160
2161     id = (ntohl(cellEntry->nusers) + 1);
2162     while (FindByID(at, ((id << 16) | cellid))) {
2163         id++;
2164         if (id > 0xffff)
2165             return 0;
2166     }
2167
2168     cellEntry->nusers = htonl(id);
2169     /* use the field nusers to keep
2170      * the next available id in that
2171      * foreign cell's group. Note :
2172      * It would seem more appropriate
2173      * to use ngroup for that and nusers
2174      * to enforce the quota, however pts
2175      * does not have an option to change
2176      * foreign users quota yet  */
2177
2178     id = (id << 16) | cellid;
2179     return id;
2180 }
2181
2182 static int
2183 inRange(struct prentry *cellEntry, afs_int32 aid)
2184 {
2185     afs_uint32 id, cellid, groupid;
2186
2187
2188     /*
2189      * The only thing that we want to make sure here is that
2190      * the id is in the legal range of this group. If it is
2191      * a duplicate we don't care since it will get caught
2192      * in a different check.
2193      */
2194
2195     cellid = aid & 0x0000ffff;
2196     groupid = (ntohl(cellEntry->id)) & 0x0000ffff;
2197     if (cellid != groupid)
2198         return 0;               /* not in range */
2199
2200     /*
2201      * if we got here we're ok but we need to update the nusers
2202      * field in order to get the id correct the next time that
2203      * we try to allocate it automatically
2204      */
2205
2206     id = aid >> 16;
2207     if (id > ntohl(cellEntry->nusers))
2208         cellEntry->nusers = htonl(id);
2209     return 1;
2210
2211 }
2212
2213 static int
2214 AddAuthGroup(struct prentry *tentry, prlist *alist, afs_int32 *size)
2215 {
2216     if (!(strchr(tentry->name, '@')))
2217         return (AddToPRList(alist, size, AUTHUSERID));
2218     else
2219         return PRSUCCESS;
2220 }