Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nfs4proc.c   Sprache: C

 
/*
 *  fs/nfs/nfs4proc.c
 *
 *  Client-side procedure declarations for NFSv4.
 *
 *  Copyright (c) 2002 The Regents of the University of Michigan.
 *  All rights reserved.
 *
 *  Kendrick Smith <kmsmith@umich.edu>
 *  Andy Adamson   <andros@umich.edu>
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the University nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


#include <linux/mm.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/ratelimit.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs.h>
#include <linux/nfs4.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
#include <linux/nfs_mount.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/module.h>
#include <linux/xattr.h>
#include <linux/utsname.h>
#include <linux/freezer.h>
#include <linux/iversion.h>

#include "nfs4_fs.h"
#include "delegation.h"
#include "internal.h"
#include "iostat.h"
#include "callback.h"
#include "pnfs.h"
#include "netns.h"
#include "sysfs.h"
#include "nfs4idmap.h"
#include "nfs4session.h"
#include "fscache.h"
#include "nfs42.h"

#include "nfs4trace.h"

#define NFSDBG_FACILITY  NFSDBG_PROC

#define NFS4_BITMASK_SZ  3

#define NFS4_POLL_RETRY_MIN (HZ/10)
#define NFS4_POLL_RETRY_MAX (15*HZ)

/* file attributes which can be mapped to nfs attributes */
#define NFS4_VALID_ATTRS (ATTR_MODE \
 | ATTR_UID \
 | ATTR_GID \
 | ATTR_SIZE \
 | ATTR_ATIME \
 | ATTR_MTIME \
 | ATTR_CTIME \
 | ATTR_ATIME_SET \
 | ATTR_MTIME_SET)

struct nfs4_opendata;
static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
         struct nfs_fattr *fattr, struct inode *inode);
static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
       struct nfs_fattr *fattr, struct iattr *sattr,
       struct nfs_open_context *ctx, struct nfs4_label *ilabel);
#ifdef CONFIG_NFS_V4_1
static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
  const struct cred *cred,
  struct nfs4_slot *slot,
  bool is_privileged);
static int nfs41_test_stateid(struct nfs_server *, const nfs4_stateid *,
         const struct cred *);
static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
         const struct cred *, bool);
#endif

#ifdef CONFIG_NFS_V4_SECURITY_LABEL
static inline struct nfs4_label *
nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
 struct iattr *sattr, struct nfs4_label *label)
{
 struct lsm_context shim;
 int err;

 if (label == NULL)
  return NULL;

 if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)
  return NULL;

 label->lfs = 0;
 label->pi = 0;
 label->len = 0;
 label->label = NULL;

 err = security_dentry_init_security(dentry, sattr->ia_mode,
    &dentry->d_name, NULL, &shim);
 if (err)
  return NULL;

 label->lsmid = shim.id;
 label->label = shim.context;
 label->len = shim.len;
 return label;
}
static inline void
nfs4_label_release_security(struct nfs4_label *label)
{
 struct lsm_context shim;

 if (label) {
  shim.context = label->label;
  shim.len = label->len;
  shim.id = label->lsmid;
  security_release_secctx(&shim);
 }
}
static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
{
 if (label)
  return server->attr_bitmask;

 return server->attr_bitmask_nl;
}
#else
static inline struct nfs4_label *
nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
 struct iattr *sattr, struct nfs4_label *l)
return NULL; }
static inline void
nfs4_label_release_security(struct nfs4_label *label)
return; }
static inline u32 *
nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
return server->attr_bitmask; }
#endif

/* Prevent leaks of NFSv4 errors into userland */
static int nfs4_map_errors(int err)
{
 if (err >= -1000)
  return err;
 switch (err) {
 case -NFS4ERR_RESOURCE:
 case -NFS4ERR_LAYOUTTRYLATER:
 case -NFS4ERR_RECALLCONFLICT:
 case -NFS4ERR_RETURNCONFLICT:
  return -EREMOTEIO;
 case -NFS4ERR_WRONGSEC:
 case -NFS4ERR_WRONG_CRED:
  return -EPERM;
 case -NFS4ERR_BADOWNER:
 case -NFS4ERR_BADNAME:
  return -EINVAL;
 case -NFS4ERR_SHARE_DENIED:
  return -EACCES;
 case -NFS4ERR_MINOR_VERS_MISMATCH:
  return -EPROTONOSUPPORT;
 case -NFS4ERR_FILE_OPEN:
  return -EBUSY;
 case -NFS4ERR_NOT_SAME:
  return -ENOTSYNC;
 case -ENETDOWN:
 case -ENETUNREACH:
  break;
 default:
  dprintk("%s could not handle NFSv4 error %d\n",
    __func__, -err);
  break;
 }
 return -EIO;
}

/*
 * This is our standard bitmap for GETATTR requests.
 */

const u32 nfs4_fattr_bitmap[3] = {
 FATTR4_WORD0_TYPE
 | FATTR4_WORD0_CHANGE
 | FATTR4_WORD0_SIZE
 | FATTR4_WORD0_FSID
 | FATTR4_WORD0_FILEID,
 FATTR4_WORD1_MODE
 | FATTR4_WORD1_NUMLINKS
 | FATTR4_WORD1_OWNER
 | FATTR4_WORD1_OWNER_GROUP
 | FATTR4_WORD1_RAWDEV
 | FATTR4_WORD1_SPACE_USED
 | FATTR4_WORD1_TIME_ACCESS
 | FATTR4_WORD1_TIME_CREATE
 | FATTR4_WORD1_TIME_METADATA
 | FATTR4_WORD1_TIME_MODIFY
 | FATTR4_WORD1_MOUNTED_ON_FILEID,
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
 FATTR4_WORD2_SECURITY_LABEL
#endif
};

static const u32 nfs4_pnfs_open_bitmap[3] = {
 FATTR4_WORD0_TYPE
 | FATTR4_WORD0_CHANGE
 | FATTR4_WORD0_SIZE
 | FATTR4_WORD0_FSID
 | FATTR4_WORD0_FILEID,
 FATTR4_WORD1_MODE
 | FATTR4_WORD1_NUMLINKS
 | FATTR4_WORD1_OWNER
 | FATTR4_WORD1_OWNER_GROUP
 | FATTR4_WORD1_RAWDEV
 | FATTR4_WORD1_SPACE_USED
 | FATTR4_WORD1_TIME_ACCESS
 | FATTR4_WORD1_TIME_CREATE
 | FATTR4_WORD1_TIME_METADATA
 | FATTR4_WORD1_TIME_MODIFY,
 FATTR4_WORD2_MDSTHRESHOLD
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
 | FATTR4_WORD2_SECURITY_LABEL
#endif
};

static const u32 nfs4_open_noattr_bitmap[3] = {
 FATTR4_WORD0_TYPE
 | FATTR4_WORD0_FILEID,
};

const u32 nfs4_statfs_bitmap[3] = {
 FATTR4_WORD0_FILES_AVAIL
 | FATTR4_WORD0_FILES_FREE
 | FATTR4_WORD0_FILES_TOTAL,
 FATTR4_WORD1_SPACE_AVAIL
 | FATTR4_WORD1_SPACE_FREE
 | FATTR4_WORD1_SPACE_TOTAL
};

const u32 nfs4_pathconf_bitmap[3] = {
 FATTR4_WORD0_MAXLINK
 | FATTR4_WORD0_MAXNAME,
 0
};

const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
   | FATTR4_WORD0_MAXREAD
   | FATTR4_WORD0_MAXWRITE
   | FATTR4_WORD0_LEASE_TIME,
   FATTR4_WORD1_TIME_DELTA
   | FATTR4_WORD1_FS_LAYOUT_TYPES,
   FATTR4_WORD2_LAYOUT_BLKSIZE
   | FATTR4_WORD2_CLONE_BLKSIZE
   | FATTR4_WORD2_CHANGE_ATTR_TYPE
   | FATTR4_WORD2_XATTR_SUPPORT
};

const u32 nfs4_fs_locations_bitmap[3] = {
 FATTR4_WORD0_CHANGE
 | FATTR4_WORD0_SIZE
 | FATTR4_WORD0_FSID
 | FATTR4_WORD0_FILEID
 | FATTR4_WORD0_FS_LOCATIONS,
 FATTR4_WORD1_OWNER
 | FATTR4_WORD1_OWNER_GROUP
 | FATTR4_WORD1_RAWDEV
 | FATTR4_WORD1_SPACE_USED
 | FATTR4_WORD1_TIME_ACCESS
 | FATTR4_WORD1_TIME_METADATA
 | FATTR4_WORD1_TIME_MODIFY
 | FATTR4_WORD1_MOUNTED_ON_FILEID,
};

static void nfs4_bitmap_copy_adjust(__u32 *dst, const __u32 *src,
        struct inode *inode, unsigned long flags)
{
 unsigned long cache_validity;

 memcpy(dst, src, NFS4_BITMASK_SZ*sizeof(*dst));
 if (!inode || !nfs_have_read_or_write_delegation(inode))
  return;

 cache_validity = READ_ONCE(NFS_I(inode)->cache_validity) | flags;

 /* Remove the attributes over which we have full control */
 dst[1] &= ~FATTR4_WORD1_RAWDEV;
 if (!(cache_validity & NFS_INO_INVALID_SIZE))
  dst[0] &= ~FATTR4_WORD0_SIZE;

 if (!(cache_validity & NFS_INO_INVALID_CHANGE))
  dst[0] &= ~FATTR4_WORD0_CHANGE;

 if (!(cache_validity & NFS_INO_INVALID_MODE))
  dst[1] &= ~FATTR4_WORD1_MODE;
 if (!(cache_validity & NFS_INO_INVALID_OTHER))
  dst[1] &= ~(FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP);

 if (!(cache_validity & NFS_INO_INVALID_BTIME))
  dst[1] &= ~FATTR4_WORD1_TIME_CREATE;

 if (nfs_have_delegated_mtime(inode)) {
  if (!(cache_validity & NFS_INO_INVALID_ATIME))
   dst[1] &= ~(FATTR4_WORD1_TIME_ACCESS|FATTR4_WORD1_TIME_ACCESS_SET);
  if (!(cache_validity & NFS_INO_INVALID_MTIME))
   dst[1] &= ~(FATTR4_WORD1_TIME_MODIFY|FATTR4_WORD1_TIME_MODIFY_SET);
  if (!(cache_validity & NFS_INO_INVALID_CTIME))
   dst[1] &= ~(FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY_SET);
 } else if (nfs_have_delegated_atime(inode)) {
  if (!(cache_validity & NFS_INO_INVALID_ATIME))
   dst[1] &= ~(FATTR4_WORD1_TIME_ACCESS|FATTR4_WORD1_TIME_ACCESS_SET);
 }
}

static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
  struct nfs4_readdir_arg *readdir)
{
 unsigned int attrs = FATTR4_WORD0_FILEID | FATTR4_WORD0_TYPE;
 __be32 *start, *p;

 if (cookie > 2) {
  readdir->cookie = cookie;
  memcpy(&readdir->verifier, verifier, sizeof(readdir->verifier));
  return;
 }

 readdir->cookie = 0;
 memset(&readdir->verifier, 0, sizeof(readdir->verifier));
 if (cookie == 2)
  return;
 
 /*
 * NFSv4 servers do not return entries for '.' and '..'
 * Therefore, we fake these entries here.  We let '.'
 * have cookie 0 and '..' have cookie 1.  Note that
 * when talking to the server, we always send cookie 0
 * instead of 1 or 2.
 */

 start = p = kmap_atomic(*readdir->pages);
 
 if (cookie == 0) {
  *p++ = xdr_one;                                  /* next */
  *p++ = xdr_zero;                   /* cookie, first word */
  *p++ = xdr_one;                   /* cookie, second word */
  *p++ = xdr_one;                             /* entry len */
  memcpy(p, ".\0\0\0", 4);                        /* entry */
  p++;
  *p++ = xdr_one;                         /* bitmap length */
  *p++ = htonl(attrs);                           /* bitmap */
  *p++ = htonl(12);             /* attribute buffer length */
  *p++ = htonl(NF4DIR);
  p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry)));
 }
 
 *p++ = xdr_one;                                  /* next */
 *p++ = xdr_zero;                   /* cookie, first word */
 *p++ = xdr_two;                   /* cookie, second word */
 *p++ = xdr_two;                             /* entry len */
 memcpy(p, "..\0\0", 4);                         /* entry */
 p++;
 *p++ = xdr_one;                         /* bitmap length */
 *p++ = htonl(attrs);                           /* bitmap */
 *p++ = htonl(12);             /* attribute buffer length */
 *p++ = htonl(NF4DIR);
 spin_lock(&dentry->d_lock);
 p = xdr_encode_hyper(p, NFS_FILEID(d_inode(dentry->d_parent)));
 spin_unlock(&dentry->d_lock);

 readdir->pgbase = (char *)p - (char *)start;
 readdir->count -= readdir->pgbase;
 kunmap_atomic(start);
}

static void nfs4_fattr_set_prechange(struct nfs_fattr *fattr, u64 version)
{
 if (!(fattr->valid & NFS_ATTR_FATTR_PRECHANGE)) {
  fattr->pre_change_attr = version;
  fattr->valid |= NFS_ATTR_FATTR_PRECHANGE;
 }
}

static void nfs4_test_and_free_stateid(struct nfs_server *server,
  nfs4_stateid *stateid,
  const struct cred *cred)
{
 const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;

 ops->test_and_free_expired(server, stateid, cred);
}

static void __nfs4_free_revoked_stateid(struct nfs_server *server,
  nfs4_stateid *stateid,
  const struct cred *cred)
{
 stateid->type = NFS4_REVOKED_STATEID_TYPE;
 nfs4_test_and_free_stateid(server, stateid, cred);
}

static void nfs4_free_revoked_stateid(struct nfs_server *server,
  const nfs4_stateid *stateid,
  const struct cred *cred)
{
 nfs4_stateid tmp;

 nfs4_stateid_copy(&tmp, stateid);
 __nfs4_free_revoked_stateid(server, &tmp, cred);
}

static long nfs4_update_delay(long *timeout)
{
 long ret;
 if (!timeout)
  return NFS4_POLL_RETRY_MAX;
 if (*timeout <= 0)
  *timeout = NFS4_POLL_RETRY_MIN;
 if (*timeout > NFS4_POLL_RETRY_MAX)
  *timeout = NFS4_POLL_RETRY_MAX;
 ret = *timeout;
 *timeout <<= 1;
 return ret;
}

static int nfs4_delay_killable(long *timeout)
{
 might_sleep();

 if (unlikely(nfs_current_task_exiting()))
  return -EINTR;
 __set_current_state(TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
 schedule_timeout(nfs4_update_delay(timeout));
 if (!__fatal_signal_pending(current))
  return 0;
 return -EINTR;
}

static int nfs4_delay_interruptible(long *timeout)
{
 might_sleep();

 if (unlikely(nfs_current_task_exiting()))
  return -EINTR;
 __set_current_state(TASK_INTERRUPTIBLE|TASK_FREEZABLE_UNSAFE);
 schedule_timeout(nfs4_update_delay(timeout));
 if (!signal_pending(current))
  return 0;
 return __fatal_signal_pending(current) ? -EINTR :-ERESTARTSYS;
}

static int nfs4_delay(long *timeout, bool interruptible)
{
 if (interruptible)
  return nfs4_delay_interruptible(timeout);
 return nfs4_delay_killable(timeout);
}

static const nfs4_stateid *
nfs4_recoverable_stateid(const nfs4_stateid *stateid)
{
 if (!stateid)
  return NULL;
 switch (stateid->type) {
 case NFS4_OPEN_STATEID_TYPE:
 case NFS4_LOCK_STATEID_TYPE:
 case NFS4_DELEGATION_STATEID_TYPE:
  return stateid;
 default:
  break;
 }
 return NULL;
}

/* This is the error handling routine for processes that are allowed
 * to sleep.
 */

static int nfs4_do_handle_exception(struct nfs_server *server,
  int errorcode, struct nfs4_exception *exception)
{
 struct nfs_client *clp = server->nfs_client;
 struct nfs4_state *state = exception->state;
 const nfs4_stateid *stateid;
 struct inode *inode = exception->inode;
 int ret = errorcode;

 exception->delay = 0;
 exception->recovering = 0;
 exception->retry = 0;

 stateid = nfs4_recoverable_stateid(exception->stateid);
 if (stateid == NULL && state != NULL)
  stateid = nfs4_recoverable_stateid(&state->stateid);

 switch(errorcode) {
  case 0:
   return 0;
  case -NFS4ERR_BADHANDLE:
  case -ESTALE:
   if (inode != NULL && S_ISREG(inode->i_mode))
    pnfs_destroy_layout(NFS_I(inode));
   break;
  case -NFS4ERR_DELEG_REVOKED:
  case -NFS4ERR_ADMIN_REVOKED:
  case -NFS4ERR_EXPIRED:
  case -NFS4ERR_BAD_STATEID:
  case -NFS4ERR_PARTNER_NO_AUTH:
   if (inode != NULL && stateid != NULL) {
    nfs_inode_find_state_and_recover(inode,
      stateid);
    goto wait_on_recovery;
   }
   fallthrough;
  case -NFS4ERR_OPENMODE:
   if (inode) {
    int err;

    err = nfs_async_inode_return_delegation(inode,
      stateid);
    if (err == 0)
     goto wait_on_recovery;
    if (stateid != NULL && stateid->type == NFS4_DELEGATION_STATEID_TYPE) {
     exception->retry = 1;
     break;
    }
   }
   if (state == NULL)
    break;
   ret = nfs4_schedule_stateid_recovery(server, state);
   if (ret < 0)
    break;
   goto wait_on_recovery;
  case -NFS4ERR_STALE_STATEID:
  case -NFS4ERR_STALE_CLIENTID:
   nfs4_schedule_lease_recovery(clp);
   goto wait_on_recovery;
  case -NFS4ERR_MOVED:
   ret = nfs4_schedule_migration_recovery(server);
   if (ret < 0)
    break;
   goto wait_on_recovery;
  case -NFS4ERR_LEASE_MOVED:
   nfs4_schedule_lease_moved_recovery(clp);
   goto wait_on_recovery;
#if defined(CONFIG_NFS_V4_1)
  case -NFS4ERR_BADSESSION:
  case -NFS4ERR_BADSLOT:
  case -NFS4ERR_BAD_HIGH_SLOT:
  case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
  case -NFS4ERR_DEADSESSION:
  case -NFS4ERR_SEQ_FALSE_RETRY:
  case -NFS4ERR_SEQ_MISORDERED:
   /* Handled in nfs41_sequence_process() */
   goto wait_on_recovery;
#endif /* defined(CONFIG_NFS_V4_1) */
  case -NFS4ERR_FILE_OPEN:
   if (exception->timeout > HZ) {
    /* We have retried a decent amount, time to
 * fail
 */

    ret = -EBUSY;
    break;
   }
   fallthrough;
  case -NFS4ERR_DELAY:
   nfs_inc_server_stats(server, NFSIOS_DELAY);
   fallthrough;
  case -NFS4ERR_GRACE:
  case -NFS4ERR_LAYOUTTRYLATER:
  case -NFS4ERR_RECALLCONFLICT:
  case -NFS4ERR_RETURNCONFLICT:
   exception->delay = 1;
   return 0;

  case -NFS4ERR_RETRY_UNCACHED_REP:
  case -NFS4ERR_OLD_STATEID:
   exception->retry = 1;
   break;
  case -NFS4ERR_BADOWNER:
   /* The following works around a Linux server bug! */
  case -NFS4ERR_BADNAME:
   if (server->caps & NFS_CAP_UIDGID_NOMAP) {
    server->caps &= ~NFS_CAP_UIDGID_NOMAP;
    exception->retry = 1;
    printk(KERN_WARNING "NFS: v4 server %s "
      "does not accept raw "
      "uid/gids. "
      "Reenabling the idmapper.\n",
      server->nfs_client->cl_hostname);
   }
 }
 /* We failed to handle the error */
 return nfs4_map_errors(ret);
wait_on_recovery:
 exception->recovering = 1;
 return 0;
}

/*
 * Track the number of NFS4ERR_DELAY related retransmissions and return
 * EAGAIN if the 'softerr' mount option is set, and we've exceeded the limit
 * set by 'nfs_delay_retrans'.
 */

static int nfs4_exception_should_retrans(const struct nfs_server *server,
      struct nfs4_exception *exception)
{
 if (server->flags & NFS_MOUNT_SOFTERR && nfs_delay_retrans >= 0) {
  if (exception->retrans++ >= (unsigned short)nfs_delay_retrans)
   return -EAGAIN;
 }
 return 0;
}

/* This is the error handling routine for processes that are allowed
 * to sleep.
 */

int nfs4_handle_exception(struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
{
 struct nfs_client *clp = server->nfs_client;
 int ret;

 ret = nfs4_do_handle_exception(server, errorcode, exception);
 if (exception->delay) {
  int ret2 = nfs4_exception_should_retrans(server, exception);
  if (ret2 < 0) {
   exception->retry = 0;
   return ret2;
  }
  ret = nfs4_delay(&exception->timeout,
    exception->interruptible);
  goto out_retry;
 }
 if (exception->recovering) {
  if (exception->task_is_privileged)
   return -EDEADLOCK;
  ret = nfs4_wait_clnt_recover(clp);
  if (test_bit(NFS_MIG_FAILED, &server->mig_status))
   return -EIO;
  goto out_retry;
 }
 return ret;
out_retry:
 if (ret == 0)
  exception->retry = 1;
 return ret;
}

static int
nfs4_async_handle_exception(struct rpc_task *task, struct nfs_server *server,
  int errorcode, struct nfs4_exception *exception)
{
 struct nfs_client *clp = server->nfs_client;
 int ret;

 if ((task->tk_rpc_status == -ENETDOWN ||
      task->tk_rpc_status == -ENETUNREACH) &&
     task->tk_flags & RPC_TASK_NETUNREACH_FATAL) {
  exception->delay = 0;
  exception->recovering = 0;
  exception->retry = 0;
  return -EIO;
 }

 ret = nfs4_do_handle_exception(server, errorcode, exception);
 if (exception->delay) {
  int ret2 = nfs4_exception_should_retrans(server, exception);
  if (ret2 < 0) {
   exception->retry = 0;
   return ret2;
  }
  rpc_delay(task, nfs4_update_delay(&exception->timeout));
  goto out_retry;
 }
 if (exception->recovering) {
  if (exception->task_is_privileged)
   return -EDEADLOCK;
  rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
  if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
   rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
  goto out_retry;
 }
 if (test_bit(NFS_MIG_FAILED, &server->mig_status))
  ret = -EIO;
 return ret;
out_retry:
 if (ret == 0) {
  exception->retry = 1;
  /*
 * For NFS4ERR_MOVED, the client transport will need to
 * be recomputed after migration recovery has completed.
 */

  if (errorcode == -NFS4ERR_MOVED)
   rpc_task_release_transport(task);
 }
 return ret;
}

int
nfs4_async_handle_error(struct rpc_task *task, struct nfs_server *server,
   struct nfs4_state *state, long *timeout)
{
 struct nfs4_exception exception = {
  .state = state,
 };

 if (task->tk_status >= 0)
  return 0;
 if (timeout)
  exception.timeout = *timeout;
 task->tk_status = nfs4_async_handle_exception(task, server,
   task->tk_status,
   &exception);
 if (exception.delay && timeout)
  *timeout = exception.timeout;
 if (exception.retry)
  return -EAGAIN;
 return 0;
}

/*
 * Return 'true' if 'clp' is using an rpc_client that is integrity protected
 * or 'false' otherwise.
 */

static bool _nfs4_is_integrity_protected(struct nfs_client *clp)
{
 rpc_authflavor_t flavor = clp->cl_rpcclient->cl_auth->au_flavor;
 return (flavor == RPC_AUTH_GSS_KRB5I) || (flavor == RPC_AUTH_GSS_KRB5P);
}

static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp)
{
 spin_lock(&clp->cl_lock);
 if (time_before(clp->cl_last_renewal,timestamp))
  clp->cl_last_renewal = timestamp;
 spin_unlock(&clp->cl_lock);
}

static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
{
 struct nfs_client *clp = server->nfs_client;

 if (!nfs4_has_session(clp))
  do_renew_lease(clp, timestamp);
}

struct nfs4_call_sync_data {
 const struct nfs_server *seq_server;
 struct nfs4_sequence_args *seq_args;
 struct nfs4_sequence_res *seq_res;
};

void nfs4_init_sequence(struct nfs4_sequence_args *args,
   struct nfs4_sequence_res *res, int cache_reply,
   int privileged)
{
 args->sa_slot = NULL;
 args->sa_cache_this = cache_reply;
 args->sa_privileged = privileged;

 res->sr_slot = NULL;
}

static void nfs40_sequence_free_slot(struct nfs4_sequence_res *res)
{
 struct nfs4_slot *slot = res->sr_slot;
 struct nfs4_slot_table *tbl;

 tbl = slot->table;
 spin_lock(&tbl->slot_tbl_lock);
 if (!nfs41_wake_and_assign_slot(tbl, slot))
  nfs4_free_slot(tbl, slot);
 spin_unlock(&tbl->slot_tbl_lock);

 res->sr_slot = NULL;
}

static int nfs40_sequence_done(struct rpc_task *task,
          struct nfs4_sequence_res *res)
{
 if (res->sr_slot != NULL)
  nfs40_sequence_free_slot(res);
 return 1;
}

#if defined(CONFIG_NFS_V4_1)

static void nfs41_release_slot(struct nfs4_slot *slot)
{
 struct nfs4_session *session;
 struct nfs4_slot_table *tbl;
 bool send_new_highest_used_slotid = false;

 if (!slot)
  return;
 tbl = slot->table;
 session = tbl->session;

 /* Bump the slot sequence number */
 if (slot->seq_done)
  slot->seq_nr++;
 slot->seq_done = 0;

 spin_lock(&tbl->slot_tbl_lock);
 /* Be nice to the server: try to ensure that the last transmitted
 * value for highest_user_slotid <= target_highest_slotid
 */

 if (tbl->highest_used_slotid > tbl->target_highest_slotid)
  send_new_highest_used_slotid = true;

 if (nfs41_wake_and_assign_slot(tbl, slot)) {
  send_new_highest_used_slotid = false;
  goto out_unlock;
 }
 nfs4_free_slot(tbl, slot);

 if (tbl->highest_used_slotid != NFS4_NO_SLOT)
  send_new_highest_used_slotid = false;
out_unlock:
 spin_unlock(&tbl->slot_tbl_lock);
 if (send_new_highest_used_slotid)
  nfs41_notify_server(session->clp);
 if (waitqueue_active(&tbl->slot_waitq))
  wake_up_all(&tbl->slot_waitq);
}

static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res)
{
 nfs41_release_slot(res->sr_slot);
 res->sr_slot = NULL;
}

static void nfs4_slot_sequence_record_sent(struct nfs4_slot *slot,
  u32 seqnr)
{
 if ((s32)(seqnr - slot->seq_nr_highest_sent) > 0)
  slot->seq_nr_highest_sent = seqnr;
}
static void nfs4_slot_sequence_acked(struct nfs4_slot *slot, u32 seqnr)
{
 nfs4_slot_sequence_record_sent(slot, seqnr);
 slot->seq_nr_last_acked = seqnr;
}

static void nfs4_probe_sequence(struct nfs_client *client, const struct cred *cred,
    struct nfs4_slot *slot)
{
 struct rpc_task *task = _nfs41_proc_sequence(client, cred, slot, true);
 if (!IS_ERR(task))
  rpc_put_task_async(task);
}

static int nfs41_sequence_process(struct rpc_task *task,
  struct nfs4_sequence_res *res)
{
 struct nfs4_session *session;
 struct nfs4_slot *slot = res->sr_slot;
 struct nfs_client *clp;
 int status;
 int ret = 1;

 if (slot == NULL)
  goto out_noaction;
 /* don't increment the sequence number if the task wasn't sent */
 if (!RPC_WAS_SENT(task) || slot->seq_done)
  goto out;

 session = slot->table->session;
 clp = session->clp;

 trace_nfs4_sequence_done(session, res);

 status = res->sr_status;
 if (task->tk_status == -NFS4ERR_DEADSESSION)
  status = -NFS4ERR_DEADSESSION;

 /* Check the SEQUENCE operation status */
 switch (status) {
 case 0:
  /* Mark this sequence number as having been acked */
  nfs4_slot_sequence_acked(slot, slot->seq_nr);
  /* Update the slot's sequence and clientid lease timer */
  slot->seq_done = 1;
  do_renew_lease(clp, res->sr_timestamp);
  /* Check sequence flags */
  nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags,
    !!slot->privileged);
  nfs41_update_target_slotid(slot->table, slot, res);
  break;
 case 1:
  /*
 * sr_status remains 1 if an RPC level error occurred.
 * The server may or may not have processed the sequence
 * operation..
 */

  nfs4_slot_sequence_record_sent(slot, slot->seq_nr);
  slot->seq_done = 1;
  goto out;
 case -NFS4ERR_DELAY:
  /* The server detected a resend of the RPC call and
 * returned NFS4ERR_DELAY as per Section 2.10.6.2
 * of RFC5661.
 */

  dprintk("%s: slot=%u seq=%u: Operation in progress\n",
   __func__,
   slot->slot_nr,
   slot->seq_nr);
  goto out_retry;
 case -NFS4ERR_RETRY_UNCACHED_REP:
 case -NFS4ERR_SEQ_FALSE_RETRY:
  /*
 * The server thinks we tried to replay a request.
 * Retry the call after bumping the sequence ID.
 */

  nfs4_slot_sequence_acked(slot, slot->seq_nr);
  goto retry_new_seq;
 case -NFS4ERR_BADSLOT:
  /*
 * The slot id we used was probably retired. Try again
 * using a different slot id.
 */

  if (slot->slot_nr < slot->table->target_highest_slotid)
   goto session_recover;
  goto retry_nowait;
 case -NFS4ERR_SEQ_MISORDERED:
  nfs4_slot_sequence_record_sent(slot, slot->seq_nr);
  /*
 * Were one or more calls using this slot interrupted?
 * If the server never received the request, then our
 * transmitted slot sequence number may be too high. However,
 * if the server did receive the request then it might
 * accidentally give us a reply with a mismatched operation.
 * We can sort this out by sending a lone sequence operation
 * to the server on the same slot.
 */

  if ((s32)(slot->seq_nr - slot->seq_nr_last_acked) > 1) {
   slot->seq_nr--;
   if (task->tk_msg.rpc_proc != &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE]) {
    nfs4_probe_sequence(clp, task->tk_msg.rpc_cred, slot);
    res->sr_slot = NULL;
   }
   goto retry_nowait;
  }
  /*
 * RFC5661:
 * A retry might be sent while the original request is
 * still in progress on the replier. The replier SHOULD
 * deal with the issue by returning NFS4ERR_DELAY as the
 * reply to SEQUENCE or CB_SEQUENCE operation, but
 * implementations MAY return NFS4ERR_SEQ_MISORDERED.
 *
 * Restart the search after a delay.
 */

  slot->seq_nr = slot->seq_nr_highest_sent;
  goto out_retry;
 case -NFS4ERR_BADSESSION:
 case -NFS4ERR_DEADSESSION:
 case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
  goto session_recover;
 default:
  /* Just update the slot sequence no. */
  slot->seq_done = 1;
 }
out:
 /* The session may be reset by one of the error handlers. */
 dprintk("%s: Error %d free the slot \n", __func__, res->sr_status);
out_noaction:
 return ret;
session_recover:
 set_bit(NFS4_SLOT_TBL_DRAINING, &session->fc_slot_table.slot_tbl_state);
 nfs4_schedule_session_recovery(session, status);
 dprintk("%s ERROR: %d Reset session\n", __func__, status);
 nfs41_sequence_free_slot(res);
 goto out;
retry_new_seq:
 ++slot->seq_nr;
retry_nowait:
 if (rpc_restart_call_prepare(task)) {
  nfs41_sequence_free_slot(res);
  task->tk_status = 0;
  ret = 0;
 }
 goto out;
out_retry:
 if (!rpc_restart_call(task))
  goto out;
 rpc_delay(task, NFS4_POLL_RETRY_MAX);
 return 0;
}

int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
{
 if (!nfs41_sequence_process(task, res))
  return 0;
 if (res->sr_slot != NULL)
  nfs41_sequence_free_slot(res);
 return 1;

}
EXPORT_SYMBOL_GPL(nfs41_sequence_done);

static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
{
 if (res->sr_slot == NULL)
  return 1;
 if (res->sr_slot->table->session != NULL)
  return nfs41_sequence_process(task, res);
 return nfs40_sequence_done(task, res);
}

static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res)
{
 if (res->sr_slot != NULL) {
  if (res->sr_slot->table->session != NULL)
   nfs41_sequence_free_slot(res);
  else
   nfs40_sequence_free_slot(res);
 }
}

int nfs4_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res)
{
 if (res->sr_slot == NULL)
  return 1;
 if (!res->sr_slot->table->session)
  return nfs40_sequence_done(task, res);
 return nfs41_sequence_done(task, res);
}
EXPORT_SYMBOL_GPL(nfs4_sequence_done);

static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata)
{
 struct nfs4_call_sync_data *data = calldata;

 dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server);

 nfs4_setup_sequence(data->seq_server->nfs_client,
       data->seq_args, data->seq_res, task);
}

static void nfs41_call_sync_done(struct rpc_task *task, void *calldata)
{
 struct nfs4_call_sync_data *data = calldata;

 nfs41_sequence_done(task, data->seq_res);
}

static const struct rpc_call_ops nfs41_call_sync_ops = {
 .rpc_call_prepare = nfs41_call_sync_prepare,
 .rpc_call_done = nfs41_call_sync_done,
};

#else /* !CONFIG_NFS_V4_1 */

static int nfs4_sequence_process(struct rpc_task *task, struct nfs4_sequence_res *res)
{
 return nfs40_sequence_done(task, res);
}

static void nfs4_sequence_free_slot(struct nfs4_sequence_res *res)
{
 if (res->sr_slot != NULL)
  nfs40_sequence_free_slot(res);
}

int nfs4_sequence_done(struct rpc_task *task,
         struct nfs4_sequence_res *res)
{
 return nfs40_sequence_done(task, res);
}
EXPORT_SYMBOL_GPL(nfs4_sequence_done);

#endif /* !CONFIG_NFS_V4_1 */

static void nfs41_sequence_res_init(struct nfs4_sequence_res *res)
{
 res->sr_timestamp = jiffies;
 res->sr_status_flags = 0;
 res->sr_status = 1;
}

static
void nfs4_sequence_attach_slot(struct nfs4_sequence_args *args,
  struct nfs4_sequence_res *res,
  struct nfs4_slot *slot)
{
 if (!slot)
  return;
 slot->privileged = args->sa_privileged ? 1 : 0;
 args->sa_slot = slot;

 res->sr_slot = slot;
}

int nfs4_setup_sequence(struct nfs_client *client,
   struct nfs4_sequence_args *args,
   struct nfs4_sequence_res *res,
   struct rpc_task *task)
{
 struct nfs4_session *session = nfs4_get_session(client);
 struct nfs4_slot_table *tbl  = client->cl_slot_tbl;
 struct nfs4_slot *slot;

 /* slot already allocated? */
 if (res->sr_slot != NULL)
  goto out_start;

 if (session)
  tbl = &session->fc_slot_table;

 spin_lock(&tbl->slot_tbl_lock);
 /* The state manager will wait until the slot table is empty */
 if (nfs4_slot_tbl_draining(tbl) && !args->sa_privileged)
  goto out_sleep;

 slot = nfs4_alloc_slot(tbl);
 if (IS_ERR(slot)) {
  if (slot == ERR_PTR(-ENOMEM))
   goto out_sleep_timeout;
  goto out_sleep;
 }
 spin_unlock(&tbl->slot_tbl_lock);

 nfs4_sequence_attach_slot(args, res, slot);

 trace_nfs4_setup_sequence(session, args);
out_start:
 nfs41_sequence_res_init(res);
 rpc_call_start(task);
 return 0;
out_sleep_timeout:
 /* Try again in 1/4 second */
 if (args->sa_privileged)
  rpc_sleep_on_priority_timeout(&tbl->slot_tbl_waitq, task,
    jiffies + (HZ >> 2), RPC_PRIORITY_PRIVILEGED);
 else
  rpc_sleep_on_timeout(&tbl->slot_tbl_waitq, task,
    NULL, jiffies + (HZ >> 2));
 spin_unlock(&tbl->slot_tbl_lock);
 return -EAGAIN;
out_sleep:
 if (args->sa_privileged)
  rpc_sleep_on_priority(&tbl->slot_tbl_waitq, task,
    RPC_PRIORITY_PRIVILEGED);
 else
  rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
 spin_unlock(&tbl->slot_tbl_lock);
 return -EAGAIN;
}
EXPORT_SYMBOL_GPL(nfs4_setup_sequence);

static void nfs40_call_sync_prepare(struct rpc_task *task, void *calldata)
{
 struct nfs4_call_sync_data *data = calldata;
 nfs4_setup_sequence(data->seq_server->nfs_client,
    data->seq_args, data->seq_res, task);
}

static void nfs40_call_sync_done(struct rpc_task *task, void *calldata)
{
 struct nfs4_call_sync_data *data = calldata;
 nfs4_sequence_done(task, data->seq_res);
}

static const struct rpc_call_ops nfs40_call_sync_ops = {
 .rpc_call_prepare = nfs40_call_sync_prepare,
 .rpc_call_done = nfs40_call_sync_done,
};

static int nfs4_call_sync_custom(struct rpc_task_setup *task_setup)
{
 int ret;
 struct rpc_task *task;

 task = rpc_run_task(task_setup);
 if (IS_ERR(task))
  return PTR_ERR(task);

 ret = task->tk_status;
 rpc_put_task(task);
 return ret;
}

static int nfs4_do_call_sync(struct rpc_clnt *clnt,
        struct nfs_server *server,
        struct rpc_message *msg,
        struct nfs4_sequence_args *args,
        struct nfs4_sequence_res *res,
        unsigned short task_flags)
{
 struct nfs_client *clp = server->nfs_client;
 struct nfs4_call_sync_data data = {
  .seq_server = server,
  .seq_args = args,
  .seq_res = res,
 };
 struct rpc_task_setup task_setup = {
  .rpc_client = clnt,
  .rpc_message = msg,
  .callback_ops = clp->cl_mvops->call_sync_ops,
  .callback_data = &data,
  .flags = task_flags,
 };

 return nfs4_call_sync_custom(&task_setup);
}

static int nfs4_call_sync_sequence(struct rpc_clnt *clnt,
       struct nfs_server *server,
       struct rpc_message *msg,
       struct nfs4_sequence_args *args,
       struct nfs4_sequence_res *res)
{
 unsigned short task_flags = 0;

 if (server->caps & NFS_CAP_MOVEABLE)
  task_flags = RPC_TASK_MOVEABLE;
 return nfs4_do_call_sync(clnt, server, msg, args, res, task_flags);
}


int nfs4_call_sync(struct rpc_clnt *clnt,
     struct nfs_server *server,
     struct rpc_message *msg,
     struct nfs4_sequence_args *args,
     struct nfs4_sequence_res *res,
     int cache_reply)
{
 nfs4_init_sequence(args, res, cache_reply, 0);
 return nfs4_call_sync_sequence(clnt, server, msg, args, res);
}

static void
nfs4_inc_nlink_locked(struct inode *inode)
{
 nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
          NFS_INO_INVALID_CTIME |
          NFS_INO_INVALID_NLINK);
 inc_nlink(inode);
}

static void
nfs4_inc_nlink(struct inode *inode)
{
 spin_lock(&inode->i_lock);
 nfs4_inc_nlink_locked(inode);
 spin_unlock(&inode->i_lock);
}

static void
nfs4_dec_nlink_locked(struct inode *inode)
{
 nfs_set_cache_invalid(inode, NFS_INO_INVALID_CHANGE |
          NFS_INO_INVALID_CTIME |
          NFS_INO_INVALID_NLINK);
 drop_nlink(inode);
}

static void
nfs4_update_changeattr_locked(struct inode *inode,
  struct nfs4_change_info *cinfo,
  unsigned long timestamp, unsigned long cache_validity)
{
 struct nfs_inode *nfsi = NFS_I(inode);
 u64 change_attr = inode_peek_iversion_raw(inode);

 if (!nfs_have_delegated_mtime(inode))
  cache_validity |= NFS_INO_INVALID_CTIME | NFS_INO_INVALID_MTIME;
 if (S_ISDIR(inode->i_mode))
  cache_validity |= NFS_INO_INVALID_DATA;

 switch (NFS_SERVER(inode)->change_attr_type) {
 case NFS4_CHANGE_TYPE_IS_UNDEFINED:
  if (cinfo->after == change_attr)
   goto out;
  break;
 default:
  if ((s64)(change_attr - cinfo->after) >= 0)
   goto out;
 }

 inode_set_iversion_raw(inode, cinfo->after);
 if (!cinfo->atomic || cinfo->before != change_attr) {
  if (S_ISDIR(inode->i_mode))
   nfs_force_lookup_revalidate(inode);

  if (!nfs_have_delegated_attributes(inode))
   cache_validity |=
    NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL |
    NFS_INO_INVALID_SIZE | NFS_INO_INVALID_OTHER |
    NFS_INO_INVALID_BLOCKS | NFS_INO_INVALID_NLINK |
    NFS_INO_INVALID_MODE | NFS_INO_INVALID_BTIME |
    NFS_INO_INVALID_XATTR;
  nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
 }
 nfsi->attrtimeo_timestamp = jiffies;
 nfsi->read_cache_jiffies = timestamp;
 nfsi->attr_gencount = nfs_inc_attr_generation_counter();
 nfsi->cache_validity &= ~NFS_INO_INVALID_CHANGE;
out:
 nfs_set_cache_invalid(inode, cache_validity);
}

void
nfs4_update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo,
  unsigned long timestamp, unsigned long cache_validity)
{
 spin_lock(&dir->i_lock);
 nfs4_update_changeattr_locked(dir, cinfo, timestamp, cache_validity);
 spin_unlock(&dir->i_lock);
}

struct nfs4_open_createattrs {
 struct nfs4_label *label;
 struct iattr *sattr;
 const __u32 verf[2];
};

static bool nfs4_clear_cap_atomic_open_v1(struct nfs_server *server,
  int err, struct nfs4_exception *exception)
{
 if (err != -EINVAL)
  return false;
 if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
  return false;
 server->caps &= ~NFS_CAP_ATOMIC_OPEN_V1;
 exception->retry = 1;
 return true;
}

static fmode_t _nfs4_ctx_to_accessmode(const struct nfs_open_context *ctx)
{
  return ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC);
}

static fmode_t _nfs4_ctx_to_openmode(const struct nfs_open_context *ctx)
{
 fmode_t ret = ctx->mode & (FMODE_READ|FMODE_WRITE);

 return (ctx->mode & FMODE_EXEC) ? FMODE_READ | ret : ret;
}

static u32
nfs4_fmode_to_share_access(fmode_t fmode)
{
 u32 res = 0;

 switch (fmode & (FMODE_READ | FMODE_WRITE)) {
 case FMODE_READ:
  res = NFS4_SHARE_ACCESS_READ;
  break;
 case FMODE_WRITE:
  res = NFS4_SHARE_ACCESS_WRITE;
  break;
 case FMODE_READ|FMODE_WRITE:
  res = NFS4_SHARE_ACCESS_BOTH;
 }
 return res;
}

static u32
nfs4_map_atomic_open_share(struct nfs_server *server,
  fmode_t fmode, int openflags)
{
 u32 res = nfs4_fmode_to_share_access(fmode);

 if (!(server->caps & NFS_CAP_ATOMIC_OPEN_V1))
  goto out;
 /* Want no delegation if we're using O_DIRECT */
 if (openflags & O_DIRECT) {
  res |= NFS4_SHARE_WANT_NO_DELEG;
  goto out;
 }
 /* res |= NFS4_SHARE_WANT_NO_PREFERENCE; */
 if (server->caps & NFS_CAP_DELEGTIME)
  res |= NFS4_SHARE_WANT_DELEG_TIMESTAMPS;
 if (server->caps & NFS_CAP_OPEN_XOR)
  res |= NFS4_SHARE_WANT_OPEN_XOR_DELEGATION;
out:
 return res;
}

static enum open_claim_type4
nfs4_map_atomic_open_claim(struct nfs_server *server,
  enum open_claim_type4 claim)
{
 if (server->caps & NFS_CAP_ATOMIC_OPEN_V1)
  return claim;
 switch (claim) {
 default:
  return claim;
 case NFS4_OPEN_CLAIM_FH:
  return NFS4_OPEN_CLAIM_NULL;
 case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
  return NFS4_OPEN_CLAIM_DELEGATE_CUR;
 case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
  return NFS4_OPEN_CLAIM_DELEGATE_PREV;
 }
}

static void nfs4_init_opendata_res(struct nfs4_opendata *p)
{
 p->o_res.f_attr = &p->f_attr;
 p->o_res.seqid = p->o_arg.seqid;
 p->c_res.seqid = p->c_arg.seqid;
 p->o_res.server = p->o_arg.server;
 p->o_res.access_request = p->o_arg.access;
 nfs_fattr_init(&p->f_attr);
 nfs_fattr_init_names(&p->f_attr, &p->owner_name, &p->group_name);
}

static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
  struct nfs4_state_owner *sp, fmode_t fmode, int flags,
  const struct nfs4_open_createattrs *c,
  enum open_claim_type4 claim,
  gfp_t gfp_mask)
{
 struct dentry *parent = dget_parent(dentry);
 struct inode *dir = d_inode(parent);
 struct nfs_server *server = NFS_SERVER(dir);
 struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
 struct nfs4_label *label = (c != NULL) ? c->label : NULL;
 struct nfs4_opendata *p;

 p = kzalloc(sizeof(*p), gfp_mask);
 if (p == NULL)
  goto err;

 p->f_attr.label = nfs4_label_alloc(server, gfp_mask);
 if (IS_ERR(p->f_attr.label))
  goto err_free_p;

 p->a_label = nfs4_label_alloc(server, gfp_mask);
 if (IS_ERR(p->a_label))
  goto err_free_f;

 alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid;
 p->o_arg.seqid = alloc_seqid(&sp->so_seqid, gfp_mask);
 if (IS_ERR(p->o_arg.seqid))
  goto err_free_label;
 nfs_sb_active(dentry->d_sb);
 p->dentry = dget(dentry);
 p->dir = parent;
 p->owner = sp;
 atomic_inc(&sp->so_count);
 p->o_arg.open_flags = flags;
 p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
 p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
 p->o_arg.share_access = nfs4_map_atomic_open_share(server,
   fmode, flags);
 if (flags & O_CREAT) {
  p->o_arg.umask = current_umask();
  p->o_arg.label = nfs4_label_copy(p->a_label, label);
  if (c->sattr != NULL && c->sattr->ia_valid != 0) {
   p->o_arg.u.attrs = &p->attrs;
   memcpy(&p->attrs, c->sattr, sizeof(p->attrs));

   memcpy(p->o_arg.u.verifier.data, c->verf,
     sizeof(p->o_arg.u.verifier.data));
  }
 }
 /* ask server to check for all possible rights as results
 * are cached */

 switch (p->o_arg.claim) {
 default:
  break;
 case NFS4_OPEN_CLAIM_NULL:
 case NFS4_OPEN_CLAIM_FH:
  p->o_arg.access = NFS4_ACCESS_READ | NFS4_ACCESS_MODIFY |
      NFS4_ACCESS_EXTEND | NFS4_ACCESS_DELETE |
      NFS4_ACCESS_EXECUTE |
      nfs_access_xattr_mask(server);
 }
 p->o_arg.clientid = server->nfs_client->cl_clientid;
 p->o_arg.id.create_time = ktime_to_ns(sp->so_seqid.create_time);
 p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
 p->o_arg.name = &dentry->d_name;
 p->o_arg.server = server;
 p->o_arg.bitmask = nfs4_bitmask(server, label);
 p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
 switch (p->o_arg.claim) {
 case NFS4_OPEN_CLAIM_NULL:
 case NFS4_OPEN_CLAIM_DELEGATE_CUR:
 case NFS4_OPEN_CLAIM_DELEGATE_PREV:
  p->o_arg.fh = NFS_FH(dir);
  break;
 case NFS4_OPEN_CLAIM_PREVIOUS:
 case NFS4_OPEN_CLAIM_FH:
 case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
 case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
  p->o_arg.fh = NFS_FH(d_inode(dentry));
 }
 p->c_arg.fh = &p->o_res.fh;
 p->c_arg.stateid = &p->o_res.stateid;
 p->c_arg.seqid = p->o_arg.seqid;
 nfs4_init_opendata_res(p);
 kref_init(&p->kref);
 return p;

err_free_label:
 nfs4_label_free(p->a_label);
err_free_f:
 nfs4_label_free(p->f_attr.label);
err_free_p:
 kfree(p);
err:
 dput(parent);
 return NULL;
}

static void nfs4_opendata_free(struct kref *kref)
{
 struct nfs4_opendata *p = container_of(kref,
   struct nfs4_opendata, kref);
 struct super_block *sb = p->dentry->d_sb;

 nfs4_lgopen_release(p->lgp);
 nfs_free_seqid(p->o_arg.seqid);
 nfs4_sequence_free_slot(&p->o_res.seq_res);
 if (p->state != NULL)
  nfs4_put_open_state(p->state);
 nfs4_put_state_owner(p->owner);

 nfs4_label_free(p->a_label);
 nfs4_label_free(p->f_attr.label);

 dput(p->dir);
 dput(p->dentry);
 nfs_sb_deactive(sb);
 nfs_fattr_free_names(&p->f_attr);
 kfree(p->f_attr.mdsthreshold);
 kfree(p);
}

static void nfs4_opendata_put(struct nfs4_opendata *p)
{
 if (p != NULL)
  kref_put(&p->kref, nfs4_opendata_free);
}

static bool nfs4_mode_match_open_stateid(struct nfs4_state *state,
  fmode_t fmode)
{
 switch(fmode & (FMODE_READ|FMODE_WRITE)) {
 case FMODE_READ|FMODE_WRITE:
  return state->n_rdwr != 0;
 case FMODE_WRITE:
  return state->n_wronly != 0;
 case FMODE_READ:
  return state->n_rdonly != 0;
 }
 WARN_ON_ONCE(1);
 return false;
}

static int can_open_cached(struct nfs4_state *state, fmode_t mode,
  int open_mode, enum open_claim_type4 claim)
{
 int ret = 0;

 if (open_mode & (O_EXCL|O_TRUNC))
  goto out;
 switch (claim) {
 case NFS4_OPEN_CLAIM_NULL:
 case NFS4_OPEN_CLAIM_FH:
  goto out;
 default:
  break;
 }
 switch (mode & (FMODE_READ|FMODE_WRITE)) {
  case FMODE_READ:
   ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0
    && state->n_rdonly != 0;
   break;
  case FMODE_WRITE:
   ret |= test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0
    && state->n_wronly != 0;
   break;
  case FMODE_READ|FMODE_WRITE:
   ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0
    && state->n_rdwr != 0;
 }
out:
 return ret;
}

static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode,
  enum open_claim_type4 claim)
{
 if (delegation == NULL)
  return 0;
 if ((delegation->type & fmode) != fmode)
  return 0;
 switch (claim) {
 case NFS4_OPEN_CLAIM_NULL:
 case NFS4_OPEN_CLAIM_FH:
  break;
 case NFS4_OPEN_CLAIM_PREVIOUS:
  if (!test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
   break;
  fallthrough;
 default:
  return 0;
 }
 nfs_mark_delegation_referenced(delegation);
 return 1;
}

static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
{
 switch (fmode) {
  case FMODE_WRITE:
   state->n_wronly++;
   break;
  case FMODE_READ:
   state->n_rdonly++;
   break;
  case FMODE_READ|FMODE_WRITE:
   state->n_rdwr++;
 }
 nfs4_state_set_mode_locked(state, state->state | fmode);
}

#ifdef CONFIG_NFS_V4_1
static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state)
{
 if (state->n_rdonly && !test_bit(NFS_O_RDONLY_STATE, &state->flags))
  return true;
 if (state->n_wronly && !test_bit(NFS_O_WRONLY_STATE, &state->flags))
  return true;
 if (state->n_rdwr && !test_bit(NFS_O_RDWR_STATE, &state->flags))
  return true;
 return false;
}
#endif /* CONFIG_NFS_V4_1 */

static void nfs_state_log_update_open_stateid(struct nfs4_state *state)
{
 if (test_and_clear_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
  wake_up_all(&state->waitq);
}

static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
{
 struct nfs_client *clp = state->owner->so_server->nfs_client;
 bool need_recover = false;

 if (test_and_clear_bit(NFS_O_RDONLY_STATE, &state->flags) && state->n_rdonly)
  need_recover = true;
 if (test_and_clear_bit(NFS_O_WRONLY_STATE, &state->flags) && state->n_wronly)
  need_recover = true;
 if (test_and_clear_bit(NFS_O_RDWR_STATE, &state->flags) && state->n_rdwr)
  need_recover = true;
 if (need_recover)
  nfs4_state_mark_reclaim_nograce(clp, state);
}

/*
 * Check for whether or not the caller may update the open stateid
 * to the value passed in by stateid.
 *
 * Note: This function relies heavily on the server implementing
 * RFC7530 Section 9.1.4.2, and RFC5661 Section 8.2.2
 * correctly.
 * i.e. The stateid seqids have to be initialised to 1, and
 * are then incremented on every state transition.
 */

static bool nfs_stateid_is_sequential(struct nfs4_state *state,
  const nfs4_stateid *stateid)
{
 if (test_bit(NFS_OPEN_STATE, &state->flags)) {
  /* The common case - we're updating to a new sequence number */
  if (nfs4_stateid_match_other(stateid, &state->open_stateid)) {
   if (nfs4_stateid_is_next(&state->open_stateid, stateid))
    return true;
   return false;
  }
  /* The server returned a new stateid */
 }
 /* This is the first OPEN in this generation */
 if (stateid->seqid == cpu_to_be32(1))
  return true;
 return false;
}

static void nfs_resync_open_stateid_locked(struct nfs4_state *state)
{
 if (!(state->n_wronly || state->n_rdonly || state->n_rdwr))
  return;
 if (state->n_wronly)
  set_bit(NFS_O_WRONLY_STATE, &state->flags);
 if (state->n_rdonly)
  set_bit(NFS_O_RDONLY_STATE, &state->flags);
 if (state->n_rdwr)
  set_bit(NFS_O_RDWR_STATE, &state->flags);
 set_bit(NFS_OPEN_STATE, &state->flags);
}

static void nfs_clear_open_stateid_locked(struct nfs4_state *state,
  nfs4_stateid *stateid, fmode_t fmode)
{
 clear_bit(NFS_O_RDWR_STATE, &state->flags);
 switch (fmode & (FMODE_READ|FMODE_WRITE)) {
 case FMODE_WRITE:
  clear_bit(NFS_O_RDONLY_STATE, &state->flags);
  break;
 case FMODE_READ:
  clear_bit(NFS_O_WRONLY_STATE, &state->flags);
  break;
 case 0:
  clear_bit(NFS_O_RDONLY_STATE, &state->flags);
  clear_bit(NFS_O_WRONLY_STATE, &state->flags);
  clear_bit(NFS_OPEN_STATE, &state->flags);
 }
 if (stateid == NULL)
  return;
 /* Handle OPEN+OPEN_DOWNGRADE races */
 if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
     !nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
  nfs_resync_open_stateid_locked(state);
  goto out;
 }
 if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
  nfs4_stateid_copy(&state->stateid, stateid);
 nfs4_stateid_copy(&state->open_stateid, stateid);
 trace_nfs4_open_stateid_update(state->inode, stateid, 0);
out:
 nfs_state_log_update_open_stateid(state);
}

static void nfs_clear_open_stateid(struct nfs4_state *state,
 nfs4_stateid *arg_stateid,
 nfs4_stateid *stateid, fmode_t fmode)
{
 write_seqlock(&state->seqlock);
 /* Ignore, if the CLOSE argment doesn't match the current stateid */
 if (nfs4_state_match_open_stateid_other(state, arg_stateid))
  nfs_clear_open_stateid_locked(state, stateid, fmode);
 write_sequnlock(&state->seqlock);
 if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
  nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
}

static void nfs_set_open_stateid_locked(struct nfs4_state *state,
  const nfs4_stateid *stateid, nfs4_stateid *freeme)
 __must_hold(&state->owner->so_lock)
 __must_hold(&state->seqlock)
 __must_hold(RCU)

{
 DEFINE_WAIT(wait);
 int status = 0;
 for (;;) {

  if (nfs_stateid_is_sequential(state, stateid))
   break;

  if (status)
   break;
  /* Rely on seqids for serialisation with NFSv4.0 */
  if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
   break;

  set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
  prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
  /*
 * Ensure we process the state changes in the same order
 * in which the server processed them by delaying the
 * update of the stateid until we are in sequence.
 */

  write_sequnlock(&state->seqlock);
  spin_unlock(&state->owner->so_lock);
  rcu_read_unlock();
  trace_nfs4_open_stateid_update_wait(state->inode, stateid, 0);

  if (!fatal_signal_pending(current) &&
      !nfs_current_task_exiting()) {
   if (schedule_timeout(5*HZ) == 0)
    status = -EAGAIN;
   else
    status = 0;
  } else
   status = -EINTR;
  finish_wait(&state->waitq, &wait);
  rcu_read_lock();
  spin_lock(&state->owner->so_lock);
  write_seqlock(&state->seqlock);
 }

 if (test_bit(NFS_OPEN_STATE, &state->flags) &&
     !nfs4_stateid_match_other(stateid, &state->open_stateid)) {
  nfs4_stateid_copy(freeme, &state->open_stateid);
  nfs_test_and_clear_all_open_stateid(state);
 }

 if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
  nfs4_stateid_copy(&state->stateid, stateid);
 nfs4_stateid_copy(&state->open_stateid, stateid);
 trace_nfs4_open_stateid_update(state->inode, stateid, status);
 nfs_state_log_update_open_stateid(state);
}

static void nfs_state_set_open_stateid(struct nfs4_state *state,
  const nfs4_stateid *open_stateid,
  fmode_t fmode,
  nfs4_stateid *freeme)
{
 /*
 * Protect the call to nfs4_state_set_mode_locked and
 * serialise the stateid update
 */

 write_seqlock(&state->seqlock);
 nfs_set_open_stateid_locked(state, open_stateid, freeme);
 switch (fmode) {
 case FMODE_READ:
  set_bit(NFS_O_RDONLY_STATE, &state->flags);
  break;
 case FMODE_WRITE:
  set_bit(NFS_O_WRONLY_STATE, &state->flags);
  break;
 case FMODE_READ|FMODE_WRITE:
  set_bit(NFS_O_RDWR_STATE, &state->flags);
 }
 set_bit(NFS_OPEN_STATE, &state->flags);
 write_sequnlock(&state->seqlock);
}

static void nfs_state_clear_open_state_flags(struct nfs4_state *state)
{
 clear_bit(NFS_O_RDWR_STATE, &state->flags);
 clear_bit(NFS_O_WRONLY_STATE, &state->flags);
 clear_bit(NFS_O_RDONLY_STATE, &state->flags);
 clear_bit(NFS_OPEN_STATE, &state->flags);
}

static void nfs_state_set_delegation(struct nfs4_state *state,
  const nfs4_stateid *deleg_stateid,
  fmode_t fmode)
{
 /*
 * Protect the call to nfs4_state_set_mode_locked and
 * serialise the stateid update
 */

 write_seqlock(&state->seqlock);
 nfs4_stateid_copy(&state->stateid, deleg_stateid);
 set_bit(NFS_DELEGATED_STATE, &state->flags);
 write_sequnlock(&state->seqlock);
}

static void nfs_state_clear_delegation(struct nfs4_state *state)
{
 write_seqlock(&state->seqlock);
 nfs4_stateid_copy(&state->stateid, &state->open_stateid);
 clear_bit(NFS_DELEGATED_STATE, &state->flags);
 write_sequnlock(&state->seqlock);
}

int update_open_stateid(struct nfs4_state *state,
  const nfs4_stateid *open_stateid,
  const nfs4_stateid *delegation,
  fmode_t fmode)
{
 struct nfs_server *server = NFS_SERVER(state->inode);
 struct nfs_client *clp = server->nfs_client;
 struct nfs_inode *nfsi = NFS_I(state->inode);
 struct nfs_delegation *deleg_cur;
 nfs4_stateid freeme = { };
 int ret = 0;

 fmode &= (FMODE_READ|FMODE_WRITE);

 rcu_read_lock();
 spin_lock(&state->owner->so_lock);
 if (open_stateid != NULL) {
  nfs_state_set_open_stateid(state, open_stateid, fmode, &freeme);
  ret = 1;
 }

 deleg_cur = nfs4_get_valid_delegation(state->inode);
 if (deleg_cur == NULL)
  goto no_delegation;

 spin_lock(&deleg_cur->lock);
 if (rcu_dereference(nfsi->delegation) != deleg_cur ||
    test_bit(NFS_DELEGATION_RETURNING, &deleg_cur->flags) ||
     (deleg_cur->type & fmode) != fmode)
  goto no_delegation_unlock;

 if (delegation == NULL)
  delegation = &deleg_cur->stateid;
 else if (!nfs4_stateid_match_other(&deleg_cur->stateid, delegation))
  goto no_delegation_unlock;

 nfs_mark_delegation_referenced(deleg_cur);
 nfs_state_set_delegation(state, &deleg_cur->stateid, fmode);
 ret = 1;
no_delegation_unlock:
 spin_unlock(&deleg_cur->lock);
no_delegation:
 if (ret)
  update_open_stateflags(state, fmode);
 spin_unlock(&state->owner->so_lock);
 rcu_read_unlock();

 if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
  nfs4_schedule_state_manager(clp);
 if (freeme.type != 0)
  nfs4_test_and_free_stateid(server, &freeme,
    state->owner->so_cred);

 return ret;
}

static bool nfs4_update_lock_stateid(struct nfs4_lock_state *lsp,
  const nfs4_stateid *stateid)
{
 struct nfs4_state *state = lsp->ls_state;
 bool ret = false;

 spin_lock(&state->state_lock);
 if (!nfs4_stateid_match_other(stateid, &lsp->ls_stateid))
  goto out_noupdate;
 if (!nfs4_stateid_is_newer(stateid, &lsp->ls_stateid))
  goto out_noupdate;
 nfs4_stateid_copy(&lsp->ls_stateid, stateid);
 ret = true;
out_noupdate:
 spin_unlock(&state->state_lock);
 return ret;
}

static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
{
 struct nfs_delegation *delegation;

 fmode &= FMODE_READ|FMODE_WRITE;
 rcu_read_lock();
 delegation = nfs4_get_valid_delegation(inode);
 if (delegation == NULL || (delegation->type & fmode) == fmode) {
  rcu_read_unlock();
  return;
 }
 rcu_read_unlock();
 nfs4_inode_return_delegation(inode);
}

static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
{
 struct nfs4_state *state = opendata->state;
 struct nfs_delegation *delegation;
 int open_mode = opendata->o_arg.open_flags;
 fmode_t fmode = opendata->o_arg.fmode;
 enum open_claim_type4 claim = opendata->o_arg.claim;
 nfs4_stateid stateid;
 int ret = -EAGAIN;

 for (;;) {
  spin_lock(&state->owner->so_lock);
  if (can_open_cached(state, fmode, open_mode, claim)) {
   update_open_stateflags(state, fmode);
   spin_unlock(&state->owner->so_lock);
   goto out_return_state;
  }
  spin_unlock(&state->owner->so_lock);
  rcu_read_lock();
  delegation = nfs4_get_valid_delegation(state->inode);
  if (!can_open_delegated(delegation, fmode, claim)) {
   rcu_read_unlock();
   break;
  }
  /* Save the delegation */
  nfs4_stateid_copy(&stateid, &delegation->stateid);
  rcu_read_unlock();
  nfs_release_seqid(opendata->o_arg.seqid);
  if (!opendata->is_recover) {
   ret = nfs_may_open(state->inode, state->owner->so_cred, open_mode);
   if (ret != 0)
    goto out;
  }
  ret = -EAGAIN;

  /* Try to update the stateid using the delegation */
  if (update_open_stateid(state, NULL, &stateid, fmode))
   goto out_return_state;
 }
out:
 return ERR_PTR(ret);
out_return_state:
 refcount_inc(&state->count);
 return state;
}

static void
nfs4_process_delegation(struct inode *inode, const struct cred *cred,
   enum open_claim_type4 claim,
   const struct nfs4_open_delegation *delegation)
{
 switch (delegation->open_delegation_type) {
 case NFS4_OPEN_DELEGATE_READ:
 case NFS4_OPEN_DELEGATE_WRITE:
 case NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG:
 case NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG:
  break;
 default:
  return;
 }
 switch (claim) {
 case NFS4_OPEN_CLAIM_DELEGATE_CUR:
 case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
  pr_err_ratelimited("NFS: Broken NFSv4 server %s is "
       "returning a delegation for "
       "OPEN(CLAIM_DELEGATE_CUR)\n",
       NFS_SERVER(inode)->nfs_client->cl_hostname);
  break;
 case NFS4_OPEN_CLAIM_PREVIOUS:
  nfs_inode_reclaim_delegation(inode, cred, delegation->type,
          &delegation->stateid,
          delegation->pagemod_limit,
          delegation->open_delegation_type);
  break;
 default:
  nfs_inode_set_delegation(inode, cred, delegation->type,
      &delegation->stateid,
      delegation->pagemod_limit,
      delegation->open_delegation_type);
 }
 if (delegation->do_recall)
  nfs_async_inode_return_delegation(inode, &delegation->stateid);
}

/*
 * Check the inode attributes against the CLAIM_PREVIOUS returned attributes
 * and update the nfs4_state.
 */

static struct nfs4_state *
_nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
{
 struct inode *inode = data->state->inode;
 struct nfs4_state *state = data->state;
 int ret;

 if (!data->rpc_done) {
  if (data->rpc_status)
   return ERR_PTR(data->rpc_status);
  return nfs4_try_open_cached(data);
 }

 ret = nfs_refresh_inode(inode, &data->f_attr);
 if (ret)
  return ERR_PTR(ret);

 nfs4_process_delegation(state->inode,
    data->owner->so_cred,
    data->o_arg.claim,
    &data->o_res.delegation);

 if (!(data->o_res.rflags & NFS4_OPEN_RESULT_NO_OPEN_STATEID)) {
  if (!update_open_stateid(state, &data->o_res.stateid,
      NULL, data->o_arg.fmode))
   return ERR_PTR(-EAGAIN);
 } else if (!update_open_stateid(state, NULL, NULL, data->o_arg.fmode))
  return ERR_PTR(-EAGAIN);
 refcount_inc(&state->count);

 return state;
}

static struct inode *
nfs4_opendata_get_inode(struct nfs4_opendata *data)
{
 struct inode *inode;

 switch (data->o_arg.claim) {
 case NFS4_OPEN_CLAIM_NULL:
 case NFS4_OPEN_CLAIM_DELEGATE_CUR:
 case NFS4_OPEN_CLAIM_DELEGATE_PREV:
  if (!(data->f_attr.valid & NFS_ATTR_FATTR))
   return ERR_PTR(-EAGAIN);
  inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh,
    &data->f_attr);
  break;
 default:
  inode = d_inode(data->dentry);
  ihold(inode);
  nfs_refresh_inode(inode, &data->f_attr);
 }
 return inode;
}

static struct nfs4_state *
nfs4_opendata_find_nfs4_state(struct nfs4_opendata *data)
{
 struct nfs4_state *state;
 struct inode *inode;

 inode = nfs4_opendata_get_inode(data);
 if (IS_ERR(inode))
  return ERR_CAST(inode);
 if (data->state != NULL && data->state->inode == inode) {
  state = data->state;
  refcount_inc(&state->count);
 } else
  state = nfs4_get_open_state(inode, data->owner);
 iput(inode);
 if (state == NULL)
  state = ERR_PTR(-ENOMEM);
 return state;
}

static struct nfs4_state *
_nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
{
 struct nfs4_state *state;

 if (!data->rpc_done) {
  state = nfs4_try_open_cached(data);
  trace_nfs4_cached_open(data->state);
  goto out;
 }

 state = nfs4_opendata_find_nfs4_state(data);
 if (IS_ERR(state))
  goto out;

 nfs4_process_delegation(state->inode,
    data->owner->so_cred,
    data->o_arg.claim,
    &data->o_res.delegation);

 if (!(data->o_res.rflags & NFS4_OPEN_RESULT_NO_OPEN_STATEID)) {
  if (!update_open_stateid(state, &data->o_res.stateid,
      NULL, data->o_arg.fmode)) {
   nfs4_put_open_state(state);
   state = ERR_PTR(-EAGAIN);
  }
 } else if (!update_open_stateid(state, NULL, NULL, data->o_arg.fmode)) {
  nfs4_put_open_state(state);
  state = ERR_PTR(-EAGAIN);
 }
out:
 nfs_release_seqid(data->o_arg.seqid);
 return state;
}

static struct nfs4_state *
nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
{
 struct nfs4_state *ret;

 if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS)
  ret =_nfs4_opendata_reclaim_to_nfs4_state(data);
 else
  ret = _nfs4_opendata_to_nfs4_state(data);
 nfs4_sequence_free_slot(&data->o_res.seq_res);
 return ret;
}

static struct nfs_open_context *
nfs4_state_find_open_context_mode(struct nfs4_state *state, fmode_t mode)
{
 struct nfs_inode *nfsi = NFS_I(state->inode);
 struct nfs_open_context *ctx;

 rcu_read_lock();
 list_for_each_entry_rcu(ctx, &nfsi->open_files, list) {
  if (ctx->state != state)
   continue;
  if ((ctx->mode & mode) != mode)
   continue;
  if (!get_nfs_open_context(ctx))
   continue;
  rcu_read_unlock();
  return ctx;
 }
 rcu_read_unlock();
 return ERR_PTR(-ENOENT);
}

static struct nfs_open_context *
nfs4_state_find_open_context(struct nfs4_state *state)
{
 struct nfs_open_context *ctx;

 ctx = nfs4_state_find_open_context_mode(state, FMODE_READ|FMODE_WRITE);
 if (!IS_ERR(ctx))
  return ctx;
 ctx = nfs4_state_find_open_context_mode(state, FMODE_WRITE);
 if (!IS_ERR(ctx))
  return ctx;
 return nfs4_state_find_open_context_mode(state, FMODE_READ);
}

static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context *ctx,
  struct nfs4_state *state, enum open_claim_type4 claim)
{
 struct nfs4_opendata *opendata;

 opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
   NULL, claim, GFP_NOFS);
 if (opendata == NULL)
  return ERR_PTR(-ENOMEM);
 opendata->state = state;
 refcount_inc(&state->count);
 return opendata;
}

static int nfs4_open_recover_helper(struct nfs4_opendata *opendata,
        fmode_t fmode)
{
 struct nfs4_state *newstate;
 struct nfs_server *server = NFS_SB(opendata->dentry->d_sb);
 int openflags = opendata->o_arg.open_flags;
 int ret;

 if (!nfs4_mode_match_open_stateid(opendata->state, fmode))
  return 0;
 opendata->o_arg.fmode = fmode;
 opendata->o_arg.share_access =
  nfs4_map_atomic_open_share(server, fmode, openflags);
 memset(&opendata->o_res, 0, sizeof(opendata->o_res));
 memset(&opendata->c_res, 0, sizeof(opendata->c_res));
 nfs4_init_opendata_res(opendata);
 ret = _nfs4_recover_proc_open(opendata);
 if (ret != 0)
  return ret; 
 newstate = nfs4_opendata_to_nfs4_state(opendata);
 if (IS_ERR(newstate))
  return PTR_ERR(newstate);
 if (newstate != opendata->state)
  ret = -ESTALE;
 nfs4_close_state(newstate, fmode);
 return ret;
}

static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state)
{
 int ret;

 /* memory barrier prior to reading state->n_* */
 smp_rmb();
 ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE);
 if (ret != 0)
  return ret;
 ret = nfs4_open_recover_helper(opendata, FMODE_WRITE);
 if (ret != 0)
  return ret;
 ret = nfs4_open_recover_helper(opendata, FMODE_READ);
 if (ret != 0)
  return ret;
 /*
 * We may have performed cached opens for all three recoveries.
 * Check if we need to update the current stateid.
 */

 if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0 &&
     !nfs4_stateid_match(&state->stateid, &state->open_stateid)) {
  write_seqlock(&state->seqlock);
  if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
   nfs4_stateid_copy(&state->stateid, &state->open_stateid);
  write_sequnlock(&state->seqlock);
 }
 return 0;
}

/*
 * OPEN_RECLAIM:
 *  reclaim state on the server after a reboot.
 */

static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
{
 struct nfs_delegation *delegation;
 struct nfs4_opendata *opendata;
 u32 delegation_type = NFS4_OPEN_DELEGATE_NONE;
 int status;

 opendata = nfs4_open_recoverdata_alloc(ctx, state,
   NFS4_OPEN_CLAIM_PREVIOUS);
 if (IS_ERR(opendata))
  return PTR_ERR(opendata);
 rcu_read_lock();
 delegation = rcu_dereference(NFS_I(state->inode)->delegation);
 if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0) {
  switch(delegation->type) {
  case FMODE_READ:
   delegation_type = NFS4_OPEN_DELEGATE_READ;
   if (test_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags))
    delegation_type = NFS4_OPEN_DELEGATE_READ_ATTRS_DELEG;
   break;
  case FMODE_WRITE:
  case FMODE_READ|FMODE_WRITE:
   delegation_type = NFS4_OPEN_DELEGATE_WRITE;
   if (test_bit(NFS_DELEGATION_DELEGTIME, &delegation->flags))
    delegation_type = NFS4_OPEN_DELEGATE_WRITE_ATTRS_DELEG;
  }
 }
 rcu_read_unlock();
 opendata->o_arg.u.delegation_type = delegation_type;
 status = nfs4_open_recover(opendata, state);
 nfs4_opendata_put(opendata);
 return status;
}

static int nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
{
 struct nfs_server *server = NFS_SERVER(state->inode);
 struct nfs4_exception exception = { };
 int err;
 do {
  err = _nfs4_do_open_reclaim(ctx, state);
  trace_nfs4_open_reclaim(ctx, 0, err);
  if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
   continue;
  if (err != -NFS4ERR_DELAY)
   break;
  nfs4_handle_exception(server, err, &exception);
 } while (exception.retry);
 return err;
}

static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
 struct nfs_open_context *ctx;
 int ret;

 ctx = nfs4_state_find_open_context(state);
 if (IS_ERR(ctx))
  return -EAGAIN;
 clear_bit(NFS_DELEGATED_STATE, &state->flags);
 nfs_state_clear_open_state_flags(state);
 ret = nfs4_do_open_reclaim(ctx, state);
 put_nfs_open_context(ctx);
 return ret;
}

static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, struct file_lock *fl, int err)
{
 switch (err) {
  default:
   printk(KERN_ERR "NFS: %s: unhandled error "
     "%d.\n", __func__, err);
   fallthrough;
  case 0:
  case -ENOENT:
  case -EAGAIN:
  case -ESTALE:
  case -ETIMEDOUT:
   break;
  case -NFS4ERR_BADSESSION:
  case -NFS4ERR_BADSLOT:
  case -NFS4ERR_BAD_HIGH_SLOT:
  case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
  case -NFS4ERR_DEADSESSION:
   return -EAGAIN;
  case -NFS4ERR_STALE_CLIENTID:
  case -NFS4ERR_STALE_STATEID:
   /* Don't recall a delegation if it was lost */
   nfs4_schedule_lease_recovery(server->nfs_client);
   return -EAGAIN;
  case -NFS4ERR_MOVED:
   nfs4_schedule_migration_recovery(server);
   return -EAGAIN;
  case -NFS4ERR_LEASE_MOVED:
   nfs4_schedule_lease_moved_recovery(server->nfs_client);
   return -EAGAIN;
  case -NFS4ERR_DELEG_REVOKED:
  case -NFS4ERR_ADMIN_REVOKED:
  case -NFS4ERR_EXPIRED:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=88 G=91

¤ Dauer der Verarbeitung: 0.40 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge