Implement client side NFS locks.

Obtained from: BSD/os
Import Ok'd by: mckusick, jkh, motd on builder.freebsd.org
This commit is contained in:
Alfred Perlstein 2001-04-17 20:45:23 +00:00
parent 0dfba3cef1
commit 603c86672c
28 changed files with 1578 additions and 27 deletions

View File

@ -27,7 +27,8 @@ enum nlm_stats {
nlm_denied = 1,
nlm_denied_nolocks = 2,
nlm_blocked = 3,
nlm_denied_grace_period = 4
nlm_denied_grace_period = 4,
nlm_deadlck = 5
};
struct nlm_holder {
@ -147,7 +148,7 @@ struct nlm_notify {
enum nlm4_stats {
nlm4_granted = 0,
nlm4_denied = 1,
nlm4_denied_nolock = 2,
nlm4_denied_nolocks = 2,
nlm4_blocked = 3,
nlm4_denied_grace_period = 4,
nlm4_deadlck = 5,
@ -253,11 +254,17 @@ struct nlm_sm_status {
opaque priv[16]; /* private data */
};
struct nlm4_notify {
string name<MAXNAMELEN>;
int32_t state;
};
/*
* Over-the-wire protocol used between the network lock managers
*/
program NLM_PROG {
version NLM_SM {
void NLM_SM_NOTIFY(struct nlm_sm_status) = 1;
} = 0;
@ -316,6 +323,6 @@ program NLM_PROG {
nlm4_shareres NLM4_SHARE(nlm4_shareargs) = 20;
nlm4_shareres NLM4_UNSHARE(nlm4_shareargs) = 21;
nlm4_res NLM4_NM_LOCK(nlm4_lockargs) = 22;
void NLM4_FREE_ALL(nlm_notify) = 23;
void NLM4_FREE_ALL(nlm4_notify) = 23;
} = 4;
} = 100021;

View File

@ -1101,6 +1101,7 @@ nfs/bootp_subr.c optional bootp
nfs/krpc_subr.c optional bootp
nfs/nfs_bio.c optional nfs
nfs/nfs_node.c optional nfs
nfs/nfs_lock.c optional nfs
nfs/nfs_nqlease.c optional nfs
nfs/nfs_serv.c optional nfs
nfs/nfs_socket.c optional nfs

View File

@ -69,7 +69,7 @@ static int lockf_debug = 0;
SYSCTL_INT(_debug, OID_AUTO, lockf_debug, CTLFLAG_RW, &lockf_debug, 0, "");
#endif
static MALLOC_DEFINE(M_LOCKF, "lockf", "Byte-range locking structures");
MALLOC_DEFINE(M_LOCKF, "lockf", "Byte-range locking structures");
#define NOLOCKF (struct lockf *)0
#define SELF 0x1

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

269
sys/nfs/nfs_lock.c Normal file
View File

@ -0,0 +1,269 @@
/*-
* Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from BSDI nfs_lock.c,v 2.4 1998/12/14 23:49:56 jch Exp
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h> /* for hz */
#include <sys/lockf.h> /* for hz */
#include <net/if.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsmount.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_lock.h>
#include <nfs/nlminfo.h>
#define NFSOWNER_1ST_LEVEL_START 1 /* initial entries */
#define NFSOWNER_2ND_LEVEL 256 /* some power of 2 */
#define NFSOWNER(tbl, i) \
(tbl)[(i) / NFSOWNER_2ND_LEVEL][(i) % NFSOWNER_2ND_LEVEL]
/*
* XXX
* We have to let the process know if the call succeeded. I'm using an extra
* field in the p_nlminfo field in the proc structure, as it is already for
* lockd stuff.
*/
/*
* nfs_advlock --
* NFS advisory byte-level locks.
*/
int
nfs_dolock(ap)
struct vop_advlock_args /* {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
} */ *ap;
{
LOCKD_MSG msg;
struct nameidata nd;
struct proc *p;
uid_t saved_uid;
struct vnode *vp, *wvp;
int error, error1;
struct flock *fl;
int fmode, ioflg;
p = curproc;
vp = ap->a_vp;
fl = ap->a_fl;
/*
* the NLM protocol doesn't allow the server to return an error
* on ranges, so we do it. Note that we should be returning
* EOVERFLOW in some cases, but we don't have it.
*/
if (fl->l_start < 0 || fl->l_len < 0 ||
((fl->l_len != 0 &&
(fl->l_start + fl->l_len - 1) < 0)))
return (EINVAL);
/*
* Fill in the information structure.
*/
msg.lm_version = LOCKD_MSG_VERSION;
msg.lm_msg_ident.pid = p->p_pid;
/*
* if there is no nfsowner table yet, allocate one.
*/
if (p->p_nlminfo == NULL) {
MALLOC(p->p_nlminfo, struct nlminfo *,
sizeof(struct nlminfo), M_LOCKF, M_WAITOK | M_ZERO);
p->p_nlminfo->pid_start = p->p_stats->p_start;
}
msg.lm_msg_ident.pid_start = p->p_nlminfo->pid_start;
msg.lm_msg_ident.msg_seq = ++(p->p_nlminfo->msg_seq);
msg.lm_fl = *fl;
msg.lm_wait = ap->a_flags & F_WAIT;
msg.lm_getlk = ap->a_op == F_GETLK;
/*
* XXX -- I think this is wrong for anything other AF_INET.
*/
msg.lm_addr = *(VFSTONFS(vp->v_mount)->nm_nam);
msg.lm_fh_len = NFS_ISV3(vp) ? VTONFS(vp)->n_fhsize : NFSX_V2FH;
bcopy(VTONFS(vp)->n_fhp, msg.lm_fh, msg.lm_fh_len);
msg.lm_nfsv3 = NFS_ISV3(vp);
msg.lm_cred = *(p->p_ucred);
/*
* Open the lock fifo. If for any reason we don't find the fifo, it
* means that the lock daemon isn't running. Translate any missing
* file error message for the user, otherwise the application will
* complain that the user's file is missing, which isn't the case.
* Note that we use proc0's cred, so the fifo is opened as root.
*/
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, _PATH_LCKFIFO, p);
/*
* XXX Hack to temporarily allow this process (regardless of it's creds)
* to open the fifo we need to write to. vn_open() really should
* take a ucred (and once it does, this code should be fixed to use
* proc0's ucred.
*/
saved_uid = p->p_ucred->cr_uid;
p->p_ucred->cr_uid = 0; /* temporarly run the vn_open as root */
fmode = FFLAGS(O_WRONLY);
error = vn_open(&nd, &fmode, 0);
p->p_ucred->cr_uid = saved_uid;
if (error != 0) {
return (error == ENOENT ? EOPNOTSUPP : error);
}
wvp = nd.ni_vp;
VOP_UNLOCK(wvp, 0, p); /* vn_open leaves it locked */
ioflg = IO_UNIT;
for (;;) {
VOP_LEASE(wvp, p, proc0.p_ucred, LEASE_WRITE);
error = vn_rdwr(UIO_WRITE, wvp, (caddr_t)&msg, sizeof(msg), 0,
UIO_SYSSPACE, ioflg, proc0.p_ucred, NULL, p);
if (error && (((ioflg & IO_NDELAY) == 0) || error != EAGAIN)) {
break;
}
/*
* If we're locking a file, wait for an answer. Unlocks succeed
* immediately.
*/
if (fl->l_type == F_UNLCK)
/*
* XXX this isn't exactly correct. The client side
* needs to continue sending it's unlock until
* it gets a responce back.
*/
break;
/*
* retry after 20 seconds if we haven't gotten a responce yet.
* This number was picked out of thin air... but is longer
* then even a reasonably loaded system should take (at least
* on a local network). XXX Probably should use a back-off
* scheme.
*/
if ((error = tsleep((void *)p->p_nlminfo,
PCATCH | PUSER, "lockd", 20*hz)) != 0) {
if (error == EWOULDBLOCK) {
/*
* We timed out, so we rewrite the request
* to the fifo, but only if it isn't already
* full.
*/
ioflg |= IO_NDELAY;
continue;
}
break;
}
if (msg.lm_getlk && p->p_nlminfo->retcode == 0) {
if (p->p_nlminfo->set_getlk_pid) {
fl->l_pid = p->p_nlminfo->getlk_pid;
} else {
fl->l_type = F_UNLCK;
}
}
error = p->p_nlminfo->retcode;
break;
}
if ((error1 = vn_close(wvp, FWRITE, proc0.p_ucred, p)) && error == 0)
return (error1);
return (error);
}
/*
* nfslockdans --
* NFS advisory byte-level locks answer from the lock daemon.
*/
int
nfslockdans(p, ansp)
struct proc *p;
struct lockd_ans *ansp;
{
int error;
/* Let root, or someone who once was root (lockd generally
* switches to the daemon uid once it is done setting up) make
* this call
*/
if ((error = suser(p)) != 0 && p->p_cred->p_svuid != 0)
return (error);
/* the version should match, or we're out of sync */
if (ansp->la_vers != LOCKD_ANS_VERSION)
return (EINVAL);
/* Find the process, set its return errno and wake it up. */
if ((p = pfind(ansp->la_msg_ident.pid)) == NULL)
return (ESRCH);
/* verify the pid hasn't been reused (if we can), and it isn't waiting
* for an answer from a more recent request. We return an EPIPE if
* the match fails, because we've already used ESRCH above, and this
* is sort of like writing on a pipe after the reader has closed it.
*/
if (p->p_nlminfo == NULL ||
((ansp->la_msg_ident.msg_seq != -1) &&
(timevalcmp(&p->p_nlminfo->pid_start,
&ansp->la_msg_ident.pid_start, !=) ||
p->p_nlminfo->msg_seq != ansp->la_msg_ident.msg_seq)))
return (EPIPE);
p->p_nlminfo->retcode = ansp->la_errno;
p->p_nlminfo->set_getlk_pid = ansp->la_set_getlk_pid;
p->p_nlminfo->getlk_pid = ansp->la_getlk_pid;
(void)wakeup((void *)p->p_nlminfo);
return (0);
}

112
sys/nfs/nfs_lock.h Normal file
View File

@ -0,0 +1,112 @@
/*-
* Copyright (c) 1998 Berkeley Software Design, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from nfs_lock.h,v 2.2 1998/04/28 19:38:41 don Exp
* $FreeBSD$
*/
/*
* lockd uses the nfssvc system call to get the unique kernel services it needs.
* It passes in a request structure with a version number at the start.
* This prevents libc from needing to change if the information passed
* between lockd and the kernel needs to change.
*
* If a structure changes, you must bump the version number.
*/
#include <nfs/nfsproto.h>
#define LOCKD_REQ_VERSION 1
struct lockd_req {
int vers; /* keep in sync with kernel please */
int op; /* F_GETLK | F_SETLK | F_UNLCK */
int owner; /* owner of lock, -1 to allocate one */
int owner_rel_ok; /* release owner if no locks left ? */
int *owner_ret; /* owner alloc/free result target */
void *fh; /* NFS file handle */
size_t fh_len; /* NFS file handle length */
u_quad_t offset; /* offset of where to start lock */
u_quad_t len; /* length of range to lock */
int type; /* F_RDLCK | F_WRLCK | F_UNLCK */
struct ucred cred; /* user credentials to use for lock */
struct sockaddr saddr; /* XXX how about non AF_INET ?? */
int pid; /* pid of lock requester */
};
/*
* The fifo where the kernel writes requests for locks on remote NFS files,
* and where lockd reads these requests.
*
*/
#define _PATH_LCKFIFO "/var/run/lock"
/*
* This structure is used to uniquely identify the process which originated
* a particular message to lockd. A sequence number is used to differentiate
* multiple messages from the same process. A process start time is used to
* detect the unlikely, but possible, event of the recycling of a pid.
*/
struct lockd_msg_ident {
pid_t pid; /* The process ID. */
struct timeval pid_start; /* Start time of process id */
int msg_seq; /* Sequence number of message */
};
#define LOCKD_MSG_VERSION 1
/*
* The structure that the kernel hands us for each lock request.
*/
typedef struct __lock_msg {
int lm_version; /* which version is this */
struct lockd_msg_ident lm_msg_ident; /* originator of the message */
struct flock lm_fl; /* The lock request. */
int lm_wait; /* The F_WAIT flag. */
int lm_getlk; /* is this a F_GETLK request */
struct sockaddr lm_addr; /* The address. */
int lm_nfsv3; /* If NFS version 3. */
size_t lm_fh_len; /* The file handle length. */
struct ucred lm_cred; /* user cred for lock req */
u_int8_t lm_fh[NFS_SMALLFH];/* The file handle. */
} LOCKD_MSG;
#define LOCKD_ANS_VERSION 1
struct lockd_ans {
int la_vers;
struct lockd_msg_ident la_msg_ident; /* originator of the message */
int la_errno;
int la_set_getlk_pid; /* use returned pid */
int la_getlk_pid; /* returned pid for F_GETLK */
};
#ifdef _KERNEL
int nfs_dolock(struct vop_advlock_args *ap);
int nfslockdans(struct proc *p, struct lockd_ans *ansp);
int nfslockdreq(struct proc *p, struct lockd_req *reqp);
#endif

View File

@ -56,6 +56,8 @@
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@ -69,6 +71,7 @@
#include <nfs/nfsnode.h>
#include <nfs/nqnfs.h>
#include <nfs/nfsrtt.h>
#include <nfs/nfs_lock.h>
static MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure");
@ -152,6 +155,12 @@ nfssvc(p, uap)
#endif /* NFS_NOSERVER */
int error;
if ((uap->flag & NFSSVC_LOCKDANS) != 0) {
struct lockd_ans la;
error = copyin(uap->argp, &la, sizeof(la));
return (error != 0 ? error : nfslockdans(p, &la));
}
/*
* Must be super user
*/

View File

@ -76,6 +76,7 @@
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nqnfs.h>
#include <nfs/nfs_lock.h>
#include <net/if.h>
#include <netinet/in.h>
@ -3057,14 +3058,8 @@ nfs_advlock(ap)
int a_flags;
} */ *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
/*
* The following kludge is to allow diskless support to work
* until a real NFS lockd is implemented. Basically, just pretend
* that this is a local lock.
*/
return (lf_advlock(ap, &(np->n_lockf), np->n_size));
return (nfs_dolock(ap));
}
/*

45
sys/nfs/nlminfo.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 1998 Berkeley Software Design, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from BSDI nlminfo.h,v 2.1 1998/03/18 01:30:38 don Exp
* $FreeBSD$
*/
/*
* Misc NLM informationi, some needed for the master lockd process, and some
* needed by every process doing nlm based locking.
*/
struct nlminfo {
/* these are used by any process doing nlm locking */
int msg_seq; /* sequence counter for lock requests */
int retcode; /* return code for lock requests */
int set_getlk_pid;
int getlk_pid;
struct timeval pid_start; /* process starting time */
};
extern void nlminfo_release __P((struct proc *p));

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

269
sys/nfsclient/nfs_lock.c Normal file
View File

@ -0,0 +1,269 @@
/*-
* Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from BSDI nfs_lock.c,v 2.4 1998/12/14 23:49:56 jch Exp
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/unistd.h>
#include <sys/vnode.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h> /* for hz */
#include <sys/lockf.h> /* for hz */
#include <net/if.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsmount.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_lock.h>
#include <nfs/nlminfo.h>
#define NFSOWNER_1ST_LEVEL_START 1 /* initial entries */
#define NFSOWNER_2ND_LEVEL 256 /* some power of 2 */
#define NFSOWNER(tbl, i) \
(tbl)[(i) / NFSOWNER_2ND_LEVEL][(i) % NFSOWNER_2ND_LEVEL]
/*
* XXX
* We have to let the process know if the call succeeded. I'm using an extra
* field in the p_nlminfo field in the proc structure, as it is already for
* lockd stuff.
*/
/*
* nfs_advlock --
* NFS advisory byte-level locks.
*/
int
nfs_dolock(ap)
struct vop_advlock_args /* {
struct vnode *a_vp;
caddr_t a_id;
int a_op;
struct flock *a_fl;
int a_flags;
} */ *ap;
{
LOCKD_MSG msg;
struct nameidata nd;
struct proc *p;
uid_t saved_uid;
struct vnode *vp, *wvp;
int error, error1;
struct flock *fl;
int fmode, ioflg;
p = curproc;
vp = ap->a_vp;
fl = ap->a_fl;
/*
* the NLM protocol doesn't allow the server to return an error
* on ranges, so we do it. Note that we should be returning
* EOVERFLOW in some cases, but we don't have it.
*/
if (fl->l_start < 0 || fl->l_len < 0 ||
((fl->l_len != 0 &&
(fl->l_start + fl->l_len - 1) < 0)))
return (EINVAL);
/*
* Fill in the information structure.
*/
msg.lm_version = LOCKD_MSG_VERSION;
msg.lm_msg_ident.pid = p->p_pid;
/*
* if there is no nfsowner table yet, allocate one.
*/
if (p->p_nlminfo == NULL) {
MALLOC(p->p_nlminfo, struct nlminfo *,
sizeof(struct nlminfo), M_LOCKF, M_WAITOK | M_ZERO);
p->p_nlminfo->pid_start = p->p_stats->p_start;
}
msg.lm_msg_ident.pid_start = p->p_nlminfo->pid_start;
msg.lm_msg_ident.msg_seq = ++(p->p_nlminfo->msg_seq);
msg.lm_fl = *fl;
msg.lm_wait = ap->a_flags & F_WAIT;
msg.lm_getlk = ap->a_op == F_GETLK;
/*
* XXX -- I think this is wrong for anything other AF_INET.
*/
msg.lm_addr = *(VFSTONFS(vp->v_mount)->nm_nam);
msg.lm_fh_len = NFS_ISV3(vp) ? VTONFS(vp)->n_fhsize : NFSX_V2FH;
bcopy(VTONFS(vp)->n_fhp, msg.lm_fh, msg.lm_fh_len);
msg.lm_nfsv3 = NFS_ISV3(vp);
msg.lm_cred = *(p->p_ucred);
/*
* Open the lock fifo. If for any reason we don't find the fifo, it
* means that the lock daemon isn't running. Translate any missing
* file error message for the user, otherwise the application will
* complain that the user's file is missing, which isn't the case.
* Note that we use proc0's cred, so the fifo is opened as root.
*/
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, _PATH_LCKFIFO, p);
/*
* XXX Hack to temporarily allow this process (regardless of it's creds)
* to open the fifo we need to write to. vn_open() really should
* take a ucred (and once it does, this code should be fixed to use
* proc0's ucred.
*/
saved_uid = p->p_ucred->cr_uid;
p->p_ucred->cr_uid = 0; /* temporarly run the vn_open as root */
fmode = FFLAGS(O_WRONLY);
error = vn_open(&nd, &fmode, 0);
p->p_ucred->cr_uid = saved_uid;
if (error != 0) {
return (error == ENOENT ? EOPNOTSUPP : error);
}
wvp = nd.ni_vp;
VOP_UNLOCK(wvp, 0, p); /* vn_open leaves it locked */
ioflg = IO_UNIT;
for (;;) {
VOP_LEASE(wvp, p, proc0.p_ucred, LEASE_WRITE);
error = vn_rdwr(UIO_WRITE, wvp, (caddr_t)&msg, sizeof(msg), 0,
UIO_SYSSPACE, ioflg, proc0.p_ucred, NULL, p);
if (error && (((ioflg & IO_NDELAY) == 0) || error != EAGAIN)) {
break;
}
/*
* If we're locking a file, wait for an answer. Unlocks succeed
* immediately.
*/
if (fl->l_type == F_UNLCK)
/*
* XXX this isn't exactly correct. The client side
* needs to continue sending it's unlock until
* it gets a responce back.
*/
break;
/*
* retry after 20 seconds if we haven't gotten a responce yet.
* This number was picked out of thin air... but is longer
* then even a reasonably loaded system should take (at least
* on a local network). XXX Probably should use a back-off
* scheme.
*/
if ((error = tsleep((void *)p->p_nlminfo,
PCATCH | PUSER, "lockd", 20*hz)) != 0) {
if (error == EWOULDBLOCK) {
/*
* We timed out, so we rewrite the request
* to the fifo, but only if it isn't already
* full.
*/
ioflg |= IO_NDELAY;
continue;
}
break;
}
if (msg.lm_getlk && p->p_nlminfo->retcode == 0) {
if (p->p_nlminfo->set_getlk_pid) {
fl->l_pid = p->p_nlminfo->getlk_pid;
} else {
fl->l_type = F_UNLCK;
}
}
error = p->p_nlminfo->retcode;
break;
}
if ((error1 = vn_close(wvp, FWRITE, proc0.p_ucred, p)) && error == 0)
return (error1);
return (error);
}
/*
* nfslockdans --
* NFS advisory byte-level locks answer from the lock daemon.
*/
int
nfslockdans(p, ansp)
struct proc *p;
struct lockd_ans *ansp;
{
int error;
/* Let root, or someone who once was root (lockd generally
* switches to the daemon uid once it is done setting up) make
* this call
*/
if ((error = suser(p)) != 0 && p->p_cred->p_svuid != 0)
return (error);
/* the version should match, or we're out of sync */
if (ansp->la_vers != LOCKD_ANS_VERSION)
return (EINVAL);
/* Find the process, set its return errno and wake it up. */
if ((p = pfind(ansp->la_msg_ident.pid)) == NULL)
return (ESRCH);
/* verify the pid hasn't been reused (if we can), and it isn't waiting
* for an answer from a more recent request. We return an EPIPE if
* the match fails, because we've already used ESRCH above, and this
* is sort of like writing on a pipe after the reader has closed it.
*/
if (p->p_nlminfo == NULL ||
((ansp->la_msg_ident.msg_seq != -1) &&
(timevalcmp(&p->p_nlminfo->pid_start,
&ansp->la_msg_ident.pid_start, !=) ||
p->p_nlminfo->msg_seq != ansp->la_msg_ident.msg_seq)))
return (EPIPE);
p->p_nlminfo->retcode = ansp->la_errno;
p->p_nlminfo->set_getlk_pid = ansp->la_set_getlk_pid;
p->p_nlminfo->getlk_pid = ansp->la_getlk_pid;
(void)wakeup((void *)p->p_nlminfo);
return (0);
}

112
sys/nfsclient/nfs_lock.h Normal file
View File

@ -0,0 +1,112 @@
/*-
* Copyright (c) 1998 Berkeley Software Design, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from nfs_lock.h,v 2.2 1998/04/28 19:38:41 don Exp
* $FreeBSD$
*/
/*
* lockd uses the nfssvc system call to get the unique kernel services it needs.
* It passes in a request structure with a version number at the start.
* This prevents libc from needing to change if the information passed
* between lockd and the kernel needs to change.
*
* If a structure changes, you must bump the version number.
*/
#include <nfs/nfsproto.h>
#define LOCKD_REQ_VERSION 1
struct lockd_req {
int vers; /* keep in sync with kernel please */
int op; /* F_GETLK | F_SETLK | F_UNLCK */
int owner; /* owner of lock, -1 to allocate one */
int owner_rel_ok; /* release owner if no locks left ? */
int *owner_ret; /* owner alloc/free result target */
void *fh; /* NFS file handle */
size_t fh_len; /* NFS file handle length */
u_quad_t offset; /* offset of where to start lock */
u_quad_t len; /* length of range to lock */
int type; /* F_RDLCK | F_WRLCK | F_UNLCK */
struct ucred cred; /* user credentials to use for lock */
struct sockaddr saddr; /* XXX how about non AF_INET ?? */
int pid; /* pid of lock requester */
};
/*
* The fifo where the kernel writes requests for locks on remote NFS files,
* and where lockd reads these requests.
*
*/
#define _PATH_LCKFIFO "/var/run/lock"
/*
* This structure is used to uniquely identify the process which originated
* a particular message to lockd. A sequence number is used to differentiate
* multiple messages from the same process. A process start time is used to
* detect the unlikely, but possible, event of the recycling of a pid.
*/
struct lockd_msg_ident {
pid_t pid; /* The process ID. */
struct timeval pid_start; /* Start time of process id */
int msg_seq; /* Sequence number of message */
};
#define LOCKD_MSG_VERSION 1
/*
* The structure that the kernel hands us for each lock request.
*/
typedef struct __lock_msg {
int lm_version; /* which version is this */
struct lockd_msg_ident lm_msg_ident; /* originator of the message */
struct flock lm_fl; /* The lock request. */
int lm_wait; /* The F_WAIT flag. */
int lm_getlk; /* is this a F_GETLK request */
struct sockaddr lm_addr; /* The address. */
int lm_nfsv3; /* If NFS version 3. */
size_t lm_fh_len; /* The file handle length. */
struct ucred lm_cred; /* user cred for lock req */
u_int8_t lm_fh[NFS_SMALLFH];/* The file handle. */
} LOCKD_MSG;
#define LOCKD_ANS_VERSION 1
struct lockd_ans {
int la_vers;
struct lockd_msg_ident la_msg_ident; /* originator of the message */
int la_errno;
int la_set_getlk_pid; /* use returned pid */
int la_getlk_pid; /* returned pid for F_GETLK */
};
#ifdef _KERNEL
int nfs_dolock(struct vop_advlock_args *ap);
int nfslockdans(struct proc *p, struct lockd_ans *ansp);
int nfslockdreq(struct proc *p, struct lockd_req *reqp);
#endif

View File

@ -56,6 +56,8 @@
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@ -69,6 +71,7 @@
#include <nfs/nfsnode.h>
#include <nfs/nqnfs.h>
#include <nfs/nfsrtt.h>
#include <nfs/nfs_lock.h>
static MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure");
@ -152,6 +155,12 @@ nfssvc(p, uap)
#endif /* NFS_NOSERVER */
int error;
if ((uap->flag & NFSSVC_LOCKDANS) != 0) {
struct lockd_ans la;
error = copyin(uap->argp, &la, sizeof(la));
return (error != 0 ? error : nfslockdans(p, &la));
}
/*
* Must be super user
*/

View File

@ -76,6 +76,7 @@
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nqnfs.h>
#include <nfs/nfs_lock.h>
#include <net/if.h>
#include <netinet/in.h>
@ -3057,14 +3058,8 @@ nfs_advlock(ap)
int a_flags;
} */ *ap;
{
register struct nfsnode *np = VTONFS(ap->a_vp);
/*
* The following kludge is to allow diskless support to work
* until a real NFS lockd is implemented. Basically, just pretend
* that this is a local lock.
*/
return (lf_advlock(ap, &(np->n_lockf), np->n_size));
return (nfs_dolock(ap));
}
/*

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

45
sys/nfsclient/nlminfo.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 1998 Berkeley Software Design, Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from BSDI nlminfo.h,v 2.1 1998/03/18 01:30:38 don Exp
* $FreeBSD$
*/
/*
* Misc NLM informationi, some needed for the master lockd process, and some
* needed by every process doing nlm based locking.
*/
struct nlminfo {
/* these are used by any process doing nlm locking */
int msg_seq; /* sequence counter for lock requests */
int retcode; /* return code for lock requests */
int set_getlk_pid;
int getlk_pid;
struct timeval pid_start; /* process starting time */
};
extern void nlminfo_release __P((struct proc *p));

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

View File

@ -56,6 +56,8 @@
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/namei.h>
#include <sys/fcntl.h>
#include <sys/lockf.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@ -69,6 +71,7 @@
#include <nfs/nfsnode.h>
#include <nfs/nqnfs.h>
#include <nfs/nfsrtt.h>
#include <nfs/nfs_lock.h>
static MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure");
@ -152,6 +155,12 @@ nfssvc(p, uap)
#endif /* NFS_NOSERVER */
int error;
if ((uap->flag & NFSSVC_LOCKDANS) != 0) {
struct lockd_ans la;
error = copyin(uap->argp, &la, sizeof(la));
return (error != 0 ? error : nfslockdans(p, &la));
}
/*
* Must be super user
*/

View File

@ -274,6 +274,7 @@ struct nfsstats {
#define NFSSVC_GOTAUTH 0x040
#define NFSSVC_AUTHINFAIL 0x080
#define NFSSVC_MNTD 0x100
#define NFSSVC_LOCKDANS 0x200
/*
* fs.nfs sysctl(3) identifiers

View File

@ -44,6 +44,8 @@
struct vop_advlock_args;
MALLOC_DECLARE(M_LOCKF);
/*
* The lockf structure is a kernel structure which contains the information
* associated with a byte range lock. The lockf structures are linked into

View File

@ -146,6 +146,7 @@ struct pargs {
* for write access.
*/
struct ithd;
struct nlminfo;
struct proc {
TAILQ_ENTRY(proc) p_procq; /* (j) Run/mutex queue. */
@ -235,6 +236,8 @@ struct proc {
struct mtx *p_blocked; /* (j) Mutex process is blocked on. */
const char *p_mtxname; /* (j) Name of mutex blocked on. */
LIST_HEAD(, mtx) p_contested; /* (j) Contested locks. */
struct nlminfo *p_nlminfo; /* (?) only used by/for lockd */
void *p_aioinfo; /* (c) ASYNC I/O info. */
struct ithd *p_ithd; /* (b) For interrupt threads only. */
int p_intr_nesting_level; /* (k) Interrupt recursion. */

View File

@ -2,11 +2,11 @@
# $NetBSD: Makefile,v 1.12 2000/08/07 16:23:31 thorpej Exp $
PROG= rpc.lockd
SRCS= nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c
SRCS= kern.c nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c
MAN= rpc.lockd.8
MLINKS= rpc.lockd.8 lockd.8
CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc
CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc -g
DPADD= ${LIBRPCSVC} ${LIBUTIL}
LDADD= -lrpcsvc -lutil

586
usr.sbin/rpc.lockd/kern.c Normal file
View File

@ -0,0 +1,586 @@
/*-
* Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Berkeley Software Design Inc's name may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from BSDI kern.c,v 1.2 1998/11/25 22:38:27 don Exp
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "nlm_prot.h"
#include "nfs/nfs_lock.h"
#include "lockd.h"
#include "lockd_lock.h"
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#define nfslockdans(_v, _ansp) \
((_ansp)->la_vers = _v, \
nfssvc(NFSSVC_LOCKDANS, _ansp))
/* Lock request owner. */
typedef struct __owner {
pid_t pid; /* Process ID. */
time_t tod; /* Time-of-day. */
} OWNER;
static OWNER owner;
static char hostname[MAXHOSTNAMELEN + 1]; /* Hostname. */
int lock_request(LOCKD_MSG *);
int test_request(LOCKD_MSG *);
void show(LOCKD_MSG *);
int unlock_request(LOCKD_MSG *);
/*
* will break because fifo needs to be repopened when EOF'd
*/
#ifdef SETUID_DAEMON
#define lockd_seteuid(uid) seteuid(uid)
#else
#define lockd_seteuid(uid) (1)
#endif
#define d_calls 0
#define d_args 0
void
client_cleanup(sig, code)
int sig;
int code;
{
(void)lockd_seteuid(0);
(void)unlink(_PATH_LCKFIFO);
exit(-1);
}
/*
* client_request --
* Loop around messages from the kernel, forwarding them off to
* NLM servers.
*/
pid_t
client_request(void)
{
LOCKD_MSG msg;
fd_set rdset;
int fd, nr, ret;
pid_t child;
/* Recreate the NLM fifo. */
(void)unlink(_PATH_LCKFIFO);
(void)umask(S_IXGRP|S_IXOTH);
if (mkfifo(_PATH_LCKFIFO, S_IWUSR | S_IRUSR)) {
syslog(LOG_ERR, "mkfifo: %s: %m", _PATH_LCKFIFO);
exit (1);
}
#define linebug syslog(LOG_ERR, "lockd: %d", __LINE__);
linebug
/*
* Create a separate process, the client code is really a separate
* daemon that shares a lot of code.
*/
switch (child = fork()) {
case -1:
err(1, "fork");
case 0:
break;
default:
return (child);
}
linebug
signal(SIGHUP, client_cleanup);
signal(SIGTERM, client_cleanup);
/* Setup. */
(void)time(&owner.tod);
owner.pid = getpid();
(void)gethostname(hostname, sizeof(hostname) - 1);
linebug
reopen:
/* Open the fifo for reading. */
if ((fd = open(_PATH_LCKFIFO, O_RDONLY /* | O_NONBLOCK */)) < 0)
syslog(LOG_ERR, "open: %s: %m", _PATH_LCKFIFO);
/* drop our root priviledges */
(void)lockd_seteuid(daemon_uid);
/* Set up the select. */
FD_ZERO(&rdset);
for (;;) {
linebug
/* Wait for contact... fifo's return EAGAIN when read with
* no data
*/
FD_SET(fd, &rdset);
(void)select(fd + 1, &rdset, NULL, NULL, NULL);
linebug
/* Read the fixed length message. */
if ((nr = read(fd, &msg, sizeof(msg))) == sizeof(msg)) {
if (d_args)
show(&msg);
if (msg.lm_version != LOCKD_MSG_VERSION) {
syslog(LOG_ERR,
"unknown msg type: %d", msg.lm_version);
}
/*
* Send it to the NLM server and don't grant the lock
* if we fail for any reason.
*/
switch (msg.lm_fl.l_type) {
case F_RDLCK:
case F_WRLCK:
if (msg.lm_getlk)
ret = test_request(&msg);
else
ret = lock_request(&msg);
break;
case F_UNLCK:
ret = unlock_request(&msg);
break;
default:
ret = 1;
syslog(LOG_ERR,
"unknown lock type: %d", msg.lm_fl.l_type);
break;
}
if (ret) {
struct lockd_ans ans;
ans.la_msg_ident = msg.lm_msg_ident;
ans.la_errno = EHOSTUNREACH;
if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
syslog((errno == EPIPE ? LOG_INFO :
LOG_ERR), "process %lu: %m",
(u_long)msg.lm_msg_ident.pid);
}
}
} else if (nr == -1) {
linebug
if (errno != EAGAIN) {
syslog(LOG_ERR, "read: %s: %m", _PATH_LCKFIFO);
goto err;
}
} else if (nr != 0) {
syslog(LOG_ERR,
"%s: discard %d bytes", _PATH_LCKFIFO, nr);
} if (nr == 0) {
close (fd);
goto reopen;
}
}
/* Reached only on error. */
err:
(void)lockd_seteuid(0);
(void)unlink(_PATH_LCKFIFO);
_exit (1);
}
void
set_auth(
CLIENT *cl,
struct ucred *ucred
)
{
if (cl->cl_auth != NULL)
cl->cl_auth->ah_ops->ah_destroy(cl->cl_auth);
cl->cl_auth = authunix_create(hostname,
ucred->cr_uid,
ucred->cr_groups[0],
ucred->cr_ngroups-1,
&ucred->cr_groups[1]);
}
/*
* test_request --
* Convert a lock LOCKD_MSG into an NLM request, and send it off.
*/
int
test_request(LOCKD_MSG *msg)
{
CLIENT *cli;
struct timeval timeout = {0, 0}; /* No timeout, no response. */
char dummy;
if (d_calls)
syslog(LOG_DEBUG, "test request: %s: %s to %s",
msg->lm_nfsv3 ? "V4" : "V1/3",
msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
from_addr((struct sockaddr_in *)&msg->lm_addr));
if (msg->lm_nfsv3) {
struct nlm4_testargs arg4;
arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
arg4.alock.caller_name = hostname;
arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg4.alock.fh.n_len = msg->lm_fh_len;
arg4.alock.oh.n_bytes = (char *)&owner;
arg4.alock.oh.n_len = sizeof(owner);
arg4.alock.svid = msg->lm_msg_ident.pid;
arg4.alock.l_offset = msg->lm_fl.l_start;
arg4.alock.l_len = msg->lm_fl.l_len;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS4)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_TEST_MSG,
xdr_nlm4_testargs, &arg4, xdr_void, &dummy, timeout);
} else {
struct nlm_testargs arg;
arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg.cookie.n_len = sizeof(msg->lm_msg_ident);
arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
arg.alock.caller_name = hostname;
arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg.alock.fh.n_len = msg->lm_fh_len;
arg.alock.oh.n_bytes = (char *)&owner;
arg.alock.oh.n_len = sizeof(owner);
arg.alock.svid = msg->lm_msg_ident.pid;
arg.alock.l_offset = msg->lm_fl.l_start;
arg.alock.l_len = msg->lm_fl.l_len;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_TEST_MSG,
xdr_nlm_testargs, &arg, xdr_void, &dummy, timeout);
}
return (0);
}
/*
* lock_request --
* Convert a lock LOCKD_MSG into an NLM request, and send it off.
*/
int
lock_request(LOCKD_MSG *msg)
{
CLIENT *cli;
struct nlm4_lockargs arg4;
struct nlm_lockargs arg;
struct timeval timeout = {0, 0}; /* No timeout, no response. */
char dummy;
if (d_calls)
syslog(LOG_DEBUG, "lock request: %s: %s to %s",
msg->lm_nfsv3 ? "V4" : "V1/3",
msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
from_addr((struct sockaddr_in *)&msg->lm_addr));
if (msg->lm_nfsv3) {
arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
arg4.block = msg->lm_wait ? 1 : 0;
arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
arg4.alock.caller_name = hostname;
arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg4.alock.fh.n_len = msg->lm_fh_len;
arg4.alock.oh.n_bytes = (char *)&owner;
arg4.alock.oh.n_len = sizeof(owner);
arg4.alock.svid = msg->lm_msg_ident.pid;
arg4.alock.l_offset = msg->lm_fl.l_start;
arg4.alock.l_len = msg->lm_fl.l_len;
arg4.reclaim = 0;
arg4.state = nsm_state;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS4)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_LOCK_MSG,
xdr_nlm4_lockargs, &arg4, xdr_void, &dummy, timeout);
} else {
arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg.cookie.n_len = sizeof(msg->lm_msg_ident);
arg.block = msg->lm_wait ? 1 : 0;
arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
arg.alock.caller_name = hostname;
arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg.alock.fh.n_len = msg->lm_fh_len;
arg.alock.oh.n_bytes = (char *)&owner;
arg.alock.oh.n_len = sizeof(owner);
arg.alock.svid = msg->lm_msg_ident.pid;
arg.alock.l_offset = msg->lm_fl.l_start;
arg.alock.l_len = msg->lm_fl.l_len;
arg.reclaim = 0;
arg.state = nsm_state;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_LOCK_MSG,
xdr_nlm_lockargs, &arg, xdr_void, &dummy, timeout);
}
return (0);
}
/*
* unlock_request --
* Convert an unlock LOCKD_MSG into an NLM request, and send it off.
*/
int
unlock_request(LOCKD_MSG *msg)
{
CLIENT *cli;
struct nlm4_unlockargs arg4;
struct nlm_unlockargs arg;
struct timeval timeout = {0, 0}; /* No timeout, no response. */
char dummy;
if (d_calls)
syslog(LOG_DEBUG, "unlock request: %s: to %s",
msg->lm_nfsv3 ? "V4" : "V1/3",
from_addr((struct sockaddr_in *)&msg->lm_addr));
if (msg->lm_nfsv3) {
arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
arg4.alock.caller_name = hostname;
arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg4.alock.fh.n_len = msg->lm_fh_len;
arg4.alock.oh.n_bytes = (char *)&owner;
arg4.alock.oh.n_len = sizeof(owner);
arg4.alock.svid = msg->lm_msg_ident.pid;
arg4.alock.l_offset = msg->lm_fl.l_start;
arg4.alock.l_len = msg->lm_fl.l_len;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS4)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_UNLOCK_MSG,
xdr_nlm4_unlockargs, &arg4, xdr_void, &dummy, timeout);
} else {
arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
arg.cookie.n_len = sizeof(msg->lm_msg_ident);
arg.alock.caller_name = hostname;
arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
arg.alock.fh.n_len = msg->lm_fh_len;
arg.alock.oh.n_bytes = (char *)&owner;
arg.alock.oh.n_len = sizeof(owner);
arg.alock.svid = msg->lm_msg_ident.pid;
arg.alock.l_offset = msg->lm_fl.l_start;
arg.alock.l_len = msg->lm_fl.l_len;
if ((cli = get_client(
(struct sockaddr *)&msg->lm_addr,
NLM_VERS)) == NULL)
return (1);
set_auth(cli, &msg->lm_cred);
(void)clnt_call(cli, NLM_UNLOCK_MSG,
xdr_nlm_unlockargs, &arg, xdr_void, &dummy, timeout);
}
return (0);
}
int
lock_answer(int pid, netobj *netcookie, int result, int *pid_p, int version)
{
struct lockd_ans ans;
if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
if (pid == -1) { /* we're screwed */
syslog(LOG_ERR, "inedible nlm cookie");
return -1;
}
ans.la_msg_ident.pid = pid;
ans.la_msg_ident.msg_seq = -1;
} else {
memcpy(&ans.la_msg_ident, netcookie->n_bytes,
sizeof(ans.la_msg_ident));
}
if (d_calls)
syslog(LOG_DEBUG, "lock answer: pid %lu: %s",
ans.la_msg_ident.pid, version == NLM_VERS4 ?
show_4state(result) : show_state(result));
ans.la_set_getlk_pid = 0;
if (version == NLM_VERS4)
switch (result) {
case nlm4_granted:
ans.la_errno = 0;
break;
default:
ans.la_errno = EACCES;
break;
case nlm4_denied:
if (pid_p == NULL)
ans.la_errno = EACCES;
else {
/* this is an answer to a nlm_test msg */
ans.la_set_getlk_pid = 1;
ans.la_getlk_pid = *pid_p;
ans.la_errno = 0;
}
break;
case nlm4_denied_nolocks:
ans.la_errno = EAGAIN;
break;
case nlm4_blocked:
return -1;
/* NOTREACHED */
case nlm4_denied_grace_period:
ans.la_errno = EAGAIN;
break;
case nlm4_deadlck:
ans.la_errno = EDEADLK;
break;
case nlm4_rofs:
ans.la_errno = EROFS;
break;
case nlm4_stale_fh:
ans.la_errno = ESTALE;
break;
case nlm4_fbig:
ans.la_errno = EFBIG;
break;
case nlm4_failed:
ans.la_errno = EACCES;
break;
}
else
switch (result) {
case nlm_granted:
ans.la_errno = 0;
break;
default:
ans.la_errno = EACCES;
break;
case nlm_denied:
if (pid_p == NULL)
ans.la_errno = EACCES;
else {
/* this is an answer to a nlm_test msg */
ans.la_set_getlk_pid = 1;
ans.la_getlk_pid = *pid_p;
ans.la_errno = 0;
}
break;
case nlm_denied_nolocks:
ans.la_errno = EAGAIN;
break;
case nlm_blocked:
return -1;
/* NOTREACHED */
case nlm_denied_grace_period:
ans.la_errno = EAGAIN;
break;
case nlm_deadlck:
ans.la_errno = EDEADLK;
break;
}
if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
syslog(((errno == EPIPE || errno == ESRCH) ?
LOG_INFO : LOG_ERR),
"process %lu: %m", (u_long)ans.la_msg_ident.pid);
return -1;
}
return 0;
}
/*
* show --
* Display the contents of a kernel LOCKD_MSG structure.
*/
void
show(LOCKD_MSG *mp)
{
static char hex[] = "0123456789abcdef";
struct fid *fidp;
fsid_t *fsidp;
size_t len;
u_int8_t *p, *t, buf[NFS_SMALLFH*3+1];
printf("process ID: %lu\n", (long)mp->lm_msg_ident.pid);
fsidp = (fsid_t *)&mp->lm_fh;
fidp = (struct fid *)((u_int8_t *)&mp->lm_fh + sizeof(fsid_t));
for (t = buf, p = (u_int8_t *)mp->lm_fh,
len = mp->lm_fh_len;
len > 0; ++p, --len) {
*t++ = '\\';
*t++ = hex[(*p & 0xf0) >> 4];
*t++ = hex[*p & 0x0f];
}
*t = '\0';
printf("fh_len %d, fh %s\n", mp->lm_fh_len, buf);
/* Show flock structure. */
printf("start %qu; len %qu; pid %lu; type %d; whence %d\n",
mp->lm_fl.l_start, mp->lm_fl.l_len, (u_long)mp->lm_fl.l_pid,
mp->lm_fl.l_type, mp->lm_fl.l_whence);
/* Show wait flag. */
printf("wait was %s\n", mp->lm_wait ? "set" : "not set");
}

View File

@ -634,10 +634,13 @@ nlm_granted_1_svc(arg, rqstp)
if (debug_level)
log_from_addr("nlm_granted", rqstp);
res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
nlm_granted, NULL, NLM_VERS) == 0 ?
nlm_granted : nlm_denied;
/* copy cookie from arg to result. See comment in nlm_test_1() */
res.cookie = arg->cookie;
res.stat.stat = nlm_granted;
return (&res);
}
@ -670,6 +673,8 @@ nlm_test_res_1_svc(arg, rqstp)
{
if (debug_level)
log_from_addr("nlm_test_res", rqstp);
(void)lock_answer(-1, &arg->cookie, arg->stat.stat,
&arg->stat.nlm_testrply_u.holder.svid, NLM_VERS);
return (NULL);
}
@ -686,6 +691,8 @@ nlm_lock_res_1_svc(arg, rqstp)
if (debug_level)
log_from_addr("nlm_lock_res", rqstp);
(void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS);
return (NULL);
}
@ -716,6 +723,9 @@ nlm_unlock_res_1_svc(arg, rqstp)
{
if (debug_level)
log_from_addr("nlm_unlock_res", rqstp);
lock_answer(&arg->cookie, arg->stat.stat, NLM_VERS);
return (NULL);
}
@ -1077,10 +1087,13 @@ nlm4_granted_4_svc(arg, rqstp)
if (debug_level)
log_from_addr("nlm4_granted", rqstp);
res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
nlm4_granted, NULL, NLM_VERS4) == 0 ?
nlm4_granted : nlm4_denied;
/* copy cookie from arg to result. See comment in nlm_test_1() */
res.cookie = arg->cookie;
res.stat.stat = nlm4_granted;
return (&res);
}
@ -1113,6 +1126,10 @@ nlm4_test_res_4_svc(arg, rqstp)
{
if (debug_level)
log_from_addr("nlm4_test_res", rqstp);
(void)lock_answer(-1, &arg->cookie, arg->stat.stat,
(int *)&arg->stat.nlm4_testrply_u.holder.svid,
NLM_VERS4);
return (NULL);
}
@ -1129,6 +1146,8 @@ nlm4_lock_res_4_svc(arg, rqstp)
if (debug_level)
log_from_addr("nlm4_lock_res", rqstp);
(void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS4);
return (NULL);
}
@ -1268,7 +1287,7 @@ nlm4_nm_lock_4_svc(arg, rqstp)
*/
void *
nlm4_free_all_4_svc(arg, rqstp)
nlm_notify *arg;
struct nlm4_notify *arg;
struct svc_req *rqstp;
{
static char dummy;

View File

@ -70,7 +70,11 @@ int debug_level = 0; /* 0 = no debugging syslog() calls */
int _rpcsvcdirty = 0;
int grace_expired;
int nsm_state;
pid_t client_pid;
struct mon mon_host;
void init_nsm(void);
int main __P((int, char **));
void nlm_prog_0 __P((struct svc_req *, SVCXPRT *));
void nlm_prog_1 __P((struct svc_req *, SVCXPRT *));
@ -208,6 +212,10 @@ main(argc, argv)
exit(1);
}
init_nsm();
client_pid = client_request();
svc_run(); /* Should never return */
exit(1);
}
@ -224,3 +232,50 @@ usage()
{
errx(1, "usage: rpc.lockd [-d <debuglevel>] [-g <grace period>]");
}
/*
* init_nsm --
* Reset the NSM state-of-the-world and acquire its state.
*/
void
init_nsm(void)
{
enum clnt_stat ret;
my_id id;
sm_stat stat;
/*
* !!!
* The my_id structure isn't used by the SM_UNMON_ALL call, as far
* as I know. Leave it empty for now.
*/
memset(&id, 0, sizeof(id));
id.my_name = "NFS NLM";
/*
* !!!
* The statd program must already be registered when lockd runs.
*/
do {
ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
xdr_my_id, &id, xdr_sm_stat, &stat);
if (ret == RPC_PROGUNAVAIL) {
warnx("%lu %s", SM_PROG, clnt_sperrno(ret));
sleep(2);
continue;
}
break;
} while (0);
if (ret != 0) {
errx(1, "%lu %s", SM_PROG, clnt_sperrno(ret));
}
nsm_state = stat.state;
/* setup constant data for SM_MON calls */
mon_host.mon_id.my_id.my_name = "localhost";
mon_host.mon_id.my_id.my_prog = NLM_PROG;
mon_host.mon_id.my_id.my_vers = NLM_SM;
mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */
}

View File

@ -37,3 +37,6 @@
extern int debug_level;
extern int grace_expired;
void sigchild_handler __P((int));
pid_t client_request(void);
extern int nsm_state;
extern pid_t client_pid;

View File

@ -162,7 +162,7 @@ getlock(lckarg, rqstp, flags)
syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
/* failed */
return (flags & LOCK_V4) ?
nlm4_denied_nolock : nlm_denied_nolocks;
nlm4_denied_nolocks : nlm_denied_nolocks;
}
if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
syslog(LOG_DEBUG, "recieved fhandle size %d, local size %d",
@ -177,7 +177,7 @@ getlock(lckarg, rqstp, flags)
syslog(LOG_NOTICE, "malloc failed: %s", strerror(errno));
free(newfl);
return (flags & LOCK_V4) ?
nlm4_denied_nolock : nlm_denied_nolocks;
nlm4_denied_nolocks : nlm_denied_nolocks;
}
newfl->client.oh.n_len = lckarg->alock.oh.n_len;
memcpy(newfl->client.oh.n_bytes, lckarg->alock.oh.n_bytes,
@ -191,7 +191,7 @@ getlock(lckarg, rqstp, flags)
free(newfl->client.oh.n_bytes);
free(newfl);
return (flags & LOCK_V4) ?
nlm4_denied_nolock : nlm_denied_nolocks;
nlm4_denied_nolocks : nlm_denied_nolocks;
}
memcpy(newfl->client_cookie.n_bytes, lckarg->cookie.n_bytes,
lckarg->cookie.n_len);
@ -462,7 +462,7 @@ do_lock(fl, block)
LIST_REMOVE(fl, lcklst);
close(fl->fd);
return (fl->flags & LOCK_V4) ?
nlm4_denied_nolock : nlm_denied_nolocks;
nlm4_denied_nolocks : nlm_denied_nolocks;
case 0:
/*
* Attempt a blocking lock. Will have to call