xref: /openbmc/linux/fs/nfs/export.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
220fa1902SPeng Tao /*
320fa1902SPeng Tao  * Copyright (c) 2015, Primary Data, Inc. All rights reserved.
420fa1902SPeng Tao  *
520fa1902SPeng Tao  * Tao Peng <bergwolf@primarydata.com>
620fa1902SPeng Tao  */
720fa1902SPeng Tao #include <linux/dcache.h>
820fa1902SPeng Tao #include <linux/exportfs.h>
920fa1902SPeng Tao #include <linux/nfs.h>
1020fa1902SPeng Tao #include <linux/nfs_fs.h>
1120fa1902SPeng Tao 
1220fa1902SPeng Tao #include "internal.h"
1320fa1902SPeng Tao #include "nfstrace.h"
1420fa1902SPeng Tao 
1520fa1902SPeng Tao #define NFSDBG_FACILITY		NFSDBG_VFS
1620fa1902SPeng Tao 
1720fa1902SPeng Tao enum {
1820fa1902SPeng Tao 	FILEID_HIGH_OFF = 0,	/* inode fileid high */
1920fa1902SPeng Tao 	FILEID_LOW_OFF,		/* inode fileid low */
2020fa1902SPeng Tao 	FILE_I_TYPE_OFF,	/* inode type */
2120fa1902SPeng Tao 	EMBED_FH_OFF		/* embeded server fh */
2220fa1902SPeng Tao };
2320fa1902SPeng Tao 
2420fa1902SPeng Tao 
nfs_exp_embedfh(__u32 * p)2520fa1902SPeng Tao static struct nfs_fh *nfs_exp_embedfh(__u32 *p)
2620fa1902SPeng Tao {
2720fa1902SPeng Tao 	return (struct nfs_fh *)(p + EMBED_FH_OFF);
2820fa1902SPeng Tao }
2920fa1902SPeng Tao 
3020fa1902SPeng Tao /*
3120fa1902SPeng Tao  * Let's break subtree checking for now... otherwise we'll have to embed parent fh
3220fa1902SPeng Tao  * but there might not be enough space.
3320fa1902SPeng Tao  */
3420fa1902SPeng Tao static int
nfs_encode_fh(struct inode * inode,__u32 * p,int * max_len,struct inode * parent)3520fa1902SPeng Tao nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
3620fa1902SPeng Tao {
3720fa1902SPeng Tao 	struct nfs_fh *server_fh = NFS_FH(inode);
3820fa1902SPeng Tao 	struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
3920fa1902SPeng Tao 	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
4020fa1902SPeng Tao 	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
4120fa1902SPeng Tao 
4220fa1902SPeng Tao 	dprintk("%s: max fh len %d inode %p parent %p",
4320fa1902SPeng Tao 		__func__, *max_len, inode, parent);
4420fa1902SPeng Tao 
45f78e4454SRichard Weinberger 	if (*max_len < len) {
4620fa1902SPeng Tao 		dprintk("%s: fh len %d too small, required %d\n",
4720fa1902SPeng Tao 			__func__, *max_len, len);
4820fa1902SPeng Tao 		*max_len = len;
4920fa1902SPeng Tao 		return FILEID_INVALID;
5020fa1902SPeng Tao 	}
5120fa1902SPeng Tao 
5220fa1902SPeng Tao 	p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
5320fa1902SPeng Tao 	p[FILEID_LOW_OFF] = NFS_FILEID(inode);
5420fa1902SPeng Tao 	p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
5520fa1902SPeng Tao 	p[len - 1] = 0; /* Padding */
5620fa1902SPeng Tao 	nfs_copy_fh(clnt_fh, server_fh);
5720fa1902SPeng Tao 	*max_len = len;
5820fa1902SPeng Tao 	dprintk("%s: result fh fileid %llu mode %u size %d\n",
5920fa1902SPeng Tao 		__func__, NFS_FILEID(inode), inode->i_mode, *max_len);
6020fa1902SPeng Tao 	return *max_len;
6120fa1902SPeng Tao }
6220fa1902SPeng Tao 
6320fa1902SPeng Tao static struct dentry *
nfs_fh_to_dentry(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)6420fa1902SPeng Tao nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
6520fa1902SPeng Tao 		 int fh_len, int fh_type)
6620fa1902SPeng Tao {
6720fa1902SPeng Tao 	struct nfs_fattr *fattr = NULL;
6820fa1902SPeng Tao 	struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
6920fa1902SPeng Tao 	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
7020fa1902SPeng Tao 	const struct nfs_rpc_ops *rpc_ops;
7120fa1902SPeng Tao 	struct dentry *dentry;
7220fa1902SPeng Tao 	struct inode *inode;
7320fa1902SPeng Tao 	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
7420fa1902SPeng Tao 	u32 *p = fid->raw;
7520fa1902SPeng Tao 	int ret;
7620fa1902SPeng Tao 
7720fa1902SPeng Tao 	/* NULL translates to ESTALE */
7820fa1902SPeng Tao 	if (fh_len < len || fh_type != len)
7920fa1902SPeng Tao 		return NULL;
8020fa1902SPeng Tao 
812ef61e0eSAnna Schumaker 	fattr = nfs_alloc_fattr_with_label(NFS_SB(sb));
8220fa1902SPeng Tao 	if (fattr == NULL) {
8320fa1902SPeng Tao 		dentry = ERR_PTR(-ENOMEM);
8420fa1902SPeng Tao 		goto out;
8520fa1902SPeng Tao 	}
8620fa1902SPeng Tao 
8720fa1902SPeng Tao 	fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
8820fa1902SPeng Tao 	fattr->mode = p[FILE_I_TYPE_OFF];
8920fa1902SPeng Tao 	fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
9020fa1902SPeng Tao 
9120fa1902SPeng Tao 	dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
9220fa1902SPeng Tao 
9320fa1902SPeng Tao 	inode = nfs_ilookup(sb, fattr, server_fh);
9420fa1902SPeng Tao 	if (inode)
9520fa1902SPeng Tao 		goto out_found;
9620fa1902SPeng Tao 
9720fa1902SPeng Tao 	rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
982ef61e0eSAnna Schumaker 	ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, NULL);
9920fa1902SPeng Tao 	if (ret) {
10020fa1902SPeng Tao 		dprintk("%s: getattr failed %d\n", __func__, ret);
10143622eabSTrond Myklebust 		trace_nfs_fh_to_dentry(sb, server_fh, fattr->fileid, ret);
10220fa1902SPeng Tao 		dentry = ERR_PTR(ret);
1032ef61e0eSAnna Schumaker 		goto out_free_fattr;
10420fa1902SPeng Tao 	}
10520fa1902SPeng Tao 
106cf7ab00aSAnna Schumaker 	inode = nfs_fhget(sb, server_fh, fattr);
10720fa1902SPeng Tao 
10820fa1902SPeng Tao out_found:
10920fa1902SPeng Tao 	dentry = d_obtain_alias(inode);
11020fa1902SPeng Tao out_free_fattr:
11120fa1902SPeng Tao 	nfs_free_fattr(fattr);
11220fa1902SPeng Tao out:
11320fa1902SPeng Tao 	return dentry;
11420fa1902SPeng Tao }
11520fa1902SPeng Tao 
11620fa1902SPeng Tao static struct dentry *
nfs_get_parent(struct dentry * dentry)11720fa1902SPeng Tao nfs_get_parent(struct dentry *dentry)
11820fa1902SPeng Tao {
11920fa1902SPeng Tao 	int ret;
12020fa1902SPeng Tao 	struct inode *inode = d_inode(dentry), *pinode;
12120fa1902SPeng Tao 	struct super_block *sb = inode->i_sb;
12220fa1902SPeng Tao 	struct nfs_server *server = NFS_SB(sb);
12320fa1902SPeng Tao 	struct nfs_fattr *fattr = NULL;
12420fa1902SPeng Tao 	struct dentry *parent;
12520fa1902SPeng Tao 	struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
12620fa1902SPeng Tao 	struct nfs_fh fh;
12720fa1902SPeng Tao 
12820fa1902SPeng Tao 	if (!ops->lookupp)
12920fa1902SPeng Tao 		return ERR_PTR(-EACCES);
13020fa1902SPeng Tao 
131ba4bc8dcSAnna Schumaker 	fattr = nfs_alloc_fattr_with_label(server);
132ba4bc8dcSAnna Schumaker 	if (fattr == NULL)
133ba4bc8dcSAnna Schumaker 		return ERR_PTR(-ENOMEM);
134ba4bc8dcSAnna Schumaker 
135ba4bc8dcSAnna Schumaker 	ret = ops->lookupp(inode, &fh, fattr);
136ba4bc8dcSAnna Schumaker 	if (ret) {
137ba4bc8dcSAnna Schumaker 		parent = ERR_PTR(ret);
13820fa1902SPeng Tao 		goto out;
13920fa1902SPeng Tao 	}
14020fa1902SPeng Tao 
141cf7ab00aSAnna Schumaker 	pinode = nfs_fhget(sb, &fh, fattr);
14220fa1902SPeng Tao 	parent = d_obtain_alias(pinode);
14320fa1902SPeng Tao out:
144ba4bc8dcSAnna Schumaker 	nfs_free_fattr(fattr);
14520fa1902SPeng Tao 	return parent;
14620fa1902SPeng Tao }
14720fa1902SPeng Tao 
14820fa1902SPeng Tao const struct export_operations nfs_export_ops = {
14920fa1902SPeng Tao 	.encode_fh = nfs_encode_fh,
15020fa1902SPeng Tao 	.fh_to_dentry = nfs_fh_to_dentry,
15120fa1902SPeng Tao 	.get_parent = nfs_get_parent,
152*dcb779fcSJeff Layton 	.flags = EXPORT_OP_NOWCC		|
153*dcb779fcSJeff Layton 		 EXPORT_OP_NOSUBTREECHK		|
154*dcb779fcSJeff Layton 		 EXPORT_OP_CLOSE_BEFORE_UNLINK	|
155*dcb779fcSJeff Layton 		 EXPORT_OP_REMOTE_FS		|
156*dcb779fcSJeff Layton 		 EXPORT_OP_NOATOMIC_ATTR	|
157*dcb779fcSJeff Layton 		 EXPORT_OP_FLUSH_ON_CLOSE,
15820fa1902SPeng Tao };
159