#include <sys/systm.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <nfs/nfs4_kprot.h>
#include <nfs/nfs4.h>
#include <sys/types.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/time.h>
#include <sys/fem.h>
#include <sys/cmn_err.h>
extern u_longlong_t nfs4_srv_caller_id;
int
recall_all_delegations(rfs4_file_t *fp, bool_t trunc, caller_context_t *ct)
{
clock_t rc;
rfs4_recall_deleg(fp, trunc, NULL);
delay(NFS4_DELEGATION_CONFLICT_DELAY);
rfs4_dbe_lock(fp->rf_dbe);
if (fp->rf_dinfo.rd_dtype == OPEN_DELEGATE_NONE) {
rfs4_dbe_unlock(fp->rf_dbe);
return (0);
}
if (ct != NULL && ct->cc_flags & CC_DONTBLOCK) {
rfs4_dbe_unlock(fp->rf_dbe);
ct->cc_flags |= CC_WOULDBLOCK;
return (NFS4ERR_DELAY);
}
while (fp->rf_dinfo.rd_dtype != OPEN_DELEGATE_NONE) {
rc = rfs4_dbe_twait(fp->rf_dbe,
ddi_get_lbolt() + SEC_TO_TICK(rfs4_lease_time));
if (rc == -1) {
rfs4_dbe_unlock(fp->rf_dbe);
rfs4_recall_deleg(fp, trunc, NULL);
rfs4_dbe_lock(fp->rf_dbe);
}
}
rfs4_dbe_unlock(fp->rf_dbe);
return (0);
}
int
deleg_rd_open(femarg_t *arg, int mode, cred_t *cr, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
if ((ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) &&
(mode & (FWRITE|FTRUNC))) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_open(arg, mode, cr, ct));
}
int
deleg_wr_open(femarg_t *arg, int mode, cred_t *cr, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_open(arg, mode, cr, ct));
}
int
deleg_wr_read(femarg_t *arg, uio_t *uiop, int ioflag, cred_t *cr,
struct caller_context *ct)
{
int rc;
rfs4_file_t *fp;
if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_read(arg, uiop, ioflag, cr, ct));
}
int
deleg_rd_write(femarg_t *arg, uio_t *uiop, int ioflag, cred_t *cr,
struct caller_context *ct)
{
int rc;
rfs4_file_t *fp;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
return (vnext_write(arg, uiop, ioflag, cr, ct));
}
int
deleg_wr_write(femarg_t *arg, uio_t *uiop, int ioflag, cred_t *cr,
struct caller_context *ct)
{
int rc;
rfs4_file_t *fp;
if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_write(arg, uiop, ioflag, cr, ct));
}
int
deleg_rd_setattr(femarg_t *arg, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
int rc;
bool_t trunc = FALSE;
rfs4_file_t *fp;
if ((vap->va_mask & AT_SIZE) && (vap->va_size == 0))
trunc = TRUE;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, trunc, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
return (vnext_setattr(arg, vap, flags, cr, ct));
}
int
deleg_wr_setattr(femarg_t *arg, vattr_t *vap, int flags, cred_t *cr,
caller_context_t *ct)
{
int rc;
bool_t trunc = FALSE;
rfs4_file_t *fp;
if (ct == NULL || (ct->cc_caller_id != nfs4_srv_caller_id)) {
if ((vap->va_mask & AT_SIZE) && (vap->va_size == 0))
trunc = TRUE;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, trunc, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_setattr(arg, vap, flags, cr, ct));
}
int
deleg_rd_rwlock(femarg_t *arg, int write_lock, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
if (write_lock) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_rwlock(arg, write_lock, ct));
}
int
deleg_wr_rwlock(femarg_t *arg, int write_lock, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_rwlock(arg, write_lock, ct));
}
int
deleg_rd_space(femarg_t *arg, int cmd, flock64_t *bfp, int flag,
offset_t offset, cred_t *cr, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
return (vnext_space(arg, cmd, bfp, flag, offset, cr, ct));
}
int
deleg_wr_space(femarg_t *arg, int cmd, flock64_t *bfp, int flag,
offset_t offset, cred_t *cr, caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
if (ct == NULL || ct->cc_caller_id != nfs4_srv_caller_id) {
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
}
return (vnext_space(arg, cmd, bfp, flag, offset, cr, ct));
}
int
deleg_rd_setsecattr(femarg_t *arg, vsecattr_t *vsap, int flag, cred_t *cr,
caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
return (vnext_setsecattr(arg, vsap, flag, cr, ct));
}
int
deleg_wr_setsecattr(femarg_t *arg, vsecattr_t *vsap, int flag, cred_t *cr,
caller_context_t *ct)
{
int rc;
rfs4_file_t *fp;
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rc = recall_all_delegations(fp, FALSE, ct);
if (rc == NFS4ERR_DELAY)
return (EAGAIN);
return (vnext_setsecattr(arg, vsap, flag, cr, ct));
}
int
deleg_rd_vnevent(femarg_t *arg, vnevent_t vnevent, vnode_t *dvp, char *name,
caller_context_t *ct)
{
clock_t rc;
rfs4_file_t *fp;
bool_t trunc = FALSE;
switch (vnevent) {
case VE_REMOVE:
case VE_PRE_RENAME_DEST:
case VE_RENAME_DEST:
trunc = TRUE;
case VE_PRE_RENAME_SRC:
case VE_RENAME_SRC:
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rfs4_recall_deleg(fp, trunc, NULL);
rfs4_dbe_lock(fp->rf_dbe);
while (fp->rf_dinfo.rd_dtype != OPEN_DELEGATE_NONE) {
rc = rfs4_dbe_twait(fp->rf_dbe,
ddi_get_lbolt() + SEC_TO_TICK(rfs4_lease_time));
if (rc == -1) {
rfs4_dbe_unlock(fp->rf_dbe);
rfs4_recall_deleg(fp, trunc, NULL);
rfs4_dbe_lock(fp->rf_dbe);
}
}
rfs4_dbe_unlock(fp->rf_dbe);
break;
default:
break;
}
return (vnext_vnevent(arg, vnevent, dvp, name, ct));
}
int
deleg_wr_vnevent(femarg_t *arg, vnevent_t vnevent, vnode_t *dvp, char *name,
caller_context_t *ct)
{
clock_t rc;
rfs4_file_t *fp;
bool_t trunc = FALSE;
switch (vnevent) {
case VE_REMOVE:
case VE_PRE_RENAME_DEST:
case VE_RENAME_DEST:
trunc = TRUE;
case VE_PRE_RENAME_SRC:
case VE_RENAME_SRC:
fp = (rfs4_file_t *)arg->fa_fnode->fn_available;
rfs4_recall_deleg(fp, trunc, NULL);
rfs4_dbe_lock(fp->rf_dbe);
while (fp->rf_dinfo.rd_dtype != OPEN_DELEGATE_NONE) {
rc = rfs4_dbe_twait(fp->rf_dbe,
ddi_get_lbolt() + SEC_TO_TICK(rfs4_lease_time));
if (rc == -1) {
rfs4_dbe_unlock(fp->rf_dbe);
rfs4_recall_deleg(fp, trunc, NULL);
rfs4_dbe_lock(fp->rf_dbe);
}
}
rfs4_dbe_unlock(fp->rf_dbe);
break;
default:
break;
}
return (vnext_vnevent(arg, vnevent, dvp, name, ct));
}