linux-afs-translator-xen-20060731
[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 RCSID
14     ("$Header$");
15
16 #if !defined(AFS_NONFSTRANS) || defined(AFS_AIX_IAUTH_ENV)
17 #include "afs/sysincludes.h"    /* Standard vendor system headers */
18 #include "afsincludes.h"        /* Afs-based standard headers */
19 #include "afs/afs_stats.h"      /* statistics */
20 #include "afs/nfsclient.h"
21 #include "rx/rx_globals.h"
22 #include "afs/pagcb.h"
23
24 void afs_nfsclient_hold(), afs_PutNfsClientPag(), afs_nfsclient_GC();
25 static void afs_nfsclient_getcreds();
26 int afs_nfsclient_sysname(), afs_nfsclient_stats(), afs_nfsclient_checkhost();
27 afs_int32 afs_nfsclient_gethost();
28 #ifdef AFS_AIX_IAUTH_ENV
29 int afs_allnfsreqs, afs_nfscalls;
30 #endif
31
32 /* routines exported to the "AFS exporter" layer */
33 struct exporterops nfs_exportops = {
34     afs_nfsclient_reqhandler,
35     afs_nfsclient_hold,
36     afs_PutNfsClientPag,        /* Used to be afs_nfsclient_rele */
37     afs_nfsclient_sysname,
38     afs_nfsclient_GC,
39     afs_nfsclient_stats,
40     afs_nfsclient_checkhost,
41     afs_nfsclient_gethost
42 };
43
44
45 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
46 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
47 extern struct afs_exporter *afs_nfsexporter;
48
49 /* Creates an nfsclientpag structure for the (uid, host) pair if one doesn't exist. RefCount is incremented and it's time stamped. */
50 static struct nfsclientpag *
51 afs_GetNfsClientPag(uid, host)
52      register afs_int32 uid, host;
53 {
54     register struct nfsclientpag *np;
55     register afs_int32 i, now;
56
57 #if defined(AFS_SGIMP_ENV)
58     osi_Assert(ISAFS_GLOCK());
59 #endif
60     AFS_STATCNT(afs_GetNfsClientPag);
61     i = NHash(host);
62     now = osi_Time();
63     MObtainWriteLock(&afs_xnfspag, 314);
64     for (np = afs_nfspags[i]; np; np = np->next) {
65         if (np->uid == uid && np->host == host) {
66             np->refCount++;
67             np->lastcall = now;
68             MReleaseWriteLock(&afs_xnfspag);
69             return np;
70         }
71     }
72     /* next try looking for NOPAG dude, if we didn't find an exact match */
73     for (np = afs_nfspags[i]; np; np = np->next) {
74         if (np->uid == NOPAG && np->host == host) {
75             np->refCount++;
76             np->lastcall = now;
77             MReleaseWriteLock(&afs_xnfspag);
78             return np;
79         }
80     }
81     np = (struct nfsclientpag *)afs_osi_Alloc(sizeof(struct nfsclientpag));
82     memset((char *)np, 0, sizeof(struct nfsclientpag));
83     /* Copy the necessary afs_exporter fields */
84     memcpy((char *)np, (char *)afs_nfsexporter, sizeof(struct afs_exporter));
85     np->next = afs_nfspags[i];
86     afs_nfspags[i] = np;
87     np->uid = uid;
88     np->host = host;
89     np->refCount = 1;
90     np->lastcall = now;
91     MReleaseWriteLock(&afs_xnfspag);
92     return np;
93 }
94
95
96 /* Decrement refCount; must always match a previous afs_FindNfsClientPag/afs_GetNfsClientPag call .
97 It's also called whenever a unixuser structure belonging to the remote user associated with the nfsclientpag structure, np, is garbage collected. */
98 void
99 afs_PutNfsClientPag(np)
100      register struct nfsclientpag *np;
101 {
102 #if defined(AFS_SGIMP_ENV)
103     osi_Assert(ISAFS_GLOCK());
104 #endif
105     AFS_STATCNT(afs_PutNfsClientPag);
106     --np->refCount;
107 }
108
109
110 /* Return the nfsclientpag structure associated with the (uid, host) or {pag, host} pair, if pag is nonzero. RefCount is incremented and it's time stamped. */
111 static struct nfsclientpag *
112 afs_FindNfsClientPag(uid, host, pag)
113      register afs_int32 uid, host, pag;
114 {
115     register struct nfsclientpag *np;
116     register 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     MObtainWriteLock(&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                 MReleaseWriteLock(&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                 MReleaseWriteLock(&afs_xnfspag);
141                 return np;
142             }
143         }
144     }
145     MReleaseWriteLock(&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                          struct AFS_UCRED **cred,
179                          afs_int32 host, afs_int32 *pagparam,
180                          struct afs_exporter **outexporter)
181 {
182     register struct nfsclientpag *np, *tnp;
183     extern struct unixuser *afs_FindUser(), *afs_GetUser();
184     register 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'. Set/Reset via a pioctl call (fs exportafs). Note that this is on top of the /etc/exports nfs requirement (i.e. /afs must be exported to all or whomever there too!)
195          */
196         afs_nfsexporter->exp_stats.rejectedcalls++;
197         return EINVAL;
198     }
199 /*    ObtainWriteLock(&afs_xnfsreq); */
200     pag = PagInCred(*cred);
201     uid = (*cred)->cr_uid;
202     if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
203         uid = pag;
204     } else if (pag != NOPAG) {
205         /* Do some minimal pag verification */
206         if (pag > getpag()) {
207             pag = NOPAG;        /* treat it as not paged since couldn't be good  */
208         } else {
209             if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
210                 if (!au->exporter) {
211                     pag = NOPAG;
212                     afs_PutUser(au, READ_LOCK);
213                     au = NULL;
214                 }
215             } else
216                 pag = NOPAG;    /*  No unixuser struct so pag not trusted  */
217         }
218     }
219     np = afs_FindNfsClientPag(uid, host, 0);
220     afs_Trace4(afs_iclSetp, CM_TRACE_NFSREQH, ICL_TYPE_INT32, pag,
221                ICL_TYPE_LONG, (*cred)->cr_uid, ICL_TYPE_INT32, host,
222                ICL_TYPE_POINTER, np);
223     /* If remote-pags are enabled, we are no longer interested in what PAG
224      * they claimed, and from here on we should behave as if they claimed
225      * none at all, which is to say we use the (local) pag named in the
226      * nfsclientpag structure (if any).  This is deferred until here so
227      * that we can log the PAG they claimed.
228      */
229     if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
230         pag = NOPAG;
231     if (!np) {
232         /* Even if there is a "good" pag coming in we don't accept it if no nfsclientpag struct exists for the user since that would mean that the translator rebooted and therefore we ignore all older pag values */
233 #ifdef  AFS_OSF_ENV
234         if (code = setpag(u.u_procp, cred, -1, &pag, 0)) {      /* XXX u.u_procp is a no-op XXX */
235 #else
236         if ((code = setpag(cred, -1, &pag, 0))) {
237 #endif
238             if (au)
239                 afs_PutUser(au, READ_LOCK);
240 /*          ReleaseWriteLock(&afs_xnfsreq);             */
241 #if defined(KERNEL_HAVE_UERROR)
242             setuerror(code);
243 #endif
244             return (code);
245         }
246         np = afs_GetNfsClientPag(uid, host);
247         np->pag = pag;
248         np->client_uid = (*cred)->cr_uid;
249     } else {
250         if (pag == NOPAG) {
251 #ifdef  AFS_OSF_ENV
252             if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) {     /* XXX u.u_procp is a no-op XXX */
253 #else
254             if ((code = setpag(cred, np->pag, &pag, 0))) {
255 #endif
256                 afs_PutNfsClientPag(np);
257 /*              ReleaseWriteLock(&afs_xnfsreq); */
258 #if defined(KERNEL_HAVE_UERROR)
259                 setuerror(code);
260 #endif
261                 return (code);
262             }
263         } else if (au->exporter
264                    && ((struct afs_exporter *)np != au->exporter)) {
265             tnp = (struct nfsclientpag *)au->exporter;
266             if (tnp->uid && (tnp->uid != (afs_int32) - 2)) {    /* allow "root" initiators */
267                 /* Pag doesn't belong to caller; treat it as an unpaged call too */
268 #ifdef  AFS_OSF_ENV
269                 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
270 #else
271                 if ((code = setpag(cred, np->pag, &pag, 0))) {
272 #endif
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 #ifdef  AFS_OSF_ENV
300     (*cred)->cr_ruid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
301 #else
302     (*cred)->cr_rgid = NFSXLATOR_CRED;  /* Identify it as nfs xlator call */
303 #endif
304 /*    ReleaseWriteLock(&afs_xnfsreq);   */
305     return 0;
306 }
307
308 void
309 afs_nfsclient_getcreds(au)
310     struct unixuser *au;
311 {
312     struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
313     struct rx_securityClass *csec;
314     struct rx_connection *tconn;
315     SysNameList tsysnames;
316     CredInfos tcreds;
317     CredInfo *tcred;
318     struct unixuser *tu;
319     struct cell *tcell;
320     int code, i, cellnum;
321
322     au->states |= UNFSGetCreds;
323     memset(&tcreds, 0, sizeof(tcreds));
324     memset(&tsysnames, 0, sizeof(tsysnames));
325
326     /* Get a connection */
327     /* This sucks a little.  We should cache the connections or something.
328      * But at this point I don't yet think it's worth the effort.
329      */
330     csec = rxnull_NewClientSecurityObject();
331     AFS_GUNLOCK();
332     tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
333     AFS_GLOCK();
334
335     /* Get the sysname, if needed */
336     if (!np->sysnamecount) {
337         AFS_GUNLOCK();
338         code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
339         AFS_GLOCK();
340         if (code ||
341             tsysnames.SysNameList_len <= 0 ||
342             tsysnames.SysNameList_len > MAXNUMSYSNAMES)
343             goto done;
344     
345         for(i = 0; i < np->sysnamecount; i++)
346             afs_osi_Free(np->sysname[i], MAXSYSNAME);
347
348         np->sysnamecount = tsysnames.SysNameList_len;
349         for(i = 0; i < np->sysnamecount; i++)
350             np->sysname[i] = tsysnames.SysNameList_val[i].sysname;
351         afs_osi_Free(tsysnames.SysNameList_val,
352                      tsysnames.SysNameList_len * sizeof(SysNameEnt));
353     }
354
355     /* Get credentials */
356     AFS_GUNLOCK();
357     code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
358     AFS_GLOCK();
359     if (code)
360         goto done;
361
362     /* Now, set the credentials they gave us... */
363     for (i = 0; i < tcreds.CredInfos_len; i++) {
364         tcred = &tcreds.CredInfos_val[i];
365
366         /* Find the cell.  If it is unknown to us, punt this entry. */
367         tcell = afs_GetCellByName(tcred->cellname, READ_LOCK);
368         afs_osi_Free(tcred->cellname, strlen(tcred->cellname) + 1);
369         if (!tcell) {
370             memset(tcred->ct.HandShakeKey, 0, 8);
371             memset(tcred->st.st_val, 0, tcred->st.st_len);
372             afs_osi_Free(tcred->st.st_val, tcred->st.st_len);
373             continue;
374         }
375         cellnum = tcell->cellNum;
376         afs_PutCell(tcell, READ_LOCK);
377
378         /* Find the appropriate unixuser.  This might be the same as
379          * the one we were passed (au), but that's OK.
380          */
381         tu = afs_GetUser(np->pag, cellnum, WRITE_LOCK);
382         if (!(tu->exporter)) {  /* Created new unixuser struct */
383             np->refCount++;             /* so it won't disappear */
384             tu->exporter = (struct afs_exporter *)np;
385         }
386
387         /* free any old secret token, and keep the new one */
388         if (tu->stp != NULL) {
389             afs_osi_Free(tu->stp, tu->stLen);
390         }
391         tu->stp = tcred->st.st_val;
392         tu->stLen = tcred->st.st_len;
393
394         /* copy the clear token */
395         memset(&tu->ct, 0, sizeof(tu->ct));
396         memcpy(tu->ct.HandShakeKey, tcred->ct.HandShakeKey, 8);
397         memset(tcred->ct.HandShakeKey, 0, 8);
398         tu->ct.AuthHandle     = tcred->ct.AuthHandle;
399         tu->ct.ViceId         = tcred->ct.ViceId;
400         tu->ct.BeginTimestamp = tcred->ct.BeginTimestamp;
401         tu->ct.EndTimestamp   = tcred->ct.EndTimestamp;
402
403         /* Set everything else, reset connections, and move on. */
404         tu->vid = tcred->vid;
405         tu->states |= UHasTokens;
406         tu->states &= ~UTokensBad;
407         afs_SetPrimary(tu, !!(tcred->states & UPrimary));
408         tu->tokenTime = osi_Time();
409         afs_ResetUserConns(tu);
410         afs_PutUser(tu, WRITE_LOCK);
411     }
412     afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
413
414 done:
415     AFS_GUNLOCK();
416     rx_DestroyConnection(tconn);
417     AFS_GLOCK();
418     au->states &= ~UNFSGetCreds;
419     afs_osi_Wakeup((void *)au);
420 }
421
422
423 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
424 void
425 afs_nfsclient_hold(np)
426      register struct nfsclientpag *np;
427 {
428 #if defined(AFS_SGIMP_ENV)
429     osi_Assert(ISAFS_GLOCK());
430 #endif
431     AFS_STATCNT(afs_nfsclient_hold);
432     np->refCount++;
433 }
434
435
436 /* check if this exporter corresponds to the specified host */
437 int
438 afs_nfsclient_checkhost(np, host)
439     register struct nfsclientpag *np;
440 {
441     if (np->type != EXP_NFS)
442         return 0;
443     return np->host == host;
444 }
445
446
447 /* get the host for this exporter, or 0 if there is an error */
448 afs_int32
449 afs_nfsclient_gethost(np)
450     register struct nfsclientpag *np;
451 {
452     if (np->type != EXP_NFS)
453         return 0;
454     return np->host;
455 }
456
457
458 /* 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 */
459 int 
460 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname, 
461                       char ***outname, int *num, int allpags)
462 {
463     register struct nfsclientpag *tnp;
464     register afs_int32 i;
465     char *cp;
466     int count, t;
467 #if defined(AFS_SGIMP_ENV)
468     osi_Assert(ISAFS_GLOCK());
469 #endif
470     AFS_STATCNT(afs_nfsclient_sysname);
471     if (allpags > 0) {
472         /* update every client, not just the one making the request */
473         i = NHash(np->host);
474         MObtainWriteLock(&afs_xnfspag, 315);
475         for (tnp = afs_nfspags[i]; tnp; tnp = tnp->next) {
476             if (tnp != np && tnp->host == np->host)
477                 afs_nfsclient_sysname(tnp, inname, outname, num, -1);
478         }
479         MReleaseWriteLock(&afs_xnfspag);
480     }
481     if (inname) {
482         if (np->sysname) {
483             for(count=0; count < np->sysnamecount;++count) {
484                 afs_osi_Free(np->sysname[count], MAXSYSNAME);
485             }
486         }
487         for(count=0; count < *num;++count) {
488             np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
489         }
490         cp = inname;
491         for(count=0; count < *num;++count) {
492             t = strlen(cp);
493             memcpy(np->sysname[count], cp, t+1); /* include null */
494             cp += t+1;
495         }
496         np->sysnamecount = *num;
497     } else if (!np->sysnamecount) {
498         return ENODEV;      /* XXX */
499     }
500     if (allpags >= 0) {
501         /* Don't touch our arguments when called recursively */
502         *outname = np->sysname;
503         *num = np->sysnamecount;
504     }
505     return 0;
506 }
507
508
509 /* 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 */
510 void
511 afs_nfsclient_GC(exporter, pag)
512      register struct afs_exporter *exporter;
513      register afs_int32 pag;
514 {
515     register struct nfsclientpag *np, **tnp, *nnp;
516     register afs_int32 i, delflag;
517
518 #if defined(AFS_SGIMP_ENV)
519     osi_Assert(ISAFS_GLOCK());
520 #endif
521     AFS_STATCNT(afs_nfsclient_GC);
522     MObtainWriteLock(&afs_xnfspag, 316);
523     for (i = 0; i < NNFSCLIENTS; i++) {
524         for (tnp = &afs_nfspags[i], np = *tnp; np; np = nnp) {
525             nnp = np->next;
526             delflag = 0;
527             if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
528                 delflag = 1;
529             if ((pag == -1) || (!pag && delflag)
530                 || (pag && (np->refCount == 0) && (np->pag == pag))) {
531                 *tnp = np->next;
532                 if (np->sysname)
533                     afs_osi_Free(np->sysname, MAXSYSNAME);
534                 afs_osi_Free(np, sizeof(struct nfsclientpag));
535             } else {
536                 tnp = &np->next;
537             }
538         }
539     }
540     MReleaseWriteLock(&afs_xnfspag);
541 }
542
543
544 int
545 afs_nfsclient_stats(export)
546      register struct afs_exporter *export;
547 {
548     /* Nothing much to do here yet since most important stats are collected directly in the afs_exporter structure itself */
549     AFS_STATCNT(afs_nfsclient_stats);
550     return 0;
551 }
552
553 #ifdef AFS_AIX41_ENV
554 /* This is exposed so that vop_fid can test it, even if iauth is not
555  *  installed.
556  */
557 extern int afs_iauth_initd;
558 #endif
559
560 #ifdef AFS_AIX_IAUTH_ENV
561 char *afs_nfs_id = "AFSNFSTRANS";
562 /* afs_iauth_verify is the AFS authenticator for NFS.
563  *
564  * always returns 0.
565  */
566 int
567 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
568                  struct AFS_UCRED *credp, struct exportinfo *exp)
569 {
570     int code;
571     struct nfsclientpag *nfs_pag;
572     afs_int32 dummypag;
573     struct afs_exporter *outexporter = 0;
574
575
576     /* Still needs basic test to see if exporter is on. And need to check the
577      * whole no submounts bit.
578      */
579
580     if (id != (long)id)
581         return 0;               /* not us. */
582
583     /* Only care if it's AFS */
584     if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
585         return 0;
586     }
587
588     AFS_GLOCK();
589     code =
590         afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
591                                  &dummypag, &outexporter);
592     if (!code && outexporter)
593         EXP_RELE(outexporter);
594
595     if (code) {
596         /* ensure anonymous cred. */
597         credp->cr_uid = credp->cr_ruid = (uid_t) - 2;   /* anonymous */
598     }
599
600     /* Mark this thread as an NFS translator thread. */
601     credp->cr_rgid = 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()
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()
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()
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 */