162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/nfs/file.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1992  Rick Sladkey
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/fs.h>
862306a36Sopenharmony_ci#include <linux/file.h>
962306a36Sopenharmony_ci#include <linux/falloc.h>
1062306a36Sopenharmony_ci#include <linux/mount.h>
1162306a36Sopenharmony_ci#include <linux/nfs_fs.h>
1262306a36Sopenharmony_ci#include <linux/nfs_ssc.h>
1362306a36Sopenharmony_ci#include "delegation.h"
1462306a36Sopenharmony_ci#include "internal.h"
1562306a36Sopenharmony_ci#include "iostat.h"
1662306a36Sopenharmony_ci#include "fscache.h"
1762306a36Sopenharmony_ci#include "pnfs.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "nfstrace.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
2262306a36Sopenharmony_ci#include "nfs42.h"
2362306a36Sopenharmony_ci#endif
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_FILE
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int
2862306a36Sopenharmony_cinfs4_file_open(struct inode *inode, struct file *filp)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct nfs_open_context *ctx;
3162306a36Sopenharmony_ci	struct dentry *dentry = file_dentry(filp);
3262306a36Sopenharmony_ci	struct dentry *parent = NULL;
3362306a36Sopenharmony_ci	struct inode *dir;
3462306a36Sopenharmony_ci	unsigned openflags = filp->f_flags;
3562306a36Sopenharmony_ci	struct iattr attr;
3662306a36Sopenharmony_ci	int err;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/*
3962306a36Sopenharmony_ci	 * If no cached dentry exists or if it's negative, NFSv4 handled the
4062306a36Sopenharmony_ci	 * opens in ->lookup() or ->create().
4162306a36Sopenharmony_ci	 *
4262306a36Sopenharmony_ci	 * We only get this far for a cached positive dentry.  We skipped
4362306a36Sopenharmony_ci	 * revalidation, so handle it here by dropping the dentry and returning
4462306a36Sopenharmony_ci	 * -EOPENSTALE.  The VFS will retry the lookup/create/open.
4562306a36Sopenharmony_ci	 */
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	dprintk("NFS: open file(%pd2)\n", dentry);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	err = nfs_check_flags(openflags);
5062306a36Sopenharmony_ci	if (err)
5162306a36Sopenharmony_ci		return err;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	/* We can't create new files here */
5462306a36Sopenharmony_ci	openflags &= ~(O_CREAT|O_EXCL);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	parent = dget_parent(dentry);
5762306a36Sopenharmony_ci	dir = d_inode(parent);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	ctx = alloc_nfs_open_context(file_dentry(filp),
6062306a36Sopenharmony_ci				     flags_to_mode(openflags), filp);
6162306a36Sopenharmony_ci	err = PTR_ERR(ctx);
6262306a36Sopenharmony_ci	if (IS_ERR(ctx))
6362306a36Sopenharmony_ci		goto out;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	attr.ia_valid = ATTR_OPEN;
6662306a36Sopenharmony_ci	if (openflags & O_TRUNC) {
6762306a36Sopenharmony_ci		attr.ia_valid |= ATTR_SIZE;
6862306a36Sopenharmony_ci		attr.ia_size = 0;
6962306a36Sopenharmony_ci		filemap_write_and_wait(inode->i_mapping);
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, NULL);
7362306a36Sopenharmony_ci	if (IS_ERR(inode)) {
7462306a36Sopenharmony_ci		err = PTR_ERR(inode);
7562306a36Sopenharmony_ci		switch (err) {
7662306a36Sopenharmony_ci		default:
7762306a36Sopenharmony_ci			goto out_put_ctx;
7862306a36Sopenharmony_ci		case -ENOENT:
7962306a36Sopenharmony_ci		case -ESTALE:
8062306a36Sopenharmony_ci		case -EISDIR:
8162306a36Sopenharmony_ci		case -ENOTDIR:
8262306a36Sopenharmony_ci		case -ELOOP:
8362306a36Sopenharmony_ci			goto out_drop;
8462306a36Sopenharmony_ci		}
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci	if (inode != d_inode(dentry))
8762306a36Sopenharmony_ci		goto out_drop;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	nfs_file_set_open_context(filp, ctx);
9062306a36Sopenharmony_ci	nfs_fscache_open_file(inode, filp);
9162306a36Sopenharmony_ci	err = 0;
9262306a36Sopenharmony_ci	filp->f_mode |= FMODE_CAN_ODIRECT;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciout_put_ctx:
9562306a36Sopenharmony_ci	put_nfs_open_context(ctx);
9662306a36Sopenharmony_ciout:
9762306a36Sopenharmony_ci	dput(parent);
9862306a36Sopenharmony_ci	return err;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ciout_drop:
10162306a36Sopenharmony_ci	d_drop(dentry);
10262306a36Sopenharmony_ci	err = -EOPENSTALE;
10362306a36Sopenharmony_ci	goto out_put_ctx;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * Flush all dirty pages, and check for write errors.
10862306a36Sopenharmony_ci */
10962306a36Sopenharmony_cistatic int
11062306a36Sopenharmony_cinfs4_file_flush(struct file *file, fl_owner_t id)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct inode	*inode = file_inode(file);
11362306a36Sopenharmony_ci	errseq_t since;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	dprintk("NFS: flush(%pD2)\n", file);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
11862306a36Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE) == 0)
11962306a36Sopenharmony_ci		return 0;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * If we're holding a write delegation, then check if we're required
12362306a36Sopenharmony_ci	 * to flush the i/o on close. If not, then just start the i/o now.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci	if (!nfs4_delegation_flush_on_close(inode))
12662306a36Sopenharmony_ci		return filemap_fdatawrite(file->f_mapping);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Flush writes to the server and return any errors */
12962306a36Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
13062306a36Sopenharmony_ci	nfs_wb_all(inode);
13162306a36Sopenharmony_ci	return filemap_check_wb_err(file->f_mapping, since);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
13562306a36Sopenharmony_cistatic ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
13662306a36Sopenharmony_ci				      struct file *file_out, loff_t pos_out,
13762306a36Sopenharmony_ci				      size_t count, unsigned int flags)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	struct nfs42_copy_notify_res *cn_resp = NULL;
14062306a36Sopenharmony_ci	struct nl4_server *nss = NULL;
14162306a36Sopenharmony_ci	nfs4_stateid *cnrs = NULL;
14262306a36Sopenharmony_ci	ssize_t ret;
14362306a36Sopenharmony_ci	bool sync = false;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* Only offload copy if superblock is the same */
14662306a36Sopenharmony_ci	if (file_in->f_op != &nfs4_file_operations)
14762306a36Sopenharmony_ci		return -EXDEV;
14862306a36Sopenharmony_ci	if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY) ||
14962306a36Sopenharmony_ci	    !nfs_server_capable(file_inode(file_in), NFS_CAP_COPY))
15062306a36Sopenharmony_ci		return -EOPNOTSUPP;
15162306a36Sopenharmony_ci	if (file_inode(file_in) == file_inode(file_out))
15262306a36Sopenharmony_ci		return -EOPNOTSUPP;
15362306a36Sopenharmony_ci	/* if the copy size if smaller than 2 RPC payloads, make it
15462306a36Sopenharmony_ci	 * synchronous
15562306a36Sopenharmony_ci	 */
15662306a36Sopenharmony_ci	if (count <= 2 * NFS_SERVER(file_inode(file_in))->rsize)
15762306a36Sopenharmony_ci		sync = true;
15862306a36Sopenharmony_ciretry:
15962306a36Sopenharmony_ci	if (!nfs42_files_from_same_server(file_in, file_out)) {
16062306a36Sopenharmony_ci		/*
16162306a36Sopenharmony_ci		 * for inter copy, if copy size is too small
16262306a36Sopenharmony_ci		 * then fallback to generic copy.
16362306a36Sopenharmony_ci		 */
16462306a36Sopenharmony_ci		if (sync)
16562306a36Sopenharmony_ci			return -EOPNOTSUPP;
16662306a36Sopenharmony_ci		cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res),
16762306a36Sopenharmony_ci				  GFP_KERNEL);
16862306a36Sopenharmony_ci		if (unlikely(cn_resp == NULL))
16962306a36Sopenharmony_ci			return -ENOMEM;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		ret = nfs42_proc_copy_notify(file_in, file_out, cn_resp);
17262306a36Sopenharmony_ci		if (ret) {
17362306a36Sopenharmony_ci			ret = -EOPNOTSUPP;
17462306a36Sopenharmony_ci			goto out;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci		nss = &cn_resp->cnr_src;
17762306a36Sopenharmony_ci		cnrs = &cn_resp->cnr_stateid;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	ret = nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count,
18062306a36Sopenharmony_ci				nss, cnrs, sync);
18162306a36Sopenharmony_ciout:
18262306a36Sopenharmony_ci	kfree(cn_resp);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (ret == -EAGAIN)
18562306a36Sopenharmony_ci		goto retry;
18662306a36Sopenharmony_ci	return ret;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
19062306a36Sopenharmony_ci				    struct file *file_out, loff_t pos_out,
19162306a36Sopenharmony_ci				    size_t count, unsigned int flags)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	ssize_t ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count,
19662306a36Sopenharmony_ci				     flags);
19762306a36Sopenharmony_ci	if (ret == -EOPNOTSUPP || ret == -EXDEV)
19862306a36Sopenharmony_ci		ret = generic_copy_file_range(file_in, pos_in, file_out,
19962306a36Sopenharmony_ci					      pos_out, count, flags);
20062306a36Sopenharmony_ci	return ret;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic loff_t nfs4_file_llseek(struct file *filep, loff_t offset, int whence)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	loff_t ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	switch (whence) {
20862306a36Sopenharmony_ci	case SEEK_HOLE:
20962306a36Sopenharmony_ci	case SEEK_DATA:
21062306a36Sopenharmony_ci		ret = nfs42_proc_llseek(filep, offset, whence);
21162306a36Sopenharmony_ci		if (ret != -EOPNOTSUPP)
21262306a36Sopenharmony_ci			return ret;
21362306a36Sopenharmony_ci		fallthrough;
21462306a36Sopenharmony_ci	default:
21562306a36Sopenharmony_ci		return nfs_file_llseek(filep, offset, whence);
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t len)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct inode *inode = file_inode(filep);
22262306a36Sopenharmony_ci	long ret;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (!S_ISREG(inode->i_mode))
22562306a36Sopenharmony_ci		return -EOPNOTSUPP;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
22862306a36Sopenharmony_ci		return -EOPNOTSUPP;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	ret = inode_newsize_ok(inode, offset + len);
23162306a36Sopenharmony_ci	if (ret < 0)
23262306a36Sopenharmony_ci		return ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (mode & FALLOC_FL_PUNCH_HOLE)
23562306a36Sopenharmony_ci		return nfs42_proc_deallocate(filep, offset, len);
23662306a36Sopenharmony_ci	return nfs42_proc_allocate(filep, offset, len);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off,
24062306a36Sopenharmony_ci		struct file *dst_file, loff_t dst_off, loff_t count,
24162306a36Sopenharmony_ci		unsigned int remap_flags)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct inode *dst_inode = file_inode(dst_file);
24462306a36Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(dst_inode);
24562306a36Sopenharmony_ci	struct inode *src_inode = file_inode(src_file);
24662306a36Sopenharmony_ci	unsigned int bs = server->clone_blksize;
24762306a36Sopenharmony_ci	bool same_inode = false;
24862306a36Sopenharmony_ci	int ret;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* NFS does not support deduplication. */
25162306a36Sopenharmony_ci	if (remap_flags & REMAP_FILE_DEDUP)
25262306a36Sopenharmony_ci		return -EOPNOTSUPP;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (remap_flags & ~REMAP_FILE_ADVISORY)
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (IS_SWAPFILE(dst_inode) || IS_SWAPFILE(src_inode))
25862306a36Sopenharmony_ci		return -ETXTBSY;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	/* check alignment w.r.t. clone_blksize */
26162306a36Sopenharmony_ci	ret = -EINVAL;
26262306a36Sopenharmony_ci	if (bs) {
26362306a36Sopenharmony_ci		if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs))
26462306a36Sopenharmony_ci			goto out;
26562306a36Sopenharmony_ci		if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count))
26662306a36Sopenharmony_ci			goto out;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (src_inode == dst_inode)
27062306a36Sopenharmony_ci		same_inode = true;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
27362306a36Sopenharmony_ci	if (same_inode) {
27462306a36Sopenharmony_ci		inode_lock(src_inode);
27562306a36Sopenharmony_ci	} else if (dst_inode < src_inode) {
27662306a36Sopenharmony_ci		inode_lock_nested(dst_inode, I_MUTEX_PARENT);
27762306a36Sopenharmony_ci		inode_lock_nested(src_inode, I_MUTEX_CHILD);
27862306a36Sopenharmony_ci	} else {
27962306a36Sopenharmony_ci		inode_lock_nested(src_inode, I_MUTEX_PARENT);
28062306a36Sopenharmony_ci		inode_lock_nested(dst_inode, I_MUTEX_CHILD);
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	/* flush all pending writes on both src and dst so that server
28462306a36Sopenharmony_ci	 * has the latest data */
28562306a36Sopenharmony_ci	ret = nfs_sync_inode(src_inode);
28662306a36Sopenharmony_ci	if (ret)
28762306a36Sopenharmony_ci		goto out_unlock;
28862306a36Sopenharmony_ci	ret = nfs_sync_inode(dst_inode);
28962306a36Sopenharmony_ci	if (ret)
29062306a36Sopenharmony_ci		goto out_unlock;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ret = nfs42_proc_clone(src_file, dst_file, src_off, dst_off, count);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* truncate inode page cache of the dst range so that future reads can fetch
29562306a36Sopenharmony_ci	 * new data from server */
29662306a36Sopenharmony_ci	if (!ret)
29762306a36Sopenharmony_ci		truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ciout_unlock:
30062306a36Sopenharmony_ci	if (same_inode) {
30162306a36Sopenharmony_ci		inode_unlock(src_inode);
30262306a36Sopenharmony_ci	} else if (dst_inode < src_inode) {
30362306a36Sopenharmony_ci		inode_unlock(src_inode);
30462306a36Sopenharmony_ci		inode_unlock(dst_inode);
30562306a36Sopenharmony_ci	} else {
30662306a36Sopenharmony_ci		inode_unlock(dst_inode);
30762306a36Sopenharmony_ci		inode_unlock(src_inode);
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ciout:
31062306a36Sopenharmony_ci	return ret < 0 ? ret : count;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int read_name_gen = 1;
31462306a36Sopenharmony_ci#define SSC_READ_NAME_BODY "ssc_read_%d"
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt,
31762306a36Sopenharmony_ci		struct nfs_fh *src_fh, nfs4_stateid *stateid)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct nfs_fattr *fattr = nfs_alloc_fattr();
32062306a36Sopenharmony_ci	struct file *filep, *res;
32162306a36Sopenharmony_ci	struct nfs_server *server;
32262306a36Sopenharmony_ci	struct inode *r_ino = NULL;
32362306a36Sopenharmony_ci	struct nfs_open_context *ctx;
32462306a36Sopenharmony_ci	struct nfs4_state_owner *sp;
32562306a36Sopenharmony_ci	char *read_name = NULL;
32662306a36Sopenharmony_ci	int len, status = 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	server = NFS_SB(ss_mnt->mnt_sb);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!fattr)
33162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	status = nfs4_proc_getattr(server, src_fh, fattr, NULL);
33462306a36Sopenharmony_ci	if (status < 0) {
33562306a36Sopenharmony_ci		res = ERR_PTR(status);
33662306a36Sopenharmony_ci		goto out;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (!S_ISREG(fattr->mode)) {
34062306a36Sopenharmony_ci		res = ERR_PTR(-EBADF);
34162306a36Sopenharmony_ci		goto out;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	res = ERR_PTR(-ENOMEM);
34562306a36Sopenharmony_ci	len = strlen(SSC_READ_NAME_BODY) + 16;
34662306a36Sopenharmony_ci	read_name = kzalloc(len, GFP_KERNEL);
34762306a36Sopenharmony_ci	if (read_name == NULL)
34862306a36Sopenharmony_ci		goto out;
34962306a36Sopenharmony_ci	snprintf(read_name, len, SSC_READ_NAME_BODY, read_name_gen++);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	r_ino = nfs_fhget(ss_mnt->mnt_sb, src_fh, fattr);
35262306a36Sopenharmony_ci	if (IS_ERR(r_ino)) {
35362306a36Sopenharmony_ci		res = ERR_CAST(r_ino);
35462306a36Sopenharmony_ci		goto out_free_name;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	filep = alloc_file_pseudo(r_ino, ss_mnt, read_name, O_RDONLY,
35862306a36Sopenharmony_ci				     r_ino->i_fop);
35962306a36Sopenharmony_ci	if (IS_ERR(filep)) {
36062306a36Sopenharmony_ci		res = ERR_CAST(filep);
36162306a36Sopenharmony_ci		iput(r_ino);
36262306a36Sopenharmony_ci		goto out_free_name;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	ctx = alloc_nfs_open_context(filep->f_path.dentry,
36662306a36Sopenharmony_ci				     flags_to_mode(filep->f_flags), filep);
36762306a36Sopenharmony_ci	if (IS_ERR(ctx)) {
36862306a36Sopenharmony_ci		res = ERR_CAST(ctx);
36962306a36Sopenharmony_ci		goto out_filep;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	res = ERR_PTR(-EINVAL);
37362306a36Sopenharmony_ci	sp = nfs4_get_state_owner(server, ctx->cred, GFP_KERNEL);
37462306a36Sopenharmony_ci	if (sp == NULL)
37562306a36Sopenharmony_ci		goto out_ctx;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	ctx->state = nfs4_get_open_state(r_ino, sp);
37862306a36Sopenharmony_ci	if (ctx->state == NULL)
37962306a36Sopenharmony_ci		goto out_stateowner;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	set_bit(NFS_SRV_SSC_COPY_STATE, &ctx->state->flags);
38262306a36Sopenharmony_ci	memcpy(&ctx->state->open_stateid.other, &stateid->other,
38362306a36Sopenharmony_ci	       NFS4_STATEID_OTHER_SIZE);
38462306a36Sopenharmony_ci	update_open_stateid(ctx->state, stateid, NULL, filep->f_mode);
38562306a36Sopenharmony_ci	set_bit(NFS_OPEN_STATE, &ctx->state->flags);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	nfs_file_set_open_context(filep, ctx);
38862306a36Sopenharmony_ci	put_nfs_open_context(ctx);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	file_ra_state_init(&filep->f_ra, filep->f_mapping->host->i_mapping);
39162306a36Sopenharmony_ci	res = filep;
39262306a36Sopenharmony_ciout_free_name:
39362306a36Sopenharmony_ci	kfree(read_name);
39462306a36Sopenharmony_ciout:
39562306a36Sopenharmony_ci	nfs_free_fattr(fattr);
39662306a36Sopenharmony_ci	return res;
39762306a36Sopenharmony_ciout_stateowner:
39862306a36Sopenharmony_ci	nfs4_put_state_owner(sp);
39962306a36Sopenharmony_ciout_ctx:
40062306a36Sopenharmony_ci	put_nfs_open_context(ctx);
40162306a36Sopenharmony_ciout_filep:
40262306a36Sopenharmony_ci	fput(filep);
40362306a36Sopenharmony_ci	goto out_free_name;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistatic void __nfs42_ssc_close(struct file *filep)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(filep);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ctx->state->flags = 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = {
41462306a36Sopenharmony_ci	.sco_open = __nfs42_ssc_open,
41562306a36Sopenharmony_ci	.sco_close = __nfs42_ssc_close,
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/**
41962306a36Sopenharmony_ci * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common
42062306a36Sopenharmony_ci *
42162306a36Sopenharmony_ci * Return values:
42262306a36Sopenharmony_ci *   None
42362306a36Sopenharmony_ci */
42462306a36Sopenharmony_civoid nfs42_ssc_register_ops(void)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl);
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci/**
43062306a36Sopenharmony_ci * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common
43162306a36Sopenharmony_ci *
43262306a36Sopenharmony_ci * Return values:
43362306a36Sopenharmony_ci *   None.
43462306a36Sopenharmony_ci */
43562306a36Sopenharmony_civoid nfs42_ssc_unregister_ops(void)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic int nfs4_setlease(struct file *file, int arg, struct file_lock **lease,
44262306a36Sopenharmony_ci			 void **priv)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	return nfs4_proc_setlease(file, arg, lease, priv);
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ciconst struct file_operations nfs4_file_operations = {
44862306a36Sopenharmony_ci	.read_iter	= nfs_file_read,
44962306a36Sopenharmony_ci	.write_iter	= nfs_file_write,
45062306a36Sopenharmony_ci	.mmap		= nfs_file_mmap,
45162306a36Sopenharmony_ci	.open		= nfs4_file_open,
45262306a36Sopenharmony_ci	.flush		= nfs4_file_flush,
45362306a36Sopenharmony_ci	.release	= nfs_file_release,
45462306a36Sopenharmony_ci	.fsync		= nfs_file_fsync,
45562306a36Sopenharmony_ci	.lock		= nfs_lock,
45662306a36Sopenharmony_ci	.flock		= nfs_flock,
45762306a36Sopenharmony_ci	.splice_read	= nfs_file_splice_read,
45862306a36Sopenharmony_ci	.splice_write	= iter_file_splice_write,
45962306a36Sopenharmony_ci	.check_flags	= nfs_check_flags,
46062306a36Sopenharmony_ci	.setlease	= nfs4_setlease,
46162306a36Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
46262306a36Sopenharmony_ci	.copy_file_range = nfs4_copy_file_range,
46362306a36Sopenharmony_ci	.llseek		= nfs4_file_llseek,
46462306a36Sopenharmony_ci	.fallocate	= nfs42_fallocate,
46562306a36Sopenharmony_ci	.remap_file_range = nfs42_remap_file_range,
46662306a36Sopenharmony_ci#else
46762306a36Sopenharmony_ci	.llseek		= nfs_file_llseek,
46862306a36Sopenharmony_ci#endif
46962306a36Sopenharmony_ci};
470