diff options
author | Sage Weil <sage@newdream.net> | 2009-10-06 14:31:13 -0400 |
---|---|---|
committer | Sage Weil <sage@newdream.net> | 2009-10-06 14:31:13 -0400 |
commit | a8e63b7d51cce4557ee7bcd8f51be5cae8547d20 (patch) | |
tree | ffb158eada99d022a5837366f0d2ca1ce6f28fd6 /fs/ceph/export.c | |
parent | 8fc91fd85950d106883852c6d215614ec28cc92d (diff) |
ceph: nfs re-export support
Basic NFS re-export support is included. This mostly works. However,
Ceph's MDS design precludes the ability to generate a (small)
filehandle that will be valid forever, so this is of limited utility.
Signed-off-by: Sage Weil <sage@newdream.net>
Diffstat (limited to 'fs/ceph/export.c')
-rw-r--r-- | fs/ceph/export.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/fs/ceph/export.c b/fs/ceph/export.c new file mode 100644 index 000000000000..fc68e39cbad6 --- /dev/null +++ b/fs/ceph/export.c | |||
@@ -0,0 +1,223 @@ | |||
1 | #include "ceph_debug.h" | ||
2 | |||
3 | #include <linux/exportfs.h> | ||
4 | #include <asm/unaligned.h> | ||
5 | |||
6 | #include "super.h" | ||
7 | |||
8 | /* | ||
9 | * NFS export support | ||
10 | * | ||
11 | * NFS re-export of a ceph mount is, at present, only semireliable. | ||
12 | * The basic issue is that the Ceph architectures doesn't lend itself | ||
13 | * well to generating filehandles that will remain valid forever. | ||
14 | * | ||
15 | * So, we do our best. If you're lucky, your inode will be in the | ||
16 | * client's cache. If it's not, and you have a connectable fh, then | ||
17 | * the MDS server may be able to find it for you. Otherwise, you get | ||
18 | * ESTALE. | ||
19 | * | ||
20 | * There are ways to this more reliable, but in the non-connectable fh | ||
21 | * case, we won't every work perfectly, and in the connectable case, | ||
22 | * some changes are needed on the MDS side to work better. | ||
23 | */ | ||
24 | |||
25 | /* | ||
26 | * Basic fh | ||
27 | */ | ||
28 | struct ceph_nfs_fh { | ||
29 | u64 ino; | ||
30 | } __attribute__ ((packed)); | ||
31 | |||
32 | /* | ||
33 | * Larger 'connectable' fh that includes parent ino and name hash. | ||
34 | * Use this whenever possible, as it works more reliably. | ||
35 | */ | ||
36 | struct ceph_nfs_confh { | ||
37 | u64 ino, parent_ino; | ||
38 | u32 parent_name_hash; | ||
39 | } __attribute__ ((packed)); | ||
40 | |||
41 | static int ceph_encode_fh(struct dentry *dentry, u32 *rawfh, int *max_len, | ||
42 | int connectable) | ||
43 | { | ||
44 | struct ceph_nfs_fh *fh = (void *)rawfh; | ||
45 | struct ceph_nfs_confh *cfh = (void *)rawfh; | ||
46 | struct dentry *parent = dentry->d_parent; | ||
47 | struct inode *inode = dentry->d_inode; | ||
48 | int type; | ||
49 | |||
50 | /* don't re-export snaps */ | ||
51 | if (ceph_snap(inode) != CEPH_NOSNAP) | ||
52 | return -EINVAL; | ||
53 | |||
54 | if (*max_len >= sizeof(*cfh)) { | ||
55 | dout("encode_fh %p connectable\n", dentry); | ||
56 | cfh->ino = ceph_ino(dentry->d_inode); | ||
57 | cfh->parent_ino = ceph_ino(parent->d_inode); | ||
58 | cfh->parent_name_hash = parent->d_name.hash; | ||
59 | *max_len = sizeof(*cfh); | ||
60 | type = 2; | ||
61 | } else if (*max_len > sizeof(*fh)) { | ||
62 | if (connectable) | ||
63 | return -ENOSPC; | ||
64 | dout("encode_fh %p\n", dentry); | ||
65 | fh->ino = ceph_ino(dentry->d_inode); | ||
66 | *max_len = sizeof(*fh); | ||
67 | type = 1; | ||
68 | } else { | ||
69 | return -ENOSPC; | ||
70 | } | ||
71 | return type; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * convert regular fh to dentry | ||
76 | * | ||
77 | * FIXME: we should try harder by querying the mds for the ino. | ||
78 | */ | ||
79 | static struct dentry *__fh_to_dentry(struct super_block *sb, | ||
80 | struct ceph_nfs_fh *fh) | ||
81 | { | ||
82 | struct inode *inode; | ||
83 | struct dentry *dentry; | ||
84 | struct ceph_vino vino; | ||
85 | int err; | ||
86 | |||
87 | dout("__fh_to_dentry %llx\n", fh->ino); | ||
88 | vino.ino = fh->ino; | ||
89 | vino.snap = CEPH_NOSNAP; | ||
90 | inode = ceph_find_inode(sb, vino); | ||
91 | if (!inode) | ||
92 | return ERR_PTR(-ESTALE); | ||
93 | |||
94 | dentry = d_obtain_alias(inode); | ||
95 | if (!dentry) { | ||
96 | pr_err("fh_to_dentry %llx -- inode %p but ENOMEM\n", | ||
97 | fh->ino, inode); | ||
98 | iput(inode); | ||
99 | return ERR_PTR(-ENOMEM); | ||
100 | } | ||
101 | err = ceph_init_dentry(dentry); | ||
102 | |||
103 | if (err < 0) { | ||
104 | iput(inode); | ||
105 | return ERR_PTR(err); | ||
106 | } | ||
107 | dout("__fh_to_dentry %llx %p dentry %p\n", fh->ino, inode, dentry); | ||
108 | return dentry; | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * convert connectable fh to dentry | ||
113 | */ | ||
114 | static struct dentry *__cfh_to_dentry(struct super_block *sb, | ||
115 | struct ceph_nfs_confh *cfh) | ||
116 | { | ||
117 | struct ceph_mds_client *mdsc = &ceph_client(sb)->mdsc; | ||
118 | struct inode *inode; | ||
119 | struct dentry *dentry; | ||
120 | struct ceph_vino vino; | ||
121 | int err; | ||
122 | |||
123 | dout("__cfh_to_dentry %llx (%llx/%x)\n", | ||
124 | cfh->ino, cfh->parent_ino, cfh->parent_name_hash); | ||
125 | |||
126 | vino.ino = cfh->ino; | ||
127 | vino.snap = CEPH_NOSNAP; | ||
128 | inode = ceph_find_inode(sb, vino); | ||
129 | if (!inode) { | ||
130 | struct ceph_mds_request *req; | ||
131 | |||
132 | req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPHASH, | ||
133 | USE_ANY_MDS); | ||
134 | if (IS_ERR(req)) | ||
135 | return ERR_PTR(PTR_ERR(req)); | ||
136 | |||
137 | req->r_ino1 = vino; | ||
138 | req->r_ino2.ino = cfh->parent_ino; | ||
139 | req->r_ino2.snap = CEPH_NOSNAP; | ||
140 | req->r_path2 = kmalloc(16, GFP_NOFS); | ||
141 | snprintf(req->r_path2, 16, "%d", cfh->parent_name_hash); | ||
142 | req->r_num_caps = 1; | ||
143 | err = ceph_mdsc_do_request(mdsc, NULL, req); | ||
144 | ceph_mdsc_put_request(req); | ||
145 | inode = ceph_find_inode(sb, vino); | ||
146 | if (!inode) | ||
147 | return ERR_PTR(err ? err : -ESTALE); | ||
148 | } | ||
149 | |||
150 | dentry = d_obtain_alias(inode); | ||
151 | if (!dentry) { | ||
152 | pr_err("cfh_to_dentry %llx -- inode %p but ENOMEM\n", | ||
153 | cfh->ino, inode); | ||
154 | iput(inode); | ||
155 | return ERR_PTR(-ENOMEM); | ||
156 | } | ||
157 | err = ceph_init_dentry(dentry); | ||
158 | if (err < 0) { | ||
159 | iput(inode); | ||
160 | return ERR_PTR(err); | ||
161 | } | ||
162 | dout("__cfh_to_dentry %llx %p dentry %p\n", cfh->ino, inode, dentry); | ||
163 | return dentry; | ||
164 | } | ||
165 | |||
166 | static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid, | ||
167 | int fh_len, int fh_type) | ||
168 | { | ||
169 | if (fh_type == 1) | ||
170 | return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw); | ||
171 | else | ||
172 | return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw); | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * get parent, if possible. | ||
177 | * | ||
178 | * FIXME: we could do better by querying the mds to discover the | ||
179 | * parent. | ||
180 | */ | ||
181 | static struct dentry *ceph_fh_to_parent(struct super_block *sb, | ||
182 | struct fid *fid, | ||
183 | int fh_len, int fh_type) | ||
184 | { | ||
185 | struct ceph_nfs_confh *cfh = (void *)fid->raw; | ||
186 | struct ceph_vino vino; | ||
187 | struct inode *inode; | ||
188 | struct dentry *dentry; | ||
189 | int err; | ||
190 | |||
191 | if (fh_type == 1) | ||
192 | return ERR_PTR(-ESTALE); | ||
193 | |||
194 | pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino, | ||
195 | cfh->parent_name_hash); | ||
196 | |||
197 | vino.ino = cfh->ino; | ||
198 | vino.snap = CEPH_NOSNAP; | ||
199 | inode = ceph_find_inode(sb, vino); | ||
200 | if (!inode) | ||
201 | return ERR_PTR(-ESTALE); | ||
202 | |||
203 | dentry = d_obtain_alias(inode); | ||
204 | if (!dentry) { | ||
205 | pr_err("fh_to_parent %llx -- inode %p but ENOMEM\n", | ||
206 | cfh->ino, inode); | ||
207 | iput(inode); | ||
208 | return ERR_PTR(-ENOMEM); | ||
209 | } | ||
210 | err = ceph_init_dentry(dentry); | ||
211 | if (err < 0) { | ||
212 | iput(inode); | ||
213 | return ERR_PTR(err); | ||
214 | } | ||
215 | dout("fh_to_parent %llx %p dentry %p\n", cfh->ino, inode, dentry); | ||
216 | return dentry; | ||
217 | } | ||
218 | |||
219 | const struct export_operations ceph_export_ops = { | ||
220 | .encode_fh = ceph_encode_fh, | ||
221 | .fh_to_dentry = ceph_fh_to_dentry, | ||
222 | .fh_to_parent = ceph_fh_to_parent, | ||
223 | }; | ||