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