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