#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/cred.h>
#include <sys/time.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/vfs_opreg.h>
#include <sys/file.h>
#include <sys/filio.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/mman.h>
#include <sys/pathname.h>
#include <sys/dirent.h>
#include <sys/debug.h>
#include <sys/vmsystm.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/swap.h>
#include <sys/errno.h>
#include <sys/strsubr.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/mount.h>
#include <sys/cmn_err.h>
#include <sys/pathconf.h>
#include <sys/utsname.h>
#include <sys/dnlc.h>
#include <sys/acl.h>
#include <sys/systeminfo.h>
#include <sys/policy.h>
#include <sys/sdt.h>
#include <sys/list.h>
#include <sys/stat.h>
#include <sys/mntent.h>
#include <sys/priv.h>
#include <rpc/types.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <nfs/nfs_acl.h>
#include <nfs/lm.h>
#include <nfs/nfs4.h>
#include <nfs/nfs4_kprot.h>
#include <nfs/rnode4.h>
#include <nfs/nfs4_clnt.h>
#include <nfs/nfsid_map.h>
#include <nfs/nfs4_idmap_impl.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/pvn.h>
#include <vm/seg.h>
#include <vm/seg_map.h>
#include <vm/seg_kpm.h>
#include <vm/seg_vn.h>
#include <fs/fs_subr.h>
#include <sys/ddi.h>
#include <sys/int_fmtio.h>
#include <sys/sunddi.h>
#include <sys/priv_names.h>
extern zone_key_t nfs4clnt_zone_key;
extern zone_key_t nfsidmap_zone_key;
static int nfs4_trigger_thread_timer = 20;
static uint_t nfs4_trigger_mount_to = 240;
typedef struct nfs4_trigger_globals {
kmutex_t ntg_forest_lock;
uint_t ntg_mount_to;
int ntg_thread_started;
nfs4_ephemeral_tree_t *ntg_forest;
} nfs4_trigger_globals_t;
kmutex_t nfs4_ephemeral_thread_lock;
zone_key_t nfs4_ephemeral_key = ZONE_KEY_UNINITIALIZED;
static void nfs4_ephemeral_start_harvester(nfs4_trigger_globals_t *);
typedef struct ephemeral_servinfo {
char *esi_hostname;
char *esi_netname;
char *esi_path;
int esi_path_len;
int esi_mount_flags;
struct netbuf *esi_addr;
struct netbuf *esi_syncaddr;
struct knetconfig *esi_knconf;
} ephemeral_servinfo_t;
typedef struct domount_args {
ephemeral_servinfo_t *dma_esi;
char *dma_hostlist;
struct nfs_args *dma_nargs;
} domount_args_t;
static int nfs4_trigger_open(vnode_t **, int, cred_t *, caller_context_t *);
static int nfs4_trigger_getattr(vnode_t *, struct vattr *, int, cred_t *,
caller_context_t *);
static int nfs4_trigger_setattr(vnode_t *, struct vattr *, int, cred_t *,
caller_context_t *);
static int nfs4_trigger_access(vnode_t *, int, int, cred_t *,
caller_context_t *);
static int nfs4_trigger_readlink(vnode_t *, struct uio *, cred_t *,
caller_context_t *);
static int nfs4_trigger_lookup(vnode_t *, char *, vnode_t **,
struct pathname *, int, vnode_t *, cred_t *, caller_context_t *,
int *, pathname_t *);
static int nfs4_trigger_create(vnode_t *, char *, struct vattr *,
enum vcexcl, int, vnode_t **, cred_t *, int, caller_context_t *,
vsecattr_t *);
static int nfs4_trigger_remove(vnode_t *, char *, cred_t *, caller_context_t *,
int);
static int nfs4_trigger_link(vnode_t *, vnode_t *, char *, cred_t *,
caller_context_t *, int);
static int nfs4_trigger_rename(vnode_t *, char *, vnode_t *, char *,
cred_t *, caller_context_t *, int);
static int nfs4_trigger_mkdir(vnode_t *, char *, struct vattr *,
vnode_t **, cred_t *, caller_context_t *, int, vsecattr_t *vsecp);
static int nfs4_trigger_rmdir(vnode_t *, char *, vnode_t *, cred_t *,
caller_context_t *, int);
static int nfs4_trigger_symlink(vnode_t *, char *, struct vattr *, char *,
cred_t *, caller_context_t *, int);
static int nfs4_trigger_cmp(vnode_t *, vnode_t *, caller_context_t *);
extern int nfs4_getattr(vnode_t *, struct vattr *, int, cred_t *,
caller_context_t *);
extern void nfs4_inactive(vnode_t *, cred_t *, caller_context_t *);
extern int nfs4_rwlock(vnode_t *, int, caller_context_t *);
extern void nfs4_rwunlock(vnode_t *, int, caller_context_t *);
extern int nfs4_lookup(vnode_t *, char *, vnode_t **,
struct pathname *, int, vnode_t *, cred_t *,
caller_context_t *, int *, pathname_t *);
extern int nfs4_pathconf(vnode_t *, int, ulong_t *, cred_t *,
caller_context_t *);
extern int nfs4_getsecattr(vnode_t *, vsecattr_t *, int, cred_t *,
caller_context_t *);
extern int nfs4_fid(vnode_t *, fid_t *, caller_context_t *);
extern int nfs4_realvp(vnode_t *, vnode_t **, caller_context_t *);
static int nfs4_trigger_mount(vnode_t *, cred_t *, vnode_t **);
static int nfs4_trigger_domount(vnode_t *, domount_args_t *, vfs_t **,
cred_t *, vnode_t **);
static int nfs4_trigger_domount_args_create(vnode_t *, cred_t *,
domount_args_t **dmap);
static void nfs4_trigger_domount_args_destroy(domount_args_t *dma,
vnode_t *vp);
static ephemeral_servinfo_t *nfs4_trigger_esi_create(vnode_t *, servinfo4_t *,
cred_t *);
static void nfs4_trigger_esi_destroy(ephemeral_servinfo_t *, vnode_t *);
static ephemeral_servinfo_t *nfs4_trigger_esi_create_mirrormount(vnode_t *,
servinfo4_t *);
static ephemeral_servinfo_t *nfs4_trigger_esi_create_referral(vnode_t *,
cred_t *);
static struct nfs_args *nfs4_trigger_nargs_create(mntinfo4_t *, servinfo4_t *,
ephemeral_servinfo_t *);
static void nfs4_trigger_nargs_destroy(struct nfs_args *);
static char *nfs4_trigger_create_mntopts(vfs_t *);
static void nfs4_trigger_destroy_mntopts(char *);
static int nfs4_trigger_add_mntopt(char *, char *, vfs_t *);
static enum clnt_stat nfs4_trigger_ping_server(servinfo4_t *, int);
static enum clnt_stat nfs4_ping_server_common(struct knetconfig *,
struct netbuf *, int);
extern int umount2_engine(vfs_t *, int, cred_t *, int);
vnodeops_t *nfs4_trigger_vnodeops;
const fs_operation_def_t nfs4_trigger_vnodeops_template[] = {
VOPNAME_OPEN, { .vop_open = nfs4_trigger_open },
VOPNAME_GETATTR, { .vop_getattr = nfs4_trigger_getattr },
VOPNAME_SETATTR, { .vop_setattr = nfs4_trigger_setattr },
VOPNAME_ACCESS, { .vop_access = nfs4_trigger_access },
VOPNAME_LOOKUP, { .vop_lookup = nfs4_trigger_lookup },
VOPNAME_CREATE, { .vop_create = nfs4_trigger_create },
VOPNAME_REMOVE, { .vop_remove = nfs4_trigger_remove },
VOPNAME_LINK, { .vop_link = nfs4_trigger_link },
VOPNAME_RENAME, { .vop_rename = nfs4_trigger_rename },
VOPNAME_MKDIR, { .vop_mkdir = nfs4_trigger_mkdir },
VOPNAME_RMDIR, { .vop_rmdir = nfs4_trigger_rmdir },
VOPNAME_SYMLINK, { .vop_symlink = nfs4_trigger_symlink },
VOPNAME_READLINK, { .vop_readlink = nfs4_trigger_readlink },
VOPNAME_INACTIVE, { .vop_inactive = nfs4_inactive },
VOPNAME_FID, { .vop_fid = nfs4_fid },
VOPNAME_RWLOCK, { .vop_rwlock = nfs4_rwlock },
VOPNAME_RWUNLOCK, { .vop_rwunlock = nfs4_rwunlock },
VOPNAME_REALVP, { .vop_realvp = nfs4_realvp },
VOPNAME_GETSECATTR, { .vop_getsecattr = nfs4_getsecattr },
VOPNAME_PATHCONF, { .vop_pathconf = nfs4_pathconf },
VOPNAME_FRLOCK, { .error = fs_error },
VOPNAME_DISPOSE, { .error = fs_error },
VOPNAME_SHRLOCK, { .error = fs_error },
VOPNAME_VNEVENT, { .vop_vnevent = fs_vnevent_support },
NULL, NULL
};
static void
nfs4_ephemeral_tree_incr(nfs4_ephemeral_tree_t *net)
{
ASSERT(mutex_owned(&net->net_cnt_lock));
net->net_refcnt++;
ASSERT(net->net_refcnt != 0);
}
static void
nfs4_ephemeral_tree_hold(nfs4_ephemeral_tree_t *net)
{
mutex_enter(&net->net_cnt_lock);
nfs4_ephemeral_tree_incr(net);
mutex_exit(&net->net_cnt_lock);
}
static void
nfs4_ephemeral_tree_decr(nfs4_ephemeral_tree_t *net)
{
ASSERT(mutex_owned(&net->net_cnt_lock));
ASSERT(net->net_refcnt != 0);
net->net_refcnt--;
}
static void
nfs4_ephemeral_tree_rele(nfs4_ephemeral_tree_t *net)
{
mutex_enter(&net->net_cnt_lock);
nfs4_ephemeral_tree_decr(net);
mutex_exit(&net->net_cnt_lock);
}
static int
nfs4_trigger_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
{
int error;
vnode_t *newvp;
error = nfs4_trigger_mount(*vpp, cr, &newvp);
if (error)
return (error);
VN_RELE(*vpp);
*vpp = newvp;
return (VOP_OPEN(vpp, flag, cr, ct));
}
void
nfs4_fake_attrs(vnode_t *vp, struct vattr *vap)
{
uint_t mask;
timespec_t now;
mask = vap->va_mask;
bzero(vap, sizeof (struct vattr));
vap->va_mask = mask;
vap->va_uid = 0;
vap->va_gid = 0;
vap->va_nlink = 1;
vap->va_size = 1;
gethrestime(&now);
vap->va_atime = now;
vap->va_mtime = now;
vap->va_ctime = now;
vap->va_type = VDIR;
vap->va_mode = 0555;
vap->va_fsid = vp->v_vfsp->vfs_dev;
vap->va_rdev = 0;
vap->va_blksize = MAXBSIZE;
vap->va_nblocks = 1;
vap->va_seq = 0;
}
static int
nfs4_trigger_getattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
int error;
if (flags & ATTR_TRIGGER) {
vnode_t *newvp;
error = nfs4_trigger_mount(vp, cr, &newvp);
if (error)
return (error);
error = VOP_GETATTR(newvp, vap, flags, cr, ct);
VN_RELE(newvp);
} else if (RP_ISSTUB_MIRRORMOUNT(VTOR4(vp))) {
error = nfs4_getattr(vp, vap, flags, cr, ct);
} else if (RP_ISSTUB_REFERRAL(VTOR4(vp))) {
nfs4_fake_attrs(vp, vap);
error = 0;
}
return (error);
}
static int
nfs4_trigger_setattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
int error;
vnode_t *newvp;
error = nfs4_trigger_mount(vp, cr, &newvp);
if (error)
return (error);
error = VOP_SETATTR(newvp, vap, flags, cr, ct);
VN_RELE(newvp);
return (error);
}
static int
nfs4_trigger_access(vnode_t *vp, int mode, int flags, cred_t *cr,
caller_context_t *ct)
{
int error;
vnode_t *newvp;
error = nfs4_trigger_mount(vp, cr, &newvp);
if (error)
return (error);
error = VOP_ACCESS(newvp, mode, flags, cr, ct);
VN_RELE(newvp);
return (error);
}
static int
nfs4_trigger_lookup(vnode_t *dvp, char *nm, vnode_t **vpp,
struct pathname *pnp, int flags, vnode_t *rdir, cred_t *cr,
caller_context_t *ct, int *deflags, pathname_t *rpnp)
{
int error;
vnode_t *newdvp;
rnode4_t *drp = VTOR4(dvp);
ASSERT(RP_ISSTUB(drp));
if (strcmp(nm, "..") == 0)
if (RP_ISSTUB_MIRRORMOUNT(drp)) {
return (nfs4_lookup(dvp, nm, vpp, pnp, flags, rdir, cr,
ct, deflags, rpnp));
} else if (RP_ISSTUB_REFERRAL(drp)) {
return (vtodv(dvp, vpp, cr, TRUE));
}
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_LOOKUP(newdvp, nm, vpp, pnp, flags, rdir, cr, ct,
deflags, rpnp);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_create(vnode_t *dvp, char *nm, struct vattr *va,
enum vcexcl exclusive, int mode, vnode_t **vpp, cred_t *cr,
int flags, caller_context_t *ct, vsecattr_t *vsecp)
{
int error;
vnode_t *newdvp;
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_CREATE(newdvp, nm, va, exclusive, mode, vpp, cr,
flags, ct, vsecp);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
int flags)
{
int error;
vnode_t *newdvp;
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_REMOVE(newdvp, nm, cr, ct, flags);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_link(vnode_t *tdvp, vnode_t *svp, char *tnm, cred_t *cr,
caller_context_t *ct, int flags)
{
int error;
vnode_t *newtdvp;
error = nfs4_trigger_mount(tdvp, cr, &newtdvp);
if (error)
return (error);
error = VOP_LINK(newtdvp, svp, tnm, cr, ct, flags);
VN_RELE(newtdvp);
return (error);
}
static int
nfs4_trigger_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
cred_t *cr, caller_context_t *ct, int flags)
{
int error;
vnode_t *newsdvp;
rnode4_t *tdrp = VTOR4(tdvp);
if (RP_ISSTUB(tdrp) && !VN_CMP(sdvp, tdvp))
return (EXDEV);
error = nfs4_trigger_mount(sdvp, cr, &newsdvp);
if (error)
return (error);
error = VOP_RENAME(newsdvp, snm, tdvp, tnm, cr, ct, flags);
VN_RELE(newsdvp);
return (error);
}
static int
nfs4_trigger_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
{
int error;
vnode_t *newdvp;
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_MKDIR(newdvp, nm, va, vpp, cr, ct, flags, vsecp);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
caller_context_t *ct, int flags)
{
int error;
vnode_t *newdvp;
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_RMDIR(newdvp, nm, cdir, cr, ct, flags);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_symlink(vnode_t *dvp, char *lnm, struct vattr *tva, char *tnm,
cred_t *cr, caller_context_t *ct, int flags)
{
int error;
vnode_t *newdvp;
error = nfs4_trigger_mount(dvp, cr, &newdvp);
if (error)
return (error);
error = VOP_SYMLINK(newdvp, lnm, tva, tnm, cr, ct, flags);
VN_RELE(newdvp);
return (error);
}
static int
nfs4_trigger_readlink(vnode_t *vp, struct uio *uiop, cred_t *cr,
caller_context_t *ct)
{
int error;
vnode_t *newvp;
error = nfs4_trigger_mount(vp, cr, &newvp);
if (error)
return (error);
error = VOP_READLINK(newvp, uiop, cr, ct);
VN_RELE(newvp);
return (error);
}
static int
nfs4_trigger_mounted_already(vnode_t *vp, vnode_t **newvpp,
bool_t *was_mounted, vfs_t **vfsp)
{
int error;
mntinfo4_t *mi = VTOMI4(vp);
*was_mounted = FALSE;
error = vn_vfsrlock_wait(vp);
if (error)
return (error);
*vfsp = vn_mountedvfs(vp);
if (*vfsp != NULL) {
error = VFS_ROOT(*vfsp, newvpp);
if (!error) {
mutex_enter(&mi->mi_lock);
if (mi->mi_ephemeral)
mi->mi_ephemeral->ne_ref_time =
gethrestime_sec();
mutex_exit(&mi->mi_lock);
*was_mounted = TRUE;
}
}
vn_vfsunlock(vp);
return (0);
}
static int
nfs4_trigger_mount(vnode_t *vp, cred_t *cr, vnode_t **newvpp)
{
int error;
vfs_t *vfsp;
rnode4_t *rp = VTOR4(vp);
mntinfo4_t *mi = VTOMI4(vp);
domount_args_t *dma;
nfs4_ephemeral_tree_t *net;
bool_t must_unlock = FALSE;
bool_t is_building = FALSE;
bool_t was_mounted = FALSE;
cred_t *mcred = NULL;
nfs4_trigger_globals_t *ntg;
zone_t *zone = curproc->p_zone;
ASSERT(RP_ISSTUB(rp));
*newvpp = NULL;
error = nfs4_trigger_mounted_already(vp, newvpp,
&was_mounted, &vfsp);
if (error || was_mounted)
goto done;
ntg = zone_getspecific(nfs4_ephemeral_key, zone);
ASSERT(ntg != NULL);
mutex_enter(&mi->mi_lock);
if (mi->mi_ephemeral_tree == NULL) {
net = kmem_zalloc(sizeof (*net), KM_SLEEP);
mutex_init(&net->net_tree_lock, NULL, MUTEX_DEFAULT, NULL);
mutex_init(&net->net_cnt_lock, NULL, MUTEX_DEFAULT, NULL);
net->net_refcnt = 1;
net->net_status = NFS4_EPHEMERAL_TREE_BUILDING;
is_building = TRUE;
mutex_enter(&ntg->ntg_forest_lock);
if (ntg->ntg_forest != NULL)
net->net_next = ntg->ntg_forest;
ntg->ntg_forest = net;
mutex_exit(&ntg->ntg_forest_lock);
mutex_enter(&net->net_tree_lock);
mi->mi_ephemeral_tree = net;
net->net_mount = mi;
mutex_exit(&mi->mi_lock);
MI4_HOLD(mi);
VFS_HOLD(mi->mi_vfsp);
} else {
net = mi->mi_ephemeral_tree;
nfs4_ephemeral_tree_hold(net);
mutex_exit(&mi->mi_lock);
mutex_enter(&net->net_tree_lock);
mutex_enter(&net->net_cnt_lock);
if (net->net_status & NFS4_EPHEMERAL_TREE_PROCESSING) {
nfs4_ephemeral_tree_decr(net);
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
return (EIO);
}
mutex_exit(&net->net_cnt_lock);
}
mutex_enter(&net->net_cnt_lock);
net->net_status |= NFS4_EPHEMERAL_TREE_MOUNTING;
mutex_exit(&net->net_cnt_lock);
must_unlock = TRUE;
error = nfs4_trigger_domount_args_create(vp, cr, &dma);
if (error)
goto done;
mcred = crdup(cr);
if (mcred == NULL) {
error = EINVAL;
nfs4_trigger_domount_args_destroy(dma, vp);
goto done;
}
crset_zone_privall(mcred);
if (is_system_labeled())
(void) setpflags(NET_MAC_AWARE, 1, mcred);
error = nfs4_trigger_domount(vp, dma, &vfsp, mcred, newvpp);
nfs4_trigger_domount_args_destroy(dma, vp);
DTRACE_PROBE2(nfs4clnt__func__referral__mount,
vnode_t *, vp, int, error);
crfree(mcred);
done:
if (must_unlock) {
mutex_enter(&net->net_cnt_lock);
net->net_status &= ~NFS4_EPHEMERAL_TREE_MOUNTING;
if (is_building)
net->net_status &= ~NFS4_EPHEMERAL_TREE_BUILDING;
else
nfs4_ephemeral_tree_decr(net);
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
}
if (!error && (newvpp == NULL || *newvpp == NULL))
error = ENOSYS;
return (error);
}
static int
nfs4_trigger_domount_args_create(vnode_t *vp, cred_t *cr, domount_args_t **dmap)
{
int nointr;
char *hostlist;
servinfo4_t *svp;
struct nfs_args *nargs, *nargs_head;
enum clnt_stat status;
ephemeral_servinfo_t *esi, *esi_first;
domount_args_t *dma;
mntinfo4_t *mi = VTOMI4(vp);
nointr = !(mi->mi_flags & MI4_INT);
hostlist = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
svp = mi->mi_curr_serv;
status = nfs4_trigger_ping_server(svp, nointr);
if (status == RPC_SUCCESS) {
esi_first = nfs4_trigger_esi_create(vp, svp, cr);
if (esi_first == NULL) {
kmem_free(hostlist, MAXPATHLEN);
return (EINVAL);
}
(void) strlcpy(hostlist, esi_first->esi_hostname, MAXPATHLEN);
nargs_head = nfs4_trigger_nargs_create(mi, svp, esi_first);
} else {
esi_first = NULL;
nargs_head = NULL;
}
nargs = nargs_head;
do {
for (svp = mi->mi_servers; svp != NULL; svp = svp->sv_next) {
struct nfs_args *next;
if (svp == mi->mi_curr_serv && esi_first != NULL)
continue;
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
if (svp->sv_flags & SV4_NOTINUSE) {
nfs_rw_exit(&svp->sv_lock);
continue;
}
nfs_rw_exit(&svp->sv_lock);
status = nfs4_trigger_ping_server(svp, nointr);
if (status == RPC_INTR) {
kmem_free(hostlist, MAXPATHLEN);
nfs4_trigger_esi_destroy(esi_first, vp);
nargs = nargs_head;
while (nargs != NULL) {
next = nargs->nfs_ext_u.nfs_extB.next;
nfs4_trigger_nargs_destroy(nargs);
nargs = next;
}
return (EINTR);
} else if (status != RPC_SUCCESS) {
continue;
}
esi = nfs4_trigger_esi_create(vp, svp, cr);
if (esi == NULL)
continue;
next = nfs4_trigger_nargs_create(mi, svp, esi);
if (esi_first == NULL) {
ASSERT(nargs == NULL);
ASSERT(nargs_head == NULL);
nargs_head = next;
esi_first = esi;
(void) strlcpy(hostlist,
esi_first->esi_hostname, MAXPATHLEN);
} else {
ASSERT(nargs_head != NULL);
nargs->nfs_ext_u.nfs_extB.next = next;
(void) strlcat(hostlist, ",", MAXPATHLEN);
(void) strlcat(hostlist, esi->esi_hostname,
MAXPATHLEN);
nfs4_trigger_esi_destroy(esi, vp);
}
nargs = next;
}
if (esi_first == NULL)
delay(drv_usectohz(1000000));
} while (esi_first == NULL);
ASSERT(nargs_head != NULL);
dma = kmem_zalloc(sizeof (domount_args_t), KM_SLEEP);
dma->dma_esi = esi_first;
dma->dma_hostlist = hostlist;
dma->dma_nargs = nargs_head;
*dmap = dma;
return (0);
}
static void
nfs4_trigger_domount_args_destroy(domount_args_t *dma, vnode_t *vp)
{
if (dma != NULL) {
if (dma->dma_esi != NULL && vp != NULL)
nfs4_trigger_esi_destroy(dma->dma_esi, vp);
if (dma->dma_hostlist != NULL)
kmem_free(dma->dma_hostlist, MAXPATHLEN);
if (dma->dma_nargs != NULL) {
struct nfs_args *nargs = dma->dma_nargs;
do {
struct nfs_args *next =
nargs->nfs_ext_u.nfs_extB.next;
nfs4_trigger_nargs_destroy(nargs);
nargs = next;
} while (nargs != NULL);
}
kmem_free(dma, sizeof (domount_args_t));
}
}
static ephemeral_servinfo_t *
nfs4_trigger_esi_create(vnode_t *vp, servinfo4_t *svp, cred_t *cr)
{
ephemeral_servinfo_t *esi;
rnode4_t *rp = VTOR4(vp);
ASSERT(RP_ISSTUB(rp));
if (RP_ISSTUB_MIRRORMOUNT(rp))
esi = nfs4_trigger_esi_create_mirrormount(vp, svp);
else if (RP_ISSTUB_REFERRAL(rp))
esi = nfs4_trigger_esi_create_referral(vp, cr);
else
esi = NULL;
return (esi);
}
static void
nfs4_trigger_esi_destroy(ephemeral_servinfo_t *esi, vnode_t *vp)
{
rnode4_t *rp = VTOR4(vp);
ASSERT(RP_ISSTUB(rp));
if (esi != NULL)
kmem_free(esi, sizeof (ephemeral_servinfo_t));
}
static ephemeral_servinfo_t *
nfs4_trigger_esi_create_mirrormount(vnode_t *vp, servinfo4_t *svp)
{
char *stubpath;
struct knetconfig *sikncp, *svkncp;
struct netbuf *bufp;
ephemeral_servinfo_t *esi;
esi = kmem_zalloc(sizeof (ephemeral_servinfo_t), KM_SLEEP);
esi->esi_mount_flags = NFSMNT_MIRRORMOUNT;
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
esi->esi_hostname = kmem_zalloc(strlen(svp->sv_hostname) + 1, KM_SLEEP);
(void) strcat(esi->esi_hostname, svp->sv_hostname);
esi->esi_addr = kmem_zalloc(sizeof (struct netbuf), KM_SLEEP);
bufp = esi->esi_addr;
bufp->len = svp->sv_addr.len;
bufp->maxlen = svp->sv_addr.maxlen;
bufp->buf = kmem_zalloc(bufp->len, KM_SLEEP);
bcopy(svp->sv_addr.buf, bufp->buf, bufp->len);
esi->esi_knconf = kmem_zalloc(sizeof (*esi->esi_knconf), KM_SLEEP);
sikncp = esi->esi_knconf;
svkncp = svp->sv_knconf;
sikncp->knc_semantics = svkncp->knc_semantics;
sikncp->knc_protofmly = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
(void) strcat((char *)sikncp->knc_protofmly,
(char *)svkncp->knc_protofmly);
sikncp->knc_proto = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
(void) strcat((char *)sikncp->knc_proto, (char *)svkncp->knc_proto);
sikncp->knc_rdev = svkncp->knc_rdev;
if (svp->sv_dhsec) {
struct netbuf *bufp;
sec_data_t *sdata;
dh_k4_clntdata_t *data;
sdata = svp->sv_dhsec;
data = (dh_k4_clntdata_t *)sdata->data;
ASSERT(sdata->rpcflavor == AUTH_DH);
bufp = kmem_zalloc(sizeof (struct netbuf), KM_SLEEP);
bufp->len = data->syncaddr.len;
bufp->maxlen = data->syncaddr.maxlen;
bufp->buf = kmem_zalloc(bufp->len, KM_SLEEP);
bcopy(data->syncaddr.buf, bufp->buf, bufp->len);
esi->esi_syncaddr = bufp;
if (data->netname != NULL) {
int nmlen = data->netnamelen;
esi->esi_netname = kmem_zalloc(nmlen + 1, KM_SLEEP);
bcopy(data->netname, esi->esi_netname, nmlen);
}
} else {
esi->esi_syncaddr = NULL;
esi->esi_netname = NULL;
}
stubpath = fn_path(VTOSV(vp)->sv_name);
ASSERT(*stubpath == '.');
stubpath += 1;
esi->esi_path_len = strlen(stubpath) + 1;
if (strcmp(svp->sv_path, "/") != 0)
esi->esi_path_len += strlen(svp->sv_path);
esi->esi_path = kmem_zalloc(esi->esi_path_len, KM_SLEEP);
if (strcmp(svp->sv_path, "/") != 0)
(void) strcat(esi->esi_path, svp->sv_path);
(void) strcat(esi->esi_path, stubpath);
stubpath -= 1;
kmem_free(stubpath, strlen(stubpath) + 1);
nfs_rw_exit(&svp->sv_lock);
return (esi);
}
int
nfs4_callmapid(utf8string *server, struct nfs_fsl_info *resp)
{
door_arg_t door_args;
door_handle_t dh;
XDR xdr;
refd_door_args_t *xdr_argsp;
refd_door_res_t *orig_resp;
k_sigset_t smask;
int xdr_len = 0;
int res_len = 16;
int orig_reslen = res_len;
int error = 0;
struct nfsidmap_globals *nig;
if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
return (ECONNREFUSED);
nig = zone_getspecific(nfsidmap_zone_key, nfs_zone());
ASSERT(nig != NULL);
mutex_enter(&nig->nfsidmap_daemon_lock);
dh = nig->nfsidmap_daemon_dh;
if (dh == NULL) {
mutex_exit(&nig->nfsidmap_daemon_lock);
cmn_err(CE_NOTE,
"nfs4_callmapid: nfsmapid daemon not " \
"running unable to resolve host name\n");
return (EINVAL);
}
door_ki_hold(dh);
mutex_exit(&nig->nfsidmap_daemon_lock);
xdr_len = xdr_sizeof(&(xdr_utf8string), server);
xdr_argsp = kmem_zalloc(xdr_len + sizeof (*xdr_argsp), KM_SLEEP);
xdr_argsp->xdr_len = xdr_len;
xdr_argsp->cmd = NFSMAPID_SRV_NETINFO;
xdrmem_create(&xdr, (char *)&xdr_argsp->xdr_arg,
xdr_len, XDR_ENCODE);
if (!xdr_utf8string(&xdr, server)) {
kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
door_ki_rele(dh);
return (1);
}
if (orig_reslen)
orig_resp = kmem_alloc(orig_reslen, KM_SLEEP);
door_args.data_ptr = (char *)xdr_argsp;
door_args.data_size = sizeof (*xdr_argsp) + xdr_argsp->xdr_len;
door_args.desc_ptr = NULL;
door_args.desc_num = 0;
door_args.rbuf = orig_resp ? (char *)orig_resp : NULL;
door_args.rsize = res_len;
sigintr(&smask, 1);
error = door_ki_upcall(dh, &door_args);
sigunintr(&smask);
door_ki_rele(dh);
kmem_free(xdr_argsp, xdr_len + sizeof (*xdr_argsp));
if (error) {
kmem_free(orig_resp, orig_reslen);
cmn_err(CE_WARN,
"nfsmapid not running cannot resolve host name");
goto out;
}
if (orig_resp && orig_reslen) {
refd_door_res_t *door_resp;
door_resp = (refd_door_res_t *)door_args.rbuf;
if ((void *)door_args.rbuf != orig_resp)
kmem_free(orig_resp, orig_reslen);
if (door_resp->res_status == 0) {
xdrmem_create(&xdr, (char *)&door_resp->xdr_res,
door_resp->xdr_len, XDR_DECODE);
bzero(resp, sizeof (struct nfs_fsl_info));
if (!xdr_nfs_fsl_info(&xdr, resp)) {
DTRACE_PROBE2(
nfs4clnt__debug__referral__upcall__xdrfail,
struct nfs_fsl_info *, resp,
char *, "nfs4_callmapid");
error = EINVAL;
}
} else {
DTRACE_PROBE2(
nfs4clnt__debug__referral__upcall__badstatus,
int, door_resp->res_status,
char *, "nfs4_callmapid");
error = door_resp->res_status;
}
kmem_free(door_args.rbuf, door_args.rsize);
}
out:
DTRACE_PROBE2(nfs4clnt__func__referral__upcall,
char *, server, int, error);
return (error);
}
int
nfs4_fetch_locations(mntinfo4_t *mi, nfs4_sharedfh_t *sfh, char *nm,
cred_t *cr, nfs4_ga_res_t *garp, COMPOUND4res_clnt *callres, bool_t lock)
{
COMPOUND4args_clnt args;
COMPOUND4res_clnt res;
nfs_argop4 *argop;
int argoplist_size = 3 * sizeof (nfs_argop4);
nfs4_server_t *sp = NULL;
int doqueue = 1;
nfs4_error_t e = { 0, NFS4_OK, RPC_SUCCESS };
int retval = 1;
struct nfs4_clnt *nfscl;
if (lock == TRUE)
(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_READER, 0);
else
ASSERT(nfs_rw_lock_held(&mi->mi_recovlock, RW_READER) ||
nfs_rw_lock_held(&mi->mi_recovlock, RW_WRITER));
sp = find_nfs4_server(mi);
if (lock == TRUE)
nfs_rw_exit(&mi->mi_recovlock);
if (sp != NULL)
mutex_exit(&sp->s_lock);
if (lock == TRUE) {
if (sp != NULL)
(void) nfs_rw_enter_sig(&sp->s_recovlock,
RW_WRITER, 0);
(void) nfs_rw_enter_sig(&mi->mi_recovlock, RW_WRITER, 0);
} else {
if (sp != NULL) {
ASSERT(nfs_rw_lock_held(&sp->s_recovlock, RW_READER) ||
nfs_rw_lock_held(&sp->s_recovlock, RW_WRITER));
}
}
argop = kmem_alloc(argoplist_size, KM_SLEEP);
args.ctag = TAG_GETATTR_FSLOCATION;
args.array_len = 3;
args.array = argop;
argop[0].argop = OP_CPUTFH;
argop[0].nfs_argop4_u.opcputfh.sfh = sfh;
argop[1].argop = OP_CLOOKUP;
argop[1].nfs_argop4_u.opclookup.cname = nm;
argop[2].argop = OP_GETATTR;
argop[2].nfs_argop4_u.opgetattr.attr_request =
FATTR4_FSID_MASK | FATTR4_FS_LOCATIONS_MASK |
FATTR4_MOUNTED_ON_FILEID_MASK;
argop[2].nfs_argop4_u.opgetattr.mi = mi;
rfs4call(mi, &args, &res, cr, &doqueue, 0, &e);
if (lock == TRUE) {
nfs_rw_exit(&mi->mi_recovlock);
if (sp != NULL)
nfs_rw_exit(&sp->s_recovlock);
}
nfscl = zone_getspecific(nfs4clnt_zone_key, nfs_zone());
nfscl->nfscl_stat.referrals.value.ui64++;
DTRACE_PROBE3(nfs4clnt__func__referral__fsloc,
nfs4_sharedfh_t *, sfh, char *, nm, nfs4_error_t *, &e);
if (e.error != 0) {
if (sp != NULL)
nfs4_server_rele(sp);
kmem_free(argop, argoplist_size);
return (0);
}
if (res.status != NFS4_OK || res.array_len < 3 ||
res.array[2].nfs_resop4_u.opgetattr.status != NFS4_OK) {
retval = 0;
goto exit;
}
*garp = res.array[2].nfs_resop4_u.opgetattr.ga_res;
DTRACE_PROBE2(nfs4clnt__debug__referral__fsloc,
nfs4_ga_res_t *, garp, char *, "nfs4_fetch_locations");
if (garp->n4g_ext_res == NULL ||
garp->n4g_ext_res->n4g_fslocations.locations_val == NULL) {
retval = 0;
goto exit;
}
if (!garp->n4g_fsid_valid)
retval = 0;
exit:
if (retval == 0) {
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&res);
} else {
ASSERT(callres != NULL);
*callres = res;
}
if (sp != NULL)
nfs4_server_rele(sp);
kmem_free(argop, argoplist_size);
return (retval);
}
int nfs4_no_referrals = 0;
vnode_t *
find_referral_stubvp(vnode_t *dvp, char *nm, cred_t *cr)
{
nfs_fh4 *stub_fh, *dfh;
nfs4_sharedfh_t *sfhp;
char *newfhval;
vnode_t *vp = NULL;
fattr4_mounted_on_fileid mnt_on_fileid;
nfs4_ga_res_t garp;
mntinfo4_t *mi;
COMPOUND4res_clnt callres;
hrtime_t t;
if (nfs4_no_referrals)
return (NULL);
mi = VTOMI4(dvp);
if (nfs4_fetch_locations(mi, VTOR4(dvp)->r_fh, nm, cr,
&garp, &callres, FALSE) == 0)
return (NULL);
mnt_on_fileid = garp.n4g_mon_fid;
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
dfh = &VTOR4(dvp)->r_fh->sfh_fh;
stub_fh = kmem_alloc(sizeof (nfs_fh4), KM_SLEEP);
stub_fh->nfs_fh4_val = kmem_alloc(dfh->nfs_fh4_len +
sizeof (fattr4_mounted_on_fileid), KM_SLEEP);
newfhval = stub_fh->nfs_fh4_val;
bcopy(dfh->nfs_fh4_val, newfhval, dfh->nfs_fh4_len);
stub_fh->nfs_fh4_len = dfh->nfs_fh4_len;
newfhval = newfhval + dfh->nfs_fh4_len;
bcopy((char *)&mnt_on_fileid, newfhval,
sizeof (fattr4_mounted_on_fileid));
stub_fh->nfs_fh4_len += sizeof (fattr4_mounted_on_fileid);
sfhp = sfh4_put(stub_fh, VTOMI4(dvp), NULL);
kmem_free(stub_fh->nfs_fh4_val, dfh->nfs_fh4_len +
sizeof (fattr4_mounted_on_fileid));
kmem_free(stub_fh, sizeof (nfs_fh4));
if (sfhp == NULL)
return (NULL);
t = gethrtime();
garp.n4g_va.va_type = VDIR;
vp = makenfs4node(sfhp, NULL, dvp->v_vfsp, t,
cr, dvp, fn_get(VTOSV(dvp)->sv_name, nm, sfhp));
if (vp != NULL)
vp->v_type = VDIR;
sfh4_rele(&sfhp);
return (vp);
}
int
nfs4_setup_referral(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr)
{
vnode_t *nvp;
rnode4_t *rp;
if ((nvp = find_referral_stubvp(dvp, nm, cr)) == NULL)
return (EINVAL);
rp = VTOR4(nvp);
mutex_enter(&rp->r_statelock);
r4_stub_referral(rp);
mutex_exit(&rp->r_statelock);
dnlc_enter(dvp, nm, nvp);
if (*vpp != NULL)
VN_RELE(*vpp);
*vpp = nvp;
return (0);
}
int
nfs4_process_referral(mntinfo4_t *mi, nfs4_sharedfh_t *sfh,
char *nm, cred_t *cr, nfs4_ga_res_t *grp, COMPOUND4res_clnt *res,
struct nfs_fsl_info *fsloc)
{
fs_location4 *fsp;
struct nfs_fsl_info nfsfsloc;
int ret, i, error;
nfs4_ga_res_t garp;
COMPOUND4res_clnt callres;
struct knetconfig *knc;
ret = nfs4_fetch_locations(mi, sfh, nm, cr, &garp, &callres, TRUE);
if (ret == 0)
return (-1);
if (r4find_by_fsid(mi, &garp.n4g_fsid)) {
DTRACE_PROBE3(nfs4clnt__debug__referral__migration,
mntinfo4_t *, mi, nfs4_ga_res_t *, &garp,
char *, "nfs4_process_referral");
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
return (-1);
}
for (i = 0; i < garp.n4g_ext_res->n4g_fslocations.locations_len; i++) {
fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[i];
if (fsp->server_len == 0 || fsp->server_val == NULL)
continue;
error = nfs4_callmapid(fsp->server_val, &nfsfsloc);
if (error != 0)
continue;
error = nfs4_ping_server_common(nfsfsloc.knconf,
nfsfsloc.addr, !(mi->mi_flags & MI4_INT));
if (error == RPC_SUCCESS)
break;
DTRACE_PROBE2(nfs4clnt__debug__referral__srvaddr,
sockaddr_in *, (struct sockaddr_in *)nfsfsloc.addr->buf,
char *, "nfs4_process_referral");
xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
}
knc = nfsfsloc.knconf;
if ((i >= garp.n4g_ext_res->n4g_fslocations.locations_len) ||
(knc->knc_protofmly == NULL) || (knc->knc_proto == NULL)) {
DTRACE_PROBE2(nfs4clnt__debug__referral__nofsloc,
nfs4_ga_res_t *, &garp, char *, "nfs4_process_referral");
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
return (-1);
}
*fsloc = nfsfsloc;
*grp = garp;
*res = callres;
return (i);
}
static ephemeral_servinfo_t *
nfs4_trigger_esi_create_referral(vnode_t *vp, cred_t *cr)
{
struct knetconfig *sikncp, *svkncp;
struct netbuf *bufp;
ephemeral_servinfo_t *esi;
vnode_t *dvp;
rnode4_t *drp;
fs_location4 *fsp;
struct nfs_fsl_info nfsfsloc;
nfs4_ga_res_t garp;
char *p;
char fn[MAXNAMELEN];
int i, index = -1;
mntinfo4_t *mi;
COMPOUND4res_clnt callres;
if (!RP_ISSTUB_REFERRAL(VTOR4(vp)))
return (NULL);
if (vtodv(vp, &dvp, CRED(), TRUE) != 0)
return (NULL);
drp = VTOR4(dvp);
if (nfs_rw_enter_sig(&drp->r_rwlock, RW_READER, INTR4(dvp))) {
VN_RELE(dvp);
return (NULL);
}
if (vtoname(vp, fn, MAXNAMELEN) != 0) {
nfs_rw_exit(&drp->r_rwlock);
VN_RELE(dvp);
return (NULL);
}
mi = VTOMI4(dvp);
index = nfs4_process_referral(mi, drp->r_fh, fn, cr,
&garp, &callres, &nfsfsloc);
nfs_rw_exit(&drp->r_rwlock);
VN_RELE(dvp);
if (index < 0)
return (NULL);
fsp = &garp.n4g_ext_res->n4g_fslocations.locations_val[index];
esi = kmem_zalloc(sizeof (ephemeral_servinfo_t), KM_SLEEP);
esi->esi_mount_flags = NFSMNT_REFERRAL;
esi->esi_hostname =
kmem_zalloc(fsp->server_val->utf8string_len + 1, KM_SLEEP);
bcopy(fsp->server_val->utf8string_val, esi->esi_hostname,
fsp->server_val->utf8string_len);
esi->esi_hostname[fsp->server_val->utf8string_len] = '\0';
bufp = kmem_alloc(sizeof (struct netbuf), KM_SLEEP);
bufp->len = nfsfsloc.addr->len;
bufp->maxlen = nfsfsloc.addr->maxlen;
bufp->buf = kmem_zalloc(bufp->len, KM_SLEEP);
bcopy(nfsfsloc.addr->buf, bufp->buf, bufp->len);
esi->esi_addr = bufp;
esi->esi_knconf = kmem_zalloc(sizeof (*esi->esi_knconf), KM_SLEEP);
sikncp = esi->esi_knconf;
DTRACE_PROBE2(nfs4clnt__debug__referral__nfsfsloc,
struct nfs_fsl_info *, &nfsfsloc,
char *, "nfs4_trigger_esi_create_referral");
svkncp = nfsfsloc.knconf;
sikncp->knc_semantics = svkncp->knc_semantics;
sikncp->knc_protofmly = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
(void) strlcat((char *)sikncp->knc_protofmly,
(char *)svkncp->knc_protofmly, KNC_STRSIZE);
sikncp->knc_proto = (caddr_t)kmem_zalloc(KNC_STRSIZE, KM_SLEEP);
(void) strlcat((char *)sikncp->knc_proto, (char *)svkncp->knc_proto,
KNC_STRSIZE);
sikncp->knc_rdev = svkncp->knc_rdev;
DTRACE_PROBE2(nfs4clnt__debug__referral__knetconf,
struct knetconfig *, sikncp,
char *, "nfs4_trigger_esi_create_referral");
esi->esi_netname = kmem_zalloc(nfsfsloc.netnm_len, KM_SLEEP);
bcopy(nfsfsloc.netname, esi->esi_netname, nfsfsloc.netnm_len);
esi->esi_syncaddr = NULL;
esi->esi_path = p = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
esi->esi_path_len = MAXPATHLEN;
*p++ = '/';
for (i = 0; i < fsp->rootpath.pathname4_len; i++) {
component4 *comp;
comp = &fsp->rootpath.pathname4_val[i];
if ((p - esi->esi_path) + comp->utf8string_len + 1 > MAXPATHLEN)
goto err;
bcopy(comp->utf8string_val, p, comp->utf8string_len);
p += comp->utf8string_len;
*p++ = '/';
}
if (fsp->rootpath.pathname4_len != 0)
*(p - 1) = '\0';
else
*p = '\0';
p = esi->esi_path;
esi->esi_path = strdup(p);
esi->esi_path_len = strlen(p) + 1;
kmem_free(p, MAXPATHLEN);
xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
return (esi);
err:
kmem_free(esi->esi_path, esi->esi_path_len);
kmem_free(esi->esi_hostname, fsp->server_val->utf8string_len + 1);
kmem_free(esi->esi_addr->buf, esi->esi_addr->len);
kmem_free(esi->esi_addr, sizeof (struct netbuf));
kmem_free(esi->esi_knconf->knc_protofmly, KNC_STRSIZE);
kmem_free(esi->esi_knconf->knc_proto, KNC_STRSIZE);
kmem_free(esi->esi_knconf, sizeof (*esi->esi_knconf));
kmem_free(esi->esi_netname, nfsfsloc.netnm_len);
kmem_free(esi, sizeof (ephemeral_servinfo_t));
xdr_free(xdr_nfs_fsl_info, (char *)&nfsfsloc);
xdr_free(xdr_COMPOUND4res_clnt, (caddr_t)&callres);
return (NULL);
}
static int
nfs4_trigger_domount(vnode_t *stubvp, domount_args_t *dma, vfs_t **vfsp,
cred_t *cr, vnode_t **newvpp)
{
struct mounta *uap;
char *mntpt, *orig_path, *path;
const char *orig_mntpt;
int retval;
int mntpt_len;
int spec_len;
zone_t *zone = curproc->p_zone;
bool_t has_leading_slash;
int i;
vfs_t *stubvfsp = stubvp->v_vfsp;
ephemeral_servinfo_t *esi = dma->dma_esi;
struct nfs_args *nargs = dma->dma_nargs;
orig_path = path = fn_path(VTOSV(stubvp)->sv_name);
orig_mntpt = (char *)refstr_value(stubvfsp->vfs_mntpt);
if (*orig_path == '.')
orig_path++;
if (zone != global_zone) {
if (strncmp(zone->zone_rootpath, orig_mntpt,
zone->zone_rootpathlen - 1) == 0) {
orig_mntpt += (zone->zone_rootpathlen - 2);
}
}
mntpt_len = strlen(orig_mntpt) + strlen(orig_path);
mntpt = kmem_zalloc(mntpt_len + 1, KM_SLEEP);
(void) strcat(mntpt, orig_mntpt);
(void) strcat(mntpt, orig_path);
kmem_free(path, strlen(path) + 1);
path = esi->esi_path;
if (*path == '.')
path++;
if (path[0] == '/' && path[1] == '/')
path++;
has_leading_slash = (*path == '/');
spec_len = strlen(dma->dma_hostlist);
spec_len += strlen(path);
if (!has_leading_slash)
spec_len++;
spec_len++;
uap = kmem_zalloc(sizeof (struct mounta), KM_SLEEP);
uap->spec = kmem_zalloc(spec_len + 1, KM_SLEEP);
(void) snprintf(uap->spec, spec_len + 1, "%s:%s%s", dma->dma_hostlist,
has_leading_slash ? "" : "/", path);
uap->dir = mntpt;
uap->flags = MS_SYSSPACE | MS_DATA;
if (stubvfsp->vfs_flag & VFS_NOMNTTAB)
uap->flags |= MS_NOMNTTAB;
uap->fstype = MNTTYPE_NFS4;
uap->dataptr = (char *)nargs;
uap->datalen = 0;
uap->flags |= MS_OPTIONSTR;
uap->optptr = nfs4_trigger_create_mntopts(stubvfsp);
if (uap->optptr == NULL) {
retval = EINVAL;
goto done;
}
uap->optlen = strlen(uap->optptr) + 1;
for (i = 0; i < 2; i++) {
int error;
bool_t was_mounted;
retval = domount(NULL, uap, stubvp, cr, vfsp);
if (retval == 0) {
retval = VFS_ROOT(*vfsp, newvpp);
VFS_RELE(*vfsp);
break;
} else if (retval != EBUSY) {
break;
}
error = nfs4_trigger_mounted_already(stubvp,
newvpp, &was_mounted, vfsp);
if (error) {
goto done;
} else if (was_mounted) {
retval = 0;
break;
}
}
done:
if (uap->optptr)
nfs4_trigger_destroy_mntopts(uap->optptr);
kmem_free(uap->spec, spec_len + 1);
kmem_free(uap, sizeof (struct mounta));
kmem_free(mntpt, mntpt_len + 1);
return (retval);
}
static struct nfs_args *
nfs4_trigger_nargs_create(mntinfo4_t *mi, servinfo4_t *svp,
ephemeral_servinfo_t *esi)
{
sec_data_t *secdata;
struct nfs_args *nargs;
nargs = kmem_zalloc(sizeof (struct nfs_args), KM_SLEEP);
(void) nfs_rw_enter_sig(&svp->sv_lock, RW_READER, 0);
nargs->addr = esi->esi_addr;
if (esi->esi_syncaddr || esi->esi_netname) {
nargs->flags |= NFSMNT_SECURE;
nargs->syncaddr = esi->esi_syncaddr;
nargs->netname = esi->esi_netname;
}
nargs->flags |= NFSMNT_KNCONF;
nargs->knconf = esi->esi_knconf;
nargs->flags |= NFSMNT_HOSTNAME;
nargs->hostname = esi->esi_hostname;
nargs->fh = esi->esi_path;
mutex_enter(&mi->mi_lock);
if (!(mi->mi_flags & MI4_HARD))
nargs->flags |= NFSMNT_SOFT;
nargs->flags |= NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_TIMEO |
NFSMNT_RETRANS;
nargs->wsize = mi->mi_stsize;
nargs->rsize = mi->mi_tsize;
nargs->timeo = mi->mi_timeo;
nargs->retrans = mi->mi_retrans;
if (mi->mi_flags & MI4_INT)
nargs->flags |= NFSMNT_INT;
if (mi->mi_flags & MI4_NOAC)
nargs->flags |= NFSMNT_NOAC;
nargs->flags |= NFSMNT_ACREGMIN | NFSMNT_ACREGMAX | NFSMNT_ACDIRMIN |
NFSMNT_ACDIRMAX;
nargs->acregmin = HR2SEC(mi->mi_acregmin);
nargs->acregmax = HR2SEC(mi->mi_acregmax);
nargs->acdirmin = HR2SEC(mi->mi_acdirmin);
nargs->acdirmax = HR2SEC(mi->mi_acdirmax);
nargs->flags |= esi->esi_mount_flags;
if (mi->mi_flags & MI4_NOCTO)
nargs->flags |= NFSMNT_NOCTO;
if (mi->mi_flags & MI4_GRPID)
nargs->flags |= NFSMNT_GRPID;
if (mi->mi_flags & MI4_LLOCK)
nargs->flags |= NFSMNT_LLOCK;
if (mi->mi_flags & MI4_NOPRINT)
nargs->flags |= NFSMNT_NOPRINT;
if (mi->mi_flags & MI4_DIRECTIO)
nargs->flags |= NFSMNT_DIRECTIO;
if (mi->mi_flags & MI4_PUBLIC && nargs->flags & NFSMNT_MIRRORMOUNT)
nargs->flags |= NFSMNT_PUBLIC;
if (nargs->flags & NFSMNT_REFERRAL) {
nargs->flags &= ~NFSMNT_DORDMA;
nargs->flags |= NFSMNT_TRYRDMA;
}
mutex_exit(&mi->mi_lock);
if (nargs->flags & NFSMNT_REFERRAL) {
nargs->flags |= NFSMNT_SECDEFAULT;
secdata = kmem_alloc(sizeof (sec_data_t), KM_SLEEP);
secdata->secmod = secdata->rpcflavor = AUTH_SYS;
secdata->data = NULL;
} else if (svp->sv_flags & SV4_TRYSECDEFAULT) {
nargs->flags |= NFSMNT_SECDEFAULT;
if (svp->sv_currsec != NULL)
secdata = copy_sec_data(svp->sv_currsec);
else if (svp->sv_secdata != NULL)
secdata = copy_sec_data(svp->sv_secdata);
else
secdata = NULL;
} else {
if (svp->sv_secdata != NULL)
secdata = copy_sec_data(svp->sv_secdata);
else
secdata = NULL;
}
nfs_rw_exit(&svp->sv_lock);
nargs->flags |= NFSMNT_NEWARGS;
nargs->nfs_args_ext = NFS_ARGS_EXTB;
nargs->nfs_ext_u.nfs_extB.secdata = secdata;
nargs->nfs_ext_u.nfs_extB.next = NULL;
return (nargs);
}
static void
nfs4_trigger_nargs_destroy(struct nfs_args *nargs)
{
nfs4_free_args(nargs);
kmem_free(nargs, sizeof (struct nfs_args));
}
int
nfs4_record_ephemeral_mount(mntinfo4_t *mi, vnode_t *mvp)
{
mntinfo4_t *mi_parent;
nfs4_ephemeral_t *eph;
nfs4_ephemeral_tree_t *net;
nfs4_ephemeral_t *prior;
nfs4_ephemeral_t *child;
nfs4_ephemeral_t *peer;
nfs4_trigger_globals_t *ntg;
zone_t *zone = curproc->p_zone;
int rc = 0;
mi_parent = VTOMI4(mvp);
ntg = zone_getspecific(nfs4_ephemeral_key, zone);
if (!ntg->ntg_thread_started) {
nfs4_ephemeral_start_harvester(ntg);
}
mutex_enter(&mi_parent->mi_lock);
mutex_enter(&mi->mi_lock);
net = mi->mi_ephemeral_tree =
mi_parent->mi_ephemeral_tree;
if (net == NULL) {
mutex_exit(&mi->mi_lock);
mutex_exit(&mi_parent->mi_lock);
return (EBUSY);
}
nfs4_ephemeral_tree_hold(net);
eph = kmem_zalloc(sizeof (*eph), KM_SLEEP);
eph->ne_mount = mi;
MI4_HOLD(mi);
VFS_HOLD(mi->mi_vfsp);
eph->ne_ref_time = gethrestime_sec();
eph->ne_mount_to = ntg->ntg_mount_to;
mi->mi_ephemeral = eph;
if (mi_parent->mi_flags & MI4_EPHEMERAL) {
prior = mi_parent->mi_ephemeral;
if (prior == NULL) {
mi->mi_flags &= ~MI4_EPHEMERAL;
mi->mi_ephemeral = NULL;
kmem_free(eph, sizeof (*eph));
VFS_RELE(mi->mi_vfsp);
MI4_RELE(mi);
nfs4_ephemeral_tree_rele(net);
rc = EBUSY;
} else {
if (prior->ne_child == NULL) {
prior->ne_child = eph;
} else {
child = prior->ne_child;
prior->ne_child = eph;
eph->ne_peer = child;
child->ne_prior = eph;
}
eph->ne_prior = prior;
}
} else {
if (net->net_root == NULL) {
net->net_root = eph;
} else {
eph->ne_peer = peer = net->net_root;
ASSERT(peer != NULL);
net->net_root = eph;
peer->ne_prior = eph;
}
eph->ne_prior = NULL;
}
mutex_exit(&mi->mi_lock);
mutex_exit(&mi_parent->mi_lock);
return (rc);
}
static void
nfs4_ephemeral_umount_cleanup(nfs4_ephemeral_t *eph)
{
nfs4_ephemeral_t *e = eph;
nfs4_ephemeral_t *peer;
nfs4_ephemeral_t *prior;
peer = eph->ne_peer;
prior = e->ne_prior;
if (prior) {
if (prior->ne_child == e) {
prior->ne_child = peer;
} else {
prior->ne_peer = peer;
}
if (peer)
peer->ne_prior = prior;
} else if (peer) {
peer->ne_mount->mi_ephemeral_tree->net_root = peer;
peer->ne_prior = NULL;
} else {
e->ne_mount->mi_ephemeral_tree->net_root = NULL;
}
}
static int
nfs4_ephemeral_unmount_engine(nfs4_ephemeral_t *eph,
int isTreeRoot, int flag, cred_t *cr)
{
nfs4_ephemeral_t *e = eph;
nfs4_ephemeral_t *prior;
mntinfo4_t *mi;
vfs_t *vfsp;
int error;
for (;;) {
if (e->ne_child) {
prior = e;
e = e->ne_child;
continue;
}
if (e == eph && isTreeRoot == FALSE)
return (0);
if (e->ne_peer) {
prior = e;
e = e->ne_peer;
continue;
}
if (e == eph && isTreeRoot == FALSE)
return (0);
prior = e->ne_prior;
mi = e->ne_mount;
mutex_enter(&mi->mi_lock);
vfsp = mi->mi_vfsp;
ASSERT(vfsp != NULL);
VFS_HOLD(vfsp);
mi->mi_flags |= MI4_EPHEMERAL_RECURSED;
mutex_exit(&mi->mi_lock);
error = umount2_engine(vfsp, flag, cr, FALSE);
if (error) {
mutex_enter(&mi->mi_lock);
mi->mi_flags &= ~MI4_EPHEMERAL_RECURSED;
mutex_exit(&mi->mi_lock);
return (error);
}
if (e == eph) {
ASSERT(prior == NULL);
return (0);
}
ASSERT(prior != NULL);
if (prior->ne_child == e) {
prior->ne_child = NULL;
} else {
ASSERT(prior->ne_peer == e);
prior->ne_peer = NULL;
}
e = prior;
}
}
void
nfs4_ephemeral_umount_unlock(bool_t *pmust_unlock,
nfs4_ephemeral_tree_t **pnet)
{
nfs4_ephemeral_tree_t *net = *pnet;
if (*pmust_unlock) {
mutex_enter(&net->net_cnt_lock);
net->net_status &= ~NFS4_EPHEMERAL_TREE_UMOUNTING;
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
*pmust_unlock = FALSE;
}
}
void
nfs4_ephemeral_umount_activate(mntinfo4_t *mi, bool_t *pmust_unlock,
nfs4_ephemeral_tree_t **pnet)
{
mutex_enter(&mi->mi_lock);
if (mi->mi_ephemeral) {
if (!(mi->mi_flags & MI4_EPHEMERAL_RECURSED))
nfs4_ephemeral_umount_cleanup(mi->mi_ephemeral);
nfs4_ephemeral_tree_rele(*pnet);
ASSERT(mi->mi_ephemeral != NULL);
kmem_free(mi->mi_ephemeral, sizeof (*mi->mi_ephemeral));
mi->mi_ephemeral = NULL;
VFS_RELE(mi->mi_vfsp);
MI4_RELE(mi);
}
mutex_exit(&mi->mi_lock);
nfs4_ephemeral_umount_unlock(pmust_unlock, pnet);
}
int
nfs4_ephemeral_umount(mntinfo4_t *mi, int flag, cred_t *cr,
bool_t *pmust_unlock, nfs4_ephemeral_tree_t **pnet)
{
int error = 0;
nfs4_ephemeral_t *eph;
nfs4_ephemeral_tree_t *net;
int is_derooting = FALSE;
int is_recursed = FALSE;
int was_locked = FALSE;
*pmust_unlock = FALSE;
mutex_enter(&mi->mi_lock);
*pnet = net = mi->mi_ephemeral_tree;
if (net == NULL) {
mutex_exit(&mi->mi_lock);
return (0);
}
eph = mi->mi_ephemeral;
is_recursed = mi->mi_flags & MI4_EPHEMERAL_RECURSED;
is_derooting = (eph == NULL);
mutex_enter(&net->net_cnt_lock);
if (!is_recursed) {
if (net->net_status &
NFS4_EPHEMERAL_TREE_LOCKED) {
if (!(flag & MS_SYSSPACE)) {
mutex_exit(&net->net_cnt_lock);
mutex_exit(&mi->mi_lock);
return (EBUSY);
}
was_locked = TRUE;
}
}
mutex_exit(&net->net_cnt_lock);
mutex_exit(&mi->mi_lock);
if (was_locked == FALSE) {
if (mutex_tryenter(&net->net_tree_lock)) {
*pmust_unlock = TRUE;
} else {
if (!is_recursed) {
mutex_enter(&net->net_cnt_lock);
if (net->net_status &
(NFS4_EPHEMERAL_TREE_DEROOTING
| NFS4_EPHEMERAL_TREE_INVALID)) {
mutex_exit(&net->net_cnt_lock);
goto is_busy;
}
mutex_exit(&net->net_cnt_lock);
mutex_enter(&net->net_tree_lock);
mutex_enter(&mi->mi_lock);
eph = mi->mi_ephemeral;
mutex_exit(&mi->mi_lock);
mutex_enter(&net->net_cnt_lock);
if (net->net_status &
NFS4_EPHEMERAL_TREE_INVALID ||
(!is_derooting && eph == NULL)) {
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
goto is_busy;
}
mutex_exit(&net->net_cnt_lock);
*pmust_unlock = TRUE;
}
}
}
if (*pmust_unlock) {
mutex_enter(&net->net_cnt_lock);
net->net_status |= NFS4_EPHEMERAL_TREE_UMOUNTING;
if (is_derooting)
net->net_status |=
NFS4_EPHEMERAL_TREE_DEROOTING;
mutex_exit(&net->net_cnt_lock);
}
if (!is_derooting) {
if (!is_recursed) {
ASSERT(eph != NULL);
error = nfs4_ephemeral_unmount_engine(eph,
FALSE, flag, cr);
if (error)
goto is_busy;
}
} else {
eph = net->net_root;
if (eph) {
error = nfs4_ephemeral_unmount_engine(eph, TRUE,
flag, cr);
if (error) {
mutex_enter(&net->net_cnt_lock);
net->net_status &=
~NFS4_EPHEMERAL_TREE_DEROOTING;
mutex_exit(&net->net_cnt_lock);
goto is_busy;
}
net->net_root = NULL;
}
mutex_enter(&net->net_cnt_lock);
net->net_status &= ~NFS4_EPHEMERAL_TREE_DEROOTING;
net->net_status |= NFS4_EPHEMERAL_TREE_INVALID;
DTRACE_NFSV4_1(nfs4clnt__dbg__ephemeral__tree__derooting,
uint_t, net->net_refcnt);
nfs4_ephemeral_tree_decr(net);
mutex_exit(&net->net_cnt_lock);
if (was_locked == FALSE)
mutex_exit(&net->net_tree_lock);
*pmust_unlock = FALSE;
mutex_enter(&mi->mi_lock);
mi->mi_ephemeral_tree = NULL;
mutex_exit(&mi->mi_lock);
}
return (0);
is_busy:
nfs4_ephemeral_umount_unlock(pmust_unlock, pnet);
return (error);
}
static void
nfs4_ephemeral_record_umount(vfs_t *vfsp, int flag,
nfs4_ephemeral_t *e, nfs4_ephemeral_t *prior)
{
int error;
if (vfsp == NULL)
return;
error = umount2_engine(vfsp, flag, kcred, FALSE);
if (error) {
if (prior) {
if (prior->ne_child == e)
prior->ne_state |=
NFS4_EPHEMERAL_CHILD_ERROR;
else
prior->ne_state |=
NFS4_EPHEMERAL_PEER_ERROR;
}
}
}
static void
nfs4_ephemeral_harvest_forest(nfs4_trigger_globals_t *ntg,
bool_t force, bool_t time_check)
{
nfs4_ephemeral_tree_t *net;
nfs4_ephemeral_tree_t *prev = NULL;
nfs4_ephemeral_tree_t *next;
nfs4_ephemeral_t *e;
nfs4_ephemeral_t *prior;
time_t now = gethrestime_sec();
nfs4_ephemeral_tree_t *harvest = NULL;
int flag;
mntinfo4_t *mi;
vfs_t *vfsp;
if (force)
flag = MS_FORCE | MS_SYSSPACE;
else
flag = MS_SYSSPACE;
mutex_enter(&ntg->ntg_forest_lock);
for (net = ntg->ntg_forest; net != NULL; net = next) {
next = net->net_next;
nfs4_ephemeral_tree_hold(net);
mutex_enter(&net->net_tree_lock);
mutex_enter(&net->net_cnt_lock);
net->net_status |= NFS4_EPHEMERAL_TREE_LOCKED;
mutex_exit(&net->net_cnt_lock);
if (force) {
if (net->net_root) {
mi = net->net_root->ne_mount;
vfsp = mi->mi_vfsp;
ASSERT(vfsp != NULL);
VFS_HOLD(vfsp);
(void) umount2_engine(vfsp, flag,
kcred, FALSE);
goto check_done;
}
}
e = net->net_root;
if (e)
e->ne_state = NFS4_EPHEMERAL_VISIT_CHILD;
while (e) {
if (e->ne_state == NFS4_EPHEMERAL_VISIT_CHILD) {
e->ne_state = NFS4_EPHEMERAL_VISIT_SIBLING;
if (e->ne_child) {
e = e->ne_child;
e->ne_state =
NFS4_EPHEMERAL_VISIT_CHILD;
}
continue;
} else if (e->ne_state ==
NFS4_EPHEMERAL_VISIT_SIBLING) {
e->ne_state = NFS4_EPHEMERAL_PROCESS_ME;
if (e->ne_peer) {
e = e->ne_peer;
e->ne_state =
NFS4_EPHEMERAL_VISIT_CHILD;
}
continue;
} else if (e->ne_state ==
NFS4_EPHEMERAL_CHILD_ERROR) {
prior = e->ne_prior;
if (prior) {
if (prior->ne_child == e)
prior->ne_state |=
NFS4_EPHEMERAL_CHILD_ERROR;
else
prior->ne_state |=
NFS4_EPHEMERAL_PEER_ERROR;
}
e->ne_state &= ~NFS4_EPHEMERAL_CHILD_ERROR;
if (e->ne_state == NFS4_EPHEMERAL_PROCESS_ME)
e = prior;
continue;
} else if (e->ne_state ==
NFS4_EPHEMERAL_PEER_ERROR) {
prior = e->ne_prior;
if (prior) {
if (prior->ne_child == e)
prior->ne_state =
NFS4_EPHEMERAL_CHILD_ERROR;
else
prior->ne_state =
NFS4_EPHEMERAL_PEER_ERROR;
}
e->ne_state &= ~NFS4_EPHEMERAL_PEER_ERROR;
continue;
}
prior = e->ne_prior;
e->ne_state = NFS4_EPHEMERAL_OK;
if (!time_check ||
now - e->ne_ref_time > e->ne_mount_to) {
mi = e->ne_mount;
vfsp = mi->mi_vfsp;
if (vfsp != NULL)
VFS_HOLD(vfsp);
nfs4_ephemeral_record_umount(vfsp, flag,
e, prior);
}
e = prior;
}
check_done:
mutex_enter(&net->net_cnt_lock);
nfs4_ephemeral_tree_decr(net);
if (net->net_refcnt == 0 &&
net->net_status & NFS4_EPHEMERAL_TREE_INVALID) {
net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED;
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
if (prev)
prev->net_next = net->net_next;
else
ntg->ntg_forest = net->net_next;
net->net_next = harvest;
harvest = net;
VFS_RELE(net->net_mount->mi_vfsp);
MI4_RELE(net->net_mount);
continue;
}
net->net_status &= ~NFS4_EPHEMERAL_TREE_LOCKED;
mutex_exit(&net->net_cnt_lock);
mutex_exit(&net->net_tree_lock);
prev = net;
}
mutex_exit(&ntg->ntg_forest_lock);
for (net = harvest; net != NULL; net = next) {
next = net->net_next;
mutex_destroy(&net->net_tree_lock);
mutex_destroy(&net->net_cnt_lock);
kmem_free(net, sizeof (*net));
}
}
static void
nfs4_ephemeral_harvester(nfs4_trigger_globals_t *ntg)
{
clock_t timeleft;
zone_t *zone = curproc->p_zone;
for (;;) {
timeleft = zone_status_timedwait(zone, ddi_get_lbolt() +
nfs4_trigger_thread_timer * hz, ZONE_IS_SHUTTING_DOWN);
if (timeleft != -1) {
ASSERT(zone_status_get(zone) >= ZONE_IS_SHUTTING_DOWN);
zthread_exit();
}
if (ntg->ntg_forest == NULL)
continue;
nfs4_ephemeral_harvest_forest(ntg, FALSE, TRUE);
}
}
static void
nfs4_ephemeral_start_harvester(nfs4_trigger_globals_t *ntg)
{
if (ntg->ntg_thread_started)
return;
mutex_enter(&nfs4_ephemeral_thread_lock);
if (ntg->ntg_thread_started) {
mutex_exit(&nfs4_ephemeral_thread_lock);
return;
}
(void) zthread_create(NULL, 0, nfs4_ephemeral_harvester,
ntg, 0, minclsyspri);
ntg->ntg_thread_started = TRUE;
mutex_exit(&nfs4_ephemeral_thread_lock);
}
static void *
nfs4_ephemeral_zsd_create(zoneid_t zoneid)
{
nfs4_trigger_globals_t *ntg;
ntg = kmem_zalloc(sizeof (*ntg), KM_SLEEP);
ntg->ntg_thread_started = FALSE;
ntg->ntg_mount_to = nfs4_trigger_mount_to;
mutex_init(&ntg->ntg_forest_lock, NULL,
MUTEX_DEFAULT, NULL);
return (ntg);
}
static void
nfs4_ephemeral_zsd_shutdown(zoneid_t zoneid, void *arg)
{
nfs4_trigger_globals_t *ntg = arg;
if (!ntg)
return;
nfs4_ephemeral_harvest_forest(ntg, FALSE, FALSE);
}
static void
nfs4_ephemeral_zsd_destroy(zoneid_t zoneid, void *arg)
{
nfs4_trigger_globals_t *ntg = arg;
if (!ntg)
return;
nfs4_ephemeral_harvest_forest(ntg, TRUE, FALSE);
mutex_destroy(&ntg->ntg_forest_lock);
kmem_free(ntg, sizeof (*ntg));
}
void
nfs4_ephemeral_fini(void)
{
(void) zone_key_delete(nfs4_ephemeral_key);
mutex_destroy(&nfs4_ephemeral_thread_lock);
}
void
nfs4_ephemeral_init(void)
{
mutex_init(&nfs4_ephemeral_thread_lock, NULL, MUTEX_DEFAULT,
NULL);
zone_key_create(&nfs4_ephemeral_key, nfs4_ephemeral_zsd_create,
nfs4_ephemeral_zsd_shutdown, nfs4_ephemeral_zsd_destroy);
}
void
nfs4_ephemeral_set_mount_to(uint_t mount_to)
{
nfs4_trigger_globals_t *ntg;
zone_t *zone = curproc->p_zone;
ntg = zone_getspecific(nfs4_ephemeral_key, zone);
ntg->ntg_mount_to = mount_to;
}
static char *
nfs4_trigger_create_mntopts(vfs_t *vfsp)
{
uint_t i;
char *mntopts;
struct vfssw *vswp;
mntopts_t *optproto;
mntopts = kmem_zalloc(MAX_MNTOPT_STR, KM_SLEEP);
vswp = vfs_getvfssw(MNTTYPE_NFS4);
optproto = &vswp->vsw_optproto;
for (i = 0; i < optproto->mo_count; i++) {
struct mntopt *mop = &optproto->mo_list[i];
if (mop->mo_flags & MO_EMPTY)
continue;
if (nfs4_trigger_add_mntopt(mntopts, mop->mo_name, vfsp)) {
kmem_free(mntopts, MAX_MNTOPT_STR);
vfs_unrefvfssw(vswp);
return (NULL);
}
}
vfs_unrefvfssw(vswp);
if (nfs4_trigger_add_mntopt(mntopts, MNTOPT_XATTR, vfsp) ||
nfs4_trigger_add_mntopt(mntopts, MNTOPT_NOXATTR, vfsp)) {
kmem_free(mntopts, MAX_MNTOPT_STR);
return (NULL);
}
return (mntopts);
}
static void
nfs4_trigger_destroy_mntopts(char *mntopts)
{
if (mntopts)
kmem_free(mntopts, MAX_MNTOPT_STR);
}
static int
nfs4_trigger_add_mntopt(char *mntopts, char *optname, vfs_t *vfsp)
{
if (mntopts == NULL || optname == NULL || vfsp == NULL)
return (EINVAL);
if (vfs_optionisset(vfsp, optname, NULL)) {
size_t mntoptslen = strlen(mntopts);
size_t optnamelen = strlen(optname);
if (mntoptslen + optnamelen + 2 > MAX_MNTOPT_STR)
return (EOVERFLOW);
if (*mntopts != '\0')
(void) strcat(mntopts, ",");
(void) strcat(mntopts, optname);
}
return (0);
}
static enum clnt_stat
nfs4_ping_server_common(struct knetconfig *knc, struct netbuf *addr, int nointr)
{
int retries;
uint_t max_msgsize;
enum clnt_stat status;
CLIENT *cl;
struct timeval timeout;
max_msgsize = 0;
retries = 1;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
if (clnt_tli_kcreate(knc, addr, NFS_PROGRAM, NFS_V4,
max_msgsize, retries, CRED(), &cl) != 0)
return (RPC_FAILED);
if (nointr)
cl->cl_nosignal = TRUE;
status = CLNT_CALL(cl, RFS_NULL, xdr_void, NULL, xdr_void, NULL,
timeout);
if (nointr)
cl->cl_nosignal = FALSE;
AUTH_DESTROY(cl->cl_auth);
CLNT_DESTROY(cl);
return (status);
}
static enum clnt_stat
nfs4_trigger_ping_server(servinfo4_t *svp, int nointr)
{
return (nfs4_ping_server_common(svp->sv_knconf, &svp->sv_addr, nointr));
}