Index: fs/fuse/fuse_ipc.c =================================================================== --- fs/fuse/fuse_ipc.c +++ fs/fuse/fuse_ipc.c @@ -688,15 +688,15 @@ break; case FUSE_GETLK: - panic("FUSE: no response body format check for FUSE_GETLK"); + err = (blen == sizeof(struct fuse_lk_out)) ? 0 : EINVAL; break; case FUSE_SETLK: - panic("FUSE: no response body format check for FUSE_SETLK"); + err = (blen == 0) ? 0 : EINVAL; break; case FUSE_SETLKW: - panic("FUSE: no response body format check for FUSE_SETLKW"); + err = (blen == 0) ? 0 : EINVAL; break; case FUSE_ACCESS: Index: fs/fuse/fuse_vnops.c =================================================================== --- fs/fuse/fuse_vnops.c +++ fs/fuse/fuse_vnops.c @@ -138,6 +138,7 @@ static vop_setextattr_t fuse_vnop_setextattr; static vop_listextattr_t fuse_vnop_listextattr; static vop_deleteextattr_t fuse_vnop_deleteextattr; +static vop_advlock_t fuse_vnop_advlock; struct vop_vector fuse_vnops = { .vop_default = &default_vnodeops, @@ -171,6 +172,7 @@ .vop_setextattr = fuse_vnop_setextattr, .vop_listextattr = fuse_vnop_listextattr, .vop_deleteextattr = fuse_vnop_deleteextattr, + .vop_advlock = fuse_vnop_advlock, }; static u_long fuse_lookup_cache_hits = 0; @@ -2200,3 +2202,171 @@ return (err); } +/* + struct vop_advlock_args { + struct vop_generic_args a_gen; + struct vnode *a_vp; + void *a_id; + int a_op; + struct flock *a_fl; + int a_flags; + }; + */ +static int +fuse_vnop_advlock(struct vop_advlock_args *ap) +{ + struct vnode *vp = ap->a_vp; + struct flock *flp = ap->a_fl; + struct ucred *cred; + struct proc *p = (struct proc *)ap->a_id; + struct fuse_dispatcher fdi; + struct fuse_lk_in *fli; + struct fuse_lk_out *flo; + struct fuse_filehandle *fufh = NULL; + enum fuse_opcode op; + fufh_type_t fufhtype = FUFH_INVALID; + int err = 0, ret; + off_t start, end, oadd; + pid_t pid; + static int non_event; + + FS_DEBUG2G("inode=%ju\n", (uintmax_t)VTOI(vp)); + + if (fuse_isdeadfs(vp) != 0) + return (ENXIO); + if ((ap->a_flags & (F_POSIX | F_FLOCK | F_REMOTE)) == 0) + return (EPERM); + if (vp->v_type != VREG) + return (EINVAL); + + /* Set up the parameters for the lock operation. */ + if (ap->a_op == F_UNLCK) { + op = FUSE_SETLK; + fufhtype = FUFH_RDONLY; + } else if (ap->a_op == F_SETLK) { + op = FUSE_SETLK; + if (flp->l_type == F_WRLCK) + fufhtype = FUFH_WRONLY; + else + fufhtype = FUFH_RDONLY; + } else if (ap->a_op == F_GETLK) { + op = FUSE_GETLK; + fufhtype = FUFH_RDONLY; + } + err = fuse_filehandle_getrw(vp, fufhtype, &fufh); + if (err != 0) + return (ENXIO); + + /* + * The start and end are defined as u64, but the GlusterFS code + * uses OFFSET_MAX, which is defined as the maximum signed + * 64bit in GlusterFS, so I've used OFF_MAX, which is the same. + */ + if ((ap->a_flags & F_FLOCK) != 0) { + cred = curthread->td_ucred; + start = 0; + end = OFF_MAX; + pid = 0; + } else { + /* POSIX lock. */ + cred = p->p_ucred; + start = flp->l_start; + if (start < 0) + return (EINVAL); + if (flp->l_len < 0) { + if (start == 0) + return (EINVAL); + end = start - 1; + start += flp->l_len; + if (start < 0) + return (EINVAL); + } else if (flp->l_len == 0) { + end = OFF_MAX; + } else { + oadd = flp->l_len - 1; + if (oadd > OFF_MAX - start) + return (EOVERFLOW); + end = start + oadd; + } + pid = flp->l_pid; + } + + /* + * I am not sure if serializing the lock operations is required, + * but I chose to do so using the exclusive vnode lock. + */ + err = vn_lock(vp, LK_EXCLUSIVE); + if (err != 0) + return (EBADF); + + /* + * Although fuse has a FUSE_SETLKW option, I didn't like the idea + * of waiting indefinitely for an upcall to reply. I also don't + * know how to interrupt a fuse operation in progress when a + * termination signal is posted to the process/thread. + * So, instead of using FUSE_SETLKW, the code uses FUSE_SETLK and then + * polls while it returns EAGAIN, after a one tick sleep. + * If a termination signal is posted to the process/thread, tsleep() + * should return EINTR and then this function will return without + * doing the lock. + */ + do { + /* Now, do the lock operation. */ + fdisp_init(&fdi, sizeof(struct fuse_lk_in)); + fdisp_make_vp(&fdi, op, vp, curthread, cred); + fli = (struct fuse_lk_in *)fdi.indata; + fli->lk.start = start; + fli->lk.end = end; + fli->lk.pid = pid; + if ((ap->a_flags & F_REMOTE) != 0) { + /* Make the owner a combination of l_pid and l_sysid. */ + fli->owner = (uint64_t)flp->l_sysid; + fli->owner = (fli->owner << 32) | (flp->l_pid & + 0xffffffff); + } else + fli->owner = (uint64_t)ap->a_id; + fli->lk.type = flp->l_type; + fli->fh = fufh->fh_id; + + err = fdisp_wait_answ(&fdi); + + if (err == 0 && op == FUSE_GETLK && (ap->a_flags & F_POSIX) + != 0) { + flo = (struct fuse_lk_out *)fdi.answ; + flp->l_type = flo->lk.type; + if (flp->l_type != F_UNLCK) { + flp->l_start = flo->lk.start; + /* lk.end is u64, but OFF_MAX is INT64_MAX. */ + if (flo->lk.end > OFF_MAX) + flo->lk.end = OFF_MAX; + if (flo->lk.end == OFF_MAX) + flp->l_len = 0; + else + flp->l_len = flo->lk.end - flo->lk.start + + 1; + flp->l_pid = flo->lk.pid; + } + } + fdisp_destroy(&fdi); + /* Sleep for a little while, if we are going to loop. */ + if (err == EAGAIN && (ap->a_flags & F_WAIT) != 0 && + ap->a_op == F_SETLK) { + VOP_UNLOCK(vp, 0); + ret = tsleep(&non_event, PZERO | PCATCH, "fuslck", 1); + if (ret == EINTR) + return (ret); + ret = vn_lock(vp, LK_EXCLUSIVE); + if (ret != 0) + return (EBADF); + } + } while (err == EAGAIN && (ap->a_flags & F_WAIT) != 0 && + ap->a_op == F_SETLK); + + VOP_UNLOCK(vp, 0); + if (err == EINVAL || err == EBADF || err == EINTR || err == EAGAIN) + return (err); + else if (err != 0) + return (EACCES); + return (0); +} +