#include <sys/param.h>
#include <sys/systm.h>
#include <sys/thread.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/mount.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/share.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/syslog.h>
#include <sys/systm.h>
#include <sys/taskq.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/queue.h>
#include <sys/sdt.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#include <rpc/xdr.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <rpc/rpcb_prot.h>
#include <rpcsvc/nlm_prot.h>
#include <rpcsvc/sm_inter.h>
#include <nfs/nfs.h>
#include <nfs/nfs_clnt.h>
#include <nfs/export.h>
#include <nfs/rnode.h>
#include "nlm_impl.h"
#define NLM_IN_GRACE(g) (ddi_get_lbolt() < (g)->grace_threshold)
struct nlm_block_cb_data {
struct nlm_host *hostp;
struct nlm_vhold *nvp;
struct flock64 *flp;
bool_t registered;
};
#define NLM_INVOKE_CALLBACK(descr, rpcp, resp, callb) \
do { \
enum clnt_stat _stat; \
\
_stat = (*(callb))(resp, NULL, (rpcp)->nr_handle); \
if (_stat != RPC_SUCCESS && _stat != RPC_TIMEDOUT) { \
struct rpc_err _err; \
\
CLNT_GETERR((rpcp)->nr_handle, &_err); \
NLM_ERR("NLM: %s callback failed: " \
"stat %d, err %d\n", descr, _stat, \
_err.re_errno); \
} \
\
_NOTE(CONSTCOND) } while (0)
static void nlm_block(
nlm4_lockargs *lockargs,
struct nlm_host *host,
struct nlm_vhold *nvp,
struct flock64 *fl,
nlm_granted_cb grant_cb,
rpcvers_t);
static vnode_t *nlm_fh_to_vp(struct netobj *);
static struct nlm_vhold *nlm_fh_to_vhold(struct nlm_host *, struct netobj *);
static void nlm_init_shrlock(struct shrlock *, nlm4_share *, struct nlm_host *);
static callb_cpr_t *nlm_block_callback(flk_cb_when_t, void *);
static int nlm_vop_frlock(vnode_t *, int, flock64_t *, int, offset_t,
struct flk_callback *, cred_t *, caller_context_t *);
static int
nlm_init_flock(struct flock64 *fl, struct nlm4_lock *nl,
struct nlm_host *host, rpcvers_t vers, short type)
{
uint64_t off, len;
bzero(fl, sizeof (*fl));
off = nl->l_offset;
len = nl->l_len;
if (vers < NLM4_VERS) {
if (off > MAX_UOFF32 || len > MAX_UOFF32)
return (EINVAL);
if (off + len > MAX_UOFF32 + 1)
return (EINVAL);
} else {
if (len == MAX_U_OFFSET_T)
len = 0;
if (len != 0 && off + (len - 1) < off)
return (EINVAL);
}
fl->l_type = type;
fl->l_whence = SEEK_SET;
fl->l_start = off;
fl->l_len = len;
fl->l_sysid = host->nh_sysid;
fl->l_pid = nl->svid;
return (0);
}
static vnode_t *
lm_fhtovp(fhandle3_t *fh)
{
vfs_t *vfsp;
vnode_t *vp;
int error;
vfsp = getvfs(&fh->_fh3_fsid);
if (vfsp == NULL)
return (NULL);
error = VFS_VGET(vfsp, &vp, (fid_t *)&(fh->_fh3_len));
VFS_RELE(vfsp);
if (error || vp == NULL)
return (NULL);
return (vp);
}
static vnode_t *
nlm_fh_to_vp(struct netobj *fh)
{
fhandle3_t *fhp;
if (fh->n_len < sizeof (fhandle_t))
return (NULL);
fhp = (fhandle3_t *)fh->n_bytes;
if (fhp->_fh3_len > NFS_FH3MAXDATA || fhp->_fh3_len == 0)
return (NULL);
return (lm_fhtovp(fhp));
}
static struct nlm_vhold *
nlm_fh_to_vhold(struct nlm_host *hostp, struct netobj *fh)
{
vnode_t *vp;
struct nlm_vhold *nvp;
vp = nlm_fh_to_vp(fh);
if (vp == NULL)
return (NULL);
nvp = nlm_vhold_get(hostp, vp);
VN_RELE(vp);
return (nvp);
}
void
nlm_do_notify1(nlm_sm_status *argp, void *res, struct svc_req *sr)
{
struct nlm_globals *g;
struct nlm_host *host;
uint16_t sysid;
g = zone_getspecific(nlm_zone_key, curzone);
bcopy(&argp->priv, &sysid, sizeof (sysid));
DTRACE_PROBE2(nsm__notify, uint16_t, sysid,
int, argp->state);
host = nlm_host_find_by_sysid(g, (sysid_t)sysid);
if (host == NULL)
return;
nlm_host_notify_server(host, argp->state);
nlm_host_notify_client(host, argp->state);
nlm_host_release(g, host);
}
void
nlm_do_notify2(nlm_sm_status *argp, void *res, struct svc_req *sr)
{
ASSERT(0);
}
void
nlm_do_test(nlm4_testargs *argp, nlm4_testres *resp,
struct svc_req *sr, nlm_testres_cb cb)
{
struct nlm_globals *g;
struct nlm_host *host;
struct nlm4_holder *lh;
struct nlm_owner_handle *oh;
nlm_rpc_t *rpcp = NULL;
vnode_t *vp = NULL;
struct netbuf *addr, *laddr;
char *netid;
char *name;
int error;
struct flock64 fl;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
name = argp->alock.caller_name;
netid = svc_getnetid(sr->rq_xprt);
addr = svc_getrpccaller(sr->rq_xprt);
laddr = svc_getrpchost(sr->rq_xprt);
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_findcreate(g, name, netid, addr, laddr);
if (host == NULL) {
resp->stat.stat = nlm4_denied_nolocks;
return;
}
if (cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0) {
resp->stat.stat = nlm4_denied_nolocks;
goto out;
}
}
vp = nlm_fh_to_vp(&argp->alock.fh);
if (vp == NULL) {
resp->stat.stat = nlm4_stale_fh;
goto out;
}
if (NLM_IN_GRACE(g)) {
resp->stat.stat = nlm4_denied_grace_period;
goto out;
}
error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers,
(argp->exclusive) ? F_WRLCK : F_RDLCK);
if (error) {
resp->stat.stat = nlm4_failed;
goto out;
}
error = nlm_vop_frlock(vp, F_GETLK, &fl,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, NULL, CRED(), NULL);
if (error) {
resp->stat.stat = nlm4_failed;
goto out;
}
if (fl.l_type == F_UNLCK) {
resp->stat.stat = nlm4_granted;
goto out;
}
resp->stat.stat = nlm4_denied;
if (sr->rq_vers < NLM4_VERS) {
uint64 maxlen;
if (fl.l_start > MAX_UOFF32)
fl.l_start = MAX_UOFF32;
maxlen = MAX_UOFF32 + 1 - fl.l_start;
if (fl.l_len > maxlen)
fl.l_len = maxlen;
}
oh = kmem_zalloc(sizeof (*oh), KM_SLEEP);
oh->oh_sysid = (sysid_t)fl.l_sysid;
lh = &resp->stat.nlm4_testrply_u.holder;
lh->exclusive = (fl.l_type == F_WRLCK);
lh->svid = fl.l_pid;
lh->oh.n_len = sizeof (*oh);
lh->oh.n_bytes = (void *)oh;
lh->l_offset = fl.l_start;
lh->l_len = fl.l_len;
out:
if (cb != NULL && rpcp != NULL)
NLM_INVOKE_CALLBACK("test", rpcp, resp, cb);
if (vp != NULL)
VN_RELE(vp);
if (rpcp != NULL)
nlm_host_rele_rpc(host, rpcp);
nlm_host_release(g, host);
}
void
nlm_do_lock(nlm4_lockargs *argp, nlm4_res *resp, struct svc_req *sr,
nlm_reply_cb reply_cb, nlm_res_cb res_cb, nlm_granted_cb grant_cb)
{
struct nlm_globals *g;
struct flock64 fl;
struct nlm_host *host = NULL;
struct netbuf *addr, *laddr;
struct nlm_vhold *nvp = NULL;
nlm_rpc_t *rpcp = NULL;
char *netid;
char *name;
int error, flags;
bool_t do_blocking = FALSE;
bool_t do_mon_req = FALSE;
enum nlm4_stats status;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
name = argp->alock.caller_name;
netid = svc_getnetid(sr->rq_xprt);
addr = svc_getrpccaller(sr->rq_xprt);
laddr = svc_getrpchost(sr->rq_xprt);
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_findcreate(g, name, netid, addr, laddr);
if (host == NULL) {
DTRACE_PROBE4(no__host, struct nlm_globals *, g,
char *, name, char *, netid, struct netbuf *, addr);
status = nlm4_denied_nolocks;
goto doreply;
}
DTRACE_PROBE3(start, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_lockargs *, argp);
if (res_cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0) {
cmn_err(CE_WARN,
"NLM: RPCBIND/PORTMAP failed at res_cb, deny\n");
ASSERT(rpcp == NULL);
status = nlm4_denied_nolocks;
goto out;
}
}
if (argp->reclaim == 0 && NLM_IN_GRACE(g)) {
status = nlm4_denied_grace_period;
goto doreply;
}
if (nlm_host_get_state(host) != argp->state)
nlm_host_notify_server(host, argp->state);
nvp = nlm_fh_to_vhold(host, &argp->alock.fh);
if (nvp == NULL) {
status = nlm4_stale_fh;
goto doreply;
}
error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers,
(argp->exclusive) ? F_WRLCK : F_RDLCK);
if (error) {
status = nlm4_failed;
goto doreply;
}
flags = F_REMOTELOCK | FREAD | FWRITE;
error = nlm_vop_frlock(nvp->nv_vp, F_SETLK, &fl, flags,
(u_offset_t)0, NULL, CRED(), NULL);
DTRACE_PROBE3(setlk__res, struct flock64 *, &fl,
int, flags, int, error);
switch (error) {
case 0:
status = nlm4_granted;
do_mon_req = TRUE;
break;
case EAGAIN:
if (argp->block == FALSE || grant_cb == NULL) {
status = nlm4_denied;
break;
}
if (!svc_reserve_thread(sr->rq_xprt)) {
status = nlm4_denied_nolocks;
break;
}
status = nlm4_blocked;
do_blocking = TRUE;
do_mon_req = TRUE;
break;
case ENOLCK:
status = nlm4_denied_nolocks;
break;
case EROFS:
status = nlm4_rofs;
break;
case EFBIG:
status = nlm4_fbig;
break;
case EDEADLK:
status = nlm4_deadlck;
break;
default:
status = nlm4_denied;
break;
}
doreply:
resp->stat.stat = status;
if (reply_cb != NULL) {
if (!(*reply_cb)(sr->rq_xprt, resp))
svcerr_systemerr(sr->rq_xprt);
}
if (res_cb != NULL && rpcp != NULL)
NLM_INVOKE_CALLBACK("lock", rpcp, resp, res_cb);
if (do_mon_req && grant_cb != NULL)
nlm_host_monitor(g, host, argp->state);
if (do_blocking) {
ASSERT(grant_cb != NULL);
(void) svc_detach_thread(sr->rq_xprt);
nlm_block(argp, host, nvp, &fl, grant_cb, sr->rq_vers);
}
out:
DTRACE_PROBE3(lock__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_res *, resp);
if (rpcp != NULL)
nlm_host_rele_rpc(host, rpcp);
nlm_vhold_release(host, nvp);
nlm_host_release(g, host);
}
static void
nlm_block(nlm4_lockargs *lockargs,
struct nlm_host *host,
struct nlm_vhold *nvp,
struct flock64 *flp,
nlm_granted_cb grant_cb,
rpcvers_t vers)
{
nlm4_testargs args;
nlm4_res res;
int error;
flk_callback_t flk_cb;
struct nlm_block_cb_data cb_data;
nlm_rpc_t *rpcp = NULL;
enum clnt_stat status;
error = nlm_slreq_register(host, nvp, flp);
if (error != 0) {
ASSERT(error == EEXIST);
return;
}
error = nlm_host_get_rpc(host, vers, &rpcp);
if (error != 0) {
(void) nlm_slreq_unregister(host, nvp, flp);
return;
}
cb_data.hostp = host;
cb_data.nvp = nvp;
cb_data.flp = flp;
cb_data.registered = TRUE;
flk_init_callback(&flk_cb, nlm_block_callback, &cb_data);
error = nlm_vop_frlock(nvp->nv_vp, F_SETLKW, flp,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, &flk_cb, CRED(), NULL);
if (cb_data.registered)
(void) nlm_slreq_unregister(host, nvp, flp);
if (error != 0) {
return;
}
bzero(&args, sizeof (args));
args.cookie = lockargs->cookie;
args.exclusive = lockargs->exclusive;
args.alock = lockargs->alock;
bzero(&res, sizeof (res));
status = (*grant_cb)(&args, &res, (rpcp)->nr_handle);
if (status != RPC_SUCCESS) {
struct rpc_err err;
CLNT_GETERR((rpcp)->nr_handle, &err);
NLM_ERR("NLM: %s callback failed: "
"stat %d, err %d\n", "grant", status,
err.re_errno);
res.stat.stat = nlm4_failed;
}
if (res.stat.stat != nlm4_granted) {
(void) nlm_vop_frlock(nvp->nv_vp, F_UNLCK, flp,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, NULL, CRED(), NULL);
}
xdr_free((xdrproc_t)xdr_nlm4_res, (void *)&res);
nlm_host_rele_rpc(host, rpcp);
}
static callb_cpr_t *
nlm_block_callback(flk_cb_when_t when, void *data)
{
struct nlm_block_cb_data *cb_data;
cb_data = (struct nlm_block_cb_data *)data;
if (when == FLK_AFTER_SLEEP) {
(void) nlm_slreq_unregister(cb_data->hostp,
cb_data->nvp, cb_data->flp);
cb_data->registered = FALSE;
}
return (0);
}
void
nlm_do_cancel(nlm4_cancargs *argp, nlm4_res *resp,
struct svc_req *sr, nlm_res_cb cb)
{
struct nlm_globals *g;
struct nlm_host *host;
struct netbuf *addr, *laddr;
struct nlm_vhold *nvp = NULL;
nlm_rpc_t *rpcp = NULL;
char *netid;
char *name;
int error;
struct flock64 fl;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
netid = svc_getnetid(sr->rq_xprt);
name = argp->alock.caller_name;
addr = svc_getrpccaller(sr->rq_xprt);
laddr = svc_getrpchost(sr->rq_xprt);
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_findcreate(g, name, netid, addr, laddr);
if (host == NULL) {
resp->stat.stat = nlm4_denied_nolocks;
return;
}
if (cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0) {
resp->stat.stat = nlm4_denied_nolocks;
goto out;
}
}
DTRACE_PROBE3(start, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_cancargs *, argp);
if (NLM_IN_GRACE(g)) {
resp->stat.stat = nlm4_denied_grace_period;
goto out;
}
nvp = nlm_fh_to_vhold(host, &argp->alock.fh);
if (nvp == NULL) {
resp->stat.stat = nlm4_stale_fh;
goto out;
}
error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers,
(argp->exclusive) ? F_WRLCK : F_RDLCK);
if (error) {
resp->stat.stat = nlm4_failed;
goto out;
}
error = nlm_slreq_unregister(host, nvp, &fl);
if (error != 0) {
resp->stat.stat = nlm4_denied;
goto out;
}
fl.l_type = F_UNLCK;
error = nlm_vop_frlock(nvp->nv_vp, F_SETLK, &fl,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, NULL, CRED(), NULL);
resp->stat.stat = (error == 0) ?
nlm4_granted : nlm4_denied;
out:
if (cb != NULL && rpcp != NULL)
NLM_INVOKE_CALLBACK("cancel", rpcp, resp, cb);
DTRACE_PROBE3(cancel__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_res *, resp);
if (rpcp != NULL)
nlm_host_rele_rpc(host, rpcp);
nlm_vhold_release(host, nvp);
nlm_host_release(g, host);
}
void
nlm_do_unlock(nlm4_unlockargs *argp, nlm4_res *resp,
struct svc_req *sr, nlm_res_cb cb)
{
struct nlm_globals *g;
struct nlm_host *host;
struct netbuf *addr, *laddr;
nlm_rpc_t *rpcp = NULL;
vnode_t *vp = NULL;
char *netid;
char *name;
int error;
struct flock64 fl;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
netid = svc_getnetid(sr->rq_xprt);
name = argp->alock.caller_name;
addr = svc_getrpccaller(sr->rq_xprt);
laddr = svc_getrpchost(sr->rq_xprt);
resp->stat.stat = nlm4_granted;
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_findcreate(g, name, netid, addr, laddr);
if (host == NULL)
return;
if (cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0)
goto out;
}
DTRACE_PROBE3(start, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_unlockargs *, argp);
if (NLM_IN_GRACE(g)) {
resp->stat.stat = nlm4_denied_grace_period;
goto out;
}
vp = nlm_fh_to_vp(&argp->alock.fh);
if (vp == NULL)
goto out;
error = nlm_init_flock(&fl, &argp->alock, host, sr->rq_vers, F_UNLCK);
if (error)
goto out;
error = nlm_vop_frlock(vp, F_SETLK, &fl,
F_REMOTELOCK | FREAD | FWRITE,
(u_offset_t)0, NULL, CRED(), NULL);
DTRACE_PROBE1(unlock__res, int, error);
out:
if (cb != NULL && rpcp != NULL)
NLM_INVOKE_CALLBACK("unlock", rpcp, resp, cb);
DTRACE_PROBE3(unlock__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_res *, resp);
if (vp != NULL)
VN_RELE(vp);
if (rpcp != NULL)
nlm_host_rele_rpc(host, rpcp);
nlm_host_release(g, host);
}
void
nlm_do_granted(nlm4_testargs *argp, nlm4_res *resp,
struct svc_req *sr, nlm_res_cb cb)
{
struct nlm_globals *g;
struct nlm_owner_handle *oh;
struct nlm_host *host;
nlm_rpc_t *rpcp = NULL;
int error;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
resp->stat.stat = nlm4_denied;
g = zone_getspecific(nlm_zone_key, curzone);
oh = (void *) argp->alock.oh.n_bytes;
if (oh == NULL)
return;
host = nlm_host_find_by_sysid(g, oh->oh_sysid);
if (host == NULL)
return;
if (cb != NULL) {
error = nlm_host_get_rpc(host, sr->rq_vers, &rpcp);
if (error != 0)
goto out;
}
if (NLM_IN_GRACE(g)) {
resp->stat.stat = nlm4_denied_grace_period;
goto out;
}
error = nlm_slock_grant(g, host, &argp->alock);
if (error == 0)
resp->stat.stat = nlm4_granted;
out:
if (cb != NULL && rpcp != NULL)
NLM_INVOKE_CALLBACK("do_granted", rpcp, resp, cb);
if (rpcp != NULL)
nlm_host_rele_rpc(host, rpcp);
nlm_host_release(g, host);
}
void
nlm_do_free_all(nlm4_notify *argp, void *res, struct svc_req *sr)
{
struct nlm_globals *g;
struct nlm_host_list host_list;
struct nlm_host *hostp;
TAILQ_INIT(&host_list);
g = zone_getspecific(nlm_zone_key, curzone);
mutex_enter(&g->clean_lock);
mutex_enter(&g->lock);
for (hostp = avl_first(&g->nlm_hosts_tree); hostp != NULL;
hostp = AVL_NEXT(&g->nlm_hosts_tree, hostp)) {
if (strcasecmp(hostp->nh_name, argp->name) == 0) {
if (hostp->nh_flags & NLM_NH_INIDLE) {
TAILQ_REMOVE(&g->nlm_idle_hosts, hostp,
nh_link);
hostp->nh_flags &= ~NLM_NH_INIDLE;
}
hostp->nh_refs++;
TAILQ_INSERT_TAIL(&host_list, hostp, nh_link);
}
}
mutex_exit(&g->lock);
while (!TAILQ_EMPTY(&host_list)) {
hostp = TAILQ_FIRST(&host_list);
TAILQ_REMOVE(&host_list, hostp, nh_link);
nlm_host_notify_server(hostp, argp->state);
nlm_host_release(g, hostp);
}
mutex_exit(&g->clean_lock);
(void) res;
(void) sr;
}
static void
nlm_init_shrlock(struct shrlock *shr,
nlm4_share *nshare, struct nlm_host *host)
{
switch (nshare->access) {
default:
case fsa_NONE:
shr->s_access = 0;
break;
case fsa_R:
shr->s_access = F_RDACC;
break;
case fsa_W:
shr->s_access = F_WRACC;
break;
case fsa_RW:
shr->s_access = F_RWACC;
break;
}
switch (nshare->mode) {
default:
case fsm_DN:
shr->s_deny = F_NODNY;
break;
case fsm_DR:
shr->s_deny = F_RDDNY;
break;
case fsm_DW:
shr->s_deny = F_WRDNY;
break;
case fsm_DRW:
shr->s_deny = F_RWDNY;
break;
}
shr->s_sysid = host->nh_sysid;
shr->s_pid = 0;
shr->s_own_len = nshare->oh.n_len;
shr->s_owner = nshare->oh.n_bytes;
}
void
nlm_do_share(nlm4_shareargs *argp, nlm4_shareres *resp, struct svc_req *sr)
{
struct nlm_globals *g;
struct nlm_host *host;
struct netbuf *addr, *laddr;
struct nlm_vhold *nvp = NULL;
char *netid;
char *name;
int error;
struct shrlock shr;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
name = argp->share.caller_name;
netid = svc_getnetid(sr->rq_xprt);
addr = svc_getrpccaller(sr->rq_xprt);
laddr = svc_getrpchost(sr->rq_xprt);
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_findcreate(g, name, netid, addr, laddr);
if (host == NULL) {
resp->stat = nlm4_denied_nolocks;
return;
}
DTRACE_PROBE3(share__start, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_shareargs *, argp);
if (argp->reclaim == 0 && NLM_IN_GRACE(g)) {
resp->stat = nlm4_denied_grace_period;
goto out;
}
nvp = nlm_fh_to_vhold(host, &argp->share.fh);
if (nvp == NULL) {
resp->stat = nlm4_stale_fh;
goto out;
}
nlm_init_shrlock(&shr, &argp->share, host);
error = VOP_SHRLOCK(nvp->nv_vp, F_SHARE, &shr,
FREAD | FWRITE, CRED(), NULL);
if (error == 0) {
resp->stat = nlm4_granted;
nlm_host_monitor(g, host, 0);
} else {
resp->stat = nlm4_denied;
}
out:
DTRACE_PROBE3(share__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_shareres *, resp);
nlm_vhold_release(host, nvp);
nlm_host_release(g, host);
}
void
nlm_do_unshare(nlm4_shareargs *argp, nlm4_shareres *resp, struct svc_req *sr)
{
struct nlm_globals *g;
struct nlm_host *host;
struct netbuf *addr;
vnode_t *vp = NULL;
char *netid;
int error;
struct shrlock shr;
nlm_copy_netobj(&resp->cookie, &argp->cookie);
netid = svc_getnetid(sr->rq_xprt);
addr = svc_getrpccaller(sr->rq_xprt);
g = zone_getspecific(nlm_zone_key, curzone);
host = nlm_host_find(g, netid, addr);
if (host == NULL) {
resp->stat = nlm4_denied_nolocks;
return;
}
DTRACE_PROBE3(unshare__start, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_shareargs *, argp);
if (NLM_IN_GRACE(g)) {
resp->stat = nlm4_denied_grace_period;
goto out;
}
vp = nlm_fh_to_vp(&argp->share.fh);
if (vp == NULL) {
resp->stat = nlm4_stale_fh;
goto out;
}
nlm_init_shrlock(&shr, &argp->share, host);
error = VOP_SHRLOCK(vp, F_UNSHARE, &shr,
FREAD | FWRITE, CRED(), NULL);
(void) error;
resp->stat = nlm4_granted;
out:
DTRACE_PROBE3(unshare__end, struct nlm_globals *, g,
struct nlm_host *, host, nlm4_shareres *, resp);
if (vp != NULL)
VN_RELE(vp);
nlm_host_release(g, host);
}
static int
nlm_vop_frlock(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct)
{
if (bfp->l_len != 0 &&
sum_overflows_off(bfp->l_start, bfp->l_len - 1)) {
return (EOVERFLOW);
}
return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
}