xref: /openbmc/linux/fs/ceph/export.c (revision 711aab1d)
1  #include <linux/ceph/ceph_debug.h>
2  
3  #include <linux/exportfs.h>
4  #include <linux/slab.h>
5  #include <asm/unaligned.h>
6  
7  #include "super.h"
8  #include "mds_client.h"
9  
10  /*
11   * Basic fh
12   */
13  struct ceph_nfs_fh {
14  	u64 ino;
15  } __attribute__ ((packed));
16  
17  /*
18   * Larger fh that includes parent ino.
19   */
20  struct ceph_nfs_confh {
21  	u64 ino, parent_ino;
22  } __attribute__ ((packed));
23  
24  static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
25  			  struct inode *parent_inode)
26  {
27  	int type;
28  	struct ceph_nfs_fh *fh = (void *)rawfh;
29  	struct ceph_nfs_confh *cfh = (void *)rawfh;
30  	int connected_handle_length = sizeof(*cfh)/4;
31  	int handle_length = sizeof(*fh)/4;
32  
33  	/* don't re-export snaps */
34  	if (ceph_snap(inode) != CEPH_NOSNAP)
35  		return -EINVAL;
36  
37  	if (parent_inode && (*max_len < connected_handle_length)) {
38  		*max_len = connected_handle_length;
39  		return FILEID_INVALID;
40  	} else if (*max_len < handle_length) {
41  		*max_len = handle_length;
42  		return FILEID_INVALID;
43  	}
44  
45  	if (parent_inode) {
46  		dout("encode_fh %llx with parent %llx\n",
47  		     ceph_ino(inode), ceph_ino(parent_inode));
48  		cfh->ino = ceph_ino(inode);
49  		cfh->parent_ino = ceph_ino(parent_inode);
50  		*max_len = connected_handle_length;
51  		type = FILEID_INO32_GEN_PARENT;
52  	} else {
53  		dout("encode_fh %llx\n", ceph_ino(inode));
54  		fh->ino = ceph_ino(inode);
55  		*max_len = handle_length;
56  		type = FILEID_INO32_GEN;
57  	}
58  	return type;
59  }
60  
61  static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
62  {
63  	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
64  	struct inode *inode;
65  	struct ceph_vino vino;
66  	int err;
67  
68  	vino.ino = ino;
69  	vino.snap = CEPH_NOSNAP;
70  	inode = ceph_find_inode(sb, vino);
71  	if (!inode) {
72  		struct ceph_mds_request *req;
73  		int mask;
74  
75  		req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
76  					       USE_ANY_MDS);
77  		if (IS_ERR(req))
78  			return ERR_CAST(req);
79  
80  		mask = CEPH_STAT_CAP_INODE;
81  		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
82  			mask |= CEPH_CAP_XATTR_SHARED;
83  		req->r_args.getattr.mask = cpu_to_le32(mask);
84  
85  		req->r_ino1 = vino;
86  		req->r_num_caps = 1;
87  		err = ceph_mdsc_do_request(mdsc, NULL, req);
88  		inode = req->r_target_inode;
89  		if (inode)
90  			ihold(inode);
91  		ceph_mdsc_put_request(req);
92  		if (!inode)
93  			return ERR_PTR(-ESTALE);
94  		if (inode->i_nlink == 0) {
95  			iput(inode);
96  			return ERR_PTR(-ESTALE);
97  		}
98  	}
99  
100  	return d_obtain_alias(inode);
101  }
102  
103  /*
104   * convert regular fh to dentry
105   */
106  static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
107  					struct fid *fid,
108  					int fh_len, int fh_type)
109  {
110  	struct ceph_nfs_fh *fh = (void *)fid->raw;
111  
112  	if (fh_type != FILEID_INO32_GEN  &&
113  	    fh_type != FILEID_INO32_GEN_PARENT)
114  		return NULL;
115  	if (fh_len < sizeof(*fh) / 4)
116  		return NULL;
117  
118  	dout("fh_to_dentry %llx\n", fh->ino);
119  	return __fh_to_dentry(sb, fh->ino);
120  }
121  
122  static struct dentry *__get_parent(struct super_block *sb,
123  				   struct dentry *child, u64 ino)
124  {
125  	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
126  	struct ceph_mds_request *req;
127  	struct inode *inode;
128  	int mask;
129  	int err;
130  
131  	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
132  				       USE_ANY_MDS);
133  	if (IS_ERR(req))
134  		return ERR_CAST(req);
135  
136  	if (child) {
137  		req->r_inode = d_inode(child);
138  		ihold(d_inode(child));
139  	} else {
140  		req->r_ino1 = (struct ceph_vino) {
141  			.ino = ino,
142  			.snap = CEPH_NOSNAP,
143  		};
144  	}
145  
146  	mask = CEPH_STAT_CAP_INODE;
147  	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
148  		mask |= CEPH_CAP_XATTR_SHARED;
149  	req->r_args.getattr.mask = cpu_to_le32(mask);
150  
151  	req->r_num_caps = 1;
152  	err = ceph_mdsc_do_request(mdsc, NULL, req);
153  	inode = req->r_target_inode;
154  	if (inode)
155  		ihold(inode);
156  	ceph_mdsc_put_request(req);
157  	if (!inode)
158  		return ERR_PTR(-ENOENT);
159  
160  	return d_obtain_alias(inode);
161  }
162  
163  static struct dentry *ceph_get_parent(struct dentry *child)
164  {
165  	/* don't re-export snaps */
166  	if (ceph_snap(d_inode(child)) != CEPH_NOSNAP)
167  		return ERR_PTR(-EINVAL);
168  
169  	dout("get_parent %p ino %llx.%llx\n",
170  	     child, ceph_vinop(d_inode(child)));
171  	return __get_parent(child->d_sb, child, 0);
172  }
173  
174  /*
175   * convert regular fh to parent
176   */
177  static struct dentry *ceph_fh_to_parent(struct super_block *sb,
178  					struct fid *fid,
179  					int fh_len, int fh_type)
180  {
181  	struct ceph_nfs_confh *cfh = (void *)fid->raw;
182  	struct dentry *dentry;
183  
184  	if (fh_type != FILEID_INO32_GEN_PARENT)
185  		return NULL;
186  	if (fh_len < sizeof(*cfh) / 4)
187  		return NULL;
188  
189  	dout("fh_to_parent %llx\n", cfh->parent_ino);
190  	dentry = __get_parent(sb, NULL, cfh->ino);
191  	if (unlikely(dentry == ERR_PTR(-ENOENT)))
192  		dentry = __fh_to_dentry(sb, cfh->parent_ino);
193  	return dentry;
194  }
195  
196  static int ceph_get_name(struct dentry *parent, char *name,
197  			 struct dentry *child)
198  {
199  	struct ceph_mds_client *mdsc;
200  	struct ceph_mds_request *req;
201  	int err;
202  
203  	mdsc = ceph_inode_to_client(d_inode(child))->mdsc;
204  	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
205  				       USE_ANY_MDS);
206  	if (IS_ERR(req))
207  		return PTR_ERR(req);
208  
209  	inode_lock(d_inode(parent));
210  
211  	req->r_inode = d_inode(child);
212  	ihold(d_inode(child));
213  	req->r_ino2 = ceph_vino(d_inode(parent));
214  	req->r_parent = d_inode(parent);
215  	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
216  	req->r_num_caps = 2;
217  	err = ceph_mdsc_do_request(mdsc, NULL, req);
218  
219  	inode_unlock(d_inode(parent));
220  
221  	if (!err) {
222  		struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
223  		memcpy(name, rinfo->dname, rinfo->dname_len);
224  		name[rinfo->dname_len] = 0;
225  		dout("get_name %p ino %llx.%llx name %s\n",
226  		     child, ceph_vinop(d_inode(child)), name);
227  	} else {
228  		dout("get_name %p ino %llx.%llx err %d\n",
229  		     child, ceph_vinop(d_inode(child)), err);
230  	}
231  
232  	ceph_mdsc_put_request(req);
233  	return err;
234  }
235  
236  const struct export_operations ceph_export_ops = {
237  	.encode_fh = ceph_encode_fh,
238  	.fh_to_dentry = ceph_fh_to_dentry,
239  	.fh_to_parent = ceph_fh_to_parent,
240  	.get_parent = ceph_get_parent,
241  	.get_name = ceph_get_name,
242  };
243