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