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