Linux Audio

Check our new training course

Loading...
v3.15
 
  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 */
 13struct ceph_nfs_fh {
 14	u64 ino;
 15} __attribute__ ((packed));
 16
 17/*
 18 * Larger fh that includes parent ino.
 19 */
 20struct ceph_nfs_confh {
 21	u64 ino, parent_ino;
 22} __attribute__ ((packed));
 23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 24static 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
 61static 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 dentry *dentry;
 66	struct ceph_vino vino;
 67	int err;
 68
 69	vino.ino = ino;
 70	vino.snap = CEPH_NOSNAP;
 71	inode = ceph_find_inode(sb, vino);
 72	if (!inode) {
 73		struct ceph_mds_request *req;
 
 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		req->r_ino1 = vino;
 81		req->r_num_caps = 1;
 82		err = ceph_mdsc_do_request(mdsc, NULL, req);
 83		inode = req->r_target_inode;
 84		if (inode)
 85			ihold(inode);
 86		ceph_mdsc_put_request(req);
 87		if (!inode)
 88			return ERR_PTR(-ESTALE);
 89	}
 
 
 90
 91	dentry = d_obtain_alias(inode);
 92	if (IS_ERR(dentry)) {
 
 
 
 
 93		iput(inode);
 94		return dentry;
 95	}
 96	err = ceph_init_dentry(dentry);
 97	if (err < 0) {
 98		dput(dentry);
 
 
 
 
 
 
 
 
 
 
 99		return ERR_PTR(err);
 
 
 
 
100	}
101	dout("__fh_to_dentry %llx %p dentry %p\n", ino, inode, dentry);
102	return dentry;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103}
104
105/*
106 * convert regular fh to dentry
107 */
108static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
109					struct fid *fid,
110					int fh_len, int fh_type)
111{
112	struct ceph_nfs_fh *fh = (void *)fid->raw;
113
 
 
 
 
 
114	if (fh_type != FILEID_INO32_GEN  &&
115	    fh_type != FILEID_INO32_GEN_PARENT)
116		return NULL;
117	if (fh_len < sizeof(*fh) / 4)
118		return NULL;
119
120	dout("fh_to_dentry %llx\n", fh->ino);
121	return __fh_to_dentry(sb, fh->ino);
122}
123
124static struct dentry *__get_parent(struct super_block *sb,
125				   struct dentry *child, u64 ino)
126{
127	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
128	struct ceph_mds_request *req;
129	struct inode *inode;
130	struct dentry *dentry;
131	int err;
132
133	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
134				       USE_ANY_MDS);
135	if (IS_ERR(req))
136		return ERR_CAST(req);
137
138	if (child) {
139		req->r_inode = child->d_inode;
140		ihold(child->d_inode);
141	} else {
142		req->r_ino1 = (struct ceph_vino) {
143			.ino = ino,
144			.snap = CEPH_NOSNAP,
145		};
146	}
 
 
 
 
 
 
147	req->r_num_caps = 1;
148	err = ceph_mdsc_do_request(mdsc, NULL, req);
 
 
 
 
 
149	inode = req->r_target_inode;
150	if (inode)
151		ihold(inode);
152	ceph_mdsc_put_request(req);
153	if (!inode)
154		return ERR_PTR(-ENOENT);
155
156	dentry = d_obtain_alias(inode);
157	if (IS_ERR(dentry)) {
158		iput(inode);
159		return dentry;
160	}
161	err = ceph_init_dentry(dentry);
162	if (err < 0) {
163		dput(dentry);
164		return ERR_PTR(err);
165	}
166	dout("__get_parent ino %llx parent %p ino %llx.%llx\n",
167	     child ? ceph_ino(child->d_inode) : ino,
168	     dentry, ceph_vinop(inode));
169	return dentry;
170}
171
172struct dentry *ceph_get_parent(struct dentry *child)
173{
174	/* don't re-export snaps */
175	if (ceph_snap(child->d_inode) != CEPH_NOSNAP)
176		return ERR_PTR(-EINVAL);
177
178	dout("get_parent %p ino %llx.%llx\n",
179	     child, ceph_vinop(child->d_inode));
180	return __get_parent(child->d_sb, child, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181}
182
183/*
184 * convert regular fh to parent
185 */
186static struct dentry *ceph_fh_to_parent(struct super_block *sb,
187					struct fid *fid,
188					int fh_len, int fh_type)
189{
190	struct ceph_nfs_confh *cfh = (void *)fid->raw;
191	struct dentry *dentry;
192
 
 
 
 
 
193	if (fh_type != FILEID_INO32_GEN_PARENT)
194		return NULL;
195	if (fh_len < sizeof(*cfh) / 4)
196		return NULL;
197
198	dout("fh_to_parent %llx\n", cfh->parent_ino);
199	dentry = __get_parent(sb, NULL, cfh->ino);
200	if (IS_ERR(dentry) && PTR_ERR(dentry) == -ENOENT)
201		dentry = __fh_to_dentry(sb, cfh->parent_ino);
202	return dentry;
203}
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205static int ceph_get_name(struct dentry *parent, char *name,
206			 struct dentry *child)
207{
208	struct ceph_mds_client *mdsc;
209	struct ceph_mds_request *req;
 
210	int err;
211
212	mdsc = ceph_inode_to_client(child->d_inode)->mdsc;
 
 
 
213	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
214				       USE_ANY_MDS);
215	if (IS_ERR(req))
216		return PTR_ERR(req);
217
218	mutex_lock(&parent->d_inode->i_mutex);
219
220	req->r_inode = child->d_inode;
221	ihold(child->d_inode);
222	req->r_ino2 = ceph_vino(parent->d_inode);
223	req->r_locked_dir = parent->d_inode;
 
224	req->r_num_caps = 2;
225	err = ceph_mdsc_do_request(mdsc, NULL, req);
226
227	mutex_unlock(&parent->d_inode->i_mutex);
228
229	if (!err) {
230		struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
231		memcpy(name, rinfo->dname, rinfo->dname_len);
232		name[rinfo->dname_len] = 0;
233		dout("get_name %p ino %llx.%llx name %s\n",
234		     child, ceph_vinop(child->d_inode), name);
235	} else {
236		dout("get_name %p ino %llx.%llx err %d\n",
237		     child, ceph_vinop(child->d_inode), err);
238	}
239
240	ceph_mdsc_put_request(req);
241	return err;
242}
243
244const struct export_operations ceph_export_ops = {
245	.encode_fh = ceph_encode_fh,
246	.fh_to_dentry = ceph_fh_to_dentry,
247	.fh_to_parent = ceph_fh_to_parent,
248	.get_parent = ceph_get_parent,
249	.get_name = ceph_get_name,
250};
v5.9
  1// SPDX-License-Identifier: GPL-2.0
  2#include <linux/ceph/ceph_debug.h>
  3
  4#include <linux/exportfs.h>
  5#include <linux/slab.h>
  6#include <asm/unaligned.h>
  7
  8#include "super.h"
  9#include "mds_client.h"
 10
 11/*
 12 * Basic fh
 13 */
 14struct ceph_nfs_fh {
 15	u64 ino;
 16} __attribute__ ((packed));
 17
 18/*
 19 * Larger fh that includes parent ino.
 20 */
 21struct ceph_nfs_confh {
 22	u64 ino, parent_ino;
 23} __attribute__ ((packed));
 24
 25/*
 26 * fh for snapped inode
 27 */
 28struct ceph_nfs_snapfh {
 29	u64 ino;
 30	u64 snapid;
 31	u64 parent_ino;
 32	u32 hash;
 33} __attribute__ ((packed));
 34
 35static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
 36			      struct inode *parent_inode)
 37{
 38	static const int snap_handle_length =
 39		sizeof(struct ceph_nfs_snapfh) >> 2;
 40	struct ceph_nfs_snapfh *sfh = (void *)rawfh;
 41	u64 snapid = ceph_snap(inode);
 42	int ret;
 43	bool no_parent = true;
 44
 45	if (*max_len < snap_handle_length) {
 46		*max_len = snap_handle_length;
 47		ret = FILEID_INVALID;
 48		goto out;
 49	}
 50
 51	ret =  -EINVAL;
 52	if (snapid != CEPH_SNAPDIR) {
 53		struct inode *dir;
 54		struct dentry *dentry = d_find_alias(inode);
 55		if (!dentry)
 56			goto out;
 57
 58		rcu_read_lock();
 59		dir = d_inode_rcu(dentry->d_parent);
 60		if (ceph_snap(dir) != CEPH_SNAPDIR) {
 61			sfh->parent_ino = ceph_ino(dir);
 62			sfh->hash = ceph_dentry_hash(dir, dentry);
 63			no_parent = false;
 64		}
 65		rcu_read_unlock();
 66		dput(dentry);
 67	}
 68
 69	if (no_parent) {
 70		if (!S_ISDIR(inode->i_mode))
 71			goto out;
 72		sfh->parent_ino = sfh->ino;
 73		sfh->hash = 0;
 74	}
 75	sfh->ino = ceph_ino(inode);
 76	sfh->snapid = snapid;
 77
 78	*max_len = snap_handle_length;
 79	ret = FILEID_BTRFS_WITH_PARENT;
 80out:
 81	dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode), ret);
 82	return ret;
 83}
 84
 85static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
 86			  struct inode *parent_inode)
 87{
 88	static const int handle_length =
 89		sizeof(struct ceph_nfs_fh) >> 2;
 90	static const int connected_handle_length =
 91		sizeof(struct ceph_nfs_confh) >> 2;
 92	int type;
 
 
 
 
 93
 
 94	if (ceph_snap(inode) != CEPH_NOSNAP)
 95		return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);
 96
 97	if (parent_inode && (*max_len < connected_handle_length)) {
 98		*max_len = connected_handle_length;
 99		return FILEID_INVALID;
100	} else if (*max_len < handle_length) {
101		*max_len = handle_length;
102		return FILEID_INVALID;
103	}
104
105	if (parent_inode) {
106		struct ceph_nfs_confh *cfh = (void *)rawfh;
107		dout("encode_fh %llx with parent %llx\n",
108		     ceph_ino(inode), ceph_ino(parent_inode));
109		cfh->ino = ceph_ino(inode);
110		cfh->parent_ino = ceph_ino(parent_inode);
111		*max_len = connected_handle_length;
112		type = FILEID_INO32_GEN_PARENT;
113	} else {
114		struct ceph_nfs_fh *fh = (void *)rawfh;
115		dout("encode_fh %llx\n", ceph_ino(inode));
116		fh->ino = ceph_ino(inode);
117		*max_len = handle_length;
118		type = FILEID_INO32_GEN;
119	}
120	return type;
121}
122
123static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
124{
125	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
126	struct inode *inode;
 
127	struct ceph_vino vino;
128	int err;
129
130	vino.ino = ino;
131	vino.snap = CEPH_NOSNAP;
132	inode = ceph_find_inode(sb, vino);
133	if (!inode) {
134		struct ceph_mds_request *req;
135		int mask;
136
137		req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
138					       USE_ANY_MDS);
139		if (IS_ERR(req))
140			return ERR_CAST(req);
141
142		mask = CEPH_STAT_CAP_INODE;
143		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
144			mask |= CEPH_CAP_XATTR_SHARED;
145		req->r_args.lookupino.mask = cpu_to_le32(mask);
146
147		req->r_ino1 = vino;
148		req->r_num_caps = 1;
149		err = ceph_mdsc_do_request(mdsc, NULL, req);
150		inode = req->r_target_inode;
151		if (inode)
152			ihold(inode);
153		ceph_mdsc_put_request(req);
154		if (!inode)
155			return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
156	}
157	return inode;
158}
159
160struct inode *ceph_lookup_inode(struct super_block *sb, u64 ino)
161{
162	struct inode *inode = __lookup_inode(sb, ino);
163	if (IS_ERR(inode))
164		return inode;
165	if (inode->i_nlink == 0) {
166		iput(inode);
167		return ERR_PTR(-ESTALE);
168	}
169	return inode;
170}
171
172static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
173{
174	struct inode *inode = __lookup_inode(sb, ino);
175	int err;
176
177	if (IS_ERR(inode))
178		return ERR_CAST(inode);
179	/* We need LINK caps to reliably check i_nlink */
180	err = ceph_do_getattr(inode, CEPH_CAP_LINK_SHARED, false);
181	if (err)
182		return ERR_PTR(err);
183	/* -ESTALE if inode as been unlinked and no file is open */
184	if ((inode->i_nlink == 0) && (atomic_read(&inode->i_count) == 1)) {
185		iput(inode);
186		return ERR_PTR(-ESTALE);
187	}
188	return d_obtain_alias(inode);
189}
190
191static struct dentry *__snapfh_to_dentry(struct super_block *sb,
192					  struct ceph_nfs_snapfh *sfh,
193					  bool want_parent)
194{
195	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
196	struct ceph_mds_request *req;
197	struct inode *inode;
198	struct ceph_vino vino;
199	int mask;
200	int err;
201	bool unlinked = false;
202
203	if (want_parent) {
204		vino.ino = sfh->parent_ino;
205		if (sfh->snapid == CEPH_SNAPDIR)
206			vino.snap = CEPH_NOSNAP;
207		else if (sfh->ino == sfh->parent_ino)
208			vino.snap = CEPH_SNAPDIR;
209		else
210			vino.snap = sfh->snapid;
211	} else {
212		vino.ino = sfh->ino;
213		vino.snap = sfh->snapid;
214	}
215	inode = ceph_find_inode(sb, vino);
216	if (inode)
217		return d_obtain_alias(inode);
218
219	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
220				       USE_ANY_MDS);
221	if (IS_ERR(req))
222		return ERR_CAST(req);
223
224	mask = CEPH_STAT_CAP_INODE;
225	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
226		mask |= CEPH_CAP_XATTR_SHARED;
227	req->r_args.lookupino.mask = cpu_to_le32(mask);
228	if (vino.snap < CEPH_NOSNAP) {
229		req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
230		if (!want_parent && sfh->ino != sfh->parent_ino) {
231			req->r_args.lookupino.parent =
232					cpu_to_le64(sfh->parent_ino);
233			req->r_args.lookupino.hash =
234					cpu_to_le32(sfh->hash);
235		}
236	}
237
238	req->r_ino1 = vino;
239	req->r_num_caps = 1;
240	err = ceph_mdsc_do_request(mdsc, NULL, req);
241	inode = req->r_target_inode;
242	if (inode) {
243		if (vino.snap == CEPH_SNAPDIR) {
244			if (inode->i_nlink == 0)
245				unlinked = true;
246			inode = ceph_get_snapdir(inode);
247		} else if (ceph_snap(inode) == vino.snap) {
248			ihold(inode);
249		} else {
250			/* mds does not support lookup snapped inode */
251			err = -EOPNOTSUPP;
252			inode = NULL;
253		}
254	}
255	ceph_mdsc_put_request(req);
256
257	if (want_parent) {
258		dout("snapfh_to_parent %llx.%llx\n err=%d\n",
259		     vino.ino, vino.snap, err);
260	} else {
261		dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
262		      vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
263	}
264	if (!inode)
265		return ERR_PTR(-ESTALE);
266	/* see comments in ceph_get_parent() */
267	return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
268}
269
270/*
271 * convert regular fh to dentry
272 */
273static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
274					struct fid *fid,
275					int fh_len, int fh_type)
276{
277	struct ceph_nfs_fh *fh = (void *)fid->raw;
278
279	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
280		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
281		return __snapfh_to_dentry(sb, sfh, false);
282	}
283
284	if (fh_type != FILEID_INO32_GEN  &&
285	    fh_type != FILEID_INO32_GEN_PARENT)
286		return NULL;
287	if (fh_len < sizeof(*fh) / 4)
288		return NULL;
289
290	dout("fh_to_dentry %llx\n", fh->ino);
291	return __fh_to_dentry(sb, fh->ino);
292}
293
294static struct dentry *__get_parent(struct super_block *sb,
295				   struct dentry *child, u64 ino)
296{
297	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
298	struct ceph_mds_request *req;
299	struct inode *inode;
300	int mask;
301	int err;
302
303	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
304				       USE_ANY_MDS);
305	if (IS_ERR(req))
306		return ERR_CAST(req);
307
308	if (child) {
309		req->r_inode = d_inode(child);
310		ihold(d_inode(child));
311	} else {
312		req->r_ino1 = (struct ceph_vino) {
313			.ino = ino,
314			.snap = CEPH_NOSNAP,
315		};
316	}
317
318	mask = CEPH_STAT_CAP_INODE;
319	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
320		mask |= CEPH_CAP_XATTR_SHARED;
321	req->r_args.getattr.mask = cpu_to_le32(mask);
322
323	req->r_num_caps = 1;
324	err = ceph_mdsc_do_request(mdsc, NULL, req);
325	if (err) {
326		ceph_mdsc_put_request(req);
327		return ERR_PTR(err);
328	}
329
330	inode = req->r_target_inode;
331	if (inode)
332		ihold(inode);
333	ceph_mdsc_put_request(req);
334	if (!inode)
335		return ERR_PTR(-ENOENT);
336
337	return d_obtain_alias(inode);
 
 
 
 
 
 
 
 
 
 
 
 
 
338}
339
340static struct dentry *ceph_get_parent(struct dentry *child)
341{
342	struct inode *inode = d_inode(child);
343	struct dentry *dn;
344
345	if (ceph_snap(inode) != CEPH_NOSNAP) {
346		struct inode* dir;
347		bool unlinked = false;
348		/* do not support non-directory */
349		if (!d_is_dir(child)) {
350			dn = ERR_PTR(-EINVAL);
351			goto out;
352		}
353		dir = __lookup_inode(inode->i_sb, ceph_ino(inode));
354		if (IS_ERR(dir)) {
355			dn = ERR_CAST(dir);
356			goto out;
357		}
358		/* There can be multiple paths to access snapped inode.
359		 * For simplicity, treat snapdir of head inode as parent */
360		if (ceph_snap(inode) != CEPH_SNAPDIR) {
361			struct inode *snapdir = ceph_get_snapdir(dir);
362			if (dir->i_nlink == 0)
363				unlinked = true;
364			iput(dir);
365			if (IS_ERR(snapdir)) {
366				dn = ERR_CAST(snapdir);
367				goto out;
368			}
369			dir = snapdir;
370		}
371		/* If directory has already been deleted, futher get_parent
372		 * will fail. Do not mark snapdir dentry as disconnected,
373		 * this prevent exportfs from doing futher get_parent. */
374		if (unlinked)
375			dn = d_obtain_root(dir);
376		else
377			dn = d_obtain_alias(dir);
378	} else {
379		dn = __get_parent(child->d_sb, child, 0);
380	}
381out:
382	dout("get_parent %p ino %llx.%llx err=%ld\n",
383	     child, ceph_vinop(inode), (long)PTR_ERR_OR_ZERO(dn));
384	return dn;
385}
386
387/*
388 * convert regular fh to parent
389 */
390static struct dentry *ceph_fh_to_parent(struct super_block *sb,
391					struct fid *fid,
392					int fh_len, int fh_type)
393{
394	struct ceph_nfs_confh *cfh = (void *)fid->raw;
395	struct dentry *dentry;
396
397	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
398		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
399		return __snapfh_to_dentry(sb, sfh, true);
400	}
401
402	if (fh_type != FILEID_INO32_GEN_PARENT)
403		return NULL;
404	if (fh_len < sizeof(*cfh) / 4)
405		return NULL;
406
407	dout("fh_to_parent %llx\n", cfh->parent_ino);
408	dentry = __get_parent(sb, NULL, cfh->ino);
409	if (unlikely(dentry == ERR_PTR(-ENOENT)))
410		dentry = __fh_to_dentry(sb, cfh->parent_ino);
411	return dentry;
412}
413
414static int __get_snap_name(struct dentry *parent, char *name,
415			   struct dentry *child)
416{
417	struct inode *inode = d_inode(child);
418	struct inode *dir = d_inode(parent);
419	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
420	struct ceph_mds_request *req = NULL;
421	char *last_name = NULL;
422	unsigned next_offset = 2;
423	int err = -EINVAL;
424
425	if (ceph_ino(inode) != ceph_ino(dir))
426		goto out;
427	if (ceph_snap(inode) == CEPH_SNAPDIR) {
428		if (ceph_snap(dir) == CEPH_NOSNAP) {
429			strcpy(name, fsc->mount_options->snapdir_name);
430			err = 0;
431		}
432		goto out;
433	}
434	if (ceph_snap(dir) != CEPH_SNAPDIR)
435		goto out;
436
437	while (1) {
438		struct ceph_mds_reply_info_parsed *rinfo;
439		struct ceph_mds_reply_dir_entry *rde;
440		int i;
441
442		req = ceph_mdsc_create_request(fsc->mdsc, CEPH_MDS_OP_LSSNAP,
443					       USE_AUTH_MDS);
444		if (IS_ERR(req)) {
445			err = PTR_ERR(req);
446			req = NULL;
447			goto out;
448		}
449		err = ceph_alloc_readdir_reply_buffer(req, inode);
450		if (err)
451			goto out;
452
453		req->r_direct_mode = USE_AUTH_MDS;
454		req->r_readdir_offset = next_offset;
455		req->r_args.readdir.flags =
456				cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS);
457		if (last_name) {
458			req->r_path2 = last_name;
459			last_name = NULL;
460		}
461
462		req->r_inode = dir;
463		ihold(dir);
464		req->r_dentry = dget(parent);
465
466		inode_lock(dir);
467		err = ceph_mdsc_do_request(fsc->mdsc, NULL, req);
468		inode_unlock(dir);
469
470		if (err < 0)
471			goto out;
472
473		rinfo = &req->r_reply_info;
474		for (i = 0; i < rinfo->dir_nr; i++) {
475			rde = rinfo->dir_entries + i;
476			BUG_ON(!rde->inode.in);
477			if (ceph_snap(inode) ==
478			    le64_to_cpu(rde->inode.in->snapid)) {
479				memcpy(name, rde->name, rde->name_len);
480				name[rde->name_len] = '\0';
481				err = 0;
482				goto out;
483			}
484		}
485
486		if (rinfo->dir_end)
487			break;
488
489		BUG_ON(rinfo->dir_nr <= 0);
490		rde = rinfo->dir_entries + (rinfo->dir_nr - 1);
491		next_offset += rinfo->dir_nr;
492		last_name = kstrndup(rde->name, rde->name_len, GFP_KERNEL);
493		if (!last_name) {
494			err = -ENOMEM;
495			goto out;
496		}
497
498		ceph_mdsc_put_request(req);
499		req = NULL;
500	}
501	err = -ENOENT;
502out:
503	if (req)
504		ceph_mdsc_put_request(req);
505	kfree(last_name);
506	dout("get_snap_name %p ino %llx.%llx err=%d\n",
507	     child, ceph_vinop(inode), err);
508	return err;
509}
510
511static int ceph_get_name(struct dentry *parent, char *name,
512			 struct dentry *child)
513{
514	struct ceph_mds_client *mdsc;
515	struct ceph_mds_request *req;
516	struct inode *inode = d_inode(child);
517	int err;
518
519	if (ceph_snap(inode) != CEPH_NOSNAP)
520		return __get_snap_name(parent, name, child);
521
522	mdsc = ceph_inode_to_client(inode)->mdsc;
523	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
524				       USE_ANY_MDS);
525	if (IS_ERR(req))
526		return PTR_ERR(req);
527
528	inode_lock(d_inode(parent));
529
530	req->r_inode = inode;
531	ihold(inode);
532	req->r_ino2 = ceph_vino(d_inode(parent));
533	req->r_parent = d_inode(parent);
534	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
535	req->r_num_caps = 2;
536	err = ceph_mdsc_do_request(mdsc, NULL, req);
537
538	inode_unlock(d_inode(parent));
539
540	if (!err) {
541		struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
542		memcpy(name, rinfo->dname, rinfo->dname_len);
543		name[rinfo->dname_len] = 0;
544		dout("get_name %p ino %llx.%llx name %s\n",
545		     child, ceph_vinop(inode), name);
546	} else {
547		dout("get_name %p ino %llx.%llx err %d\n",
548		     child, ceph_vinop(inode), err);
549	}
550
551	ceph_mdsc_put_request(req);
552	return err;
553}
554
555const struct export_operations ceph_export_ops = {
556	.encode_fh = ceph_encode_fh,
557	.fh_to_dentry = ceph_fh_to_dentry,
558	.fh_to_parent = ceph_fh_to_parent,
559	.get_parent = ceph_get_parent,
560	.get_name = ceph_get_name,
561};