#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/file_internal.h>
#include <sys/malloc.h>
#include <sys/lockf.h>
#include <sys/kpi_mbuf.h>
#include <sys/mount_internal.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/resourcevar.h>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/user.h>
#include <sys/vnode_internal.h>
#include <kern/thread.h>
#include <kern/host.h>
#include <machine/limits.h>
#include <net/if.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfs_gss.h>
#include <nfs/nfsmount.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_lock.h>
#include <mach/host_priv.h>
#include <mach/mig_errors.h>
#include <mach/host_special_ports.h>
#include <lockd/lockd_mach.h>
extern void ipc_port_release_send(ipc_port_t);
#define OFF_MAX QUAD_MAX
static uint64_t nfs_lockxid = 0;
static LOCKD_MSG_QUEUE nfs_pendlockq;
struct nfs_lock_pid {
TAILQ_ENTRY(nfs_lock_pid) lp_lru;
LIST_ENTRY(nfs_lock_pid) lp_hash;
int lp_valid;
int lp_time;
pid_t lp_pid;
struct timeval lp_pid_start;
};
#define NFS_LOCK_PID_HASH_SIZE 64 // XXX tune me
#define NFS_LOCK_PID_HASH(pid) \
(&nfs_lock_pid_hash_tbl[(pid) & nfs_lock_pid_hash])
static LIST_HEAD(, nfs_lock_pid) *nfs_lock_pid_hash_tbl;
static TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_lru;
static u_long nfs_lock_pid_hash;
static uint32_t nfs_lock_pid_hash_trusted;
static lck_grp_t *nfs_lock_lck_grp;
static lck_mtx_t *nfs_lock_mutex;
void nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *);
void nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *);
int nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *, struct lockd_ans *);
LOCKD_MSG_REQUEST *nfs_lockdmsg_find_by_answer(struct lockd_ans *);
LOCKD_MSG_REQUEST *nfs_lockdmsg_find_by_xid(uint64_t);
uint64_t nfs_lockxid_get(void);
int nfs_lock_pid_check(proc_t, int);
int nfs_lockd_send_request(LOCKD_MSG *, int);
void
nfs_lockinit(void)
{
TAILQ_INIT(&nfs_pendlockq);
nfs_lock_pid_hash_trusted = 1;
nfs_lock_pid_hash_tbl = hashinit(NFS_LOCK_PID_HASH_SIZE,
M_TEMP, &nfs_lock_pid_hash);
TAILQ_INIT(&nfs_lock_pid_lru);
nfs_lock_lck_grp = lck_grp_alloc_init("nfs_lock", LCK_GRP_ATTR_NULL);
nfs_lock_mutex = lck_mtx_alloc_init(nfs_lock_lck_grp, LCK_ATTR_NULL);
}
void
nfs_lockd_mount_change(int i)
{
mach_port_t lockd_port = IPC_PORT_NULL;
kern_return_t kr;
int send_shutdown;
lck_mtx_lock(nfs_lock_mutex);
nfs_lockd_mounts += i;
send_shutdown = ((nfs_lockd_mounts == 0) && nfs_lockd_request_sent);
if (send_shutdown)
nfs_lockd_request_sent = 0;
lck_mtx_unlock(nfs_lock_mutex);
if (!send_shutdown)
return;
kr = host_get_lockd_port(host_priv_self(), &lockd_port);
if ((kr != KERN_SUCCESS) || !IPC_PORT_VALID(lockd_port)) {
printf("nfs_lockd_mount_change: shutdown couldn't get port, kr %d, port %s\n",
kr, (lockd_port == IPC_PORT_NULL) ? "NULL" :
(lockd_port == IPC_PORT_DEAD) ? "DEAD" : "VALID");
return;
}
kr = lockd_shutdown(lockd_port);
if (kr != KERN_SUCCESS)
printf("nfs_lockd_mount_change: shutdown %d\n", kr);
ipc_port_release_send(lockd_port);
}
inline void
nfs_lockdmsg_enqueue(LOCKD_MSG_REQUEST *msgreq)
{
LOCKD_MSG_REQUEST *mr;
mr = TAILQ_LAST(&nfs_pendlockq, nfs_lock_msg_queue);
if (!mr || (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
TAILQ_INSERT_TAIL(&nfs_pendlockq, msgreq, lmr_next);
return;
}
while (mr && (msgreq->lmr_msg.lm_xid > mr->lmr_msg.lm_xid)) {
mr = TAILQ_PREV(mr, nfs_lock_msg_queue, lmr_next);
}
if (mr) {
TAILQ_INSERT_AFTER(&nfs_pendlockq, mr, msgreq, lmr_next);
} else {
TAILQ_INSERT_HEAD(&nfs_pendlockq, msgreq, lmr_next);
}
}
inline void
nfs_lockdmsg_dequeue(LOCKD_MSG_REQUEST *msgreq)
{
TAILQ_REMOVE(&nfs_pendlockq, msgreq, lmr_next);
}
inline LOCKD_MSG_REQUEST *
nfs_lockdmsg_find_by_xid(uint64_t lockxid)
{
LOCKD_MSG_REQUEST *mr;
TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
if (mr->lmr_msg.lm_xid == lockxid)
return mr;
if (mr->lmr_msg.lm_xid > lockxid)
return NULL;
}
return mr;
}
inline int
nfs_lockdmsg_compare_to_answer(LOCKD_MSG_REQUEST *msgreq, struct lockd_ans *ansp)
{
if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO))
return 1;
if (msgreq->lmr_msg.lm_fl.l_pid != ansp->la_pid)
return 1;
if (msgreq->lmr_msg.lm_fl.l_start != ansp->la_start)
return 1;
if (msgreq->lmr_msg.lm_fl.l_len != ansp->la_len)
return 1;
if (msgreq->lmr_msg.lm_fh_len != ansp->la_fh_len)
return 1;
if (bcmp(msgreq->lmr_msg.lm_fh, ansp->la_fh, ansp->la_fh_len))
return 1;
return 0;
}
inline LOCKD_MSG_REQUEST *
nfs_lockdmsg_find_by_answer(struct lockd_ans *ansp)
{
LOCKD_MSG_REQUEST *mr;
if (!(ansp->la_flags & LOCKD_ANS_LOCK_INFO))
return NULL;
TAILQ_FOREACH(mr, &nfs_pendlockq, lmr_next) {
if (!nfs_lockdmsg_compare_to_answer(mr, ansp))
break;
}
return mr;
}
inline uint64_t
nfs_lockxid_get(void)
{
LOCKD_MSG_REQUEST *mr;
if (!nfs_lockxid) {
struct timeval tv;
microtime(&tv);
nfs_lockxid = (uint64_t)tv.tv_sec << 12;
}
do {
if (++nfs_lockxid == 0)
nfs_lockxid++;
if (!(mr = TAILQ_LAST(&nfs_pendlockq, nfs_lock_msg_queue)) ||
(mr->lmr_msg.lm_xid < nfs_lockxid)) {
break;
}
} while (nfs_lockdmsg_find_by_xid(nfs_lockxid));
return nfs_lockxid;
}
int
nfs_lock_pid_check(proc_t p, int addflag)
{
struct nfs_lock_pid *lp, *lplru, *lplru_next, *mlp;
TAILQ_HEAD(, nfs_lock_pid) nfs_lock_pid_free;
proc_t plru = PROC_NULL;
pid_t pid;
int error = 0;
struct timeval now;
TAILQ_INIT(&nfs_lock_pid_free);
mlp = NULL;
loop:
pid = proc_pid(p);
error = ENOENT;
lp = NFS_LOCK_PID_HASH(pid)->lh_first;
for (; lp != NULL; lp = lp->lp_hash.le_next)
if (lp->lp_pid == pid) {
if (timevalcmp(&lp->lp_pid_start, &p->p_start, ==)) {
TAILQ_REMOVE(&nfs_lock_pid_lru, lp, lp_lru);
microuptime(&now);
lp->lp_time = now.tv_sec;
TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lp, lp_lru);
error = 0;
break;
}
LIST_REMOVE(lp, lp_hash);
lp->lp_valid = 0;
TAILQ_REMOVE(&nfs_lock_pid_lru, lp, lp_lru);
TAILQ_INSERT_HEAD(&nfs_lock_pid_lru, lp, lp_lru);
lp = NULL;
break;
}
if (!lp)
lp = mlp;
if ((error == ENOENT) && addflag && !lp) {
int lrucnt = 0;
microuptime(&now);
for (lplru = TAILQ_FIRST(&nfs_lock_pid_lru); lplru; lplru = lplru_next) {
lplru_next = TAILQ_NEXT(lplru, lp_lru);
if (lplru->lp_valid && (lplru->lp_time >= (now.tv_sec - 2))) {
break;
}
TAILQ_REMOVE(&nfs_lock_pid_lru, lplru, lp_lru);
if (!lplru->lp_valid || !(plru = proc_find(lplru->lp_pid)) ||
timevalcmp(&lplru->lp_pid_start, &plru->p_start, !=)) {
if (plru != PROC_NULL) {
proc_rele(plru);
plru = PROC_NULL;
}
LIST_REMOVE(lplru, lp_hash);
if (!lp) {
lp = lplru;
} else {
TAILQ_INSERT_HEAD(&nfs_lock_pid_free, lplru, lp_lru);
}
} else {
if (plru != PROC_NULL) {
proc_rele(plru);
plru = PROC_NULL;
}
lplru->lp_time = now.tv_sec;
TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lplru, lp_lru);
}
if (++lrucnt > 8)
break;
}
if (!lp) {
lck_mtx_unlock(nfs_lock_mutex);
MALLOC(mlp, struct nfs_lock_pid *, sizeof(struct nfs_lock_pid),
M_TEMP, M_WAITOK | M_ZERO);
lck_mtx_lock(nfs_lock_mutex);
if (mlp)
goto loop;
error = ENOMEM;
}
}
if ((error == ENOENT) && addflag && lp) {
lp->lp_pid = pid;
lp->lp_pid_start = p->p_start;
LIST_INSERT_HEAD(NFS_LOCK_PID_HASH(lp->lp_pid), lp, lp_hash);
lp->lp_valid = 1;
lp->lp_time = now.tv_sec;
TAILQ_INSERT_TAIL(&nfs_lock_pid_lru, lp, lp_lru);
error = 0;
}
if ((mlp && (lp != mlp)) || TAILQ_FIRST(&nfs_lock_pid_free)) {
lck_mtx_unlock(nfs_lock_mutex);
if (mlp && (lp != mlp)) {
FREE(mlp, M_TEMP);
}
while ((lp = TAILQ_FIRST(&nfs_lock_pid_free))) {
TAILQ_REMOVE(&nfs_lock_pid_free, lp, lp_lru);
FREE(lp, M_TEMP);
}
lck_mtx_lock(nfs_lock_mutex);
}
return (error);
}
#define MACH_MAX_TRIES 3
int
nfs_lockd_send_request(LOCKD_MSG *msg, int interruptable)
{
kern_return_t kr;
int retries = 0;
mach_port_t lockd_port = IPC_PORT_NULL;
kr = host_get_lockd_port(host_priv_self(), &lockd_port);
if (kr != KERN_SUCCESS || !IPC_PORT_VALID(lockd_port))
return (ENOTSUP);
do {
do {
kr = lockd_request(
lockd_port,
msg->lm_version,
msg->lm_flags,
msg->lm_xid,
msg->lm_fl.l_start,
msg->lm_fl.l_len,
msg->lm_fl.l_pid,
msg->lm_fl.l_type,
msg->lm_fl.l_whence,
(uint32_t *)&msg->lm_addr,
(uint32_t *)&msg->lm_cred,
msg->lm_fh_len,
msg->lm_fh);
if (kr != KERN_SUCCESS)
printf("lockd_request received %d!\n", kr);
} while (!interruptable && kr == MACH_SEND_INTERRUPTED);
} while (kr == MIG_SERVER_DIED && retries++ < MACH_MAX_TRIES);
ipc_port_release_send(lockd_port);
switch (kr) {
case MACH_SEND_INTERRUPTED:
return (EINTR);
default:
return (EAGAIN);
}
return (kr);
}
int
nfs3_vnop_advlock(
struct vnop_advlock_args *ap)
{
vfs_context_t ctx;
proc_t p;
LOCKD_MSG_REQUEST msgreq;
LOCKD_MSG *msg;
vnode_t vp;
nfsnode_t np;
int error, error2;
int interruptable, modified;
struct flock *fl;
struct nfsmount *nmp;
struct nfs_vattr nvattr;
off_t start, end;
struct timeval now;
int timeo, endtime, lastmsg, wentdown = 0;
int lockpidcheck, nfsvers;
struct sockaddr *saddr;
struct timespec ts;
ctx = ap->a_context;
p = vfs_context_proc(ctx);
vp = ap->a_vp;
fl = ap->a_fl;
np = VTONFS(vp);
nmp = VTONMP(vp);
if (!nmp)
return (ENXIO);
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_flag & NFSMNT_NOLOCKS) {
lck_mtx_unlock(&nmp->nm_lock);
return (ENOTSUP);
}
nfsvers = nmp->nm_vers;
lck_mtx_unlock(&nmp->nm_lock);
if (fl->l_whence != SEEK_END) {
if ((fl->l_whence != SEEK_CUR && fl->l_whence != SEEK_SET) ||
fl->l_start < 0 ||
(fl->l_len > 0 && fl->l_len - 1 > OFF_MAX - fl->l_start) ||
(fl->l_len < 0 && fl->l_start + fl->l_len < 0))
return (EINVAL);
}
lck_mtx_lock(nfs_lock_mutex);
lockpidcheck = nfs_lock_pid_check(p, 0);
lck_mtx_unlock(nfs_lock_mutex);
if (lockpidcheck) {
if (lockpidcheck != ENOENT)
return (lockpidcheck);
if ((ap->a_op == F_UNLCK) && nfs_lock_pid_hash_trusted)
return (0);
}
switch (fl->l_whence) {
case SEEK_SET:
case SEEK_CUR:
start = fl->l_start;
break;
case SEEK_END:
if ((error = nfs_node_lock(np)))
return (error);
modified = (np->n_flag & NMODIFIED);
nfs_node_unlock(np);
if (modified && ((error = nfs_vinvalbuf(vp, V_SAVE, ctx, 1))))
return (error);
if ((error = nfs_getattr(np, &nvattr, ctx, NGA_UNCACHED)))
return (error);
nfs_data_lock(np, NFS_DATA_LOCK_SHARED);
start = np->n_size + fl->l_start;
nfs_data_unlock(np);
break;
default:
return (EINVAL);
}
if (fl->l_len == 0)
end = -1;
else if (fl->l_len > 0)
end = start + fl->l_len - 1;
else {
end = start - 1;
start += fl->l_len;
}
if (start < 0)
return (EINVAL);
if ((nfsvers == NFS_VER2) &&
((start >= 0x80000000) || (end >= 0x80000000)))
return (EINVAL);
bzero(&msgreq, sizeof(msgreq));
msg = &msgreq.lmr_msg;
msg->lm_version = LOCKD_MSG_VERSION;
msg->lm_flags = 0;
msg->lm_fl = *fl;
msg->lm_fl.l_start = start;
if (end != -1)
msg->lm_fl.l_len = end - start + 1;
msg->lm_fl.l_pid = vfs_context_pid(ctx);
if (ap->a_flags & F_WAIT)
msg->lm_flags |= LOCKD_MSG_BLOCK;
if (ap->a_op == F_GETLK)
msg->lm_flags |= LOCKD_MSG_TEST;
nmp = VTONMP(vp);
if (!nmp)
return (ENXIO);
lck_mtx_lock(&nmp->nm_lock);
saddr = mbuf_data(nmp->nm_nam);
bcopy(saddr, &msg->lm_addr, min(sizeof msg->lm_addr, saddr->sa_len));
msg->lm_fh_len = (nfsvers == NFS_VER2) ? NFSX_V2FH : np->n_fhsize;
bcopy(np->n_fhp, msg->lm_fh, msg->lm_fh_len);
if (nfsvers == NFS_VER3)
msg->lm_flags |= LOCKD_MSG_NFSV3;
cru2x(vfs_context_ucred(ctx), &msg->lm_cred);
microuptime(&now);
lastmsg = now.tv_sec - ((nmp->nm_tprintf_delay) - (nmp->nm_tprintf_initial_delay));
interruptable = nmp->nm_flag & NFSMNT_INT;
lck_mtx_unlock(&nmp->nm_lock);
lck_mtx_lock(nfs_lock_mutex);
msg->lm_xid = nfs_lockxid_get();
nfs_lockdmsg_enqueue(&msgreq);
timeo = 2;
for (;;) {
nfs_lockd_request_sent = 1;
lck_mtx_unlock(nfs_lock_mutex);
error = nfs_lockd_send_request(msg, interruptable);
lck_mtx_lock(nfs_lock_mutex);
if (error && error != EAGAIN)
break;
wait_for_granted:
error = EWOULDBLOCK;
ts.tv_sec = 2;
ts.tv_nsec = 0;
microuptime(&now);
endtime = now.tv_sec + timeo;
while (now.tv_sec < endtime) {
error = error2 = 0;
if (!msgreq.lmr_answered)
error = msleep(&msgreq, nfs_lock_mutex, PCATCH | PUSER, "lockd", &ts);
if (msgreq.lmr_answered) {
nmp = VTONMP(vp);
if ((msgreq.lmr_errno == ENOTSUP) && nmp &&
(nmp->nm_state & NFSSTA_LOCKSWORK)) {
error = EWOULDBLOCK;
} else {
error = 0;
}
break;
}
if (error != EWOULDBLOCK)
break;
nmp = VTONMP(vp);
if ((error2 = nfs_sigintr(nmp, NULL, vfs_context_thread(ctx), 0))) {
error = error2;
if (fl->l_type == F_UNLCK)
printf("nfs_vnop_advlock: aborting unlock request, error %d\n", error);
break;
}
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_flag & NFSMNT_NOLOCKS) {
lck_mtx_unlock(&nmp->nm_lock);
break;
}
interruptable = nmp->nm_flag & NFSMNT_INT;
lck_mtx_unlock(&nmp->nm_lock);
microuptime(&now);
}
if (error) {
nmp = VTONMP(vp);
if ((error2 = nfs_sigintr(nmp, NULL, vfs_context_thread(ctx), 0))) {
error = error2;
if (error2 != EINTR) {
if (fl->l_type == F_UNLCK)
printf("nfs_vnop_advlock: aborting unlock request, error %d\n", error);
break;
}
}
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_flag & NFSMNT_NOLOCKS) {
if (error == EWOULDBLOCK)
error = ENOTSUP;
lck_mtx_unlock(&nmp->nm_lock);
break;
}
interruptable = nmp->nm_flag & NFSMNT_INT;
if (error != EWOULDBLOCK) {
lck_mtx_unlock(&nmp->nm_lock);
if ((msgreq.lmr_errno == EINPROGRESS) &&
!(msg->lm_flags & LOCKD_MSG_CANCEL)) {
msg->lm_flags |= LOCKD_MSG_CANCEL;
nfs_lockdmsg_dequeue(&msgreq);
msg->lm_xid = nfs_lockxid_get();
nfs_lockdmsg_enqueue(&msgreq);
msgreq.lmr_saved_errno = error;
msgreq.lmr_errno = 0;
msgreq.lmr_answered = 0;
timeo = 2;
continue;
}
break;
}
microuptime(&now);
if ((msgreq.lmr_errno != EINPROGRESS) &&
!(msg->lm_flags & LOCKD_MSG_DENIED_GRACE) &&
(nmp->nm_tprintf_initial_delay != 0) &&
((lastmsg + nmp->nm_tprintf_delay) < now.tv_sec)) {
lck_mtx_unlock(&nmp->nm_lock);
lastmsg = now.tv_sec;
nfs_down(nmp, vfs_context_thread(ctx), 0, NFSSTA_LOCKTIMEO, "lockd not responding");
wentdown = 1;
} else
lck_mtx_unlock(&nmp->nm_lock);
if (msgreq.lmr_errno == EINPROGRESS) {
msg->lm_flags |= LOCKD_MSG_CANCEL;
nfs_lockdmsg_dequeue(&msgreq);
msg->lm_xid = nfs_lockxid_get();
nfs_lockdmsg_enqueue(&msgreq);
msgreq.lmr_saved_errno = msgreq.lmr_errno;
msgreq.lmr_errno = 0;
msgreq.lmr_answered = 0;
timeo = 2;
continue;
}
if (msg->lm_flags & LOCKD_MSG_DENIED_GRACE) {
msg->lm_flags &= ~LOCKD_MSG_DENIED_GRACE;
nfs_lockdmsg_dequeue(&msgreq);
msg->lm_xid = nfs_lockxid_get();
nfs_lockdmsg_enqueue(&msgreq);
msgreq.lmr_saved_errno = 0;
msgreq.lmr_errno = 0;
msgreq.lmr_answered = 0;
timeo = 2;
continue;
}
timeo *= 2;
if (timeo > 60)
timeo = 60;
continue;
}
nfs_up(VTONMP(vp), vfs_context_thread(ctx), NFSSTA_LOCKTIMEO,
wentdown ? "lockd alive again" : NULL);
wentdown = 0;
if (msgreq.lmr_answered && (msg->lm_flags & LOCKD_MSG_DENIED_GRACE)) {
timeo = 4;
msgreq.lmr_answered = 0;
goto wait_for_granted;
}
if (msgreq.lmr_errno == EINPROGRESS) {
timeo = 60;
msgreq.lmr_answered = 0;
goto wait_for_granted;
}
if ((msg->lm_flags & LOCKD_MSG_CANCEL) &&
(msgreq.lmr_saved_errno == EINPROGRESS)) {
msg->lm_flags &= ~LOCKD_MSG_CANCEL;
nfs_lockdmsg_dequeue(&msgreq);
msg->lm_xid = nfs_lockxid_get();
nfs_lockdmsg_enqueue(&msgreq);
msgreq.lmr_saved_errno = 0;
msgreq.lmr_errno = 0;
msgreq.lmr_answered = 0;
timeo = 2;
continue;
}
if ((msg->lm_flags & LOCKD_MSG_TEST) && msgreq.lmr_errno == 0) {
if (msg->lm_fl.l_type != F_UNLCK) {
fl->l_type = msg->lm_fl.l_type;
fl->l_pid = msg->lm_fl.l_pid;
fl->l_start = msg->lm_fl.l_start;
fl->l_len = msg->lm_fl.l_len;
fl->l_whence = SEEK_SET;
} else
fl->l_type = F_UNLCK;
}
if (msg->lm_flags & LOCKD_MSG_CANCEL) {
msg->lm_flags &= ~LOCKD_MSG_CANCEL;
error = msgreq.lmr_saved_errno;
} else
error = msgreq.lmr_errno;
nmp = VTONMP(vp);
if ((error == ENOTSUP) && nmp && !(nmp->nm_state & NFSSTA_LOCKSWORK)) {
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_flag |= NFSMNT_NOLOCKS;
nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
lck_mtx_unlock(&nmp->nm_lock);
printf("lockd returned ENOTSUP, disabling locks for nfs server: %s\n",
vfs_statfs(nmp->nm_mountp)->f_mntfromname);
}
if (!error) {
if (nmp) {
lck_mtx_lock(&nmp->nm_lock);
if (!(nmp->nm_state & NFSSTA_LOCKSWORK))
nmp->nm_state |= NFSSTA_LOCKSWORK;
lck_mtx_unlock(&nmp->nm_lock);
}
if ((lockpidcheck == ENOENT) &&
((ap->a_op == F_SETLK) || (ap->a_op == F_SETLKW))) {
error = nfs_lock_pid_check(p, 1);
if (error) {
nfs_lock_pid_hash_trusted = 0;
printf("nfs_vnop_advlock: pid add failed - no longer trusted\n");
}
}
}
break;
}
nfs_lockdmsg_dequeue(&msgreq);
lck_mtx_unlock(nfs_lock_mutex);
return (error);
}
int
nfslockdans(proc_t p, struct lockd_ans *ansp)
{
LOCKD_MSG_REQUEST *msgreq;
int error;
error = proc_suser(p);
if (error)
return (error);
if (ansp->la_version != LOCKD_ANS_VERSION)
return (EINVAL);
lck_mtx_lock(nfs_lock_mutex);
msgreq = nfs_lockdmsg_find_by_xid(ansp->la_xid);
if (ansp->la_flags & LOCKD_ANS_GRANTED) {
if (!msgreq || nfs_lockdmsg_compare_to_answer(msgreq, ansp))
msgreq = nfs_lockdmsg_find_by_answer(ansp);
if (msgreq && (msgreq->lmr_msg.lm_flags & LOCKD_MSG_CANCEL))
msgreq = NULL;
}
if (!msgreq) {
lck_mtx_unlock(nfs_lock_mutex);
return (EPIPE);
}
msgreq->lmr_errno = ansp->la_errno;
if ((msgreq->lmr_msg.lm_flags & LOCKD_MSG_TEST) && msgreq->lmr_errno == 0) {
if (ansp->la_flags & LOCKD_ANS_LOCK_INFO) {
if (ansp->la_flags & LOCKD_ANS_LOCK_EXCL)
msgreq->lmr_msg.lm_fl.l_type = F_WRLCK;
else
msgreq->lmr_msg.lm_fl.l_type = F_RDLCK;
msgreq->lmr_msg.lm_fl.l_pid = ansp->la_pid;
msgreq->lmr_msg.lm_fl.l_start = ansp->la_start;
msgreq->lmr_msg.lm_fl.l_len = ansp->la_len;
} else {
msgreq->lmr_msg.lm_fl.l_type = F_UNLCK;
}
}
if (ansp->la_flags & LOCKD_ANS_DENIED_GRACE)
msgreq->lmr_msg.lm_flags |= LOCKD_MSG_DENIED_GRACE;
msgreq->lmr_answered = 1;
lck_mtx_unlock(nfs_lock_mutex);
wakeup(msgreq);
return (0);
}