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