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