18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  linux/fs/nfs/file.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1992  Rick Sladkey
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/fs.h>
88c2ecf20Sopenharmony_ci#include <linux/file.h>
98c2ecf20Sopenharmony_ci#include <linux/falloc.h>
108c2ecf20Sopenharmony_ci#include <linux/mount.h>
118c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
128c2ecf20Sopenharmony_ci#include <linux/nfs_ssc.h>
138c2ecf20Sopenharmony_ci#include "delegation.h"
148c2ecf20Sopenharmony_ci#include "internal.h"
158c2ecf20Sopenharmony_ci#include "iostat.h"
168c2ecf20Sopenharmony_ci#include "fscache.h"
178c2ecf20Sopenharmony_ci#include "pnfs.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "nfstrace.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
228c2ecf20Sopenharmony_ci#include "nfs42.h"
238c2ecf20Sopenharmony_ci#endif
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_FILE
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int
288c2ecf20Sopenharmony_cinfs4_file_open(struct inode *inode, struct file *filp)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx;
318c2ecf20Sopenharmony_ci	struct dentry *dentry = file_dentry(filp);
328c2ecf20Sopenharmony_ci	struct dentry *parent = NULL;
338c2ecf20Sopenharmony_ci	struct inode *dir;
348c2ecf20Sopenharmony_ci	unsigned openflags = filp->f_flags;
358c2ecf20Sopenharmony_ci	fmode_t f_mode;
368c2ecf20Sopenharmony_ci	struct iattr attr;
378c2ecf20Sopenharmony_ci	int err;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/*
408c2ecf20Sopenharmony_ci	 * If no cached dentry exists or if it's negative, NFSv4 handled the
418c2ecf20Sopenharmony_ci	 * opens in ->lookup() or ->create().
428c2ecf20Sopenharmony_ci	 *
438c2ecf20Sopenharmony_ci	 * We only get this far for a cached positive dentry.  We skipped
448c2ecf20Sopenharmony_ci	 * revalidation, so handle it here by dropping the dentry and returning
458c2ecf20Sopenharmony_ci	 * -EOPENSTALE.  The VFS will retry the lookup/create/open.
468c2ecf20Sopenharmony_ci	 */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	dprintk("NFS: open file(%pd2)\n", dentry);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	err = nfs_check_flags(openflags);
518c2ecf20Sopenharmony_ci	if (err)
528c2ecf20Sopenharmony_ci		return err;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	f_mode = filp->f_mode;
558c2ecf20Sopenharmony_ci	if ((openflags & O_ACCMODE) == 3)
568c2ecf20Sopenharmony_ci		f_mode |= flags_to_mode(openflags);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* We can't create new files here */
598c2ecf20Sopenharmony_ci	openflags &= ~(O_CREAT|O_EXCL);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	parent = dget_parent(dentry);
628c2ecf20Sopenharmony_ci	dir = d_inode(parent);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ctx = alloc_nfs_open_context(file_dentry(filp), f_mode, filp);
658c2ecf20Sopenharmony_ci	err = PTR_ERR(ctx);
668c2ecf20Sopenharmony_ci	if (IS_ERR(ctx))
678c2ecf20Sopenharmony_ci		goto out;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	attr.ia_valid = ATTR_OPEN;
708c2ecf20Sopenharmony_ci	if (openflags & O_TRUNC) {
718c2ecf20Sopenharmony_ci		attr.ia_valid |= ATTR_SIZE;
728c2ecf20Sopenharmony_ci		attr.ia_size = 0;
738c2ecf20Sopenharmony_ci		filemap_write_and_wait(inode->i_mapping);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, NULL);
778c2ecf20Sopenharmony_ci	if (IS_ERR(inode)) {
788c2ecf20Sopenharmony_ci		err = PTR_ERR(inode);
798c2ecf20Sopenharmony_ci		switch (err) {
808c2ecf20Sopenharmony_ci		default:
818c2ecf20Sopenharmony_ci			goto out_put_ctx;
828c2ecf20Sopenharmony_ci		case -ENOENT:
838c2ecf20Sopenharmony_ci		case -ESTALE:
848c2ecf20Sopenharmony_ci		case -EISDIR:
858c2ecf20Sopenharmony_ci		case -ENOTDIR:
868c2ecf20Sopenharmony_ci		case -ELOOP:
878c2ecf20Sopenharmony_ci			goto out_drop;
888c2ecf20Sopenharmony_ci		}
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	if (inode != d_inode(dentry))
918c2ecf20Sopenharmony_ci		goto out_drop;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	nfs_file_set_open_context(filp, ctx);
948c2ecf20Sopenharmony_ci	nfs_fscache_open_file(inode, filp);
958c2ecf20Sopenharmony_ci	err = 0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciout_put_ctx:
988c2ecf20Sopenharmony_ci	put_nfs_open_context(ctx);
998c2ecf20Sopenharmony_ciout:
1008c2ecf20Sopenharmony_ci	dput(parent);
1018c2ecf20Sopenharmony_ci	return err;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ciout_drop:
1048c2ecf20Sopenharmony_ci	d_drop(dentry);
1058c2ecf20Sopenharmony_ci	err = -EOPENSTALE;
1068c2ecf20Sopenharmony_ci	goto out_put_ctx;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Flush all dirty pages, and check for write errors.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistatic int
1138c2ecf20Sopenharmony_cinfs4_file_flush(struct file *file, fl_owner_t id)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct inode	*inode = file_inode(file);
1168c2ecf20Sopenharmony_ci	errseq_t since;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	dprintk("NFS: flush(%pD2)\n", file);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
1218c2ecf20Sopenharmony_ci	if ((file->f_mode & FMODE_WRITE) == 0)
1228c2ecf20Sopenharmony_ci		return 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/*
1258c2ecf20Sopenharmony_ci	 * If we're holding a write delegation, then check if we're required
1268c2ecf20Sopenharmony_ci	 * to flush the i/o on close. If not, then just start the i/o now.
1278c2ecf20Sopenharmony_ci	 */
1288c2ecf20Sopenharmony_ci	if (!nfs4_delegation_flush_on_close(inode))
1298c2ecf20Sopenharmony_ci		return filemap_fdatawrite(file->f_mapping);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* Flush writes to the server and return any errors */
1328c2ecf20Sopenharmony_ci	since = filemap_sample_wb_err(file->f_mapping);
1338c2ecf20Sopenharmony_ci	nfs_wb_all(inode);
1348c2ecf20Sopenharmony_ci	return filemap_check_wb_err(file->f_mapping, since);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
1388c2ecf20Sopenharmony_cistatic ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
1398c2ecf20Sopenharmony_ci				      struct file *file_out, loff_t pos_out,
1408c2ecf20Sopenharmony_ci				      size_t count, unsigned int flags)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct nfs42_copy_notify_res *cn_resp = NULL;
1438c2ecf20Sopenharmony_ci	struct nl4_server *nss = NULL;
1448c2ecf20Sopenharmony_ci	nfs4_stateid *cnrs = NULL;
1458c2ecf20Sopenharmony_ci	ssize_t ret;
1468c2ecf20Sopenharmony_ci	bool sync = false;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Only offload copy if superblock is the same */
1498c2ecf20Sopenharmony_ci	if (file_in->f_op != &nfs4_file_operations)
1508c2ecf20Sopenharmony_ci		return -EXDEV;
1518c2ecf20Sopenharmony_ci	if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY) ||
1528c2ecf20Sopenharmony_ci	    !nfs_server_capable(file_inode(file_in), NFS_CAP_COPY))
1538c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1548c2ecf20Sopenharmony_ci	if (file_inode(file_in) == file_inode(file_out))
1558c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1568c2ecf20Sopenharmony_ci	/* if the copy size if smaller than 2 RPC payloads, make it
1578c2ecf20Sopenharmony_ci	 * synchronous
1588c2ecf20Sopenharmony_ci	 */
1598c2ecf20Sopenharmony_ci	if (count <= 2 * NFS_SERVER(file_inode(file_in))->rsize)
1608c2ecf20Sopenharmony_ci		sync = true;
1618c2ecf20Sopenharmony_ciretry:
1628c2ecf20Sopenharmony_ci	if (!nfs42_files_from_same_server(file_in, file_out)) {
1638c2ecf20Sopenharmony_ci		/* for inter copy, if copy size if smaller than 12 RPC
1648c2ecf20Sopenharmony_ci		 * payloads, fallback to traditional copy. There are
1658c2ecf20Sopenharmony_ci		 * 14 RPCs during an NFSv4.x mount between source/dest
1668c2ecf20Sopenharmony_ci		 * servers.
1678c2ecf20Sopenharmony_ci		 */
1688c2ecf20Sopenharmony_ci		if (sync ||
1698c2ecf20Sopenharmony_ci			count <= 14 * NFS_SERVER(file_inode(file_in))->rsize)
1708c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
1718c2ecf20Sopenharmony_ci		cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res),
1728c2ecf20Sopenharmony_ci				GFP_NOFS);
1738c2ecf20Sopenharmony_ci		if (unlikely(cn_resp == NULL))
1748c2ecf20Sopenharmony_ci			return -ENOMEM;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		ret = nfs42_proc_copy_notify(file_in, file_out, cn_resp);
1778c2ecf20Sopenharmony_ci		if (ret) {
1788c2ecf20Sopenharmony_ci			ret = -EOPNOTSUPP;
1798c2ecf20Sopenharmony_ci			goto out;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci		nss = &cn_resp->cnr_src;
1828c2ecf20Sopenharmony_ci		cnrs = &cn_resp->cnr_stateid;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	ret = nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count,
1858c2ecf20Sopenharmony_ci				nss, cnrs, sync);
1868c2ecf20Sopenharmony_ciout:
1878c2ecf20Sopenharmony_ci	if (!nfs42_files_from_same_server(file_in, file_out))
1888c2ecf20Sopenharmony_ci		kfree(cn_resp);
1898c2ecf20Sopenharmony_ci	if (ret == -EAGAIN)
1908c2ecf20Sopenharmony_ci		goto retry;
1918c2ecf20Sopenharmony_ci	return ret;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
1958c2ecf20Sopenharmony_ci				    struct file *file_out, loff_t pos_out,
1968c2ecf20Sopenharmony_ci				    size_t count, unsigned int flags)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	ssize_t ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count,
2018c2ecf20Sopenharmony_ci				     flags);
2028c2ecf20Sopenharmony_ci	if (ret == -EOPNOTSUPP || ret == -EXDEV)
2038c2ecf20Sopenharmony_ci		ret = generic_copy_file_range(file_in, pos_in, file_out,
2048c2ecf20Sopenharmony_ci					      pos_out, count, flags);
2058c2ecf20Sopenharmony_ci	return ret;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic loff_t nfs4_file_llseek(struct file *filep, loff_t offset, int whence)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	loff_t ret;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	switch (whence) {
2138c2ecf20Sopenharmony_ci	case SEEK_HOLE:
2148c2ecf20Sopenharmony_ci	case SEEK_DATA:
2158c2ecf20Sopenharmony_ci		ret = nfs42_proc_llseek(filep, offset, whence);
2168c2ecf20Sopenharmony_ci		if (ret != -EOPNOTSUPP)
2178c2ecf20Sopenharmony_ci			return ret;
2188c2ecf20Sopenharmony_ci		fallthrough;
2198c2ecf20Sopenharmony_ci	default:
2208c2ecf20Sopenharmony_ci		return nfs_file_llseek(filep, offset, whence);
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t len)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(filep);
2278c2ecf20Sopenharmony_ci	long ret;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (!S_ISREG(inode->i_mode))
2308c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
2338c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	ret = inode_newsize_ok(inode, offset + len);
2368c2ecf20Sopenharmony_ci	if (ret < 0)
2378c2ecf20Sopenharmony_ci		return ret;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (mode & FALLOC_FL_PUNCH_HOLE)
2408c2ecf20Sopenharmony_ci		return nfs42_proc_deallocate(filep, offset, len);
2418c2ecf20Sopenharmony_ci	return nfs42_proc_allocate(filep, offset, len);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off,
2458c2ecf20Sopenharmony_ci		struct file *dst_file, loff_t dst_off, loff_t count,
2468c2ecf20Sopenharmony_ci		unsigned int remap_flags)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	struct inode *dst_inode = file_inode(dst_file);
2498c2ecf20Sopenharmony_ci	struct nfs_server *server = NFS_SERVER(dst_inode);
2508c2ecf20Sopenharmony_ci	struct inode *src_inode = file_inode(src_file);
2518c2ecf20Sopenharmony_ci	unsigned int bs = server->clone_blksize;
2528c2ecf20Sopenharmony_ci	bool same_inode = false;
2538c2ecf20Sopenharmony_ci	int ret;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	/* NFS does not support deduplication. */
2568c2ecf20Sopenharmony_ci	if (remap_flags & REMAP_FILE_DEDUP)
2578c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (remap_flags & ~REMAP_FILE_ADVISORY)
2608c2ecf20Sopenharmony_ci		return -EINVAL;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (IS_SWAPFILE(dst_inode) || IS_SWAPFILE(src_inode))
2638c2ecf20Sopenharmony_ci		return -ETXTBSY;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* check alignment w.r.t. clone_blksize */
2668c2ecf20Sopenharmony_ci	ret = -EINVAL;
2678c2ecf20Sopenharmony_ci	if (bs) {
2688c2ecf20Sopenharmony_ci		if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs))
2698c2ecf20Sopenharmony_ci			goto out;
2708c2ecf20Sopenharmony_ci		if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count))
2718c2ecf20Sopenharmony_ci			goto out;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (src_inode == dst_inode)
2758c2ecf20Sopenharmony_ci		same_inode = true;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
2788c2ecf20Sopenharmony_ci	if (same_inode) {
2798c2ecf20Sopenharmony_ci		inode_lock(src_inode);
2808c2ecf20Sopenharmony_ci	} else if (dst_inode < src_inode) {
2818c2ecf20Sopenharmony_ci		inode_lock_nested(dst_inode, I_MUTEX_PARENT);
2828c2ecf20Sopenharmony_ci		inode_lock_nested(src_inode, I_MUTEX_CHILD);
2838c2ecf20Sopenharmony_ci	} else {
2848c2ecf20Sopenharmony_ci		inode_lock_nested(src_inode, I_MUTEX_PARENT);
2858c2ecf20Sopenharmony_ci		inode_lock_nested(dst_inode, I_MUTEX_CHILD);
2868c2ecf20Sopenharmony_ci	}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* flush all pending writes on both src and dst so that server
2898c2ecf20Sopenharmony_ci	 * has the latest data */
2908c2ecf20Sopenharmony_ci	ret = nfs_sync_inode(src_inode);
2918c2ecf20Sopenharmony_ci	if (ret)
2928c2ecf20Sopenharmony_ci		goto out_unlock;
2938c2ecf20Sopenharmony_ci	ret = nfs_sync_inode(dst_inode);
2948c2ecf20Sopenharmony_ci	if (ret)
2958c2ecf20Sopenharmony_ci		goto out_unlock;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	ret = nfs42_proc_clone(src_file, dst_file, src_off, dst_off, count);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	/* truncate inode page cache of the dst range so that future reads can fetch
3008c2ecf20Sopenharmony_ci	 * new data from server */
3018c2ecf20Sopenharmony_ci	if (!ret)
3028c2ecf20Sopenharmony_ci		truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ciout_unlock:
3058c2ecf20Sopenharmony_ci	if (same_inode) {
3068c2ecf20Sopenharmony_ci		inode_unlock(src_inode);
3078c2ecf20Sopenharmony_ci	} else if (dst_inode < src_inode) {
3088c2ecf20Sopenharmony_ci		inode_unlock(src_inode);
3098c2ecf20Sopenharmony_ci		inode_unlock(dst_inode);
3108c2ecf20Sopenharmony_ci	} else {
3118c2ecf20Sopenharmony_ci		inode_unlock(dst_inode);
3128c2ecf20Sopenharmony_ci		inode_unlock(src_inode);
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ciout:
3158c2ecf20Sopenharmony_ci	return ret < 0 ? ret : count;
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int read_name_gen = 1;
3198c2ecf20Sopenharmony_ci#define SSC_READ_NAME_BODY "ssc_read_%d"
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt,
3228c2ecf20Sopenharmony_ci		struct nfs_fh *src_fh, nfs4_stateid *stateid)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct nfs_fattr *fattr = nfs_alloc_fattr();
3258c2ecf20Sopenharmony_ci	struct file *filep, *res;
3268c2ecf20Sopenharmony_ci	struct nfs_server *server;
3278c2ecf20Sopenharmony_ci	struct inode *r_ino = NULL;
3288c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx;
3298c2ecf20Sopenharmony_ci	struct nfs4_state_owner *sp;
3308c2ecf20Sopenharmony_ci	char *read_name = NULL;
3318c2ecf20Sopenharmony_ci	int len, status = 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	server = NFS_SERVER(ss_mnt->mnt_root->d_inode);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (!fattr)
3368c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	status = nfs4_proc_getattr(server, src_fh, fattr, NULL, NULL);
3398c2ecf20Sopenharmony_ci	if (status < 0) {
3408c2ecf20Sopenharmony_ci		res = ERR_PTR(status);
3418c2ecf20Sopenharmony_ci		goto out;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (!S_ISREG(fattr->mode)) {
3458c2ecf20Sopenharmony_ci		res = ERR_PTR(-EBADF);
3468c2ecf20Sopenharmony_ci		goto out;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	res = ERR_PTR(-ENOMEM);
3508c2ecf20Sopenharmony_ci	len = strlen(SSC_READ_NAME_BODY) + 16;
3518c2ecf20Sopenharmony_ci	read_name = kzalloc(len, GFP_NOFS);
3528c2ecf20Sopenharmony_ci	if (read_name == NULL)
3538c2ecf20Sopenharmony_ci		goto out;
3548c2ecf20Sopenharmony_ci	snprintf(read_name, len, SSC_READ_NAME_BODY, read_name_gen++);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	r_ino = nfs_fhget(ss_mnt->mnt_root->d_inode->i_sb, src_fh, fattr,
3578c2ecf20Sopenharmony_ci			NULL);
3588c2ecf20Sopenharmony_ci	if (IS_ERR(r_ino)) {
3598c2ecf20Sopenharmony_ci		res = ERR_CAST(r_ino);
3608c2ecf20Sopenharmony_ci		goto out_free_name;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	filep = alloc_file_pseudo(r_ino, ss_mnt, read_name, FMODE_READ,
3648c2ecf20Sopenharmony_ci				     r_ino->i_fop);
3658c2ecf20Sopenharmony_ci	if (IS_ERR(filep)) {
3668c2ecf20Sopenharmony_ci		res = ERR_CAST(filep);
3678c2ecf20Sopenharmony_ci		iput(r_ino);
3688c2ecf20Sopenharmony_ci		goto out_free_name;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci	filep->f_mode |= FMODE_READ;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	ctx = alloc_nfs_open_context(filep->f_path.dentry, filep->f_mode,
3738c2ecf20Sopenharmony_ci					filep);
3748c2ecf20Sopenharmony_ci	if (IS_ERR(ctx)) {
3758c2ecf20Sopenharmony_ci		res = ERR_CAST(ctx);
3768c2ecf20Sopenharmony_ci		goto out_filep;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	res = ERR_PTR(-EINVAL);
3808c2ecf20Sopenharmony_ci	sp = nfs4_get_state_owner(server, ctx->cred, GFP_KERNEL);
3818c2ecf20Sopenharmony_ci	if (sp == NULL)
3828c2ecf20Sopenharmony_ci		goto out_ctx;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	ctx->state = nfs4_get_open_state(r_ino, sp);
3858c2ecf20Sopenharmony_ci	if (ctx->state == NULL)
3868c2ecf20Sopenharmony_ci		goto out_stateowner;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	set_bit(NFS_SRV_SSC_COPY_STATE, &ctx->state->flags);
3898c2ecf20Sopenharmony_ci	memcpy(&ctx->state->open_stateid.other, &stateid->other,
3908c2ecf20Sopenharmony_ci	       NFS4_STATEID_OTHER_SIZE);
3918c2ecf20Sopenharmony_ci	update_open_stateid(ctx->state, stateid, NULL, filep->f_mode);
3928c2ecf20Sopenharmony_ci	set_bit(NFS_OPEN_STATE, &ctx->state->flags);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	nfs_file_set_open_context(filep, ctx);
3958c2ecf20Sopenharmony_ci	put_nfs_open_context(ctx);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	file_ra_state_init(&filep->f_ra, filep->f_mapping->host->i_mapping);
3988c2ecf20Sopenharmony_ci	res = filep;
3998c2ecf20Sopenharmony_ciout_free_name:
4008c2ecf20Sopenharmony_ci	kfree(read_name);
4018c2ecf20Sopenharmony_ciout:
4028c2ecf20Sopenharmony_ci	nfs_free_fattr(fattr);
4038c2ecf20Sopenharmony_ci	return res;
4048c2ecf20Sopenharmony_ciout_stateowner:
4058c2ecf20Sopenharmony_ci	nfs4_put_state_owner(sp);
4068c2ecf20Sopenharmony_ciout_ctx:
4078c2ecf20Sopenharmony_ci	put_nfs_open_context(ctx);
4088c2ecf20Sopenharmony_ciout_filep:
4098c2ecf20Sopenharmony_ci	fput(filep);
4108c2ecf20Sopenharmony_ci	goto out_free_name;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void __nfs42_ssc_close(struct file *filep)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct nfs_open_context *ctx = nfs_file_open_context(filep);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ctx->state->flags = 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = {
4218c2ecf20Sopenharmony_ci	.sco_open = __nfs42_ssc_open,
4228c2ecf20Sopenharmony_ci	.sco_close = __nfs42_ssc_close,
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/**
4268c2ecf20Sopenharmony_ci * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common
4278c2ecf20Sopenharmony_ci *
4288c2ecf20Sopenharmony_ci * Return values:
4298c2ecf20Sopenharmony_ci *   None
4308c2ecf20Sopenharmony_ci */
4318c2ecf20Sopenharmony_civoid nfs42_ssc_register_ops(void)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl);
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci/**
4378c2ecf20Sopenharmony_ci * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common
4388c2ecf20Sopenharmony_ci *
4398c2ecf20Sopenharmony_ci * Return values:
4408c2ecf20Sopenharmony_ci *   None.
4418c2ecf20Sopenharmony_ci */
4428c2ecf20Sopenharmony_civoid nfs42_ssc_unregister_ops(void)
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci#endif /* CONFIG_NFS_V4_2 */
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ciconst struct file_operations nfs4_file_operations = {
4498c2ecf20Sopenharmony_ci	.read_iter	= nfs_file_read,
4508c2ecf20Sopenharmony_ci	.write_iter	= nfs_file_write,
4518c2ecf20Sopenharmony_ci	.mmap		= nfs_file_mmap,
4528c2ecf20Sopenharmony_ci	.open		= nfs4_file_open,
4538c2ecf20Sopenharmony_ci	.flush		= nfs4_file_flush,
4548c2ecf20Sopenharmony_ci	.release	= nfs_file_release,
4558c2ecf20Sopenharmony_ci	.fsync		= nfs_file_fsync,
4568c2ecf20Sopenharmony_ci	.lock		= nfs_lock,
4578c2ecf20Sopenharmony_ci	.flock		= nfs_flock,
4588c2ecf20Sopenharmony_ci	.splice_read	= generic_file_splice_read,
4598c2ecf20Sopenharmony_ci	.splice_write	= iter_file_splice_write,
4608c2ecf20Sopenharmony_ci	.check_flags	= nfs_check_flags,
4618c2ecf20Sopenharmony_ci	.setlease	= simple_nosetlease,
4628c2ecf20Sopenharmony_ci#ifdef CONFIG_NFS_V4_2
4638c2ecf20Sopenharmony_ci	.copy_file_range = nfs4_copy_file_range,
4648c2ecf20Sopenharmony_ci	.llseek		= nfs4_file_llseek,
4658c2ecf20Sopenharmony_ci	.fallocate	= nfs42_fallocate,
4668c2ecf20Sopenharmony_ci	.remap_file_range = nfs42_remap_file_range,
4678c2ecf20Sopenharmony_ci#else
4688c2ecf20Sopenharmony_ci	.llseek		= nfs_file_llseek,
4698c2ecf20Sopenharmony_ci#endif
4708c2ecf20Sopenharmony_ci};
471