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