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