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