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