xref: /openbmc/linux/fs/nfs/export.c (revision 1c0a0af5)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Copyright (c) 2015, Primary Data, Inc. All rights reserved.
4   *
5   * Tao Peng <bergwolf@primarydata.com>
6   */
7  #include <linux/dcache.h>
8  #include <linux/exportfs.h>
9  #include <linux/nfs.h>
10  #include <linux/nfs_fs.h>
11  
12  #include "internal.h"
13  #include "nfstrace.h"
14  
15  #define NFSDBG_FACILITY		NFSDBG_VFS
16  
17  enum {
18  	FILEID_HIGH_OFF = 0,	/* inode fileid high */
19  	FILEID_LOW_OFF,		/* inode fileid low */
20  	FILE_I_TYPE_OFF,	/* inode type */
21  	EMBED_FH_OFF		/* embeded server fh */
22  };
23  
24  
25  static struct nfs_fh *nfs_exp_embedfh(__u32 *p)
26  {
27  	return (struct nfs_fh *)(p + EMBED_FH_OFF);
28  }
29  
30  /*
31   * Let's break subtree checking for now... otherwise we'll have to embed parent fh
32   * but there might not be enough space.
33   */
34  static int
35  nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
36  {
37  	struct nfs_fh *server_fh = NFS_FH(inode);
38  	struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
39  	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
40  	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
41  
42  	dprintk("%s: max fh len %d inode %p parent %p",
43  		__func__, *max_len, inode, parent);
44  
45  	if (*max_len < len) {
46  		dprintk("%s: fh len %d too small, required %d\n",
47  			__func__, *max_len, len);
48  		*max_len = len;
49  		return FILEID_INVALID;
50  	}
51  
52  	p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
53  	p[FILEID_LOW_OFF] = NFS_FILEID(inode);
54  	p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
55  	p[len - 1] = 0; /* Padding */
56  	nfs_copy_fh(clnt_fh, server_fh);
57  	*max_len = len;
58  	dprintk("%s: result fh fileid %llu mode %u size %d\n",
59  		__func__, NFS_FILEID(inode), inode->i_mode, *max_len);
60  	return *max_len;
61  }
62  
63  static struct dentry *
64  nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
65  		 int fh_len, int fh_type)
66  {
67  	struct nfs_fattr *fattr = NULL;
68  	struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
69  	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
70  	const struct nfs_rpc_ops *rpc_ops;
71  	struct dentry *dentry;
72  	struct inode *inode;
73  	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
74  	u32 *p = fid->raw;
75  	int ret;
76  
77  	/* NULL translates to ESTALE */
78  	if (fh_len < len || fh_type != len)
79  		return NULL;
80  
81  	fattr = nfs_alloc_fattr_with_label(NFS_SB(sb));
82  	if (fattr == NULL) {
83  		dentry = ERR_PTR(-ENOMEM);
84  		goto out;
85  	}
86  
87  	fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
88  	fattr->mode = p[FILE_I_TYPE_OFF];
89  	fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
90  
91  	dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
92  
93  	inode = nfs_ilookup(sb, fattr, server_fh);
94  	if (inode)
95  		goto out_found;
96  
97  	rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
98  	ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, NULL);
99  	if (ret) {
100  		dprintk("%s: getattr failed %d\n", __func__, ret);
101  		trace_nfs_fh_to_dentry(sb, server_fh, fattr->fileid, ret);
102  		dentry = ERR_PTR(ret);
103  		goto out_free_fattr;
104  	}
105  
106  	inode = nfs_fhget(sb, server_fh, fattr);
107  
108  out_found:
109  	dentry = d_obtain_alias(inode);
110  out_free_fattr:
111  	nfs_free_fattr(fattr);
112  out:
113  	return dentry;
114  }
115  
116  static struct dentry *
117  nfs_get_parent(struct dentry *dentry)
118  {
119  	int ret;
120  	struct inode *inode = d_inode(dentry), *pinode;
121  	struct super_block *sb = inode->i_sb;
122  	struct nfs_server *server = NFS_SB(sb);
123  	struct nfs_fattr *fattr = NULL;
124  	struct dentry *parent;
125  	struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
126  	struct nfs_fh fh;
127  
128  	if (!ops->lookupp)
129  		return ERR_PTR(-EACCES);
130  
131  	fattr = nfs_alloc_fattr_with_label(server);
132  	if (fattr == NULL)
133  		return ERR_PTR(-ENOMEM);
134  
135  	ret = ops->lookupp(inode, &fh, fattr);
136  	if (ret) {
137  		parent = ERR_PTR(ret);
138  		goto out;
139  	}
140  
141  	pinode = nfs_fhget(sb, &fh, fattr);
142  	parent = d_obtain_alias(pinode);
143  out:
144  	nfs_free_fattr(fattr);
145  	return parent;
146  }
147  
148  const struct export_operations nfs_export_ops = {
149  	.encode_fh = nfs_encode_fh,
150  	.fh_to_dentry = nfs_fh_to_dentry,
151  	.get_parent = nfs_get_parent,
152  	.flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK|
153  		EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS|
154  		EXPORT_OP_NOATOMIC_ATTR,
155  };
156