diff options
author | rmesta <none@none> | 2006-06-07 09:06:06 -0700 |
---|---|---|
committer | rmesta <none@none> | 2006-06-07 09:06:06 -0700 |
commit | 1cc553493b17fa6a6770261bbfeb258f354ebf48 (patch) | |
tree | ee396fead89a1fa538377353f6aad6cd9fba5ae8 /usr/src/uts/common/fs/nfs/nfs_auth.c | |
parent | 9dc0df1bac950d6e491f9a7c7e4888f2b301cb15 (diff) | |
download | illumos-joyent-1cc553493b17fa6a6770261bbfeb258f354ebf48.tar.gz |
6417056 nfssrv should use doors/xdr as its IPC mech to mountd(1m)
--HG--
rename : usr/src/uts/common/rpcsvc/nfsauth_prot.x => deleted_files/usr/src/uts/common/rpcsvc/nfsauth_prot.x
Diffstat (limited to 'usr/src/uts/common/fs/nfs/nfs_auth.c')
-rw-r--r-- | usr/src/uts/common/fs/nfs/nfs_auth.c | 366 |
1 files changed, 261 insertions, 105 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs_auth.c b/usr/src/uts/common/fs/nfs/nfs_auth.c index e4bf46d8e2..46157945f8 100644 --- a/usr/src/uts/common/fs/nfs/nfs_auth.c +++ b/usr/src/uts/common/fs/nfs/nfs_auth.c @@ -2,9 +2,8 @@ * CDDL HEADER START * * The contents of this file are subject to the terms of the - * Common Development and Distribution License, Version 1.0 only - * (the "License"). You may not use this file except in compliance - * with the License. + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. @@ -20,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -37,6 +36,8 @@ #include <sys/pathname.h> #include <sys/utsname.h> #include <sys/debug.h> +#include <sys/door.h> +#include <sys/sdt.h> #include <rpc/types.h> #include <rpc/auth.h> @@ -45,7 +46,7 @@ #include <nfs/nfs.h> #include <nfs/export.h> #include <nfs/nfs_clnt.h> -#include <rpcsvc/nfsauth_prot.h> +#include <nfs/auth.h> #define EQADDR(a1, a2) \ (bcmp((char *)(a1)->buf, (char *)(a2)->buf, (a1)->len) == 0 && \ @@ -68,45 +69,31 @@ int nfsauth_cache_reclaim; */ static int nfsauth_timeout = 20; +/* + * mountd is a server-side only daemon. This will need to be + * revisited if the NFS server is ever made zones-aware. + */ +kmutex_t mountd_lock; +door_handle_t mountd_dh; + void -nfsauth_init(void) +mountd_args(uint_t did) { - vnode_t *kvp; - int error; - char addrbuf[SYS_NMLN+16]; + mutex_enter(&mountd_lock); + if (mountd_dh) + door_ki_rele(mountd_dh); + mountd_dh = door_ki_lookup(did); + mutex_exit(&mountd_lock); +} +void +nfsauth_init(void) +{ /* - * Setup netconfig. - * Assume a connectionless loopback transport. + * mountd can be restarted by smf(5). We need to make sure + * the updated door handle will safely make it to mountd_dh */ - if ((error = lookupname("/dev/ticotsord", UIO_SYSSPACE, FOLLOW, - NULLVPP, &kvp)) != 0) { - cmn_err(CE_CONT, "nfsauth: lookupname: %d\n", error); - return; - } - - auth_knconf.knc_rdev = kvp->v_rdev; - auth_knconf.knc_protofmly = NC_LOOPBACK; - auth_knconf.knc_semantics = NC_TPI_COTS_ORD; - VN_RELE(kvp); - - (void) strcpy(addrbuf, utsname.nodename); - (void) strcat(addrbuf, ".nfsauth"); - - svp.sv_knconf = &auth_knconf; - svp.sv_addr.buf = kmem_alloc(strlen(addrbuf)+1, KM_SLEEP); - (void) strcpy(svp.sv_addr.buf, addrbuf); - svp.sv_addr.len = (uint_t)strlen(addrbuf); - svp.sv_addr.maxlen = svp.sv_addr.len; - svp.sv_secdata = kmem_alloc(sizeof (struct sec_data), KM_SLEEP); - svp.sv_secdata->rpcflavor = AUTH_LOOPBACK; - svp.sv_secdata->data = NULL; - - ci.cl_prog = NFSAUTH_PROG; - ci.cl_vers = NFSAUTH_VERS; - ci.cl_readsize = 0; - ci.cl_retrans = 1; - ci.cl_flags = 0x0; + mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL); /* * Allocate nfsauth cache handle @@ -129,12 +116,6 @@ nfsauth_fini(void) kmem_cache_destroy(exi_cache_handle); } -static int -nfsauth_clget(CLIENT **newcl, struct chtab **chp) -{ - return (clget(&ci, &svp, CRED(), newcl, chp)); -} - /* * Convert the address in a netbuf to * a hash index for the auth_cache table. @@ -204,6 +185,22 @@ nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req) return (access); } +static void +sys_log(const char *msg) +{ + static time_t tstamp = 0; + time_t now; + + /* + * msg is shown (at most) once per minute + */ + now = gethrestime_sec(); + if ((tstamp + 60) < now) { + tstamp = now; + cmn_err(CE_WARN, msg); + } +} + /* * Get the access information from the cache or callup to the mountd * to get and cache the access information in the kernel. @@ -211,17 +208,25 @@ nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req) int nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor) { - struct netbuf addr, *claddr; - struct auth_cache **head, *ap; - CLIENT *clnt; - struct chtab *ch; - struct auth_req request; - struct auth_res result; - enum clnt_stat rpcstat; - int access; - struct timeval timout; - static time_t exi_msg = 0; - time_t now; + struct netbuf addr; + struct netbuf *claddr; + struct auth_cache **head; + struct auth_cache *ap; + int access; + varg_t varg = {0}; + nfsauth_res_t res = {0}; + XDR xdrs_a; + XDR xdrs_r; + size_t absz; + caddr_t abuf; + size_t rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2); + char result[BYTES_PER_XDR_UNIT * 2] = {0}; + caddr_t rbuf = (caddr_t)&result; + int last = 0; + door_arg_t da; + door_info_t di; + door_handle_t dh; + uint_t ntries = 0; /* * Now check whether this client already @@ -235,7 +240,7 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor) claddr = svc_getrpccaller(req->rq_xprt); addr = *claddr; - addr.buf = mem_alloc(addr.len); + addr.buf = kmem_alloc(addr.len, KM_SLEEP); bcopy(claddr->buf, addr.buf, claddr->len); addrmask(&addr, svc_getaddrmask(req->rq_xprt)); head = &exi->exi_cache[hash(&addr)]; @@ -265,62 +270,213 @@ nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor) * so we need to call the nfsauth service in the * mount daemon. */ - - if (nfsauth_clget(&clnt, &ch)) { +retry: + mutex_enter(&mountd_lock); + dh = mountd_dh; + if (dh) + door_ki_hold(dh); + mutex_exit(&mountd_lock); + + if (dh == NULL) { + /* + * The rendezvous point has not been established yet ! + * This could mean that either mountd(1m) has not yet + * been started or that _this_ routine nuked the door + * handle after receiving an EINTR for a REVOKED door. + * + * Returning NFSAUTH_DROP will cause the NFS client + * to retransmit the request, so let's try to be more + * rescillient and attempt for ntries before we bail. + */ + if (++ntries % NFSAUTH_DR_TRYCNT) { + delay(hz); + goto retry; + } + sys_log("nfsauth: mountd has not established door"); kmem_free(addr.buf, addr.len); return (NFSAUTH_DROP); } + ntries = 0; + varg.vers = V_PROTO; + varg.arg_u.arg.cmd = NFSAUTH_ACCESS; + varg.arg_u.arg.areq.req_client.n_len = addr.len; + varg.arg_u.arg.areq.req_client.n_bytes = addr.buf; + varg.arg_u.arg.areq.req_netid = svc_getnetid(req->rq_xprt); + varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path; + varg.arg_u.arg.areq.req_flavor = flavor; - timout.tv_sec = nfsauth_timeout; - timout.tv_usec = 0; - - request.req_client.n_len = addr.len; - request.req_client.n_bytes = addr.buf; - request.req_netid = svc_getnetid(req->rq_xprt); - request.req_path = exi->exi_export.ex_path; - request.req_flavor = flavor; + /* + * Setup the XDR stream for encoding the arguments. Notice that + * in addition to the args having variable fields (req_netid and + * req_path), the argument data structure is itself versioned, + * so we need to make sure we can size the arguments buffer + * appropriately to encode all the args. If we can't get sizing + * info _or_ properly encode the arguments, there's really no + * point in continuting, so we fail the request. + */ + DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg); + if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) { + door_ki_rele(dh); + kmem_free(addr.buf, addr.len); + return (NFSAUTH_DENIED); + } + abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP); + xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE); + if (!xdr_varg(&xdrs_a, &varg)) { + door_ki_rele(dh); + goto fail; + } + XDR_DESTROY(&xdrs_a); - rpcstat = clnt_call(clnt, NFSAUTH_ACCESS, - (xdrproc_t)xdr_auth_req, (caddr_t)&request, - (xdrproc_t)xdr_auth_res, (caddr_t)&result, - timout); + /* + * The result (nfsauth_res_t) is always two int's, so we don't + * have to dynamically size (or allocate) the results buffer. + * Now that we've got what we need, we prep the door arguments + * and place the call. + */ + da.data_ptr = (char *)abuf; + da.data_size = absz; + da.desc_ptr = NULL; + da.desc_num = 0; + da.rbuf = (char *)rbuf; + da.rsize = rbsz; + + switch (door_ki_upcall(dh, &da)) { + case 0: /* Success */ + if (da.data_ptr != da.rbuf && da.data_size == 0) { + /* + * The door_return that contained the data + * failed ! We're here because of the 2nd + * door_return (w/o data) such that we can + * get control of the thread (and exit + * gracefully). + */ + DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil, + door_arg_t *, &da); + door_ki_rele(dh); + goto fail; + + } else if (rbuf != da.rbuf) { + /* + * The only time this should be true + * is iff userland wanted to hand us + * a bigger response than what we + * expect; that should not happen + * (nfsauth_res_t is only 2 int's), + * but we check nevertheless. + */ + rbuf = da.rbuf; + rbsz = da.rsize; + + } else if (rbsz > da.data_size) { + /* + * We were expecting two int's; but if + * userland fails in encoding the XDR + * stream, we detect that here, since + * the mountd forces down only one byte + * in such scenario. + */ + door_ki_rele(dh); + goto fail; + } + door_ki_rele(dh); + break; - switch (rpcstat) { - case RPC_SUCCESS: - access = result.auth_perm; - break; - case RPC_INTR: - break; - case RPC_TIMEDOUT: - /* - * Show messages no more than once per minute - */ - now = gethrestime_sec(); - if ((exi_msg + 60) < now) { - exi_msg = now; - cmn_err(CE_WARN, "nfsauth: mountd not responding"); - } - break; - default: - /* - * Show messages no more than once per minute - */ - now = gethrestime_sec(); - if ((exi_msg + 60) < now) { - char *errmsg; - - exi_msg = now; - errmsg = clnt_sperror(clnt, "nfsauth upcall failed"); - cmn_err(CE_WARN, errmsg); - kmem_free(errmsg, MAXPATHLEN); - } - break; + case EAGAIN: + /* + * Server out of resources; back off for a bit + */ + door_ki_rele(dh); + kmem_free(abuf, absz); + delay(hz); + goto retry; + /* NOTREACHED */ + + case EINTR: + if (!door_ki_info(dh, &di)) { + if (di.di_attributes & DOOR_REVOKED) { + /* + * The server barfed and revoked + * the (existing) door on us; we + * want to wait to give smf(5) a + * chance to restart mountd(1m) + * and establish a new door handle. + */ + mutex_enter(&mountd_lock); + if (dh == mountd_dh) + mountd_dh = NULL; + mutex_exit(&mountd_lock); + door_ki_rele(dh); + kmem_free(abuf, absz); + delay(hz); + goto retry; + } + /* + * If the door was _not_ revoked on us, + * then more than likely we took an INTR, + * so we need to fail the operation. + */ + door_ki_rele(dh); + goto fail; + } + /* + * The only failure that can occur from getting + * the door info is EINVAL, so we let the code + * below handle it. + */ + /* FALLTHROUGH */ + + case EBADF: + case EINVAL: + default: + /* + * If we have a stale door handle, give smf a last + * chance to start it by sleeping for a little bit. + * If we're still hosed, we'll fail the call. + * + * Since we're going to reacquire the door handle + * upon the retry, we opt to sleep for a bit and + * _not_ to clear mountd_dh. If mountd restarted + * and was able to set mountd_dh, we should see + * the new instance; if not, we won't get caught + * up in the retry/DELAY loop. + */ + door_ki_rele(dh); + if (!last) { + delay(hz); + last++; + goto retry; + } + sys_log("nfsauth: stale mountd door handle"); + goto fail; } - clfree(clnt, ch); - if (rpcstat != RPC_SUCCESS) { - kmem_free(addr.buf, addr.len); - return (NFSAUTH_DROP); + /* + * No door errors encountered; setup the XDR stream for decoding + * the results. If we fail to decode the results, we've got no + * other recourse than to fail the request. + */ + xdrmem_create(&xdrs_r, rbuf, rbsz, XDR_DECODE); + if (!xdr_nfsauth_res(&xdrs_r, &res)) + goto fail; + XDR_DESTROY(&xdrs_r); + + DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res); + switch (res.stat) { + case NFSAUTH_DR_OKAY: + access = res.ares.auth_perm; + kmem_free(abuf, absz); + break; + + case NFSAUTH_DR_EFAIL: + case NFSAUTH_DR_DECERR: + case NFSAUTH_DR_BADCMD: + default: +fail: + kmem_free(addr.buf, addr.len); + kmem_free(abuf, absz); + return (NFSAUTH_DENIED); + /* NOTREACHED */ } /* |