xref: /linux/fs/ceph/export.c (revision f3a8b6645dc2e60d11f20c1c23afd964ff4e55ae)
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 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 		int mask;
75 
76 		req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
77 					       USE_ANY_MDS);
78 		if (IS_ERR(req))
79 			return ERR_CAST(req);
80 
81 		mask = CEPH_STAT_CAP_INODE;
82 		if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
83 			mask |= CEPH_CAP_XATTR_SHARED;
84 		req->r_args.getattr.mask = cpu_to_le32(mask);
85 
86 		req->r_ino1 = vino;
87 		req->r_num_caps = 1;
88 		err = ceph_mdsc_do_request(mdsc, NULL, req);
89 		inode = req->r_target_inode;
90 		if (inode)
91 			ihold(inode);
92 		ceph_mdsc_put_request(req);
93 		if (!inode)
94 			return ERR_PTR(-ESTALE);
95 	}
96 
97 	dentry = d_obtain_alias(inode);
98 	if (IS_ERR(dentry))
99 		return dentry;
100 	err = ceph_init_dentry(dentry);
101 	if (err < 0) {
102 		dput(dentry);
103 		return ERR_PTR(err);
104 	}
105 	dout("__fh_to_dentry %llx %p dentry %p\n", ino, inode, dentry);
106 	return dentry;
107 }
108 
109 /*
110  * convert regular fh to dentry
111  */
112 static struct dentry *ceph_fh_to_dentry(struct super_block *sb,
113 					struct fid *fid,
114 					int fh_len, int fh_type)
115 {
116 	struct ceph_nfs_fh *fh = (void *)fid->raw;
117 
118 	if (fh_type != FILEID_INO32_GEN  &&
119 	    fh_type != FILEID_INO32_GEN_PARENT)
120 		return NULL;
121 	if (fh_len < sizeof(*fh) / 4)
122 		return NULL;
123 
124 	dout("fh_to_dentry %llx\n", fh->ino);
125 	return __fh_to_dentry(sb, fh->ino);
126 }
127 
128 static struct dentry *__get_parent(struct super_block *sb,
129 				   struct dentry *child, u64 ino)
130 {
131 	struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
132 	struct ceph_mds_request *req;
133 	struct inode *inode;
134 	struct dentry *dentry;
135 	int mask;
136 	int err;
137 
138 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPPARENT,
139 				       USE_ANY_MDS);
140 	if (IS_ERR(req))
141 		return ERR_CAST(req);
142 
143 	if (child) {
144 		req->r_inode = d_inode(child);
145 		ihold(d_inode(child));
146 	} else {
147 		req->r_ino1 = (struct ceph_vino) {
148 			.ino = ino,
149 			.snap = CEPH_NOSNAP,
150 		};
151 	}
152 
153 	mask = CEPH_STAT_CAP_INODE;
154 	if (ceph_security_xattr_wanted(d_inode(sb->s_root)))
155 		mask |= CEPH_CAP_XATTR_SHARED;
156 	req->r_args.getattr.mask = cpu_to_le32(mask);
157 
158 	req->r_num_caps = 1;
159 	err = ceph_mdsc_do_request(mdsc, NULL, req);
160 	inode = req->r_target_inode;
161 	if (inode)
162 		ihold(inode);
163 	ceph_mdsc_put_request(req);
164 	if (!inode)
165 		return ERR_PTR(-ENOENT);
166 
167 	dentry = d_obtain_alias(inode);
168 	if (IS_ERR(dentry))
169 		return dentry;
170 	err = ceph_init_dentry(dentry);
171 	if (err < 0) {
172 		dput(dentry);
173 		return ERR_PTR(err);
174 	}
175 	dout("__get_parent ino %llx parent %p ino %llx.%llx\n",
176 	     child ? ceph_ino(d_inode(child)) : ino,
177 	     dentry, ceph_vinop(inode));
178 	return dentry;
179 }
180 
181 static struct dentry *ceph_get_parent(struct dentry *child)
182 {
183 	/* don't re-export snaps */
184 	if (ceph_snap(d_inode(child)) != CEPH_NOSNAP)
185 		return ERR_PTR(-EINVAL);
186 
187 	dout("get_parent %p ino %llx.%llx\n",
188 	     child, ceph_vinop(d_inode(child)));
189 	return __get_parent(child->d_sb, child, 0);
190 }
191 
192 /*
193  * convert regular fh to parent
194  */
195 static struct dentry *ceph_fh_to_parent(struct super_block *sb,
196 					struct fid *fid,
197 					int fh_len, int fh_type)
198 {
199 	struct ceph_nfs_confh *cfh = (void *)fid->raw;
200 	struct dentry *dentry;
201 
202 	if (fh_type != FILEID_INO32_GEN_PARENT)
203 		return NULL;
204 	if (fh_len < sizeof(*cfh) / 4)
205 		return NULL;
206 
207 	dout("fh_to_parent %llx\n", cfh->parent_ino);
208 	dentry = __get_parent(sb, NULL, cfh->ino);
209 	if (unlikely(dentry == ERR_PTR(-ENOENT)))
210 		dentry = __fh_to_dentry(sb, cfh->parent_ino);
211 	return dentry;
212 }
213 
214 static int ceph_get_name(struct dentry *parent, char *name,
215 			 struct dentry *child)
216 {
217 	struct ceph_mds_client *mdsc;
218 	struct ceph_mds_request *req;
219 	int err;
220 
221 	mdsc = ceph_inode_to_client(d_inode(child))->mdsc;
222 	req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPNAME,
223 				       USE_ANY_MDS);
224 	if (IS_ERR(req))
225 		return PTR_ERR(req);
226 
227 	inode_lock(d_inode(parent));
228 
229 	req->r_inode = d_inode(child);
230 	ihold(d_inode(child));
231 	req->r_ino2 = ceph_vino(d_inode(parent));
232 	req->r_locked_dir = d_inode(parent);
233 	req->r_num_caps = 2;
234 	err = ceph_mdsc_do_request(mdsc, NULL, req);
235 
236 	inode_unlock(d_inode(parent));
237 
238 	if (!err) {
239 		struct ceph_mds_reply_info_parsed *rinfo = &req->r_reply_info;
240 		memcpy(name, rinfo->dname, rinfo->dname_len);
241 		name[rinfo->dname_len] = 0;
242 		dout("get_name %p ino %llx.%llx name %s\n",
243 		     child, ceph_vinop(d_inode(child)), name);
244 	} else {
245 		dout("get_name %p ino %llx.%llx err %d\n",
246 		     child, ceph_vinop(d_inode(child)), err);
247 	}
248 
249 	ceph_mdsc_put_request(req);
250 	return err;
251 }
252 
253 const struct export_operations ceph_export_ops = {
254 	.encode_fh = ceph_encode_fh,
255 	.fh_to_dentry = ceph_fh_to_dentry,
256 	.fh_to_parent = ceph_fh_to_parent,
257 	.get_parent = ceph_get_parent,
258 	.get_name = ceph_get_name,
259 };
260