HeaderLookupTable_t::initCache()
{
    idCache.resize(TOTAL_KEYWORDS);
    for (int j = MIN_HASH_VALUE; j <= MAX_HASH_VALUE; ++j) { //MAX_HASH_VALUE is exported by gperf
        if (HttpHeaderDefinitionsTable[j].name[0] != '\0') { //some slots are empty
            idCache[static_cast<int>(HttpHeaderDefinitionsTable[j].id)] =
                & HttpHeaderDefinitionsTable[j];
        }
    }
    //check after the fact. The cache array must be full
    for (auto e : idCache) {
        assert(e->name);
    }
}


static int decode_destroy_clientid(struct xdr_stream *xdr, void *dummy)
{
	return decode_op_hdr(xdr, OP_DESTROY_CLIENTID);
}

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>

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}

static struct {
	int stat;
	int errno;
} nfs_errtbl[] = {
	{ NFS4_OK,		0		},
	{ NFS4ERR_PERM,		-EPERM		},
	{ NFS4ERR_NOENT,	-ENOENT		},
	{ NFS4ERR_IO,		-errno_NFSERR_IO},
	{ NFS4ERR_NXIO,		-ENXIO		},
	{ NFS4ERR_ACCESS,	-EACCES		},
	{ NFS4ERR_EXIST,	-EEXIST		},
	{ NFS4ERR_XDEV,		-EXDEV		},
	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
	{ NFS4ERR_ISDIR,	-EISDIR		},
	{ NFS4ERR_INVAL,	-EINVAL		},
	{ NFS4ERR_FBIG,		-EFBIG		},
	{ NFS4ERR_NOSPC,	-ENOSPC		},
	{ NFS4ERR_ROFS,		-EROFS		},
	{ NFS4ERR_MLINK,	-EMLINK		},
	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
	{ NFS4ERR_DQUOT,	-EDQUOT		},
	{ NFS4ERR_STALE,	-ESTALE		},
	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
	{ NFS4ERR_SERVERFAULT,	-EREMOTEIO	},
	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
	{ NFS4ERR_LOCKED,	-EAGAIN		},
	{ NFS4ERR_SYMLINK,	-ELOOP		},
	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
	{ NFS4ERR_NOXATTR,	-ENODATA	},
	{ NFS4ERR_XATTR2BIG,	-E2BIG		},
	{ -1,			-EIO		}
};


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

struct nfs4_opendata

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

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

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

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_prepare = nfs4_open_confirm_prepare,
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

struct nfs4_closedata {
	struct inode *inode;
	struct nfs4_state *state;
	struct nfs_closeargs arg;
	struct nfs_closeres res;
	struct {
		struct nfs4_layoutreturn_args arg;
		struct nfs4_layoutreturn_res res;
		struct nfs4_xdr_opaque_data ld_private;
		u32 roc_barrier;
		bool roc;
	} lr;
	struct nfs_fattr fattr;
	unsigned long timestamp;
}


static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
		struct nfs_pathconf *pathconf)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;

	do {
		err = nfs4_handle_exception(server,
				_nfs4_proc_pathconf(server, fhandle, pathconf),
				&exception);
	} while (exception.retry);
	return err;
}


static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
		struct nfs_fattr *fattr, struct nfs_fh *fh,
		struct nfs4_fs_locations *fs_loc, struct nfs4_label *label,
		const struct nfs_server *server)
{
	int status;
	umode_t fmode = 0;
	uint32_t type;
	int32_t err;

	status = decode_attr_type(xdr, bitmap, &type);
	if (status < 0)
		goto xdr_error;
	fattr->mode = 0;
	if (status != 0) {
		fattr->mode |= nfs_type2fmt[type];
		fattr->valid |= status;
	}

	status = decode_attr_change(xdr, bitmap, &fattr->change_attr);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_size(xdr, bitmap, &fattr->size);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_fsid(xdr, bitmap, &fattr->fsid);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	err = 0;
	status = decode_attr_error(xdr, bitmap, &err);
	if (status < 0)
		goto xdr_error;

	status = decode_attr_filehandle(xdr, bitmap, fh);
	if (status < 0)
		goto xdr_error;

	status = decode_attr_fileid(xdr, bitmap, &fattr->fileid);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_fs_locations(xdr, bitmap, fs_loc);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = -EIO;
	if (unlikely(bitmap[0]))
		goto xdr_error;

	status = decode_attr_mode(xdr, bitmap, &fmode);
	if (status < 0)
		goto xdr_error;
	if (status != 0) {
		fattr->mode |= fmode;
		fattr->valid |= status;
	}

	status = decode_attr_nlink(xdr, bitmap, &fattr->nlink);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_owner(xdr, bitmap, server, &fattr->uid, fattr->owner_name);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_group(xdr, bitmap, server, &fattr->gid, fattr->group_name);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_rdev(xdr, bitmap, &fattr->rdev);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_space_used(xdr, bitmap, &fattr->du.nfs3.used);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_time_access(xdr, bitmap, &fattr->atime);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_time_metadata(xdr, bitmap, &fattr->ctime);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = decode_attr_mounted_on_fileid(xdr, bitmap, &fattr->mounted_on_fileid);
	if (status < 0)
		goto xdr_error;
	fattr->valid |= status;

	status = -EIO;
	if (unlikely(bitmap[1]))
		goto xdr_error;

	status = decode_attr_mdsthreshold(xdr, bitmap, fattr->mdsthreshold);
	if (status < 0)
		goto xdr_error;

	if (label) {
		status = decode_attr_security_label(xdr, bitmap, label);
		if (status < 0)
			goto xdr_error;
		fattr->valid |= status;
	}

xdr_error:
	dprintk("%s: xdr returned %d\n", __func__, -status);
	return status;
}

#define errno_NFSERR_IO		EIO

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
				1 /* layout type */

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static void nfs4_xdr_enc_layoutreturn(struct rpc_rqst *req,
				      struct xdr_stream *xdr,
				      const void *data)
{
	const struct nfs4_layoutreturn_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, NFS_FH(args->inode), &hdr);
	encode_layoutreturn(xdr, args, &hdr);
	encode_nops(&hdr);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static int nfs4_opendata_access(const struct cred *cred,
				struct nfs4_opendata *opendata,
				struct nfs4_state *state, fmode_t fmode,
				int openflags)
{
	struct nfs_access_entry cache;
	u32 mask, flags;

	/* access call failed or for some reason the server doesn't
	 * support any access modes -- defer access call until later */
	if (opendata->o_res.access_supported == 0)
		return 0;

	mask = 0;
	/*
	 * Use openflags to check for exec, because fmode won't
	 * always have FMODE_EXEC set when file open for exec.
	 */
	if (openflags & __FMODE_EXEC) {
		/* ONLY check for exec rights */
		if (S_ISDIR(state->inode->i_mode))
			mask = NFS4_ACCESS_LOOKUP;
		else
			mask = NFS4_ACCESS_EXECUTE;
	} else if ((fmode & FMODE_READ) && !opendata->file_created)
		mask = NFS4_ACCESS_READ;

	cache.cred = cred;
	nfs_access_set_mask(&cache, opendata->o_res.access_result);
	nfs_access_add_cache(state->inode, &cache);

	flags = NFS4_ACCESS_READ | NFS4_ACCESS_EXECUTE | NFS4_ACCESS_LOOKUP;
	if ((mask & ~cache.mask & flags) == 0)
		return 0;

	return -EACCES;
}

struct nfs4_opendata

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

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


static void nfs4_xdr_enc_get_lease_time(struct rpc_rqst *req,
					struct xdr_stream *xdr,
					const void *data)
{
	const struct nfs4_get_lease_time_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(&args->la_seq_args),
	};
	const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME };

	encode_compound_hdr(xdr, req, &hdr);
	encode_sequence(xdr, &args->la_seq_args, &hdr);
	encode_putrootfh(xdr, &hdr);
	encode_fsinfo(xdr, lease_bitmap, &hdr);
	encode_nops(&hdr);
}


static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl)
{
	uint64_t offset, length, clientid;
	__be32 *p;
	uint32_t namelen, type;

	p = xdr_inline_decode(xdr, 32); /* read 32 bytes */
	if (unlikely(!p))
		return -EIO;
	p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */
	p = xdr_decode_hyper(p, &length);
	type = be32_to_cpup(p++); /* 4 byte read */
	if (fl != NULL) { /* manipulate file lock */
		fl->fl_start = (loff_t)offset;
		fl->fl_end = fl->fl_start + (loff_t)length - 1;
		if (length == ~(uint64_t)0)
			fl->fl_end = OFFSET_MAX;
		fl->fl_type = F_WRLCK;
		if (type & 1)
			fl->fl_type = F_RDLCK;
		fl->fl_pid = 0;
	}
	p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */
	namelen = be32_to_cpup(p); /* read 4 bytes */  /* have read all 32 bytes now */
	p = xdr_inline_decode(xdr, namelen); /* variable size field */
	if (likely(!p))
		return -EIO;
	return -NFS4ERR_DENIED;
}

#define errno_NFSERR_IO		EIO

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define decode_setclientid_maxsz \
				(op_decode_hdr_maxsz + \
				2 /* clientid */

#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
				1 /* layout type */

#define encode_layoutcommit_maxsz (op_encode_hdr_maxsz +          \
				2 /* offset */

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
{
	int status;

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

#define errno_NFSERR_IO		EIO

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}

static struct {
	int stat;
	int errno;
} nfs_errtbl[] = {
	{ NFS4_OK,		0		},
	{ NFS4ERR_PERM,		-EPERM		},
	{ NFS4ERR_NOENT,	-ENOENT		},
	{ NFS4ERR_IO,		-errno_NFSERR_IO},
	{ NFS4ERR_NXIO,		-ENXIO		},
	{ NFS4ERR_ACCESS,	-EACCES		},
	{ NFS4ERR_EXIST,	-EEXIST		},
	{ NFS4ERR_XDEV,		-EXDEV		},
	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
	{ NFS4ERR_ISDIR,	-EISDIR		},
	{ NFS4ERR_INVAL,	-EINVAL		},
	{ NFS4ERR_FBIG,		-EFBIG		},
	{ NFS4ERR_NOSPC,	-ENOSPC		},
	{ NFS4ERR_ROFS,		-EROFS		},
	{ NFS4ERR_MLINK,	-EMLINK		},
	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
	{ NFS4ERR_DQUOT,	-EDQUOT		},
	{ NFS4ERR_STALE,	-ESTALE		},
	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
	{ NFS4ERR_SERVERFAULT,	-EREMOTEIO	},
	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
	{ NFS4ERR_LOCKED,	-EAGAIN		},
	{ NFS4ERR_SYMLINK,	-ELOOP		},
	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
	{ NFS4ERR_NOXATTR,	-ENODATA	},
	{ NFS4ERR_XATTR2BIG,	-E2BIG		},
	{ -1,			-EIO		}
};


static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
		struct nfs4_state *state)
{
	__be32 seqid_open;
	u32 dst_seqid;
	bool ret;
	int seq;

	for (;;) {
		ret = false;
		if (!nfs4_valid_open_stateid(state))
			break;
		seq = read_seqbegin(&state->seqlock);
		if (!nfs4_state_match_open_stateid_other(state, dst)) {
			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 = cpu_to_be32(dst_seqid + 1);
		else
			dst->seqid = seqid_open;
		ret = true;
		break;
	}

	return ret;
}


static int _nfs4_do_open(struct inode *dir,
			struct nfs_open_context *ctx,
			int flags,
			const struct nfs4_open_createattrs *c,
			int *opened)
{
	struct nfs4_state_owner  *sp;
	struct nfs4_state     *state = NULL;
	struct nfs_server       *server = NFS_SERVER(dir);
	struct nfs4_opendata *opendata;
	struct dentry *dentry = ctx->dentry;
	const struct cred *cred = ctx->cred;
	struct nfs4_threshold **ctx_th = &ctx->mdsthreshold;
	fmode_t fmode = _nfs4_ctx_to_openmode(ctx);
	enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
	struct iattr *sattr = c->sattr;
	struct nfs4_label *label = c->label;
	struct nfs4_label *olabel = NULL;
	int status;

	/* Protect against reboot recovery conflicts */
	status = -ENOMEM;
	sp = nfs4_get_state_owner(server, cred, GFP_KERNEL);
	if (sp == NULL) {
		dprintk("nfs4_do_open: nfs4_get_state_owner failed!\n");
		goto out_err;
	}
	status = nfs4_client_recover_expired_lease(server->nfs_client);
	if (status != 0)
		goto err_put_state_owner;
	if (d_really_is_positive(dentry))
		nfs4_return_incompatible_delegation(d_inode(dentry), fmode);
	status = -ENOMEM;
	if (d_really_is_positive(dentry))
		claim = NFS4_OPEN_CLAIM_FH;
	opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags,
			c, claim, GFP_KERNEL);
	if (opendata == NULL)
		goto err_put_state_owner;

	if (label) {
		olabel = nfs4_label_alloc(server, GFP_KERNEL);
		if (IS_ERR(olabel)) {
			status = PTR_ERR(olabel);
			goto err_opendata_put;
		}
	}

	if (server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
		if (!opendata->f_attr.mdsthreshold) {
			opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
			if (!opendata->f_attr.mdsthreshold)
				goto err_free_label;
		}
		opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
	}
	if (d_really_is_positive(dentry))
		opendata->state = nfs4_get_open_state(d_inode(dentry), sp);

	status = _nfs4_open_and_get_state(opendata, flags, ctx);
	if (status != 0)
		goto err_free_label;
	state = ctx->state;

	if ((opendata->o_arg.open_flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL) &&
	    (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
		unsigned attrs = nfs4_exclusive_attrset(opendata, sattr, &label);
		/*
		 * send create attributes which was not set by open
		 * with an extra setattr.
		 */
		if (attrs || label) {
			unsigned ia_old = sattr->ia_valid;

			sattr->ia_valid = attrs;
			nfs_fattr_init(opendata->o_res.f_attr);
			status = nfs4_do_setattr(state->inode, cred,
					opendata->o_res.f_attr, sattr,
					ctx, label, olabel);
			if (status == 0) {
				nfs_setattr_update_inode(state->inode, sattr,
						opendata->o_res.f_attr);
				nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
			}
			sattr->ia_valid = ia_old;
		}
	}
	if (opened && opendata->file_created)
		*opened = 1;

	if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server)) {
		*ctx_th = opendata->f_attr.mdsthreshold;
		opendata->f_attr.mdsthreshold = NULL;
	}

	nfs4_label_free(olabel);

	nfs4_opendata_put(opendata);
	nfs4_put_state_owner(sp);
	return 0;
err_free_label:
	nfs4_label_free(olabel);
err_opendata_put:
	nfs4_opendata_put(opendata);
err_put_state_owner:
	nfs4_put_state_owner(sp);
out_err:
	return status;
}


nfs4_bind_one_conn_to_session_done(struct rpc_task *task, void *calldata)
{
	struct nfs41_bind_conn_to_session_args *args = task->tk_msg.rpc_argp;
	struct nfs41_bind_conn_to_session_res *res = task->tk_msg.rpc_resp;
	struct nfs_client *clp = args->client;

	switch (task->tk_status) {
	case -NFS4ERR_BADSESSION:
	case -NFS4ERR_DEADSESSION:
		nfs4_schedule_session_recovery(clp->cl_session,
				task->tk_status);
	}
	if (args->dir == NFS4_CDFC4_FORE_OR_BOTH &&
			res->dir != NFS4_CDFS4_BOTH) {
		rpc_task_close_connection(task);
		if (args->retries++ < MAX_BIND_CONN_TO_SESSION_RETRIES)
			rpc_restart_call(task);
	}
}

struct nfs4_opendata

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_prepare = nfs4_open_confirm_prepare,
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

struct nfs4_closedata {
	struct inode *inode;
	struct nfs4_state *state;
	struct nfs_closeargs arg;
	struct nfs_closeres res;
	struct {
		struct nfs4_layoutreturn_args arg;
		struct nfs4_layoutreturn_res res;
		struct nfs4_xdr_opaque_data ld_private;
		u32 roc_barrier;
		bool roc;
	} lr;
	struct nfs_fattr fattr;
	unsigned long timestamp;
}

static const struct rpc_call_ops nfs4_close_ops = {
	.rpc_call_prepare = nfs4_close_prepare,
	.rpc_call_done = nfs4_close_done,
	.rpc_release = nfs4_free_closedata,
};

struct nfs4_createdata {
	struct rpc_message msg;
	struct nfs4_create_arg arg;
	struct nfs4_create_res res;
	struct nfs_fh fh;
	struct nfs_fattr fattr;
	struct nfs4_label *label;
}

struct nfs4_renewdata {
	struct nfs_client	*client;
	unsigned long		timestamp;
}

static const struct rpc_call_ops nfs4_renew_ops = {
	.rpc_call_done = nfs4_renew_done,
	.rpc_release = nfs4_renew_release,
};

struct nfs4_cached_acl {
	int cached;
	size_t len;
	char data[];
}

static const struct rpc_call_ops nfs4_setclientid_ops = {
	.rpc_call_done = nfs4_setclientid_done,
};


static int _nfs4_proc_open_confirm(struct nfs4_opendata *data)
{
	struct nfs_server *server = NFS_SERVER(d_inode(data->dir));
	struct rpc_task *task;
	struct  rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_CONFIRM],
		.rpc_argp = &data->c_arg,
		.rpc_resp = &data->c_res,
		.rpc_cred = data->owner->so_cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = &nfs4_open_confirm_ops,
		.callback_data = data,
		.workqueue = nfsiod_workqueue,
		.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
	};
	int status;

	nfs4_init_sequence(&data->c_arg.seq_args, &data->c_res.seq_res, 1,
				data->is_recover);
	kref_get(&data->kref);
	data->rpc_done = false;
	data->rpc_status = 0;
	data->timestamp = jiffies;
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	status = rpc_wait_for_completion_task(task);
	if (status != 0) {
		data->cancelled = true;
		smp_wmb();
	} else
		status = data->rpc_status;
	rpc_put_task(task);
	return status;
}


static int decode_attr_layout_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_LAYOUT_BLKSIZE) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		*res = be32_to_cpup(p);
		bitmap[2] &= ~FATTR4_WORD2_LAYOUT_BLKSIZE;
	}
	return 0;
}


nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
		    void *data)
{
	struct nfs_getaclres *res = data;
	struct compound_hdr hdr;
	int status;

	if (res->acl_scratch != NULL) {
		void *p = page_address(res->acl_scratch);
		xdr_set_scratch_buffer(xdr, p, PAGE_SIZE);
	}
	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_getacl(xdr, rqstp, res);

out:
	return status;
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}

static struct {
	int stat;
	int errno;
} nfs_errtbl[] = {
	{ NFS4_OK,		0		},
	{ NFS4ERR_PERM,		-EPERM		},
	{ NFS4ERR_NOENT,	-ENOENT		},
	{ NFS4ERR_IO,		-errno_NFSERR_IO},
	{ NFS4ERR_NXIO,		-ENXIO		},
	{ NFS4ERR_ACCESS,	-EACCES		},
	{ NFS4ERR_EXIST,	-EEXIST		},
	{ NFS4ERR_XDEV,		-EXDEV		},
	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
	{ NFS4ERR_ISDIR,	-EISDIR		},
	{ NFS4ERR_INVAL,	-EINVAL		},
	{ NFS4ERR_FBIG,		-EFBIG		},
	{ NFS4ERR_NOSPC,	-ENOSPC		},
	{ NFS4ERR_ROFS,		-EROFS		},
	{ NFS4ERR_MLINK,	-EMLINK		},
	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
	{ NFS4ERR_DQUOT,	-EDQUOT		},
	{ NFS4ERR_STALE,	-ESTALE		},
	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
	{ NFS4ERR_SERVERFAULT,	-EREMOTEIO	},
	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
	{ NFS4ERR_LOCKED,	-EAGAIN		},
	{ NFS4ERR_SYMLINK,	-ELOOP		},
	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
	{ NFS4ERR_NOXATTR,	-ENODATA	},
	{ NFS4ERR_XATTR2BIG,	-E2BIG		},
	{ -1,			-EIO		}
};


static void nfs4_proc_unlink_setup(struct rpc_message *msg,
		struct dentry *dentry,
		struct inode *inode)
{
	struct nfs_removeargs *args = msg->rpc_argp;
	struct nfs_removeres *res = msg->rpc_resp;

	res->server = NFS_SB(dentry->d_sb);
	msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
	nfs4_init_sequence(&args->seq_args, &res->seq_res, 1, 0);

	nfs_fattr_init(res->dir_attr);

	if (inode)
		nfs4_inode_return_delegation(inode);
}


static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp,
				    struct xdr_stream *xdr,
				    void *data)
{
	struct nfs_openres *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_open(xdr, res);
	if (status)
		goto out;
	if (res->access_request)
		decode_access(xdr, &res->access_supported, &res->access_result);
	decode_getfattr(xdr, res->f_attr, res->server);
	if (res->lg_res)
		decode_layoutget(xdr, rqstp, res->lg_res);
out:
	return status;
}


static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req,
				     struct xdr_stream *xdr,
				     void *data)
{
	struct nfs4_fs_locations_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, req);
	if (status)
		goto out;
	status = decode_putfh(xdr);
	if (status)
		goto out;
	if (res->migration) {
		xdr_enter_page(xdr, PAGE_SIZE);
		status = decode_getfattr_generic(xdr,
					&res->fs_locations->fattr,
					 NULL, res->fs_locations,
					 NULL, res->fs_locations->server);
		if (status)
			goto out;
		if (res->renew)
			status = decode_renew(xdr);
	} else {
		status = decode_lookup(xdr);
		if (status)
			goto out;
		xdr_enter_page(xdr, PAGE_SIZE);
		status = decode_getfattr_generic(xdr,
					&res->fs_locations->fattr,
					 NULL, res->fs_locations,
					 NULL, res->fs_locations->server);
	}
out:
	return status;
}


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

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_prepare = nfs4_open_confirm_prepare,
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

struct nfs4_closedata {
	struct inode *inode;
	struct nfs4_state *state;
	struct nfs_closeargs arg;
	struct nfs_closeres res;
	struct {
		struct nfs4_layoutreturn_args arg;
		struct nfs4_layoutreturn_res res;
		struct nfs4_xdr_opaque_data ld_private;
		u32 roc_barrier;
		bool roc;
	} lr;
	struct nfs_fattr fattr;
	unsigned long timestamp;
}

static const struct rpc_call_ops nfs4_close_ops = {
	.rpc_call_prepare = nfs4_close_prepare,
	.rpc_call_done = nfs4_close_done,
	.rpc_release = nfs4_free_closedata,
};

struct nfs4_renewdata {
	struct nfs_client	*client;
	unsigned long		timestamp;
}


static inline int nfs4_server_supports_acls(struct nfs_server *server)
{
	return server->caps & NFS_CAP_ACLS;
}


_nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
		    struct nfs_fsinfo *info,
		    struct nfs4_secinfo_flavors *flavors, bool use_integrity)
{
	struct nfs41_secinfo_no_name_args args = {
		.style = SECINFO_STYLE_CURRENT_FH,
	};
	struct nfs4_secinfo_res res = {
		.flavors = flavors,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SECINFO_NO_NAME],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	struct rpc_clnt *clnt = server->client;
	struct nfs4_call_sync_data data = {
		.seq_server = server,
		.seq_args = &args.seq_args,
		.seq_res = &res.seq_res,
	};
	struct rpc_task_setup task_setup = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = server->nfs_client->cl_mvops->call_sync_ops,
		.callback_data = &data,
		.flags = RPC_TASK_NO_ROUND_ROBIN,
	};
	const struct cred *cred = NULL;
	int status;

	if (use_integrity) {
		clnt = server->nfs_client->cl_rpcclient;
		task_setup.rpc_client = clnt;

		cred = nfs4_get_clid_cred(server->nfs_client);
		msg.rpc_cred = cred;
	}

	dprintk("--> %s\n", __func__);
	nfs4_init_sequence(&args.seq_args, &res.seq_res, 0, 0);
	status = nfs4_call_sync_custom(&task_setup);
	dprintk("<-- %s status=%d\n", __func__, status);

	put_cred(cred);

	return status;
}


int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
{
	struct nfs4_get_lease_time_args args;
	struct nfs4_get_lease_time_res res = {
		.lr_fsinfo = fsinfo,
	};
	struct nfs4_get_lease_time_data data = {
		.args = &args,
		.res = &res,
		.clp = clp,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GET_LEASE_TIME],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	struct rpc_task_setup task_setup = {
		.rpc_client = clp->cl_rpcclient,
		.rpc_message = &msg,
		.callback_ops = &nfs4_get_lease_time_ops,
		.callback_data = &data,
		.flags = RPC_TASK_TIMEOUT,
	};

	nfs4_init_sequence(&args.la_seq_args, &res.lr_seq_res, 0, 1);
	return nfs4_call_sync_custom(&task_setup);
}


nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
{
	const struct cred *cred = lsp->ls_state->owner->so_cred;

	nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
	nfs4_free_lock_state(server, lsp);
}


static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
					     struct xdr_stream *xdr,
					     const void *data)
{
	const struct nfs4_setclientid_res *arg = data;
	struct compound_hdr hdr = {
		.nops	= 0,
	};

	encode_compound_hdr(xdr, req, &hdr);
	encode_setclientid_confirm(xdr, arg, &hdr);
	encode_nops(&hdr);
}


static int _nfs4_proc_readlink(struct inode *inode, struct page *page,
		unsigned int pgbase, unsigned int pglen)
{
	struct nfs4_readlink args = {
		.fh       = NFS_FH(inode),
		.pgbase	  = pgbase,
		.pglen    = pglen,
		.pages    = &page,
	};
	struct nfs4_readlink_res res;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READLINK],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};

	return nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
}


static int nfs4_xattr_get_nfs4_acl(const struct xattr_handler *handler,
				   struct dentry *unused, struct inode *inode,
				   const char *key, void *buf, size_t buflen)
{
	return nfs4_proc_get_acl(inode, buf, buflen);
}


static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
			     void *data)
{
	struct nfs_openres *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_open(xdr, res);
	if (status)
		goto out;
	status = decode_getfh(xdr, &res->fh);
	if (status)
		goto out;
	if (res->access_request)
		decode_access(xdr, &res->access_supported, &res->access_result);
	decode_getfattr_label(xdr, res->f_attr, res->f_label, res->server);
	if (res->lg_res)
		decode_layoutget(xdr, rqstp, res->lg_res);
out:
	return status;
}


static int decode_attr_filehandle(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fh *fh)
{
	__be32 *p;
	u32 len;

	if (fh != NULL)
		memset(fh, 0, sizeof(*fh));

	if (unlikely(bitmap[0] & (FATTR4_WORD0_FILEHANDLE - 1U)))
		return -EIO;
	if (likely(bitmap[0] & FATTR4_WORD0_FILEHANDLE)) {
		p = xdr_inline_decode(xdr, 4);
		if (unlikely(!p))
			return -EIO;
		len = be32_to_cpup(p);
		if (len > NFS4_FHSIZE)
			return -EIO;
		p = xdr_inline_decode(xdr, len);
		if (unlikely(!p))
			return -EIO;
		if (fh != NULL) {
			memcpy(fh->data, p, len);
			fh->size = len;
		}
		bitmap[0] &= ~FATTR4_WORD0_FILEHANDLE;
	}
	return 0;
}

#define errno_NFSERR_IO		EIO

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_putfh_maxsz	(op_encode_hdr_maxsz + 1 + \
				(NFS4_FHSIZE >> 2))

#define decode_getfh_maxsz      (op_decode_hdr_maxsz + 1 + \
				((3+NFS4_FHSIZE) >> 2))

#define IMPL_NAME_LIMIT (sizeof(utsname()->sysname) + sizeof(utsname()->release) + \
			 sizeof(utsname()->version) + sizeof(utsname()->machine) + 8)

#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \
				encode_verifier_maxsz + \
				1 /* co_ownerid.len */

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


xdr_decode_nfstime4(__be32 *p, struct timespec64 *t)
{
	__u64 sec;

	p = xdr_decode_hyper(p, &sec);
	t-> tv_sec = sec;
	t->tv_nsec = be32_to_cpup(p++);
	return p;
}

struct compound_hdr

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}

static struct {
	int stat;
	int errno;
} nfs_errtbl[] = {
	{ NFS4_OK,		0		},
	{ NFS4ERR_PERM,		-EPERM		},
	{ NFS4ERR_NOENT,	-ENOENT		},
	{ NFS4ERR_IO,		-errno_NFSERR_IO},
	{ NFS4ERR_NXIO,		-ENXIO		},
	{ NFS4ERR_ACCESS,	-EACCES		},
	{ NFS4ERR_EXIST,	-EEXIST		},
	{ NFS4ERR_XDEV,		-EXDEV		},
	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
	{ NFS4ERR_ISDIR,	-EISDIR		},
	{ NFS4ERR_INVAL,	-EINVAL		},
	{ NFS4ERR_FBIG,		-EFBIG		},
	{ NFS4ERR_NOSPC,	-ENOSPC		},
	{ NFS4ERR_ROFS,		-EROFS		},
	{ NFS4ERR_MLINK,	-EMLINK		},
	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
	{ NFS4ERR_DQUOT,	-EDQUOT		},
	{ NFS4ERR_STALE,	-ESTALE		},
	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
	{ NFS4ERR_SERVERFAULT,	-EREMOTEIO	},
	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
	{ NFS4ERR_LOCKED,	-EAGAIN		},
	{ NFS4ERR_SYMLINK,	-ELOOP		},
	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
	{ NFS4ERR_NOXATTR,	-ENODATA	},
	{ NFS4ERR_XATTR2BIG,	-E2BIG		},
	{ -1,			-EIO		}
};


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


static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
				   const struct qstr *name,
				   struct nfs4_fs_locations *fs_locations,
				   struct page *page)
{
	struct nfs_server *server = NFS_SERVER(dir);
	u32 bitmask[3];
	struct nfs4_fs_locations_arg args = {
		.dir_fh = NFS_FH(dir),
		.name = name,
		.page = page,
		.bitmask = bitmask,
	};
	struct nfs4_fs_locations_res res = {
		.fs_locations = fs_locations,
	};
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS],
		.rpc_argp = &args,
		.rpc_resp = &res,
	};
	int status;

	dprintk("%s: start\n", __func__);

	bitmask[0] = nfs4_fattr_bitmap[0] | FATTR4_WORD0_FS_LOCATIONS;
	bitmask[1] = nfs4_fattr_bitmap[1];

	/* Ask for the fileid of the absent filesystem if mounted_on_fileid
	 * is not supported */
	if (NFS_SERVER(dir)->attr_bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)
		bitmask[0] &= ~FATTR4_WORD0_FILEID;
	else
		bitmask[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;

	nfs_fattr_init(&fs_locations->fattr);
	fs_locations->server = server;
	fs_locations->nlocations = 0;
	status = nfs4_call_sync(client, server, &msg, &args.seq_args, &res.seq_res, 0);
	dprintk("%s: returned status = %d\n", __func__, status);
	return status;
}


encode_layoutget(struct xdr_stream *xdr,
		      const struct nfs4_layoutget_args *args,
		      struct compound_hdr *hdr)
{
	__be32 *p;

	encode_op_hdr(xdr, OP_LAYOUTGET, decode_layoutget_maxsz, hdr);
	p = reserve_space(xdr, 36);
	*p++ = cpu_to_be32(0);     /* Signal layout available */
	*p++ = cpu_to_be32(args->type);
	*p++ = cpu_to_be32(args->range.iomode);
	p = xdr_encode_hyper(p, args->range.offset);
	p = xdr_encode_hyper(p, args->range.length);
	p = xdr_encode_hyper(p, args->minlength);
	encode_nfs4_stateid(xdr, &args->stateid);
	encode_uint32(xdr, args->maxcount);

	dprintk("%s: 1st type:0x%x iomode:%d off:%lu len:%lu mc:%d\n",
		__func__,
		args->type,
		args->range.iomode,
		(unsigned long)args->range.offset,
		(unsigned long)args->range.length,
		args->maxcount);
}

struct compound_hdr

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \
				encode_verifier_maxsz + \
				1 /* co_ownerid.len */

#define encode_getdeviceinfo_maxsz (op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_DEVICEID4_SIZE) + \
				1 /* layout type */

#define decode_layoutget_maxsz	(op_decode_hdr_maxsz + 8 + \
				decode_stateid_maxsz + \
				XDR_QUADLEN(PNFS_LAYOUT_MAXSIZE) + 1)

#define encode_layoutcommit_maxsz (op_encode_hdr_maxsz +          \
				2 /* offset */

#define decode_layoutget_maxsz	0

#define NFS4_dec_open_sz        (compound_decode_hdr_maxsz + \
				decode_sequence_maxsz + \
				decode_putfh_maxsz + \
				decode_open_maxsz + \
				decode_access_maxsz + \
				decode_getfh_maxsz + \
				decode_getattr_maxsz + \
				decode_layoutget_maxsz)

#define NFS4_dec_open_noattr_sz	(compound_decode_hdr_maxsz + \
					decode_sequence_maxsz + \
					decode_putfh_maxsz + \
					decode_open_maxsz + \
					decode_access_maxsz + \
					decode_getattr_maxsz + \
					decode_layoutget_maxsz)

#define NFS4_dec_layoutget_sz	(compound_decode_hdr_maxsz + \
				decode_sequence_maxsz + \
				decode_putfh_maxsz +        \
				decode_layoutget_maxsz)

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
	struct iattr *sattr, struct nfs4_label *label)
{
	int err;

	if (label == NULL)
		return NULL;

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

	err = security_dentry_init_security(dentry, sattr->ia_mode,
				&dentry->d_name, (void **)&label->label, &label->len);
	if (err == 0)
		return label;

	return NULL;
}


static int decode_setclientid(struct xdr_stream *xdr, struct nfs4_setclientid_res *res)
{
	__be32 *p;
	uint32_t opnum;
	int32_t nfserr;

	p = xdr_inline_decode(xdr, 8);
	if (unlikely(!p))
		return -EIO;
	opnum = be32_to_cpup(p++);
	if (opnum != OP_SETCLIENTID) {
		dprintk("nfs: decode_setclientid: Server returned operation"
			" %d\n", opnum);
		return -EIO;
	}
	nfserr = be32_to_cpup(p);
	if (nfserr == NFS_OK) {
		p = xdr_inline_decode(xdr, 8 + NFS4_VERIFIER_SIZE);
		if (unlikely(!p))
			return -EIO;
		p = xdr_decode_hyper(p, &res->clientid);
		memcpy(res->confirm.data, p, NFS4_VERIFIER_SIZE);
	} else if (nfserr == NFSERR_CLID_INUSE) {
		uint32_t len;

		/* skip netid string */
		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;

		/* skip uaddr string */
		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;
		return -NFSERR_CLID_INUSE;
	} else
		return nfs4_stat_to_errno(nfserr);

	return 0;
}

#define errno_NFSERR_IO		EIO

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_verifier_maxsz	(XDR_QUADLEN(NFS4_VERIFIER_SIZE))

#define decode_verifier_maxsz	(XDR_QUADLEN(NFS4_VERIFIER_SIZE))

#define encode_setclientid_maxsz \
				(op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_VERIFIER_SIZE) + \
				/* client name */

#define decode_setclientid_maxsz \
				(op_decode_hdr_maxsz + \
				2 /* clientid */

#define encode_setclientid_confirm_maxsz \
				(op_encode_hdr_maxsz + \
				3 + (NFS4_VERIFIER_SIZE >> 2))

#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \
				encode_verifier_maxsz + \
				1 /* co_ownerid.len */

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static void nfs4_xdr_enc_create_session(struct rpc_rqst *req,
					struct xdr_stream *xdr,
					const void *data)
{
	const struct nfs41_create_session_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = args->client->cl_mvops->minor_version,
	};

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

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_setclientid_maxsz \
				(op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_VERIFIER_SIZE) + \
				/* client name */

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
{
	ssize_t error, error2, error3;

	error = generic_listxattr(dentry, list, size);
	if (error < 0)
		return error;
	if (list) {
		list += error;
		size -= error;
	}

	error2 = nfs4_listxattr_nfs4_label(d_inode(dentry), list, size);
	if (error2 < 0)
		return error2;

	if (list) {
		list += error2;
		size -= error2;
	}

	error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, size);
	if (error3 < 0)
		return error3;

	return error + error2 + error3;
}


int nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cred)
{
	rpc_authflavor_t authflavor = clp->cl_rpcclient->cl_auth->au_flavor;
	int status;

	/* try SP4_MACH_CRED if krb5i/p	*/
	if (authflavor == RPC_AUTH_GSS_KRB5I ||
	    authflavor == RPC_AUTH_GSS_KRB5P) {
		status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED);
		if (!status)
			return 0;
	}

	/* try SP4_NONE */
	return _nfs4_proc_exchange_id(clp, cred, SP4_NONE);
}


int nfs4_proc_bind_one_conn_to_session(struct rpc_clnt *clnt,
		struct rpc_xprt *xprt,
		struct nfs_client *clp,
		const struct cred *cred)
{
	int status;
	struct nfs41_bind_conn_to_session_args args = {
		.client = clp,
		.dir = NFS4_CDFC4_FORE_OR_BOTH,
		.retries = 0,
	};
	struct nfs41_bind_conn_to_session_res res;
	struct rpc_message msg = {
		.rpc_proc =
			&nfs4_procedures[NFSPROC4_CLNT_BIND_CONN_TO_SESSION],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = clnt,
		.rpc_xprt = xprt,
		.callback_ops = &nfs4_bind_one_conn_to_session_ops,
		.rpc_message = &msg,
		.flags = RPC_TASK_TIMEOUT,
	};
	struct rpc_task *task;

	nfs4_copy_sessionid(&args.sessionid, &clp->cl_session->sess_id);
	if (!(clp->cl_session->flags & SESSION4_BACK_CHAN))
		args.dir = NFS4_CDFC4_FORE;

	/* Do not set the backchannel flag unless this is clnt->cl_xprt */
	if (xprt != rcu_access_pointer(clnt->cl_xprt))
		args.dir = NFS4_CDFC4_FORE;

	task = rpc_run_task(&task_setup_data);
	if (!IS_ERR(task)) {
		status = task->tk_status;
		rpc_put_task(task);
	} else
		status = PTR_ERR(task);
	trace_nfs4_bind_conn_to_session(clp, status);
	if (status == 0) {
		if (memcmp(res.sessionid.data,
		    clp->cl_session->sess_id.data, NFS4_MAX_SESSIONID_LEN)) {
			dprintk("NFS: %s: Session ID mismatch\n", __func__);
			return -EIO;
		}
		if ((res.dir & args.dir) != res.dir || res.dir == 0) {
			dprintk("NFS: %s: Unexpected direction from server\n",
				__func__);
			return -EIO;
		}
		if (res.use_conn_in_rdma_mode != args.use_conn_in_rdma_mode) {
			dprintk("NFS: %s: Server returned RDMA mode = true\n",
				__func__);
			return -EIO;
		}
	}

	return status;
}


static void nfs4_renew_release(void *calldata)
{
	struct nfs4_renewdata *data = calldata;
	struct nfs_client *clp = data->client;

	if (refcount_read(&clp->cl_count) > 1)
		nfs4_schedule_state_renewal(clp);
	nfs_put_client(clp);
	kfree(data);
}


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


static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr)
{
	struct nfs_server *server = NFS_SERVER(hdr->inode);

	trace_nfs4_read(hdr, task->tk_status);
	if (task->tk_status < 0) {
		struct nfs4_exception exception = {
			.inode = hdr->inode,
			.state = hdr->args.context->state,
			.stateid = &hdr->args.stateid,
		};
		task->tk_status = nfs4_async_handle_exception(task,
				server, task->tk_status, &exception);
		if (exception.retry) {
			rpc_restart_call_prepare(task);
			return -EAGAIN;
		}
	}

	if (task->tk_status > 0)
		renew_lease(server, hdr->timestamp);
	return 0;
}


nfs4_wake_lock_waiter(wait_queue_entry_t *wait, unsigned int mode, int flags, void *key)
{
	int ret;
	struct nfs4_lock_waiter	*waiter	= wait->private;

	/* NULL key means to wake up everyone */
	if (key) {
		struct cb_notify_lock_args	*cbnl = key;
		struct nfs_lowner		*lowner = &cbnl->cbnl_owner,
						*wowner = waiter->owner;

		/* Only wake if the callback was for the same owner. */
		if (lowner->id != wowner->id || lowner->s_dev != wowner->s_dev)
			return 0;

		/* Make sure it's for the right inode */
		if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh))
			return 0;
	}

	/* override "private" so we can use default_wake_function */
	wait->private = waiter->task;
	ret = woken_wake_function(wait, mode, flags, key);
	if (ret)
		list_del_init(&wait->entry);
	wait->private = waiter;
	return ret;
}


static void nfs41_free_stateid_done(struct rpc_task *task, void *calldata)
{
	struct nfs_free_stateid_data *data = calldata;

	nfs41_sequence_done(task, &data->res.seq_res);

	switch (task->tk_status) {
	case -NFS4ERR_DELAY:
		if (nfs4_async_handle_error(task, data->server, NULL, NULL) == -EAGAIN)
			rpc_restart_call_prepare(task);
	}
}


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

	encode_op_hdr(xdr, OP_BIND_CONN_TO_SESSION,
		decode_bind_conn_to_session_maxsz, hdr);
	encode_opaque_fixed(xdr, args->sessionid.data, NFS4_MAX_SESSIONID_LEN);
	p = xdr_reserve_space(xdr, 8);
	*p++ = cpu_to_be32(args->dir);
	*p = (args->use_conn_in_rdma_mode) ? cpu_to_be32(1) : cpu_to_be32(0);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define encode_sequence_maxsz	(op_encode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 4)

#define decode_sequence_maxsz	(op_decode_hdr_maxsz + \
				XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN) + 5)

#define NFS4_dec_bind_conn_to_session_sz \
				(compound_decode_hdr_maxsz + \
				 decode_bind_conn_to_session_maxsz)

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static void nfs4_lock_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_lockdata *data = calldata;
	struct nfs4_state *state = data->lsp->ls_state;

	dprintk("%s: begin!\n", __func__);
	if (nfs_wait_on_sequence(data->arg.lock_seqid, task) != 0)
		goto out_wait;
	/* Do we need to do an open_to_lock_owner? */
	if (!test_bit(NFS_LOCK_INITIALIZED, &data->lsp->ls_flags)) {
		if (nfs_wait_on_sequence(data->arg.open_seqid, task) != 0) {
			goto out_release_lock_seqid;
		}
		nfs4_stateid_copy(&data->arg.open_stateid,
				&state->open_stateid);
		data->arg.new_lock_owner = 1;
		data->res.open_seqid = data->arg.open_seqid;
	} else {
		data->arg.new_lock_owner = 0;
		nfs4_stateid_copy(&data->arg.lock_stateid,
				&data->lsp->ls_stateid);
	}
	if (!nfs4_valid_open_stateid(state)) {
		data->rpc_status = -EBADF;
		task->tk_action = NULL;
		goto out_release_open_seqid;
	}
	data->timestamp = jiffies;
	if (nfs4_setup_sequence(data->server->nfs_client,
				&data->arg.seq_args,
				&data->res.seq_res,
				task) == 0)
		return;
out_release_open_seqid:
	nfs_release_seqid(data->arg.open_seqid);
out_release_lock_seqid:
	nfs_release_seqid(data->arg.lock_seqid);
out_wait:
	nfs4_sequence_done(task, &data->res.seq_res);
	dprintk("%s: done!, ret = %d\n", __func__, data->rpc_status);
}


static void nfs4_xdr_enc_sequence(struct rpc_rqst *req, struct xdr_stream *xdr,
				  const void *data)
{
	const struct nfs4_sequence_args *args = data;
	struct compound_hdr hdr = {
		.minorversion = nfs4_xdr_minorversion(args),
	};

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


static int decode_attr_time_delta(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_DELTA - 1U)))
		return -EIO;
	if (likely(bitmap[1] & FATTR4_WORD1_TIME_DELTA)) {
		status = decode_attr_time(xdr, time);
		bitmap[1] &= ~FATTR4_WORD1_TIME_DELTA;
	}
	dprintk("%s: time_delta=%lld %ld\n", __func__, time->tv_sec,
		time->tv_nsec);
	return status;
}


static void encode_getattr(struct xdr_stream *xdr,
		const __u32 *bitmap, const __u32 *mask, size_t len,
		struct compound_hdr *hdr)
{
	__u32 masked_bitmap[nfs4_fattr_bitmap_maxsz];

	encode_op_hdr(xdr, OP_GETATTR, decode_getattr_maxsz, hdr);
	if (mask) {
		if (WARN_ON_ONCE(len > ARRAY_SIZE(masked_bitmap)))
			len = ARRAY_SIZE(masked_bitmap);
		len = mask_bitmap4(bitmap, mask, masked_bitmap, len);
		bitmap = masked_bitmap;
	}
	xdr_encode_bitmap4(xdr, bitmap, len);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define nfs4_fattr_bitmap_maxsz 4

#define encode_getattr_maxsz    (op_encode_hdr_maxsz + nfs4_fattr_bitmap_maxsz)

#define decode_mdsthreshold_maxsz (1 + 1 + nfs4_fattr_bitmap_maxsz + 1 + 8)

#define nfs4_fattr_maxsz	(nfs4_fattr_bitmap_maxsz + \
				nfs4_fattr_value_maxsz)

#define decode_getattr_maxsz    (op_decode_hdr_maxsz + nfs4_fattr_maxsz)

#define encode_attrs_maxsz	(nfs4_fattr_bitmap_maxsz + \
				 1 + 2 + 1 + \
				nfs4_owner_maxsz + \
				nfs4_group_maxsz + \
				nfs4_label_maxsz + \
				1 + nfstime4_maxsz + \
				1 + nfstime4_maxsz)

#define decode_fsinfo_maxsz	(op_decode_hdr_maxsz + \
				 nfs4_fattr_bitmap_maxsz + 4 + 8 + 5)

#define decode_open_maxsz	(op_decode_hdr_maxsz + \
				decode_stateid_maxsz + \
				decode_change_info_maxsz + 1 + \
				nfs4_fattr_bitmap_maxsz + \
				decode_delegation_maxsz)

#define decode_setattr_maxsz	(op_decode_hdr_maxsz + \
				 nfs4_fattr_bitmap_maxsz)

#define decode_create_maxsz	(op_decode_hdr_maxsz + \
				decode_change_info_maxsz + \
				nfs4_fattr_bitmap_maxsz)

#define decode_statfs_maxsz	(decode_getattr_maxsz)

#define decode_getacl_maxsz	(op_decode_hdr_maxsz + \
				 nfs4_fattr_bitmap_maxsz + 1 + 1)

#define encode_exchange_id_maxsz (op_encode_hdr_maxsz + \
				encode_verifier_maxsz + \
				1 /* co_ownerid.len */

#define NFS4_dec_write_sz	(compound_decode_hdr_maxsz + \
				decode_sequence_maxsz + \
				decode_putfh_maxsz + \
				decode_write_maxsz + \
				decode_getattr_maxsz)

#define NFS4_dec_open_sz        (compound_decode_hdr_maxsz + \
				decode_sequence_maxsz + \
				decode_putfh_maxsz + \
				decode_open_maxsz + \
				decode_access_maxsz + \
				decode_getfh_maxsz + \
				decode_getattr_maxsz + \
				decode_layoutget_maxsz)

#define NFS4_dec_open_noattr_sz	(compound_decode_hdr_maxsz + \
					decode_sequence_maxsz + \
					decode_putfh_maxsz + \
					decode_open_maxsz + \
					decode_access_maxsz + \
					decode_getattr_maxsz + \
					decode_layoutget_maxsz)

#define NFS4_dec_close_sz	(compound_decode_hdr_maxsz + \
				 decode_sequence_maxsz + \
				 decode_putfh_maxsz + \
				 decode_layoutreturn_maxsz + \
				 decode_close_maxsz + \
				 decode_getattr_maxsz)

#define NFS4_dec_setattr_sz	(compound_decode_hdr_maxsz + \
				 decode_sequence_maxsz + \
				 decode_putfh_maxsz + \
				 decode_setattr_maxsz + \
				 decode_getattr_maxsz)


nfs4_init_nonuniform_client_string(struct nfs_client *clp)
{
	size_t len;
	char *str;

	if (clp->cl_owner_id != NULL)
		return 0;

	rcu_read_lock();
	len = 14 +
		strlen(clp->cl_rpcclient->cl_nodename) +
		1 +
		strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) +
		1;
	rcu_read_unlock();
	if (nfs4_client_id_uniquifier[0] != '\0')
		len += strlen(nfs4_client_id_uniquifier) + 1;
	if (len > NFS4_OPAQUE_LIMIT + 1)
		return -EINVAL;

	/*
	 * Since this string is allocated at mount time, and held until the
	 * nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying
	 * about a memory-reclaim deadlock.
	 */
	str = kmalloc(len, GFP_KERNEL);
	if (!str)
		return -ENOMEM;

	rcu_read_lock();
	if (nfs4_client_id_uniquifier[0] != '\0')
		scnprintf(str, len, "Linux NFSv4.0 %s/%s/%s",
			  clp->cl_rpcclient->cl_nodename,
			  nfs4_client_id_uniquifier,
			  rpc_peeraddr2str(clp->cl_rpcclient,
					   RPC_DISPLAY_ADDR));
	else
		scnprintf(str, len, "Linux NFSv4.0 %s/%s",
			  clp->cl_rpcclient->cl_nodename,
			  rpc_peeraddr2str(clp->cl_rpcclient,
					   RPC_DISPLAY_ADDR));
	rcu_read_unlock();

	clp->cl_owner_id = str;
	return 0;
}


static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_OPEN_CONFIRM, decode_open_confirm_maxsz, hdr);
	encode_nfs4_stateid(xdr, arg->stateid);
	encode_nfs4_seqid(xdr, arg->seqid);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define decode_open_confirm_maxsz \
				(op_decode_hdr_maxsz + \
				 decode_stateid_maxsz)

#define NFS4_dec_open_confirm_sz \
				(compound_decode_hdr_maxsz + \
				 decode_putfh_maxsz + \
				 decode_open_confirm_maxsz)

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static void nfs4_xdr_enc_locku(struct rpc_rqst *req, struct xdr_stream *xdr,
			       const void *data)
{
	const struct nfs_locku_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_locku(xdr, args, &hdr);
	encode_nops(&hdr);
}


static int nfs4_get_referral(struct rpc_clnt *client, struct inode *dir,
			     const struct qstr *name, struct nfs_fattr *fattr,
			     struct nfs_fh *fhandle)
{
	int status = -ENOMEM;
	struct page *page = NULL;
	struct nfs4_fs_locations *locations = NULL;

	page = alloc_page(GFP_KERNEL);
	if (page == NULL)
		goto out;
	locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL);
	if (locations == NULL)
		goto out;

	status = nfs4_proc_fs_locations(client, dir, name, locations, page);
	if (status != 0)
		goto out;

	/*
	 * If the fsid didn't change, this is a migration event, not a
	 * referral.  Cause us to drop into the exception handler, which
	 * will kick off migration recovery.
	 */
	if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) {
		dprintk("%s: server did not return a different fsid for"
			" a referral at %s\n", __func__, name->name);
		status = -NFS4ERR_MOVED;
		goto out;
	}
	/* Fixup attributes for the nfs_lookup() call to nfs_fhget() */
	nfs_fixup_referral_attributes(&locations->fattr);

	/* replace the lookup nfs_fattr with the locations nfs_fattr */
	memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr));
	memset(fhandle, 0, sizeof(struct nfs_fh));
out:
	if (page)
		__free_page(page);
	kfree(locations);
	return status;
}


static int nfs41_free_stateid(struct nfs_server *server,
		const nfs4_stateid *stateid,
		const struct cred *cred,
		bool privileged)
{
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
		.rpc_cred = cred,
	};
	struct rpc_task_setup task_setup = {
		.rpc_client = server->client,
		.rpc_message = &msg,
		.callback_ops = &nfs41_free_stateid_ops,
		.flags = RPC_TASK_ASYNC,
	};
	struct nfs_free_stateid_data *data;
	struct rpc_task *task;

	nfs4_state_protect(server->nfs_client, NFS_SP4_MACH_CRED_STATEID,
		&task_setup.rpc_client, &msg);

	dprintk("NFS call  free_stateid %p\n", stateid);
	data = kmalloc(sizeof(*data), GFP_NOFS);
	if (!data)
		return -ENOMEM;
	data->server = server;
	nfs4_stateid_copy(&data->args.stateid, stateid);

	task_setup.callback_data = data;

	msg.rpc_argp = &data->args;
	msg.rpc_resp = &data->res;
	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1, privileged);
	task = rpc_run_task(&task_setup);
	if (IS_ERR(task))
		return PTR_ERR(task);
	rpc_put_task(task);
	return 0;
}


static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cred,
			u32 sp4_how)
{
	struct rpc_task *task;
	struct nfs41_exchange_id_args *argp;
	struct nfs41_exchange_id_res *resp;
	unsigned long now = jiffies;
	int status;

	task = nfs4_run_exchange_id(clp, cred, sp4_how, NULL);
	if (IS_ERR(task))
		return PTR_ERR(task);

	argp = task->tk_msg.rpc_argp;
	resp = task->tk_msg.rpc_resp;
	status = task->tk_status;
	if (status  != 0)
		goto out;

	status = nfs4_check_cl_exchange_flags(resp->flags);
	if (status  != 0)
		goto out;

	status = nfs4_sp4_select_mode(clp, &resp->state_protect);
	if (status != 0)
		goto out;

	do_renew_lease(clp, now);

	clp->cl_clientid = resp->clientid;
	clp->cl_exchange_flags = resp->flags;
	clp->cl_seqid = resp->seqid;
	/* Client ID is not confirmed */
	if (!(resp->flags & EXCHGID4_FLAG_CONFIRMED_R))
		clear_bit(NFS4_SESSION_ESTABLISHED,
			  &clp->cl_session->session_state);

	if (clp->cl_serverscope != NULL &&
	    !nfs41_same_server_scope(clp->cl_serverscope,
				resp->server_scope)) {
		dprintk("%s: server_scope mismatch detected\n",
			__func__);
		set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
	}

	swap(clp->cl_serverowner, resp->server_owner);
	swap(clp->cl_serverscope, resp->server_scope);
	swap(clp->cl_implid, resp->impl_id);

	/* Save the EXCHANGE_ID verifier session trunk tests */
	memcpy(clp->cl_confirm.data, argp->verifier.data,
	       sizeof(clp->cl_confirm.data));
out:
	trace_nfs4_exchange_id(clp, status);
	rpc_put_task(task);
	return status;
}


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

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}

static struct {
	int stat;
	int errno;
} nfs_errtbl[] = {
	{ NFS4_OK,		0		},
	{ NFS4ERR_PERM,		-EPERM		},
	{ NFS4ERR_NOENT,	-ENOENT		},
	{ NFS4ERR_IO,		-errno_NFSERR_IO},
	{ NFS4ERR_NXIO,		-ENXIO		},
	{ NFS4ERR_ACCESS,	-EACCES		},
	{ NFS4ERR_EXIST,	-EEXIST		},
	{ NFS4ERR_XDEV,		-EXDEV		},
	{ NFS4ERR_NOTDIR,	-ENOTDIR	},
	{ NFS4ERR_ISDIR,	-EISDIR		},
	{ NFS4ERR_INVAL,	-EINVAL		},
	{ NFS4ERR_FBIG,		-EFBIG		},
	{ NFS4ERR_NOSPC,	-ENOSPC		},
	{ NFS4ERR_ROFS,		-EROFS		},
	{ NFS4ERR_MLINK,	-EMLINK		},
	{ NFS4ERR_NAMETOOLONG,	-ENAMETOOLONG	},
	{ NFS4ERR_NOTEMPTY,	-ENOTEMPTY	},
	{ NFS4ERR_DQUOT,	-EDQUOT		},
	{ NFS4ERR_STALE,	-ESTALE		},
	{ NFS4ERR_BADHANDLE,	-EBADHANDLE	},
	{ NFS4ERR_BAD_COOKIE,	-EBADCOOKIE	},
	{ NFS4ERR_NOTSUPP,	-ENOTSUPP	},
	{ NFS4ERR_TOOSMALL,	-ETOOSMALL	},
	{ NFS4ERR_SERVERFAULT,	-EREMOTEIO	},
	{ NFS4ERR_BADTYPE,	-EBADTYPE	},
	{ NFS4ERR_LOCKED,	-EAGAIN		},
	{ NFS4ERR_SYMLINK,	-ELOOP		},
	{ NFS4ERR_OP_ILLEGAL,	-EOPNOTSUPP	},
	{ NFS4ERR_DEADLOCK,	-EDEADLK	},
	{ NFS4ERR_NOXATTR,	-ENODATA	},
	{ NFS4ERR_XATTR2BIG,	-E2BIG		},
	{ -1,			-EIO		}
};


static int _nfs4_proc_create_session(struct nfs_client *clp,
		const struct cred *cred)
{
	struct nfs4_session *session = clp->cl_session;
	struct nfs41_create_session_args args = {
		.client = clp,
		.clientid = clp->cl_clientid,
		.seqid = clp->cl_seqid,
		.cb_program = NFS4_CALLBACK,
	};
	struct nfs41_create_session_res res;

	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE_SESSION],
		.rpc_argp = &args,
		.rpc_resp = &res,
		.rpc_cred = cred,
	};
	int status;

	nfs4_init_channel_attrs(&args, clp->cl_rpcclient);
	args.flags = (SESSION4_PERSIST | SESSION4_BACK_CHAN);

	status = rpc_call_sync(session->clp->cl_rpcclient, &msg,
			       RPC_TASK_TIMEOUT | RPC_TASK_NO_ROUND_ROBIN);
	trace_nfs4_create_session(clp, status);

	switch (status) {
	case -NFS4ERR_STALE_CLIENTID:
	case -NFS4ERR_DELAY:
	case -ETIMEDOUT:
	case -EACCES:
	case -EAGAIN:
		goto out;
	}

	clp->cl_seqid++;
	if (!status) {
		/* Verify the session's negotiated channel_attrs values */
		status = nfs4_verify_channel_attrs(&args, &res);
		/* Increment the clientid slot sequence id */
		if (status)
			goto out;
		nfs4_update_session(session, &res);
	}
out:
	return status;
}


static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *fl, int recovery_type)
{
	struct nfs4_lockdata *data;
	struct rpc_task *task;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LOCK],
		.rpc_cred = state->owner->so_cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_client = NFS_CLIENT(state->inode),
		.rpc_message = &msg,
		.callback_ops = &nfs4_lock_ops,
		.workqueue = nfsiod_workqueue,
		.flags = RPC_TASK_ASYNC | RPC_TASK_CRED_NOREF,
	};
	int ret;

	dprintk("%s: begin!\n", __func__);
	data = nfs4_alloc_lockdata(fl, nfs_file_open_context(fl->fl_file),
			fl->fl_u.nfs4_fl.owner,
			recovery_type == NFS_LOCK_NEW ? GFP_KERNEL : GFP_NOFS);
	if (data == NULL)
		return -ENOMEM;
	if (IS_SETLKW(cmd))
		data->arg.block = 1;
	nfs4_init_sequence(&data->arg.seq_args, &data->res.seq_res, 1,
				recovery_type > NFS_LOCK_NEW);
	msg.rpc_argp = &data->arg;
	msg.rpc_resp = &data->res;
	task_setup_data.callback_data = data;
	if (recovery_type > NFS_LOCK_NEW) {
		if (recovery_type == NFS_LOCK_RECLAIM)
			data->arg.reclaim = NFS_LOCK_RECLAIM;
	} else
		data->arg.new_lock = 1;
	task = rpc_run_task(&task_setup_data);
	if (IS_ERR(task))
		return PTR_ERR(task);
	ret = rpc_wait_for_completion_task(task);
	if (ret == 0) {
		ret = data->rpc_status;
		if (ret)
			nfs4_handle_setlk_error(data->server, data->lsp,
					data->arg.new_lock_owner, ret);
	} else
		data->cancelled = true;
	rpc_put_task(task);
	dprintk("%s: done, ret = %d!\n", __func__, ret);
	trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
	return ret;
}

struct nfs4_opendata

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

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

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

static const struct rpc_call_ops nfs4_open_confirm_ops = {
	.rpc_call_prepare = nfs4_open_confirm_prepare,
	.rpc_call_done = nfs4_open_confirm_done,
	.rpc_release = nfs4_open_confirm_release,
};

static const struct rpc_call_ops nfs4_open_ops = {
	.rpc_call_prepare = nfs4_open_prepare,
	.rpc_call_done = nfs4_open_done,
	.rpc_release = nfs4_open_release,
};

struct nfs4_closedata {
	struct inode *inode;
	struct nfs4_state *state;
	struct nfs_closeargs arg;
	struct nfs_closeres res;
	struct {
		struct nfs4_layoutreturn_args arg;
		struct nfs4_layoutreturn_res res;
		struct nfs4_xdr_opaque_data ld_private;
		u32 roc_barrier;
		bool roc;
	} lr;
	struct nfs_fattr fattr;
	unsigned long timestamp;
}


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

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


nfs4_listxattr_nfs4_user(struct inode *inode, char *list, size_t list_len)
{
	return 0;
}


int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
				struct nfs_fattr *fattr, struct nfs4_label *label,
				struct inode *inode)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;
	do {
		err = _nfs4_proc_getattr(server, fhandle, fattr, label, inode);
		trace_nfs4_getattr(server, fhandle, fattr, err);
		err = nfs4_handle_exception(server, err,
				&exception);
	} while (exception.retry);
	return err;
}


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

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


static void encode_uint64(struct xdr_stream *xdr, u64 n)
{
	WARN_ON_ONCE(xdr_stream_encode_u64(xdr, n) < 0);
}


static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
		struct page *page, unsigned int len, struct iattr *sattr,
		struct nfs4_label *label)
{
	struct nfs4_createdata *data;
	int status = -ENAMETOOLONG;

	if (len > NFS4_MAXPATHLEN)
		goto out;

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

	data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
	data->arg.u.symlink.pages = &page;
	data->arg.u.symlink.len = len;
	data->arg.label = label;

	status = nfs4_do_create(dir, dentry, data);

	nfs4_free_createdata(data);
out:
	return status;
}


static void nfs4_lock_release(void *calldata)
{
	struct nfs4_lockdata *data = calldata;

	dprintk("%s: begin!\n", __func__);
	nfs_free_seqid(data->arg.open_seqid);
	if (data->cancelled && data->rpc_status == 0) {
		struct rpc_task *task;
		task = nfs4_do_unlck(&data->fl, data->ctx, data->lsp,
				data->arg.lock_seqid);
		if (!IS_ERR(task))
			rpc_put_task_async(task);
		dprintk("%s: cancelling lock!\n", __func__);
	} else
		nfs_free_seqid(data->arg.lock_seqid);
	nfs4_put_lock_state(data->lsp);
	put_nfs_open_context(data->ctx);
	kfree(data);
	dprintk("%s: done!\n", __func__);
}


static void nfs4_proc_commit_rpc_prepare(struct rpc_task *task, struct nfs_commit_data *data)
{
	nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client,
			&data->args.seq_args,
			&data->res.seq_res,
			task);
}


nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
{
	struct nfs_release_lockowner_data *data;
	struct rpc_message msg = {
		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER],
	};

	if (server->nfs_client->cl_mvops->minor_version != 0)
		return;

	data = kmalloc(sizeof(*data), GFP_NOFS);
	if (!data)
		return;
	data->lsp = lsp;
	data->server = server;
	data->args.lock_owner.clientid = server->nfs_client->cl_clientid;
	data->args.lock_owner.id = lsp->ls_seqid.owner_id;
	data->args.lock_owner.s_dev = server->s_dev;

	msg.rpc_argp = &data->args;
	msg.rpc_resp = &data->res;
	nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0, 0);
	rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
}


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

	nfs4_setup_sequence(data->o_arg.server->nfs_client,
			   &data->c_arg.seq_args, &data->c_res.seq_res, task);
}


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

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


static bool nfs41_match_stateid(const nfs4_stateid *s1,
		const nfs4_stateid *s2)
{
	if (s1->type != s2->type)
		return false;

	if (memcmp(s1->other, s2->other, sizeof(s1->other)) != 0)
		return false;

	if (s1->seqid == s2->seqid)
		return true;

	return s1->seqid == 0 || s2->seqid == 0;
}

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

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


static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request)
{
	struct nfs_server *server = NFS_SERVER(state->inode);
	struct nfs4_exception exception = {
		.inode = state->inode,
	};
	int err;

	do {
		/* Cache the lock if possible... */
		if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0)
			return 0;
		err = _nfs4_do_setlk(state, F_SETLK, request, NFS_LOCK_RECLAIM);
		if (err != -NFS4ERR_DELAY)
			break;
		nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
}


static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	int err;

	do {
		err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
		trace_nfs4_fsinfo(server, fhandle, fsinfo->fattr, err);
		if (err == 0) {
			nfs4_set_lease_period(server->nfs_client, fsinfo->lease_time * HZ);
			break;
		}
		err = nfs4_handle_exception(server, err, &exception);
	} while (exception.retry);
	return err;
}


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

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

struct nfs4_opendata

static int _nfs4_recover_proc_open(struct nfs4_opendata *data);

static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);

static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);

static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label, struct inode *inode);

static int nfs4_do_setattr(struct inode *inode, const struct cred *cred,
			    struct nfs_fattr *fattr, struct iattr *sattr,
			    struct nfs_open_context *ctx, struct nfs4_label *ilabel,
			    struct nfs4_label *olabel);

static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp,
		const struct cred *cred,
		struct nfs4_slot *slot,
		bool is_privileged);

static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
		const struct cred *);

static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
		const struct cred *, bool);

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

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

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

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

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


static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
{
	struct nfs4_opendata *data = calldata;
	struct nfs4_state_owner *sp = data->owner;
	struct nfs_client *clp = sp->so_server->nfs_client;
	enum open_claim_type4 claim = data->o_arg.claim;

	if (nfs_wait_on_sequence(data->o_arg.seqid, task) != 0)
		goto out_wait;
	/*
	 * Check if we still need to send an OPEN call, or if we can use
	 * a delegation instead.
	 */
	if (data->state != NULL) {
		struct nfs_delegation *delegation;

		if (can_open_cached(data->state, data->o_arg.fmode,
					data->o_arg.open_flags, claim))
			goto out_no_action;
		rcu_read_lock();
		delegation = nfs4_get_valid_delegation(data->state->inode);
		if (can_open_delegated(delegation, data->o_arg.fmode, claim))
			goto unlock_no_action;
		rcu_read_unlock();
	}
	/* Update client id. */
	data->o_arg.clientid = clp->cl_clientid;
	switch (claim) {
	default:
		break;
	case NFS4_OPEN_CLAIM_PREVIOUS:
	case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
	case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
		data->o_arg.open_bitmap = &nfs4_open_noattr_bitmap[0];
		/* Fall through */
	case NFS4_OPEN_CLAIM_FH:
		task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR];
	}
	data->timestamp = jiffies;
	if (nfs4_setup_sequence(data->o_arg.server->nfs_client,
				&data->o_arg.seq_args,
				&data->o_res.seq_res,
				task) != 0)
		nfs_release_seqid(data->o_arg.seqid);

	/* Set the create mode (note dependency on the session type) */
	data->o_arg.createmode = NFS4_CREATE_UNCHECKED;
	if (data->o_arg.open_flags & O_EXCL) {
		data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE;
		if (nfs4_has_persistent_session(clp))
			data->o_arg.createmode = NFS4_CREATE_GUARDED;
		else if (clp->cl_mvops->minor_version > 0)
			data->o_arg.createmode = NFS4_CREATE_EXCLUSIVE4_1;
	}
	return;
unlock_no_action:
	trace_nfs4_cached_open(data->state);
	rcu_read_unlock();
out_no_action:
	task->tk_action = NULL;
out_wait:
	nfs4_sequence_done(task, &data->o_res.seq_res);
}


static void encode_release_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_RELEASE_LOCKOWNER, decode_release_lockowner_maxsz, hdr);
	encode_lockowner(xdr, lowner);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define decode_release_lockowner_maxsz \
				(op_decode_hdr_maxsz)

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


nfs4_init_callback_netid(const struct nfs_client *clp, char *buf, size_t len)
{
	if (strchr(clp->cl_ipaddr, ':') != NULL)
		return scnprintf(buf, len, "tcp6");
	else
		return scnprintf(buf, len, "tcp");
}


static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
		struct iattr *sattr)
{
	struct nfs_server *server = NFS_SERVER(dir);
	struct nfs4_exception exception = {
		.interruptible = true,
	};
	struct nfs4_label l, *label = NULL;
	int err;

	label = nfs4_label_init_security(dir, dentry, sattr, &l);

	if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
		sattr->ia_mode &= ~current_umask();
	do {
		err = _nfs4_proc_mkdir(dir, dentry, sattr, label);
		trace_nfs4_mkdir(dir, &dentry->d_name, err);
		err = nfs4_handle_exception(NFS_SERVER(dir), err,
				&exception);
	} while (exception.retry);
	nfs4_label_release_security(label);

	return err;
}


nfs4_xdr_dec_setacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
		    void *data)
{
	struct nfs_setaclres *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_setattr(xdr);
out:
	return status;
}


static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
{
	encode_op_hdr(xdr, OP_READLINK, decode_readlink_maxsz, hdr);
}

struct compound_hdr

static int nfs4_stat_to_errno(int);

static void encode_layoutget(struct xdr_stream *xdr,
			     const struct nfs4_layoutget_args *args,
			     struct compound_hdr *hdr);

static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
			     struct nfs4_layoutget_res *res);

#define decode_readlink_maxsz	(op_decode_hdr_maxsz + 1 + 1)

#define NFS4_dec_readlink_sz	(compound_decode_hdr_maxsz + \
				decode_sequence_maxsz + \
				decode_putfh_maxsz + \
				decode_readlink_maxsz)

const u32 nfs41_maxwrite_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				      compound_encode_hdr_maxsz +
				      encode_sequence_maxsz +
				      encode_putfh_maxsz +
				      encode_getattr_maxsz) *
				     XDR_UNIT);

const u32 nfs41_maxread_overhead = ((RPC_MAX_HEADER_WITH_AUTH +
				     compound_decode_hdr_maxsz +
				     decode_sequence_maxsz +
				     decode_putfh_maxsz) *
				    XDR_UNIT);

const u32 nfs41_maxgetdevinfo_overhead = ((RPC_MAX_REPHEADER_WITH_AUTH +
					   compound_decode_hdr_maxsz +
					   decode_sequence_maxsz) *
					  XDR_UNIT);

static const umode_t nfs_type2fmt[] = {
	[NF4BAD] = 0,
	[NF4REG] = S_IFREG,
	[NF4DIR] = S_IFDIR,
	[NF4BLK] = S_IFBLK,
	[NF4CHR] = S_IFCHR,
	[NF4LNK] = S_IFLNK,
	[NF4SOCK] = S_IFSOCK,
	[NF4FIFO] = S_IFIFO,
	[NF4ATTRDIR] = 0,
	[NF4NAMEDATTR] = 0,
};

struct compound_hdr {
	int32_t		status;
	uint32_t	nops;
	__be32 *	nops_p;
	uint32_t	taglen;
	char *		tag;
	uint32_t	replen;		/* expected reply words */
	u32		minorversion;
}


static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct nfs4_state *state, const nfs4_stateid *stateid, struct file_lock *fl, int err)
{
	switch (err) {
		default:
			printk(KERN_ERR "NFS: %s: unhandled error "
					"%d.\n", __func__, err);
		case 0:
		case -ENOENT:
		case -EAGAIN:
		case -ESTALE:
		case -ETIMEDOUT:
			break;
		case -NFS4ERR_BADSESSION:
		case -NFS4ERR_BADSLOT:
		case -NFS4ERR_BAD_HIGH_SLOT:
		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
		case -NFS4ERR_DEADSESSION:
			return -EAGAIN;
		case -NFS4ERR_STALE_CLIENTID:
		case -NFS4ERR_STALE_STATEID:
			/* Don't recall a delegation if it was lost */
			nfs4_schedule_lease_recovery(server->nfs_client);
			return -EAGAIN;
		case -NFS4ERR_MOVED:
			nfs4_schedule_migration_recovery(server);
			return -EAGAIN;
		case -NFS4ERR_LEASE_MOVED:
			nfs4_schedule_lease_moved_recovery(server->nfs_client);
			return -EAGAIN;
		case -NFS4ERR_DELEG_REVOKED:
		case -NFS4ERR_ADMIN_REVOKED:
		case -NFS4ERR_EXPIRED:
		case -NFS4ERR_BAD_STATEID:
		case -NFS4ERR_OPENMODE:
			nfs_inode_find_state_and_recover(state->inode,
					stateid);
			nfs4_schedule_stateid_recovery(server, state);
			return -EAGAIN;
		case -NFS4ERR_DELAY:
		case -NFS4ERR_GRACE:
			ssleep(1);
			return -EAGAIN;
		case -ENOMEM:
		case -NFS4ERR_DENIED:
			if (fl) {
				struct nfs4_lock_state *lsp = fl->fl_u.nfs4_fl.owner;
				if (lsp)
					set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
			}
			return 0;
	}
	return err;
}