- /*
- * The NLM protocol doesn't allow the server to return an error
- * on ranges, so we do it. Pre LFS (Large File Summit)
- * standards required EINVAL for the range errors. More recent
- * standards use EOVERFLOW, but their EINVAL wording still
- * encompasses these errors.
- * Any code sensitive to this is either:
- * 1) written pre-LFS and so can handle only EINVAL, or
- * 2) written post-LFS and thus ought to be tolerant of pre-LFS
- * implementations.
- * Since returning EOVERFLOW certainly breaks 1), we return EINVAL.
- */
- 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);
- }
- /*
- * If daemon is running take a ref on its fifo vnode
- */
- if (!(wvp = nfslockdvnode)) {
- if (!nfslockdwaiting && !nfslockdstarttimeout)
- return (ENOTSUP);
- /*
- * Don't wake lock daemon if it hasn't been started yet and
- * this is an unlock request (since we couldn't possibly
- * actually have a lock on the file). This could be an
- * uninformed unlock request due to closef()'s behavior of doing
- * unlocks on all files if a process has had a lock on ANY file.
- */
- if (!nfslockdvnode && (fl->l_type == F_UNLCK))
- return (EINVAL);
- microuptime(&now);
- if (nfslockdwaiting) {
- /* wake up lock daemon */
- nfslockdstarttimeout = now.tv_sec + 60;
- (void)wakeup((void *)&nfslockdwaiting);
- }
- /* wait on nfslockdvnode for a while to allow daemon to start */
- while (!nfslockdvnode && (now.tv_sec < nfslockdstarttimeout)) {
- error = tsleep((void *)&nfslockdvnode, PCATCH | PUSER, "lockdstart", 2*hz);
- if (error && (error != EWOULDBLOCK))
- return (error);
- /* check that we still have our mount... */
- /* ...and that we still support locks */
- nmp = VFSTONFS(vnode_mount(vp));
- if (!nmp)
- return (ENXIO);
- if (nmp->nm_flag & NFSMNT_NOLOCKS)
- return (ENOTSUP);
- if (!error)
- break;
- microuptime(&now);
- }
- /*
- * check for nfslockdvnode
- * If it hasn't started by now, there's a problem.
- */
- if (!(wvp = nfslockdvnode))
- return (ENOTSUP);
- }
- error = vnode_getwithref(wvp);
- if (error)
- return (ENOTSUP);
- error = vnode_ref(wvp);
- if (error) {
- vnode_put(wvp);
- return (ENOTSUP);
- }
-
- /*
- * Need to check if this process has successfully acquired an NFS lock before.
- * If not, and this is an unlock request we can simply return success here.
- */
- lockpidcheck = nfs_lock_pid_check(p, 0, vp);
- if (lockpidcheck) {
- if (lockpidcheck != ENOENT) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (lockpidcheck);
- }
- if (ap->a_op == F_UNLCK) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (0);
- }
- }
-
- /*
- * The NFS Lock Manager protocol doesn't directly handle
- * negative lengths or SEEK_END, so we need to normalize
- * things here where we have all the info.
- * (Note: SEEK_CUR is already adjusted for at this point)
- */
- /* Convert the flock structure into a start and end. */
- switch (fl->l_whence) {
- case SEEK_SET:
- case SEEK_CUR:
- /*
- * Caller is responsible for adding any necessary offset
- * to fl->l_start when SEEK_CUR is used.
- */
- start = fl->l_start;
- break;
- case SEEK_END:
- /* need to flush, and refetch attributes to make */
- /* sure we have the correct end of file offset */
- if (np->n_flag & NMODIFIED) {
- NATTRINVALIDATE(np);
- error = nfs_vinvalbuf(vp, V_SAVE, cred, p, 1);
- if (error) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (error);
- }
- }
- NATTRINVALIDATE(np);
-
- error = nfs_getattr(vp, &nvattr, cred, p);
- if (error) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (error);
- }
- start = np->n_size + fl->l_start;
- break;
- default:
- vnode_rele(wvp);
- vnode_put(wvp);
- return (EINVAL);
- }
- if (fl->l_len == 0)
- end = -1;
- else if (fl->l_len > 0)
- end = start + fl->l_len - 1;
- else { /* l_len is negative */
- end = start - 1;
- start += fl->l_len;
- }
- if (start < 0) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (EINVAL);
- }
- if (!NFS_ISV3(vp) &&
- ((start >= 0x80000000) || (end >= 0x80000000))) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (EINVAL);
- }
-
- /*
- * Fill in the information structure.
- */
- msgreq.lmr_answered = 0;
- msgreq.lmr_errno = 0;
- msgreq.lmr_saved_errno = 0;
- 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 = proc_pid(p);
-
- 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 = VFSTONFS(vnode_mount(vp));
- if (!nmp) {
- vnode_rele(wvp);
- vnode_put(wvp);
- return (ENXIO);
- }
-
- saddr = mbuf_data(nmp->nm_nam);