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