xref: /openbmc/linux/fs/ceph/export.c (revision 985b9ee8)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
23d14c5d2SYehuda Sadeh #include <linux/ceph/ceph_debug.h>
3a8e63b7dSSage Weil 
4a8e63b7dSSage Weil #include <linux/exportfs.h>
55a0e3ad6STejun Heo #include <linux/slab.h>
6a8e63b7dSSage Weil #include <asm/unaligned.h>
7a8e63b7dSSage Weil 
8a8e63b7dSSage Weil #include "super.h"
93d14c5d2SYehuda Sadeh #include "mds_client.h"
1085529096SJeff Layton #include "crypto.h"
11a8e63b7dSSage Weil 
12a8e63b7dSSage Weil /*
13a8e63b7dSSage Weil  * Basic fh
14a8e63b7dSSage Weil  */
15a8e63b7dSSage Weil struct ceph_nfs_fh {
16a8e63b7dSSage Weil 	u64 ino;
17a8e63b7dSSage Weil } __attribute__ ((packed));
18a8e63b7dSSage Weil 
19a8e63b7dSSage Weil /*
204f32b42dSYan, Zheng  * Larger fh that includes parent ino.
21a8e63b7dSSage Weil  */
22a8e63b7dSSage Weil struct ceph_nfs_confh {
23a8e63b7dSSage Weil 	u64 ino, parent_ino;
24a8e63b7dSSage Weil } __attribute__ ((packed));
25a8e63b7dSSage Weil 
26570df4e9SYan, Zheng /*
27570df4e9SYan, Zheng  * fh for snapped inode
28570df4e9SYan, Zheng  */
29570df4e9SYan, Zheng struct ceph_nfs_snapfh {
30570df4e9SYan, Zheng 	u64 ino;
31570df4e9SYan, Zheng 	u64 snapid;
32570df4e9SYan, Zheng 	u64 parent_ino;
33570df4e9SYan, Zheng 	u32 hash;
34570df4e9SYan, Zheng } __attribute__ ((packed));
35570df4e9SYan, Zheng 
ceph_encode_snapfh(struct inode * inode,u32 * rawfh,int * max_len,struct inode * parent_inode)36570df4e9SYan, Zheng static int ceph_encode_snapfh(struct inode *inode, u32 *rawfh, int *max_len,
37570df4e9SYan, Zheng 			      struct inode *parent_inode)
38570df4e9SYan, Zheng {
39536cc331SKrzysztof Wilczynski 	static const int snap_handle_length =
40570df4e9SYan, Zheng 		sizeof(struct ceph_nfs_snapfh) >> 2;
41570df4e9SYan, Zheng 	struct ceph_nfs_snapfh *sfh = (void *)rawfh;
42570df4e9SYan, Zheng 	u64 snapid = ceph_snap(inode);
43570df4e9SYan, Zheng 	int ret;
44570df4e9SYan, Zheng 	bool no_parent = true;
45570df4e9SYan, Zheng 
46570df4e9SYan, Zheng 	if (*max_len < snap_handle_length) {
47570df4e9SYan, Zheng 		*max_len = snap_handle_length;
48570df4e9SYan, Zheng 		ret = FILEID_INVALID;
49570df4e9SYan, Zheng 		goto out;
50570df4e9SYan, Zheng 	}
51570df4e9SYan, Zheng 
52570df4e9SYan, Zheng 	ret =  -EINVAL;
53570df4e9SYan, Zheng 	if (snapid != CEPH_SNAPDIR) {
54570df4e9SYan, Zheng 		struct inode *dir;
55570df4e9SYan, Zheng 		struct dentry *dentry = d_find_alias(inode);
56570df4e9SYan, Zheng 		if (!dentry)
57570df4e9SYan, Zheng 			goto out;
58570df4e9SYan, Zheng 
59570df4e9SYan, Zheng 		rcu_read_lock();
60570df4e9SYan, Zheng 		dir = d_inode_rcu(dentry->d_parent);
61570df4e9SYan, Zheng 		if (ceph_snap(dir) != CEPH_SNAPDIR) {
62570df4e9SYan, Zheng 			sfh->parent_ino = ceph_ino(dir);
63570df4e9SYan, Zheng 			sfh->hash = ceph_dentry_hash(dir, dentry);
64570df4e9SYan, Zheng 			no_parent = false;
65570df4e9SYan, Zheng 		}
66570df4e9SYan, Zheng 		rcu_read_unlock();
67570df4e9SYan, Zheng 		dput(dentry);
68570df4e9SYan, Zheng 	}
69570df4e9SYan, Zheng 
70570df4e9SYan, Zheng 	if (no_parent) {
71570df4e9SYan, Zheng 		if (!S_ISDIR(inode->i_mode))
72570df4e9SYan, Zheng 			goto out;
73570df4e9SYan, Zheng 		sfh->parent_ino = sfh->ino;
74570df4e9SYan, Zheng 		sfh->hash = 0;
75570df4e9SYan, Zheng 	}
76570df4e9SYan, Zheng 	sfh->ino = ceph_ino(inode);
77570df4e9SYan, Zheng 	sfh->snapid = snapid;
78570df4e9SYan, Zheng 
79570df4e9SYan, Zheng 	*max_len = snap_handle_length;
80570df4e9SYan, Zheng 	ret = FILEID_BTRFS_WITH_PARENT;
81570df4e9SYan, Zheng out:
82570df4e9SYan, Zheng 	dout("encode_snapfh %llx.%llx ret=%d\n", ceph_vinop(inode), ret);
83570df4e9SYan, Zheng 	return ret;
84570df4e9SYan, Zheng }
85570df4e9SYan, Zheng 
ceph_encode_fh(struct inode * inode,u32 * rawfh,int * max_len,struct inode * parent_inode)86c862868bSSage Weil static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
87c862868bSSage Weil 			  struct inode *parent_inode)
88a8e63b7dSSage Weil {
89536cc331SKrzysztof Wilczynski 	static const int handle_length =
90570df4e9SYan, Zheng 		sizeof(struct ceph_nfs_fh) >> 2;
91536cc331SKrzysztof Wilczynski 	static const int connected_handle_length =
92570df4e9SYan, Zheng 		sizeof(struct ceph_nfs_confh) >> 2;
9392923dcbSAneesh Kumar K.V 	int type;
94a8e63b7dSSage Weil 
95a8e63b7dSSage Weil 	if (ceph_snap(inode) != CEPH_NOSNAP)
96570df4e9SYan, Zheng 		return ceph_encode_snapfh(inode, rawfh, max_len, parent_inode);
97a8e63b7dSSage Weil 
984f32b42dSYan, Zheng 	if (parent_inode && (*max_len < connected_handle_length)) {
994f32b42dSYan, Zheng 		*max_len = connected_handle_length;
1004f32b42dSYan, Zheng 		return FILEID_INVALID;
1014f32b42dSYan, Zheng 	} else if (*max_len < handle_length) {
1024f32b42dSYan, Zheng 		*max_len = handle_length;
1034f32b42dSYan, Zheng 		return FILEID_INVALID;
1044f32b42dSYan, Zheng 	}
105f6af75daSCyril Roelandt 
106c862868bSSage Weil 	if (parent_inode) {
107570df4e9SYan, Zheng 		struct ceph_nfs_confh *cfh = (void *)rawfh;
1084f32b42dSYan, Zheng 		dout("encode_fh %llx with parent %llx\n",
1094f32b42dSYan, Zheng 		     ceph_ino(inode), ceph_ino(parent_inode));
1104f32b42dSYan, Zheng 		cfh->ino = ceph_ino(inode);
1114f32b42dSYan, Zheng 		cfh->parent_ino = ceph_ino(parent_inode);
112bba0cd0eSAneesh Kumar K.V 		*max_len = connected_handle_length;
1134f32b42dSYan, Zheng 		type = FILEID_INO32_GEN_PARENT;
114e5f86dc3SSage Weil 	} else {
115570df4e9SYan, Zheng 		struct ceph_nfs_fh *fh = (void *)rawfh;
1164f32b42dSYan, Zheng 		dout("encode_fh %llx\n", ceph_ino(inode));
117c862868bSSage Weil 		fh->ino = ceph_ino(inode);
11892923dcbSAneesh Kumar K.V 		*max_len = handle_length;
1194f32b42dSYan, Zheng 		type = FILEID_INO32_GEN;
120e5f86dc3SSage Weil 	}
121a8e63b7dSSage Weil 	return type;
122a8e63b7dSSage Weil }
123a8e63b7dSSage Weil 
__lookup_inode(struct super_block * sb,u64 ino)124570df4e9SYan, Zheng static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
125a8e63b7dSSage Weil {
126985b9ee8SXiubo Li 	struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc;
127a8e63b7dSSage Weil 	struct inode *inode;
128a8e63b7dSSage Weil 	struct ceph_vino vino;
129a8e63b7dSSage Weil 	int err;
130a8e63b7dSSage Weil 
1314f32b42dSYan, Zheng 	vino.ino = ino;
132a8e63b7dSSage Weil 	vino.snap = CEPH_NOSNAP;
133d4f6b31dSJeff Layton 
134d4f6b31dSJeff Layton 	if (ceph_vino_is_reserved(vino))
135d4f6b31dSJeff Layton 		return ERR_PTR(-ESTALE);
136d4f6b31dSJeff Layton 
137a8e63b7dSSage Weil 	inode = ceph_find_inode(sb, vino);
1383c454cf2SSage Weil 	if (!inode) {
1393c454cf2SSage Weil 		struct ceph_mds_request *req;
140315f2408SYan, Zheng 		int mask;
1413c454cf2SSage Weil 
1423c454cf2SSage Weil 		req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
1433c454cf2SSage Weil 					       USE_ANY_MDS);
1443c454cf2SSage Weil 		if (IS_ERR(req))
1453c454cf2SSage Weil 			return ERR_CAST(req);
1463c454cf2SSage Weil 
147315f2408SYan, Zheng 		mask = CEPH_STAT_CAP_INODE;
148315f2408SYan, Zheng 		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
149315f2408SYan, Zheng 			mask |= CEPH_CAP_XATTR_SHARED;
150570df4e9SYan, Zheng 		req->r_args.lookupino.mask = cpu_to_le32(mask);
151315f2408SYan, Zheng 
1523c454cf2SSage Weil 		req->r_ino1 = vino;
1533c454cf2SSage Weil 		req->r_num_caps = 1;
1543c454cf2SSage Weil 		err = ceph_mdsc_do_request(mdsc, NULL, req);
15545e3d3eeSSage Weil 		inode = req->r_target_inode;
15645e3d3eeSSage Weil 		if (inode)
15770b666c3SSage Weil 			ihold(inode);
1583c454cf2SSage Weil 		ceph_mdsc_put_request(req);
159a8e63b7dSSage Weil 		if (!inode)
1603886274aSLuis Henriques 			return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
1615d6451b1SJeff Layton 	} else {
1625d6451b1SJeff Layton 		if (ceph_inode_is_shutdown(inode)) {
1635d6451b1SJeff Layton 			iput(inode);
1645d6451b1SJeff Layton 			return ERR_PTR(-ESTALE);
1655d6451b1SJeff Layton 		}
166570df4e9SYan, Zheng 	}
167570df4e9SYan, Zheng 	return inode;
168570df4e9SYan, Zheng }
169570df4e9SYan, Zheng 
ceph_lookup_inode(struct super_block * sb,u64 ino)170570df4e9SYan, Zheng struct inode *ceph_lookup_inode(struct super_block *sb, u64 ino)
171570df4e9SYan, Zheng {
172570df4e9SYan, Zheng 	struct inode *inode = __lookup_inode(sb, ino);
173570df4e9SYan, Zheng 	if (IS_ERR(inode))
174570df4e9SYan, Zheng 		return inode;
17503f21904SLuis Henriques 	if (inode->i_nlink == 0) {
17603f21904SLuis Henriques 		iput(inode);
17703f21904SLuis Henriques 		return ERR_PTR(-ESTALE);
17803f21904SLuis Henriques 	}
1793886274aSLuis Henriques 	return inode;
1803886274aSLuis Henriques }
1813886274aSLuis Henriques 
__fh_to_dentry(struct super_block * sb,u64 ino)1823886274aSLuis Henriques static struct dentry *__fh_to_dentry(struct super_block *sb, u64 ino)
1833886274aSLuis Henriques {
184570df4e9SYan, Zheng 	struct inode *inode = __lookup_inode(sb, ino);
185bd04b919SXiubo Li 	struct ceph_inode_info *ci = ceph_inode(inode);
186878dabb6SLuis Henriques 	int err;
187878dabb6SLuis Henriques 
1883886274aSLuis Henriques 	if (IS_ERR(inode))
1893886274aSLuis Henriques 		return ERR_CAST(inode);
190878dabb6SLuis Henriques 	/* We need LINK caps to reliably check i_nlink */
191878dabb6SLuis Henriques 	err = ceph_do_getattr(inode, CEPH_CAP_LINK_SHARED, false);
1921775c7ddSJeff Layton 	if (err) {
1931775c7ddSJeff Layton 		iput(inode);
194878dabb6SLuis Henriques 		return ERR_PTR(err);
1951775c7ddSJeff Layton 	}
196878dabb6SLuis Henriques 	/* -ESTALE if inode as been unlinked and no file is open */
197bd04b919SXiubo Li 	if ((inode->i_nlink == 0) && !__ceph_is_file_opened(ci)) {
198570df4e9SYan, Zheng 		iput(inode);
199570df4e9SYan, Zheng 		return ERR_PTR(-ESTALE);
200570df4e9SYan, Zheng 	}
201ad5cb123SAl Viro 	return d_obtain_alias(inode);
202a8e63b7dSSage Weil }
203a8e63b7dSSage Weil 
__snapfh_to_dentry(struct super_block * sb,struct ceph_nfs_snapfh * sfh,bool want_parent)204570df4e9SYan, Zheng static struct dentry *__snapfh_to_dentry(struct super_block *sb,
205570df4e9SYan, Zheng 					  struct ceph_nfs_snapfh *sfh,
206570df4e9SYan, Zheng 					  bool want_parent)
207570df4e9SYan, Zheng {
208985b9ee8SXiubo Li 	struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc;
209570df4e9SYan, Zheng 	struct ceph_mds_request *req;
210570df4e9SYan, Zheng 	struct inode *inode;
211570df4e9SYan, Zheng 	struct ceph_vino vino;
212570df4e9SYan, Zheng 	int mask;
213570df4e9SYan, Zheng 	int err;
214570df4e9SYan, Zheng 	bool unlinked = false;
215570df4e9SYan, Zheng 
216570df4e9SYan, Zheng 	if (want_parent) {
217570df4e9SYan, Zheng 		vino.ino = sfh->parent_ino;
218570df4e9SYan, Zheng 		if (sfh->snapid == CEPH_SNAPDIR)
219570df4e9SYan, Zheng 			vino.snap = CEPH_NOSNAP;
220570df4e9SYan, Zheng 		else if (sfh->ino == sfh->parent_ino)
221570df4e9SYan, Zheng 			vino.snap = CEPH_SNAPDIR;
222570df4e9SYan, Zheng 		else
223570df4e9SYan, Zheng 			vino.snap = sfh->snapid;
224570df4e9SYan, Zheng 	} else {
225570df4e9SYan, Zheng 		vino.ino = sfh->ino;
226570df4e9SYan, Zheng 		vino.snap = sfh->snapid;
227570df4e9SYan, Zheng 	}
228d4f6b31dSJeff Layton 
229d4f6b31dSJeff Layton 	if (ceph_vino_is_reserved(vino))
230d4f6b31dSJeff Layton 		return ERR_PTR(-ESTALE);
231d4f6b31dSJeff Layton 
232570df4e9SYan, Zheng 	inode = ceph_find_inode(sb, vino);
2335d6451b1SJeff Layton 	if (inode) {
2345d6451b1SJeff Layton 		if (ceph_inode_is_shutdown(inode)) {
2355d6451b1SJeff Layton 			iput(inode);
2365d6451b1SJeff Layton 			return ERR_PTR(-ESTALE);
2375d6451b1SJeff Layton 		}
238570df4e9SYan, Zheng 		return d_obtain_alias(inode);
2395d6451b1SJeff Layton 	}
240570df4e9SYan, Zheng 
241570df4e9SYan, Zheng 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
242570df4e9SYan, Zheng 				       USE_ANY_MDS);
243570df4e9SYan, Zheng 	if (IS_ERR(req))
244570df4e9SYan, Zheng 		return ERR_CAST(req);
245570df4e9SYan, Zheng 
246570df4e9SYan, Zheng 	mask = CEPH_STAT_CAP_INODE;
247570df4e9SYan, Zheng 	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
248570df4e9SYan, Zheng 		mask |= CEPH_CAP_XATTR_SHARED;
249570df4e9SYan, Zheng 	req->r_args.lookupino.mask = cpu_to_le32(mask);
250570df4e9SYan, Zheng 	if (vino.snap < CEPH_NOSNAP) {
251570df4e9SYan, Zheng 		req->r_args.lookupino.snapid = cpu_to_le64(vino.snap);
252570df4e9SYan, Zheng 		if (!want_parent && sfh->ino != sfh->parent_ino) {
253570df4e9SYan, Zheng 			req->r_args.lookupino.parent =
254570df4e9SYan, Zheng 					cpu_to_le64(sfh->parent_ino);
255570df4e9SYan, Zheng 			req->r_args.lookupino.hash =
256570df4e9SYan, Zheng 					cpu_to_le32(sfh->hash);
257570df4e9SYan, Zheng 		}
258570df4e9SYan, Zheng 	}
259570df4e9SYan, Zheng 
260570df4e9SYan, Zheng 	req->r_ino1 = vino;
261570df4e9SYan, Zheng 	req->r_num_caps = 1;
262570df4e9SYan, Zheng 	err = ceph_mdsc_do_request(mdsc, NULL, req);
263570df4e9SYan, Zheng 	inode = req->r_target_inode;
264570df4e9SYan, Zheng 	if (inode) {
265570df4e9SYan, Zheng 		if (vino.snap == CEPH_SNAPDIR) {
266570df4e9SYan, Zheng 			if (inode->i_nlink == 0)
267570df4e9SYan, Zheng 				unlinked = true;
268570df4e9SYan, Zheng 			inode = ceph_get_snapdir(inode);
269570df4e9SYan, Zheng 		} else if (ceph_snap(inode) == vino.snap) {
270570df4e9SYan, Zheng 			ihold(inode);
271570df4e9SYan, Zheng 		} else {
272570df4e9SYan, Zheng 			/* mds does not support lookup snapped inode */
2733e10a15fSJeff Layton 			inode = ERR_PTR(-EOPNOTSUPP);
274570df4e9SYan, Zheng 		}
2753e10a15fSJeff Layton 	} else {
2763e10a15fSJeff Layton 		inode = ERR_PTR(-ESTALE);
277570df4e9SYan, Zheng 	}
278570df4e9SYan, Zheng 	ceph_mdsc_put_request(req);
279570df4e9SYan, Zheng 
280570df4e9SYan, Zheng 	if (want_parent) {
281570df4e9SYan, Zheng 		dout("snapfh_to_parent %llx.%llx\n err=%d\n",
282570df4e9SYan, Zheng 		     vino.ino, vino.snap, err);
283570df4e9SYan, Zheng 	} else {
284570df4e9SYan, Zheng 		dout("snapfh_to_dentry %llx.%llx parent %llx hash %x err=%d",
285570df4e9SYan, Zheng 		      vino.ino, vino.snap, sfh->parent_ino, sfh->hash, err);
286570df4e9SYan, Zheng 	}
2873e10a15fSJeff Layton 	if (IS_ERR(inode))
2883e10a15fSJeff Layton 		return ERR_CAST(inode);
289570df4e9SYan, Zheng 	/* see comments in ceph_get_parent() */
290570df4e9SYan, Zheng 	return unlinked ? d_obtain_root(inode) : d_obtain_alias(inode);
291570df4e9SYan, Zheng }
292570df4e9SYan, Zheng 
293a8e63b7dSSage Weil /*
2944f32b42dSYan, Zheng  * convert regular fh to dentry
295a8e63b7dSSage Weil  */
ceph_fh_to_dentry(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)2964f32b42dSYan, Zheng static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
2974f32b42dSYan, Zheng 					struct fid *fid,
298a8e63b7dSSage Weil 					int fh_len, int fh_type)
299a8e63b7dSSage Weil {
3004f32b42dSYan, Zheng 	struct ceph_nfs_fh *fh = (void *)fid->raw;
3014f32b42dSYan, Zheng 
302570df4e9SYan, Zheng 	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
303570df4e9SYan, Zheng 		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
304570df4e9SYan, Zheng 		return __snapfh_to_dentry(sb, sfh, false);
305570df4e9SYan, Zheng 	}
306570df4e9SYan, Zheng 
3074f32b42dSYan, Zheng 	if (fh_type != FILEID_INO32_GEN  &&
3084f32b42dSYan, Zheng 	    fh_type != FILEID_INO32_GEN_PARENT)
3094f32b42dSYan, Zheng 		return NULL;
3104f32b42dSYan, Zheng 	if (fh_len < sizeof(*fh) / 4)
3114f32b42dSYan, Zheng 		return NULL;
3124f32b42dSYan, Zheng 
3134f32b42dSYan, Zheng 	dout("fh_to_dentry %llx\n", fh->ino);
3144f32b42dSYan, Zheng 	return __fh_to_dentry(sb, fh->ino);
315a8e63b7dSSage Weil }
316a8e63b7dSSage Weil 
__get_parent(struct super_block * sb,struct dentry * child,u64 ino)3179017c2ecSYan, Zheng static struct dentry *__get_parent(struct super_block *sb,
3189017c2ecSYan, Zheng 				   struct dentry *child, u64 ino)
3199017c2ecSYan, Zheng {
320985b9ee8SXiubo Li 	struct ceph_mds_client *mdsc = ceph_sb_to_fs_client(sb)->mdsc;
3219017c2ecSYan, Zheng 	struct ceph_mds_request *req;
3229017c2ecSYan, Zheng 	struct inode *inode;
323315f2408SYan, Zheng 	int mask;
3249017c2ecSYan, Zheng 	int err;
3259017c2ecSYan, Zheng 
3269017c2ecSYan, Zheng 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
3279017c2ecSYan, Zheng 				       USE_ANY_MDS);
3289017c2ecSYan, Zheng 	if (IS_ERR(req))
3299017c2ecSYan, Zheng 		return ERR_CAST(req);
3309017c2ecSYan, Zheng 
3319017c2ecSYan, Zheng 	if (child) {
3322b0143b5SDavid Howells 		req->r_inode = d_inode(child);
3332b0143b5SDavid Howells 		ihold(d_inode(child));
3349017c2ecSYan, Zheng 	} else {
3359017c2ecSYan, Zheng 		req->r_ino1 = (struct ceph_vino) {
3369017c2ecSYan, Zheng 			.ino = ino,
3379017c2ecSYan, Zheng 			.snap = CEPH_NOSNAP,
3389017c2ecSYan, Zheng 		};
3399017c2ecSYan, Zheng 	}
340315f2408SYan, Zheng 
341315f2408SYan, Zheng 	mask = CEPH_STAT_CAP_INODE;
342315f2408SYan, Zheng 	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
343315f2408SYan, Zheng 		mask |= CEPH_CAP_XATTR_SHARED;
344315f2408SYan, Zheng 	req->r_args.getattr.mask = cpu_to_le32(mask);
345315f2408SYan, Zheng 
3469017c2ecSYan, Zheng 	req->r_num_caps = 1;
3479017c2ecSYan, Zheng 	err = ceph_mdsc_do_request(mdsc, NULL, req);
348c6d50296SQiujun Huang 	if (err) {
349c6d50296SQiujun Huang 		ceph_mdsc_put_request(req);
350c6d50296SQiujun Huang 		return ERR_PTR(err);
351c6d50296SQiujun Huang 	}
352c6d50296SQiujun Huang 
3539017c2ecSYan, Zheng 	inode = req->r_target_inode;
3549017c2ecSYan, Zheng 	if (inode)
3559017c2ecSYan, Zheng 		ihold(inode);
3569017c2ecSYan, Zheng 	ceph_mdsc_put_request(req);
3579017c2ecSYan, Zheng 	if (!inode)
3589017c2ecSYan, Zheng 		return ERR_PTR(-ENOENT);
3599017c2ecSYan, Zheng 
360ad5cb123SAl Viro 	return d_obtain_alias(inode);
3619017c2ecSYan, Zheng }
3629017c2ecSYan, Zheng 
ceph_get_parent(struct dentry * child)363e84be11cSFengguang Wu static struct dentry *ceph_get_parent(struct dentry *child)
3649017c2ecSYan, Zheng {
365570df4e9SYan, Zheng 	struct inode *inode = d_inode(child);
366570df4e9SYan, Zheng 	struct dentry *dn;
3679017c2ecSYan, Zheng 
368570df4e9SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP) {
369570df4e9SYan, Zheng 		struct inode* dir;
370570df4e9SYan, Zheng 		bool unlinked = false;
371570df4e9SYan, Zheng 		/* do not support non-directory */
372570df4e9SYan, Zheng 		if (!d_is_dir(child)) {
373570df4e9SYan, Zheng 			dn = ERR_PTR(-EINVAL);
374570df4e9SYan, Zheng 			goto out;
375570df4e9SYan, Zheng 		}
376570df4e9SYan, Zheng 		dir = __lookup_inode(inode->i_sb, ceph_ino(inode));
377570df4e9SYan, Zheng 		if (IS_ERR(dir)) {
378570df4e9SYan, Zheng 			dn = ERR_CAST(dir);
379570df4e9SYan, Zheng 			goto out;
380570df4e9SYan, Zheng 		}
381570df4e9SYan, Zheng 		/* There can be multiple paths to access snapped inode.
382570df4e9SYan, Zheng 		 * For simplicity, treat snapdir of head inode as parent */
383570df4e9SYan, Zheng 		if (ceph_snap(inode) != CEPH_SNAPDIR) {
384570df4e9SYan, Zheng 			struct inode *snapdir = ceph_get_snapdir(dir);
385570df4e9SYan, Zheng 			if (dir->i_nlink == 0)
386570df4e9SYan, Zheng 				unlinked = true;
387570df4e9SYan, Zheng 			iput(dir);
388570df4e9SYan, Zheng 			if (IS_ERR(snapdir)) {
389570df4e9SYan, Zheng 				dn = ERR_CAST(snapdir);
390570df4e9SYan, Zheng 				goto out;
391570df4e9SYan, Zheng 			}
392570df4e9SYan, Zheng 			dir = snapdir;
393570df4e9SYan, Zheng 		}
394570df4e9SYan, Zheng 		/* If directory has already been deleted, futher get_parent
395570df4e9SYan, Zheng 		 * will fail. Do not mark snapdir dentry as disconnected,
396570df4e9SYan, Zheng 		 * this prevent exportfs from doing futher get_parent. */
397570df4e9SYan, Zheng 		if (unlinked)
398570df4e9SYan, Zheng 			dn = d_obtain_root(dir);
399570df4e9SYan, Zheng 		else
400570df4e9SYan, Zheng 			dn = d_obtain_alias(dir);
401570df4e9SYan, Zheng 	} else {
402570df4e9SYan, Zheng 		dn = __get_parent(child->d_sb, child, 0);
403570df4e9SYan, Zheng 	}
404570df4e9SYan, Zheng out:
405570df4e9SYan, Zheng 	dout("get_parent %p ino %llx.%llx err=%ld\n",
40603af439aSHariprasad Kelam 	     child, ceph_vinop(inode), (long)PTR_ERR_OR_ZERO(dn));
407570df4e9SYan, Zheng 	return dn;
4089017c2ecSYan, Zheng }
4099017c2ecSYan, Zheng 
410a8e63b7dSSage Weil /*
4118996f4f2SYan, Zheng  * convert regular fh to parent
412a8e63b7dSSage Weil  */
ceph_fh_to_parent(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)413a8e63b7dSSage Weil static struct dentry *ceph_fh_to_parent(struct super_block *sb,
414a8e63b7dSSage Weil 					struct fid *fid,
415a8e63b7dSSage Weil 					int fh_len, int fh_type)
416a8e63b7dSSage Weil {
417a8e63b7dSSage Weil 	struct ceph_nfs_confh *cfh = (void *)fid->raw;
418a8e63b7dSSage Weil 	struct dentry *dentry;
419a8e63b7dSSage Weil 
420570df4e9SYan, Zheng 	if (fh_type == FILEID_BTRFS_WITH_PARENT) {
421570df4e9SYan, Zheng 		struct ceph_nfs_snapfh *sfh = (void *)fid->raw;
422570df4e9SYan, Zheng 		return __snapfh_to_dentry(sb, sfh, true);
423570df4e9SYan, Zheng 	}
424570df4e9SYan, Zheng 
4258996f4f2SYan, Zheng 	if (fh_type != FILEID_INO32_GEN_PARENT)
4268996f4f2SYan, Zheng 		return NULL;
42735c2a7f4SHugh Dickins 	if (fh_len < sizeof(*cfh) / 4)
4288996f4f2SYan, Zheng 		return NULL;
429a8e63b7dSSage Weil 
4308996f4f2SYan, Zheng 	dout("fh_to_parent %llx\n", cfh->parent_ino);
4318996f4f2SYan, Zheng 	dentry = __get_parent(sb, NULL, cfh->ino);
432b42b90d1SAl Viro 	if (unlikely(dentry == ERR_PTR(-ENOENT)))
4338996f4f2SYan, Zheng 		dentry = __fh_to_dentry(sb, cfh->parent_ino);
434a8e63b7dSSage Weil 	return dentry;
435a8e63b7dSSage Weil }
436a8e63b7dSSage Weil 
__get_snap_name(struct dentry * parent,char * name,struct dentry * child)437570df4e9SYan, Zheng static int __get_snap_name(struct dentry *parent, char *name,
438570df4e9SYan, Zheng 			   struct dentry *child)
439570df4e9SYan, Zheng {
440570df4e9SYan, Zheng 	struct inode *inode = d_inode(child);
441570df4e9SYan, Zheng 	struct inode *dir = d_inode(parent);
442985b9ee8SXiubo Li 	struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
443570df4e9SYan, Zheng 	struct ceph_mds_request *req = NULL;
444570df4e9SYan, Zheng 	char *last_name = NULL;
445570df4e9SYan, Zheng 	unsigned next_offset = 2;
446570df4e9SYan, Zheng 	int err = -EINVAL;
447570df4e9SYan, Zheng 
448570df4e9SYan, Zheng 	if (ceph_ino(inode) != ceph_ino(dir))
449570df4e9SYan, Zheng 		goto out;
450570df4e9SYan, Zheng 	if (ceph_snap(inode) == CEPH_SNAPDIR) {
451570df4e9SYan, Zheng 		if (ceph_snap(dir) == CEPH_NOSNAP) {
452570df4e9SYan, Zheng 			strcpy(name, fsc->mount_options->snapdir_name);
453570df4e9SYan, Zheng 			err = 0;
454570df4e9SYan, Zheng 		}
455570df4e9SYan, Zheng 		goto out;
456570df4e9SYan, Zheng 	}
457570df4e9SYan, Zheng 	if (ceph_snap(dir) != CEPH_SNAPDIR)
458570df4e9SYan, Zheng 		goto out;
459570df4e9SYan, Zheng 
460570df4e9SYan, Zheng 	while (1) {
461570df4e9SYan, Zheng 		struct ceph_mds_reply_info_parsed *rinfo;
462570df4e9SYan, Zheng 		struct ceph_mds_reply_dir_entry *rde;
463570df4e9SYan, Zheng 		int i;
464570df4e9SYan, Zheng 
465570df4e9SYan, Zheng 		req = ceph_mdsc_create_request(fsc->mdsc, CEPH_MDS_OP_LSSNAP,
466570df4e9SYan, Zheng 					       USE_AUTH_MDS);
467570df4e9SYan, Zheng 		if (IS_ERR(req)) {
468570df4e9SYan, Zheng 			err = PTR_ERR(req);
469570df4e9SYan, Zheng 			req = NULL;
470570df4e9SYan, Zheng 			goto out;
471570df4e9SYan, Zheng 		}
472570df4e9SYan, Zheng 		err = ceph_alloc_readdir_reply_buffer(req, inode);
473570df4e9SYan, Zheng 		if (err)
474570df4e9SYan, Zheng 			goto out;
475570df4e9SYan, Zheng 
476570df4e9SYan, Zheng 		req->r_direct_mode = USE_AUTH_MDS;
477570df4e9SYan, Zheng 		req->r_readdir_offset = next_offset;
478570df4e9SYan, Zheng 		req->r_args.readdir.flags =
479570df4e9SYan, Zheng 				cpu_to_le16(CEPH_READDIR_REPLY_BITFLAGS);
480570df4e9SYan, Zheng 		if (last_name) {
481570df4e9SYan, Zheng 			req->r_path2 = last_name;
482570df4e9SYan, Zheng 			last_name = NULL;
483570df4e9SYan, Zheng 		}
484570df4e9SYan, Zheng 
485570df4e9SYan, Zheng 		req->r_inode = dir;
486570df4e9SYan, Zheng 		ihold(dir);
487570df4e9SYan, Zheng 		req->r_dentry = dget(parent);
488570df4e9SYan, Zheng 
489570df4e9SYan, Zheng 		inode_lock(dir);
490570df4e9SYan, Zheng 		err = ceph_mdsc_do_request(fsc->mdsc, NULL, req);
491570df4e9SYan, Zheng 		inode_unlock(dir);
492570df4e9SYan, Zheng 
493570df4e9SYan, Zheng 		if (err < 0)
494570df4e9SYan, Zheng 			goto out;
495570df4e9SYan, Zheng 
496570df4e9SYan, Zheng 		rinfo = &req->r_reply_info;
497570df4e9SYan, Zheng 		for (i = 0; i < rinfo->dir_nr; i++) {
498570df4e9SYan, Zheng 			rde = rinfo->dir_entries + i;
499570df4e9SYan, Zheng 			BUG_ON(!rde->inode.in);
500570df4e9SYan, Zheng 			if (ceph_snap(inode) ==
501570df4e9SYan, Zheng 			    le64_to_cpu(rde->inode.in->snapid)) {
502570df4e9SYan, Zheng 				memcpy(name, rde->name, rde->name_len);
503570df4e9SYan, Zheng 				name[rde->name_len] = '\0';
504570df4e9SYan, Zheng 				err = 0;
505570df4e9SYan, Zheng 				goto out;
506570df4e9SYan, Zheng 			}
507570df4e9SYan, Zheng 		}
508570df4e9SYan, Zheng 
509570df4e9SYan, Zheng 		if (rinfo->dir_end)
510570df4e9SYan, Zheng 			break;
511570df4e9SYan, Zheng 
512570df4e9SYan, Zheng 		BUG_ON(rinfo->dir_nr <= 0);
513570df4e9SYan, Zheng 		rde = rinfo->dir_entries + (rinfo->dir_nr - 1);
514570df4e9SYan, Zheng 		next_offset += rinfo->dir_nr;
515570df4e9SYan, Zheng 		last_name = kstrndup(rde->name, rde->name_len, GFP_KERNEL);
516570df4e9SYan, Zheng 		if (!last_name) {
517570df4e9SYan, Zheng 			err = -ENOMEM;
518570df4e9SYan, Zheng 			goto out;
519570df4e9SYan, Zheng 		}
520570df4e9SYan, Zheng 
521570df4e9SYan, Zheng 		ceph_mdsc_put_request(req);
522570df4e9SYan, Zheng 		req = NULL;
523570df4e9SYan, Zheng 	}
524570df4e9SYan, Zheng 	err = -ENOENT;
525570df4e9SYan, Zheng out:
526570df4e9SYan, Zheng 	if (req)
527570df4e9SYan, Zheng 		ceph_mdsc_put_request(req);
528570df4e9SYan, Zheng 	kfree(last_name);
529570df4e9SYan, Zheng 	dout("get_snap_name %p ino %llx.%llx err=%d\n",
530570df4e9SYan, Zheng 	     child, ceph_vinop(inode), err);
531570df4e9SYan, Zheng 	return err;
532570df4e9SYan, Zheng }
533570df4e9SYan, Zheng 
ceph_get_name(struct dentry * parent,char * name,struct dentry * child)53419913b4eSYan, Zheng static int ceph_get_name(struct dentry *parent, char *name,
53519913b4eSYan, Zheng 			 struct dentry *child)
53619913b4eSYan, Zheng {
53719913b4eSYan, Zheng 	struct ceph_mds_client *mdsc;
53819913b4eSYan, Zheng 	struct ceph_mds_request *req;
53985529096SJeff Layton 	struct inode *dir = d_inode(parent);
540570df4e9SYan, Zheng 	struct inode *inode = d_inode(child);
54185529096SJeff Layton 	struct ceph_mds_reply_info_parsed *rinfo;
54219913b4eSYan, Zheng 	int err;
54319913b4eSYan, Zheng 
544570df4e9SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
545570df4e9SYan, Zheng 		return __get_snap_name(parent, name, child);
546570df4e9SYan, Zheng 
547985b9ee8SXiubo Li 	mdsc = ceph_inode_to_fs_client(inode)->mdsc;
54819913b4eSYan, Zheng 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
54919913b4eSYan, Zheng 				       USE_ANY_MDS);
55019913b4eSYan, Zheng 	if (IS_ERR(req))
55119913b4eSYan, Zheng 		return PTR_ERR(req);
55219913b4eSYan, Zheng 
55385529096SJeff Layton 	inode_lock(dir);
554570df4e9SYan, Zheng 	req->r_inode = inode;
555570df4e9SYan, Zheng 	ihold(inode);
5562b0143b5SDavid Howells 	req->r_ino2 = ceph_vino(d_inode(parent));
55785529096SJeff Layton 	req->r_parent = dir;
55885529096SJeff Layton 	ihold(dir);
5593dd69aabSJeff Layton 	set_bit(CEPH_MDS_R_PARENT_LOCKED, &req->r_req_flags);
56019913b4eSYan, Zheng 	req->r_num_caps = 2;
56119913b4eSYan, Zheng 	err = ceph_mdsc_do_request(mdsc, NULL, req);
56285529096SJeff Layton 	inode_unlock(dir);
56319913b4eSYan, Zheng 
56485529096SJeff Layton 	if (err)
56585529096SJeff Layton 		goto out;
56619913b4eSYan, Zheng 
56785529096SJeff Layton 	rinfo = &req->r_reply_info;
56885529096SJeff Layton 	if (!IS_ENCRYPTED(dir)) {
56919913b4eSYan, Zheng 		memcpy(name, rinfo->dname, rinfo->dname_len);
57019913b4eSYan, Zheng 		name[rinfo->dname_len] = 0;
57119913b4eSYan, Zheng 	} else {
57285529096SJeff Layton 		struct fscrypt_str oname = FSTR_INIT(NULL, 0);
57385529096SJeff Layton 		struct ceph_fname fname = { .dir	= dir,
57485529096SJeff Layton 					    .name	= rinfo->dname,
57585529096SJeff Layton 					    .ctext	= rinfo->altname,
57685529096SJeff Layton 					    .name_len	= rinfo->dname_len,
57785529096SJeff Layton 					    .ctext_len	= rinfo->altname_len };
57819913b4eSYan, Zheng 
57985529096SJeff Layton 		err = ceph_fname_alloc_buffer(dir, &oname);
58085529096SJeff Layton 		if (err < 0)
58185529096SJeff Layton 			goto out;
58285529096SJeff Layton 
58385529096SJeff Layton 		err = ceph_fname_to_usr(&fname, NULL, &oname, NULL);
58485529096SJeff Layton 		if (!err) {
58585529096SJeff Layton 			memcpy(name, oname.name, oname.len);
58685529096SJeff Layton 			name[oname.len] = 0;
58785529096SJeff Layton 		}
58885529096SJeff Layton 		ceph_fname_free_buffer(dir, &oname);
58985529096SJeff Layton 	}
59085529096SJeff Layton out:
59185529096SJeff Layton 	dout("get_name %p ino %llx.%llx err %d %s%s\n",
59285529096SJeff Layton 		     child, ceph_vinop(inode), err,
59385529096SJeff Layton 		     err ? "" : "name ", err ? "" : name);
59419913b4eSYan, Zheng 	ceph_mdsc_put_request(req);
59519913b4eSYan, Zheng 	return err;
59619913b4eSYan, Zheng }
59719913b4eSYan, Zheng 
598a8e63b7dSSage Weil const struct export_operations ceph_export_ops = {
599a8e63b7dSSage Weil 	.encode_fh = ceph_encode_fh,
600a8e63b7dSSage Weil 	.fh_to_dentry = ceph_fh_to_dentry,
601a8e63b7dSSage Weil 	.fh_to_parent = ceph_fh_to_parent,
6029017c2ecSYan, Zheng 	.get_parent = ceph_get_parent,
60319913b4eSYan, Zheng 	.get_name = ceph_get_name,
604a8e63b7dSSage Weil };
605