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