Use unsigned addresses in the NFS exporter
[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(register afs_int32 uid, register afs_uint32 host)
51 {
52     register struct nfsclientpag *np;
53     register 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      register 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     register struct nfsclientpag *np;
115     register 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     register struct nfsclientpag *np, *tnp;
182     extern struct unixuser *afs_FindUser(), *afs_GetUser();
183     register 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     SysNameList tsysnames;
310     CredInfos tcreds;
311     CredInfo *tcred;
312     struct unixuser *tu;
313     struct cell *tcell;
314     int code, i, cellnum;
315
316     au->states |= UNFSGetCreds;
317     memset(&tcreds, 0, sizeof(tcreds));
318     memset(&tsysnames, 0, sizeof(tsysnames));
319
320     /* Get a connection */
321     /* This sucks a little.  We should cache the connections or something.
322      * But at this point I don't yet think it's worth the effort.
323      */
324     csec = rxnull_NewClientSecurityObject();
325     AFS_GUNLOCK();
326     tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
327     AFS_GLOCK();
328
329     /* Get the sysname, if needed */
330     if (!np->sysnamecount) {
331         AFS_GUNLOCK();
332         code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
333         AFS_GLOCK();
334         if (code ||
335             tsysnames.SysNameList_len <= 0 ||
336             tsysnames.SysNameList_len > MAXNUMSYSNAMES)
337             goto done;
338     
339         for(i = 0; i < np->sysnamecount; i++)
340             afs_osi_Free(np->sysname[i], MAXSYSNAME);
341
342         np->sysnamecount = tsysnames.SysNameList_len;
343         for(i = 0; i < np->sysnamecount; i++)
344             np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
345         afs_osi_Free(tsysnames.SysNameList_val,
346                      tsysnames.SysNameList_len * sizeof(SysNameEnt));
347     }
348
349     /* Get credentials */
350     AFS_GUNLOCK();
351     code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
352     AFS_GLOCK();
353     if (code)
354         goto done;
355
356     /* Now, set the credentials they gave us... */
357     for (i = 0; i < tcreds.CredInfos_len; i++) {
358         tcred = &tcreds.CredInfos_val[i];
359
360         /* Find the cell.  If it is unknown to us, punt this entry. */
361         tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
362         afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
363         if (!tcell) {
364             memset(tcred->ct.HandShakeKey, 0, 8);
365             memset(tcred->st.st_val, 0, tcred->st.st_len);
366             afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
367             continue;
368         }
369         cellnum = tcell->cellNum;
370         afs_PutCell(tcell, READ_LOCK);
371
372         /* Find the appropriate unixuser.  This might be the same as
373          * the one we were passed (au), but that's OK.
374          */
375         tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
376         if (!(tu->exporter)) {  /* Created new unixuser struct */
377             np->refCount++;             /* so it won't disappear */
378             tu->exporter = (struct afs_exporter *)np;
379         }
380
381         /* free any old secret token, and keep the new one */
382         if (tu->stp != NULL) {
383             afs_osi_Free(tu->stp, tu->stLen);
384         }
385         tu->stp = tcred->st.st_val;
386         tu->stLen = tcred->st.st_len;
387
388         /* copy the clear token */
389         memset(&tu->ct, 0, sizeof(tu->ct));
390         memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
391         memset(tcred->ct.HandShakeKey, 0, 8);
392         tu->ct.AuthHandle     = tcred->ct.AuthHandle;
393         tu->ct.ViceId         = tcred->ct.ViceId;
394         tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
395         tu->ct.EndTimestamp   = tcred->ct.EndTimestamp;
396
397         /* Set everything else, reset connections, and move on. */
398         tu->vid = tcred->vid;
399         tu->states |= UHasTokens;
400         tu->states &= ~UTokensBad;
401         afs_SetPrimary(tu, !!(tcred->states & UPrimary));
402         tu->tokenTime = osi_Time();
403         afs_ResetUserConns(tu);
404         afs_PutUser(tu, WRITE_LOCK);
405     }
406     afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
407
408 done:
409     AFS_GUNLOCK();
410     rx_DestroyConnection(tconn);
411     AFS_GLOCK();
412     au->states &= ~UNFSGetCreds;
413     afs_osi_Wakeup((void *)au);
414 }
415
416
417 /* It's called whenever a new unixuser structure is created for the remote
418  * user associated with the nfsclientpag structure, np */
419 void
420 afs_nfsclient_hold(register struct nfsclientpag *np)
421 {
422 #if defined(AFS_SGIMP_ENV)
423     osi_Assert(ISAFS_GLOCK());
424 #endif
425     AFS_STATCNT(afs_nfsclient_hold);
426     np->refCount++;
427 }
428
429
430 /* check if this exporter corresponds to the specified host */
431 int
432 afs_nfsclient_checkhost(register struct nfsclientpag *np, afs_uint32 host)
433 {
434     if (np->type != EXP_NFS)
435         return 0;
436     return np->host == host;
437 }
438
439
440 /* get the host for this exporter, or 0 if there is an error */
441 afs_uint32
442 afs_nfsclient_gethost(register struct nfsclientpag *np)
443 {
444     if (np->type != EXP_NFS)
445         return 0;
446     return np->host;
447 }
448
449
450 /* if inname is non-null, a new system name value is set for the remote
451  * user (inname contains the new sysname). In all cases, outname returns
452  * the current sysname value for this remote user */
453 int 
454 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname, 
455                       char ***outname, int *num, int allpags)
456 {
457     register struct nfsclientpag *tnp;
458     register afs_int32 i;
459     char *cp;
460     int count, t;
461 #if defined(AFS_SGIMP_ENV)
462     osi_Assert(ISAFS_GLOCK());
463 #endif
464     AFS_STATCNT(afs_nfsclient_sysname);
465     if (allpags > 0) {
466         /* update every client, not just the one making the request */
467         i = NHash(np->host);
468         ObtainWriteLock(&afs_xnfspag, 315);
469         for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
470             if (tnp != np && tnp->host == np->host)
471                 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
472         }
473         ReleaseWriteLock(&afs_xnfspag);
474     }
475     if (inname) {
476             for(count=0; count < np->sysnamecount;++count) {
477                 afs_osi_Free(np->sysname[count], MAXSYSNAME);
478                 np->sysname[count] = NULL;
479             }
480         for(count=0; count < *num;++count) {
481             np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
482         }
483         cp = inname;
484         for(count=0; count < *num;++count) {
485             t = strlen(cp);
486             memcpy(np->sysname[count], cp, t+1); /* include null */
487             cp += t+1;
488         }
489         np->sysnamecount = *num;
490     }
491     if (allpags >= 0) {
492         /* Don't touch our arguments when called recursively */
493         *outname = np->sysname;
494         *num = np->sysnamecount;
495         if (!np->sysname[0])
496             return ENODEV; /* XXX */
497     }
498     return 0;
499 }
500
501
502 /* Garbage collect routine for the nfs exporter. When pag is -1 then all
503  * entries are removed (used by the nfsclient_shutdown routine); else if
504  * it's non zero then only the entry with that pag is removed, else all
505  * "timedout" entries are removed. TimedOut entries are those who have no
506  * "unixuser" structures associated with them (i.e. unixusercnt == 0) and
507  * they haven't had any activity the last NFSCLIENTGC seconds */
508 void
509 afs_nfsclient_GC(register struct afs_exporter *exporter,
510                  register afs_int32 pag)
511 {
512     register struct nfsclientpag *np, **tnp, *nnp;
513     register afs_int32 i, delflag;
514         int count;
515
516 #if defined(AFS_SGIMP_ENV)
517     osi_Assert(ISAFS_GLOCK());
518 #endif
519     AFS_STATCNT(afs_nfsclient_GC);
520     ObtainWriteLock(&afs_xnfspag, 316);
521     for (i = 0; i < NNFSCLIENTS; i++) {
522         for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
523             nnp = np->next;
524             delflag = 0;
525             if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
526                 delflag = 1;
527             if ((pag == -1) || (!pag && delflag)
528                 || (pag && (np->refCount == 0) && (np->pag == pag))) {
529                 *tnp = np->next;
530                 for(count=0; count < np->sysnamecount;++count) {
531                         afs_osi_Free(np->sysname[count], MAXSYSNAME);
532                 }
533                 afs_osi_Free(np, sizeof(struct nfsclientpag));
534             } else {
535                 tnp = &np->next;
536             }
537         }
538     }
539     ReleaseWriteLock(&afs_xnfspag);
540 }
541
542
543 int
544 afs_nfsclient_stats(register struct afs_exporter *export)
545 {
546     /* Nothing much to do here yet since most important stats are collected 
547      * directly in the afs_exporter structure itself */
548     AFS_STATCNT(afs_nfsclient_stats);
549     return 0;
550 }
551
552 #ifdef AFS_AIX41_ENV
553 /* This is exposed so that vop_fid can test it, even if iauth is not
554  *  installed.
555  */
556 extern int afs_iauth_initd;
557 #endif
558
559 #ifdef AFS_AIX_IAUTH_ENV
560 char *afs_nfs_id = "AFSNFSTRANS";
561 /* afs_iauth_verify is the AFS authenticator for NFS.
562  *
563  * always returns 0.
564  */
565 int
566 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
567                  afs_ucred_t *credp, struct exportinfo *exp)
568 {
569     int code;
570     struct nfsclientpag *nfs_pag;
571     afs_int32 dummypag;
572     struct afs_exporter *outexporter = 0;
573
574
575     /* Still needs basic test to see if exporter is on. And need to check the
576      * whole no submounts bit.
577      */
578
579     if (id != (long)id)
580         return 0;               /* not us. */
581
582     /* Only care if it's AFS */
583     if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
584         return 0;
585     }
586
587     AFS_GLOCK();
588     code =
589         afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
590                                  &dummypag, &outexporter);
591     if (!code && outexporter)
592         EXP_RELE(outexporter);
593
594     if (code) {
595         /* ensure anonymous cred. */
596         afs_set_cr_uid(credp, (uid_t) -2;       /* anonymous */
597         afs_set_cr_ruid(credp, (uid_t) -2;
598     }
599
600     /* Mark this thread as an NFS translator thread. */
601     afs_set_cr_rgid(credp, NFSXLATOR_CRED);
602
603     AFS_GUNLOCK();
604     return 0;
605 }
606
607 /* afs_iauth_register - register the iauth verify routine. Returns 0 on success
608  * and -1 on failure. Can fail because DFS has already registered.
609  */
610 int
611 afs_iauth_register(void)
612 {
613     if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
614         return -1;
615     else {
616         afs_iauth_initd = 1;
617         return 0;
618     }
619 }
620
621 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown. 
622  */
623 void
624 afs_iauth_unregister(void)
625 {
626     if (afs_iauth_initd)
627         nfs_iauth_unregister((unsigned long)afs_nfs_id);
628     afs_iauth_initd = 0;
629 }
630 #endif /* AFS_AIX_IAUTH_ENV */
631
632
633
634 void
635 shutdown_nfsclnt(void)
636 {
637 #if defined(AFS_SGIMP_ENV)
638     osi_Assert(ISAFS_GLOCK());
639 #endif
640     AFS_STATCNT(afs_nfsclient_shutdown);
641 #ifdef AFS_AIX_IAUTH_ENV
642     afs_iauth_register();
643 #endif
644     afs_nfsclient_GC(afs_nfsexporter, -1);
645     init_nfsexporter = 0;
646 }
647 #endif /* AFS_NONFSTRANS */