#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/cred.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/buf.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/pathname.h>
#include <sys/uio.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/siginfo.h>
#include <sys/tiuser.h>
#include <sys/statvfs.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/strsubr.h>
#include <sys/stropts.h>
#include <sys/timod.h>
#include <sys/t_kuser.h>
#include <sys/kmem.h>
#include <sys/kstat.h>
#include <sys/dirent.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/unistd.h>
#include <sys/vtrace.h>
#include <sys/mode.h>
#include <sys/acl.h>
#include <sys/sdt.h>
#include <sys/debug.h>
#include <rpc/types.h>
#include <rpc/auth.h>
#include <rpc/auth_unix.h>
#include <rpc/auth_des.h>
#include <rpc/svc.h>
#include <rpc/xdr.h>
#include <rpc/rpc_rdma.h>
#include <nfs/nfs.h>
#include <nfs/export.h>
#include <nfs/nfssys.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs_acl.h>
#include <nfs/nfs_log.h>
#include <nfs/lm.h>
#include <nfs/nfs_dispatch.h>
#include <nfs/nfs4_drc.h>
#include <sys/modctl.h>
#include <sys/cladm.h>
#include <sys/clconf.h>
#include <sys/tsol/label.h>
#define MAXHOST 32
const char *kinet_ntop6(uchar_t *, char *, size_t);
static struct modlmisc modlmisc = {
&mod_miscops, "NFS server module"
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
zone_key_t nfssrv_zone_key;
list_t nfssrv_globals_list;
krwlock_t nfssrv_globals_rwl;
kmem_cache_t *nfs_xuio_cache;
int nfs_loaned_buffers = 0;
char **rfs4_dss_newpaths;
uint_t rfs4_dss_numnewpaths;
nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths;
int
_init(void)
{
int status;
nfs_srvinit();
status = mod_install((struct modlinkage *)&modlinkage);
if (status != 0) {
nfs_srvfini();
return (status);
}
nfs_srv_quiesce_func = nfs_srv_quiesce_all;
nfs_srv_dss_func = rfs4_dss_setpaths;
rfs4_dss_paths = rfs4_dss_oldpaths = NULL;
nfs_xuio_cache = kmem_cache_create("nfs_xuio_cache",
sizeof (nfs_xuio_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
return (status);
}
int
_fini()
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
#define PUBLICFH_CHECK(ne, disp, exi, fsid, xfid) \
((disp->dis_flags & RPC_PUBLICFH_OK) && \
((exi->exi_export.ex_flags & EX_PUBLIC) || \
(exi == ne->exi_public && exportmatch(ne->exi_root, \
fsid, xfid))))
static void nfs_srv_shutdown_all(int);
static void rfs4_server_start(nfs_globals_t *, int);
static void nullfree(void);
static void rfs_dispatch(struct svc_req *, SVCXPRT *);
static void acl_dispatch(struct svc_req *, SVCXPRT *);
static int checkauth(struct exportinfo *, struct svc_req *, cred_t *, int,
bool_t, bool_t *);
static char *client_name(struct svc_req *req);
static char *client_addr(struct svc_req *req, char *buf);
extern bool_t sec_svc_inrootlist(int, caddr_t, int, caddr_t *);
static void *nfs_server_zone_init(zoneid_t);
static void nfs_server_zone_fini(zoneid_t, void *);
static void nfs_server_zone_shutdown(zoneid_t, void *);
#define NFSLOG_COPY_NETBUF(exi, xprt, nb) { \
(nb)->maxlen = (xprt)->xp_rtaddr.maxlen; \
(nb)->len = (xprt)->xp_rtaddr.len; \
(nb)->buf = kmem_alloc((nb)->len, KM_SLEEP); \
bcopy((xprt)->xp_rtaddr.buf, (nb)->buf, (nb)->len); \
}
static int MCLpath(char **);
static void URLparse(char *);
static SVC_CALLOUT __nfs_sc_clts[] = {
{ NFS_PROGRAM, NFS_VERSMIN, NFS_VERSMAX, rfs_dispatch },
{ NFS_ACL_PROGRAM, NFS_ACL_VERSMIN, NFS_ACL_VERSMAX, acl_dispatch }
};
static SVC_CALLOUT_TABLE nfs_sct_clts = {
sizeof (__nfs_sc_clts) / sizeof (__nfs_sc_clts[0]), FALSE,
__nfs_sc_clts
};
static SVC_CALLOUT __nfs_sc_cots[] = {
{ NFS_PROGRAM, NFS_VERSMIN, NFS_VERSMAX, rfs_dispatch },
{ NFS_ACL_PROGRAM, NFS_ACL_VERSMIN, NFS_ACL_VERSMAX, acl_dispatch }
};
static SVC_CALLOUT_TABLE nfs_sct_cots = {
sizeof (__nfs_sc_cots) / sizeof (__nfs_sc_cots[0]), FALSE, __nfs_sc_cots
};
static SVC_CALLOUT __nfs_sc_rdma[] = {
{ NFS_PROGRAM, NFS_VERSMIN, NFS_VERSMAX, rfs_dispatch },
{ NFS_ACL_PROGRAM, NFS_ACL_VERSMIN, NFS_ACL_VERSMAX, acl_dispatch }
};
static SVC_CALLOUT_TABLE nfs_sct_rdma = {
sizeof (__nfs_sc_rdma) / sizeof (__nfs_sc_rdma[0]), FALSE, __nfs_sc_rdma
};
nvlist_t *rfs4_dss_paths, *rfs4_dss_oldpaths;
bool_t rfs4_minorvers_mismatch(struct svc_req *, SVCXPRT *, void *);
static uint_t nfs_server_tsd_key;
nfs_globals_t *
nfs_srv_getzg(void)
{
nfs_globals_t *ng;
ng = tsd_get(nfs_server_tsd_key);
if (ng == NULL) {
ng = zone_getspecific(nfssrv_zone_key, curzone);
(void) tsd_set(nfs_server_tsd_key, ng);
}
return (ng);
}
void
nfs_srv_offline(void)
{
nfs_globals_t *ng;
ng = nfs_srv_getzg();
mutex_enter(&ng->nfs_server_upordown_lock);
if (ng->nfs_server_upordown == NFS_SERVER_RUNNING) {
ng->nfs_server_upordown = NFS_SERVER_OFFLINE;
}
mutex_exit(&ng->nfs_server_upordown_lock);
}
void
nfs_srv_stop_all(void)
{
int quiesce = 0;
nfs_srv_shutdown_all(quiesce);
}
void
nfs_srv_quiesce_all(void)
{
int quiesce = 1;
nfs_srv_shutdown_all(quiesce);
}
static void
nfs_srv_shutdown_all(int quiesce)
{
nfs_globals_t *ng = nfs_srv_getzg();
mutex_enter(&ng->nfs_server_upordown_lock);
if (quiesce) {
if (ng->nfs_server_upordown == NFS_SERVER_RUNNING ||
ng->nfs_server_upordown == NFS_SERVER_OFFLINE) {
ng->nfs_server_upordown = NFS_SERVER_QUIESCED;
cv_signal(&ng->nfs_server_upordown_cv);
rfs4_dss_numnewpaths = 0;
rfs4_dss_newpaths = NULL;
cmn_err(CE_NOTE, "nfs_server: server is now quiesced; "
"NFSv4 state has been preserved");
}
} else {
if (ng->nfs_server_upordown == NFS_SERVER_OFFLINE) {
ng->nfs_server_upordown = NFS_SERVER_STOPPING;
mutex_exit(&ng->nfs_server_upordown_lock);
rfs4_state_zone_fini();
rfs4_fini_drc();
mutex_enter(&ng->nfs_server_upordown_lock);
ng->nfs_server_upordown = NFS_SERVER_STOPPED;
rfs4_dss_numnewpaths = 0;
rfs4_dss_newpaths = NULL;
cv_signal(&ng->nfs_server_upordown_cv);
}
}
mutex_exit(&ng->nfs_server_upordown_lock);
}
static int
nfs_srv_set_sc_versions(struct file *fp, SVC_CALLOUT_TABLE **sctpp,
rpcvers_t versmin, rpcvers_t versmax)
{
struct strioctl strioc;
struct T_info_ack tinfo;
int error, retval;
strioc.ic_cmd = TI_GETINFO;
strioc.ic_timout = -1;
strioc.ic_len = sizeof (tinfo);
strioc.ic_dp = (char *)&tinfo;
tinfo.PRIM_type = T_INFO_REQ;
error = strioctl(fp->f_vnode, I_STR, (intptr_t)&strioc, 0, K_TO_K,
CRED(), &retval);
if (error || retval)
return (error);
switch (tinfo.SERV_type) {
case T_CLTS:
if (versmax == NFS_V4)
return (EINVAL);
__nfs_sc_clts[0].sc_versmin = versmin;
__nfs_sc_clts[0].sc_versmax = versmax;
__nfs_sc_clts[1].sc_versmin = versmin;
__nfs_sc_clts[1].sc_versmax = versmax;
*sctpp = &nfs_sct_clts;
break;
case T_COTS:
case T_COTS_ORD:
__nfs_sc_cots[0].sc_versmin = versmin;
__nfs_sc_cots[0].sc_versmax = versmax;
if (versmax > NFS_ACL_VERSMAX)
versmax = NFS_ACL_VERSMAX;
__nfs_sc_cots[1].sc_versmin = versmin;
__nfs_sc_cots[1].sc_versmax = versmax;
*sctpp = &nfs_sct_cots;
break;
default:
error = EINVAL;
}
return (error);
}
int
nfs_svc(struct nfs_svc_args *arg, model_t model)
{
nfs_globals_t *ng;
file_t *fp;
SVCMASTERXPRT *xprt;
int error;
int readsize;
char buf[KNC_STRSIZE];
size_t len;
STRUCT_HANDLE(nfs_svc_args, uap);
struct netbuf addrmask;
SVC_CALLOUT_TABLE *sctp = NULL;
ng = nfs_srv_getzg();
STRUCT_SET_HANDLE(uap, model, arg);
if ((fp = getf(STRUCT_FGET(uap, fd))) == NULL)
return (EBADF);
if ((error = nfs_export_get_rootfh(ng)) != 0)
return (error);
readsize = nfs3tsize() + (RPC_MAXDATASIZE - NFS_MAXDATA);
if (readsize < RPC_MAXDATASIZE)
readsize = RPC_MAXDATASIZE;
error = copyinstr((const char *)STRUCT_FGETP(uap, netid), buf,
KNC_STRSIZE, &len);
if (error) {
releasef(STRUCT_FGET(uap, fd));
return (error);
}
addrmask.len = STRUCT_FGET(uap, addrmask.len);
addrmask.maxlen = STRUCT_FGET(uap, addrmask.maxlen);
addrmask.buf = kmem_alloc(addrmask.maxlen, KM_SLEEP);
error = copyin(STRUCT_FGETP(uap, addrmask.buf), addrmask.buf,
addrmask.len);
if (error) {
releasef(STRUCT_FGET(uap, fd));
kmem_free(addrmask.buf, addrmask.maxlen);
return (error);
}
ng->nfs_versmin = STRUCT_FGET(uap, nfs_versmin);
ng->nfs_versmax = STRUCT_FGET(uap, nfs_versmax);
if ((ng->nfs_versmin > ng->nfs_versmax) ||
(ng->nfs_versmin < NFS_SRV_VERS_MIN) ||
(ng->nfs_versmax > NFS_SRV_VERS_MAX)) {
cmn_err(CE_NOTE, "%s: bad min (%u) or max (%u) version number",
"NFS", ng->nfs_versmin, ng->nfs_versmax);
ng->nfs_versmin = NFS_SRV_VERSMIN_DEFAULT;
ng->nfs_versmax = NFS_SRV_VERSMAX_DEFAULT;
}
error = nfs_srv_set_sc_versions(fp, &sctp,
NFS_PROT_VERSION(ng->nfs_versmin),
NFS_PROT_VERSION(ng->nfs_versmax));
if (error != 0) {
releasef(STRUCT_FGET(uap, fd));
kmem_free(addrmask.buf, addrmask.maxlen);
return (error);
}
if (NFS_PROT_VERSION(ng->nfs_versmax) == NFS_V4)
rfs4_server_start(ng, STRUCT_FGET(uap, delegation));
error = svc_tli_kcreate(fp, readsize, buf, &addrmask, &xprt,
sctp, NULL, NFS_SVCPOOL_ID, TRUE);
if (error)
kmem_free(addrmask.buf, addrmask.maxlen);
releasef(STRUCT_FGET(uap, fd));
if (cluster_bootflags & CLUSTER_BOOTED)
lm_global_nlmid = clconf_get_nodeid();
return (error);
}
static void
rfs4_server_start(nfs_globals_t *ng, int nfs4_srv_delegation)
{
nfs4_minor_t nfs4_minor_max;
nfs4_minor_max = NFS_PROT_V4_MINORVERSION(ng->nfs_versmax);
mutex_enter(&ng->nfs_server_upordown_lock);
if (ng->nfs_server_upordown != NFS_SERVER_RUNNING) {
while (ng->nfs_server_upordown == NFS_SERVER_STOPPING ||
ng->nfs_server_upordown == NFS_SERVER_OFFLINE)
cv_wait(&ng->nfs_server_upordown_cv,
&ng->nfs_server_upordown_lock);
if (ng->nfs_server_upordown != NFS_SERVER_RUNNING) {
(void) svc_pool_control(NFS_SVCPOOL_ID,
SVCPSET_UNREGISTER_PROC, (void *)&nfs_srv_offline);
(void) svc_pool_control(NFS_SVCPOOL_ID,
SVCPSET_SHUTDOWN_PROC, (void *)&nfs_srv_stop_all);
rfs4_do_server_start(ng->nfs_server_upordown,
nfs4_srv_delegation, nfs4_minor_max,
cluster_bootflags & CLUSTER_BOOTED);
ng->nfs_server_upordown = NFS_SERVER_RUNNING;
}
cv_signal(&ng->nfs_server_upordown_cv);
}
mutex_exit(&ng->nfs_server_upordown_lock);
}
int
rdma_start(struct rdma_svc_args *rsa)
{
nfs_globals_t *ng;
int error;
rdma_xprt_group_t started_rdma_xprts;
rdma_stat stat;
int svc_state = 0;
if ((rsa->nfs_versmin > rsa->nfs_versmax) ||
(rsa->nfs_versmin < NFS_SRV_VERS_MIN) ||
(rsa->nfs_versmax > NFS_SRV_VERS_MAX)) {
rsa->nfs_versmin = NFS_SRV_VERSMIN_DEFAULT;
rsa->nfs_versmax = NFS_SRV_VERSMAX_DEFAULT;
}
ng = nfs_srv_getzg();
ng->nfs_versmin = rsa->nfs_versmin;
ng->nfs_versmax = rsa->nfs_versmax;
__nfs_sc_rdma[0].sc_versmin = NFS_PROT_VERSION(rsa->nfs_versmin);
__nfs_sc_rdma[0].sc_versmax = NFS_PROT_VERSION(rsa->nfs_versmax);
__nfs_sc_rdma[1].sc_versmin = NFS_PROT_VERSION(rsa->nfs_versmin);
__nfs_sc_rdma[1].sc_versmax =
MIN(NFS_PROT_VERSION(rsa->nfs_versmax), NFS_ACL_VERSMAX);
if (NFS_PROT_VERSION(rsa->nfs_versmax) == NFS_V4)
rfs4_server_start(ng, rsa->delegation);
started_rdma_xprts.rtg_count = 0;
started_rdma_xprts.rtg_listhead = NULL;
started_rdma_xprts.rtg_poolid = rsa->poolid;
restart:
error = svc_rdma_kcreate(rsa->netid, &nfs_sct_rdma, rsa->poolid,
&started_rdma_xprts);
svc_state = !error;
while (!error) {
stat = rdma_kwait();
if ((stat == RDMA_HCA_DETACH || stat == RDMA_INTR) &&
svc_state) {
rdma_stop(&started_rdma_xprts);
svc_state = 0;
}
if (stat == RDMA_INTR)
return (0);
if ((stat == RDMA_HCA_ATTACH) && (svc_state == 0))
goto restart;
}
return (error);
}
void
rpc_null(caddr_t *argp, caddr_t *resp, struct exportinfo *exi,
struct svc_req *req, cred_t *cr, bool_t ro)
{
}
void
rpc_null_v3(caddr_t *argp, caddr_t *resp, struct exportinfo *exi,
struct svc_req *req, cred_t *cr, bool_t ro)
{
DTRACE_NFSV3_4(op__null__start, struct svc_req *, req,
cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi);
DTRACE_NFSV3_4(op__null__done, struct svc_req *, req,
cred_t *, cr, vnode_t *, NULL, struct exportinfo *, exi);
}
static void
rfs_error(caddr_t *argp, caddr_t *resp, struct exportinfo *exi,
struct svc_req *req, cred_t *cr, bool_t ro)
{
}
static void
nullfree(void)
{
}
static char *rfscallnames_v2[] = {
"RFS2_NULL",
"RFS2_GETATTR",
"RFS2_SETATTR",
"RFS2_ROOT",
"RFS2_LOOKUP",
"RFS2_READLINK",
"RFS2_READ",
"RFS2_WRITECACHE",
"RFS2_WRITE",
"RFS2_CREATE",
"RFS2_REMOVE",
"RFS2_RENAME",
"RFS2_LINK",
"RFS2_SYMLINK",
"RFS2_MKDIR",
"RFS2_RMDIR",
"RFS2_READDIR",
"RFS2_STATFS"
};
static struct rpcdisp rfsdisptab_v2[] = {
{rpc_null,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{rfs_getattr,
xdr_fhandle, xdr_fastfhandle, sizeof (fhandle_t),
xdr_attrstat, xdr_fastattrstat, sizeof (struct nfsattrstat),
nullfree, RPC_IDEMPOTENT|RPC_ALLOWANON|RPC_MAPRESP,
rfs_getattr_getfh},
{rfs_setattr,
xdr_saargs, NULL_xdrproc_t, sizeof (struct nfssaargs),
xdr_attrstat, xdr_fastattrstat, sizeof (struct nfsattrstat),
nullfree, RPC_MAPRESP,
rfs_setattr_getfh},
{rfs_error,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{rfs_lookup,
xdr_diropargs, NULL_xdrproc_t, sizeof (struct nfsdiropargs),
xdr_diropres, xdr_fastdiropres, sizeof (struct nfsdiropres),
nullfree, RPC_IDEMPOTENT|RPC_MAPRESP|RPC_PUBLICFH_OK,
rfs_lookup_getfh},
{rfs_readlink,
xdr_fhandle, xdr_fastfhandle, sizeof (fhandle_t),
xdr_rdlnres, NULL_xdrproc_t, sizeof (struct nfsrdlnres),
rfs_rlfree, RPC_IDEMPOTENT,
rfs_readlink_getfh},
{rfs_read,
xdr_readargs, NULL_xdrproc_t, sizeof (struct nfsreadargs),
xdr_rdresult, NULL_xdrproc_t, sizeof (struct nfsrdresult),
rfs_rdfree, RPC_IDEMPOTENT,
rfs_read_getfh},
{rfs_error,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{rfs_write,
xdr_writeargs, NULL_xdrproc_t, sizeof (struct nfswriteargs),
xdr_attrstat, xdr_fastattrstat, sizeof (struct nfsattrstat),
nullfree, RPC_MAPRESP,
rfs_write_getfh},
{rfs_create,
xdr_creatargs, NULL_xdrproc_t, sizeof (struct nfscreatargs),
xdr_diropres, xdr_fastdiropres, sizeof (struct nfsdiropres),
nullfree, RPC_MAPRESP,
rfs_create_getfh},
{rfs_remove,
xdr_diropargs, NULL_xdrproc_t, sizeof (struct nfsdiropargs),
#ifdef _LITTLE_ENDIAN
xdr_enum, xdr_fastenum, sizeof (enum nfsstat),
#else
xdr_enum, NULL_xdrproc_t, sizeof (enum nfsstat),
#endif
nullfree, RPC_MAPRESP,
rfs_remove_getfh},
{rfs_rename,
xdr_rnmargs, NULL_xdrproc_t, sizeof (struct nfsrnmargs),
#ifdef _LITTLE_ENDIAN
xdr_enum, xdr_fastenum, sizeof (enum nfsstat),
#else
xdr_enum, NULL_xdrproc_t, sizeof (enum nfsstat),
#endif
nullfree, RPC_MAPRESP,
rfs_rename_getfh},
{rfs_link,
xdr_linkargs, NULL_xdrproc_t, sizeof (struct nfslinkargs),
#ifdef _LITTLE_ENDIAN
xdr_enum, xdr_fastenum, sizeof (enum nfsstat),
#else
xdr_enum, NULL_xdrproc_t, sizeof (enum nfsstat),
#endif
nullfree, RPC_MAPRESP,
rfs_link_getfh},
{rfs_symlink,
xdr_slargs, NULL_xdrproc_t, sizeof (struct nfsslargs),
#ifdef _LITTLE_ENDIAN
xdr_enum, xdr_fastenum, sizeof (enum nfsstat),
#else
xdr_enum, NULL_xdrproc_t, sizeof (enum nfsstat),
#endif
nullfree, RPC_MAPRESP,
rfs_symlink_getfh},
{rfs_mkdir,
xdr_creatargs, NULL_xdrproc_t, sizeof (struct nfscreatargs),
xdr_diropres, xdr_fastdiropres, sizeof (struct nfsdiropres),
nullfree, RPC_MAPRESP,
rfs_mkdir_getfh},
{rfs_rmdir,
xdr_diropargs, NULL_xdrproc_t, sizeof (struct nfsdiropargs),
#ifdef _LITTLE_ENDIAN
xdr_enum, xdr_fastenum, sizeof (enum nfsstat),
#else
xdr_enum, NULL_xdrproc_t, sizeof (enum nfsstat),
#endif
nullfree, RPC_MAPRESP,
rfs_rmdir_getfh},
{rfs_readdir,
xdr_rddirargs, NULL_xdrproc_t, sizeof (struct nfsrddirargs),
xdr_putrddirres, NULL_xdrproc_t, sizeof (struct nfsrddirres),
rfs_rddirfree, RPC_IDEMPOTENT,
rfs_readdir_getfh},
{rfs_statfs,
xdr_fhandle, xdr_fastfhandle, sizeof (fhandle_t),
xdr_statfs, xdr_faststatfs, sizeof (struct nfsstatfs),
nullfree, RPC_IDEMPOTENT|RPC_ALLOWANON|RPC_MAPRESP,
rfs_statfs_getfh},
};
static char *rfscallnames_v3[] = {
"RFS3_NULL",
"RFS3_GETATTR",
"RFS3_SETATTR",
"RFS3_LOOKUP",
"RFS3_ACCESS",
"RFS3_READLINK",
"RFS3_READ",
"RFS3_WRITE",
"RFS3_CREATE",
"RFS3_MKDIR",
"RFS3_SYMLINK",
"RFS3_MKNOD",
"RFS3_REMOVE",
"RFS3_RMDIR",
"RFS3_RENAME",
"RFS3_LINK",
"RFS3_READDIR",
"RFS3_READDIRPLUS",
"RFS3_FSSTAT",
"RFS3_FSINFO",
"RFS3_PATHCONF",
"RFS3_COMMIT"
};
static struct rpcdisp rfsdisptab_v3[] = {
{rpc_null_v3,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{rfs3_getattr,
xdr_nfs_fh3_server, NULL_xdrproc_t, sizeof (GETATTR3args),
xdr_GETATTR3res, NULL_xdrproc_t, sizeof (GETATTR3res),
nullfree, (RPC_IDEMPOTENT | RPC_ALLOWANON),
rfs3_getattr_getfh},
{rfs3_setattr,
xdr_SETATTR3args, NULL_xdrproc_t, sizeof (SETATTR3args),
xdr_SETATTR3res, NULL_xdrproc_t, sizeof (SETATTR3res),
nullfree, 0,
rfs3_setattr_getfh},
{rfs3_lookup,
xdr_diropargs3, NULL_xdrproc_t, sizeof (LOOKUP3args),
xdr_LOOKUP3res, NULL_xdrproc_t, sizeof (LOOKUP3res),
nullfree, (RPC_IDEMPOTENT | RPC_PUBLICFH_OK),
rfs3_lookup_getfh},
{rfs3_access,
xdr_ACCESS3args, NULL_xdrproc_t, sizeof (ACCESS3args),
xdr_ACCESS3res, NULL_xdrproc_t, sizeof (ACCESS3res),
nullfree, RPC_IDEMPOTENT,
rfs3_access_getfh},
{rfs3_readlink,
xdr_nfs_fh3_server, NULL_xdrproc_t, sizeof (READLINK3args),
xdr_READLINK3res, NULL_xdrproc_t, sizeof (READLINK3res),
rfs3_readlink_free, RPC_IDEMPOTENT,
rfs3_readlink_getfh},
{rfs3_read,
xdr_READ3args, NULL_xdrproc_t, sizeof (READ3args),
xdr_READ3res, NULL_xdrproc_t, sizeof (READ3res),
rfs3_read_free, RPC_IDEMPOTENT,
rfs3_read_getfh},
{rfs3_write,
xdr_WRITE3args, NULL_xdrproc_t, sizeof (WRITE3args),
xdr_WRITE3res, NULL_xdrproc_t, sizeof (WRITE3res),
nullfree, 0,
rfs3_write_getfh},
{rfs3_create,
xdr_CREATE3args, NULL_xdrproc_t, sizeof (CREATE3args),
xdr_CREATE3res, NULL_xdrproc_t, sizeof (CREATE3res),
nullfree, 0,
rfs3_create_getfh},
{rfs3_mkdir,
xdr_MKDIR3args, NULL_xdrproc_t, sizeof (MKDIR3args),
xdr_MKDIR3res, NULL_xdrproc_t, sizeof (MKDIR3res),
nullfree, 0,
rfs3_mkdir_getfh},
{rfs3_symlink,
xdr_SYMLINK3args, NULL_xdrproc_t, sizeof (SYMLINK3args),
xdr_SYMLINK3res, NULL_xdrproc_t, sizeof (SYMLINK3res),
nullfree, 0,
rfs3_symlink_getfh},
{rfs3_mknod,
xdr_MKNOD3args, NULL_xdrproc_t, sizeof (MKNOD3args),
xdr_MKNOD3res, NULL_xdrproc_t, sizeof (MKNOD3res),
nullfree, 0,
rfs3_mknod_getfh},
{rfs3_remove,
xdr_diropargs3, NULL_xdrproc_t, sizeof (REMOVE3args),
xdr_REMOVE3res, NULL_xdrproc_t, sizeof (REMOVE3res),
nullfree, 0,
rfs3_remove_getfh},
{rfs3_rmdir,
xdr_diropargs3, NULL_xdrproc_t, sizeof (RMDIR3args),
xdr_RMDIR3res, NULL_xdrproc_t, sizeof (RMDIR3res),
nullfree, 0,
rfs3_rmdir_getfh},
{rfs3_rename,
xdr_RENAME3args, NULL_xdrproc_t, sizeof (RENAME3args),
xdr_RENAME3res, NULL_xdrproc_t, sizeof (RENAME3res),
nullfree, 0,
rfs3_rename_getfh},
{rfs3_link,
xdr_LINK3args, NULL_xdrproc_t, sizeof (LINK3args),
xdr_LINK3res, NULL_xdrproc_t, sizeof (LINK3res),
nullfree, 0,
rfs3_link_getfh},
{rfs3_readdir,
xdr_READDIR3args, NULL_xdrproc_t, sizeof (READDIR3args),
xdr_READDIR3res, NULL_xdrproc_t, sizeof (READDIR3res),
rfs3_readdir_free, RPC_IDEMPOTENT,
rfs3_readdir_getfh},
{rfs3_readdirplus,
xdr_READDIRPLUS3args, NULL_xdrproc_t, sizeof (READDIRPLUS3args),
xdr_READDIRPLUS3res, NULL_xdrproc_t, sizeof (READDIRPLUS3res),
rfs3_readdirplus_free, RPC_AVOIDWORK,
rfs3_readdirplus_getfh},
{rfs3_fsstat,
xdr_nfs_fh3_server, NULL_xdrproc_t, sizeof (FSSTAT3args),
xdr_FSSTAT3res, NULL_xdrproc_t, sizeof (FSSTAT3res),
nullfree, RPC_IDEMPOTENT,
rfs3_fsstat_getfh},
{rfs3_fsinfo,
xdr_nfs_fh3_server, NULL_xdrproc_t, sizeof (FSINFO3args),
xdr_FSINFO3res, NULL_xdrproc_t, sizeof (FSINFO3res),
nullfree, RPC_IDEMPOTENT|RPC_ALLOWANON,
rfs3_fsinfo_getfh},
{rfs3_pathconf,
xdr_nfs_fh3_server, NULL_xdrproc_t, sizeof (PATHCONF3args),
xdr_PATHCONF3res, NULL_xdrproc_t, sizeof (PATHCONF3res),
nullfree, RPC_IDEMPOTENT,
rfs3_pathconf_getfh},
{rfs3_commit,
xdr_COMMIT3args, NULL_xdrproc_t, sizeof (COMMIT3args),
xdr_COMMIT3res, NULL_xdrproc_t, sizeof (COMMIT3res),
nullfree, RPC_IDEMPOTENT,
rfs3_commit_getfh},
};
static char *rfscallnames_v4[] = {
"RFS4_NULL",
"RFS4_COMPOUND",
"RFS4_NULL",
"RFS4_NULL",
"RFS4_NULL",
"RFS4_NULL",
"RFS4_NULL",
"RFS4_NULL",
"RFS4_CREATE"
};
static struct rpcdisp rfsdisptab_v4[] = {
[NFSPROC4_NULL] = {
.dis_proc = NULL,
.dis_xdrargs = xdr_void,
.dis_fastxdrargs = NULL_xdrproc_t,
.dis_argsz = 0,
.dis_xdrres = xdr_void,
.dis_fastxdrres = NULL_xdrproc_t,
.dis_ressz = 0,
.dis_resfree = nullfree,
.dis_flags = RPC_IDEMPOTENT,
.dis_getfh = NULL
},
[NFSPROC4_COMPOUND] = {
.dis_proc = NULL,
.dis_xdrargs = xdr_COMPOUND4args_srv,
.dis_fastxdrargs = NULL_xdrproc_t,
.dis_argsz = sizeof (COMPOUND4args),
.dis_xdrres = xdr_COMPOUND4res_srv,
.dis_fastxdrres = NULL_xdrproc_t,
.dis_ressz = sizeof (COMPOUND4res),
.dis_resfree = rfs4_compound_free,
.dis_flags = 0,
.dis_getfh = NULL
},
};
union rfs_args {
fhandle_t nfs2_getattr_args;
struct nfssaargs nfs2_setattr_args;
struct nfsdiropargs nfs2_lookup_args;
fhandle_t nfs2_readlink_args;
struct nfsreadargs nfs2_read_args;
struct nfswriteargs nfs2_write_args;
struct nfscreatargs nfs2_create_args;
struct nfsdiropargs nfs2_remove_args;
struct nfsrnmargs nfs2_rename_args;
struct nfslinkargs nfs2_link_args;
struct nfsslargs nfs2_symlink_args;
struct nfscreatargs nfs2_mkdir_args;
struct nfsdiropargs nfs2_rmdir_args;
struct nfsrddirargs nfs2_readdir_args;
fhandle_t nfs2_statfs_args;
GETATTR3args nfs3_getattr_args;
SETATTR3args nfs3_setattr_args;
LOOKUP3args nfs3_lookup_args;
ACCESS3args nfs3_access_args;
READLINK3args nfs3_readlink_args;
READ3args nfs3_read_args;
WRITE3args nfs3_write_args;
CREATE3args nfs3_create_args;
MKDIR3args nfs3_mkdir_args;
SYMLINK3args nfs3_symlink_args;
MKNOD3args nfs3_mknod_args;
REMOVE3args nfs3_remove_args;
RMDIR3args nfs3_rmdir_args;
RENAME3args nfs3_rename_args;
LINK3args nfs3_link_args;
READDIR3args nfs3_readdir_args;
READDIRPLUS3args nfs3_readdirplus_args;
FSSTAT3args nfs3_fsstat_args;
FSINFO3args nfs3_fsinfo_args;
PATHCONF3args nfs3_pathconf_args;
COMMIT3args nfs3_commit_args;
COMPOUND4args nfs4_compound_args;
};
union rfs_res {
struct nfsattrstat nfs2_getattr_res;
struct nfsattrstat nfs2_setattr_res;
struct nfsdiropres nfs2_lookup_res;
struct nfsrdlnres nfs2_readlink_res;
struct nfsrdresult nfs2_read_res;
struct nfsattrstat nfs2_write_res;
struct nfsdiropres nfs2_create_res;
enum nfsstat nfs2_remove_res;
enum nfsstat nfs2_rename_res;
enum nfsstat nfs2_link_res;
enum nfsstat nfs2_symlink_res;
struct nfsdiropres nfs2_mkdir_res;
enum nfsstat nfs2_rmdir_res;
struct nfsrddirres nfs2_readdir_res;
struct nfsstatfs nfs2_statfs_res;
GETATTR3res nfs3_getattr_res;
SETATTR3res nfs3_setattr_res;
LOOKUP3res nfs3_lookup_res;
ACCESS3res nfs3_access_res;
READLINK3res nfs3_readlink_res;
READ3res nfs3_read_res;
WRITE3res nfs3_write_res;
CREATE3res nfs3_create_res;
MKDIR3res nfs3_mkdir_res;
SYMLINK3res nfs3_symlink_res;
MKNOD3res nfs3_mknod_res;
REMOVE3res nfs3_remove_res;
RMDIR3res nfs3_rmdir_res;
RENAME3res nfs3_rename_res;
LINK3res nfs3_link_res;
READDIR3res nfs3_readdir_res;
READDIRPLUS3res nfs3_readdirplus_res;
FSSTAT3res nfs3_fsstat_res;
FSINFO3res nfs3_fsinfo_res;
PATHCONF3res nfs3_pathconf_res;
COMMIT3res nfs3_commit_res;
COMPOUND4res nfs4_compound_res;
};
static struct rpc_disptable rfs_disptable[] = {
{sizeof (rfsdisptab_v2) / sizeof (rfsdisptab_v2[0]),
rfscallnames_v2,
rfsdisptab_v2},
{sizeof (rfsdisptab_v3) / sizeof (rfsdisptab_v3[0]),
rfscallnames_v3,
rfsdisptab_v3},
{sizeof (rfsdisptab_v4) / sizeof (rfsdisptab_v4[0]),
rfscallnames_v4,
rfsdisptab_v4},
};
static int nfs_portmon = 0;
#ifdef DEBUG
static int rfs_no_fast_xdrargs = 0;
static int rfs_no_fast_xdrres = 0;
#endif
union acl_args {
GETACL2args acl2_getacl_args;
SETACL2args acl2_setacl_args;
GETATTR2args acl2_getattr_args;
ACCESS2args acl2_access_args;
GETXATTRDIR2args acl2_getxattrdir_args;
GETACL3args acl3_getacl_args;
SETACL3args acl3_setacl;
GETXATTRDIR3args acl3_getxattrdir_args;
};
union acl_res {
GETACL2res acl2_getacl_res;
SETACL2res acl2_setacl_res;
GETATTR2res acl2_getattr_res;
ACCESS2res acl2_access_res;
GETXATTRDIR2args acl2_getxattrdir_res;
GETACL3res acl3_getacl_res;
SETACL3res acl3_setacl_res;
GETXATTRDIR3res acl3_getxattrdir_res;
};
static bool_t
auth_tooweak(struct svc_req *req, char *res)
{
if (req->rq_vers == NFS_VERSION && req->rq_proc == RFS_LOOKUP) {
struct nfsdiropres *dr = (struct nfsdiropres *)res;
if ((enum wnfsstat)dr->dr_status == WNFSERR_CLNT_FLAVOR)
return (TRUE);
} else if (req->rq_vers == NFS_V3 && req->rq_proc == NFSPROC3_LOOKUP) {
LOOKUP3res *resp = (LOOKUP3res *)res;
if ((enum wnfsstat)resp->status == WNFSERR_CLNT_FLAVOR)
return (TRUE);
}
return (FALSE);
}
static void
common_dispatch(struct svc_req *req, SVCXPRT *xprt, rpcvers_t min_vers,
rpcvers_t max_vers, char *pgmname, struct rpc_disptable *disptable)
{
int which;
rpcvers_t vers;
char *args;
union {
union rfs_args ra;
union acl_args aa;
} args_buf;
char *res;
union {
union rfs_res rr;
union acl_res ar;
} res_buf;
struct rpcdisp *disp = NULL;
int dis_flags = 0;
cred_t *cr;
int error = 0;
int anon_ok;
struct exportinfo *exi = NULL;
unsigned int nfslog_rec_id;
int dupstat;
struct dupreq *dr;
int authres;
bool_t publicfh_ok = FALSE;
enum_t auth_flavor;
bool_t dupcached = FALSE;
struct netbuf nb;
bool_t logging_enabled = FALSE;
struct exportinfo *nfslog_exi = NULL;
char **procnames;
char cbuf[INET6_ADDRSTRLEN];
bool_t ro = FALSE;
nfs_globals_t *ng = nfs_srv_getzg();
nfs_export_t *ne = ng->nfs_export;
kstat_named_t *svstat, *procstat;
ASSERT(req->rq_prog == NFS_PROGRAM || req->rq_prog == NFS_ACL_PROGRAM);
vers = req->rq_vers;
svstat = ng->svstat[req->rq_vers];
procstat = (req->rq_prog == NFS_PROGRAM) ?
ng->rfsproccnt[vers] : ng->aclproccnt[vers];
if (vers < min_vers || vers > max_vers) {
svcerr_progvers(req->rq_xprt, min_vers, max_vers);
error++;
cmn_err(CE_NOTE, "%s: bad version number %u", pgmname, vers);
goto done;
}
vers -= min_vers;
which = req->rq_proc;
if (which < 0 || which >= disptable[(int)vers].dis_nprocs) {
svcerr_noproc(req->rq_xprt);
error++;
goto done;
}
procstat[which].value.ui64++;
disp = &disptable[(int)vers].dis_table[which];
procnames = disptable[(int)vers].dis_procnames;
auth_flavor = req->rq_cred.oa_flavor;
args = (char *)&args_buf;
#ifdef DEBUG
if (rfs_no_fast_xdrargs || (auth_flavor == RPCSEC_GSS) ||
disp->dis_fastxdrargs == NULL_xdrproc_t ||
!SVC_GETARGS(xprt, disp->dis_fastxdrargs, (char *)&args))
#else
if ((auth_flavor == RPCSEC_GSS) ||
disp->dis_fastxdrargs == NULL_xdrproc_t ||
!SVC_GETARGS(xprt, disp->dis_fastxdrargs, (char *)&args))
#endif
{
bzero(args, disp->dis_argsz);
if (!SVC_GETARGS(xprt, disp->dis_xdrargs, args)) {
error++;
if (rfs4_minorvers_mismatch(req, xprt, (void *)args))
goto done;
svcerr_decode(xprt);
cmn_err(CE_NOTE,
"Failed to decode arguments for %s version %u "
"procedure %s client %s%s",
pgmname, vers + min_vers, procnames[which],
client_name(req), client_addr(req, cbuf));
goto done;
}
}
if (req->rq_vers == 4) {
error += rfs4_dispatch(disp, req, xprt, args);
goto done;
}
dis_flags = disp->dis_flags;
if (disp->dis_getfh != NULL) {
void *fh;
fsid_t *fsid;
fid_t *fid, *xfid;
fhandle_t *fh2;
nfs_fh3 *fh3;
fh = (*disp->dis_getfh)(args);
switch (req->rq_vers) {
case NFS_VERSION:
fh2 = (fhandle_t *)fh;
fsid = &fh2->fh_fsid;
fid = (fid_t *)&fh2->fh_len;
xfid = (fid_t *)&fh2->fh_xlen;
break;
case NFS_V3:
fh3 = (nfs_fh3 *)fh;
fsid = &fh3->fh3_fsid;
fid = FH3TOFIDP(fh3);
xfid = FH3TOXFIDP(fh3);
break;
}
if ((dis_flags & RPC_ALLOWANON) && EQFID(fid, xfid))
anon_ok = 1;
else
anon_ok = 0;
cr = svc_xprt_cred(xprt);
exi = checkexport(fsid, xfid);
if (exi != NULL) {
publicfh_ok = PUBLICFH_CHECK(ne, disp, exi, fsid, xfid);
if (PSEUDO(exi)) {
svcerr_weakauth(xprt);
error++;
goto done;
}
authres = checkauth(exi, req, cr, anon_ok, publicfh_ok,
&ro);
if (authres <= 0) {
if (authres == 0) {
svcerr_weakauth(xprt);
error++;
}
goto done;
}
}
} else
cr = NULL;
if ((dis_flags & RPC_MAPRESP) && (auth_flavor != RPCSEC_GSS)) {
res = (char *)SVC_GETRES(xprt, disp->dis_ressz);
if (res == NULL)
res = (char *)&res_buf;
} else
res = (char *)&res_buf;
if (!(dis_flags & RPC_IDEMPOTENT)) {
dupstat = SVC_DUP_EXT(xprt, req, res, disp->dis_ressz, &dr,
&dupcached);
switch (dupstat) {
case DUP_ERROR:
svcerr_systemerr(xprt);
error++;
goto done;
case DUP_INPROGRESS:
if (res != (char *)&res_buf)
SVC_FREERES(xprt);
error++;
goto done;
case DUP_NEW:
case DUP_DROP:
curthread->t_flag |= T_DONTPEND;
(*disp->dis_proc)(args, res, exi, req, cr, ro);
curthread->t_flag &= ~T_DONTPEND;
if (curthread->t_flag & T_WOULDBLOCK) {
curthread->t_flag &= ~T_WOULDBLOCK;
SVC_DUPDONE_EXT(xprt, dr, res, NULL,
disp->dis_ressz, DUP_DROP);
if (res != (char *)&res_buf)
SVC_FREERES(xprt);
error++;
goto done;
}
if (dis_flags & RPC_AVOIDWORK) {
SVC_DUPDONE_EXT(xprt, dr, res, NULL,
disp->dis_ressz, DUP_DROP);
} else {
SVC_DUPDONE_EXT(xprt, dr, res,
disp->dis_resfree == nullfree ? NULL :
disp->dis_resfree,
disp->dis_ressz, DUP_DONE);
dupcached = TRUE;
}
break;
case DUP_DONE:
break;
}
} else {
curthread->t_flag |= T_DONTPEND;
(*disp->dis_proc)(args, res, exi, req, cr, ro);
curthread->t_flag &= ~T_DONTPEND;
if (curthread->t_flag & T_WOULDBLOCK) {
curthread->t_flag &= ~T_WOULDBLOCK;
if (res != (char *)&res_buf)
SVC_FREERES(xprt);
error++;
goto done;
}
}
if (auth_tooweak(req, res)) {
svcerr_weakauth(xprt);
error++;
goto done;
}
if (nfslog_buffer_list != NULL) {
nfslog_exi = nfslog_get_exi(ne, exi, req, res, &nfslog_rec_id);
logging_enabled = (nfslog_exi != NULL);
if (logging_enabled) {
NFSLOG_COPY_NETBUF(nfslog_exi, xprt, &nb);
if (res != (char *)&res_buf) {
bcopy(res, (char *)&res_buf, disp->dis_ressz);
}
}
}
#ifdef DEBUG
if (rfs_no_fast_xdrres == 0 && res != (char *)&res_buf)
#else
if (res != (char *)&res_buf)
#endif
{
if (!svc_sendreply(xprt, disp->dis_fastxdrres, res)) {
cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
svcerr_systemerr(xprt);
error++;
}
} else {
if (!svc_sendreply(xprt, disp->dis_xdrres, res)) {
cmn_err(CE_NOTE, "%s: bad sendreply", pgmname);
svcerr_systemerr(xprt);
error++;
}
}
if (logging_enabled) {
nfslog_write_record(nfslog_exi, req, args, (char *)&res_buf,
cr, &nb, nfslog_rec_id, NFSLOG_ONE_BUFFER);
exi_rele(nfslog_exi);
kmem_free((&nb)->buf, (&nb)->len);
}
if (disp->dis_resfree != nullfree && dupcached == FALSE) {
(*disp->dis_resfree)(res);
}
done:
if (disp) {
if (!SVC_FREEARGS(xprt, disp->dis_xdrargs, args)) {
cmn_err(CE_NOTE, "%s: bad freeargs", pgmname);
error++;
}
} else {
if (!SVC_FREEARGS(xprt, (xdrproc_t)0, (caddr_t)0)) {
cmn_err(CE_NOTE, "%s: bad freeargs", pgmname);
error++;
}
}
if (exi != NULL)
exi_rele(exi);
svstat[NFS_BADCALLS].value.ui64 += error;
svstat[NFS_CALLS].value.ui64++;
}
static void
rfs_dispatch(struct svc_req *req, SVCXPRT *xprt)
{
common_dispatch(req, xprt, NFS_VERSMIN, NFS_VERSMAX,
"NFS", rfs_disptable);
}
static char *aclcallnames_v2[] = {
"ACL2_NULL",
"ACL2_GETACL",
"ACL2_SETACL",
"ACL2_GETATTR",
"ACL2_ACCESS",
"ACL2_GETXATTRDIR"
};
static struct rpcdisp acldisptab_v2[] = {
{rpc_null,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{acl2_getacl,
xdr_GETACL2args, xdr_fastGETACL2args, sizeof (GETACL2args),
xdr_GETACL2res, NULL_xdrproc_t, sizeof (GETACL2res),
acl2_getacl_free, RPC_IDEMPOTENT,
acl2_getacl_getfh},
{acl2_setacl,
xdr_SETACL2args, NULL_xdrproc_t, sizeof (SETACL2args),
#ifdef _LITTLE_ENDIAN
xdr_SETACL2res, xdr_fastSETACL2res, sizeof (SETACL2res),
#else
xdr_SETACL2res, NULL_xdrproc_t, sizeof (SETACL2res),
#endif
nullfree, RPC_MAPRESP,
acl2_setacl_getfh},
{acl2_getattr,
xdr_GETATTR2args, xdr_fastGETATTR2args, sizeof (GETATTR2args),
#ifdef _LITTLE_ENDIAN
xdr_GETATTR2res, xdr_fastGETATTR2res, sizeof (GETATTR2res),
#else
xdr_GETATTR2res, NULL_xdrproc_t, sizeof (GETATTR2res),
#endif
nullfree, RPC_IDEMPOTENT|RPC_ALLOWANON|RPC_MAPRESP,
acl2_getattr_getfh},
{acl2_access,
xdr_ACCESS2args, xdr_fastACCESS2args, sizeof (ACCESS2args),
#ifdef _LITTLE_ENDIAN
xdr_ACCESS2res, xdr_fastACCESS2res, sizeof (ACCESS2res),
#else
xdr_ACCESS2res, NULL_xdrproc_t, sizeof (ACCESS2res),
#endif
nullfree, RPC_IDEMPOTENT|RPC_MAPRESP,
acl2_access_getfh},
{acl2_getxattrdir,
xdr_GETXATTRDIR2args, NULL_xdrproc_t, sizeof (GETXATTRDIR2args),
xdr_GETXATTRDIR2res, NULL_xdrproc_t, sizeof (GETXATTRDIR2res),
nullfree, RPC_IDEMPOTENT,
acl2_getxattrdir_getfh},
};
static char *aclcallnames_v3[] = {
"ACL3_NULL",
"ACL3_GETACL",
"ACL3_SETACL",
"ACL3_GETXATTRDIR"
};
static struct rpcdisp acldisptab_v3[] = {
{rpc_null,
xdr_void, NULL_xdrproc_t, 0,
xdr_void, NULL_xdrproc_t, 0,
nullfree, RPC_IDEMPOTENT,
0},
{acl3_getacl,
xdr_GETACL3args, NULL_xdrproc_t, sizeof (GETACL3args),
xdr_GETACL3res, NULL_xdrproc_t, sizeof (GETACL3res),
acl3_getacl_free, RPC_IDEMPOTENT,
acl3_getacl_getfh},
{acl3_setacl,
xdr_SETACL3args, NULL_xdrproc_t, sizeof (SETACL3args),
xdr_SETACL3res, NULL_xdrproc_t, sizeof (SETACL3res),
nullfree, 0,
acl3_setacl_getfh},
{acl3_getxattrdir,
xdr_GETXATTRDIR3args, NULL_xdrproc_t, sizeof (GETXATTRDIR3args),
xdr_GETXATTRDIR3res, NULL_xdrproc_t, sizeof (GETXATTRDIR3res),
nullfree, RPC_IDEMPOTENT,
acl3_getxattrdir_getfh},
};
static struct rpc_disptable acl_disptable[] = {
{sizeof (acldisptab_v2) / sizeof (acldisptab_v2[0]),
aclcallnames_v2,
acldisptab_v2},
{sizeof (acldisptab_v3) / sizeof (acldisptab_v3[0]),
aclcallnames_v3,
acldisptab_v3},
};
static void
acl_dispatch(struct svc_req *req, SVCXPRT *xprt)
{
common_dispatch(req, xprt, NFS_ACL_VERSMIN, NFS_ACL_VERSMAX,
"ACL", acl_disptable);
}
int
checkwin(int flavor, int window, struct svc_req *req)
{
struct authdes_cred *adc;
switch (flavor) {
case AUTH_DES:
adc = (struct authdes_cred *)req->rq_clntcred;
CTASSERT(sizeof (struct authdes_cred) <= RQCRED_SIZE);
if (adc->adc_fullname.window > window)
return (0);
break;
default:
break;
}
return (1);
}
static int
checkauth(struct exportinfo *exi, struct svc_req *req, cred_t *cr, int anon_ok,
bool_t publicfh_ok, bool_t *ro)
{
int i, nfsflavor, rpcflavor, stat, access;
struct secinfo *secp;
caddr_t principal;
char buf[INET6_ADDRSTRLEN];
int anon_res = 0;
uid_t uid;
gid_t gid;
uint_t ngids;
gid_t *gids;
if (nfs_portmon) {
struct sockaddr *ca;
ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
if (ca == NULL)
return (0);
if ((ca->sa_family == AF_INET &&
ntohs(((struct sockaddr_in *)ca)->sin_port) >=
IPPORT_RESERVED) ||
(ca->sa_family == AF_INET6 &&
ntohs(((struct sockaddr_in6 *)ca)->sin6_port) >=
IPPORT_RESERVED)) {
cmn_err(CE_NOTE,
"nfs_server: client %s%ssent NFS request from "
"unprivileged port",
client_name(req), client_addr(req, buf));
return (0);
}
}
stat = sec_svc_getcred(req, cr, &principal, &nfsflavor);
if (!stat && nfsflavor != AUTH_UNIX) {
cmn_err(CE_NOTE,
"nfs_server: couldn't get unix cred for %s",
client_name(req));
return (0);
}
if (publicfh_ok)
return (1);
rpcflavor = req->rq_cred.oa_flavor;
access = nfsauth_access(exi, req, cr, &uid, &gid, &ngids, &gids);
if (access & NFSAUTH_DROP)
return (-1);
if (access & NFSAUTH_RO)
*ro = TRUE;
if (access & NFSAUTH_DENIED) {
if (anon_ok == 1)
rpcflavor = AUTH_NONE;
else
return (0);
} else if (access & NFSAUTH_MAPNONE) {
rpcflavor = AUTH_NONE;
} else if (access & NFSAUTH_WRONGSEC) {
return (0);
}
if (rpcflavor != AUTH_SYS)
kmem_free(gids, ngids * sizeof (gid_t));
switch (rpcflavor) {
case AUTH_NONE:
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
break;
case AUTH_UNIX:
if (!stat || (crgetuid(cr) == 0 &&
!(access & NFSAUTH_UIDMAP))) {
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
} else if (crgetuid(cr) == 0 && access & NFSAUTH_ROOT) {
secp = NULL;
for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
struct secinfo *sptr;
sptr = &exi->exi_export.ex_secinfo[i];
if (sptr->s_secinfo.sc_nfsnum == nfsflavor) {
secp = sptr;
break;
}
}
if (secp != NULL) {
(void) crsetugid(cr, secp->s_rootid,
secp->s_rootid);
(void) crsetgroups(cr, 0, NULL);
}
} else if (crgetuid(cr) != uid || crgetgid(cr) != gid) {
if (crsetugid(cr, uid, gid) != 0)
anon_res = crsetugid(cr,
exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
} else if (access & NFSAUTH_GROUPS) {
(void) crsetgroups(cr, ngids, gids);
}
kmem_free(gids, ngids * sizeof (gid_t));
break;
case AUTH_DES:
case RPCSEC_GSS:
secp = NULL;
for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
if (exi->exi_export.ex_secinfo[i].s_secinfo.sc_nfsnum ==
nfsflavor) {
secp = &exi->exi_export.ex_secinfo[i];
break;
}
}
if (!secp) {
cmn_err(CE_NOTE, "nfs_server: client %s%shad "
"no secinfo data for flavor %d",
client_name(req), client_addr(req, buf),
nfsflavor);
return (0);
}
if (!checkwin(rpcflavor, secp->s_window, req)) {
cmn_err(CE_NOTE,
"nfs_server: client %s%sused invalid "
"auth window value",
client_name(req), client_addr(req, buf));
return (0);
}
if (principal && sec_svc_inrootlist(rpcflavor, principal,
secp->s_rootcnt, secp->s_rootnames)) {
if (crgetuid(cr) == 0 && secp->s_rootid == 0)
return (1);
(void) crsetugid(cr, secp->s_rootid, secp->s_rootid);
(void) crsetgroups(cr, 0, NULL);
return (1);
}
if (crgetuid(cr) != 0 &&
(crgetuid(cr) != UID_NOBODY || crgetgid(cr) != GID_NOBODY))
return (1);
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
break;
default:
return (0);
}
if (anon_res != 0) {
if (anon_ok == 0) {
cmn_err(CE_NOTE,
"nfs_server: client %s%ssent wrong "
"authentication for %s",
client_name(req), client_addr(req, buf),
exi->exi_export.ex_path ?
exi->exi_export.ex_path : "?");
return (0);
}
if (crsetugid(cr, UID_NOBODY, GID_NOBODY) != 0)
return (0);
}
return (1);
}
int
checkauth4(struct compound_state *cs, struct svc_req *req)
{
int i, rpcflavor, access;
struct secinfo *secp;
char buf[MAXHOST + 1];
int anon_res = 0, nfsflavor;
struct exportinfo *exi;
cred_t *cr;
caddr_t principal;
uid_t uid;
gid_t gid;
uint_t ngids;
gid_t *gids;
exi = cs->exi;
cr = cs->cr;
principal = cs->principal;
nfsflavor = cs->nfsflavor;
ASSERT(cr != NULL);
rpcflavor = req->rq_cred.oa_flavor;
cs->access &= ~CS_ACCESS_LIMITED;
if (nfs_portmon) {
struct sockaddr *ca;
ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
if (ca == NULL)
return (0);
if ((ca->sa_family == AF_INET &&
ntohs(((struct sockaddr_in *)ca)->sin_port) >=
IPPORT_RESERVED) ||
(ca->sa_family == AF_INET6 &&
ntohs(((struct sockaddr_in6 *)ca)->sin6_port) >=
IPPORT_RESERVED)) {
cmn_err(CE_NOTE,
"nfs_server: client %s%ssent NFSv4 request from "
"unprivileged port",
client_name(req), client_addr(req, buf));
return (0);
}
}
access = nfsauth4_access(cs->exi, cs->vp, req, cr, &uid, &gid, &ngids,
&gids);
if (access & NFSAUTH_WRONGSEC)
return (-2);
if (access & NFSAUTH_DROP)
return (-1);
if (access & NFSAUTH_DENIED) {
if (exi->exi_export.ex_seccnt > 0)
return (0);
} else if (access & NFSAUTH_LIMITED) {
cs->access |= CS_ACCESS_LIMITED;
} else if (access & NFSAUTH_MAPNONE) {
rpcflavor = AUTH_NONE;
}
if (rpcflavor != AUTH_SYS)
kmem_free(gids, ngids * sizeof (gid_t));
switch (rpcflavor) {
case AUTH_NONE:
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
break;
case AUTH_UNIX:
if (crgetuid(cr) == 0 && !(access & NFSAUTH_UIDMAP)) {
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
} else if (crgetuid(cr) == 0 && access & NFSAUTH_ROOT) {
secp = NULL;
for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
struct secinfo *sptr;
sptr = &exi->exi_export.ex_secinfo[i];
if (sptr->s_secinfo.sc_nfsnum == nfsflavor) {
secp = &exi->exi_export.ex_secinfo[i];
break;
}
}
if (secp != NULL) {
(void) crsetugid(cr, secp->s_rootid,
secp->s_rootid);
(void) crsetgroups(cr, 0, NULL);
}
} else if (crgetuid(cr) != uid || crgetgid(cr) != gid) {
if (crsetugid(cr, uid, gid) != 0)
anon_res = crsetugid(cr,
exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
} if (access & NFSAUTH_GROUPS) {
(void) crsetgroups(cr, ngids, gids);
}
kmem_free(gids, ngids * sizeof (gid_t));
break;
default:
secp = NULL;
for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
if (exi->exi_export.ex_secinfo[i].s_secinfo.sc_nfsnum ==
nfsflavor) {
secp = &exi->exi_export.ex_secinfo[i];
break;
}
}
if (!secp) {
cmn_err(CE_NOTE, "nfs_server: client %s%shad "
"no secinfo data for flavor %d",
client_name(req), client_addr(req, buf),
nfsflavor);
return (0);
}
if (!checkwin(rpcflavor, secp->s_window, req)) {
cmn_err(CE_NOTE,
"nfs_server: client %s%sused invalid "
"auth window value",
client_name(req), client_addr(req, buf));
return (0);
}
if (principal && sec_svc_inrootlist(rpcflavor, principal,
secp->s_rootcnt, secp->s_rootnames)) {
if (crgetuid(cr) == 0 && secp->s_rootid == 0)
return (1);
(void) crsetugid(cr, secp->s_rootid, secp->s_rootid);
(void) crsetgroups(cr, 0, NULL);
return (1);
}
if (crgetuid(cr) != 0 &&
(crgetuid(cr) != UID_NOBODY || crgetgid(cr) != GID_NOBODY))
return (1);
anon_res = crsetugid(cr, exi->exi_export.ex_anon,
exi->exi_export.ex_anon);
(void) crsetgroups(cr, 0, NULL);
break;
}
if (anon_res != 0) {
cmn_err(CE_NOTE,
"nfs_server: client %s%ssent wrong "
"authentication for %s",
client_name(req), client_addr(req, buf),
exi->exi_export.ex_path ?
exi->exi_export.ex_path : "?");
return (0);
}
return (1);
}
static char *
client_name(struct svc_req *req)
{
char *hostname = NULL;
if (req->rq_cred.oa_flavor == AUTH_UNIX) {
hostname = ((struct authunix_parms *)
req->rq_clntcred)->aup_machname;
}
if (hostname == NULL)
hostname = "";
return (hostname);
}
static char *
client_addr(struct svc_req *req, char *buf)
{
struct sockaddr *ca;
uchar_t *b;
char *frontspace = "";
if (req->rq_cred.oa_flavor == AUTH_UNIX)
frontspace = " ";
ca = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
if (ca->sa_family == AF_INET) {
b = (uchar_t *)&((struct sockaddr_in *)ca)->sin_addr;
(void) sprintf(buf, "%s(%d.%d.%d.%d) ", frontspace,
b[0] & 0xFF, b[1] & 0xFF, b[2] & 0xFF, b[3] & 0xFF);
} else if (ca->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)ca;
(void) kinet_ntop6((uchar_t *)&sin6->sin6_addr,
buf, INET6_ADDRSTRLEN);
} else {
(void) sprintf(buf, frontspace);
}
return (buf);
}
void
nfs_srvinit(void)
{
rw_init(&nfssrv_globals_rwl, NULL, RW_DEFAULT, NULL);
list_create(&nfssrv_globals_list, sizeof (nfs_globals_t),
offsetof(nfs_globals_t, nfs_g_link));
tsd_create(&nfs_server_tsd_key, NULL);
nfs_exportinit();
rfs_srvrinit();
rfs3_srvrinit();
rfs4_srvrinit();
nfsauth_init();
zone_key_create(&nfssrv_zone_key, nfs_server_zone_init,
nfs_server_zone_shutdown, nfs_server_zone_fini);
}
void
nfs_srvfini(void)
{
(void) zone_key_delete(nfssrv_zone_key);
nfsauth_fini();
rfs4_srvrfini();
rfs3_srvrfini();
rfs_srvrfini();
nfs_exportfini();
tsd_destroy(&nfs_server_tsd_key);
list_destroy(&nfssrv_globals_list);
rw_destroy(&nfssrv_globals_rwl);
}
static void *
nfs_server_zone_init(zoneid_t zoneid)
{
nfs_globals_t *ng;
ng = kmem_zalloc(sizeof (*ng), KM_SLEEP);
ng->nfs_versmin = NFS_SRV_VERSMIN_DEFAULT;
ng->nfs_versmax = NFS_SRV_VERSMAX_DEFAULT;
ng->nfs_server_upordown = NFS_SERVER_STOPPED;
mutex_init(&ng->nfs_server_upordown_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&ng->nfs_server_upordown_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&ng->rdma_wait_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&ng->rdma_wait_cv, NULL, CV_DEFAULT, NULL);
ng->nfs_zoneid = zoneid;
nfs_export_zone_init(ng);
rfs_stat_zone_init(ng);
rfs_srv_zone_init(ng);
rfs3_srv_zone_init(ng);
rfs4_srv_zone_init(ng);
nfsauth_zone_init(ng);
rw_enter(&nfssrv_globals_rwl, RW_WRITER);
list_insert_tail(&nfssrv_globals_list, ng);
rw_exit(&nfssrv_globals_rwl);
return (ng);
}
static void
nfs_server_zone_shutdown(zoneid_t zoneid, void *data)
{
nfs_globals_t *ng;
ng = (nfs_globals_t *)data;
nfsauth_zone_shutdown(ng);
nfs_export_zone_shutdown(ng);
}
static void
nfs_server_zone_fini(zoneid_t zoneid, void *data)
{
nfs_globals_t *ng;
ng = (nfs_globals_t *)data;
rw_enter(&nfssrv_globals_rwl, RW_WRITER);
list_remove(&nfssrv_globals_list, ng);
rw_exit(&nfssrv_globals_rwl);
nfsauth_zone_fini(ng);
rfs4_srv_zone_fini(ng);
rfs3_srv_zone_fini(ng);
rfs_srv_zone_fini(ng);
rfs_stat_zone_fini(ng);
nfs_export_zone_fini(ng);
mutex_destroy(&ng->nfs_server_upordown_lock);
cv_destroy(&ng->nfs_server_upordown_cv);
mutex_destroy(&ng->rdma_wait_mutex);
cv_destroy(&ng->rdma_wait_cv);
kmem_free(ng, sizeof (*ng));
}
void
mblk_to_iov(mblk_t *m, int cnt, struct iovec *iovp)
{
while (m != NULL && cnt-- > 0) {
iovp->iov_base = (caddr_t)m->b_rptr;
iovp->iov_len = (m->b_wptr - m->b_rptr);
iovp++;
m = m->b_cont;
}
}
int
rfs_publicfh_mclookup(char *p, vnode_t *dvp, cred_t *cr, vnode_t **vpp,
struct exportinfo **exi, struct sec_ol *sec)
{
int pathflag;
vnode_t *mc_dvp = NULL;
vnode_t *realvp;
int error;
*exi = NULL;
if ((pathflag = MCLpath(&p)) == -1)
return (EIO);
if (pathflag == SECURITY_QUERY) {
if ((sec->sec_index = (uint_t)(*p)) > 0) {
sec->sec_flags |= SEC_QUERY;
p++;
if ((pathflag = MCLpath(&p)) == -1)
return (EIO);
} else {
cmn_err(CE_NOTE,
"nfs_server: invalid security index %d, "
"violating WebNFS SNEGO protocol.", sec->sec_index);
return (EIO);
}
}
if (p[0] == '\0') {
error = ENOENT;
goto publicfh_done;
}
error = rfs_pathname(p, &mc_dvp, vpp, dvp, cr, pathflag);
if (error == EINVAL) {
error = rfs_pathname(p, NULL, vpp, dvp, cr, pathflag);
if (!error) {
ASSERT(*vpp != NULL);
if ((*vpp)->v_type == VDIR) {
VN_HOLD(*vpp);
mc_dvp = *vpp;
} else {
VN_RELE(*vpp);
error = EINVAL;
}
}
}
if (error)
goto publicfh_done;
if (*vpp == NULL) {
error = ENOENT;
goto publicfh_done;
}
ASSERT(mc_dvp != NULL);
ASSERT(*vpp != NULL);
if ((*vpp)->v_type == VDIR) {
do {
(void) VOP_ACCESS(*vpp, 0, 0, cr, NULL);
if (vn_mountedvfs(*vpp) != NULL) {
error = traverse(vpp);
if (error) {
VN_RELE(*vpp);
goto publicfh_done;
}
}
if (VOP_REALVP(*vpp, &realvp, NULL) == 0 &&
realvp != *vpp) {
VN_HOLD(realvp);
VN_RELE(*vpp);
*vpp = realvp;
} else {
break;
}
} while (TRUE);
VN_RELE(mc_dvp);
mc_dvp = NULL;
} else {
if (vn_mountedvfs(mc_dvp) != NULL) {
error = traverse(&mc_dvp);
if (error) {
VN_RELE(*vpp);
goto publicfh_done;
}
}
if (VOP_REALVP(mc_dvp, &realvp, NULL) == 0 &&
realvp != mc_dvp) {
VN_HOLD(realvp);
VN_RELE(mc_dvp);
mc_dvp = realvp;
}
}
error = nfs_check_vpexi(mc_dvp, *vpp, kcred, exi);
if (error != 0) {
VN_RELE(*vpp);
goto publicfh_done;
}
if (PSEUDO(*exi)) {
error = ENOENT;
VN_RELE(*vpp);
goto publicfh_done;
}
if (((*exi)->exi_export.ex_flags & EX_INDEX) &&
((*vpp)->v_type == VDIR) && (pathflag == URLPATH)) {
vnode_t *tvp, *tmc_dvp;
tmc_dvp = mc_dvp;
mc_dvp = tvp = *vpp;
error = rfs_pathname((*exi)->exi_export.ex_index, NULL, vpp,
mc_dvp, cr, NATIVEPATH);
if (error == ENOENT) {
*vpp = tvp;
mc_dvp = tmc_dvp;
error = 0;
} else {
if (tmc_dvp)
VN_RELE(tmc_dvp);
if (error)
goto publicfh_done;
ASSERT(*exi != NULL);
exi_rele(*exi);
*exi = NULL;
error = nfs_check_vpexi(mc_dvp, *vpp, kcred, exi);
if (error != 0) {
VN_RELE(*vpp);
goto publicfh_done;
}
}
}
publicfh_done:
if (mc_dvp)
VN_RELE(mc_dvp);
return (error);
}
int
rfs_pathname(
char *path,
vnode_t **dirvpp,
vnode_t **compvpp,
vnode_t *startdvp,
cred_t *cr,
int pathflag)
{
char namebuf[TYPICALMAXPATHLEN];
struct pathname pn;
int error;
ASSERT3U(crgetzoneid(cr), ==, curzone->zone_id);
if (*path == '/') {
while (*path == '/')
path++;
startdvp = ZONE_ROOTVP();
}
error = pn_get_buf(path, UIO_SYSSPACE, &pn, namebuf, sizeof (namebuf));
if (error == 0) {
if (pn.pn_pathlen != 0 && pathflag == URLPATH) {
URLparse(pn.pn_path);
if ((pn.pn_pathlen = strlen(pn.pn_path)) == 0)
return (ENOENT);
}
VN_HOLD(startdvp);
error = lookuppnvp(&pn, NULL, NO_FOLLOW, dirvpp, compvpp,
ZONE_ROOTVP(), startdvp, cr);
}
if (error == ENAMETOOLONG) {
error = pn_get(path, UIO_SYSSPACE, &pn);
if (error != 0)
return (error);
if (pn.pn_pathlen != 0 && pathflag == URLPATH) {
URLparse(pn.pn_path);
if ((pn.pn_pathlen = strlen(pn.pn_path)) == 0) {
pn_free(&pn);
return (ENOENT);
}
}
VN_HOLD(startdvp);
error = lookuppnvp(&pn, NULL, NO_FOLLOW, dirvpp, compvpp,
ZONE_ROOTVP(), startdvp, cr);
pn_free(&pn);
}
return (error);
}
static int
MCLpath(char **path)
{
unsigned char c = (unsigned char)**path;
if (c >= 0x20 && c <= 0x7E)
return (URLPATH);
switch (c) {
case 0x80:
(*path)++;
return (NATIVEPATH);
case 0x81:
(*path)++;
return (SECURITY_QUERY);
default:
return (-1);
}
}
#define fromhex(c) ((c >= '0' && c <= '9') ? (c - '0') : \
((c >= 'A' && c <= 'F') ? (c - 'A' + 10) :\
((c >= 'a' && c <= 'f') ? (c - 'a' + 10) : 0)))
static void
URLparse(char *str)
{
char *p, *q;
p = q = str;
while (*p) {
*q = *p;
if (*p++ == '%') {
if (*p) {
*q = fromhex(*p) * 16;
p++;
if (*p) {
*q += fromhex(*p);
p++;
}
}
}
q++;
}
*q = '\0';
}
int
nfs_check_vpexi(vnode_t *mc_dvp, vnode_t *vp, cred_t *cr,
struct exportinfo **exi)
{
int walk;
int error = 0;
*exi = nfs_vptoexi(mc_dvp, vp, cr, &walk, NULL, FALSE);
if (*exi == NULL)
error = EACCES;
else {
if ((*exi)->exi_export.ex_flags & EX_NOSUB && walk > 0)
error = EACCES;
}
return (error);
}
ts_label_t *
nfs_getflabel(vnode_t *vp, struct exportinfo *exi)
{
zone_t *zone;
ts_label_t *zone_label;
char *path;
mutex_enter(&vp->v_lock);
if (vp->v_path != vn_vpath_empty) {
zone = zone_find_by_any_path(vp->v_path, B_FALSE);
mutex_exit(&vp->v_lock);
} else {
path = exi->exi_export.ex_path;
if (path == NULL) {
mutex_exit(&vp->v_lock);
return (NULL);
}
zone = zone_find_by_any_path(path, B_FALSE);
mutex_exit(&vp->v_lock);
}
zone_label = zone->zone_slabel;
label_hold(zone_label);
zone_rele(zone);
return (zone_label);
}
boolean_t
do_rfs_label_check(bslabel_t *clabel, vnode_t *vp, int flag,
struct exportinfo *exi)
{
bslabel_t *slabel;
ts_label_t *tslabel;
boolean_t result;
if ((tslabel = nfs_getflabel(vp, exi)) == NULL) {
return (B_FALSE);
}
slabel = label2bslabel(tslabel);
DTRACE_PROBE4(tx__rfs__log__info__labelcheck, char *,
"comparing server's file label(1) with client label(2) (vp(3))",
bslabel_t *, slabel, bslabel_t *, clabel, vnode_t *, vp);
if (flag == EQUALITY_CHECK)
result = blequal(clabel, slabel);
else
result = bldominates(clabel, slabel);
label_rele(tslabel);
return (result);
}
void
rfs_free_xuio(void *free_arg)
{
uint_t ref;
nfs_xuio_t *nfsuiop = (nfs_xuio_t *)free_arg;
ref = atomic_dec_uint_nv(&nfsuiop->nu_ref);
if (ref != 0)
return;
if (((uio_t *)nfsuiop)->uio_extflg & UIO_XUIO) {
(void) VOP_RETZCBUF(nfsuiop->nu_vp, (xuio_t *)free_arg, NULL,
NULL);
VN_RELE(nfsuiop->nu_vp);
}
kmem_cache_free(nfs_xuio_cache, free_arg);
}
xuio_t *
rfs_setup_xuio(vnode_t *vp)
{
nfs_xuio_t *nfsuiop;
nfsuiop = kmem_cache_alloc(nfs_xuio_cache, KM_SLEEP);
bzero(nfsuiop, sizeof (nfs_xuio_t));
nfsuiop->nu_vp = vp;
nfsuiop->nu_ref = 1;
nfsuiop->nu_frtn.free_func = rfs_free_xuio;
nfsuiop->nu_frtn.free_arg = (char *)nfsuiop;
nfsuiop->nu_uio.xu_type = UIOTYPE_ZEROCOPY;
return (&nfsuiop->nu_uio);
}
mblk_t *
uio_to_mblk(uio_t *uiop)
{
struct iovec *iovp;
int i;
mblk_t *mp, *mp1;
nfs_xuio_t *nfsuiop = (nfs_xuio_t *)uiop;
if (uiop->uio_iovcnt == 0)
return (NULL);
iovp = uiop->uio_iov;
mp = mp1 = esballoca((uchar_t *)iovp->iov_base, iovp->iov_len,
BPRI_MED, &nfsuiop->nu_frtn);
ASSERT(mp != NULL);
mp->b_wptr += iovp->iov_len;
mp->b_datap->db_type = M_DATA;
for (i = 1; i < uiop->uio_iovcnt; i++) {
iovp = (uiop->uio_iov + i);
mp1->b_cont = esballoca(
(uchar_t *)iovp->iov_base, iovp->iov_len, BPRI_MED,
&nfsuiop->nu_frtn);
mp1 = mp1->b_cont;
ASSERT(mp1 != NULL);
mp1->b_wptr += iovp->iov_len;
mp1->b_datap->db_type = M_DATA;
}
nfsuiop->nu_ref = uiop->uio_iovcnt;
return (mp);
}
mblk_t *
rfs_read_alloc(uint_t len, struct iovec **iov, int *iovcnt)
{
struct iovec *iovarr;
mblk_t *mp, **mpp = ∓
size_t mpsize;
uint_t remain = len;
int i, err = 0;
*iovcnt = howmany(len, kmem_max_cached);
iovarr = kmem_alloc(*iovcnt * sizeof (struct iovec), KM_SLEEP);
*iov = iovarr;
for (i = 0; i < *iovcnt; remain -= mpsize, i++) {
ASSERT(remain <= len);
ASSERT(kmem_max_cached % BYTES_PER_XDR_UNIT == 0);
mpsize = MIN(kmem_max_cached, remain);
*mpp = allocb_wait(RNDUP(mpsize), BPRI_MED, STR_NOSIG, &err);
ASSERT(*mpp != NULL);
ASSERT(err == 0);
iovarr[i].iov_base = (caddr_t)(*mpp)->b_rptr;
iovarr[i].iov_len = mpsize;
mpp = &(*mpp)->b_cont;
}
return (mp);
}
void
rfs_rndup_mblks(mblk_t *mp, uint_t len, int buf_loaned)
{
int i;
int alloc_err = 0;
mblk_t *rmp;
uint_t mpsize, remainder;
remainder = P2NPHASE(len, BYTES_PER_XDR_UNIT);
if (!buf_loaned) {
while ((mpsize = MBLKSIZE(mp)) < len) {
ASSERT(mpsize % BYTES_PER_XDR_UNIT == 0);
mp->b_wptr += mpsize;
len -= mpsize;
mp = mp->b_cont;
ASSERT(mp != NULL);
}
ASSERT(len + remainder <= mpsize);
mp->b_wptr += len;
for (i = 0; i < remainder; i++)
*mp->b_wptr++ = '\0';
return;
}
if (remainder == 0)
return;
while (mp->b_cont != NULL)
mp = mp->b_cont;
rmp = allocb_wait(remainder, BPRI_MED, STR_NOSIG, &alloc_err);
ASSERT(rmp != NULL);
ASSERT(alloc_err == 0);
for (i = 0; i < remainder; i++)
*rmp->b_wptr++ = '\0';
rmp->b_datap->db_type = M_DATA;
mp->b_cont = rmp;
}