afs: maintain afs_users buckets in sorted order
[openafs.git] / src / afs / afs_user.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  * Implements:
12  */
13 #include <afsconfig.h>
14 #include "afs/param.h"
15
16
17 #include "afs/stds.h"
18 #include "afs/sysincludes.h"    /* Standard vendor system headers */
19
20 #if !defined(UKERNEL)
21 #if !defined(AFS_LINUX20_ENV)
22 #include <net/if.h>
23 #endif
24 #include <netinet/in.h>
25
26 #ifdef AFS_SGI62_ENV
27 #include "h/hashing.h"
28 #endif
29 #if !defined(AFS_HPUX110_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV)
30 #include <netinet/in_var.h>
31 #endif /* ! AFS_HPUX110_ENV */
32 #endif /* !defined(UKERNEL) */
33
34 #include "afsincludes.h"        /* Afs-based standard headers */
35 #include "afs/afs_stats.h"      /* afs statistics */
36
37 #if     defined(AFS_SUN5_ENV)
38 #include <inet/led.h>
39 #include <inet/common.h>
40 #include <netinet/ip6.h>
41 #include <inet/ip.h>
42 #endif
43
44 #include "afs/afs_axscache.h"
45
46 /* Exported variables */
47 afs_rwlock_t afs_xuser;
48 struct unixuser *afs_users[NUSERS];
49
50
51 #ifndef AFS_PAG_MANAGER
52 /* Forward declarations */
53 void afs_ResetAccessCache(afs_int32 uid, afs_int32 cell, int alock);
54 #endif /* !AFS_PAG_MANAGER */
55
56
57 /* Called from afs_Daemon to garbage collect unixusers no longer using system,
58  * and their conns.  The aforce parameter tells the function to flush all
59  * *unauthenticated* conns, no matter what their expiration time; it exists
60  * because after we choose our final rx epoch, we want to stop using calls with
61  * other epochs as soon as possible (old file servers act bizarrely when they
62  * see epoch changes).
63  */
64 void
65 afs_GCUserData(int aforce)
66 {
67     struct unixuser *tu, **lu, *nu;
68     int i;
69     afs_int32 now, delFlag;
70
71     AFS_STATCNT(afs_GCUserData);
72     /* Obtain locks in valid order */
73     ObtainWriteLock(&afs_xuser, 95);
74 #ifndef AFS_PAG_MANAGER
75     ObtainReadLock(&afs_xserver);
76     ObtainWriteLock(&afs_xconn, 96);
77 #endif
78     now = osi_Time();
79     for (i = 0; i < NUSERS; i++) {
80         for (lu = &afs_users[i], tu = *lu; tu; tu = nu) {
81             delFlag = 0;        /* should we delete this dude? */
82             /* Don't garbage collect users in use now (refCount) */
83             if (tu->refCount == 0) {
84                 if (tu->tokens) {
85                     /* Need to walk the token stack, and dispose of
86                      * all expired tokens */
87                     afs_DiscardExpiredTokens(&tu->tokens, now);
88                     if (!afs_HasUsableTokens(tu->tokens, now))
89                         delFlag = 1;
90                 } else {
91                     if (aforce || (tu->tokenTime < now - NOTOKTIMEOUT))
92                         delFlag = 1;
93                 }
94             }
95             nu = tu->next;
96             if (delFlag) {
97                 *lu = tu->next;
98 #ifndef AFS_PAG_MANAGER
99                 afs_ReleaseConnsUser(tu);
100 #endif
101                 afs_FreeTokens(&tu->tokens);
102
103                 if (tu->exporter)
104                     EXP_RELE(tu->exporter);
105                 afs_osi_Free(tu, sizeof(struct unixuser));
106             } else {
107                 lu = &tu->next;
108             }
109         }
110     }
111 #ifndef AFS_PAG_MANAGER
112     ReleaseWriteLock(&afs_xconn);
113 #endif
114 #ifndef AFS_PAG_MANAGER
115     ReleaseReadLock(&afs_xserver);
116 #endif
117     ReleaseWriteLock(&afs_xuser);
118
119 }                               /*afs_GCUserData */
120
121 static struct unixuser *
122 afs_FindUserNoLock(afs_int32 auid, afs_int32 acell)
123 {
124     struct unixuser *tu;
125     afs_int32 i;
126
127     AFS_STATCNT(afs_FindUser);
128     i = UHash(auid);
129     for (tu = afs_users[i]; tu; tu = tu->next) {
130         if (tu->uid == auid && ((tu->cell == acell) || (acell == -1))) {
131             tu->refCount++;
132             return tu;
133         }
134     }
135     return NULL;
136
137 }
138
139 #ifndef AFS_PAG_MANAGER
140 /*
141  * Check for unixusers who encountered bad tokens, and reset the access
142  * cache for these guys.  Can't do this when token expiration detected,
143  * since too many locks are set then.
144  */
145 void
146 afs_CheckTokenCache(void)
147 {
148     int i;
149     struct unixuser *tu;
150     afs_int32 now;
151     struct vcache *tvc;
152     struct axscache *tofreelist;
153     int do_scan = 0;
154
155     AFS_STATCNT(afs_CheckCacheResets);
156     ObtainReadLock(&afs_xvcache);
157     ObtainReadLock(&afs_xuser);
158     now = osi_Time();
159     for (i = 0; i < NUSERS; i++) {
160         for (tu = afs_users[i]; tu; tu = tu->next) {
161             /*
162              * If tokens are still good and user has Kerberos tickets,
163              * check expiration
164              */
165             if ((tu->states & UHasTokens) && !(tu->states & UTokensBad)) {
166                 if (!afs_HasUsableTokens(tu->tokens, now)) {
167                     /*
168                      * This token has expired, warn users and reset access
169                      * cache.
170                      */
171                     tu->states |= (UTokensBad | UNeedsReset);
172                 }
173             }
174             if (tu->states & UNeedsReset)
175                 do_scan = 1;
176         }
177     }
178     /* Skip the potentially expensive scan if nothing to do */
179     if (!do_scan)
180         goto done;
181
182     tofreelist = NULL;
183     for (i = 0; i < VCSIZE; i++) {
184         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
185             /* really should do this under cache write lock, but that.
186              * is hard to under locking hierarchy */
187             if (tvc->Access) {
188                 struct axscache **ac, **nac;
189
190                 for ( ac = &tvc->Access; *ac;)  {
191                     nac = &(*ac)->next;
192                     tu = afs_FindUserNoLock((*ac)->uid, tvc->f.fid.Cell);
193                     if (tu == NULL || (tu->states & UNeedsReset)) {
194                         struct axscache *tmp;
195                         tmp = *ac;
196                         *ac = *nac;
197                         tmp->next = tofreelist;
198                         tofreelist = tmp;
199                     } else
200                         ac = nac;
201                     if (tu != NULL)
202                         tu->refCount--;
203                 }
204             }
205         }
206     }
207     afs_FreeAllAxs(&tofreelist);
208     for (i = 0; i < NUSERS; i++) {
209         for (tu = afs_users[i]; tu; tu = tu->next) {
210             if (tu->states & UNeedsReset)
211                 tu->states &= ~UNeedsReset;
212         }
213     }
214
215 done:
216     ReleaseReadLock(&afs_xuser);
217     ReleaseReadLock(&afs_xvcache);
218 }                               /*afs_CheckTokenCache */
219
220
221 /* Remove any access caches associated with this uid+cell
222  * by scanning the entire vcache table.  Specify cell=-1
223  * to remove all access caches associated with this uid
224  * regardless of cell.
225  */
226 void
227 afs_ResetAccessCache(afs_int32 uid, afs_int32 cell, int alock)
228 {
229     int i;
230     struct vcache *tvc;
231     struct axscache *ac;
232
233     AFS_STATCNT(afs_ResetAccessCache);
234     if (alock)
235         ObtainReadLock(&afs_xvcache);
236     for (i = 0; i < VCSIZE; i++) {
237         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
238             /* really should do this under cache write lock, but that.
239              * is hard to under locking hierarchy */
240             if (tvc->Access && (cell == -1 || tvc->f.fid.Cell == cell)) {
241                 ac = afs_FindAxs(tvc->Access, uid);
242                 if (ac) {
243                     afs_RemoveAxs(&tvc->Access, ac);
244                 }
245             }
246         }
247     }
248     if (alock)
249         ReleaseReadLock(&afs_xvcache);
250
251 }                               /*afs_ResetAccessCache */
252
253
254 /*
255  * Ensure all connections make use of new tokens.  Discard incorrectly-cached
256  * access info.
257  */
258 void
259 afs_ResetUserConns(struct unixuser *auser)
260 {
261     int i, j;
262     struct srvAddr *sa;
263     struct sa_conn_vector *tcv;
264
265     AFS_STATCNT(afs_ResetUserConns);
266     ObtainReadLock(&afs_xsrvAddr);
267     ObtainWriteLock(&afs_xconn, 98);
268
269     for (i = 0; i < NSERVERS; i++) {
270         for (sa = afs_srvAddrs[i]; sa; sa = sa->next_bkt) {
271             for (tcv = sa->conns; tcv; tcv = tcv->next) {
272                 if (tcv->user == auser) {
273                     for(j = 0; j < CVEC_LEN; ++j) {
274                         (tcv->cvec[j]).forceConnectFS = 1;
275                     }
276                 }
277             }
278         }
279     }
280
281     ReleaseWriteLock(&afs_xconn);
282     ReleaseReadLock(&afs_xsrvAddr);
283     afs_ResetAccessCache(auser->uid, auser->cell, 1);
284     auser->states &= ~UNeedsReset;
285 }                               /*afs_ResetUserConns */
286 #endif /* !AFS_PAG_MANAGER */
287
288
289 struct unixuser *
290 afs_FindUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
291 {
292     struct unixuser *tu;
293
294     ObtainWriteLock(&afs_xuser, 99);
295     tu = afs_FindUserNoLock(auid, acell);
296     ReleaseWriteLock(&afs_xuser);
297     if (tu)
298         afs_LockUser(tu, locktype, 365);
299     return tu;
300 }                               /*afs_FindUser */
301
302
303 /*------------------------------------------------------------------------
304  * EXPORTED afs_ComputePAGStats
305  *
306  * Description:
307  *      Compute a set of stats concerning PAGs used by this machine.
308  *
309  * Arguments:
310  *      None.
311  *
312  * Returns:
313  *      Nothing.
314  *
315  * Environment:
316  *      The results are put in the structure responsible for keeping
317  *      detailed CM stats.  Note: entries corresponding to a single PAG
318  *      will appear on the identical hash chain, so sweeping the chain
319  *      will find all entries related to a single PAG.
320  *
321  * Side Effects:
322  *      As advertised.
323  *------------------------------------------------------------------------*/
324
325 void
326 afs_ComputePAGStats(void)
327 {
328     struct unixuser *currPAGP;  /*Ptr to curr PAG */
329     struct unixuser *cmpPAGP;   /*Ptr to PAG being compared */
330     struct afs_stats_AuthentInfo *authP;        /*Ptr to stats area */
331     int curr_Record;            /*Curr record */
332     int currChain;              /*Curr hash chain */
333     int currChainLen;           /*Length of curr hash chain */
334     int currPAGRecords;         /*# records in curr PAG */
335
336     /*
337      * Lock out everyone else from scribbling on the PAG entries.
338      */
339     ObtainReadLock(&afs_xuser);
340
341     /*
342      * Initialize the tallies, then sweep through each hash chain.  We
343      * can't bzero the structure, since some fields are cumulative over
344      * the CM's lifetime.
345      */
346     curr_Record = 0;
347     authP = &(afs_stats_cmfullperf.authent);
348     authP->curr_PAGs = 0;
349     authP->curr_Records = 0;
350     authP->curr_AuthRecords = 0;
351     authP->curr_UnauthRecords = 0;
352     authP->curr_MaxRecordsInPAG = 0;
353     authP->curr_LongestChain = 0;
354
355     for (currChain = 0; currChain < NUSERS; currChain++) {
356         currChainLen = 0;
357         for (currPAGP = afs_users[currChain]; currPAGP;
358              currPAGP = currPAGP->next) {
359             /*
360              * Bump the number of records on this current chain, along with
361              * the total number of records in existence.
362              */
363             currChainLen++;
364             curr_Record++;
365             /*
366              * We've found a previously-uncounted PAG.  If it's been deleted
367              * but just not garbage-collected yet, we step over it.
368              */
369             if (!(currPAGP->states & UHasTokens))
370                 continue;
371
372             /*
373              * If this PAG record has already been ``counted', namely
374              * associated with a PAG and tallied, clear its bit and move on.
375              */
376             (authP->curr_Records)++;
377             if (currPAGP->states & UPAGCounted) {
378                 currPAGP->states &= ~UPAGCounted;
379                 continue;
380             }
381
382
383
384             /*We've counted this one already */
385             /*
386              * Jot initial info down, then sweep down the rest of the hash
387              * chain, looking for matching PAG entries.  Note: to properly
388              * ``count'' the current record, we first compare it to itself
389              * in the following loop.
390              */
391             (authP->curr_PAGs)++;
392             currPAGRecords = 0;
393
394             for (cmpPAGP = currPAGP; cmpPAGP; cmpPAGP = cmpPAGP->next) {
395                 if (currPAGP->uid == cmpPAGP->uid) {
396                     /*
397                      * The records belong to the same PAG.  First, mark the
398                      * new record as ``counted'' and bump the PAG size.
399                      * Then, record the state of its ticket, if any.
400                      */
401                     cmpPAGP->states |= UPAGCounted;
402                     currPAGRecords++;
403                     if ((cmpPAGP->states & UHasTokens)
404                         && !(cmpPAGP->states & UTokensBad))
405                         (authP->curr_AuthRecords)++;
406                     else
407                         (authP->curr_UnauthRecords)++;
408                 }               /*Records belong to same PAG */
409             }                   /*Compare to rest of PAG records in chain */
410
411             /*
412              * In the above comparisons, the current PAG record has been
413              * marked as counted.  Erase this mark before moving on.
414              */
415             currPAGP->states &= ~UPAGCounted;
416
417             /*
418              * We've compared our current PAG record with all remaining
419              * PAG records in the hash chain.  Update our tallies, and
420              * perhaps even our lifetime high water marks.  After that,
421              * remove our search mark and advance to the next comparison
422              * pair.
423              */
424             if (currPAGRecords > authP->curr_MaxRecordsInPAG) {
425                 authP->curr_MaxRecordsInPAG = currPAGRecords;
426                 if (currPAGRecords > authP->HWM_MaxRecordsInPAG)
427                     authP->HWM_MaxRecordsInPAG = currPAGRecords;
428             }
429         }                       /*Sweep a hash chain */
430
431         /*
432          * If the chain we just finished zipping through is the longest we've
433          * seen yet, remember this fact before advancing to the next chain.
434          */
435         if (currChainLen > authP->curr_LongestChain) {
436             authP->curr_LongestChain = currChainLen;
437             if (currChainLen > authP->HWM_LongestChain)
438                 authP->HWM_LongestChain = currChainLen;
439         }
440
441     }                           /*For each hash chain in afs_user */
442
443     /*
444      * Now that we've counted everything up, we can consider all-time
445      * numbers.
446      */
447     if (authP->curr_PAGs > authP->HWM_PAGs)
448         authP->HWM_PAGs = authP->curr_PAGs;
449     if (authP->curr_Records > authP->HWM_Records)
450         authP->HWM_Records = authP->curr_Records;
451
452     /*
453      * People are free to manipulate the PAG structures now.
454      */
455     ReleaseReadLock(&afs_xuser);
456
457 }                               /*afs_ComputePAGStats */
458
459 /*!
460  * Obtain a unixuser for the specified uid and cell;
461  * if no existing match found, allocate a new one.
462  *
463  * \param[in] auid      uid/PAG value
464  * \param[in] acell     cell number; if -1, match on auid only
465  * \param[in] locktype  locktype desired on returned unixuser
466  *
467  * \post unixuser is chained in afs_users[], returned with <locktype> held
468  *
469  * \note   Maintain unixusers in sorted order within hash bucket to enable
470  *         small lookup optimizations.
471  */
472
473 struct unixuser *
474 afs_GetUser(afs_int32 auid, afs_int32 acell, afs_int32 locktype)
475 {
476     struct unixuser *tu, *xu = 0, *pu = 0;
477     afs_int32 i;
478     afs_int32 RmtUser = 0;
479
480     AFS_STATCNT(afs_GetUser);
481     i = UHash(auid);
482     ObtainWriteLock(&afs_xuser, 104);
483     /* unixusers are sorted by uid in each hash bucket */
484     for (tu = afs_users[i]; tu && (tu->uid <= auid) ; xu = tu, tu = tu->next) {
485         if (tu->uid == auid) {
486             RmtUser = 0;
487             pu = NULL;
488             if (tu->exporter) {
489                 RmtUser = 1;
490                 pu = tu;
491             }
492             if (tu->cell == -1 && acell != -1) {
493                 /* Here we setup the real cell for the client */
494                 tu->cell = acell;
495                 tu->refCount++;
496                 goto done;
497             } else if (tu->cell == acell || acell == -1) {
498                 tu->refCount++;
499                 goto done;
500             }
501         }
502     }
503     /* no matching unixuser found; repurpose the tu pointer to
504      * allocate a new unixuser.
505      * xu will be insertion point for our new unixuser.
506      */
507     tu = afs_osi_Alloc(sizeof(struct unixuser));
508     osi_Assert(tu != NULL);
509 #ifndef AFS_NOSTATS
510     afs_stats_cmfullperf.authent.PAGCreations++;
511 #endif /* AFS_NOSTATS */
512     memset(tu, 0, sizeof(struct unixuser));
513     AFS_RWLOCK_INIT(&tu->lock, "unixuser lock");
514     /* insert new nu in sorted order after xu */
515     if (xu == NULL) {
516         tu->next = afs_users[i];
517         afs_users[i] = tu;
518     } else {
519         tu->next = xu->next;
520         xu->next = tu;
521     }
522     if (RmtUser) {
523         /*
524          * This is for the case where an additional unixuser struct is
525          * created because the remote client is accessing a different cell;
526          * we simply rerecord relevant information from the original
527          * structure
528          */
529         if (pu && pu->exporter) {
530             tu->exporter = pu->exporter;
531             (void)EXP_HOLD(tu->exporter);
532         }
533     }
534     tu->uid = auid;
535     tu->cell = acell;
536     tu->viceId = UNDEFVID;
537     tu->refCount = 1;
538     tu->tokenTime = osi_Time();
539     /* fall through to return the new one */
540
541  done:
542     ReleaseWriteLock(&afs_xuser);
543     afs_LockUser(tu, locktype, 364);
544     return tu;
545
546 }                               /*afs_GetUser */
547
548 void
549 afs_LockUser(struct unixuser *au, afs_int32 locktype,
550              unsigned int src_indicator)
551 {
552     switch (locktype) {
553     case READ_LOCK:
554         ObtainReadLock(&au->lock);
555         break;
556     case WRITE_LOCK:
557         ObtainWriteLock(&au->lock, src_indicator);
558         break;
559     case SHARED_LOCK:
560         ObtainSharedLock(&au->lock, src_indicator);
561         break;
562     default:
563         /* noop */
564         break;
565     }
566 }
567
568 void
569 afs_PutUser(struct unixuser *au, afs_int32 locktype)
570 {
571     AFS_STATCNT(afs_PutUser);
572
573     switch (locktype) {
574     case READ_LOCK:
575         ReleaseReadLock(&au->lock);
576         break;
577     case WRITE_LOCK:
578         ReleaseWriteLock(&au->lock);
579         break;
580     case SHARED_LOCK:
581         ReleaseSharedLock(&au->lock);
582         break;
583     default:
584         /* noop */
585         break;
586     }
587
588     --au->refCount;
589 }                               /*afs_PutUser */
590
591
592 /*
593  * Set the primary flag on a unixuser structure, ensuring that exactly one
594  * dude has the flag set at any time for a particular unix uid.
595  */
596 void
597 afs_SetPrimary(struct unixuser *au, int aflag)
598 {
599     struct unixuser *tu;
600     int i;
601     struct unixuser *pu;
602
603     AFS_STATCNT(afs_SetPrimary);
604     i = UHash(au->uid);
605     pu = NULL;
606     ObtainWriteLock(&afs_xuser, 105);
607     /*
608      * See if anyone is this uid's primary cell yet; recording in pu the
609      * corresponding user
610      */
611     for (tu = afs_users[i]; tu; tu = tu->next) {
612         if (tu->uid == au->uid && (tu->states & UPrimary)) {
613             pu = tu;
614         }
615     }
616     if (pu && !(pu->states & UHasTokens)) {
617         /*
618          * Primary user has unlogged, don't treat him as primary any longer;
619          * note that we want to treat him as primary until now, so that
620          * people see a primary identity until now.
621          */
622         pu->states &= ~UPrimary;
623         pu = NULL;
624     }
625     if (aflag == 1) {
626         /* setting au to be primary */
627         if (pu)
628             pu->states &= ~UPrimary;
629         au->states |= UPrimary;
630     } else if (aflag == 0) {
631         /* we don't know if we're supposed to be primary or not */
632         if (!pu || au == pu) {
633             au->states |= UPrimary;
634         } else
635             au->states &= ~UPrimary;
636     }
637     ReleaseWriteLock(&afs_xuser);
638
639 }                               /*afs_SetPrimary */
640
641 void
642 afs_NotifyUser(struct unixuser *auser, int event)
643 {
644 #ifdef AFS_DARWIN_ENV
645     darwin_notify_perms(auser, event);
646 #endif
647 }
648
649 /**
650  * Mark all of the unixuser records held for a particular PAG as
651  * expired
652  *
653  * @param[in] pag
654  *      PAG to expire records for
655  */
656 void
657 afs_MarkUserExpired(afs_int32 pag)
658 {
659     afs_int32 i;
660     struct unixuser *tu;
661
662     i = UHash(pag);
663     ObtainWriteLock(&afs_xuser, 9);
664     for (tu = afs_users[i]; tu; tu = tu->next) {
665         if (tu->uid == pag) {
666             tu->states &= ~UHasTokens;
667             tu->tokenTime = 0;
668         }
669     }
670     ReleaseWriteLock(&afs_xuser);
671 }
672
673
674 #if AFS_GCPAGS
675
676 /*
677  * Called by osi_TraverseProcTable (from afs_GCPAGs) for each
678  * process in the system.
679  * If the specified process uses a PAG, clear that PAG's temporary
680  * 'deleteme' flag.
681  */
682
683 /*
684  * This variable keeps track of the number of UID-base
685  * tokens in the afs_users table. When it's zero
686  * the per process loop in GCPAGs doesn't have to
687  * check processes without pags against the afs_users table.
688  */
689 static afs_int32 afs_GCPAGs_UIDBaseTokenCount = 0;
690
691 /*
692  * These variables keep track of the number of times
693  * afs_GCPAGs_perproc_func() is called.  If it is not called at all when
694  * walking the process table, there is something wrong and we should not
695  * prematurely expire any tokens.
696  */
697 static size_t afs_GCPAGs_perproc_count = 0;
698 static size_t afs_GCPAGs_cred_count = 0;
699
700 /*
701  * LOCKS: afs_GCPAGs_perproc_func requires write lock on afs_xuser
702  */
703 #if !defined(LINUX_KEYRING_SUPPORT) && (!defined(STRUCT_TASK_STRUCT_HAS_CRED) || defined(HAVE_LINUX_RCU_READ_LOCK))
704 void
705 afs_GCPAGs_perproc_func(afs_proc_t * pproc)
706 {
707     afs_int32 pag, hash, uid;
708     const afs_ucred_t *pcred;
709
710     afs_GCPAGs_perproc_count++;
711
712     pcred = afs_osi_proc2cred(pproc);
713     if (!pcred)
714         return;
715
716     afs_GCPAGs_cred_count++;
717
718     pag = PagInCred(pcred);
719 #if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV) || defined(AFS_LINUX22_ENV)
720     uid = (pag != NOPAG ? pag : afs_cr_uid(pcred));
721 #elif defined(AFS_SUN510_ENV)
722     uid = (pag != NOPAG ? pag : crgetruid(pcred));
723 #else
724     uid = (pag != NOPAG ? pag : afs_cr_ruid(pcred));
725 #endif
726     hash = UHash(uid);
727
728     /* if this token is PAG based, or it's UID based and
729      * UID-based tokens exist */
730     if ((pag != NOPAG) || (afs_GCPAGs_UIDBaseTokenCount)) {
731         /* find the entries for this uid in all cells and clear the not
732          * referenced flag.  Can't use afs_FindUser, because it just returns
733          * the specific cell asked for, or the first one found.
734          */
735         struct unixuser *pu;
736         for (pu = afs_users[hash]; pu; pu = pu->next) {
737             if (pu->uid == uid) {
738                 if (pu->states & TMP_UPAGNotReferenced) {
739                     /* clear the 'deleteme' flag for this entry */
740                     pu->states &= ~TMP_UPAGNotReferenced;
741                     if (pag == NOPAG) {
742                         /* This is a uid based token that hadn't
743                          * previously been cleared, so decrement the
744                          * outstanding uid based token count */
745                         afs_GCPAGs_UIDBaseTokenCount--;
746                     }
747                 }
748             }
749         }
750     }
751 }
752 #endif
753
754 /*
755  * Go through the process table, find all unused PAGs
756  * and cause them to be deleted during the next GC.
757  *
758  * returns the number of PAGs marked for deletion
759  *
760  * On AIX we free PAGs when the last accessing process exits,
761  * so this routine is not needed.
762  *
763  * In AFS WebSecure, we explicitly call unlog when we remove
764  * an entry in the login cache, so this routine is not needed.
765  */
766
767 afs_int32
768 afs_GCPAGs(afs_int32 * ReleasedCount)
769 {
770     struct unixuser *pu;
771     int i;
772
773     if (afs_gcpags != AFS_GCPAGS_OK) {
774         return 0;
775     }
776
777     *ReleasedCount = 0;
778
779     /* first, loop through afs_users, setting the temporary 'deleteme' flag */
780     ObtainWriteLock(&afs_xuser, 419);
781     afs_GCPAGs_UIDBaseTokenCount = 0;
782     for (i = 0; i < NUSERS; i++) {
783         for (pu = afs_users[i]; pu; pu = pu->next) {
784             pu->states |= TMP_UPAGNotReferenced;
785             if (((pu->uid >> 24) & 0xff) != 'A') {
786                 /* this is a uid-based token, */
787                 /* increment the count */
788                 afs_GCPAGs_UIDBaseTokenCount++;
789             }
790         }
791     }
792
793     /* Now, iterate through the systems process table,
794      * for each process, mark it's PAGs (if any) in use.
795      * i.e. clear the temporary deleteme flag.
796      */
797     afs_GCPAGs_perproc_count = 0;
798     afs_GCPAGs_cred_count = 0;
799
800     afs_osi_TraverseProcTable();
801
802     /* If there is an internal problem and afs_GCPAGs_perproc_func()
803      * does not get called, disable gcpags so that we do not
804      * accidentally expire all the tokens in the system.
805      */
806     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_perproc_count) {
807         afs_gcpags = AFS_GCPAGS_EPROCWALK;
808     }
809
810     if (afs_gcpags == AFS_GCPAGS_OK && !afs_GCPAGs_cred_count) {
811         afs_gcpags = AFS_GCPAGS_ECREDWALK;
812     }
813
814     /* Now, go through afs_users again, any that aren't in use
815      * (temp deleteme flag still set) will be marked for later deletion,
816      * by setting their expire times to 0.
817      */
818     for (i = 0; i < NUSERS; i++) {
819         for (pu = afs_users[i]; pu; pu = pu->next) {
820             if (pu->states & TMP_UPAGNotReferenced) {
821
822                 /* clear the temp flag */
823                 pu->states &= ~TMP_UPAGNotReferenced;
824
825                 /* Is this entry on behalf of a 'remote' user ?
826                  * i.e. nfs translator, etc.
827                  */
828                 if (!pu->exporter && afs_gcpags == AFS_GCPAGS_OK) {
829                     /* make afs_GCUserData remove this entry  */
830                     pu->states &= ~UHasTokens;
831                     pu->tokenTime = 0;
832
833                     (*ReleasedCount)++; /* remember how many we marked (info only) */
834                 }
835             }
836         }
837     }
838
839     ReleaseWriteLock(&afs_xuser);
840
841     return 0;
842 }
843
844 #endif /* AFS_GCPAGS */