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