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