Loading...
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};
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 * NFS export support
12 *
13 * NFS re-export of a ceph mount is, at present, only semireliable.
14 * The basic issue is that the Ceph architectures doesn't lend itself
15 * well to generating filehandles that will remain valid forever.
16 *
17 * So, we do our best. If you're lucky, your inode will be in the
18 * client's cache. If it's not, and you have a connectable fh, then
19 * the MDS server may be able to find it for you. Otherwise, you get
20 * ESTALE.
21 *
22 * There are ways to this more reliable, but in the non-connectable fh
23 * case, we won't every work perfectly, and in the connectable case,
24 * some changes are needed on the MDS side to work better.
25 */
26
27/*
28 * Basic fh
29 */
30struct ceph_nfs_fh {
31 u64 ino;
32} __attribute__ ((packed));
33
34/*
35 * Larger 'connectable' fh that includes parent ino and name hash.
36 * Use this whenever possible, as it works more reliably.
37 */
38struct ceph_nfs_confh {
39 u64 ino, parent_ino;
40 u32 parent_name_hash;
41} __attribute__ ((packed));
42
43static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len,
44 int connectable)
45{
46 int type;
47 struct ceph_nfs_fh *fh = (void *)rawfh;
48 struct ceph_nfs_confh *cfh = (void *)rawfh;
49 struct dentry *parent;
50 struct inode *inode = dentry->d_inode;
51 int connected_handle_length = sizeof(*cfh)/4;
52 int handle_length = sizeof(*fh)/4;
53
54 /* don't re-export snaps */
55 if (ceph_snap(inode) != CEPH_NOSNAP)
56 return -EINVAL;
57
58 spin_lock(&dentry->d_lock);
59 parent = dget(dentry->d_parent);
60 spin_unlock(&dentry->d_lock);
61
62 if (*max_len >= connected_handle_length) {
63 dout("encode_fh %p connectable\n", dentry);
64 cfh->ino = ceph_ino(dentry->d_inode);
65 cfh->parent_ino = ceph_ino(parent->d_inode);
66 cfh->parent_name_hash = ceph_dentry_hash(parent->d_inode,
67 dentry);
68 *max_len = connected_handle_length;
69 type = 2;
70 } else if (*max_len >= handle_length) {
71 if (connectable) {
72 *max_len = connected_handle_length;
73 type = 255;
74 } else {
75 dout("encode_fh %p\n", dentry);
76 fh->ino = ceph_ino(dentry->d_inode);
77 *max_len = handle_length;
78 type = 1;
79 }
80 } else {
81 *max_len = handle_length;
82 type = 255;
83 }
84 dput(parent);
85 return type;
86}
87
88/*
89 * convert regular fh to dentry
90 *
91 * FIXME: we should try harder by querying the mds for the ino.
92 */
93static struct dentry *__fh_to_dentry(struct super_block *sb,
94 struct ceph_nfs_fh *fh)
95{
96 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
97 struct inode *inode;
98 struct dentry *dentry;
99 struct ceph_vino vino;
100 int err;
101
102 dout("__fh_to_dentry %llx\n", fh->ino);
103 vino.ino = fh->ino;
104 vino.snap = CEPH_NOSNAP;
105 inode = ceph_find_inode(sb, vino);
106 if (!inode) {
107 struct ceph_mds_request *req;
108
109 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
110 USE_ANY_MDS);
111 if (IS_ERR(req))
112 return ERR_CAST(req);
113
114 req->r_ino1 = vino;
115 req->r_num_caps = 1;
116 err = ceph_mdsc_do_request(mdsc, NULL, req);
117 inode = req->r_target_inode;
118 if (inode)
119 ihold(inode);
120 ceph_mdsc_put_request(req);
121 if (!inode)
122 return ERR_PTR(-ESTALE);
123 }
124
125 dentry = d_obtain_alias(inode);
126 if (IS_ERR(dentry)) {
127 pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n",
128 fh->ino, inode);
129 iput(inode);
130 return dentry;
131 }
132 err = ceph_init_dentry(dentry);
133 if (err < 0) {
134 iput(inode);
135 return ERR_PTR(err);
136 }
137 dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry);
138 return dentry;
139}
140
141/*
142 * convert connectable fh to dentry
143 */
144static struct dentry *__cfh_to_dentry(struct super_block *sb,
145 struct ceph_nfs_confh *cfh)
146{
147 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
148 struct inode *inode;
149 struct dentry *dentry;
150 struct ceph_vino vino;
151 int err;
152
153 dout("__cfh_to_dentry %llx (%llx/%x)\n",
154 cfh->ino, cfh->parent_ino, cfh->parent_name_hash);
155
156 vino.ino = cfh->ino;
157 vino.snap = CEPH_NOSNAP;
158 inode = ceph_find_inode(sb, vino);
159 if (!inode) {
160 struct ceph_mds_request *req;
161
162 req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH,
163 USE_ANY_MDS);
164 if (IS_ERR(req))
165 return ERR_CAST(req);
166
167 req->r_ino1 = vino;
168 req->r_ino2.ino = cfh->parent_ino;
169 req->r_ino2.snap = CEPH_NOSNAP;
170 req->r_path2 = kmalloc(16, GFP_NOFS);
171 snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash);
172 req->r_num_caps = 1;
173 err = ceph_mdsc_do_request(mdsc, NULL, req);
174 inode = req->r_target_inode;
175 if (inode)
176 ihold(inode);
177 ceph_mdsc_put_request(req);
178 if (!inode)
179 return ERR_PTR(err ? err : -ESTALE);
180 }
181
182 dentry = d_obtain_alias(inode);
183 if (IS_ERR(dentry)) {
184 pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n",
185 cfh->ino, inode);
186 iput(inode);
187 return dentry;
188 }
189 err = ceph_init_dentry(dentry);
190 if (err < 0) {
191 iput(inode);
192 return ERR_PTR(err);
193 }
194 dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry);
195 return dentry;
196}
197
198static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid,
199 int fh_len, int fh_type)
200{
201 if (fh_type == 1)
202 return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw);
203 else
204 return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw);
205}
206
207/*
208 * get parent, if possible.
209 *
210 * FIXME: we could do better by querying the mds to discover the
211 * parent.
212 */
213static struct dentry *ceph_fh_to_parent(struct super_block *sb,
214 struct fid *fid,
215 int fh_len, int fh_type)
216{
217 struct ceph_nfs_confh *cfh = (void *)fid->raw;
218 struct ceph_vino vino;
219 struct inode *inode;
220 struct dentry *dentry;
221 int err;
222
223 if (fh_type == 1)
224 return ERR_PTR(-ESTALE);
225
226 pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino,
227 cfh->parent_name_hash);
228
229 vino.ino = cfh->ino;
230 vino.snap = CEPH_NOSNAP;
231 inode = ceph_find_inode(sb, vino);
232 if (!inode)
233 return ERR_PTR(-ESTALE);
234
235 dentry = d_obtain_alias(inode);
236 if (IS_ERR(dentry)) {
237 pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n",
238 cfh->ino, inode);
239 iput(inode);
240 return dentry;
241 }
242 err = ceph_init_dentry(dentry);
243 if (err < 0) {
244 iput(inode);
245 return ERR_PTR(err);
246 }
247 dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry);
248 return dentry;
249}
250
251const struct export_operations ceph_export_ops = {
252 .encode_fh = ceph_encode_fh,
253 .fh_to_dentry = ceph_fh_to_dentry,
254 .fh_to_parent = ceph_fh_to_parent,
255};