2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include "afs/param.h"
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"
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;
32 /* routines exported to the "AFS exporter" layer */
33 struct exporterops nfs_exportops = {
34 afs_nfsclient_reqhandler,
36 afs_PutNfsClientPag, /* Used to be afs_nfsclient_rele */
37 afs_nfsclient_sysname,
40 afs_nfsclient_checkhost,
45 struct nfsclientpag *afs_nfspags[NNFSCLIENTS];
46 afs_lock_t afs_xnfspag /*, afs_xnfsreq */ ;
47 extern struct afs_exporter *afs_nfsexporter;
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;
54 register struct nfsclientpag *np;
55 register afs_int32 i, now;
57 #if defined(AFS_SGIMP_ENV)
58 osi_Assert(ISAFS_GLOCK());
60 AFS_STATCNT(afs_GetNfsClientPag);
63 MObtainWriteLock(&afs_xnfspag, 314);
64 for (np = afs_nfspags[i]; np; np = np->next) {
65 if (np->uid == uid && np->host == host) {
68 MReleaseWriteLock(&afs_xnfspag);
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) {
77 MReleaseWriteLock(&afs_xnfspag);
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];
91 MReleaseWriteLock(&afs_xnfspag);
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. */
99 afs_PutNfsClientPag(np)
100 register struct nfsclientpag *np;
102 #if defined(AFS_SGIMP_ENV)
103 osi_Assert(ISAFS_GLOCK());
105 AFS_STATCNT(afs_PutNfsClientPag);
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;
115 register struct nfsclientpag *np;
116 register afs_int32 i;
118 #if defined(AFS_SGIMP_ENV)
119 osi_Assert(ISAFS_GLOCK());
121 AFS_STATCNT(afs_FindNfsClientPag);
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))) {
128 np->lastcall = osi_Time();
129 MReleaseWriteLock(&afs_xnfspag);
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) {
139 np->lastcall = osi_Time();
140 MReleaseWriteLock(&afs_xnfspag);
145 MReleaseWriteLock(&afs_xnfspag);
150 /* routine to initialize the exporter, made global so we can call it
153 struct afs_exporter *afs_nfsexported = 0;
154 static afs_int32 init_nfsexporter = 0;
157 afs_nfsclient_init(void)
159 #if defined(AFS_SGIMP_ENV)
160 osi_Assert(ISAFS_GLOCK());
162 if (!init_nfsexporter) {
163 extern struct afs_exporter *exporter_add();
165 init_nfsexporter = 1;
166 LOCK_INIT(&afs_xnfspag, "afs_xnfspag");
168 exporter_add(0, &nfs_exportops, EXP_EXPORTED, EXP_NFS, NULL);
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).
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)
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;
188 AFS_STATCNT(afs_nfsclient_reqhandler);
189 if (!afs_nfsexporter)
190 afs_nfsexporter = afs_nfsexported;
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!)
196 afs_nfsexporter->exp_stats.rejectedcalls++;
199 /* ObtainWriteLock(&afs_xnfsreq); */
200 pag = PagInCred(*cred);
201 uid = (*cred)->cr_uid;
202 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS) && pag != NOPAG) {
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 */
209 if ((au = afs_FindUser(pag, -1, READ_LOCK))) {
212 afs_PutUser(au, READ_LOCK);
216 pag = NOPAG; /* No unixuser struct so pag not trusted */
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.
229 if ((afs_nfsexporter->exp_states & EXP_CLIPAGS))
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 */
234 if (code = setpag(u.u_procp, cred, -1, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
236 if ((code = setpag(cred, -1, &pag, 0))) {
239 afs_PutUser(au, READ_LOCK);
240 /* ReleaseWriteLock(&afs_xnfsreq); */
241 #if defined(KERNEL_HAVE_UERROR)
246 np = afs_GetNfsClientPag(uid, host);
248 np->client_uid = (*cred)->cr_uid;
252 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
254 if ((code = setpag(cred, np->pag, &pag, 0))) {
256 afs_PutNfsClientPag(np);
257 /* ReleaseWriteLock(&afs_xnfsreq); */
258 #if defined(KERNEL_HAVE_UERROR)
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 */
269 if (code = setpag(u.u_procp, cred, np->pag, &pag, 0)) { /* XXX u.u_procp is a no-op XXX */
271 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)
281 afs_nfsexporter->exp_stats.invalidpag++;
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);
297 *outexporter = (struct afs_exporter *)np;
298 afs_PutUser(au, WRITE_LOCK);
300 (*cred)->cr_ruid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
302 (*cred)->cr_rgid = NFSXLATOR_CRED; /* Identify it as nfs xlator call */
304 /* ReleaseWriteLock(&afs_xnfsreq); */
309 afs_nfsclient_getcreds(au)
312 struct nfsclientpag *np = (struct nfsclientpag *)(au->exporter);
313 struct rx_securityClass *csec;
314 struct rx_connection *tconn;
315 SysNameList tsysnames;
320 int code, i, cellnum;
322 au->states |= UNFSGetCreds;
323 memset(&tcreds, 0, sizeof(tcreds));
324 memset(&tsysnames, 0, sizeof(tsysnames));
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.
330 csec = rxnull_NewClientSecurityObject();
332 tconn = rx_NewConnection(np->host, htons(7001), PAGCB_SERVICEID, csec, 0);
335 /* Get the sysname, if needed */
336 if (!np->sysnamecount) {
338 code = PAGCB_GetSysName(tconn, np->uid, &tsysnames);
341 tsysnames.SysNameList_len <= 0 ||
342 tsysnames.SysNameList_len > MAXNUMSYSNAMES)
345 for(i = 0; i < np->sysnamecount; i++)
346 afs_osi_Free(np->sysname[i], MAXSYSNAME);
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));
355 /* Get credentials */
357 code = PAGCB_GetCreds(tconn, np->uid, &tcreds);
362 /* Now, set the credentials they gave us... */
363 for (i = 0; i < tcreds.CredInfos_len; i++) {
364 tcred = &tcreds.CredInfos_val[i];
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);
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);
375 cellnum = tcell->cellNum;
376 afs_PutCell(tcell, READ_LOCK);
378 /* Find the appropriate unixuser. This might be the same as
379 * the one we were passed (au), but that's OK.
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;
387 /* free any old secret token, and keep the new one */
388 if (tu->stp != NULL) {
389 afs_osi_Free(tu->stp, tu->stLen);
391 tu->stp = tcred->st.st_val;
392 tu->stLen = tcred->st.st_len;
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;
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);
412 afs_osi_Free(tcreds.CredInfos_val, tcreds.CredInfos_len * sizeof(CredInfo));
416 rx_DestroyConnection(tconn);
418 au->states &= ~UNFSGetCreds;
419 afs_osi_Wakeup((void *)au);
423 /* It's called whenever a new unixuser structure is created for the remote user associated with the nfsclientpag structure, np */
425 afs_nfsclient_hold(np)
426 register struct nfsclientpag *np;
428 #if defined(AFS_SGIMP_ENV)
429 osi_Assert(ISAFS_GLOCK());
431 AFS_STATCNT(afs_nfsclient_hold);
436 /* check if this exporter corresponds to the specified host */
438 afs_nfsclient_checkhost(np, host)
439 register struct nfsclientpag *np;
441 if (np->type != EXP_NFS)
443 return np->host == host;
447 /* get the host for this exporter, or 0 if there is an error */
449 afs_nfsclient_gethost(np)
450 register struct nfsclientpag *np;
452 if (np->type != EXP_NFS)
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 */
460 afs_nfsclient_sysname(register struct nfsclientpag *np, char *inname,
461 char ***outname, int *num, int allpags)
463 register struct nfsclientpag *tnp;
464 register afs_int32 i;
467 #if defined(AFS_SGIMP_ENV)
468 osi_Assert(ISAFS_GLOCK());
470 AFS_STATCNT(afs_nfsclient_sysname);
472 /* update every client, not just the one making the request */
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);
479 MReleaseWriteLock(&afs_xnfspag);
483 for(count=0; count < np->sysnamecount;++count) {
484 afs_osi_Free(np->sysname[count], MAXSYSNAME);
487 for(count=0; count < *num;++count) {
488 np->sysname[count]= afs_osi_Alloc(MAXSYSNAME);
491 for(count=0; count < *num;++count) {
493 memcpy(np->sysname[count], cp, t+1); /* include null */
496 np->sysnamecount = *num;
497 } else if (!np->sysnamecount) {
498 return ENODEV; /* XXX */
501 /* Don't touch our arguments when called recursively */
502 *outname = np->sysname;
503 *num = np->sysnamecount;
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 */
511 afs_nfsclient_GC(exporter, pag)
512 register struct afs_exporter *exporter;
513 register afs_int32 pag;
515 register struct nfsclientpag *np, **tnp, *nnp;
516 register afs_int32 i, delflag;
518 #if defined(AFS_SGIMP_ENV)
519 osi_Assert(ISAFS_GLOCK());
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) {
527 if (np->refCount == 0 && np->lastcall < osi_Time() - NFSCLIENTGC)
529 if ((pag == -1) || (!pag && delflag)
530 || (pag && (np->refCount == 0) && (np->pag == pag))) {
533 afs_osi_Free(np->sysname, MAXSYSNAME);
534 afs_osi_Free(np, sizeof(struct nfsclientpag));
540 MReleaseWriteLock(&afs_xnfspag);
545 afs_nfsclient_stats(export)
546 register struct afs_exporter *export;
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);
554 /* This is exposed so that vop_fid can test it, even if iauth is not
557 extern int afs_iauth_initd;
560 #ifdef AFS_AIX_IAUTH_ENV
561 char *afs_nfs_id = "AFSNFSTRANS";
562 /* afs_iauth_verify is the AFS authenticator for NFS.
567 afs_iauth_verify(long id, fsid_t * fsidp, long host, int uid,
568 struct AFS_UCRED *credp, struct exportinfo *exp)
571 struct nfsclientpag *nfs_pag;
573 struct afs_exporter *outexporter = 0;
576 /* Still needs basic test to see if exporter is on. And need to check the
577 * whole no submounts bit.
581 return 0; /* not us. */
583 /* Only care if it's AFS */
584 if ((fsidp->val[0] != AFS_VFSMAGIC) || (fsidp->val[1] != AFS_VFSFSID)) {
590 afs_nfsclient_reqhandler((struct afs_exporter *)0, &credp, host,
591 &dummypag, &outexporter);
592 if (!code && outexporter)
593 EXP_RELE(outexporter);
596 /* ensure anonymous cred. */
597 credp->cr_uid = credp->cr_ruid = (uid_t) - 2; /* anonymous */
600 /* Mark this thread as an NFS translator thread. */
601 credp->cr_rgid = NFSXLATOR_CRED;
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.
613 if (nfs_iauth_register((unsigned long)afs_nfs_id, afs_iauth_verify))
621 /* afs_iauth_unregister - unregister the iauth verify routine. Called on shutdown.
624 afs_iauth_unregister()
627 nfs_iauth_unregister((unsigned long)afs_nfs_id);
630 #endif /* AFS_AIX_IAUTH_ENV */
637 #if defined(AFS_SGIMP_ENV)
638 osi_Assert(ISAFS_GLOCK());
640 AFS_STATCNT(afs_nfsclient_shutdown);
641 #ifdef AFS_AIX_IAUTH_ENV
642 afs_iauth_register();
644 afs_nfsclient_GC(afs_nfsexporter, -1);
645 init_nfsexporter = 0;
647 #endif /* AFS_NONFSTRANS */