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