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