xref: /openbmc/linux/fs/nfs/nfs4file.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ce4ef7c0SBryan Schumaker /*
3ce4ef7c0SBryan Schumaker  *  linux/fs/nfs/file.c
4ce4ef7c0SBryan Schumaker  *
5ce4ef7c0SBryan Schumaker  *  Copyright (C) 1992  Rick Sladkey
6ce4ef7c0SBryan Schumaker  */
7f4ac1674SAnna Schumaker #include <linux/fs.h>
8bea51b30SPeng Tao #include <linux/file.h>
9f4ac1674SAnna Schumaker #include <linux/falloc.h>
10f2aedb71SDavid Howells #include <linux/mount.h>
11ce4ef7c0SBryan Schumaker #include <linux/nfs_fs.h>
120cfcd405SDai Ngo #include <linux/nfs_ssc.h>
135445b1fbSTrond Myklebust #include "delegation.h"
14ce4ef7c0SBryan Schumaker #include "internal.h"
155445b1fbSTrond Myklebust #include "iostat.h"
16a4ff1468SDavid Howells #include "fscache.h"
17ce4ef7c0SBryan Schumaker #include "pnfs.h"
18ce4ef7c0SBryan Schumaker 
1981b79afbSTrond Myklebust #include "nfstrace.h"
2081b79afbSTrond Myklebust 
211c6dcbe5SAnna Schumaker #ifdef CONFIG_NFS_V4_2
221c6dcbe5SAnna Schumaker #include "nfs42.h"
231c6dcbe5SAnna Schumaker #endif
241c6dcbe5SAnna Schumaker 
25ce4ef7c0SBryan Schumaker #define NFSDBG_FACILITY		NFSDBG_FILE
26ce4ef7c0SBryan Schumaker 
27ce4ef7c0SBryan Schumaker static int
nfs4_file_open(struct inode * inode,struct file * filp)28ce4ef7c0SBryan Schumaker nfs4_file_open(struct inode *inode, struct file *filp)
29ce4ef7c0SBryan Schumaker {
30ce4ef7c0SBryan Schumaker 	struct nfs_open_context *ctx;
31be62a1a8SMiklos Szeredi 	struct dentry *dentry = file_dentry(filp);
32ce4ef7c0SBryan Schumaker 	struct dentry *parent = NULL;
33ce4ef7c0SBryan Schumaker 	struct inode *dir;
34ce4ef7c0SBryan Schumaker 	unsigned openflags = filp->f_flags;
35ce4ef7c0SBryan Schumaker 	struct iattr attr;
36ce4ef7c0SBryan Schumaker 	int err;
37ce4ef7c0SBryan Schumaker 
38ce4ef7c0SBryan Schumaker 	/*
39ce4ef7c0SBryan Schumaker 	 * If no cached dentry exists or if it's negative, NFSv4 handled the
40ce4ef7c0SBryan Schumaker 	 * opens in ->lookup() or ->create().
41ce4ef7c0SBryan Schumaker 	 *
42ce4ef7c0SBryan Schumaker 	 * We only get this far for a cached positive dentry.  We skipped
43ce4ef7c0SBryan Schumaker 	 * revalidation, so handle it here by dropping the dentry and returning
44ce4ef7c0SBryan Schumaker 	 * -EOPENSTALE.  The VFS will retry the lookup/create/open.
45ce4ef7c0SBryan Schumaker 	 */
46ce4ef7c0SBryan Schumaker 
476de1472fSAl Viro 	dprintk("NFS: open file(%pd2)\n", dentry);
48ce4ef7c0SBryan Schumaker 
4918a60089SBenjamin Coddington 	err = nfs_check_flags(openflags);
5018a60089SBenjamin Coddington 	if (err)
5118a60089SBenjamin Coddington 		return err;
5218a60089SBenjamin Coddington 
53ce4ef7c0SBryan Schumaker 	/* We can't create new files here */
54ce4ef7c0SBryan Schumaker 	openflags &= ~(O_CREAT|O_EXCL);
55ce4ef7c0SBryan Schumaker 
56ce4ef7c0SBryan Schumaker 	parent = dget_parent(dentry);
572b0143b5SDavid Howells 	dir = d_inode(parent);
58ce4ef7c0SBryan Schumaker 
596f1c1d95SChenXiaoSong 	ctx = alloc_nfs_open_context(file_dentry(filp),
606f1c1d95SChenXiaoSong 				     flags_to_mode(openflags), filp);
61ce4ef7c0SBryan Schumaker 	err = PTR_ERR(ctx);
62ce4ef7c0SBryan Schumaker 	if (IS_ERR(ctx))
63ce4ef7c0SBryan Schumaker 		goto out;
64ce4ef7c0SBryan Schumaker 
65ce4ef7c0SBryan Schumaker 	attr.ia_valid = ATTR_OPEN;
66ce4ef7c0SBryan Schumaker 	if (openflags & O_TRUNC) {
67ce4ef7c0SBryan Schumaker 		attr.ia_valid |= ATTR_SIZE;
68ce4ef7c0SBryan Schumaker 		attr.ia_size = 0;
698b7d9d09STrond Myklebust 		filemap_write_and_wait(inode->i_mapping);
70ce4ef7c0SBryan Schumaker 	}
71ce4ef7c0SBryan Schumaker 
72c5c3fb5fSKinglong Mee 	inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr, NULL);
73ce4ef7c0SBryan Schumaker 	if (IS_ERR(inode)) {
74ce4ef7c0SBryan Schumaker 		err = PTR_ERR(inode);
75ce4ef7c0SBryan Schumaker 		switch (err) {
76ce4ef7c0SBryan Schumaker 		default:
7790cf500eSTrond Myklebust 			goto out_put_ctx;
7890cf500eSTrond Myklebust 		case -ENOENT:
7990cf500eSTrond Myklebust 		case -ESTALE:
8090cf500eSTrond Myklebust 		case -EISDIR:
8190cf500eSTrond Myklebust 		case -ENOTDIR:
8290cf500eSTrond Myklebust 		case -ELOOP:
83ce4ef7c0SBryan Schumaker 			goto out_drop;
84ce4ef7c0SBryan Schumaker 		}
85ce4ef7c0SBryan Schumaker 	}
862b0143b5SDavid Howells 	if (inode != d_inode(dentry))
87ce4ef7c0SBryan Schumaker 		goto out_drop;
88ce4ef7c0SBryan Schumaker 
89ce4ef7c0SBryan Schumaker 	nfs_file_set_open_context(filp, ctx);
90f1fe29b4SDavid Howells 	nfs_fscache_open_file(inode, filp);
91ce4ef7c0SBryan Schumaker 	err = 0;
925ee3d10fSDave Wysochanski 	filp->f_mode |= FMODE_CAN_ODIRECT;
93ce4ef7c0SBryan Schumaker 
94ce4ef7c0SBryan Schumaker out_put_ctx:
95ce4ef7c0SBryan Schumaker 	put_nfs_open_context(ctx);
96ce4ef7c0SBryan Schumaker out:
97ce4ef7c0SBryan Schumaker 	dput(parent);
98ce4ef7c0SBryan Schumaker 	return err;
99ce4ef7c0SBryan Schumaker 
100ce4ef7c0SBryan Schumaker out_drop:
101ce4ef7c0SBryan Schumaker 	d_drop(dentry);
102ce4ef7c0SBryan Schumaker 	err = -EOPENSTALE;
103ce4ef7c0SBryan Schumaker 	goto out_put_ctx;
104ce4ef7c0SBryan Schumaker }
105ce4ef7c0SBryan Schumaker 
1065445b1fbSTrond Myklebust /*
1075445b1fbSTrond Myklebust  * Flush all dirty pages, and check for write errors.
1085445b1fbSTrond Myklebust  */
1095445b1fbSTrond Myklebust static int
nfs4_file_flush(struct file * file,fl_owner_t id)1105445b1fbSTrond Myklebust nfs4_file_flush(struct file *file, fl_owner_t id)
1115445b1fbSTrond Myklebust {
1125445b1fbSTrond Myklebust 	struct inode	*inode = file_inode(file);
11367dd23f9SScott Mayhew 	errseq_t since;
1145445b1fbSTrond Myklebust 
1155445b1fbSTrond Myklebust 	dprintk("NFS: flush(%pD2)\n", file);
1165445b1fbSTrond Myklebust 
1175445b1fbSTrond Myklebust 	nfs_inc_stats(inode, NFSIOS_VFSFLUSH);
1185445b1fbSTrond Myklebust 	if ((file->f_mode & FMODE_WRITE) == 0)
1195445b1fbSTrond Myklebust 		return 0;
1205445b1fbSTrond Myklebust 
1215445b1fbSTrond Myklebust 	/*
1225445b1fbSTrond Myklebust 	 * If we're holding a write delegation, then check if we're required
1235445b1fbSTrond Myklebust 	 * to flush the i/o on close. If not, then just start the i/o now.
1245445b1fbSTrond Myklebust 	 */
1255445b1fbSTrond Myklebust 	if (!nfs4_delegation_flush_on_close(inode))
1265445b1fbSTrond Myklebust 		return filemap_fdatawrite(file->f_mapping);
1275445b1fbSTrond Myklebust 
1285445b1fbSTrond Myklebust 	/* Flush writes to the server and return any errors */
12967dd23f9SScott Mayhew 	since = filemap_sample_wb_err(file->f_mapping);
13067dd23f9SScott Mayhew 	nfs_wb_all(inode);
13167dd23f9SScott Mayhew 	return filemap_check_wb_err(file->f_mapping, since);
1325445b1fbSTrond Myklebust }
1335445b1fbSTrond Myklebust 
1341c6dcbe5SAnna Schumaker #ifdef CONFIG_NFS_V4_2
__nfs4_copy_file_range(struct file * file_in,loff_t pos_in,struct file * file_out,loff_t pos_out,size_t count,unsigned int flags)13564bf5ff5SDave Chinner static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
1362e72448bSAnna Schumaker 				      struct file *file_out, loff_t pos_out,
1372e72448bSAnna Schumaker 				      size_t count, unsigned int flags)
1382e72448bSAnna Schumaker {
1390491567bSOlga Kornievskaia 	struct nfs42_copy_notify_res *cn_resp = NULL;
1401d38f3f0SOlga Kornievskaia 	struct nl4_server *nss = NULL;
1411d38f3f0SOlga Kornievskaia 	nfs4_stateid *cnrs = NULL;
1420491567bSOlga Kornievskaia 	ssize_t ret;
14312751010SOlga Kornievskaia 	bool sync = false;
1440491567bSOlga Kornievskaia 
1455dae222aSAmir Goldstein 	/* Only offload copy if superblock is the same */
1468dff1df5SOlga Kornievskaia 	if (file_in->f_op != &nfs4_file_operations)
1475dae222aSAmir Goldstein 		return -EXDEV;
148d8a6ad91SDave Wysochanski 	if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY) ||
149d8a6ad91SDave Wysochanski 	    !nfs_server_capable(file_inode(file_in), NFS_CAP_COPY))
1500769663bSOlga Kornievskaia 		return -EOPNOTSUPP;
151837bb1d7STrond Myklebust 	if (file_inode(file_in) == file_inode(file_out))
1520769663bSOlga Kornievskaia 		return -EOPNOTSUPP;
15312751010SOlga Kornievskaia 	/* if the copy size if smaller than 2 RPC payloads, make it
15412751010SOlga Kornievskaia 	 * synchronous
15512751010SOlga Kornievskaia 	 */
15612751010SOlga Kornievskaia 	if (count <= 2 * NFS_SERVER(file_inode(file_in))->rsize)
15712751010SOlga Kornievskaia 		sync = true;
1580e65a32cSOlga Kornievskaia retry:
1590491567bSOlga Kornievskaia 	if (!nfs42_files_from_same_server(file_in, file_out)) {
160ca7d1d1aSDai Ngo 		/*
161ca7d1d1aSDai Ngo 		 * for inter copy, if copy size is too small
162ca7d1d1aSDai Ngo 		 * then fallback to generic copy.
16312751010SOlga Kornievskaia 		 */
164ca7d1d1aSDai Ngo 		if (sync)
16512751010SOlga Kornievskaia 			return -EOPNOTSUPP;
1660491567bSOlga Kornievskaia 		cn_resp = kzalloc(sizeof(struct nfs42_copy_notify_res),
1674fb547beSTrond Myklebust 				  GFP_KERNEL);
1680491567bSOlga Kornievskaia 		if (unlikely(cn_resp == NULL))
1690491567bSOlga Kornievskaia 			return -ENOMEM;
1700491567bSOlga Kornievskaia 
1710491567bSOlga Kornievskaia 		ret = nfs42_proc_copy_notify(file_in, file_out, cn_resp);
1720491567bSOlga Kornievskaia 		if (ret) {
1730491567bSOlga Kornievskaia 			ret = -EOPNOTSUPP;
1740491567bSOlga Kornievskaia 			goto out;
1750491567bSOlga Kornievskaia 		}
1761d38f3f0SOlga Kornievskaia 		nss = &cn_resp->cnr_src;
1771d38f3f0SOlga Kornievskaia 		cnrs = &cn_resp->cnr_stateid;
1780491567bSOlga Kornievskaia 	}
1791d38f3f0SOlga Kornievskaia 	ret = nfs42_proc_copy(file_in, pos_in, file_out, pos_out, count,
18012751010SOlga Kornievskaia 				nss, cnrs, sync);
1810491567bSOlga Kornievskaia out:
1820491567bSOlga Kornievskaia 	kfree(cn_resp);
18398c27f27STom Rix 
1840e65a32cSOlga Kornievskaia 	if (ret == -EAGAIN)
1850e65a32cSOlga Kornievskaia 		goto retry;
1860491567bSOlga Kornievskaia 	return ret;
1872e72448bSAnna Schumaker }
1882e72448bSAnna Schumaker 
nfs4_copy_file_range(struct file * file_in,loff_t pos_in,struct file * file_out,loff_t pos_out,size_t count,unsigned int flags)18964bf5ff5SDave Chinner static ssize_t nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
19064bf5ff5SDave Chinner 				    struct file *file_out, loff_t pos_out,
19164bf5ff5SDave Chinner 				    size_t count, unsigned int flags)
19264bf5ff5SDave Chinner {
19364bf5ff5SDave Chinner 	ssize_t ret;
19464bf5ff5SDave Chinner 
19564bf5ff5SDave Chinner 	ret = __nfs4_copy_file_range(file_in, pos_in, file_out, pos_out, count,
19664bf5ff5SDave Chinner 				     flags);
1975dae222aSAmir Goldstein 	if (ret == -EOPNOTSUPP || ret == -EXDEV)
19864bf5ff5SDave Chinner 		ret = generic_copy_file_range(file_in, pos_in, file_out,
19964bf5ff5SDave Chinner 					      pos_out, count, flags);
20064bf5ff5SDave Chinner 	return ret;
20164bf5ff5SDave Chinner }
20264bf5ff5SDave Chinner 
nfs4_file_llseek(struct file * filep,loff_t offset,int whence)2031c6dcbe5SAnna Schumaker static loff_t nfs4_file_llseek(struct file *filep, loff_t offset, int whence)
2041c6dcbe5SAnna Schumaker {
2051c6dcbe5SAnna Schumaker 	loff_t ret;
2061c6dcbe5SAnna Schumaker 
2071c6dcbe5SAnna Schumaker 	switch (whence) {
2081c6dcbe5SAnna Schumaker 	case SEEK_HOLE:
2091c6dcbe5SAnna Schumaker 	case SEEK_DATA:
2101c6dcbe5SAnna Schumaker 		ret = nfs42_proc_llseek(filep, offset, whence);
211e67afa7eSZhang Xiaoxu 		if (ret != -EOPNOTSUPP)
2121c6dcbe5SAnna Schumaker 			return ret;
213df561f66SGustavo A. R. Silva 		fallthrough;
2141c6dcbe5SAnna Schumaker 	default:
2151c6dcbe5SAnna Schumaker 		return nfs_file_llseek(filep, offset, whence);
2161c6dcbe5SAnna Schumaker 	}
2171c6dcbe5SAnna Schumaker }
218f4ac1674SAnna Schumaker 
nfs42_fallocate(struct file * filep,int mode,loff_t offset,loff_t len)219f4ac1674SAnna Schumaker static long nfs42_fallocate(struct file *filep, int mode, loff_t offset, loff_t len)
220f4ac1674SAnna Schumaker {
221f4ac1674SAnna Schumaker 	struct inode *inode = file_inode(filep);
222f4ac1674SAnna Schumaker 	long ret;
223f4ac1674SAnna Schumaker 
224f4ac1674SAnna Schumaker 	if (!S_ISREG(inode->i_mode))
225f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
226f4ac1674SAnna Schumaker 
227624bd5b7SAnna Schumaker 	if ((mode != 0) && (mode != (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE)))
228f4ac1674SAnna Schumaker 		return -EOPNOTSUPP;
229f4ac1674SAnna Schumaker 
230f4ac1674SAnna Schumaker 	ret = inode_newsize_ok(inode, offset + len);
231f4ac1674SAnna Schumaker 	if (ret < 0)
232f4ac1674SAnna Schumaker 		return ret;
233f4ac1674SAnna Schumaker 
234624bd5b7SAnna Schumaker 	if (mode & FALLOC_FL_PUNCH_HOLE)
235f830f7ddSAnna Schumaker 		return nfs42_proc_deallocate(filep, offset, len);
236f830f7ddSAnna Schumaker 	return nfs42_proc_allocate(filep, offset, len);
237f4ac1674SAnna Schumaker }
238bea51b30SPeng Tao 
nfs42_remap_file_range(struct file * src_file,loff_t src_off,struct file * dst_file,loff_t dst_off,loff_t count,unsigned int remap_flags)23942ec3d4cSDarrick J. Wong static loff_t nfs42_remap_file_range(struct file *src_file, loff_t src_off,
24042ec3d4cSDarrick J. Wong 		struct file *dst_file, loff_t dst_off, loff_t count,
2412e5dfc99SDarrick J. Wong 		unsigned int remap_flags)
242bea51b30SPeng Tao {
243bea51b30SPeng Tao 	struct inode *dst_inode = file_inode(dst_file);
244811b7b85SPeng Tao 	struct nfs_server *server = NFS_SERVER(dst_inode);
24504b38d60SChristoph Hellwig 	struct inode *src_inode = file_inode(src_file);
246811b7b85SPeng Tao 	unsigned int bs = server->clone_blksize;
24721fad313SChristoph Hellwig 	bool same_inode = false;
248bea51b30SPeng Tao 	int ret;
249bea51b30SPeng Tao 
2509026b3a9SDarrick J. Wong 	/* NFS does not support deduplication. */
2519026b3a9SDarrick J. Wong 	if (remap_flags & REMAP_FILE_DEDUP)
2529026b3a9SDarrick J. Wong 		return -EOPNOTSUPP;
2539026b3a9SDarrick J. Wong 
2549026b3a9SDarrick J. Wong 	if (remap_flags & ~REMAP_FILE_ADVISORY)
2552e5dfc99SDarrick J. Wong 		return -EINVAL;
2562e5dfc99SDarrick J. Wong 
257f5fdf124SMurphy Zhou 	if (IS_SWAPFILE(dst_inode) || IS_SWAPFILE(src_inode))
258f5fdf124SMurphy Zhou 		return -ETXTBSY;
259f5fdf124SMurphy Zhou 
260811b7b85SPeng Tao 	/* check alignment w.r.t. clone_blksize */
261811b7b85SPeng Tao 	ret = -EINVAL;
262811b7b85SPeng Tao 	if (bs) {
263811b7b85SPeng Tao 		if (!IS_ALIGNED(src_off, bs) || !IS_ALIGNED(dst_off, bs))
26404b38d60SChristoph Hellwig 			goto out;
265811b7b85SPeng Tao 		if (!IS_ALIGNED(count, bs) && i_size_read(src_inode) != (src_off + count))
26604b38d60SChristoph Hellwig 			goto out;
267811b7b85SPeng Tao 	}
268811b7b85SPeng Tao 
26904b38d60SChristoph Hellwig 	if (src_inode == dst_inode)
27004b38d60SChristoph Hellwig 		same_inode = true;
27121fad313SChristoph Hellwig 
272bea51b30SPeng Tao 	/* XXX: do we lock at all? what if server needs CB_RECALL_LAYOUT? */
27321fad313SChristoph Hellwig 	if (same_inode) {
2745955102cSAl Viro 		inode_lock(src_inode);
27521fad313SChristoph Hellwig 	} else if (dst_inode < src_inode) {
2765955102cSAl Viro 		inode_lock_nested(dst_inode, I_MUTEX_PARENT);
2775955102cSAl Viro 		inode_lock_nested(src_inode, I_MUTEX_CHILD);
278bea51b30SPeng Tao 	} else {
2795955102cSAl Viro 		inode_lock_nested(src_inode, I_MUTEX_PARENT);
2805955102cSAl Viro 		inode_lock_nested(dst_inode, I_MUTEX_CHILD);
281bea51b30SPeng Tao 	}
282bea51b30SPeng Tao 
283bea51b30SPeng Tao 	/* flush all pending writes on both src and dst so that server
284bea51b30SPeng Tao 	 * has the latest data */
285bea51b30SPeng Tao 	ret = nfs_sync_inode(src_inode);
286bea51b30SPeng Tao 	if (ret)
287bea51b30SPeng Tao 		goto out_unlock;
288bea51b30SPeng Tao 	ret = nfs_sync_inode(dst_inode);
289bea51b30SPeng Tao 	if (ret)
290bea51b30SPeng Tao 		goto out_unlock;
291bea51b30SPeng Tao 
29204b38d60SChristoph Hellwig 	ret = nfs42_proc_clone(src_file, dst_file, src_off, dst_off, count);
293bea51b30SPeng Tao 
294bea51b30SPeng Tao 	/* truncate inode page cache of the dst range so that future reads can fetch
295bea51b30SPeng Tao 	 * new data from server */
296bea51b30SPeng Tao 	if (!ret)
297bea51b30SPeng Tao 		truncate_inode_pages_range(&dst_inode->i_data, dst_off, dst_off + count - 1);
298bea51b30SPeng Tao 
299bea51b30SPeng Tao out_unlock:
30021fad313SChristoph Hellwig 	if (same_inode) {
3015955102cSAl Viro 		inode_unlock(src_inode);
30221fad313SChristoph Hellwig 	} else if (dst_inode < src_inode) {
3035955102cSAl Viro 		inode_unlock(src_inode);
3045955102cSAl Viro 		inode_unlock(dst_inode);
305bea51b30SPeng Tao 	} else {
3065955102cSAl Viro 		inode_unlock(dst_inode);
3075955102cSAl Viro 		inode_unlock(src_inode);
308bea51b30SPeng Tao 	}
30904b38d60SChristoph Hellwig out:
31042ec3d4cSDarrick J. Wong 	return ret < 0 ? ret : count;
311bea51b30SPeng Tao }
312ec4b0925SOlga Kornievskaia 
313ec4b0925SOlga Kornievskaia static int read_name_gen = 1;
314ec4b0925SOlga Kornievskaia #define SSC_READ_NAME_BODY "ssc_read_%d"
315ec4b0925SOlga Kornievskaia 
__nfs42_ssc_open(struct vfsmount * ss_mnt,struct nfs_fh * src_fh,nfs4_stateid * stateid)3160cfcd405SDai Ngo static struct file *__nfs42_ssc_open(struct vfsmount *ss_mnt,
3170cfcd405SDai Ngo 		struct nfs_fh *src_fh, nfs4_stateid *stateid)
318ec4b0925SOlga Kornievskaia {
319156cd285STrond Myklebust 	struct nfs_fattr *fattr = nfs_alloc_fattr();
320ec4b0925SOlga Kornievskaia 	struct file *filep, *res;
321ec4b0925SOlga Kornievskaia 	struct nfs_server *server;
322ec4b0925SOlga Kornievskaia 	struct inode *r_ino = NULL;
323ec4b0925SOlga Kornievskaia 	struct nfs_open_context *ctx;
324ec4b0925SOlga Kornievskaia 	struct nfs4_state_owner *sp;
325f751c545SOlga Kornievskaia 	char *read_name = NULL;
326ec4b0925SOlga Kornievskaia 	int len, status = 0;
327ec4b0925SOlga Kornievskaia 
328f6957b71SAl Viro 	server = NFS_SB(ss_mnt->mnt_sb);
329ec4b0925SOlga Kornievskaia 
330156cd285STrond Myklebust 	if (!fattr)
331156cd285STrond Myklebust 		return ERR_PTR(-ENOMEM);
332ec4b0925SOlga Kornievskaia 
3332ef61e0eSAnna Schumaker 	status = nfs4_proc_getattr(server, src_fh, fattr, NULL);
334ec4b0925SOlga Kornievskaia 	if (status < 0) {
335ec4b0925SOlga Kornievskaia 		res = ERR_PTR(status);
336ec4b0925SOlga Kornievskaia 		goto out;
337ec4b0925SOlga Kornievskaia 	}
338ec4b0925SOlga Kornievskaia 
339fcfc8be1SOlga Kornievskaia 	if (!S_ISREG(fattr->mode)) {
340fcfc8be1SOlga Kornievskaia 		res = ERR_PTR(-EBADF);
341fcfc8be1SOlga Kornievskaia 		goto out;
342fcfc8be1SOlga Kornievskaia 	}
343fcfc8be1SOlga Kornievskaia 
344ec4b0925SOlga Kornievskaia 	res = ERR_PTR(-ENOMEM);
345ec4b0925SOlga Kornievskaia 	len = strlen(SSC_READ_NAME_BODY) + 16;
3464fb547beSTrond Myklebust 	read_name = kzalloc(len, GFP_KERNEL);
347ec4b0925SOlga Kornievskaia 	if (read_name == NULL)
348ec4b0925SOlga Kornievskaia 		goto out;
349ec4b0925SOlga Kornievskaia 	snprintf(read_name, len, SSC_READ_NAME_BODY, read_name_gen++);
350ec4b0925SOlga Kornievskaia 
351f6957b71SAl Viro 	r_ino = nfs_fhget(ss_mnt->mnt_sb, src_fh, fattr);
352ec4b0925SOlga Kornievskaia 	if (IS_ERR(r_ino)) {
353ec4b0925SOlga Kornievskaia 		res = ERR_CAST(r_ino);
354f751c545SOlga Kornievskaia 		goto out_free_name;
355ec4b0925SOlga Kornievskaia 	}
356ec4b0925SOlga Kornievskaia 
357aa97a3efSTrond Myklebust 	filep = alloc_file_pseudo(r_ino, ss_mnt, read_name, O_RDONLY,
358ec4b0925SOlga Kornievskaia 				     r_ino->i_fop);
359ec4b0925SOlga Kornievskaia 	if (IS_ERR(filep)) {
360ec4b0925SOlga Kornievskaia 		res = ERR_CAST(filep);
361fcfc8be1SOlga Kornievskaia 		iput(r_ino);
362f751c545SOlga Kornievskaia 		goto out_free_name;
363ec4b0925SOlga Kornievskaia 	}
364ec4b0925SOlga Kornievskaia 
3656f1c1d95SChenXiaoSong 	ctx = alloc_nfs_open_context(filep->f_path.dentry,
3666f1c1d95SChenXiaoSong 				     flags_to_mode(filep->f_flags), filep);
367ec4b0925SOlga Kornievskaia 	if (IS_ERR(ctx)) {
368ec4b0925SOlga Kornievskaia 		res = ERR_CAST(ctx);
369ec4b0925SOlga Kornievskaia 		goto out_filep;
370ec4b0925SOlga Kornievskaia 	}
371ec4b0925SOlga Kornievskaia 
372ec4b0925SOlga Kornievskaia 	res = ERR_PTR(-EINVAL);
373ec4b0925SOlga Kornievskaia 	sp = nfs4_get_state_owner(server, ctx->cred, GFP_KERNEL);
374ec4b0925SOlga Kornievskaia 	if (sp == NULL)
375ec4b0925SOlga Kornievskaia 		goto out_ctx;
376ec4b0925SOlga Kornievskaia 
377ec4b0925SOlga Kornievskaia 	ctx->state = nfs4_get_open_state(r_ino, sp);
378ec4b0925SOlga Kornievskaia 	if (ctx->state == NULL)
379ec4b0925SOlga Kornievskaia 		goto out_stateowner;
380ec4b0925SOlga Kornievskaia 
3810b9018b9SOlga Kornievskaia 	set_bit(NFS_SRV_SSC_COPY_STATE, &ctx->state->flags);
382ec4b0925SOlga Kornievskaia 	memcpy(&ctx->state->open_stateid.other, &stateid->other,
383ec4b0925SOlga Kornievskaia 	       NFS4_STATEID_OTHER_SIZE);
384ec4b0925SOlga Kornievskaia 	update_open_stateid(ctx->state, stateid, NULL, filep->f_mode);
385fe8eb820SDai Ngo 	set_bit(NFS_OPEN_STATE, &ctx->state->flags);
386ec4b0925SOlga Kornievskaia 
387ec4b0925SOlga Kornievskaia 	nfs_file_set_open_context(filep, ctx);
388ec4b0925SOlga Kornievskaia 	put_nfs_open_context(ctx);
389ec4b0925SOlga Kornievskaia 
390ec4b0925SOlga Kornievskaia 	file_ra_state_init(&filep->f_ra, filep->f_mapping->host->i_mapping);
391ec4b0925SOlga Kornievskaia 	res = filep;
392f751c545SOlga Kornievskaia out_free_name:
393f751c545SOlga Kornievskaia 	kfree(read_name);
394ec4b0925SOlga Kornievskaia out:
395156cd285STrond Myklebust 	nfs_free_fattr(fattr);
396ec4b0925SOlga Kornievskaia 	return res;
397ec4b0925SOlga Kornievskaia out_stateowner:
398ec4b0925SOlga Kornievskaia 	nfs4_put_state_owner(sp);
399ec4b0925SOlga Kornievskaia out_ctx:
400ec4b0925SOlga Kornievskaia 	put_nfs_open_context(ctx);
401ec4b0925SOlga Kornievskaia out_filep:
402ec4b0925SOlga Kornievskaia 	fput(filep);
403f751c545SOlga Kornievskaia 	goto out_free_name;
404ec4b0925SOlga Kornievskaia }
4050cfcd405SDai Ngo 
__nfs42_ssc_close(struct file * filep)4060cfcd405SDai Ngo static void __nfs42_ssc_close(struct file *filep)
407ec4b0925SOlga Kornievskaia {
408ec4b0925SOlga Kornievskaia 	struct nfs_open_context *ctx = nfs_file_open_context(filep);
409ec4b0925SOlga Kornievskaia 
410ec4b0925SOlga Kornievskaia 	ctx->state->flags = 0;
411ec4b0925SOlga Kornievskaia }
4120cfcd405SDai Ngo 
4130cfcd405SDai Ngo static const struct nfs4_ssc_client_ops nfs4_ssc_clnt_ops_tbl = {
4140cfcd405SDai Ngo 	.sco_open = __nfs42_ssc_open,
4150cfcd405SDai Ngo 	.sco_close = __nfs42_ssc_close,
4160cfcd405SDai Ngo };
4170cfcd405SDai Ngo 
4180cfcd405SDai Ngo /**
4190cfcd405SDai Ngo  * nfs42_ssc_register_ops - Wrapper to register NFS_V4 ops in nfs_common
4200cfcd405SDai Ngo  *
4210cfcd405SDai Ngo  * Return values:
4220cfcd405SDai Ngo  *   None
4230cfcd405SDai Ngo  */
nfs42_ssc_register_ops(void)4240cfcd405SDai Ngo void nfs42_ssc_register_ops(void)
4250cfcd405SDai Ngo {
4260cfcd405SDai Ngo 	nfs42_ssc_register(&nfs4_ssc_clnt_ops_tbl);
4270cfcd405SDai Ngo }
4280cfcd405SDai Ngo 
4290cfcd405SDai Ngo /**
4300cfcd405SDai Ngo  * nfs42_ssc_unregister_ops - wrapper to un-register NFS_V4 ops in nfs_common
4310cfcd405SDai Ngo  *
4320cfcd405SDai Ngo  * Return values:
4330cfcd405SDai Ngo  *   None.
4340cfcd405SDai Ngo  */
nfs42_ssc_unregister_ops(void)4350cfcd405SDai Ngo void nfs42_ssc_unregister_ops(void)
4360cfcd405SDai Ngo {
4370cfcd405SDai Ngo 	nfs42_ssc_unregister(&nfs4_ssc_clnt_ops_tbl);
4380cfcd405SDai Ngo }
4396b7153daSChristoph Hellwig #endif /* CONFIG_NFS_V4_2 */
440bea51b30SPeng Tao 
nfs4_setlease(struct file * file,int arg,struct file_lock ** lease,void ** priv)441*ed5f17f6SLuca Vizzarro static int nfs4_setlease(struct file *file, int arg, struct file_lock **lease,
442e93a5e93STrond Myklebust 			 void **priv)
443e93a5e93STrond Myklebust {
444e93a5e93STrond Myklebust 	return nfs4_proc_setlease(file, arg, lease, priv);
445e93a5e93STrond Myklebust }
446e93a5e93STrond Myklebust 
447ce4ef7c0SBryan Schumaker const struct file_operations nfs4_file_operations = {
4483aa2d199SAl Viro 	.read_iter	= nfs_file_read,
449edaf4369SAl Viro 	.write_iter	= nfs_file_write,
450ce4ef7c0SBryan Schumaker 	.mmap		= nfs_file_mmap,
451ce4ef7c0SBryan Schumaker 	.open		= nfs4_file_open,
4525445b1fbSTrond Myklebust 	.flush		= nfs4_file_flush,
453ce4ef7c0SBryan Schumaker 	.release	= nfs_file_release,
4544ff79bc7SChristoph Hellwig 	.fsync		= nfs_file_fsync,
455ce4ef7c0SBryan Schumaker 	.lock		= nfs_lock,
456ce4ef7c0SBryan Schumaker 	.flock		= nfs_flock,
457a7db5034SDavid Howells 	.splice_read	= nfs_file_splice_read,
4584da54c21SAl Viro 	.splice_write	= iter_file_splice_write,
459ce4ef7c0SBryan Schumaker 	.check_flags	= nfs_check_flags,
460e93a5e93STrond Myklebust 	.setlease	= nfs4_setlease,
4616b7153daSChristoph Hellwig #ifdef CONFIG_NFS_V4_2
4622e72448bSAnna Schumaker 	.copy_file_range = nfs4_copy_file_range,
4636b7153daSChristoph Hellwig 	.llseek		= nfs4_file_llseek,
4646b7153daSChristoph Hellwig 	.fallocate	= nfs42_fallocate,
4652e5dfc99SDarrick J. Wong 	.remap_file_range = nfs42_remap_file_range,
4666b7153daSChristoph Hellwig #else
4676b7153daSChristoph Hellwig 	.llseek		= nfs_file_llseek,
4686b7153daSChristoph Hellwig #endif
469ce4ef7c0SBryan Schumaker };
470