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