int smb_vop_other_opens(vnode_t *vp, int mode) { return (((mode & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || (((mode & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || ((mode & FREAD) && vn_has_other_opens(vp, V_READ)) || (((mode & FREAD) == 0) && vn_is_opened(vp, V_READ)) || vn_is_mapped(vp, V_RDORWR)); }
/* ARGSUSED */ int fs_frlock(register vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset, flk_callback_t *flk_cbp, cred_t *cr, caller_context_t *ct) { int frcmd; int nlmid; int error = 0; flk_callback_t serialize_callback; int serialize = 0; v_mode_t mode; switch (cmd) { case F_GETLK: case F_O_GETLK: if (flag & F_REMOTELOCK) { frcmd = RCMDLCK; } else if (flag & F_PXFSLOCK) { frcmd = PCMDLCK; } else { frcmd = 0; bfp->l_pid = ttoproc(curthread)->p_pid; bfp->l_sysid = 0; } break; case F_SETLK_NBMAND: /* * Are NBMAND locks allowed on this file? */ if (!vp->v_vfsp || !(vp->v_vfsp->vfs_flag & VFS_NBMAND)) { error = EINVAL; goto done; } if (vp->v_type != VREG) { error = EINVAL; goto done; } /*FALLTHROUGH*/ case F_SETLK: if (flag & F_REMOTELOCK) { frcmd = SETFLCK|RCMDLCK; } else if (flag & F_PXFSLOCK) { frcmd = SETFLCK|PCMDLCK; } else { frcmd = SETFLCK; bfp->l_pid = ttoproc(curthread)->p_pid; bfp->l_sysid = 0; } if (cmd == F_SETLK_NBMAND && (bfp->l_type == F_RDLCK || bfp->l_type == F_WRLCK)) { frcmd |= NBMLCK; } if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_WRITER); serialize = 1; if (frcmd & NBMLCK) { mode = (bfp->l_type == F_RDLCK) ? V_READ : V_RDANDWR; if (vn_is_mapped(vp, mode)) { error = EAGAIN; goto done; } } } break; case F_SETLKW: if (flag & F_REMOTELOCK) { frcmd = SETFLCK|SLPFLCK|RCMDLCK; } else if (flag & F_PXFSLOCK) { frcmd = SETFLCK|SLPFLCK|PCMDLCK; } else { frcmd = SETFLCK|SLPFLCK; bfp->l_pid = ttoproc(curthread)->p_pid; bfp->l_sysid = 0; } if (nbl_need_check(vp)) { nbl_start_crit(vp, RW_WRITER); serialize = 1; } break; case F_HASREMOTELOCKS: nlmid = GETNLMID(bfp->l_sysid); if (nlmid != 0) { /* booted as a cluster */ l_has_rmt(bfp) = cl_flk_has_remote_locks_for_nlmid(vp, nlmid); } else { /* not booted as a cluster */ l_has_rmt(bfp) = flk_has_remote_locks(vp); } goto done; default: error = EINVAL; goto done; } /* * If this is a blocking lock request and we're serializing lock * requests, modify the callback list to leave the critical region * while we're waiting for the lock. */ if (serialize && (frcmd & SLPFLCK) != 0) { flk_add_callback(&serialize_callback, frlock_serialize_blocked, vp, flk_cbp); flk_cbp = &serialize_callback; } error = reclock(vp, bfp, frcmd, flag, offset, flk_cbp); done: if (serialize) nbl_end_crit(vp); return (error); }
/* * State support for delegation. * Set the state delegation type for this state; * This routine is called from open via rfs4_grant_delegation and the entry * locks on sp and sp->rs_finfo are assumed. */ static rfs4_deleg_state_t * rfs4_deleg_state(rfs4_state_t *sp, open_delegation_type4 dtype, int *recall) { rfs4_file_t *fp = sp->rs_finfo; bool_t create = TRUE; rfs4_deleg_state_t *dsp; vnode_t *vp; int open_prev = *recall; int ret; int fflags = 0; ASSERT(rfs4_dbe_islocked(sp->rs_dbe)); ASSERT(rfs4_dbe_islocked(fp->rf_dbe)); /* Shouldn't happen */ if (fp->rf_dinfo.rd_recall_count != 0 || (fp->rf_dinfo.rd_dtype == OPEN_DELEGATE_READ && dtype != OPEN_DELEGATE_READ)) { return (NULL); } /* Unlock to avoid deadlock */ rfs4_dbe_unlock(fp->rf_dbe); rfs4_dbe_unlock(sp->rs_dbe); dsp = rfs4_finddeleg(sp, &create); rfs4_dbe_lock(sp->rs_dbe); rfs4_dbe_lock(fp->rf_dbe); if (dsp == NULL) return (NULL); /* * It is possible that since we dropped the lock * in order to call finddeleg, the rfs4_file_t * was marked such that we should not grant a * delegation, if so bail out. */ if (fp->rf_dinfo.rd_hold_grant > 0) { rfs4_deleg_state_rele(dsp); return (NULL); } if (create == FALSE) { if (sp->rs_owner->ro_client == dsp->rds_client && dsp->rds_dtype == dtype) { return (dsp); } else { rfs4_deleg_state_rele(dsp); return (NULL); } } /* * Check that this file has not been delegated to another * client */ if (fp->rf_dinfo.rd_recall_count != 0 || fp->rf_dinfo.rd_dtype == OPEN_DELEGATE_WRITE || (fp->rf_dinfo.rd_dtype == OPEN_DELEGATE_READ && dtype != OPEN_DELEGATE_READ)) { rfs4_deleg_state_rele(dsp); return (NULL); } vp = fp->rf_vp; /* vnevent_support returns 0 if file system supports vnevents */ if (vnevent_support(vp, NULL)) { rfs4_deleg_state_rele(dsp); return (NULL); } /* Calculate the fflags for this OPEN. */ if (sp->rs_share_access & OPEN4_SHARE_ACCESS_READ) fflags |= FREAD; if (sp->rs_share_access & OPEN4_SHARE_ACCESS_WRITE) fflags |= FWRITE; *recall = 0; /* * Before granting a delegation we need to know if anyone else has * opened the file in a conflicting mode. However, first we need to * know how we opened the file to check the counts properly. */ if (dtype == OPEN_DELEGATE_READ) { if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || vn_is_mapped(vp, V_WRITE)) { if (open_prev) { *recall = 1; } else { rfs4_deleg_state_rele(dsp); return (NULL); } } ret = fem_install(vp, deleg_rdops, (void *)fp, OPUNIQ, rfs4_mon_hold, rfs4_mon_rele); if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || vn_is_mapped(vp, V_WRITE)) { if (open_prev) { *recall = 1; } else { (void) fem_uninstall(vp, deleg_rdops, (void *)fp); rfs4_deleg_state_rele(dsp); return (NULL); } } /* * Because a client can hold onto a delegation after the * file has been closed, we need to keep track of the * access to this file. Otherwise the CIFS server would * not know about the client accessing the file and could * inappropriately grant an OPLOCK. * fem_install() returns EBUSY when asked to install a * OPUNIQ monitor more than once. Therefore, check the * return code because we only want this done once. */ if (ret == 0) vn_open_upgrade(vp, FREAD); } else { /* WRITE */ if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || ((fflags & FREAD) && vn_has_other_opens(vp, V_READ)) || (((fflags & FREAD) == 0) && vn_is_opened(vp, V_READ)) || vn_is_mapped(vp, V_RDORWR)) { if (open_prev) { *recall = 1; } else { rfs4_deleg_state_rele(dsp); return (NULL); } } ret = fem_install(vp, deleg_wrops, (void *)fp, OPUNIQ, rfs4_mon_hold, rfs4_mon_rele); if (((fflags & FWRITE) && vn_has_other_opens(vp, V_WRITE)) || (((fflags & FWRITE) == 0) && vn_is_opened(vp, V_WRITE)) || ((fflags & FREAD) && vn_has_other_opens(vp, V_READ)) || (((fflags & FREAD) == 0) && vn_is_opened(vp, V_READ)) || vn_is_mapped(vp, V_RDORWR)) { if (open_prev) { *recall = 1; } else { (void) fem_uninstall(vp, deleg_wrops, (void *)fp); rfs4_deleg_state_rele(dsp); return (NULL); } } /* * Because a client can hold onto a delegation after the * file has been closed, we need to keep track of the * access to this file. Otherwise the CIFS server would * not know about the client accessing the file and could * inappropriately grant an OPLOCK. * fem_install() returns EBUSY when asked to install a * OPUNIQ monitor more than once. Therefore, check the * return code because we only want this done once. */ if (ret == 0) vn_open_upgrade(vp, FREAD|FWRITE); } /* Place on delegation list for file */ ASSERT(!list_link_active(&dsp->rds_node)); list_insert_tail(&fp->rf_delegstatelist, dsp); dsp->rds_dtype = fp->rf_dinfo.rd_dtype = dtype; /* Update delegation stats for this file */ fp->rf_dinfo.rd_time_lastgrant = gethrestime_sec(); /* reset since this is a new delegation */ fp->rf_dinfo.rd_conflicted_client = 0; fp->rf_dinfo.rd_ever_recalled = FALSE; if (dtype == OPEN_DELEGATE_READ) fp->rf_dinfo.rd_rdgrants++; else fp->rf_dinfo.rd_wrgrants++; return (dsp); }