freebsd: properly track vcache references
[openafs.git] / src / afs / afs_nfsclnt.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 #include <afsconfig.h>
11 #include "afs/param.h"
12
13
14 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
15 #include "afs/sysincludes.h"    /* Standard vendor system headers */
16 #include "afsincludes.h"        /* Afs-based standard headers */
17 #include "afs/afs_stats.h"      /* statistics */
18 #include "afs/nfsclient.h"
19 #include "rx/rx_globals.h"
20 #include "afs/pagcb.h"
21
22 void afs_nfsclient_hold(), afs_PutNfsClientPag(), afs_nfsclient_GC();
23 static void afs_nfsclient_getcreds();
24 int afs_nfsclient_sysname(), afs_nfsclient_stats(), afs_nfsclient_checkhost();
25 afs_uint32 afs_nfsclient_gethost();
26 #ifdef AFS_AIX_IAUTH_ENV
27 int afs_allnfsreqs, afs_nfscalls;
28 #endif
29
30 /* routines exported to the "AFS exporter" layer */
31 struct exporterops nfs_exportops = {
32     afs_nfsclient_reqhandler,
33     afs_nfsclient_hold,
34     afs_PutNfsClientPag,        /* Used to be afs_nfsclient_rele */
35     afs_nfsclient_sysname,
36     afs_nfsclient_GC,
37     afs_nfsclient_stats,
38     afs_nfsclient_checkhost,
39     afs_nfsclient_gethost
40 };
41
42
43 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
44 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
45 extern struct afs_exporter *afs_nfsexporter;
46
47 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't
48  * exist. RefCount is incremented and it's time stamped. */
49 static struct nfsclientpag *
50 afs_GetNfsClientPag(afs_int32 uid, afs_uint32 host)
51 {
52     struct nfsclientpag *np;
53     afs_int32 i, now;
54
55 #if defined(AFS_SGIMP_ENV)
56     osi_Assert(ISAFS_GLOCK());
57 #endif
58     AFS_STATCNT(afs_GetNfsClientPag);
59     i = NHash(host);
60     now = osi_Time();
61     ObtainWriteLock(&afs_xnfspag, 314);
62     for (np = afs_nfspags[i]; np; np = np->next) {
63         if (np->uid == uid && np->host == host) {
64             np->refCount++;
65             np->lastcall = now;
66             ReleaseWriteLock(&afs_xnfspag);
67             return np;
68         }
69     }
70     /* next try looking for NOPAG dude, if we didn't find an exact match */
71     for (np = afs_nfspags[i]; np; np = np->next) {
72         if (np->uid == NOPAG && np->host == host) {
73             np->refCount++;
74             np->lastcall = now;
75             ReleaseWriteLock(&afs_xnfspag);
76             return np;
77         }
78     }
79     np = afs_osi_Alloc(sizeof(struct nfsclientpag));
80     osi_Assert(np != NULL);
81     memset(np, 0, sizeof(struct nfsclientpag));
82     /* Copy the necessary afs_exporter fields */
83     memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
84     np->next = afs_nfspags[i];
85     afs_nfspags[i] = np;
86     np->uid = uid;
87     np->host = host;
88     np->refCount = 1;
89     np->lastcall = now;
90     ReleaseWriteLock(&afs_xnfspag);
91     return np;
92 }
93
94
95 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
96 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
97 void
98 afs_PutNfsClientPag(np)
99      struct nfsclientpag *np;
100 {
101 #if defined(AFS_SGIMP_ENV)
102     osi_Assert(ISAFS_GLOCK());
103 #endif
104     AFS_STATCNT(afs_PutNfsClientPag);
105     --np->refCount;
106 }
107
108
109 /* Return the nfsclientpag structure associated with the (uid, host) or
110  * {pag, host} pair, if pag is nonzero. RefCount is incremented and it's
111  * time stamped. */
112 static struct nfsclientpag *
113 afs_FindNfsClientPag(afs_int32 uid, afs_uint32 host, afs_int32 pag)
114 {
115     struct nfsclientpag *np;
116     afs_int32 i;
117
118 #if defined(AFS_SGIMP_ENV)
119     osi_Assert(ISAFS_GLOCK());
120 #endif
121     AFS_STATCNT(afs_FindNfsClientPag);
122     i = NHash(host);
123     ObtainWriteLock(&afs_xnfspag, 315);
124     for (np = afs_nfspags[i]; np; np = np->next) {
125         if (np->host == host) {
126             if ((pag && pag == np->pag) || (!pag && (uid == np->uid))) {
127                 np->refCount++;
128                 np->lastcall = osi_Time();
129                 ReleaseWriteLock(&afs_xnfspag);
130                 return np;
131             }
132         }
133     }
134     /* still not there, try looking for a wildcard dude */
135     for (np = afs_nfspags[i]; np; np = np->next) {
136         if (np->host == host) {
137             if (np->uid == NOPAG) {
138                 np->refCount++;
139                 np->lastcall = osi_Time();
140                 ReleaseWriteLock(&afs_xnfspag);
141                 return np;
142             }
143         }
144     }
145     ReleaseWriteLock(&afs_xnfspag);
146     return NULL;
147 }
148
149
150 /* routine to initialize the exporter, made global so we can call it
151  * from pioctl calls.
152  */
153 struct afs_exporter *afs_nfsexported = 0;
154 static afs_int32 init_nfsexporter = 0;
155
156 void
157 afs_nfsclient_init(void)
158 {
159 #if defined(AFS_SGIMP_ENV)
160     osi_Assert(ISAFS_GLOCK());
161 #endif
162     if (!init_nfsexporter) {
163         extern struct afs_exporter *exporter_add();
164
165         init_nfsexporter = 1;
166         LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
167         afs_nfsexported =
168             exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
169     }
170 }
171
172
173 /* Main handler routine for the NFS exporter. It's called in the early
174  * phases of any remote call (via the NFS server or pioctl).
175  */
176 int
177 afs_nfsclient_reqhandler(struct afs_exporter *exporter,
178                          afs_ucred_t **cred,
179                          afs_uint32 host, afs_int32 *pagparam,
180                          struct afs_exporter **outexporter)
181 {
182     struct nfsclientpag *np, *tnp;
183     extern struct unixuser *afs_FindUser(), *afs_GetUser();
184     struct unixuser *au = 0;
185     afs_int32 uid, pag, code = 0;
186
187     AFS_ASSERT_GLOCK();
188     AFS_STATCNT(afs_nfsclient_reqhandler);
189     if (!afs_nfsexporter)
190         afs_nfsexporter = afs_nfsexported;
191
192     afs_nfsexporter->exp_stats.calls++;
193     if (!(afs_nfsexporter->exp_states & EXP_EXPORTED)) {
194         /* No afs requests accepted as long as EXPORTED flag is turned 'off'.
195          * Set/Reset via a pioctl call (fs exportafs). Note that this is on
196          * top of the /etc/exports nfs requirement (i.e. /afs must be
197          * exported to all or whomever there too!)
198          */
199         afs_nfsexporter->exp_stats.rejectedcalls++;
200         return EINVAL;
201     }
202 /*    ObtainWriteLock(&afs_xnfsreq); */
203     pag = PagInCred(*cred);
204 #if defined(AFS_SUN510_ENV)
205     uid = crgetuid(*cred);
206 #else
207     uid = afs_cr_uid(*cred);
208 #endif
209     /* Do this early, so pag management knows */
210     afs_set_cr_rgid(*cred, NFSXLATOR_CRED);     /* Identify it as nfs xlator call */
211     if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
212         uid = pag;
213     } else if (pag != NOPAG) {
214         /* Do some minimal pag verification */
215         if (pag > getpag()) {
216             pag = NOPAG;        /* treat it as not paged since couldn't be good  */
217         } else {
218             if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
219                 if (!au->exporter) {
220                     pag = NOPAG;
221                     afs_PutUser(au, READ_LOCK);
222                     au = NULL;
223                 }
224             } else
225                 pag = NOPAG;    /*  No unixuser struct so pag not trusted  */
226         }
227     }
228     np = afs_FindNfsClientPag(uid, host, 0);
229     afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
230                ICL_TYPE_LONG, afs_cr_uid(*cred), ICL_TYPE_INT32, host,
231                ICL_TYPE_POINTER, np);
232     /* If remote-pags are enabled, we are no longer interested in what PAG
233      * they claimed, and from here on we should behave as if they claimed
234      * none at all, which is to say we use the (local) pag named in the
235      * nfsclientpag structure (if any).  This is deferred until here so
236      * that we can log the PAG they claimed.
237      */
238     if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
239         pag = NOPAG;
240     if (!np) {
241         /* Even if there is a "good" pag coming in we don't accept it if no
242          * nfsclientpag struct exists for the user since that would mean
243          * that the translator rebooted and therefore we ignore all older
244          * pag values
245          */
246         if ((code = setpag(cred, -1, &pag, 0))) {
247             if (au)
248                 afs_PutUser(au, READ_LOCK);
249 /*          ReleaseWriteLock(&afs_xnfsreq);             */
250 #if defined(KERNEL_HAVE_UERROR)
251             setuerror(code);
252 #endif
253             return (code);
254         }
255         np = afs_GetNfsClientPag(uid, host);
256         np->pag = pag;
257         np->client_uid = afs_cr_uid(*cred);
258     } else {
259         if (pag == NOPAG) {
260             if ((code = setpag(cred, np->pag, &pag, 0))) {
261                 afs_PutNfsClientPag(np);
262 /*              ReleaseWriteLock(&afs_xnfsreq); */
263 #if defined(KERNEL_HAVE_UERROR)
264                 setuerror(code);
265 #endif
266                 return (code);
267             }
268         } else if (au->exporter
269                    && ((struct afs_exporter *)np != au->exporter)) {
270             tnp = (struct nfsclientpag *)au->exporter;
271             if (tnp->uid && (tnp->uid != (afs_int32) - 2)) {    /* allow "root" initiators */
272                 /* Pag doesn't belong to caller; treat it as an unpaged call too */
273                 if ((code = setpag(cred, np->pag, &pag, 0))) {
274                     afs_PutNfsClientPag(np);
275                     afs_PutUser(au, READ_LOCK);
276                     /*      ReleaseWriteLock(&afs_xnfsreq);     */
277 #if defined(KERNEL_HAVE_UERROR)
278                     setuerror(code);
279 #endif
280                     return (code);
281                 }
282                 afs_nfsexporter->exp_stats.invalidpag++;
283             }
284         }
285     }
286     if (au)
287         afs_PutUser(au, READ_LOCK);
288     au = afs_GetUser(pag, -1, WRITE_LOCK);
289     if (!(au->exporter)) {      /* Created new unixuser struct */
290         np->refCount++;         /* so it won't disappear */
291         au->exporter = (struct afs_exporter *)np;
292         if ((afs_nfsexporter->exp_states & EXP_CALLBACK))
293             afs_nfsclient_getcreds(au);
294     } else while (au->states & UNFSGetCreds) {
295         afs_osi_Sleep((void *)au);
296     }
297     *pagparam = pag;
298     *outexporter = (struct afs_exporter *)np;
299     afs_PutUser(au, WRITE_LOCK);
300 /*    ReleaseWriteLock(&afs_xnfsreq);   */
301     return 0;
302 }
303
304 void
305 afs_nfsclient_getcreds(struct unixuser *au)
306 {
307     struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
308     struct rx_securityClass *csec;
309     struct rx_connection *tconn;
310     union tokenUnion *tokenPtr;
311     struct rxkadToken *token;
312     SysNameList tsysnames;
313     CredInfos tcreds;
314     CredInfo *tcred;
315     struct unixuser *tu;
316     struct cell *tcell;
317     int code, i, cellnum;
318
319     au->states |= UNFSGetCreds;
320     memset(&tcreds, 0, sizeof(tcreds));
321     memset(&tsysnames, 0, sizeof(tsysnames));
322
323     /* Get a connection */
324     /* This sucks a little.  We should cache the connections or something.
325      * But at this point I don't yet think it's worth the effort.
326      */
327     csec = rxnull_NewClientSecurityObject();
328     AFS_GUNLOCK();
329     tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
330     AFS_GLOCK();
331
332     /* Get the sysname, if needed */
333     if (!np->sysnamecount) {
334         AFS_GUNLOCK();
335         code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
336         AFS_GLOCK();
337         if (code ||
338             tsysnames.SysNameList_len <= 0 ||
339             tsysnames.SysNameList_len > MAXNUMSYSNAMES)
340             goto done;
341
342         for(i = 0; i < np->sysnamecount; i++)
343             afs_osi_Free(np->sysname[i], MAXSYSNAME);
344
345         np->sysnamecount = tsysnames.SysNameList_len;
346         for(i = 0; i < np->sysnamecount; i++)
347             np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
348         afs_osi_Free(tsysnames.SysNameList_val,
349                      tsysnames.SysNameList_len * sizeof(SysNameEnt));
350     }
351
352     /* Get credentials */
353     AFS_GUNLOCK();
354     code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
355     AFS_GLOCK();
356     if (code)
357         goto done;
358
359     /* Now, set the credentials they gave us... */
360     for (i = 0; i < tcreds.CredInfos_len; i++) {
361         tcred = &tcreds.CredInfos_val[i];
362
363         /* Find the cell.  If it is unknown to us, punt this entry. */
364         tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
365         afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
366         if (!tcell) {
367             memset(tcred->ct.HandShakeKey, 0, 8);
368             memset(tcred->st.st_val, 0, tcred->st.st_len);
369             afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
370             continue;
371         }
372         cellnum = tcell->cellNum;
373         afs_PutCell(tcell, READ_LOCK);
374
375         /* Find the appropriate unixuser.  This might be the same as
376          * the one we were passed (au), but that's OK.
377          */
378         tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
379         if (!(tu->exporter)) {  /* Created new unixuser struct */
380             np->refCount++;             /* so it won't disappear */
381             tu->exporter = (struct afs_exporter *)np;
382         }
383
384         afs_FreeTokens(&tu->tokens);
385
386         /* Add a new rxkad token. Using the afs_AddRxkadToken interface
387          * would require another copy, so we do this the hard way */
388         tokenPtr = afs_AddToken(&tu->tokens, 2);
389         token = &tokenPtr->rxkad;
390         token->ticket = tcred->st.st_val;
391         token->ticketLen = tcred->st.st_len;
392
393         /* copy the clear token */
394         memset(&token->clearToken, 0, sizeof(token->clearToken));
395         memcpy(token->clearToken.HandShakeKey, tcred->ct.HandShakeKey, 8);
396         memset(tcred->ct.HandShakeKey, 0, 8);
397         token->clearToken.AuthHandle     = tcred->ct.AuthHandle;
398         token->clearToken.ViceId         = tcred->ct.ViceId;
399         token->clearToken.BeginTimestamp = tcred->ct.BeginTimestamp;
400         token->clearToken.EndTimestamp   = tcred->ct.EndTimestamp;
401
402         /* Set everything else, reset connections, and move on. */
403         tu->viceId = tcred->vid;
404         tu->states |= UHasTokens;
405         tu->states &= ~UTokensBad;
406         afs_SetPrimary(tu, !!(tcred->states & UPrimary));
407         tu->tokenTime = osi_Time();
408         afs_ResetUserConns(tu);
409         afs_PutUser(tu, WRITE_LOCK);
410     }
411     afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
412
413 done:
414     AFS_GUNLOCK();
415     rx_DestroyConnection(tconn);
416     AFS_GLOCK();
417     au->states &= ~UNFSGetCreds;
418     afs_osi_Wakeup((void *)au);
419 }
420
421
422 /* It's called whenever a new unixuser structure is created for the remote
423  * user associated with the nfsclientpag structure, np */
424 void
425 afs_nfsclient_hold(struct nfsclientpag *np)
426 {
427 #if defined(AFS_SGIMP_ENV)
428     osi_Assert(ISAFS_GLOCK());
429 #endif
430     AFS_STATCNT(afs_nfsclient_hold);
431     np->refCount++;
432 }
433
434
435 /* check if this exporter corresponds to the specified host */
436 int
437 afs_nfsclient_checkhost(struct nfsclientpag *np, afs_uint32 host)
438 {
439     if (np->type != EXP_NFS)
440         return 0;
441     return np->host == host;
442 }
443
444
445 /* get the host for this exporter, or 0 if there is an error */
446 afs_uint32
447 afs_nfsclient_gethost(struct nfsclientpag *np)
448 {
449     if (np->type != EXP_NFS)
450         return 0;
451     return np->host;
452 }
453
454
455 /* if inname is non-null, a new system name value is set for the remote
456  * user (inname contains the new sysname). In all cases, outname returns
457  * the current sysname value for this remote user */
458 int
459 afs_nfsclient_sysname(struct nfsclientpag *np, char *inname,
460                       char ***outname, int *num, int allpags)
461 {
462     struct nfsclientpag *tnp;
463     afs_int32 i;
464     char *cp;
465     int count, t;
466 #if defined(AFS_SGIMP_ENV)
467     osi_Assert(ISAFS_GLOCK());
468 #endif
469     AFS_STATCNT(afs_nfsclient_sysname);
470     if (allpags > 0) {
471         /* update every client, not just the one making the request */
472         i = NHash(np->host);
473         ObtainWriteLock(&afs_xnfspag, 315);
474         for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
475             if (tnp != np && tnp->host == np->host)
476                 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
477         }
478         ReleaseWriteLock(&afs_xnfspag);
479     }
480     if (inname) {
481             for(count=0; count < np->sysnamecount;++count) {
482                 afs_osi_Free(np->sysname[count], MAXSYSNAME);
483                 np->sysname[count] = NULL;
484             }
485         for(count=0; count < *num;++count) {
486             np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
487             osi_Assert(np->sysname[count] != NULL);
488         }
489         cp = inname;
490         for(count=0; count < *num;++count) {
491             t = strlen(cp);
492             memcpy(np->sysname[count], cp, t+1); /* include null */
493             cp += t+1;
494         }
495         np->sysnamecount = *num;
496     }
497     if (allpags >= 0) {
498         /* Don't touch our arguments when called recursively */
499         *outname = np->sysname;
500         *num = np->sysnamecount;
501         if (!np->sysname[0])
502             return ENODEV; /* XXX */
503     }
504     return 0;
505 }
506
507
508 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
509  * entries are removed (used by the nfsclient_shutdown routine); else if
510  * it's non zero then only the entry with that pag is removed, else all
511  * "timedout" entries are removed. TimedOut entries are those who have no
512  * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
513  * they haven't had any activity the last NFSCLIENTGC seconds */
514 void
515 afs_nfsclient_GC(struct afs_exporter *exporter,
516                  afs_int32 pag)
517 {
518     struct nfsclientpag *np, **tnp, *nnp;
519     afs_int32 i, delflag;
520         int count;
521
522 #if defined(AFS_SGIMP_ENV)
523     osi_Assert(ISAFS_GLOCK());
524 #endif
525     AFS_STATCNT(afs_nfsclient_GC);
526     ObtainWriteLock(&afs_xnfspag, 316);
527     for (i = 0; i < NNFSCLIENTS; i++) {
528         for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
529             nnp = np->next;
530             delflag = 0;
531             if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
532                 delflag = 1;
533             if ((pag == -1) || (!pag && delflag)
534                 || (pag && (np->refCount == 0) && (np->pag == pag))) {
535                 *tnp = np->next;
536                 for(count=0; count < np->sysnamecount;++count) {
537                         afs_osi_Free(np->sysname[count], MAXSYSNAME);
538                 }
539                 afs_osi_Free(np, sizeof(struct nfsclientpag));
540             } else {
541                 tnp = &np->next;
542             }
543         }
544     }
545     ReleaseWriteLock(&afs_xnfspag);
546 }
547
548
549 int
550 afs_nfsclient_stats(struct afs_exporter *export)
551 {
552     /* Nothing much to do here yet since most important stats are collected
553      * directly in the afs_exporter structure itself */
554     AFS_STATCNT(afs_nfsclient_stats);
555     return 0;
556 }
557
558 #ifdef AFS_AIX41_ENV
559 /* This is exposed so that vop_fid can test it, even if iauth is not
560  *  installed.
561  */
562 extern int afs_iauth_initd;
563 #endif
564
565 #ifdef AFS_AIX_IAUTH_ENV
566 char *afs_nfs_id = "AFSNFSTRANS";
567 /* afs_iauth_verify is the AFS authenticator for NFS.
568  *
569  * always returns 0.
570  */
571 int
572 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
573                  afs_ucred_t *credp, struct exportinfo *exp)
574 {
575     int code;
576     struct nfsclientpag *nfs_pag;
577     afs_int32 dummypag;
578     struct afs_exporter *outexporter = 0;
579
580
581     /* Still needs basic test to see if exporter is on. And need to check the
582      * whole no submounts bit.
583      */
584
585     if (id != (long)id)
586         return 0;               /* not us. */
587
588     /* Only care if it's AFS */
589     if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
590         return 0;
591     }
592
593     AFS_GLOCK();
594     code =
595         afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
596                                  &dummypag, &outexporter);
597     if (!code && outexporter)
598         EXP_RELE(outexporter);
599
600     if (code) {
601         /* ensure anonymous cred. */
602         afs_set_cr_uid(credp, (uid_t) -2;       /* anonymous */
603         afs_set_cr_ruid(credp, (uid_t) -2;
604     }
605
606     /* Mark this thread as an NFS translator thread. */
607     afs_set_cr_rgid(credp, NFSXLATOR_CRED);
608
609     AFS_GUNLOCK();
610     return 0;
611 }
612
613 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
614  * and -1 on failure. Can fail because DFS has already registered.
615  */
616 int
617 afs_iauth_register(void)
618 {
619     if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
620         return -1;
621     else {
622         afs_iauth_initd = 1;
623         return 0;
624     }
625 }
626
627 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
628  */
629 void
630 afs_iauth_unregister(void)
631 {
632     if (afs_iauth_initd)
633         nfs_iauth_unregister((unsigned long)afs_nfs_id);
634     afs_iauth_initd = 0;
635 }
636 #endif /* AFS_AIX_IAUTH_ENV */
637
638
639
640 void
641 shutdown_nfsclnt(void)
642 {
643 #if defined(AFS_SGIMP_ENV)
644     osi_Assert(ISAFS_GLOCK());
645 #endif
646     AFS_STATCNT(afs_nfsclient_shutdown);
647 #ifdef AFS_AIX_IAUTH_ENV
648     afs_iauth_register();
649 #endif
650     afs_nfsclient_GC(afs_nfsexporter, -1);
651     init_nfsexporter = 0;
652 }
653 #endif /* AFS_NONFSTRANS */