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