nfs_lock.c revision 82174
1/*- 2 * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Berkeley Software Design Inc's name may not be used to endorse or 13 * promote products derived from this software without specific prior 14 * written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN INC BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * from BSDI nfs_lock.c,v 2.4 1998/12/14 23:49:56 jch Exp 29 * $FreeBSD: head/sys/nfsclient/nfs_lock.c 82174 2001-08-23 08:20:21Z ache $ 30 */ 31 32#include <machine/limits.h> 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/fcntl.h> 36#include <sys/kernel.h> /* for hz */ 37#include <sys/lock.h> 38#include <sys/malloc.h> 39#include <sys/lockf.h> /* for hz */ /* Must come after sys/malloc.h */ 40#include <sys/mbuf.h> 41#include <sys/mount.h> 42#include <sys/namei.h> 43#include <sys/proc.h> 44#include <sys/resourcevar.h> 45#include <sys/socket.h> 46#include <sys/socket.h> 47#include <sys/unistd.h> 48#include <sys/vnode.h> 49 50#include <net/if.h> 51 52#include <nfs/rpcv2.h> 53#include <nfs/nfsproto.h> 54#include <nfs/nfs.h> 55#include <nfs/nfsmount.h> 56#include <nfs/nfsnode.h> 57#include <nfs/nfs_lock.h> 58#include <nfs/nlminfo.h> 59 60#define NFSOWNER_1ST_LEVEL_START 1 /* initial entries */ 61#define NFSOWNER_2ND_LEVEL 256 /* some power of 2 */ 62 63#define NFSOWNER(tbl, i) \ 64 (tbl)[(i) / NFSOWNER_2ND_LEVEL][(i) % NFSOWNER_2ND_LEVEL] 65 66/* 67 * XXX 68 * We have to let the process know if the call succeeded. I'm using an extra 69 * field in the p_nlminfo field in the proc structure, as it is already for 70 * lockd stuff. 71 */ 72 73/* 74 * nfs_advlock -- 75 * NFS advisory byte-level locks. 76 */ 77int 78nfs_dolock(ap) 79 struct vop_advlock_args /* { 80 struct vnode *a_vp; 81 caddr_t a_id; 82 int a_op; 83 struct flock *a_fl; 84 int a_flags; 85 } */ *ap; 86{ 87 LOCKD_MSG msg; 88 struct nameidata nd; 89 struct proc *p; 90 uid_t saved_uid; 91 struct vnode *vp, *wvp; 92 int error, error1; 93 struct flock *fl; 94 int fmode, ioflg; 95 96 p = curproc; 97 vp = ap->a_vp; 98 fl = ap->a_fl; 99 100 /* 101 * the NLM protocol doesn't allow the server to return an error 102 * on ranges, so we do it. 103 */ 104 if (fl->l_start < 0 || fl->l_len < 0) 105 return (EINVAL); 106 if (fl->l_len != 0 && (fl->l_len - 1 > OFF_MAX - fl->l_start)) 107 return (EOVERFLOW); 108 109 /* 110 * Fill in the information structure. 111 */ 112 msg.lm_version = LOCKD_MSG_VERSION; 113 msg.lm_msg_ident.pid = p->p_pid; 114 /* 115 * if there is no nfsowner table yet, allocate one. 116 */ 117 if (p->p_nlminfo == NULL) { 118 MALLOC(p->p_nlminfo, struct nlminfo *, 119 sizeof(struct nlminfo), M_LOCKF, M_WAITOK | M_ZERO); 120 p->p_nlminfo->pid_start = p->p_stats->p_start; 121 } 122 msg.lm_msg_ident.pid_start = p->p_nlminfo->pid_start; 123 msg.lm_msg_ident.msg_seq = ++(p->p_nlminfo->msg_seq); 124 125 msg.lm_fl = *fl; 126 msg.lm_wait = ap->a_flags & F_WAIT; 127 msg.lm_getlk = ap->a_op == F_GETLK; 128 /* 129 * XXX -- I think this is wrong for anything other AF_INET. 130 */ 131 msg.lm_addr = *(VFSTONFS(vp->v_mount)->nm_nam); 132 msg.lm_fh_len = NFS_ISV3(vp) ? VTONFS(vp)->n_fhsize : NFSX_V2FH; 133 bcopy(VTONFS(vp)->n_fhp, msg.lm_fh, msg.lm_fh_len); 134 msg.lm_nfsv3 = NFS_ISV3(vp); 135 msg.lm_cred = *(p->p_ucred); 136 137 /* 138 * Open the lock fifo. If for any reason we don't find the fifo, it 139 * means that the lock daemon isn't running. Translate any missing 140 * file error message for the user, otherwise the application will 141 * complain that the user's file is missing, which isn't the case. 142 * Note that we use proc0's cred, so the fifo is opened as root. 143 */ 144 NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, _PATH_LCKFIFO, p); 145 146 /* 147 * XXX Hack to temporarily allow this process (regardless of it's creds) 148 * to open the fifo we need to write to. vn_open() really should 149 * take a ucred (and once it does, this code should be fixed to use 150 * proc0's ucred. 151 */ 152 saved_uid = p->p_ucred->cr_uid; 153 p->p_ucred->cr_uid = 0; /* temporarly run the vn_open as root */ 154 155 fmode = FFLAGS(O_WRONLY); 156 error = vn_open(&nd, &fmode, 0); 157 p->p_ucred->cr_uid = saved_uid; 158 if (error != 0) { 159 return (error == ENOENT ? EOPNOTSUPP : error); 160 } 161 wvp = nd.ni_vp; 162 VOP_UNLOCK(wvp, 0, p); /* vn_open leaves it locked */ 163 164 165 ioflg = IO_UNIT; 166 for (;;) { 167 VOP_LEASE(wvp, p, proc0.p_ucred, LEASE_WRITE); 168 169 error = vn_rdwr(UIO_WRITE, wvp, (caddr_t)&msg, sizeof(msg), 0, 170 UIO_SYSSPACE, ioflg, proc0.p_ucred, NULL, p); 171 172 if (error && (((ioflg & IO_NDELAY) == 0) || error != EAGAIN)) { 173 break; 174 } 175 /* 176 * If we're locking a file, wait for an answer. Unlocks succeed 177 * immediately. 178 */ 179 if (fl->l_type == F_UNLCK) 180 /* 181 * XXX this isn't exactly correct. The client side 182 * needs to continue sending it's unlock until 183 * it gets a responce back. 184 */ 185 break; 186 187 /* 188 * retry after 20 seconds if we haven't gotten a responce yet. 189 * This number was picked out of thin air... but is longer 190 * then even a reasonably loaded system should take (at least 191 * on a local network). XXX Probably should use a back-off 192 * scheme. 193 */ 194 if ((error = tsleep((void *)p->p_nlminfo, 195 PCATCH | PUSER, "lockd", 20*hz)) != 0) { 196 if (error == EWOULDBLOCK) { 197 /* 198 * We timed out, so we rewrite the request 199 * to the fifo, but only if it isn't already 200 * full. 201 */ 202 ioflg |= IO_NDELAY; 203 continue; 204 } 205 206 break; 207 } 208 209 if (msg.lm_getlk && p->p_nlminfo->retcode == 0) { 210 if (p->p_nlminfo->set_getlk_pid) { 211 fl->l_pid = p->p_nlminfo->getlk_pid; 212 } else { 213 fl->l_type = F_UNLCK; 214 } 215 } 216 error = p->p_nlminfo->retcode; 217 break; 218 } 219 220 if ((error1 = vn_close(wvp, FWRITE, proc0.p_ucred, p)) && error == 0) 221 return (error1); 222 223 return (error); 224} 225 226/* 227 * nfslockdans -- 228 * NFS advisory byte-level locks answer from the lock daemon. 229 */ 230int 231nfslockdans(p, ansp) 232 struct proc *p; 233 struct lockd_ans *ansp; 234{ 235 int error; 236 237 /* Let root, or someone who once was root (lockd generally 238 * switches to the daemon uid once it is done setting up) make 239 * this call. 240 * 241 * XXX This authorization check is probably not right. 242 */ 243 if ((error = suser(p)) != 0 && p->p_ucred->cr_svuid != 0) 244 return (error); 245 246 /* the version should match, or we're out of sync */ 247 if (ansp->la_vers != LOCKD_ANS_VERSION) 248 return (EINVAL); 249 250 /* Find the process, set its return errno and wake it up. */ 251 if ((p = pfind(ansp->la_msg_ident.pid)) == NULL) 252 return (ESRCH); 253 254 /* verify the pid hasn't been reused (if we can), and it isn't waiting 255 * for an answer from a more recent request. We return an EPIPE if 256 * the match fails, because we've already used ESRCH above, and this 257 * is sort of like writing on a pipe after the reader has closed it. 258 */ 259 if (p->p_nlminfo == NULL || 260 ((ansp->la_msg_ident.msg_seq != -1) && 261 (timevalcmp(&p->p_nlminfo->pid_start, 262 &ansp->la_msg_ident.pid_start, !=) || 263 p->p_nlminfo->msg_seq != ansp->la_msg_ident.msg_seq))) { 264 PROC_UNLOCK(p); 265 return (EPIPE); 266 } 267 268 p->p_nlminfo->retcode = ansp->la_errno; 269 p->p_nlminfo->set_getlk_pid = ansp->la_set_getlk_pid; 270 p->p_nlminfo->getlk_pid = ansp->la_getlk_pid; 271 272 (void)wakeup((void *)p->p_nlminfo); 273 274 PROC_UNLOCK(p); 275 return (0); 276} 277