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