Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * vi:set cin noet sw=4 tw=70: | |
3 | * Copyright 2006, International Business Machines Corporation and others. | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * This software has been released under the terms of the IBM Public | |
7 | * License. For details, see the LICENSE file in the top-level source | |
8 | * directory or online at http://www.openafs.org/dl/license10.html | |
9 | */ | |
10 | ||
11 | /* | |
12 | * Filesystem export operations for Linux | |
13 | */ | |
14 | #include <afsconfig.h> | |
15 | #include "afs/param.h" | |
16 | ||
17 | ||
18 | #include <linux/module.h> /* early to avoid printf->printk mapping */ | |
19 | #include <linux/fs.h> | |
20 | #ifdef HAVE_LINUX_EXPORTFS_H | |
21 | #include <linux/exportfs.h> | |
22 | #endif | |
23 | #include "afs/sysincludes.h" | |
24 | #include "afsincludes.h" | |
25 | #include "afs/afs_dynroot.h" | |
26 | ||
27 | #if !defined(AFS_NONFSTRANS) | |
28 | ||
29 | /* #define OSI_EXPORT_DEBUG */ | |
30 | ||
31 | extern struct dentry_operations afs_dentry_operations; | |
32 | #if defined(NEW_EXPORT_OPS) | |
33 | static struct dentry *afs_export_get_dentry(struct super_block *sb, | |
34 | void *inump); | |
35 | #endif | |
36 | ||
37 | struct get_name_data { | |
38 | char *name; | |
39 | struct VenusFid fid; | |
40 | int found; | |
41 | }; | |
42 | ||
43 | /* | |
44 | * Linux reserved the following filehandle types: | |
45 | * - 0 is always the filesystem root; NFS deals with this for us | |
46 | * - 1,2 are reserved by Linux for inode-number-based filehandles | |
47 | * - 0xff is reserved by linux | |
48 | * | |
49 | * We encode filehandles for AFS files using the types defined below. | |
50 | * Internally, our "object ID" is a VenusFid; if we get a filehandle | |
51 | * with a more-stable cell ID, we'll turn it into a cell number in | |
52 | * the decode_fh wrapper. | |
53 | */ | |
54 | ||
55 | #define AFSFH_VENUSFID 0xa0 /* cell, volume, vnode, uniq */ | |
56 | #define AFSFH_CELLFID 0xa1 /* cellhandle, volume, vnode, uniq */ | |
57 | #define AFSFH_NET_VENUSFID 0xa2 /* net cell, volume, vnode, uniq */ | |
58 | #define AFSFH_NET_CELLFID 0xa3 /* net cellhandle, volume, vnode, uniq */ | |
59 | #define AFSFH_DYN_RO_CELL 0xd0 /* cellhandle for RO root.cell mount */ | |
60 | #define AFSFH_DYN_RW_CELL 0xd1 /* cellhandle for RW root.cell mount */ | |
61 | #define AFSFH_DYN_RO_LINK 0xd2 /* cellhandle for RO root.cell symlink */ | |
62 | #define AFSFH_DYN_RW_LINK 0xd3 /* cellhandle for RW root.cell symlink */ | |
63 | #define AFSFH_DYN_MOUNT 0xd4 /* cellhandle, volume for mount point */ | |
64 | #define AFSFH_DYN_SYMLINK 0xd5 /* hash of dynroot symlink target */ | |
65 | ||
66 | static int afs_encode_fh(struct dentry *de, __u32 *fh, int *max_len, | |
67 | int connectable) | |
68 | { | |
69 | struct vcache *tvc; | |
70 | struct cell *tc; | |
71 | int vntype; | |
72 | ||
73 | if (!de->d_inode) /* encode a negative dentry?! */ | |
74 | return 255; | |
75 | if (*max_len < 4) /* not enough space */ | |
76 | return 255; | |
77 | ||
78 | tvc = VTOAFS(de->d_inode); | |
79 | ||
80 | #ifdef OSI_EXPORT_DEBUG | |
81 | printk("afs: encode_fh(0x%08x/%d/%d.%d)\n", | |
82 | tvc->f.fid.Cell, tvc->f.fid.Fid.Volume, | |
83 | tvc->f.fid.Fid.Vnode, tvc->f.fid.Fid.Unique); | |
84 | #endif | |
85 | if (afs_IsDynrootAnyFid(&tvc->f.fid)) { | |
86 | vntype = VNUM_TO_VNTYPE(tvc->f.fid.Fid.Vnode); | |
87 | switch (vntype) { | |
88 | case 0: | |
89 | /* encode as a normal filehandle */ | |
90 | break; | |
91 | ||
92 | case VN_TYPE_MOUNT: | |
93 | if (*max_len < 5) { | |
94 | return 255; | |
95 | } | |
96 | /* fall through */ | |
97 | ||
98 | case VN_TYPE_CELL: | |
99 | case VN_TYPE_ALIAS: | |
100 | AFS_GLOCK(); | |
101 | tc = afs_GetCellByIndex(VNUM_TO_CIDX(tvc->f.fid.Fid.Vnode), | |
102 | READ_LOCK); | |
103 | if (!tc) { | |
104 | AFS_GUNLOCK(); | |
105 | return 255; | |
106 | } | |
107 | memcpy((void *)fh, tc->cellHandle, 16); | |
108 | afs_PutCell(tc, READ_LOCK); | |
109 | AFS_GUNLOCK(); | |
110 | if (vntype == VN_TYPE_MOUNT) { | |
111 | fh[4] = htonl(tvc->f.fid.Fid.Unique); | |
112 | *max_len = 5; | |
113 | return AFSFH_DYN_MOUNT; | |
114 | } | |
115 | *max_len = 4; | |
116 | if (vntype == VN_TYPE_CELL) { | |
117 | return AFSFH_DYN_RO_CELL | VNUM_TO_RW(tvc->f.fid.Fid.Vnode); | |
118 | } else { | |
119 | return AFSFH_DYN_RO_LINK | VNUM_TO_RW(tvc->f.fid.Fid.Vnode); | |
120 | } | |
121 | ||
122 | case VN_TYPE_SYMLINK: | |
123 | /* XXX fill in filehandle for dynroot symlink */ | |
124 | /* XXX return AFSFH_DYN_SYMLINK; */ | |
125 | ||
126 | default: | |
127 | return 255; | |
128 | } | |
129 | } | |
130 | ||
131 | if (*max_len < 7) { | |
132 | /* not big enough for a migratable filehandle */ | |
133 | /* always encode in network order */ | |
134 | fh[0] = htonl(tvc->f.fid.Cell); | |
135 | fh[1] = htonl(tvc->f.fid.Fid.Volume); | |
136 | fh[2] = htonl(tvc->f.fid.Fid.Vnode); | |
137 | fh[3] = htonl(tvc->f.fid.Fid.Unique); | |
138 | *max_len = 4; | |
139 | return AFSFH_NET_VENUSFID; | |
140 | } | |
141 | ||
142 | AFS_GLOCK(); | |
143 | tc = afs_GetCell(tvc->f.fid.Cell, READ_LOCK); | |
144 | if (!tc) { | |
145 | AFS_GUNLOCK(); | |
146 | return 255; | |
147 | } | |
148 | memcpy((void *)fh, tc->cellHandle, 16); | |
149 | afs_PutCell(tc, READ_LOCK); | |
150 | AFS_GUNLOCK(); | |
151 | /* always encode in network order */ | |
152 | fh[4] = htonl(tvc->f.fid.Fid.Volume); | |
153 | fh[5] = htonl(tvc->f.fid.Fid.Vnode); | |
154 | fh[6] = htonl(tvc->f.fid.Fid.Unique); | |
155 | ||
156 | *max_len = 7; | |
157 | return AFSFH_NET_CELLFID; | |
158 | } | |
159 | ||
160 | #if defined(NEW_EXPORT_OPS) | |
161 | static struct dentry *afs_fh_to_dentry(struct super_block *sb, struct fid *fh_fid, | |
162 | int fh_len, int fh_type) | |
163 | #else | |
164 | static struct dentry *afs_decode_fh(struct super_block *sb, __u32 *fh, | |
165 | int fh_len, int fh_type, | |
166 | int (*acceptable)(void *, struct dentry *), | |
167 | void *context) | |
168 | #endif | |
169 | { | |
170 | struct VenusFid fid; | |
171 | struct cell *tc; | |
172 | struct dentry *result; | |
173 | #if defined(NEW_EXPORT_OPS) | |
174 | __u32 *fh = (__u32 *)fh_fid->raw; | |
175 | #endif | |
176 | ||
177 | ||
178 | switch (fh_type) { | |
179 | case AFSFH_VENUSFID: | |
180 | if (fh_len != 4) | |
181 | return NULL; | |
182 | fid.Cell = fh[0]; | |
183 | fid.Fid.Volume = fh[1]; | |
184 | fid.Fid.Vnode = fh[2]; | |
185 | fid.Fid.Unique = fh[3]; | |
186 | break; | |
187 | ||
188 | case AFSFH_CELLFID: | |
189 | if (fh_len != 7) | |
190 | return NULL; | |
191 | AFS_GLOCK(); | |
192 | tc = afs_GetCellByHandle((void *)fh, READ_LOCK); | |
193 | if (!tc) { | |
194 | AFS_GUNLOCK(); | |
195 | return NULL; | |
196 | } | |
197 | fid.Cell = tc->cellNum; | |
198 | fid.Fid.Volume = fh[4]; | |
199 | fid.Fid.Vnode = fh[5]; | |
200 | fid.Fid.Unique = fh[6]; | |
201 | afs_PutCell(tc, READ_LOCK); | |
202 | AFS_GUNLOCK(); | |
203 | break; | |
204 | ||
205 | case AFSFH_NET_VENUSFID: | |
206 | fid.Cell = ntohl(fh[0]); | |
207 | fid.Fid.Volume = ntohl(fh[1]); | |
208 | fid.Fid.Vnode = ntohl(fh[2]); | |
209 | fid.Fid.Unique = ntohl(fh[3]); | |
210 | break; | |
211 | ||
212 | case AFSFH_NET_CELLFID: | |
213 | if (fh_len != 7) | |
214 | return NULL; | |
215 | AFS_GLOCK(); | |
216 | tc = afs_GetCellByHandle((void *)fh, READ_LOCK); | |
217 | if (!tc) { | |
218 | AFS_GUNLOCK(); | |
219 | return NULL; | |
220 | } | |
221 | fid.Cell = tc->cellNum; | |
222 | fid.Fid.Volume = ntohl(fh[4]); | |
223 | fid.Fid.Vnode = ntohl(fh[5]); | |
224 | fid.Fid.Unique = ntohl(fh[6]); | |
225 | afs_PutCell(tc, READ_LOCK); | |
226 | AFS_GUNLOCK(); | |
227 | break; | |
228 | ||
229 | case AFSFH_DYN_RO_CELL: | |
230 | case AFSFH_DYN_RW_CELL: | |
231 | if (fh_len != 4) | |
232 | return NULL; | |
233 | AFS_GLOCK(); | |
234 | tc = afs_GetCellByHandle((void *)fh, READ_LOCK); | |
235 | if (!tc) { | |
236 | AFS_GUNLOCK(); | |
237 | return NULL; | |
238 | } | |
239 | afs_GetDynrootFid(&fid); | |
240 | fid.Fid.Vnode = VNUM_FROM_CIDX_RW(tc->cellIndex, fh_type & 1); | |
241 | fid.Fid.Unique = 1; | |
242 | afs_PutCell(tc, READ_LOCK); | |
243 | AFS_GUNLOCK(); | |
244 | break; | |
245 | ||
246 | case AFSFH_DYN_RO_LINK: | |
247 | case AFSFH_DYN_RW_LINK: | |
248 | if (fh_len != 4) | |
249 | return NULL; | |
250 | AFS_GLOCK(); | |
251 | tc = afs_GetCellByHandle((void *)fh, READ_LOCK); | |
252 | if (!tc) { | |
253 | AFS_GUNLOCK(); | |
254 | return NULL; | |
255 | } | |
256 | afs_GetDynrootFid(&fid); | |
257 | fid.Fid.Vnode = VNUM_FROM_CAIDX_RW(tc->cellIndex, fh_type & 1); | |
258 | fid.Fid.Unique = 1; | |
259 | afs_PutCell(tc, READ_LOCK); | |
260 | AFS_GUNLOCK(); | |
261 | break; | |
262 | ||
263 | case AFSFH_DYN_MOUNT: | |
264 | if (fh_len != 5) | |
265 | return NULL; | |
266 | AFS_GLOCK(); | |
267 | tc = afs_GetCellByHandle((void *)fh, READ_LOCK); | |
268 | if (!tc) { | |
269 | AFS_GUNLOCK(); | |
270 | return NULL; | |
271 | } | |
272 | afs_GetDynrootFid(&fid); | |
273 | fid.Fid.Vnode = VNUM_FROM_TYPEID(VN_TYPE_MOUNT, | |
274 | tc->cellIndex << 2); | |
275 | fid.Fid.Unique = ntohl(fh[4]); | |
276 | afs_PutCell(tc, READ_LOCK); | |
277 | AFS_GUNLOCK(); | |
278 | break; | |
279 | ||
280 | case AFSFH_DYN_SYMLINK: | |
281 | /* XXX parse dynroot symlink filehandle */ | |
282 | /* break; */ | |
283 | ||
284 | default: | |
285 | return NULL; | |
286 | } | |
287 | ||
288 | #if defined(NEW_EXPORT_OPS) | |
289 | result = afs_export_get_dentry(sb, &fid); | |
290 | #else | |
291 | result = sb->s_export_op->find_exported_dentry(sb, &fid, 0, | |
292 | acceptable, context); | |
293 | ||
294 | #endif | |
295 | ||
296 | #ifdef OSI_EXPORT_DEBUG | |
297 | if (!result) { | |
298 | printk("afs: decode_fh(0x%08x/%d/%d.%d): no dentry\n", | |
299 | fid.Cell, fid.Fid.Volume, | |
300 | fid.Fid.Vnode, fid.Fid.Unique); | |
301 | } else if (IS_ERR(result)) { | |
302 | printk("afs: decode_fh(0x%08x/%d/%d.%d): error %ld\n", | |
303 | fid.Cell, fid.Fid.Volume, | |
304 | fid.Fid.Vnode, fid.Fid.Unique, PTR_ERR(result)); | |
305 | } | |
306 | #endif | |
307 | return result; | |
308 | } | |
309 | ||
310 | static int update_dir_parent(struct vrequest *areq, struct vcache *adp) | |
311 | { | |
312 | struct VenusFid tfid; | |
313 | struct dcache *tdc; | |
314 | afs_size_t dirOffset, dirLen; | |
315 | int code; | |
316 | ||
317 | redo: | |
318 | if (!(adp->f.states & CStatd)) { | |
319 | if ((code = afs_VerifyVCache2(adp, areq))) { | |
320 | #ifdef OSI_EXPORT_DEBUG | |
321 | printk("afs: update_dir_parent(0x%08x/%d/%d.%d): VerifyVCache2: %d\n", | |
322 | adp->f.fid.Cell, adp->f.fid.Fid.Volume, | |
323 | adp->f.fid.Fid.Vnode, adp->f.fid.Fid.Unique, code); | |
324 | #endif | |
325 | return code; | |
326 | } | |
327 | } | |
328 | ||
329 | tdc = afs_GetDCache(adp, (afs_size_t) 0, areq, &dirOffset, &dirLen, 1); | |
330 | if (!tdc) { | |
331 | #ifdef OSI_EXPORT_DEBUG | |
332 | printk("afs: update_dir_parent(0x%08x/%d/%d.%d): no dcache\n", | |
333 | adp->f.fid.Cell, adp->f.fid.Fid.Volume, | |
334 | adp->f.fid.Fid.Vnode, adp->f.fid.Fid.Unique); | |
335 | #endif | |
336 | return EIO; | |
337 | } | |
338 | ||
339 | /* now we will just call dir package with appropriate inode. | |
340 | * Dirs are always fetched in their entirety for now */ | |
341 | ObtainSharedLock(&adp->lock, 801); | |
342 | ObtainReadLock(&tdc->lock); | |
343 | ||
344 | /* | |
345 | * Make sure that the data in the cache is current. There are two | |
346 | * cases we need to worry about: | |
347 | * 1. The cache data is being fetched by another process. | |
348 | * 2. The cache data is no longer valid | |
349 | */ | |
350 | while ((adp->f.states & CStatd) | |
351 | && (tdc->dflags & DFFetching) | |
352 | && hsame(adp->f.m.DataVersion, tdc->f.versionNo)) { | |
353 | ReleaseReadLock(&tdc->lock); | |
354 | ReleaseSharedLock(&adp->lock); | |
355 | afs_osi_Sleep(&tdc->validPos); | |
356 | ObtainSharedLock(&adp->lock, 802); | |
357 | ObtainReadLock(&tdc->lock); | |
358 | } | |
359 | if (!(adp->f.states & CStatd) | |
360 | || !hsame(adp->f.m.DataVersion, tdc->f.versionNo)) { | |
361 | ReleaseReadLock(&tdc->lock); | |
362 | ReleaseSharedLock(&adp->lock); | |
363 | afs_PutDCache(tdc); | |
364 | #ifdef OSI_EXPORT_DEBUG | |
365 | printk("afs: update_dir_parent(0x%08x/%d/%d.%d): dir changed; retrying\n", | |
366 | adp->f.fid.Cell, adp->f.fid.Fid.Volume, | |
367 | adp->f.fid.Fid.Vnode, adp->f.fid.Fid.Unique); | |
368 | #endif | |
369 | goto redo; | |
370 | } | |
371 | ||
372 | /* lookup the name in the appropriate dir, and return a cache entry | |
373 | * on the resulting fid */ | |
374 | code = afs_dir_Lookup(tdc, "..", &tfid.Fid); | |
375 | ||
376 | ReleaseReadLock(&tdc->lock); | |
377 | afs_PutDCache(tdc); | |
378 | ||
379 | if (!code) { | |
380 | UpgradeSToWLock(&adp->lock, 803); | |
381 | adp->f.parent.vnode = tfid.Fid.Vnode; | |
382 | adp->f.parent.unique = tfid.Fid.Unique; | |
383 | } | |
384 | #ifdef OSI_EXPORT_DEBUG | |
385 | if (code) { | |
386 | printk("afs: update_dir_parent(0x%08x/%d/%d.%d): afs_dir_Lookup: %d\n", | |
387 | adp->f.fid.Cell, adp->f.fid.Fid.Volume, | |
388 | adp->f.fid.Fid.Vnode, adp->f.fid.Fid.Unique, code); | |
389 | } else { | |
390 | printk("afs: update_dir_parent(0x%08x/%d/%d.%d) => %d.%d\n", | |
391 | adp->f.fid.Cell, adp->f.fid.Fid.Volume, | |
392 | adp->f.fid.Fid.Vnode, adp->f.fid.Fid.Unique, | |
393 | adp->parent.vnode, adp->parent.unique); | |
394 | } | |
395 | #endif | |
396 | ReleaseSharedLock(&adp->lock); | |
397 | return code; | |
398 | } | |
399 | ||
400 | ||
401 | static int UnEvalFakeStat(struct vrequest *areq, struct vcache **vcpp) | |
402 | { | |
403 | struct VenusFid tfid; | |
404 | struct volume *tvp; | |
405 | struct vcache *tvc; | |
406 | int code; | |
407 | ||
408 | if (!afs_fakestat_enable) | |
409 | return 0; | |
410 | ||
411 | if (*vcpp == afs_globalVp || vType(*vcpp) != VDIR || (*vcpp)->mvstat != AFS_MVSTAT_ROOT) | |
412 | return 0; | |
413 | ||
414 | /* Figure out what FID to look for */ | |
415 | tvp = afs_GetVolume(&(*vcpp)->f.fid, 0, READ_LOCK); | |
416 | if (!tvp) { | |
417 | #ifdef OSI_EXPORT_DEBUG | |
418 | printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): no volume\n", | |
419 | (*vcpp)->f.fid.Cell, (*vcpp)->f.fid.Fid.Volume, | |
420 | (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique); | |
421 | #endif | |
422 | return ENOENT; | |
423 | } | |
424 | tfid = tvp->mtpoint; | |
425 | afs_PutVolume(tvp, READ_LOCK); | |
426 | ||
427 | tvc = afs_GetVCache(&tfid, areq, NULL, NULL); | |
428 | if (!tvc) { | |
429 | #ifdef OSI_EXPORT_DEBUG | |
430 | printk("afs: UnEvalFakeStat(0x%08x/%d/%d.%d): GetVCache(0x%08x/%d/%d.%d) failed\n", | |
431 | (*vcpp)->f.fid.Cell, (*vcpp)->f.fid.Fid.Volume, | |
432 | (*vcpp)->f.fid.Fid.Vnode, (*vcpp)->f.fid.Fid.Unique, | |
433 | tfid.Cell, tfid.Fid.Volume, | |
434 | tfid.Fid.Vnode, tfid.Fid.Unique); | |
435 | #endif | |
436 | return ENOENT; | |
437 | } | |
438 | ||
439 | if (afs_fakestat_enable == 2) { | |
440 | ObtainWriteLock(&tvc->lock, 806); | |
441 | code = afs_HandleLink(tvc, areq); | |
442 | if (code) { | |
443 | ReleaseWriteLock(&tvc->lock); | |
444 | afs_PutVCache(tvc); | |
445 | return code; | |
446 | } | |
447 | if (!strchr(tvc->linkData, ':')) { | |
448 | ReleaseWriteLock(&tvc->lock); | |
449 | afs_PutVCache(tvc); | |
450 | return 0; | |
451 | } | |
452 | ReleaseWriteLock(&tvc->lock); | |
453 | } | |
454 | ||
455 | afs_PutVCache(*vcpp); | |
456 | *vcpp = tvc; | |
457 | return 0; | |
458 | } | |
459 | ||
460 | ||
461 | /* | |
462 | * Given a FID, obtain or construct a dentry, or return an error. | |
463 | * This should be called with the BKL and AFS_GLOCK held. | |
464 | */ | |
465 | static struct dentry *get_dentry_from_fid(cred_t *credp, struct VenusFid *afid) | |
466 | { | |
467 | struct vrequest *treq = NULL; | |
468 | struct vcache *vcp; | |
469 | struct vattr *vattr = NULL; | |
470 | struct inode *ip; | |
471 | struct dentry *dp; | |
472 | afs_int32 code; | |
473 | ||
474 | code = afs_CreateAttr(&vattr); | |
475 | if (code) { | |
476 | return ERR_PTR(-afs_CheckCode(code, NULL, 104)); | |
477 | } | |
478 | ||
479 | code = afs_CreateReq(&treq, credp); | |
480 | if (code) { | |
481 | #ifdef OSI_EXPORT_DEBUG | |
482 | printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): afs_CreateReq: %d\n", | |
483 | afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, | |
484 | code); | |
485 | #endif | |
486 | afs_DestroyAttr(vattr); | |
487 | return ERR_PTR(-afs_CheckCode(code, NULL, 101)); | |
488 | } | |
489 | vcp = afs_GetVCache(afid, treq, NULL, NULL); | |
490 | if (vcp == NULL) { | |
491 | #ifdef OSI_EXPORT_DEBUG | |
492 | printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): no vcache\n", | |
493 | afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); | |
494 | #endif | |
495 | afs_DestroyReq(treq); | |
496 | afs_DestroyAttr(vattr); | |
497 | return NULL; | |
498 | } | |
499 | ||
500 | /* | |
501 | * Now, it might be that we just caused a directory vnode to | |
502 | * spring into existence, in which case its parent FID is unset. | |
503 | * We need to do something about that, but only because we care | |
504 | * in our own get_parent(), below -- the common code never looks | |
505 | * at parentVnode on directories, except for VIOCGETVCXSTATUS. | |
506 | * So, if this fails, we don't really care very much. | |
507 | */ | |
508 | if (vType(vcp) == VDIR && vcp->mvstat != AFS_MVSTAT_ROOT && !vcp->f.parent.vnode) | |
509 | update_dir_parent(treq, vcp); | |
510 | ||
511 | /* | |
512 | * If this is a volume root directory and fakestat is enabled, | |
513 | * we might need to replace the directory by a mount point. | |
514 | */ | |
515 | code = UnEvalFakeStat(treq, &vcp); | |
516 | if (code) { | |
517 | #ifdef OSI_EXPORT_DEBUG | |
518 | printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): UnEvalFakeStat: %d\n", | |
519 | afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique, | |
520 | code); | |
521 | #endif | |
522 | afs_PutVCache(vcp); | |
523 | code = afs_CheckCode(code, treq, 103); | |
524 | afs_DestroyReq(treq); | |
525 | afs_DestroyAttr(vattr); | |
526 | return ERR_PTR(-code); | |
527 | } | |
528 | ||
529 | ip = AFSTOV(vcp); | |
530 | afs_getattr(vcp, vattr, credp); | |
531 | afs_fill_inode(ip, vattr); | |
532 | ||
533 | /* d_alloc_anon might block, so we shouldn't hold the glock */ | |
534 | AFS_GUNLOCK(); | |
535 | dp = d_alloc_anon(ip); | |
536 | AFS_GLOCK(); | |
537 | ||
538 | if (!dp) { | |
539 | iput(ip); | |
540 | #ifdef OSI_EXPORT_DEBUG | |
541 | printk("afs: get_dentry_from_fid(0x%08x/%d/%d.%d): out of memory\n", | |
542 | afid->Cell, afid->Fid.Volume, afid->Fid.Vnode, afid->Fid.Unique); | |
543 | #endif | |
544 | afs_DestroyReq(treq); | |
545 | afs_DestroyAttr(vattr); | |
546 | return ERR_PTR(-ENOMEM); | |
547 | } | |
548 | ||
549 | dp->d_op = &afs_dentry_operations; | |
550 | afs_DestroyReq(treq); | |
551 | afs_DestroyAttr(vattr); | |
552 | return dp; | |
553 | } | |
554 | ||
555 | static struct dentry *afs_export_get_dentry(struct super_block *sb, | |
556 | void *inump) | |
557 | { | |
558 | struct dentry *dp; | |
559 | cred_t *credp; | |
560 | ||
561 | credp = crref(); | |
562 | AFS_GLOCK(); | |
563 | ||
564 | dp = get_dentry_from_fid(credp, inump); | |
565 | ||
566 | AFS_GUNLOCK(); | |
567 | crfree(credp); | |
568 | ||
569 | return dp; | |
570 | } | |
571 | ||
572 | ||
573 | static int get_name_hook(void *hdata, char *name, | |
574 | afs_int32 vnode, afs_int32 unique) | |
575 | { | |
576 | struct get_name_data *data = (struct get_name_data *)hdata; | |
577 | int len; | |
578 | ||
579 | if (vnode == data->fid.Fid.Vnode && unique == data->fid.Fid.Unique) { | |
580 | len = strlen(name); | |
581 | if (len > NAME_MAX) len = NAME_MAX; | |
582 | memcpy(data->name, name, len); | |
583 | data->name[len] = '\0'; | |
584 | data->found = 1; | |
585 | } | |
586 | return 0; | |
587 | } | |
588 | ||
589 | static int afs_export_get_name(struct dentry *parent, char *name, | |
590 | struct dentry *child) | |
591 | { | |
592 | struct afs_fakestat_state fakestate; | |
593 | struct get_name_data data; | |
594 | struct vrequest *treq = NULL; | |
595 | struct volume *tvp; | |
596 | struct vcache *vcp; | |
597 | struct dcache *tdc; | |
598 | cred_t *credp; | |
599 | afs_size_t dirOffset, dirLen; | |
600 | afs_int32 code = 0; | |
601 | ||
602 | if (!parent->d_inode) { | |
603 | #ifdef OSI_EXPORT_DEBUG | |
604 | /* can't lookup name in a negative dentry */ | |
605 | printk("afs: get_name(%s, %s): no parent inode\n", | |
606 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
607 | child->d_name.name ? (char *)child->d_name.name : "?"); | |
608 | #endif | |
609 | return -EIO; | |
610 | } | |
611 | if (!child->d_inode) { | |
612 | #ifdef OSI_EXPORT_DEBUG | |
613 | /* can't find the FID of negative dentry */ | |
614 | printk("afs: get_name(%s, %s): no child inode\n", | |
615 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
616 | child->d_name.name ? (char *)child->d_name.name : "?"); | |
617 | #endif | |
618 | return -ENOENT; | |
619 | } | |
620 | ||
621 | afs_InitFakeStat(&fakestate); | |
622 | ||
623 | credp = crref(); | |
624 | AFS_GLOCK(); | |
625 | ||
626 | vcp = VTOAFS(child->d_inode); | |
627 | ||
628 | /* special case dynamic mount directory */ | |
629 | if (afs_IsDynrootMount(vcp)) { | |
630 | #ifdef OSI_EXPORT_DEBUG | |
631 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): this is the dynmount dir\n", | |
632 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
633 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
634 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
635 | #endif | |
636 | data.fid = vcp->f.fid; | |
637 | if (VTOAFS(parent->d_inode) == afs_globalVp) | |
638 | strcpy(name, AFS_DYNROOT_MOUNTNAME); | |
639 | else | |
640 | code = -ENOENT; | |
641 | goto done; | |
642 | } | |
643 | ||
644 | /* Figure out what FID to look for */ | |
645 | if (vcp->mvstat == AFS_MVSTAT_ROOT) { /* volume root */ | |
646 | tvp = afs_GetVolume(&vcp->f.fid, 0, READ_LOCK); | |
647 | if (!tvp) { | |
648 | #ifdef OSI_EXPORT_DEBUG | |
649 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no volume for root\n", | |
650 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
651 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
652 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
653 | #endif | |
654 | code = ENOENT; | |
655 | goto done; | |
656 | } | |
657 | data.fid = tvp->mtpoint; | |
658 | afs_PutVolume(tvp, READ_LOCK); | |
659 | } else { | |
660 | data.fid = vcp->f.fid; | |
661 | } | |
662 | ||
663 | vcp = VTOAFS(parent->d_inode); | |
664 | #ifdef OSI_EXPORT_DEBUG | |
665 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): parent is 0x%08x/%d/%d.%d\n", | |
666 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
667 | data.fid.Cell, data.fid.Fid.Volume, | |
668 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
669 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
670 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
671 | #endif | |
672 | code = afs_CreateReq(&treq, credp); | |
673 | if (code) { | |
674 | #ifdef OSI_EXPORT_DEBUG | |
675 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): afs_CreateReq: %d\n", | |
676 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
677 | data.fid.Cell, data.fid.Fid.Volume, | |
678 | data.fid.Fid.Vnode, data.fid.Fid.Unique, code); | |
679 | #endif | |
680 | goto done; | |
681 | } | |
682 | ||
683 | /* a dynamic mount point in the dynamic mount directory */ | |
684 | if (afs_IsDynrootMount(vcp) && afs_IsDynrootAnyFid(&data.fid) | |
685 | && VNUM_TO_VNTYPE(data.fid.Fid.Vnode) == VN_TYPE_MOUNT) { | |
686 | #ifdef OSI_EXPORT_DEBUG | |
687 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dynamic mount point\n", | |
688 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
689 | data.fid.Cell, data.fid.Fid.Volume, | |
690 | data.fid.Fid.Vnode, data.fid.Fid.Unique); | |
691 | #endif | |
692 | vcp = afs_GetVCache(&data.fid, treq, NULL, NULL); | |
693 | if (vcp) { | |
694 | ObtainReadLock(&vcp->lock); | |
695 | if (strlen(vcp->linkData + 1) <= NAME_MAX) | |
696 | strcpy(name, vcp->linkData + 1); | |
697 | else | |
698 | code = ENOENT; | |
699 | ReleaseReadLock(&vcp->lock); | |
700 | afs_PutVCache(vcp); | |
701 | } else { | |
702 | #ifdef OSI_EXPORT_DEBUG | |
703 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): no vcache\n", | |
704 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
705 | data.fid.Cell, data.fid.Fid.Volume, | |
706 | data.fid.Fid.Vnode, data.fid.Fid.Unique); | |
707 | #endif | |
708 | code = ENOENT; | |
709 | } | |
710 | goto done; | |
711 | } | |
712 | ||
713 | code = afs_EvalFakeStat(&vcp, &fakestate, treq); | |
714 | if (code) | |
715 | goto done; | |
716 | ||
717 | if (vcp->f.fid.Cell != data.fid.Cell || | |
718 | vcp->f.fid.Fid.Volume != data.fid.Fid.Volume) { | |
719 | /* parent is not the expected cell and volume; thus it | |
720 | * cannot possibly contain the fid we are looking for */ | |
721 | #ifdef OSI_EXPORT_DEBUG | |
722 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): wrong parent 0x%08x/%d\n", | |
723 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
724 | data.fid.Cell, data.fid.Fid.Volume, | |
725 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
726 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume); | |
727 | #endif | |
728 | code = ENOENT; | |
729 | goto done; | |
730 | } | |
731 | ||
732 | ||
733 | redo: | |
734 | if (!(vcp->f.states & CStatd)) { | |
735 | if ((code = afs_VerifyVCache2(vcp, treq))) { | |
736 | #ifdef OSI_EXPORT_DEBUG | |
737 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): VerifyVCache2(0x%08x/%d/%d.%d): %d\n", | |
738 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
739 | data.fid.Cell, data.fid.Fid.Volume, | |
740 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
741 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
742 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); | |
743 | #endif | |
744 | goto done; | |
745 | } | |
746 | } | |
747 | ||
748 | tdc = afs_GetDCache(vcp, (afs_size_t) 0, treq, &dirOffset, &dirLen, 1); | |
749 | if (!tdc) { | |
750 | #ifdef OSI_EXPORT_DEBUG | |
751 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): GetDCache(0x%08x/%d/%d.%d): %d\n", | |
752 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
753 | data.fid.Cell, data.fid.Fid.Volume, | |
754 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
755 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
756 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); | |
757 | #endif | |
758 | code = EIO; | |
759 | goto done; | |
760 | } | |
761 | ||
762 | ObtainReadLock(&vcp->lock); | |
763 | ObtainReadLock(&tdc->lock); | |
764 | ||
765 | /* | |
766 | * Make sure that the data in the cache is current. There are two | |
767 | * cases we need to worry about: | |
768 | * 1. The cache data is being fetched by another process. | |
769 | * 2. The cache data is no longer valid | |
770 | */ | |
771 | while ((vcp->f.states & CStatd) | |
772 | && (tdc->dflags & DFFetching) | |
773 | && hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) { | |
774 | ReleaseReadLock(&tdc->lock); | |
775 | ReleaseReadLock(&vcp->lock); | |
776 | afs_osi_Sleep(&tdc->validPos); | |
777 | ObtainReadLock(&vcp->lock); | |
778 | ObtainReadLock(&tdc->lock); | |
779 | } | |
780 | if (!(vcp->f.states & CStatd) | |
781 | || !hsame(vcp->f.m.DataVersion, tdc->f.versionNo)) { | |
782 | ReleaseReadLock(&tdc->lock); | |
783 | ReleaseReadLock(&vcp->lock); | |
784 | afs_PutDCache(tdc); | |
785 | #ifdef OSI_EXPORT_DEBUG | |
786 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): dir (0x%08x/%d/%d.%d) changed; retrying\n", | |
787 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
788 | data.fid.Cell, data.fid.Fid.Volume, | |
789 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
790 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
791 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
792 | #endif | |
793 | goto redo; | |
794 | } | |
795 | ||
796 | data.name = name; | |
797 | data.found = 0; | |
798 | code = afs_dir_EnumerateDir(tdc, get_name_hook, &data); | |
799 | if (!code && !data.found) { | |
800 | #ifdef OSI_EXPORT_DEBUG | |
801 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): not found\n", | |
802 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
803 | data.fid.Cell, data.fid.Fid.Volume, | |
804 | data.fid.Fid.Vnode, data.fid.Fid.Unique); | |
805 | #endif | |
806 | code = ENOENT; | |
807 | } else if (code) { | |
808 | #ifdef OSI_EXPORT_DEBUG | |
809 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d): Enumeratedir(0x%08x/%d/%d.%d): %d\n", | |
810 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
811 | data.fid.Cell, data.fid.Fid.Volume, | |
812 | data.fid.Fid.Vnode, data.fid.Fid.Unique, | |
813 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
814 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); | |
815 | #endif | |
816 | } | |
817 | ||
818 | ReleaseReadLock(&tdc->lock); | |
819 | ReleaseReadLock(&vcp->lock); | |
820 | afs_PutDCache(tdc); | |
821 | ||
822 | done: | |
823 | if (!code) { | |
824 | printk("afs: get_name(%s, 0x%08x/%d/%d.%d) => %s\n", | |
825 | parent->d_name.name ? (char *)parent->d_name.name : "?", | |
826 | data.fid.Cell, data.fid.Fid.Volume, | |
827 | data.fid.Fid.Vnode, data.fid.Fid.Unique, name); | |
828 | } | |
829 | afs_PutFakeStat(&fakestate); | |
830 | code = afs_CheckCode(code, treq, 102); | |
831 | afs_DestroyReq(treq); | |
832 | AFS_GUNLOCK(); | |
833 | crfree(credp); | |
834 | return -code; | |
835 | } | |
836 | ||
837 | ||
838 | static struct dentry *afs_export_get_parent(struct dentry *child) | |
839 | { | |
840 | struct VenusFid tfid; | |
841 | struct vrequest *treq = NULL; | |
842 | struct cell *tcell; | |
843 | struct vcache *vcp; | |
844 | struct dentry *dp = NULL; | |
845 | cred_t *credp; | |
846 | afs_uint32 cellidx; | |
847 | int code; | |
848 | ||
849 | if (!child->d_inode) { | |
850 | /* can't find the parent of a negative dentry */ | |
851 | #ifdef OSI_EXPORT_DEBUG | |
852 | printk("afs: get_parent(%s): no inode\n", | |
853 | child->d_name.name ? (char *)child->d_name.name : "?"); | |
854 | #endif | |
855 | return ERR_PTR(-EIO); | |
856 | } | |
857 | ||
858 | credp = crref(); | |
859 | AFS_GLOCK(); | |
860 | ||
861 | vcp = VTOAFS(child->d_inode); | |
862 | ||
863 | if (afs_IsDynrootMount(vcp)) { | |
864 | /* the dynmount directory; parent is always the AFS root */ | |
865 | tfid = afs_globalVp->f.fid; | |
866 | ||
867 | } else if (afs_IsDynrootAny(vcp) && | |
868 | VNUM_TO_VNTYPE(vcp->f.fid.Fid.Vnode) == VN_TYPE_MOUNT) { | |
869 | /* a mount point in the dynmount directory */ | |
870 | afs_GetDynrootMountFid(&tfid); | |
871 | ||
872 | } else if (vcp->mvstat == AFS_MVSTAT_ROOT) { | |
873 | /* volume root */ | |
874 | ObtainReadLock(&vcp->lock); | |
875 | if (vcp->mvid.parent && vcp->mvid.parent->Fid.Volume) { | |
876 | tfid = *vcp->mvid.parent; | |
877 | ReleaseReadLock(&vcp->lock); | |
878 | } else { | |
879 | ReleaseReadLock(&vcp->lock); | |
880 | tcell = afs_GetCell(vcp->f.fid.Cell, READ_LOCK); | |
881 | if (!tcell) { | |
882 | #ifdef OSI_EXPORT_DEBUG | |
883 | printk("afs: get_parent(0x%08x/%d/%d.%d): no cell\n", | |
884 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
885 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
886 | #endif | |
887 | dp = ERR_PTR(-ENOENT); | |
888 | goto done; | |
889 | } | |
890 | ||
891 | cellidx = tcell->cellIndex; | |
892 | afs_PutCell(tcell, READ_LOCK); | |
893 | ||
894 | afs_GetDynrootMountFid(&tfid); | |
895 | tfid.Fid.Vnode = VNUM_FROM_TYPEID(VN_TYPE_MOUNT, cellidx << 2); | |
896 | tfid.Fid.Unique = vcp->f.fid.Fid.Volume; | |
897 | } | |
898 | ||
899 | } else { | |
900 | /* any other vnode */ | |
901 | if (vType(vcp) == VDIR && !vcp->f.parent.vnode && vcp->mvstat != AFS_MVSTAT_MTPT) { | |
902 | code = afs_CreateReq(&treq, credp); | |
903 | if (code) { | |
904 | #ifdef OSI_EXPORT_DEBUG | |
905 | printk("afs: get_parent(0x%08x/%d/%d.%d): afs_CreateReq: %d\n", | |
906 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
907 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); | |
908 | #endif | |
909 | dp = ERR_PTR(-ENOENT); | |
910 | goto done; | |
911 | } else { | |
912 | code = update_dir_parent(treq, vcp); | |
913 | if (code) { | |
914 | #ifdef OSI_EXPORT_DEBUG | |
915 | printk("afs: get_parent(0x%08x/%d/%d.%d): update_dir_parent: %d\n", | |
916 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
917 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, code); | |
918 | #endif | |
919 | dp = ERR_PTR(-ENOENT); | |
920 | goto done; | |
921 | } | |
922 | } | |
923 | } | |
924 | ||
925 | tfid.Cell = vcp->f.fid.Cell; | |
926 | tfid.Fid.Volume = vcp->f.fid.Fid.Volume; | |
927 | tfid.Fid.Vnode = vcp->f.parent.vnode; | |
928 | tfid.Fid.Unique = vcp->f.parent.unique; | |
929 | } | |
930 | ||
931 | #ifdef OSI_EXPORT_DEBUG | |
932 | printk("afs: get_parent(0x%08x/%d/%d.%d): => 0x%08x/%d/%d.%d\n", | |
933 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
934 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique, | |
935 | tfid.Cell, tfid.Fid.Volume, tfid.Fid.Vnode, tfid.Fid.Unique); | |
936 | #endif | |
937 | ||
938 | dp = get_dentry_from_fid(credp, &tfid); | |
939 | if (!dp) { | |
940 | #ifdef OSI_EXPORT_DEBUG | |
941 | printk("afs: get_parent(0x%08x/%d/%d.%d): no dentry\n", | |
942 | vcp->f.fid.Cell, vcp->f.fid.Fid.Volume, | |
943 | vcp->f.fid.Fid.Vnode, vcp->f.fid.Fid.Unique); | |
944 | #endif | |
945 | dp = ERR_PTR(-ENOENT); | |
946 | } | |
947 | ||
948 | done: | |
949 | afs_DestroyReq(treq); | |
950 | AFS_GUNLOCK(); | |
951 | crfree(credp); | |
952 | ||
953 | return dp; | |
954 | } | |
955 | ||
956 | ||
957 | struct export_operations afs_export_ops = { | |
958 | .encode_fh = afs_encode_fh, | |
959 | #if defined(NEW_EXPORT_OPS) | |
960 | .fh_to_dentry = afs_fh_to_dentry, | |
961 | #else | |
962 | .decode_fh = afs_decode_fh, | |
963 | .get_dentry = afs_export_get_dentry, | |
964 | #endif | |
965 | .get_name = afs_export_get_name, | |
966 | .get_parent = afs_export_get_parent, | |
967 | }; | |
968 | ||
969 | #endif |