Home | History | Annotate | Line # | Download | only in common
      1 /*	$NetBSD: nfs_lock.c,v 1.3 2020/01/02 15:42:26 thorpej Exp $	*/
      2 /*-
      3  * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. Berkeley Software Design Inc's name may not be used to endorse or
     14  *    promote products derived from this software without specific prior
     15  *    written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND
     18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20  * ARE DISCLAIMED.  IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE
     21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  *
     29  *      from BSDI nfs_lock.c,v 2.4 1998/12/14 23:49:56 jch Exp
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 /* __FBSDID("FreeBSD: head/sys/nfs/nfs_lock.c 303382 2016-07-27 11:08:59Z kib "); */
     34 __RCSID("$NetBSD: nfs_lock.c,v 1.3 2020/01/02 15:42:26 thorpej Exp $");
     35 
     36 #include <sys/param.h>
     37 #include <sys/systm.h>
     38 #include <sys/conf.h>
     39 #include <sys/fcntl.h>
     40 #include <sys/kernel.h>		/* for hz */
     41 #include <sys/limits.h>
     42 #include <sys/lock.h>
     43 #include <sys/malloc.h>
     44 #include <sys/lockf.h>		/* for hz */ /* Must come after sys/malloc.h */
     45 #include <sys/mbuf.h>
     46 #include <sys/mount.h>
     47 #include <sys/namei.h>
     48 #include <sys/priv.h>
     49 #include <sys/proc.h>
     50 #include <sys/resourcevar.h>
     51 #include <sys/socket.h>
     52 #include <sys/socket.h>
     53 #include <sys/unistd.h>
     54 #include <sys/vnode.h>
     55 
     56 #include <net/if.h>
     57 
     58 #include <fs/nfs/common/nfsproto.h>
     59 #include <fs/nfs/common/nfs_lock.h>
     60 #include <fs/nfs/client/nfs.h>
     61 #include <fs/nfs/client/nfsmount.h>
     62 #include <fs/nfs/client/nfsnode.h>
     63 #include <fs/nfs/client/nlminfo.h>
     64 
     65 extern void (*nlminfo_release_p)(struct proc *p);
     66 
     67 vop_advlock_t	*nfs_advlock_p = nfs_dolock;
     68 vop_reclaim_t	*nfs_reclaim_p = NULL;
     69 
     70 static MALLOC_DEFINE(M_NFSLOCK, "nfsclient_lock", "NFS lock request");
     71 static MALLOC_DEFINE(M_NLMINFO, "nfsclient_nlminfo",
     72     "NFS lock process structure");
     73 
     74 static int nfslockdans(struct thread *td, struct lockd_ans *ansp);
     75 static void nlminfo_release(struct proc *p);
     76 /*
     77  * --------------------------------------------------------------------
     78  * A miniature device driver which the userland uses to talk to us.
     79  *
     80  */
     81 
     82 static struct cdev *nfslock_dev;
     83 static struct mtx nfslock_mtx;
     84 static int nfslock_isopen;
     85 static TAILQ_HEAD(,__lock_msg)	nfslock_list;
     86 
     87 static int
     88 nfslock_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
     89 {
     90 	int error;
     91 
     92 	error = priv_check(td, PRIV_NFS_LOCKD);
     93 	if (error)
     94 		return (error);
     95 
     96 	mtx_lock(&nfslock_mtx);
     97 	if (!nfslock_isopen) {
     98 		error = 0;
     99 		nfslock_isopen = 1;
    100 	} else {
    101 		error = EOPNOTSUPP;
    102 	}
    103 	mtx_unlock(&nfslock_mtx);
    104 
    105 	return (error);
    106 }
    107 
    108 static int
    109 nfslock_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
    110 {
    111 	struct __lock_msg *lm;
    112 
    113 	mtx_lock(&nfslock_mtx);
    114 	nfslock_isopen = 0;
    115 	while (!TAILQ_EMPTY(&nfslock_list)) {
    116 		lm = TAILQ_FIRST(&nfslock_list);
    117 		/* XXX: answer request */
    118 		TAILQ_REMOVE(&nfslock_list, lm, lm_link);
    119 		free(lm, M_NFSLOCK);
    120 	}
    121 	mtx_unlock(&nfslock_mtx);
    122 	return (0);
    123 }
    124 
    125 static int
    126 nfslock_read(struct cdev *dev, struct uio *uio, int ioflag)
    127 {
    128 	int error;
    129 	struct __lock_msg *lm;
    130 
    131 	if (uio->uio_resid != sizeof *lm)
    132 		return (EOPNOTSUPP);
    133 	lm = NULL;
    134 	error = 0;
    135 	mtx_lock(&nfslock_mtx);
    136 	while (TAILQ_EMPTY(&nfslock_list)) {
    137 		error = msleep(&nfslock_list, &nfslock_mtx, PSOCK | PCATCH,
    138 		    "nfslockd", 0);
    139 		if (error)
    140 			break;
    141 	}
    142 	if (!error) {
    143 		lm = TAILQ_FIRST(&nfslock_list);
    144 		TAILQ_REMOVE(&nfslock_list, lm, lm_link);
    145 	}
    146 	mtx_unlock(&nfslock_mtx);
    147 	if (!error) {
    148 		error = uiomove(lm, sizeof *lm, uio);
    149 		free(lm, M_NFSLOCK);
    150 	}
    151 	return (error);
    152 }
    153 
    154 static int
    155 nfslock_write(struct cdev *dev, struct uio *uio, int ioflag)
    156 {
    157 	struct lockd_ans la;
    158 	int error;
    159 
    160 	if (uio->uio_resid != sizeof la)
    161 		return (EOPNOTSUPP);
    162 	error = uiomove(&la, sizeof la, uio);
    163 	if (!error)
    164 		error = nfslockdans(curthread, &la);
    165 	return (error);
    166 }
    167 
    168 static int
    169 nfslock_send(struct __lock_msg *lm)
    170 {
    171 	struct __lock_msg *lm2;
    172 	int error;
    173 
    174 	error = 0;
    175 	lm2 = malloc(sizeof *lm2, M_NFSLOCK, M_WAITOK);
    176 	mtx_lock(&nfslock_mtx);
    177 	if (nfslock_isopen) {
    178 		memcpy(lm2, lm, sizeof *lm2);
    179 		TAILQ_INSERT_TAIL(&nfslock_list, lm2, lm_link);
    180 		wakeup(&nfslock_list);
    181 	} else {
    182 		error = EOPNOTSUPP;
    183 	}
    184 	mtx_unlock(&nfslock_mtx);
    185 	if (error)
    186 		free(lm2, M_NFSLOCK);
    187 	return (error);
    188 }
    189 
    190 static struct cdevsw nfslock_cdevsw = {
    191 	.d_version =	D_VERSION,
    192 	.d_open =	nfslock_open,
    193 	.d_close =	nfslock_close,
    194 	.d_read =	nfslock_read,
    195 	.d_write =	nfslock_write,
    196 	.d_name =	"nfslock"
    197 };
    198 
    199 static int
    200 nfslock_modevent(module_t mod __unused, int type, void *data __unused)
    201 {
    202 
    203 	switch (type) {
    204 	case MOD_LOAD:
    205 		if (bootverbose)
    206 			printf("nfslock: pseudo-device\n");
    207 		mtx_init(&nfslock_mtx, "nfslock", NULL, MTX_DEF);
    208 		TAILQ_INIT(&nfslock_list);
    209 		nlminfo_release_p = nlminfo_release;
    210 		nfslock_dev = make_dev(&nfslock_cdevsw, 0,
    211 		    UID_ROOT, GID_KMEM, 0600, _PATH_NFSLCKDEV);
    212 		return (0);
    213 	default:
    214 		return (EOPNOTSUPP);
    215 	}
    216 }
    217 
    218 DEV_MODULE(nfslock, nfslock_modevent, NULL);
    219 MODULE_VERSION(nfslock, 1);
    220 
    221 
    222 /*
    223  * XXX
    224  * We have to let the process know if the call succeeded.  I'm using an extra
    225  * field in the p_nlminfo field in the proc structure, as it is already for
    226  * lockd stuff.
    227  */
    228 
    229 /*
    230  * nfs_advlock --
    231  *      NFS advisory byte-level locks.
    232  *
    233  * The vnode shall be (shared) locked on the entry, it is
    234  * unconditionally unlocked after.
    235  */
    236 int
    237 nfs_dolock(struct vop_advlock_args *ap)
    238 {
    239 	LOCKD_MSG msg;
    240 	struct thread *td;
    241 	struct vnode *vp;
    242 	int error;
    243 	struct flock *fl;
    244 	struct proc *p;
    245 	struct nfsmount *nmp;
    246 	struct timeval btv;
    247 
    248 	td = curthread;
    249 	p = td->td_proc;
    250 
    251 	vp = ap->a_vp;
    252 	fl = ap->a_fl;
    253 	nmp = VFSTONFS(vp->v_mount);
    254 
    255 	ASSERT_VOP_LOCKED(vp, "nfs_dolock");
    256 
    257 	nmp->nm_getinfo(vp, msg.lm_fh, &msg.lm_fh_len, &msg.lm_addr,
    258 	    &msg.lm_nfsv3, NULL, NULL);
    259 	VOP_UNLOCK(vp, 0);
    260 
    261 	/*
    262 	 * the NLM protocol doesn't allow the server to return an error
    263 	 * on ranges, so we do it.
    264 	 */
    265 	if (fl->l_whence != SEEK_END) {
    266 		if ((fl->l_whence != SEEK_CUR && fl->l_whence != SEEK_SET) ||
    267 		    fl->l_start < 0 ||
    268 		    (fl->l_len < 0 &&
    269 		     (fl->l_start == 0 || fl->l_start + fl->l_len < 0)))
    270 			return (EINVAL);
    271 		if (fl->l_len > 0 &&
    272 			 (fl->l_len - 1 > OFF_MAX - fl->l_start))
    273 			return (EOVERFLOW);
    274 	}
    275 
    276 	/*
    277 	 * Fill in the information structure.
    278 	 */
    279 	msg.lm_version = LOCKD_MSG_VERSION;
    280 	msg.lm_msg_ident.pid = p->p_pid;
    281 
    282 	mtx_lock(&Giant);
    283 	/*
    284 	 * if there is no nfsowner table yet, allocate one.
    285 	 */
    286 	if (p->p_nlminfo == NULL) {
    287 		p->p_nlminfo = malloc(sizeof(struct nlminfo),
    288 		    M_NLMINFO, M_WAITOK | M_ZERO);
    289 		p->p_nlminfo->pid_start = p->p_stats->p_start;
    290 		getmicroboottime(&btv);
    291 		timevaladd(&p->p_nlminfo->pid_start, &btv);
    292 	}
    293 	msg.lm_msg_ident.pid_start = p->p_nlminfo->pid_start;
    294 	msg.lm_msg_ident.msg_seq = ++(p->p_nlminfo->msg_seq);
    295 
    296 	msg.lm_fl = *fl;
    297 	msg.lm_wait = ap->a_flags & F_WAIT;
    298 	msg.lm_getlk = ap->a_op == F_GETLK;
    299 	cru2x(td->td_ucred, &msg.lm_cred);
    300 
    301 	for (;;) {
    302 		error = nfslock_send(&msg);
    303 		if (error)
    304 			goto out;
    305 
    306 		/* Unlocks succeed immediately.  */
    307 		if (fl->l_type == F_UNLCK)
    308 			goto out;
    309 
    310 		/*
    311 		 * Retry after 20 seconds if we haven't gotten a response yet.
    312 		 * This number was picked out of thin air... but is longer
    313 		 * then even a reasonably loaded system should take (at least
    314 		 * on a local network).  XXX Probably should use a back-off
    315 		 * scheme.
    316 		 *
    317 		 * XXX: No PCATCH here since we currently have no useful
    318 		 * way to signal to the userland rpc.lockd that the request
    319 		 * has been aborted.  Once the rpc.lockd implementation
    320 		 * can handle aborts, and we report them properly,
    321 		 * PCATCH can be put back.  In the mean time, if we did
    322 		 * permit aborting, the lock attempt would "get lost"
    323 		 * and the lock would get stuck in the locked state.
    324 		 */
    325 		error = tsleep(p->p_nlminfo, PUSER, "lockd", 20*hz);
    326 		if (error != 0) {
    327 			if (error == EWOULDBLOCK) {
    328 				/*
    329 				 * We timed out, so we rewrite the request
    330 				 * to the fifo.
    331 				 */
    332 				continue;
    333 			}
    334 
    335 			break;
    336 		}
    337 
    338 		if (msg.lm_getlk && p->p_nlminfo->retcode == 0) {
    339 			if (p->p_nlminfo->set_getlk_pid) {
    340 				fl->l_sysid = 0; /* XXX */
    341 				fl->l_pid = p->p_nlminfo->getlk_pid;
    342 			} else {
    343 				fl->l_type = F_UNLCK;
    344 			}
    345 		}
    346 		error = p->p_nlminfo->retcode;
    347 		break;
    348 	}
    349  out:
    350 	mtx_unlock(&Giant);
    351 	return (error);
    352 }
    353 
    354 /*
    355  * nfslockdans --
    356  *      NFS advisory byte-level locks answer from the lock daemon.
    357  */
    358 static int
    359 nfslockdans(struct thread *td, struct lockd_ans *ansp)
    360 {
    361 	struct proc *targetp;
    362 
    363 	/* the version should match, or we're out of sync */
    364 	if (ansp->la_vers != LOCKD_ANS_VERSION)
    365 		return (EINVAL);
    366 
    367 	/* Find the process, set its return errno and wake it up. */
    368 	if ((targetp = pfind(ansp->la_msg_ident.pid)) == NULL)
    369 		return (ESRCH);
    370 
    371 	/* verify the pid hasn't been reused (if we can), and it isn't waiting
    372 	 * for an answer from a more recent request.  We return an EPIPE if
    373 	 * the match fails, because we've already used ESRCH above, and this
    374 	 * is sort of like writing on a pipe after the reader has closed it.
    375 	 */
    376 	if (targetp->p_nlminfo == NULL ||
    377 	    ((ansp->la_msg_ident.msg_seq != -1) &&
    378 	      (timevalcmp(&targetp->p_nlminfo->pid_start,
    379 			&ansp->la_msg_ident.pid_start, !=) ||
    380 	       targetp->p_nlminfo->msg_seq != ansp->la_msg_ident.msg_seq))) {
    381 		PROC_UNLOCK(targetp);
    382 		return (EPIPE);
    383 	}
    384 
    385 	targetp->p_nlminfo->retcode = ansp->la_errno;
    386 	targetp->p_nlminfo->set_getlk_pid = ansp->la_set_getlk_pid;
    387 	targetp->p_nlminfo->getlk_pid = ansp->la_getlk_pid;
    388 
    389 	wakeup(targetp->p_nlminfo);
    390 
    391 	PROC_UNLOCK(targetp);
    392 	return (0);
    393 }
    394 
    395 /*
    396  * Free nlminfo attached to process.
    397  */
    398 void
    399 nlminfo_release(struct proc *p)
    400 {
    401 	free(p->p_nlminfo, M_NLMINFO);
    402 	p->p_nlminfo = NULL;
    403 }
    404