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 {
126*985b9ee8SXiubo 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 {
208*985b9ee8SXiubo 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 {
320*985b9ee8SXiubo 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);
442*985b9ee8SXiubo 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
547*985b9ee8SXiubo 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