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