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