static void encode_commit(struct xdr_stream *xdr, const struct nfs_commitargs *args, struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_COMMIT, decode_commit_maxsz, hdr);
	p = reserve_space(xdr, 12);
	p = xdr_encode_hyper(p, args->offset);
	*p = cpu_to_be32(args->count);
}


static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
	__be32 *p;

	*res = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*res = be32_to_cpup(p);
		bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT;
	}
	dprintk("%s: link support=%s\n", __func__, *res == 0 ? "false" : "true");
	return 0;
}

nfs: Fix getxattr kernel panic and memory overflow

Move the buffer size check to decode_attr_security_label() before memcpy()
Only call memcpy() if the buffer is large enough

Fixes: aa9c2669626c ("NFS: Client implementation of Labeled-NFS")
Signed-off-by: Jeffrey Mitchell <jeffrey.mitchell@starlab.io>
[Trond: clean up duplicate test of label->len != 0]
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>


static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct inode *inode = state->inode;
	struct nfs4_state_owner *sp = state->owner;
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs_seqid *seqid;
	struct nfs4_lock_state *lsp;
	struct rpc_task *task;
	struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
	int status = 0;
	unsigned char fl_flags = request->fl_flags;

	status = nfs4_set_lock_state(state, request);
	/* Unlock _before_ we do the RPC call */
	request->fl_flags |= FL_EXISTS;
	/* Exclude nfs_delegation_claim_locks() */
	mutex_lock(&sp->so_delegreturn_mutex);
	/* Exclude nfs4_reclaim_open_stateid() - note nesting! */
	down_read(&nfsi->rwsem);
	if (locks_lock_inode_wait(inode, request) == -ENOENT) {
		up_read(&nfsi->rwsem);
		mutex_unlock(&sp->so_delegreturn_mutex);
		goto out;
	}
	up_read(&nfsi->rwsem);
	mutex_unlock(&sp->so_delegreturn_mutex);
	if (status != 0)
		goto out;
	/* Is this a delegated lock? */
	lsp = request->fl_u.nfs4_fl.owner;
	if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) == 0)
		goto out;
	alloc_seqid = NFS_SERVER(inode)->nfs_client->cl_mvops->alloc_seqid;
	seqid = alloc_seqid(&lsp->ls_seqid, GFP_KERNEL);
	status = -ENOMEM;
	if (IS_ERR(seqid))
		goto out;
	task = nfs4_do_unlck(request, nfs_file_open_context(request->fl_file), lsp, seqid);
	status = PTR_ERR(task);
	if (IS_ERR(task))
		goto out;
	status = rpc_wait_for_completion_task(task);
	rpc_put_task(task);
out:
	request->fl_flags = fl_flags;
	trace_nfs4_unlock(request, state, F_SETLK, status);
	return status;
}


static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
{
	__be32 *p;
	int ret = 0;

	*fileid = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEID - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_FILEID)) {
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, fileid);
		bitmap[0] &= ~FATTR4_WORD0_FILEID;
		ret = NFS_ATTR_FATTR_FILEID;
	}
	dprintk("%s: fileid=%Lu\n", __func__, (unsigned long long)*fileid);
	return ret;
}


static int decode_secinfo(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
{
	int status = decode_op_hdr(xdr, OP_SECINFO);
	if (status)
		return status;
	return decode_secinfo_common(xdr, res);
}


static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
{
	if (ctx->state == NULL)
		return;
	if (is_sync)
		nfs4_close_sync(ctx->state, _nfs4_ctx_to_openmode(ctx));
	else
		nfs4_close_state(ctx->state, _nfs4_ctx_to_openmode(ctx));
}


static int decode_sessionid(struct xdr_stream *xdr, struct nfs4_sessionid *sid)
{
	return decode_opaque_fixed(xdr, sid->data, NFS4_MAX_SESSIONID_LEN);
}


static int nfs4_xdr_dec_readdir(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
				void *data)
{
	struct nfs4_readdir_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_readdir(xdr, rqstp, res);
out:
	return status;
}


static int decode_attr_mode(struct xdr_stream *xdr, uint32_t *bitmap, umode_t *mode)
{
	uint32_t tmp;
	__be32 *p;
	int ret = 0;

	*mode = 0;
	if (unlikely(bitmap[1] & (FATTR4_WORD1_MODE - 1U)))
		return -EIO;
	if (likely(bitmap[1] & FATTR4_WORD1_MODE)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		tmp = be32_to_cpup(p);
		*mode = tmp & ~S_IFMT;
		bitmap[1] &= ~FATTR4_WORD1_MODE;
		ret = NFS_ATTR_FATTR_MODE;
	}
	dprintk("%s: file mode=0%o\n", __func__, (unsigned int)*mode);
	return ret;
}


static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
{
	__be32 *p;
	uint32_t delegation_type;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	delegation_type = be32_to_cpup(p);
	res->delegation_type = 0;
	switch (delegation_type) {
	case NFS4_OPEN_DELEGATE_NONE:
		return 0;
	case NFS4_OPEN_DELEGATE_READ:
	case NFS4_OPEN_DELEGATE_WRITE:
		return decode_rw_delegation(xdr, delegation_type, res);
	case NFS4_OPEN_DELEGATE_NONE_EXT:
		return decode_no_delegation(xdr, res);
	}
	return -EIO;
}


nfs4_dec_nlink_locked(struct inode *inode)
{
	NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
	drop_nlink(inode);
}


static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
	__be32 *p;

	*res = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*res = be32_to_cpup(p);
		bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT;
	}
	dprintk("%s: symlink support=%s\n", __func__, *res == 0 ? "false" : "true");
	return 0;
}


static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
{
	fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
		NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_MOUNTPOINT;
	fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	fattr->nlink = 2;
}


static int nfs4_get_security_label(struct inode *inode, void *buf,
					size_t buflen)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;

	if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
		return -EOPNOTSUPP;

	do {
		err = _nfs4_get_security_label(inode, buf, buflen);
		trace_nfs4_get_security_label(inode, err);
		err = nfs4_handle_exception(NFS_SERVER(inode), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int _nfs4_proc_open(struct nfs4_opendata *data,
			   struct nfs_open_context *ctx)
{
	struct inode *dir = d_inode(data->dir);
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs_openargs *o_arg = &data->o_arg;
	struct nfs_openres *o_res = &data->o_res;
	int status;

	status = nfs4_run_open_task(data, ctx);
	if (!data->rpc_done)
		return status;
	if (status != 0) {
		if (status == -NFS4ERR_BADNAME &&
				!(o_arg->open_flags & O_CREAT))
			return -ENOENT;
		return status;
	}

	nfs_fattr_map_and_free_names(server, &data->f_attr);

	if (o_arg->open_flags & O_CREAT) {
		if (o_arg->open_flags & O_EXCL)
			data->file_created = true;
		else if (o_res->cinfo.before != o_res->cinfo.after)
			data->file_created = true;
		if (data->file_created ||
		    inode_peek_iversion_raw(dir) != o_res->cinfo.after)
			nfs4_update_changeattr(dir, &o_res->cinfo,
					o_res->f_attr->time_start,
					NFS_INO_INVALID_DATA);
	}
	if ((o_res->rflags & NFS4_OPEN_RESULT_LOCKTYPE_POSIX) == 0)
		server->caps &= ~NFS_CAP_POSIX_LOCK;
	if(o_res->rflags & NFS4_OPEN_RESULT_CONFIRM) {
		status = _nfs4_proc_open_confirm(data);
		if (status != 0)
			return status;
	}
	if (!(o_res->f_attr->valid & NFS_ATTR_FATTR)) {
		nfs4_sequence_free_slot(&o_res->seq_res);
		nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr,
				o_res->f_label, NULL);
	}
	return 0;
}


static void encode_compound_hdr(struct xdr_stream *xdr,
				struct rpc_rqst *req,
				struct compound_hdr *hdr)
{
	__be32 *p;

	/* initialize running count of expected bytes in reply.
	 * NOTE: the replied tag SHOULD be the same is the one sent,
	 * but this is not required as a MUST for the server to do so. */
	hdr->replen = 3 + hdr->taglen;

	WARN_ON_ONCE(hdr->taglen > NFS4_MAXTAGLEN);
	encode_string(xdr, hdr->taglen, hdr->tag);
	p = reserve_space(xdr, 8);
	*p++ = cpu_to_be32(hdr->minorversion);
	hdr->nops_p = p;
	*p = cpu_to_be32(hdr->nops);
}


static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr)
{
	if (!(((fattr->valid & NFS_ATTR_FATTR_MOUNTED_ON_FILEID) ||
	       (fattr->valid & NFS_ATTR_FATTR_FILEID)) &&
	      (fattr->valid & NFS_ATTR_FATTR_FSID) &&
	      (fattr->valid & NFS_ATTR_FATTR_V4_LOCATIONS)))
		return;

	fattr->valid |= NFS_ATTR_FATTR_TYPE | NFS_ATTR_FATTR_MODE |
		NFS_ATTR_FATTR_NLINK | NFS_ATTR_FATTR_V4_REFERRAL;
	fattr->mode = S_IFDIR | S_IRUGO | S_IXUGO;
	fattr->nlink = 2;
}


static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
			 struct nfs_getaclres *res)
{
	unsigned int savep;
	uint32_t attrlen,
		 bitmap[3] = {0};
	int status;
	unsigned int pg_offset;

	res->acl_len = 0;
	if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
		goto out;

	xdr_enter_page(xdr, xdr->buf->page_len);

	/* Calculate the offset of the page data */
	pg_offset = xdr->buf->head[0].iov_len;

	if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
		goto out;
	if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
		goto out;

	if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {

		/* The bitmap (xdr len + bitmaps) and the attr xdr len words
		 * are stored with the acl data to handle the problem of
		 * variable length bitmaps.*/
		res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
		res->acl_len = attrlen;

		/* Check for receive buffer overflow */
		if (res->acl_len > (xdr->nwords << 2) ||
		    res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
			res->acl_flags |= NFS4_ACL_TRUNC;
			dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
					attrlen, xdr->nwords << 2);
		}
	} else
		status = -EOPNOTSUPP;

out:
	return status;
}


static int nfs4_xdr_dec_lookupp(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
		void *data)
{
	struct nfs4_lookupp_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_lookupp(xdr);
	if (status)
		goto out;
	status = decode_getfh(xdr, res->fh);
	if (status)
		goto out;
	status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
out:
	return status;
}


static int decode_attr_group(struct xdr_stream *xdr, uint32_t *bitmap,
		const struct nfs_server *server, kgid_t *gid,
		struct nfs4_string *group_name)
{
	ssize_t len;
	char *p;

	*gid = make_kgid(&init_user_ns, -2);
	if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER_GROUP - 1U)))
		return -EIO;
	if (!(bitmap[1] & FATTR4_WORD1_OWNER_GROUP))
		return 0;
	bitmap[1] &= ~FATTR4_WORD1_OWNER_GROUP;

	if (group_name != NULL) {
		len = decode_nfs4_string(xdr, group_name, GFP_NOIO);
		if (len <= 0)
			goto out;
		dprintk("%s: name=%s\n", __func__, group_name->data);
		return NFS_ATTR_FATTR_GROUP_NAME;
	} else {
		len = xdr_stream_decode_opaque_inline(xdr, (void **)&p,
				XDR_MAX_NETOBJ);
		if (len <= 0 || nfs_map_group_to_gid(server, p, len, gid) != 0)
			goto out;
		dprintk("%s: gid=%d\n", __func__, (int)from_kgid(&init_user_ns, *gid));
		return NFS_ATTR_FATTR_GROUP;
	}
out:
	if (len == -EBADMSG)
		return -EIO;
	return 0;
}


int nfs4_do_close(struct nfs4_state *state, gfp_t gfp_mask, int wait)
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs_seqid *(*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
	struct nfs4_closedata *calldata;
	struct nfs4_state_owner *sp = state->owner;
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CLOSE],
		.rpc_cred = state->owner->so_cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = &nfs4_close_ops,
		.workqueue = nfsiod_workqueue,
		.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
	};
	int status = -ENOMEM;

	nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_CLEANUP,
		&task_setup_data.rpc_client, &msg);

	calldata = kzalloc(sizeof(*calldata), gfp_mask);
	if (calldata == NULL)
		goto out;
	nfs4_init_sequence(&calldata->arg.seq_args, &calldata->res.seq_res, 1, 0);
	calldata->inode = state->inode;
	calldata->state = state;
	calldata->arg.fh = NFS_FH(state->inode);
	if (!nfs4_copy_open_stateid(&calldata->arg.stateid, state))
		goto out_free_calldata;
	/* Serialization for the sequence id */
	alloc_seqid = server->nfs_client->cl_mvops->alloc_seqid;
	calldata->arg.seqid = alloc_seqid(&state->owner->so_seqid, gfp_mask);
	if (IS_ERR(calldata->arg.seqid))
		goto out_free_calldata;
	nfs_fattr_init(&calldata->fattr);
	calldata->arg.fmode = 0;
	calldata->lr.arg.ld_private = &calldata->lr.ld_private;
	calldata->res.fattr = &calldata->fattr;
	calldata->res.seqid = calldata->arg.seqid;
	calldata->res.server = server;
	calldata->res.lr_ret = -NFS4ERR_NOMATCHING_LAYOUT;
	calldata->lr.roc = pnfs_roc(state->inode,
			&calldata->lr.arg, &calldata->lr.res, msg.rpc_cred);
	if (calldata->lr.roc) {
		calldata->arg.lr_args = &calldata->lr.arg;
		calldata->res.lr_res = &calldata->lr.res;
	}
	nfs_sb_active(calldata->inode->i_sb);

	msg.rpc_argp = &calldata->arg;
	msg.rpc_resp = &calldata->res;
	task_setup_data.callback_data = calldata;
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	status = 0;
	if (wait)
		status = rpc_wait_for_completion_task(task);
	rpc_put_task(task);
	return status;
out_free_calldata:
	kfree(calldata);
out:
	nfs4_put_open_state(state);
	nfs4_put_state_owner(sp);
	return status;
}


static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data)
{
	struct nfs4_reclaim_complete_data *calldata = data;
	struct nfs_client *clp = calldata->clp;
	struct nfs4_sequence_res *res = &calldata->res.seq_res;

	dprintk("--> %s\n", __func__);
	if (!nfs41_sequence_done(task, res))
		return;

	trace_nfs4_reclaim_complete(clp, task->tk_status);
	if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) {
		rpc_restart_call_prepare(task);
		return;
	}
	dprintk("<-- %s\n", __func__);
}


static int nfs41_test_stateid(struct nfs_server *server,
		nfs4_stateid *stateid,
		const struct cred *cred)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = _nfs41_test_stateid(server, stateid, cred);
		nfs4_handle_delay_or_session_error(server, err, &exception);
	} while (exception.retry);
	return err;
}


static int nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred,
		u64 cookie, struct page **pages, unsigned int count, bool plus)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = _nfs4_proc_readdir(dentry, cred, cookie,
				pages, count, plus);
		trace_nfs4_readdir(d_inode(dentry), err);
		err = nfs4_handle_exception(NFS_SERVER(d_inode(dentry)), err,
				&exception);
	} while (exception.retry);
	return err;
}


static bool nfs4_sync_lock_stateid(nfs4_stateid *dst,
		struct nfs4_lock_state *lsp)
{
	struct nfs4_state *state = lsp->ls_state;
	bool ret;

	spin_lock(&state->state_lock);
	ret = !nfs4_stateid_match_other(dst, &lsp->ls_stateid);
	nfs4_stateid_copy(dst, &lsp->ls_stateid);
	spin_unlock(&state->state_lock);
	return ret;
}


static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
{
	struct nfs_server *server = NFS_SERVER(inode);
	int ret;

	if (!nfs4_server_supports_acls(server))
		return -EOPNOTSUPP;
	ret = nfs_revalidate_inode(server, inode);
	if (ret < 0)
		return ret;
	if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
		nfs_zap_acl_cache(inode);
	ret = nfs4_read_cached_acl(inode, buf, buflen);
	if (ret != -ENOENT)
		/* -ENOENT is returned if there is no ACL or if there is an ACL
		 * but no cached acl data, just the acl length */
		return ret;
	return nfs4_get_acl_uncached(inode, buf, buflen);
}


static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_OPEN_DOWNGRADE, decode_open_downgrade_maxsz, hdr);
	encode_nfs4_stateid(xdr, &arg->stateid);
	encode_nfs4_seqid(xdr, arg->seqid);
	encode_share_access(xdr, arg->share_access);
}


static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_REMOVE, decode_remove_maxsz, hdr);
	encode_string(xdr, name->len, name->name);
}


static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_LOCKU, decode_locku_maxsz, hdr);
	encode_uint32(xdr, nfs4_lock_type(args->fl, 0));
	encode_nfs4_seqid(xdr, args->seqid);
	encode_nfs4_stateid(xdr, &args->stateid);
	p = reserve_space(xdr, 16);
	p = xdr_encode_hyper(p, args->fl->fl_start);
	xdr_encode_hyper(p, nfs4_lock_length(args->fl));
}


static void nfs4_release_lockowner_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs_release_lockowner_data *data = calldata;
	struct nfs_server *server = data->server;
	nfs4_setup_sequence(server->nfs_client, &data->args.seq_args,
			   &data->res.seq_res, task);
	data->args.lock_owner.clientid = server->nfs_client->cl_clientid;
	data->timestamp = jiffies;
}


static int decode_rename(struct xdr_stream *xdr, struct nfs4_change_info *old_cinfo,
	      struct nfs4_change_info *new_cinfo)
{
	int status;

	status = decode_op_hdr(xdr, OP_RENAME);
	if (status)
		goto out;
	if ((status = decode_change_info(xdr, old_cinfo)))
		goto out;
	status = decode_change_info(xdr, new_cinfo);
out:
	return status;
}


static int decode_secinfo_gss(struct xdr_stream *xdr,
			      struct nfs4_secinfo4 *flavor)
{
	u32 oid_len;
	__be32 *p;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	oid_len = be32_to_cpup(p);
	if (oid_len > GSS_OID_MAX_LEN)
		return -EINVAL;

	p = xdr_inline_decode(xdr, oid_len);
	if (unlikely(!p))
		return -EIO;
	memcpy(flavor->flavor_info.oid.data, p, oid_len);
	flavor->flavor_info.oid.len = oid_len;

	p = xdr_inline_decode(xdr, 8);
	if (unlikely(!p))
		return -EIO;
	flavor->flavor_info.qop = be32_to_cpup(p++);
	flavor->flavor_info.service = be32_to_cpup(p);

	return 0;
}


static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
{
	if (label)
		return server->attr_bitmask;

	return server->attr_bitmask_nl;
}


static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
				struct nfs_fsinfo *info, rpc_authflavor_t flavor)
{
	struct rpc_auth_create_args auth_args = {
		.pseudoflavor = flavor,
	};
	struct rpc_auth *auth;

	auth = rpcauth_create(&auth_args, server->client);
	if (IS_ERR(auth))
		return -EACCES;
	return nfs4_lookup_root(server, fhandle, info);
}


static void nfs4_xdr_enc_setclientid(struct rpc_rqst *req,
				     struct xdr_stream *xdr,
				     const void *data)
{
	const struct nfs4_setclientid *sc = data;
	struct compound_hdr hdr = {
		.nops	= 0,
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_setclientid(xdr, sc, &hdr);
	encode_nops(&hdr);
}


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) {
		ret = nfs4_delay(&exception->timeout,
				exception->interruptible);
		goto out_retry;
	}
	if (exception->recovering) {
		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 void nfs4_delegreturn_release(void *calldata)
{
	struct nfs4_delegreturndata *data = calldata;
	struct inode *inode = data->inode;

	if (inode) {
		if (data->lr.roc)
			pnfs_roc_release(&data->lr.arg, &data->lr.res,
					data->res.lr_ret);
		nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
		nfs_iput_and_deactive(inode);
	}
	kfree(calldata);
}


static int nfs4_proc_remove(struct inode *dir, struct dentry *dentry)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	struct inode *inode = d_inode(dentry);
	int err;

	if (inode) {
		if (inode->i_nlink == 1)
			nfs4_inode_return_delegation(inode);
		else
			nfs4_inode_make_writeable(inode);
	}
	do {
		err = _nfs4_proc_remove(dir, &dentry->d_name, NF4REG);
		trace_nfs4_remove(dir, &dentry->d_name, err);
		err = nfs4_handle_exception(NFS_SERVER(dir), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int nfs4_do_open_expired(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_open_expired(ctx, state);
		trace_nfs4_open_expired(ctx, 0, err);
		if (nfs4_clear_cap_atomic_open_v1(server, err, &exception))
			continue;
		switch (err) {
		default:
			goto out;
		case -NFS4ERR_GRACE:
		case -NFS4ERR_DELAY:
			nfs4_handle_exception(server, err, &exception);
			err = 0;
		}
	} while (exception.retry);
out:
	return err;
}


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 int decode_sequence(struct xdr_stream *xdr,
			   struct nfs4_sequence_res *res,
			   struct rpc_rqst *rqstp)
{
#if defined(CONFIG_NFS_V4_1)
	struct nfs4_session *session;
	struct nfs4_sessionid id;
	u32 dummy;
	int status;
	__be32 *p;

	if (res->sr_slot == NULL)
		return 0;
	if (!res->sr_slot->table->session)
		return 0;

	status = decode_op_hdr(xdr, OP_SEQUENCE);
	if (!status)
		status = decode_sessionid(xdr, &id);
	if (unlikely(status))
		goto out_err;

	/*
	 * If the server returns different values for sessionID, slotID or
	 * sequence number, the server is looney tunes.
	 */
	status = -EREMOTEIO;
	session = res->sr_slot->table->session;

	if (memcmp(id.data, session->sess_id.data,
		   NFS4_MAX_SESSIONID_LEN)) {
		dprintk("%s Invalid session id\n", __func__);
		goto out_err;
	}

	p = xdr_inline_decode(xdr, 20);
	if (unlikely(!p))
		goto out_overflow;

	/* seqid */
	dummy = be32_to_cpup(p++);
	if (dummy != res->sr_slot->seq_nr) {
		dprintk("%s Invalid sequence number\n", __func__);
		goto out_err;
	}
	/* slot id */
	dummy = be32_to_cpup(p++);
	if (dummy != res->sr_slot->slot_nr) {
		dprintk("%s Invalid slot id\n", __func__);
		goto out_err;
	}
	/* highest slot id */
	res->sr_highest_slotid = be32_to_cpup(p++);
	/* target highest slot id */
	res->sr_target_highest_slotid = be32_to_cpup(p++);
	/* result flags */
	res->sr_status_flags = be32_to_cpup(p);
	status = 0;
out_err:
	res->sr_status = status;
	return status;
out_overflow:
	status = -EIO;
	goto out_err;
#else  /* CONFIG_NFS_V4_1 */
	return 0;
#endif /* CONFIG_NFS_V4_1 */
}


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);
}


static void nfs4_update_session(struct nfs4_session *session,
		struct nfs41_create_session_res *res)
{
	nfs4_copy_sessionid(&session->sess_id, &res->sessionid);
	/* Mark client id and session as being confirmed */
	session->clp->cl_exchange_flags |= EXCHGID4_FLAG_CONFIRMED_R;
	set_bit(NFS4_SESSION_ESTABLISHED, &session->session_state);
	session->flags = res->flags;
	memcpy(&session->fc_attrs, &res->fc_attrs, sizeof(session->fc_attrs));
	if (res->flags & SESSION4_BACK_CHAN)
		memcpy(&session->bc_attrs, &res->bc_attrs,
				sizeof(session->bc_attrs));
}


static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string)
{
	ssize_t ret = xdr_stream_decode_opaque_inline(xdr, (void **)string,
			NFS4_OPAQUE_LIMIT);
	if (unlikely(ret < 0))
		return -EIO;
	*len = ret;
	return 0;
}


static int decode_attr_owner(struct xdr_stream *xdr, uint32_t *bitmap,
		const struct nfs_server *server, kuid_t *uid,
		struct nfs4_string *owner_name)
{
	ssize_t len;
	char *p;

	*uid = make_kuid(&init_user_ns, -2);
	if (unlikely(bitmap[1] & (FATTR4_WORD1_OWNER - 1U)))
		return -EIO;
	if (!(bitmap[1] & FATTR4_WORD1_OWNER))
		return 0;
	bitmap[1] &= ~FATTR4_WORD1_OWNER;

	if (owner_name != NULL) {
		len = decode_nfs4_string(xdr, owner_name, GFP_NOIO);
		if (len <= 0)
			goto out;
		dprintk("%s: name=%s\n", __func__, owner_name->data);
		return NFS_ATTR_FATTR_OWNER_NAME;
	} else {
		len = xdr_stream_decode_opaque_inline(xdr, (void **)&p,
				XDR_MAX_NETOBJ);
		if (len <= 0 || nfs_map_name_to_uid(server, p, len, uid) != 0)
			goto out;
		dprintk("%s: uid=%d\n", __func__, (int)from_kuid(&init_user_ns, *uid));
		return NFS_ATTR_FATTR_OWNER;
	}
out:
	if (len == -EBADMSG)
		return -EIO;
	return 0;
}


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 decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
{
	__be32 *p;
	int status = 0;

	*res = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_AVAIL - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_FILES_AVAIL)) {
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, res);
		bitmap[0] &= ~FATTR4_WORD0_FILES_AVAIL;
	}
	dprintk("%s: files avail=%Lu\n", __func__, (unsigned long long)*res);
	return status;
}


static int nfs4_xdr_dec_layoutreturn(struct rpc_rqst *rqstp,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs4_layoutreturn_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_layoutreturn(xdr, res);
out:
	return status;
}


static int nfs4_xdr_dec_layoutcommit(struct rpc_rqst *rqstp,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs4_layoutcommit_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_layoutcommit(xdr, rqstp, res);
	if (status)
		goto out;
	decode_getfattr(xdr, res->fattr, res->server);
out:
	return status;
}


static void nfs4_free_closedata(void *data)
{
	struct nfs4_closedata *calldata = data;
	struct nfs4_state_owner *sp = calldata->state->owner;
	struct super_block *sb = calldata->state->inode->i_sb;

	if (calldata->lr.roc)
		pnfs_roc_release(&calldata->lr.arg, &calldata->lr.res,
				calldata->res.lr_ret);
	nfs4_put_open_state(calldata->state);
	nfs_free_seqid(calldata->arg.seqid);
	nfs4_put_state_owner(sp);
	nfs_sb_deactive(sb);
	kfree(calldata);
}


static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
{
	struct nfs_writeverf *verf = res->verf;
	int status;

	status = decode_op_hdr(xdr, OP_COMMIT);
	if (!status)
		status = decode_write_verifier(xdr, &verf->verifier);
	if (!status)
		verf->committed = NFS_FILE_SYNC;
	return status;
}


static int nfs4_do_set_security_label(struct inode *inode,
		struct nfs4_label *ilabel,
		struct nfs_fattr *fattr,
		struct nfs4_label *olabel)
{
	struct nfs4_exception exception = { };
	int err;

	do {
		err = _nfs4_do_set_security_label(inode, ilabel,
				fattr, olabel);
		trace_nfs4_set_security_label(inode, err);
		err = nfs4_handle_exception(NFS_SERVER(inode), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int decode_attr_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
{
	ssize_t ret;
	ret = decode_bitmap4(xdr, bitmap, 3);
	return ret < 0 ? ret : 0;
}


static int nfs4_xdr_dec_write(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			      void *data)
{
	struct nfs_pgio_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	res->op_status = hdr.status;
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_write(xdr, res);
	if (status)
		goto out;
	if (res->fattr)
		decode_getfattr(xdr, res->fattr, res->server);
	if (!status)
		status = res->count;
out:
	return status;
}


static int decode_attr_time(struct xdr_stream *xdr, struct timespec64 *time)
{
	__be32 *p;

	p = xdr_inline_decode(xdr, nfstime4_maxsz << 2);
	if (unlikely(!p))
		return -EIO;
	xdr_decode_nfstime4(p, time);
	return 0;
}


static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr, struct nfs4_label *label)
{
	struct nfs4_createdata *data;
	int status = -ENOMEM;

	data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4DIR);
	if (data == NULL)
		goto out;

	data->arg.label = label;
	status = nfs4_do_create(dir, dentry, data);

	nfs4_free_createdata(data);
out:
	return status;
}


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 void nfs4_xdr_enc_bind_conn_to_session(struct rpc_rqst *req,
				struct xdr_stream *xdr,
				const void *data)
{
	const struct nfs41_bind_conn_to_session_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = args->client->cl_mvops->minor_version,
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_bind_conn_to_session(xdr, args, &hdr);
	encode_nops(&hdr);
}


nfs4_inc_nlink_locked(struct inode *inode)
{
	NFS_I(inode)->cache_validity |= NFS_INO_INVALID_OTHER;
	inc_nlink(inode);
}


static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_fsinfo_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_sequence(xdr, &res->seq_res, req);
	if (!status)
		status = decode_putfh(xdr);
	if (!status)
		status = decode_fsinfo(xdr, res->fsinfo);
	return status;
}


_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;

	if (data->o_res.delegation_type != 0)
		nfs4_opendata_check_deleg(data, state);
	if (!update_open_stateid(state, &data->o_res.stateid,
				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 int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
	__be32 *p;

	*res = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*res = be32_to_cpup(p);
		bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT;
	}
	dprintk("%s: ACLs supported=%u\n", __func__, (unsigned int)*res);
	return 0;
}


static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
					    struct xdr_stream *xdr,
					    void *data)
{
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_setclientid_confirm(xdr);
	return status;
}


static void nfs4_xdr_enc_commit(struct rpc_rqst *req, struct xdr_stream *xdr,
				const void *data)
{
	const struct nfs_commitargs *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fh, &hdr);
	encode_commit(xdr, args, &hdr);
	encode_nops(&hdr);
}


static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
{
	encode_getattr(xdr, nfs4_fs_locations_bitmap, bitmask,
			ARRAY_SIZE(nfs4_fs_locations_bitmap), hdr);
}


static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_GETFH, decode_getfh_maxsz, hdr);
}


static int nfs4_xdr_dec_pathconf(struct rpc_rqst *req, struct xdr_stream *xdr,
				 void *data)
{
	struct nfs4_pathconf_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_sequence(xdr, &res->seq_res, req);
	if (!status)
		status = decode_putfh(xdr);
	if (!status)
		status = decode_pathconf(xdr, res->pathconf);
	return status;
}


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);
}


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);
}


decode_bitmap4(struct xdr_stream *xdr, uint32_t *bitmap, size_t sz)
{
	ssize_t ret;

	ret = xdr_stream_decode_uint32_array(xdr, bitmap, sz);
	if (likely(ret >= 0))
		return ret;
	if (ret != -EMSGSIZE)
		return -EIO;
	return sz;
}


static void nfs4_xdr_enc_lock(struct rpc_rqst *req, struct xdr_stream *xdr,
			      const void *data)
{
	const struct nfs_lock_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fh, &hdr);
	encode_lock(xdr, args, &hdr);
	encode_nops(&hdr);
}


static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
				   struct dentry *dentry, struct nfs_fh *fhandle,
				   struct nfs_fattr *fattr, struct nfs4_label *label)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	struct rpc_clnt *client = *clnt;
	const struct qstr *name = &dentry->d_name;
	int err;
	do {
		err = _nfs4_proc_lookup(client, dir, dentry, fhandle, fattr, label);
		trace_nfs4_lookup(dir, name, err);
		switch (err) {
		case -NFS4ERR_BADNAME:
			err = -ENOENT;
			goto out;
		case -NFS4ERR_MOVED:
			err = nfs4_get_referral(client, dir, name, fattr, fhandle);
			if (err == -NFS4ERR_MOVED)
				err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception);
			goto out;
		case -NFS4ERR_WRONGSEC:
			err = -EPERM;
			if (client != *clnt)
				goto out;
			client = nfs4_negotiate_security(client, dir, name);
			if (IS_ERR(client))
				return PTR_ERR(client);

			exception.retry = 1;
			break;
		default:
			err = nfs4_handle_exception(NFS_SERVER(dir), err, &exception);
		}
	} while (exception.retry);

out:
	if (err == 0)
		*clnt = client;
	else if (client != *clnt)
		rpc_shutdown_client(client);

	return err;
}


static void nfs4_xdr_enc_create(struct rpc_rqst *req, struct xdr_stream *xdr,
				const void *data)
{
	const struct nfs4_create_arg *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->dir_fh, &hdr);
	encode_create(xdr, args, &hdr);
	encode_getfh(xdr, &hdr);
	encode_getfattr(xdr, args->bitmask, &hdr);
	encode_nops(&hdr);
}


static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
				    struct xdr_stream *xdr,
				    void *data)
{
	struct nfs4_delegreturnres *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status != 0)
		goto out;
	if (res->lr_res) {
		status = decode_layoutreturn(xdr, res->lr_res);
		res->lr_ret = status;
		if (status)
			goto out;
	}
	if (res->fattr) {
		status = decode_getfattr(xdr, res->fattr, res->server);
		if (status != 0)
			goto out;
	}
	status = decode_delegreturn(xdr);
out:
	return status;
}


static int nfs4_xdr_dec_fsid_present(struct rpc_rqst *rqstp,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs4_fsid_present_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_getfh(xdr, res->fh);
	if (status)
		goto out;
	if (res->renew)
		status = decode_renew(xdr);
out:
	return status;
}


static int decode_attr_files_free(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res)
{
	__be32 *p;
	int status = 0;

	*res = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILES_FREE - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_FILES_FREE)) {
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, res);
		bitmap[0] &= ~FATTR4_WORD0_FILES_FREE;
	}
	dprintk("%s: files free=%Lu\n", __func__, (unsigned long long)*res);
	return status;
}


static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
		const struct qstr *name, struct iattr *sattr, u32 ftype)
{
	struct nfs4_createdata *data;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data != NULL) {
		struct nfs_server *server = NFS_SERVER(dir);

		data->label = nfs4_label_alloc(server, GFP_KERNEL);
		if (IS_ERR(data->label))
			goto out_free;

		data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
		data->msg.rpc_argp = &data->arg;
		data->msg.rpc_resp = &data->res;
		data->arg.dir_fh = NFS_FH(dir);
		data->arg.server = server;
		data->arg.name = name;
		data->arg.attrs = sattr;
		data->arg.ftype = ftype;
		data->arg.bitmask = nfs4_bitmask(server, data->label);
		data->arg.umask = current_umask();
		data->res.server = server;
		data->res.fh = &data->fh;
		data->res.fattr = &data->fattr;
		data->res.label = data->label;
		nfs_fattr_init(data->res.fattr);
	}
	return data;
out_free:
	kfree(data);
	return NULL;
}


	__must_hold(RCU)

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

		if (!nfs_need_update_open_stateid(state, stateid))
			return;
		if (!test_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
			break;
		if (status)
			break;
		/* Rely on seqids for serialisation with NFSv4.0 */
		if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
			break;

		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 (!signal_pending(current)) {
			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 int decode_attr_change(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *change)
{
	__be32 *p;
	int ret = 0;

	*change = 0;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_CHANGE - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_CHANGE)) {
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, change);
		bitmap[0] &= ~FATTR4_WORD0_CHANGE;
		ret = NFS_ATTR_FATTR_CHANGE;
	}
	dprintk("%s: change attribute=%Lu\n", __func__,
			(unsigned long long)*change);
	return ret;
}


static int nfs4_xdr_dec_create_session(struct rpc_rqst *rqstp,
				       struct xdr_stream *xdr,
				       void *res)
{
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_create_session(xdr, res);
	return status;
}


static int nfs4_proc_destroy_clientid(struct nfs_client *clp,
		const struct cred *cred)
{
	unsigned int loop;
	int ret;

	for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
		ret = _nfs4_proc_destroy_clientid(clp, cred);
		switch (ret) {
		case -NFS4ERR_DELAY:
		case -NFS4ERR_CLIENTID_BUSY:
			ssleep(1);
			break;
		default:
			return ret;
		}
	}
	return 0;
}


static void encode_nfs4_seqid(struct xdr_stream *xdr,
		const struct nfs_seqid *seqid)
{
	if (seqid != NULL)
		encode_uint32(xdr, seqid->sequence->counter);
	else
		encode_uint32(xdr, 0);
}


static int decode_threshold_hint(struct xdr_stream *xdr,
				  uint32_t *bitmap,
				  uint64_t *res,
				  uint32_t hint_bit)
{
	__be32 *p;

	*res = 0;
	if (likely(bitmap[0] & hint_bit)) {
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, res);
	}
	return 0;
}


static int decode_test_stateid(struct xdr_stream *xdr,
			       struct nfs41_test_stateid_res *res)
{
	__be32 *p;
	int status;
	int num_res;

	status = decode_op_hdr(xdr, OP_TEST_STATEID);
	if (status)
		return status;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	num_res = be32_to_cpup(p++);
	if (num_res != 1)
		return -EIO;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	res->status = be32_to_cpup(p++);

	return status;
}


static int decode_setattr(struct xdr_stream *xdr)
{
	int status;

	status = decode_op_hdr(xdr, OP_SETATTR);
	if (status)
		return status;
	if (decode_bitmap4(xdr, NULL, 0) >= 0)
		return 0;
	return -EIO;
}


static void nfs4_sync_open_stateid(nfs4_stateid *dst,
		struct nfs4_state *state)
{
	__be32 seqid_open;
	u32 dst_seqid;
	int seq;

	for (;;) {
		if (!nfs4_valid_open_stateid(state))
			break;
		seq = read_seqbegin(&state->seqlock);
		if (!nfs4_state_match_open_stateid_other(state, dst)) {
			nfs4_stateid_copy(dst, &state->open_stateid);
			if (read_seqretry(&state->seqlock, seq))
				continue;
			break;
		}
		seqid_open = state->open_stateid.seqid;
		if (read_seqretry(&state->seqlock, seq))
			continue;

		dst_seqid = be32_to_cpu(dst->seqid);
		if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) < 0)
			dst->seqid = seqid_open;
		break;
	}
}


nfs_alloc_no_seqid(struct nfs_seqid_counter *arg1, gfp_t arg2)
{
	return NULL;
}


static void encode_reclaim_complete(struct xdr_stream *xdr,
				    const struct nfs41_reclaim_complete_args *args,
				    struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_RECLAIM_COMPLETE, decode_reclaim_complete_maxsz, hdr);
	encode_uint32(xdr, args->one_fs);
}


static void nfs4_xdr_enc_layoutcommit(struct rpc_rqst *req,
				      struct xdr_stream *xdr,
				      const void *priv)
{
	const struct nfs4_layoutcommit_args *args = priv;
	struct nfs4_layoutcommit_data *data =
		container_of(args, struct nfs4_layoutcommit_data, args);
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, NFS_FH(args->inode), &hdr);
	encode_layoutcommit(xdr, data->args.inode, args, &hdr);
	encode_getfattr(xdr, args->bitmask, &hdr);
	encode_nops(&hdr);
}


static int decode_close(struct xdr_stream *xdr, struct nfs_closeres *res)
{
	int status;

	status = decode_op_hdr(xdr, OP_CLOSE);
	if (status != -EIO)
		nfs_increment_open_seqid(status, res->seqid);
	if (!status)
		status = decode_invalid_stateid(xdr, &res->stateid);
	return status;
}


static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_create_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_create(xdr, &res->dir_cinfo);
	if (status)
		goto out;
	status = decode_getfh(xdr, res->fh);
	if (status)
		goto out;
	decode_getfattr_label(xdr, res->fattr, res->label, res->server);
out:
	return status;
}


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;
}


static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr, struct nfs4_label *label, dev_t rdev)
{
	struct nfs4_createdata *data;
	int mode = sattr->ia_mode;
	int status = -ENOMEM;

	data = nfs4_alloc_createdata(dir, &dentry->d_name, sattr, NF4SOCK);
	if (data == NULL)
		goto out;

	if (S_ISFIFO(mode))
		data->arg.ftype = NF4FIFO;
	else if (S_ISBLK(mode)) {
		data->arg.ftype = NF4BLK;
		data->arg.u.device.specdata1 = MAJOR(rdev);
		data->arg.u.device.specdata2 = MINOR(rdev);
	}
	else if (S_ISCHR(mode)) {
		data->arg.ftype = NF4CHR;
		data->arg.u.device.specdata1 = MAJOR(rdev);
		data->arg.u.device.specdata2 = MINOR(rdev);
	} else if (!S_ISSOCK(mode)) {
		status = -EINVAL;
		goto out_free;
	}

	data->arg.label = label;
	status = nfs4_do_create(dir, dentry, data);
out_free:
	nfs4_free_createdata(data);
out:
	return status;
}


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;
}


nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx,
		int open_flags, struct iattr *attr, int *opened)
{
	struct nfs4_state *state;
	struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;

	label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);

	/* Protect against concurrent sillydeletes */
	state = nfs4_do_open(dir, ctx, open_flags, attr, label, opened);

	nfs4_label_release_security(label);

	if (IS_ERR(state))
		return ERR_CAST(state);
	return state->inode;
}


static void nfs4_renew_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_renewdata *data = calldata;
	struct nfs_client *clp = data->client;
	unsigned long timestamp = data->timestamp;

	trace_nfs4_renew_async(clp, task->tk_status);
	switch (task->tk_status) {
	case 0:
		break;
	case -NFS4ERR_LEASE_MOVED:
		nfs4_schedule_lease_moved_recovery(clp);
		break;
	default:
		/* Unless we're shutting down, schedule state recovery! */
		if (test_bit(NFS_CS_RENEWD, &clp->cl_res_state) == 0)
			return;
		if (task->tk_status != NFS4ERR_CB_PATH_DOWN) {
			nfs4_schedule_lease_recovery(clp);
			return;
		}
		nfs4_schedule_path_down_recovery(clp);
	}
	do_renew_lease(clp, timestamp);
}


static int decode_lockt(struct xdr_stream *xdr, struct nfs_lockt_res *res)
{
	int status;
	status = decode_op_hdr(xdr, OP_LOCKT);
	if (status == -NFS4ERR_DENIED)
		return decode_lock_denied(xdr, res->denied);
	return status;
}


static __be32 *reserve_space(struct xdr_stream *xdr, size_t nbytes)
{
	__be32 *p = xdr_reserve_space(xdr, nbytes);
	BUG_ON(!p);
	return p;
}


static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
{
	struct nfs4_secinfo4 *sec_flavor;
	unsigned int i, num_flavors;
	int status;
	__be32 *p;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;

	res->flavors->num_flavors = 0;
	num_flavors = be32_to_cpup(p);

	for (i = 0; i < num_flavors; i++) {
		sec_flavor = &res->flavors->flavors[i];
		if ((char *)&sec_flavor[1] - (char *)res->flavors > PAGE_SIZE)
			break;

		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		sec_flavor->flavor = be32_to_cpup(p);

		if (sec_flavor->flavor == RPC_AUTH_GSS) {
			status = decode_secinfo_gss(xdr, sec_flavor);
			if (status)
				goto out;
		}
		res->flavors->num_flavors++;
	}

	status = 0;
out:
	return status;
}


static int nfs4_proc_renew(struct nfs_client *clp, const struct cred *cred)
{
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_RENEW],
		.rpc_argp	= clp,
		.rpc_cred	= cred,
	};
	unsigned long now = jiffies;
	int status;

	status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
	if (status < 0)
		return status;
	do_renew_lease(clp, now);
	return 0;
}


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 int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req,
		       struct nfs_pgio_res *res)
{
	__be32 *p;
	uint32_t count, eof, recvd;
	int status;

	status = decode_op_hdr(xdr, OP_READ);
	if (status)
		return status;
	p = xdr_inline_decode(xdr, 8);
	if (unlikely(!p))
		return -EIO;
	eof = be32_to_cpup(p++);
	count = be32_to_cpup(p);
	recvd = xdr_read_pages(xdr, count);
	if (count > recvd) {
		dprintk("NFS: server cheating in read reply: "
				"count %u > recvd %u\n", count, recvd);
		count = recvd;
		eof = 0;
	}
	res->eof = eof;
	res->count = count;
	return 0;
}


static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs_removeres *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_remove(xdr, &res->cinfo);
out:
	return status;
}


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 int decode_attr_maxread(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
	__be32 *p;
	int status = 0;

	*res = 1024;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXREAD - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_MAXREAD)) {
		uint64_t maxread;
		p = xdr_inline_decode(xdr, 8);
		if (unlikely(!p))
			return -EIO;
		xdr_decode_hyper(p, &maxread);
		if (maxread > 0x7FFFFFFF)
			maxread = 0x7FFFFFFF;
		*res = (uint32_t)maxread;
		bitmap[0] &= ~FATTR4_WORD0_MAXREAD;
	}
	dprintk("%s: maxread=%lu\n", __func__, (unsigned long)*res);
	return status;
}


static void nfs4_xdr_enc_fsid_present(struct rpc_rqst *req,
				      struct xdr_stream *xdr,
				      const void *data)
{
	const struct nfs4_fsid_present_arg *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fh, &hdr);
	encode_getfh(xdr, &hdr);
	if (args->renew)
		encode_renew(xdr, args->clientid, &hdr);
	encode_nops(&hdr);
}


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 int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_accessres *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status != 0)
		goto out;
	status = decode_access(xdr, &res->supported, &res->access);
	if (status != 0)
		goto out;
	if (res->fattr)
		decode_getfattr(xdr, res->fattr, res->server);
out:
	return status;
}


static void encode_destroy_clientid(struct xdr_stream *xdr,
				   uint64_t clientid,
				   struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_DESTROY_CLIENTID, decode_destroy_clientid_maxsz, hdr);
	encode_uint64(xdr, clientid);
}


encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_RESTOREFH, decode_restorefh_maxsz, hdr);
}


static inline void encode_claim_delegate_cur_fh(struct xdr_stream *xdr, const nfs4_stateid *stateid)
{
	__be32 *p;

	p = reserve_space(xdr, 4);
	*p = cpu_to_be32(NFS4_OPEN_CLAIM_DELEG_CUR_FH);
	encode_nfs4_stateid(xdr, stateid);
}


static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state *state)
{
	struct nfs_delegation *delegation;
	struct nfs4_opendata *opendata;
	fmode_t delegation_type = 0;
	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)
		delegation_type = delegation->type;
	rcu_read_unlock();
	opendata->o_arg.u.delegation_type = delegation_type;
	status = nfs4_open_recover(opendata, state);
	nfs4_opendata_put(opendata);
	return status;
}


static void nfs4_xdr_enc_lookup(struct rpc_rqst *req, struct xdr_stream *xdr,
				const void *data)
{
	const struct nfs4_lookup_arg *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->dir_fh, &hdr);
	encode_lookup(xdr, args->name, &hdr);
	encode_getfh(xdr, &hdr);
	encode_getfattr(xdr, args->bitmask, &hdr);
	encode_nops(&hdr);
}


static int nfs4_xdr_dec_destroy_clientid(struct rpc_rqst *rqstp,
					struct xdr_stream *xdr,
					void *res)
{
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_destroy_clientid(xdr, res);
	return status;
}


nfs4_proc_bind_conn_to_session_callback(struct rpc_clnt *clnt,
		struct rpc_xprt *xprt,
		void *calldata)
{
	struct rpc_bind_conn_calldata *p = calldata;

	return nfs4_proc_bind_one_conn_to_session(clnt, xprt, p->clp, p->cred);
}


static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = _nfs4_proc_access(inode, entry);
		trace_nfs4_access(inode, err);
		err = nfs4_handle_exception(NFS_SERVER(inode), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int _nfs4_recover_proc_open(struct nfs4_opendata *data)
{
	struct inode *dir = d_inode(data->dir);
	struct nfs_openres *o_res = &data->o_res;
	int status;

	status = nfs4_run_open_task(data, NULL);
	if (status != 0 || !data->rpc_done)
		return status;

	nfs_fattr_map_and_free_names(NFS_SERVER(dir), &data->f_attr);

	if (o_res->rflags & NFS4_OPEN_RESULT_CONFIRM)
		status = _nfs4_proc_open_confirm(data);

	return status;
}


static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_statfs_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (!status)
		status = decode_sequence(xdr, &res->seq_res, req);
	if (!status)
		status = decode_putfh(xdr);
	if (!status)
		status = decode_statfs(xdr, res->fsstat);
	return status;
}


encode_getdeviceinfo(struct xdr_stream *xdr,
		     const struct nfs4_getdeviceinfo_args *args,
		     struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_GETDEVICEINFO, decode_getdeviceinfo_maxsz, hdr);
	p = reserve_space(xdr, NFS4_DEVICEID4_SIZE + 4 + 4);
	p = xdr_encode_opaque_fixed(p, args->pdev->dev_id.data,
				    NFS4_DEVICEID4_SIZE);
	*p++ = cpu_to_be32(args->pdev->layout_type);
	*p++ = cpu_to_be32(args->pdev->maxcount);	/* gdia_maxcount */

	p = reserve_space(xdr, 4 + 4);
	*p++ = cpu_to_be32(1);			/* bitmap length */
	*p++ = cpu_to_be32(args->notify_types);
}


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);
}


nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
{
	struct nfs_open_context *ctx;
	struct nfs4_state *state;
	int status;

	/* verify open state */
	ctx = nfs_file_open_context(filp);
	state = ctx->state;

	if (IS_GETLK(cmd)) {
		if (state != NULL)
			return nfs4_proc_getlk(state, F_GETLK, request);
		return 0;
	}

	if (!(IS_SETLK(cmd) || IS_SETLKW(cmd)))
		return -EINVAL;

	if (request->fl_type == F_UNLCK) {
		if (state != NULL)
			return nfs4_proc_unlck(state, cmd, request);
		return 0;
	}

	if (state == NULL)
		return -ENOLCK;

	if ((request->fl_flags & FL_POSIX) &&
	    !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
		return -ENOLCK;

	/*
	 * Don't rely on the VFS having checked the file open mode,
	 * since it won't do this for flock() locks.
	 */
	switch (request->fl_type) {
	case F_RDLCK:
		if (!(filp->f_mode & FMODE_READ))
			return -EBADF;
		break;
	case F_WRLCK:
		if (!(filp->f_mode & FMODE_WRITE))
			return -EBADF;
	}

	status = nfs4_set_lock_state(state, request);
	if (status != 0)
		return status;

	return nfs4_retry_setlk(state, cmd, request);
}


static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
		const nfs4_stateid *stateid)
{
	u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
	u32 stateid_seqid = be32_to_cpu(stateid->seqid);

	if (stateid_seqid == state_seqid + 1U ||
	    (stateid_seqid == 1U && state_seqid == 0xffffffffU))
		nfs_state_log_update_open_stateid(state);
	else
		set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
}


int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
		struct nfs4_setclientid_res *arg,
		const struct cred *cred)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
		.rpc_argp = arg,
		.rpc_cred = cred,
	};
	int status;

	dprintk("NFS call  setclientid_confirm auth=%s, (client ID %llx)\n",
		clp->cl_rpcclient->cl_auth->au_ops->au_name,
		clp->cl_clientid);
	status = rpc_call_sync(clp->cl_rpcclient, &msg,
			       RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN);
	trace_nfs4_setclientid_confirm(clp, status);
	dprintk("NFS reply setclientid_confirm: %d\n", status);
	return status;
}


static int decode_setclientid_confirm(struct xdr_stream *xdr)
{
	return decode_op_hdr(xdr, OP_SETCLIENTID_CONFIRM);
}


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 inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
{
	struct nfs_inode *nfsi = NFS_I(inode);
	struct nfs4_cached_acl *acl;
	int ret = -ENOENT;

	spin_lock(&inode->i_lock);
	acl = nfsi->nfs4_acl;
	if (acl == NULL)
		goto out;
	if (buf == NULL) /* user is just asking for length */
		goto out_len;
	if (acl->cached == 0)
		goto out;
	ret = -ERANGE; /* see getxattr(2) man page */
	if (acl->len > buflen)
		goto out;
	memcpy(buf, acl->data, acl->len);
out_len:
	ret = acl->len;
out:
	spin_unlock(&inode->i_lock);
	return ret;
}


static int decode_opaque_fixed(struct xdr_stream *xdr, void *buf, size_t len)
{
	ssize_t ret = xdr_stream_decode_opaque_fixed(xdr, buf, len);
	if (unlikely(ret < 0))
		return -EIO;
	return 0;
}


static int decode_delegation_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
	stateid->type = NFS4_DELEGATION_STATEID_TYPE;
	return decode_stateid(xdr, stateid);
}


int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
		      struct nfs4_secinfo_flavors *flavors)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = -NFS4ERR_WRONGSEC;

		/* try to use integrity protection with machine cred */
		if (_nfs4_is_integrity_protected(NFS_SERVER(dir)->nfs_client))
			err = _nfs4_proc_secinfo(dir, name, flavors, true);

		/*
		 * if unable to use integrity protection, or SECINFO with
		 * integrity protection returns NFS4ERR_WRONGSEC (which is
		 * disallowed by spec, but exists in deployed servers) use
		 * the current filesystem's rpc_client and the user cred.
		 */
		if (err == -NFS4ERR_WRONGSEC)
			err = _nfs4_proc_secinfo(dir, name, flavors, false);

		trace_nfs4_secinfo(dir, name, err);
		err = nfs4_handle_exception(NFS_SERVER(dir), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int decode_attr_security_label(struct xdr_stream *xdr, uint32_t *bitmap,
					struct nfs4_label *label)
{
	uint32_t pi = 0;
	uint32_t lfs = 0;
	__u32 len;
	__be32 *p;
	int status = 0;

	if (unlikely(bitmap[2] & (FATTR4_WORD2_SECURITY_LABEL - 1U)))
		return -EIO;
	if (likely(bitmap[2] & FATTR4_WORD2_SECURITY_LABEL)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		lfs = be32_to_cpup(p++);
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		pi = be32_to_cpup(p++);
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		len = be32_to_cpup(p++);
		p = xdr_inline_decode(xdr, len);
		if (unlikely(!p))
			return -EIO;
		if (len < NFS4_MAXLABELLEN) {
			if (label) {
				if (label->len) {
					if (label->len < len)
						return -ERANGE;
					memcpy(label->label, p, len);
				}
				label->len = len;
				label->pi = pi;
				label->lfs = lfs;
				status = NFS_ATTR_FATTR_V4_SECURITY_LABEL;
			}
			bitmap[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
		} else
			printk(KERN_WARNING "%s: label too long (%u)!\n",
					__func__, len);
	}
	if (label && label->label)
		dprintk("%s: label=%s, len=%d, PI=%d, LFS=%d\n", __func__,
			(char *)label->label, label->len, label->pi, label->lfs);
	return status;
}


static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct nfs_inode *nfsi = NFS_I(state->inode);
	struct nfs4_state_owner *sp = state->owner;
	unsigned char fl_flags = request->fl_flags;
	int status;

	request->fl_flags |= FL_ACCESS;
	status = locks_lock_inode_wait(state->inode, request);
	if (status < 0)
		goto out;
	mutex_lock(&sp->so_delegreturn_mutex);
	down_read(&nfsi->rwsem);
	if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
		/* Yes: cache locks! */
		/* ...but avoid races with delegation recall... */
		request->fl_flags = fl_flags & ~FL_SLEEP;
		status = locks_lock_inode_wait(state->inode, request);
		up_read(&nfsi->rwsem);
		mutex_unlock(&sp->so_delegreturn_mutex);
		goto out;
	}
	up_read(&nfsi->rwsem);
	mutex_unlock(&sp->so_delegreturn_mutex);
	status = _nfs4_do_setlk(state, cmd, request, NFS_LOCK_NEW);
out:
	request->fl_flags = fl_flags;
	return status;
}


int nfs4_proc_create_session(struct nfs_client *clp, const struct cred *cred)
{
	int status;
	unsigned *ptr;
	struct nfs4_session *session = clp->cl_session;

	dprintk("--> %s clp=%p session=%p\n", __func__, clp, session);

	status = _nfs4_proc_create_session(clp, cred);
	if (status)
		goto out;

	/* Init or reset the session slot tables */
	status = nfs4_setup_session_slot_tables(session);
	dprintk("slot table setup returned %d\n", status);
	if (status)
		goto out;

	ptr = (unsigned *)&session->sess_id.data[0];
	dprintk("%s client>seqid %d sessionid %u:%u:%u:%u\n", __func__,
		clp->cl_seqid, ptr[0], ptr[1], ptr[2], ptr[3]);
out:
	dprintk("<-- %s\n", __func__);
	return status;
}


int nfs4_proc_fsid_present(struct inode *inode, const struct cred *cred)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs_client *clp = server->nfs_client;
	const struct nfs4_mig_recovery_ops *ops =
					clp->cl_mvops->mig_recovery_ops;
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int status;

	dprintk("%s: FSID %llx:%llx on \"%s\"\n", __func__,
		(unsigned long long)server->fsid.major,
		(unsigned long long)server->fsid.minor,
		clp->cl_hostname);
	nfs_display_fhandle(NFS_FH(inode), __func__);

	do {
		status = ops->fsid_present(inode, cred);
		if (status != -NFS4ERR_DELAY)
			break;
		nfs4_handle_exception(server, status, &exception);
	} while (exception.retry);
	return status;
}


static void nfs4_layoutcommit_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutcommit_data *data = calldata;
	struct nfs_server *server = NFS_SERVER(data->args.inode);

	nfs4_setup_sequence(server->nfs_client,
			&data->args.seq_args,
			&data->res.seq_res,
			task);
}


static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
{
	encode_opaque_fixed(xdr, verf->data, NFS4_VERIFIER_SIZE);
}


static void encode_getfattr_open(struct xdr_stream *xdr, const u32 *bitmask,
				 const u32 *open_bitmap,
				 struct compound_hdr *hdr)
{
	encode_getattr(xdr, open_bitmap, bitmask, 3, hdr);
}


static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;

	do {
		err = _nfs4_proc_getlk(state, cmd, request);
		trace_nfs4_get_lock(request, state, cmd, err);
		err = nfs4_handle_exception(NFS_SERVER(state->inode), err,
				&exception);
	} while (exception.retry);
	return err;
}


static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
				     struct xdr_stream *xdr,
				     const void *data)
{
	const struct nfs4_delegreturnargs *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fhandle, &hdr);
	if (args->lr_args)
		encode_layoutreturn(xdr, args->lr_args, &hdr);
	if (args->bitmask)
		encode_getfattr(xdr, args->bitmask, &hdr);
	encode_delegreturn(xdr, args->stateid, &hdr);
	encode_nops(&hdr);
}


static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
{
	if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) {
		int ret;
		ret = decode_attr_bitmap(xdr, bitmask);
		if (unlikely(ret < 0))
			return ret;
		bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS;
	} else
		bitmask[0] = bitmask[1] = bitmask[2] = 0;
	dprintk("%s: bitmask=%08x:%08x:%08x\n", __func__,
		bitmask[0], bitmask[1], bitmask[2]);
	return 0;
}


static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path)
{
	u32 n;
	__be32 *p;
	int status = 0;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	n = be32_to_cpup(p);
	if (n == 0)
		goto root_path;
	dprintk("pathname4: ");
	if (n > NFS4_PATHNAME_MAXCOMPONENTS) {
		dprintk("cannot parse %d components in path\n", n);
		goto out_eio;
	}
	for (path->ncomponents = 0; path->ncomponents < n; path->ncomponents++) {
		struct nfs4_string *component = &path->components[path->ncomponents];
		status = decode_opaque_inline(xdr, &component->len, &component->data);
		if (unlikely(status != 0))
			goto out_eio;
		ifdebug (XDR)
			pr_cont("%s%.*s ",
				(path->ncomponents != n ? "/ " : ""),
				component->len, component->data);
	}
out:
	return status;
root_path:
/* a root pathname is sent as a zero component4 */
	path->ncomponents = 1;
	path->components[0].len=0;
	path->components[0].data=NULL;
	dprintk("pathname4: /\n");
	goto out;
out_eio:
	dprintk(" status %d", status);
	status = -EIO;
	goto out;
}


static void nfs4_layoutreturn_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_layoutreturn *lrp = calldata;
	struct nfs_server *server;

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

	if (!nfs41_sequence_process(task, &lrp->res.seq_res))
		return;

	/*
	 * Was there an RPC level error? Assume the call succeeded,
	 * and that we need to release the layout
	 */
	if (task->tk_rpc_status != 0 && RPC_WAS_SENT(task)) {
		lrp->res.lrs_present = 0;
		return;
	}

	server = NFS_SERVER(lrp->args.inode);
	switch (task->tk_status) {
	case -NFS4ERR_OLD_STATEID:
		if (nfs4_layout_refresh_old_stateid(&lrp->args.stateid,
					&lrp->args.range,
					lrp->args.inode))
			goto out_restart;
		/* Fallthrough */
	default:
		task->tk_status = 0;
		/* Fallthrough */
	case 0:
		break;
	case -NFS4ERR_DELAY:
		if (nfs4_async_handle_error(task, server, NULL, NULL) != -EAGAIN)
			break;
		goto out_restart;
	}
	dprintk("<-- %s\n", __func__);
	return;
out_restart:
	task->tk_status = 0;
	nfs4_sequence_free_slot(&lrp->res.seq_res);
	rpc_restart_call_prepare(task);
}


static void encode_lookupp(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_LOOKUPP, decode_lookupp_maxsz, hdr);
}


static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred, const nfs4_stateid *stateid, int issync)
{
	struct nfs4_delegreturndata *data;
	struct nfs_server *server = NFS_SERVER(inode);
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_DELEGRETURN],
		.rpc_cred = cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = &nfs4_delegreturn_ops,
		.flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
	};
	int status = 0;

	data = kzalloc(sizeof(*data), GFP_NOFS);
	if (data == NULL)
		return -ENOMEM;
	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);

	nfs4_state_protect(server->nfs_client,
			NFS_SP4_MACH_CRED_CLEANUP,
			&task_setup_data.rpc_client, &msg);

	data->args.fhandle = &data->fh;
	data->args.stateid = &data->stateid;
	data->args.bitmask = server->cache_consistency_bitmask;
	nfs_copy_fh(&data->fh, NFS_FH(inode));
	nfs4_stateid_copy(&data->stateid, stateid);
	data->res.fattr = &data->fattr;
	data->res.server = server;
	data->res.lr_ret = -NFS4ERR_NOMATCHING_LAYOUT;
	data->lr.arg.ld_private = &data->lr.ld_private;
	nfs_fattr_init(data->res.fattr);
	data->timestamp = jiffies;
	data->rpc_status = 0;
	data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res, cred);
	data->inode = nfs_igrab_and_active(inode);
	if (data->inode) {
		if (data->lr.roc) {
			data->args.lr_args = &data->lr.arg;
			data->res.lr_res = &data->lr.res;
		}
	} else if (data->lr.roc) {
		pnfs_roc_release(&data->lr.arg, &data->lr.res, 0);
		data->lr.roc = false;
	}

	task_setup_data.callback_data = data;
	msg.rpc_argp = &data->args;
	msg.rpc_resp = &data->res;
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	if (!issync)
		goto out;
	status = rpc_wait_for_completion_task(task);
	if (status != 0)
		goto out;
	status = data->rpc_status;
out:
	rpc_put_task(task);
	return status;
}


static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
					struct xdr_stream *xdr,
					const void *data)
{
	const struct nfs_closeargs *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_putfh(xdr, args->fh, &hdr);
	if (args->lr_args)
		encode_layoutreturn(xdr, args->lr_args, &hdr);
	encode_open_downgrade(xdr, args, &hdr);
	encode_nops(&hdr);
}


static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct nfs4_accessargs args = {
		.fh = NFS_FH(inode),
		.access = entry->mask,
	};
	struct nfs4_accessres res = {
		.server = server,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_ACCESS],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = entry->cred,
	};
	int status = 0;

	if (!nfs4_have_delegation(inode, FMODE_READ)) {
		res.fattr = nfs_alloc_fattr();
		if (res.fattr == NULL)
			return -ENOMEM;
		args.bitmask = server->cache_consistency_bitmask;
	}
	status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
	if (!status) {
		nfs_access_set_mask(entry, res.access);
		if (res.fattr)
			nfs_refresh_inode(inode, res.fattr);
	}
	nfs_free_fattr(res.fattr);
	return status;
}


static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_OPEN, decode_open_maxsz, hdr);
	encode_openhdr(xdr, arg);
	encode_opentype(xdr, arg);
	switch (arg->claim) {
	case NFS4_OPEN_CLAIM_NULL:
		encode_claim_null(xdr, arg->name);
		break;
	case NFS4_OPEN_CLAIM_PREVIOUS:
		encode_claim_previous(xdr, arg->u.delegation_type);
		break;
	case NFS4_OPEN_CLAIM_DELEGATE_CUR:
		encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
		break;
	case NFS4_OPEN_CLAIM_FH:
		encode_claim_fh(xdr);
		break;
	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
		encode_claim_delegate_cur_fh(xdr, &arg->u.delegation);
		break;
	default:
		BUG();
	}
}


static int decode_attr_time_access(struct xdr_stream *xdr, uint32_t *bitmap, struct timespec64 *time)
{
	int status = 0;

	time->tv_sec = 0;
	time->tv_nsec = 0;
	if (unlikely(bitmap[1] & (FATTR4_WORD1_TIME_ACCESS - 1U)))
		return -EIO;
	if (likely(bitmap[1] & FATTR4_WORD1_TIME_ACCESS)) {
		status = decode_attr_time(xdr, time);
		if (status == 0)
			status = NFS_ATTR_FATTR_ATIME;
		bitmap[1] &= ~FATTR4_WORD1_TIME_ACCESS;
	}
	dprintk("%s: atime=%lld\n", __func__, time->tv_sec);
	return status;
}


static void nfs4_xdr_enc_free_stateid(struct rpc_rqst *req,
				     struct xdr_stream *xdr,
				     const void *data)
{
	const struct nfs41_free_stateid_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->seq_args),
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->seq_args, &hdr);
	encode_free_stateid(xdr, args, &hdr);
	encode_nops(&hdr);
}


static void encode_free_stateid(struct xdr_stream *xdr,
				const struct nfs41_free_stateid_args *args,
				struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, hdr);
	encode_nfs4_stateid(xdr, &args->stateid);
}


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);
}


static int decode_open_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
	stateid->type = NFS4_OPEN_STATEID_TYPE;
	return decode_stateid(xdr, stateid);
}


static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_PUTROOTFH, decode_putrootfh_maxsz, hdr);
}


static void encode_test_stateid(struct xdr_stream *xdr,
				const struct nfs41_test_stateid_args *args,
				struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_TEST_STATEID, decode_test_stateid_maxsz, hdr);
	encode_uint32(xdr, 1);
	encode_nfs4_stateid(xdr, args->stateid);
}


static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state,
		const nfs4_stateid *stateid)
{
	nfs_remove_bad_delegation(state->inode, stateid);
	nfs_state_clear_delegation(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);
		/* cached opens have already been processed */
		goto update;
	}

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

	if (data->o_res.delegation_type != 0)
		nfs4_opendata_check_deleg(data, state);
update:
	if (!update_open_stateid(state, &data->o_res.stateid,
				NULL, data->o_arg.fmode))
		return ERR_PTR(-EAGAIN);
	refcount_inc(&state->count);

	return state;
}


static void encode_secinfo(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_SECINFO, decode_secinfo_maxsz, hdr);
	encode_string(xdr, name->len, name->name);
}


static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res)
{
	int n;
	__be32 *p;
	int status = -EIO;

	if (unlikely(bitmap[0] & (FATTR4_WORD0_FS_LOCATIONS -1U)))
		goto out;
	status = 0;
	if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS)))
		goto out;
	bitmap[0] &= ~FATTR4_WORD0_FS_LOCATIONS;
	status = -EIO;
	/* Ignore borken servers that return unrequested attrs */
	if (unlikely(res == NULL))
		goto out;
	dprintk("%s: fsroot:\n", __func__);
	status = decode_pathname(xdr, &res->fs_path);
	if (unlikely(status != 0))
		goto out;
	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		goto out_eio;
	n = be32_to_cpup(p);
	if (n <= 0)
		goto out_eio;
	for (res->nlocations = 0; res->nlocations < n; res->nlocations++) {
		u32 m;
		struct nfs4_fs_location *loc;

		if (res->nlocations == NFS4_FS_LOCATIONS_MAXENTRIES)
			break;
		loc = &res->locations[res->nlocations];
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			goto out_eio;
		m = be32_to_cpup(p);

		dprintk("%s: servers:\n", __func__);
		for (loc->nservers = 0; loc->nservers < m; loc->nservers++) {
			struct nfs4_string *server;

			if (loc->nservers == NFS4_FS_LOCATION_MAXSERVERS) {
				unsigned int i;
				dprintk("%s: using first %u of %u servers "
					"returned for location %u\n",
						__func__,
						NFS4_FS_LOCATION_MAXSERVERS,
						m, res->nlocations);
				for (i = loc->nservers; i < m; i++) {
					unsigned int len;
					char *data;
					status = decode_opaque_inline(xdr, &len, &data);
					if (unlikely(status != 0))
						goto out_eio;
				}
				break;
			}
			server = &loc->servers[loc->nservers];
			status = decode_opaque_inline(xdr, &server->len, &server->data);
			if (unlikely(status != 0))
				goto out_eio;
			dprintk("%s ", server->data);
		}
		status = decode_pathname(xdr, &loc->rootpath);
		if (unlikely(status != 0))
			goto out_eio;
	}
	if (res->nlocations != 0)
		status = NFS_ATTR_FATTR_V4_LOCATIONS;
out:
	dprintk("%s: fs_locations done, error = %d\n", __func__, status);
	return status;
out_eio:
	status = -EIO;
	goto out;
}


static int nfs4_xdr_dec_test_stateid(struct rpc_rqst *rqstp,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs41_test_stateid_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_test_stateid(xdr, res);
out:
	return status;
}


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, data->f_label);
		break;
	default:
		inode = d_inode(data->dentry);
		ihold(inode);
		nfs_refresh_inode(inode, &data->f_attr);
	}
	return inode;
}


static int decode_lookup(struct xdr_stream *xdr)
{
	return decode_op_hdr(xdr, OP_LOOKUP);
}


static int decode_delegreturn(struct xdr_stream *xdr)
{
	return decode_op_hdr(xdr, OP_DELEGRETURN);
}


static void encode_uint32(struct xdr_stream *xdr, u32 n)
{
	WARN_ON_ONCE(xdr_stream_encode_u32(xdr, n) < 0);
}


static bool __decode_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected,
		int *nfs_retval)
{
	__be32 *p;
	uint32_t opnum;
	int32_t nfserr;

	p = xdr_inline_decode(xdr, 8);
	if (unlikely(!p))
		goto out_overflow;
	opnum = be32_to_cpup(p++);
	if (unlikely(opnum != expected))
		goto out_bad_operation;
	if (unlikely(*p != cpu_to_be32(NFS_OK)))
		goto out_status;
	*nfs_retval = 0;
	return true;
out_status:
	nfserr = be32_to_cpup(p);
	trace_nfs4_xdr_status(xdr, opnum, nfserr);
	*nfs_retval = nfs4_stat_to_errno(nfserr);
	return true;
out_bad_operation:
	dprintk("nfs: Server returned operation"
		" %d but we issued a request for %d\n",
			opnum, expected);
	*nfs_retval = -EREMOTEIO;
	return false;
out_overflow:
	*nfs_retval = -EIO;
	return false;
}


nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data, bool sync)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTCOMMIT],
		.rpc_argp = &data->args,
		.rpc_resp = &data->res,
		.rpc_cred = data->cred,
	};
	struct rpc_task_setup task_setup_data = {
		.task = &data->task,
		.rpc_client = NFS_CLIENT(data->args.inode),
		.rpc_message = &msg,
		.callback_ops = &nfs4_layoutcommit_ops,
		.callback_data = data,
	};
	struct rpc_task *task;
	int status = 0;

	dprintk("NFS: initiating layoutcommit call. sync %d "
		"lbw: %llu inode %lu\n", sync,
		data->args.lastbytewritten,
		data->args.inode->i_ino);

	if (!sync) {
		data->inode = nfs_igrab_and_active(data->args.inode);
		if (data->inode == NULL) {
			nfs4_layoutcommit_release(data);
			return -EAGAIN;
		}
		task_setup_data.flags = RPC_TASK_ASYNC;
	}
	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, 0);
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	if (sync)
		status = task->tk_status;
	trace_nfs4_layoutcommit(data->args.inode, &data->args.stateid, status);
	dprintk("%s: status %d\n", __func__, status);
	rpc_put_task(task);
	return status;
}


static int decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
{
	return decode_opaque_fixed(xdr, stateid, NFS4_STATEID_SIZE);
}


static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			      void *data)
{
	struct nfs_closeres *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	if (res->lr_res) {
		status = decode_layoutreturn(xdr, res->lr_res);
		res->lr_ret = status;
		if (status)
			goto out;
	}
	if (res->fattr != NULL) {
		status = decode_getfattr(xdr, res->fattr, res->server);
		if (status != 0)
			goto out;
	}
	status = decode_close(xdr, res);
out:
	return status;
}


static int _nfs4_proc_commit(struct file *dst, struct nfs_commitargs *args,
				struct nfs_commitres *res)
{
	struct inode *dst_inode = file_inode(dst);
	struct nfs_server *server = NFS_SERVER(dst_inode);
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_COMMIT],
		.rpc_argp = args,
		.rpc_resp = res,
	};

	args->fh = NFS_FH(dst_inode);
	return nfs4_call_sync(server->client, server, &msg,
			&args->seq_args, &res->seq_res, 1);
}


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 int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			       void *data)
{
	struct nfs4_lookup_res *res = data;
	struct compound_hdr hdr;
	int status;

	status = decode_compound_hdr(xdr, &hdr);
	if (status)
		goto out;
	status = decode_sequence(xdr, &res->seq_res, rqstp);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	status = decode_lookup(xdr);
	if (status)
		goto out;
	status = decode_getfh(xdr, res->fh);
	if (status)
		goto out;
	status = decode_getfattr_label(xdr, res->fattr, res->label, res->server);
out:
	return status;
}


static int decode_attr_maxlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *maxlink)
{
	__be32 *p;
	int status = 0;

	*maxlink = 1;
	if (unlikely(bitmap[0] & (FATTR4_WORD0_MAXLINK - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_MAXLINK)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*maxlink = be32_to_cpup(p);
		bitmap[0] &= ~FATTR4_WORD0_MAXLINK;
	}
	dprintk("%s: maxlink=%u\n", __func__, *maxlink);
	return status;
}


int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp, bool sync)
{
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
		.rpc_argp = &lrp->args,
		.rpc_resp = &lrp->res,
		.rpc_cred = lrp->cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = NFS_SERVER(lrp->args.inode)->client,
		.rpc_message = &msg,
		.callback_ops = &nfs4_layoutreturn_call_ops,
		.callback_data = lrp,
	};
	int status = 0;

	nfs4_state_protect(NFS_SERVER(lrp->args.inode)->nfs_client,
			NFS_SP4_MACH_CRED_PNFS_CLEANUP,
			&task_setup_data.rpc_client, &msg);

	dprintk("--> %s\n", __func__);
	if (!sync) {
		lrp->inode = nfs_igrab_and_active(lrp->args.inode);
		if (!lrp->inode) {
			nfs4_layoutreturn_release(lrp);
			return -EAGAIN;
		}
		task_setup_data.flags |= RPC_TASK_ASYNC;
	}
	nfs4_init_sequence(&lrp->args.seq_args, &lrp->res.seq_res, 1, 0);
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	if (sync)
		status = task->tk_status;
	trace_nfs4_layoutreturn(lrp->args.inode, &lrp->args.stateid, status);
	dprintk("<-- %s status=%d\n", __func__, status);
	rpc_put_task(task);
	return status;
}


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 void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_ACCESS, decode_access_maxsz, hdr);
	encode_uint32(xdr, access);
}


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 void encode_write(struct xdr_stream *xdr, const struct nfs_pgio_args *args,
			 struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_WRITE, decode_write_maxsz, hdr);
	encode_nfs4_stateid(xdr, &args->stateid);

	p = reserve_space(xdr, 16);
	p = xdr_encode_hyper(p, args->offset);
	*p++ = cpu_to_be32(args->stable);
	*p = cpu_to_be32(args->count);

	xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
}


static int decode_attr_nlink(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *nlink)
{
	__be32 *p;
	int ret = 0;

	*nlink = 1;
	if (unlikely(bitmap[1] & (FATTR4_WORD1_NUMLINKS - 1U)))
		return -EIO;
	if (likely(bitmap[1] & FATTR4_WORD1_NUMLINKS)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*nlink = be32_to_cpup(p);
		bitmap[1] &= ~FATTR4_WORD1_NUMLINKS;
		ret = NFS_ATTR_FATTR_NLINK;
	}
	dprintk("%s: nlink=%u\n", __func__, (unsigned int)*nlink);
	return ret;
}


static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;

	nfs40_sequence_done(task, &data->c_res.seq_res);

	data->rpc_status = task->tk_status;
	if (data->rpc_status == 0) {
		nfs4_stateid_copy(&data->o_res.stateid, &data->c_res.stateid);
		nfs_confirm_seqid(&data->owner->so_seqid, 0);
		renew_lease(data->o_res.server, data->timestamp);
		data->rpc_done = true;
	}
}


static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_SETCLIENTID, decode_setclientid_maxsz, hdr);
	encode_nfs4_verifier(xdr, setclientid->sc_verifier);

	encode_string(xdr, strlen(setclientid->sc_clnt->cl_owner_id),
			setclientid->sc_clnt->cl_owner_id);
	p = reserve_space(xdr, 4);
	*p = cpu_to_be32(setclientid->sc_prog);
	encode_string(xdr, setclientid->sc_netid_len, setclientid->sc_netid);
	encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
	p = reserve_space(xdr, 4);
	*p = cpu_to_be32(setclientid->sc_clnt->cl_cb_ident);
}


static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{
	int status;

	status = nfs41_check_delegation_stateid(state);
	if (status != NFS_OK)
		return status;
	nfs41_delegation_recover_stateid(state);

	status = nfs41_check_expired_locks(state);
	if (status != NFS_OK)
		return status;
	status = nfs41_check_open_stateid(state);
	if (status != NFS_OK)
		status = nfs4_open_expired(sp, state);
	return status;
}


nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
		  struct iattr *sattr)
{
	struct inode *inode = d_inode(dentry);
	const struct cred *cred = NULL;
	struct nfs_open_context *ctx = NULL;
	struct nfs4_label *label = NULL;
	int status;

	if (pnfs_ld_layoutret_on_setattr(inode) &&
	    sattr->ia_valid & ATTR_SIZE &&
	    sattr->ia_size < i_size_read(inode))
		pnfs_commit_and_return_layout(inode);

	nfs_fattr_init(fattr);

	/* Deal with open(O_TRUNC) */
	if (sattr->ia_valid & ATTR_OPEN)
		sattr->ia_valid &= ~(ATTR_MTIME|ATTR_CTIME);

	/* Optimization: if the end result is no change, don't RPC */
	if ((sattr->ia_valid & ~(ATTR_FILE|ATTR_OPEN)) == 0)
		return 0;

	/* Search for an existing open(O_WRITE) file */
	if (sattr->ia_valid & ATTR_FILE) {

		ctx = nfs_file_open_context(sattr->ia_file);
		if (ctx)
			cred = ctx->cred;
	}

	label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
	if (IS_ERR(label))
		return PTR_ERR(label);

	/* Return any delegations if we're going to change ACLs */
	if ((sattr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
		nfs4_inode_make_writeable(inode);

	status = nfs4_do_setattr(inode, cred, fattr, sattr, ctx, NULL, label);
	if (status == 0) {
		nfs_setattr_update_inode(inode, sattr, fattr);
		nfs_setsecurity(inode, fattr, label);
	}
	nfs4_label_free(label);
	return status;
}


static int decode_attr_clone_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
				     uint32_t *res)
{
	__be32 *p;

	dprintk("%s: bitmap is %x\n", __func__, bitmap[2]);
	*res = 0;
	if (bitmap[2] & FATTR4_WORD2_CLONE_BLKSIZE) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*res = be32_to_cpup(p);
		bitmap[2] &= ~FATTR4_WORD2_CLONE_BLKSIZE;
	}
	return 0;
}


static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_RENAME, decode_rename_maxsz, hdr);
	encode_string(xdr, oldname->len, oldname->name);
	encode_string(xdr, newname->len, newname->name);
}


static int _nfs41_proc_fsid_present(struct inode *inode, const struct cred *cred)
{
	struct nfs_server *server = NFS_SERVER(inode);
	struct rpc_clnt *clnt = server->client;
	struct nfs4_fsid_present_arg args = {
		.fh		= NFS_FH(inode),
	};
	struct nfs4_fsid_present_res res = {
	};
	struct rpc_message msg = {
		.rpc_proc	= &nfs4_procedures[NFSPROC4_CLNT_FSID_PRESENT],
		.rpc_argp	= &args,
		.rpc_resp	= &res,
		.rpc_cred	= cred,
	};
	int status;

	res.fh = nfs_alloc_fhandle();
	if (res.fh == NULL)
		return -ENOMEM;

	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 1);
	status = nfs4_call_sync_sequence(clnt, server, &msg,
						&args.seq_args, &res.seq_res);
	nfs_free_fhandle(res.fh);
	if (status == NFS4_OK &&
	    res.seq_res.sr_status_flags & SEQ4_STATUS_LEASE_MOVED)
		status = -NFS4ERR_LEASE_MOVED;
	return status;
}


static int nfs41_check_open_stateid(struct nfs4_state *state)
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	nfs4_stateid *stateid = &state->open_stateid;
	const struct cred *cred = state->owner->so_cred;
	int status;

	if (test_bit(NFS_OPEN_STATE, &state->flags) == 0)
		return -NFS4ERR_BAD_STATEID;
	status = nfs41_test_and_free_expired_stateid(server, stateid, cred);
	trace_nfs4_test_open_stateid(state, NULL, status);
	if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) {
		nfs_state_clear_open_state_flags(state);
		stateid->type = NFS4_INVALID_STATEID_TYPE;
		return status;
	}
	if (nfs_open_stateid_recover_openmode(state))
		return -NFS4ERR_OPENMODE;
	return NFS_OK;
}


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;
}


static int decode_no_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
{
	__be32 *p;
	uint32_t why_no_delegation;

	p = xdr_inline_decode(xdr, 4);
	if (unlikely(!p))
		return -EIO;
	why_no_delegation = be32_to_cpup(p);
	switch (why_no_delegation) {
		case WND4_CONTENTION:
		case WND4_RESOURCE:
			xdr_inline_decode(xdr, 4);
			/* Ignore for now */
	}
	return 0;
}


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;
		/* Fall through */
	default:
		return 0;
	}
	nfs_mark_delegation_referenced(delegation);
	return 1;
}


static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
{
	WARN_ON_ONCE(xdr_stream_encode_opaque(xdr, str, len) < 0);
}


static void encode_renew(struct xdr_stream *xdr, clientid4 clid,
			 struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_RENEW, decode_renew_maxsz, hdr);
	encode_uint64(xdr, clid);
}


static int nfs4_proc_rmdir(struct inode *dir, const struct qstr *name)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;

	do {
		err = _nfs4_proc_remove(dir, name, NF4DIR);
		trace_nfs4_remove(dir, name, err);
		err = nfs4_handle_exception(NFS_SERVER(dir), err,
				&exception);
	} while (exception.retry);
	return err;
}


static int nfs4_proc_pgio_rpc_prepare(struct rpc_task *task,
				      struct nfs_pgio_header *hdr)
{
	if (nfs4_setup_sequence(NFS_SERVER(hdr->inode)->nfs_client,
			&hdr->args.seq_args,
			&hdr->res.seq_res,
			task))
		return 0;
	if (nfs4_set_rw_stateid(&hdr->args.stateid, hdr->args.context,
				hdr->args.lock_context,
				hdr->rw_mode) == -EIO)
		return -EIO;
	if (unlikely(test_bit(NFS_CONTEXT_BAD, &hdr->args.context->flags)))
		return -EIO;
	return 0;
}


nfs4_run_exchange_id(struct nfs_client *clp, const struct cred *cred,
			u32 sp4_how, struct rpc_xprt *xprt)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
		.rpc_cred = cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = clp->cl_rpcclient,
		.callback_ops = &nfs4_exchange_id_call_ops,
		.rpc_message = &msg,
		.flags = RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN,
	};
	struct nfs41_exchange_id_data *calldata;
	int status;

	if (!refcount_inc_not_zero(&clp->cl_count))
		return ERR_PTR(-EIO);

	status = -ENOMEM;
	calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
	if (!calldata)
		goto out;

	nfs4_init_boot_verifier(clp, &calldata->args.verifier);

	status = nfs4_init_uniform_client_string(clp);
	if (status)
		goto out_calldata;

	calldata->res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
						GFP_NOFS);
	status = -ENOMEM;
	if (unlikely(calldata->res.server_owner == NULL))
		goto out_calldata;

	calldata->res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
					GFP_NOFS);
	if (unlikely(calldata->res.server_scope == NULL))
		goto out_server_owner;

	calldata->res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
	if (unlikely(calldata->res.impl_id == NULL))
		goto out_server_scope;

	switch (sp4_how) {
	case SP4_NONE:
		calldata->args.state_protect.how = SP4_NONE;
		break;

	case SP4_MACH_CRED:
		calldata->args.state_protect = nfs4_sp4_mach_cred_request;
		break;

	default:
		/* unsupported! */
		WARN_ON_ONCE(1);
		status = -EINVAL;
		goto out_impl_id;
	}
	if (xprt) {
		task_setup_data.rpc_xprt = xprt;
		task_setup_data.flags |= RPC_TASK_SOFTCONN;
		memcpy(calldata->args.verifier.data, clp->cl_confirm.data,
				sizeof(calldata->args.verifier.data));
	}
	calldata->args.client = clp;
	calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
	EXCHGID4_FLAG_BIND_PRINC_STATEID;
#ifdef CONFIG_NFS_V4_1_MIGRATION
	calldata->args.flags |= EXCHGID4_FLAG_SUPP_MOVED_MIGR;
#endif
	msg.rpc_argp = &calldata->args;
	msg.rpc_resp = &calldata->res;
	task_setup_data.callback_data = calldata;

	return rpc_run_task(&task_setup_data);

out_impl_id:
	kfree(calldata->res.impl_id);
out_server_scope:
	kfree(calldata->res.server_scope);
out_server_owner:
	kfree(calldata->res.server_owner);
out_calldata:
	kfree(calldata);
out:
	nfs_put_client(clp);
	return ERR_PTR(status);
}


static void nfs41_free_stateid_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs_free_stateid_data *data = calldata;
	nfs4_setup_sequence(data->server->nfs_client,
			&data->args.seq_args,
			&data->res.seq_res,
			task);
}