diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/afs/dir.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/afs/dir.c')
-rw-r--r-- | fs/afs/dir.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c new file mode 100644 index 000000000000..6682d6d7f294 --- /dev/null +++ b/fs/afs/dir.c | |||
@@ -0,0 +1,666 @@ | |||
1 | /* dir.c: AFS filesystem directory handling | ||
2 | * | ||
3 | * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/fs.h> | ||
18 | #include <linux/pagemap.h> | ||
19 | #include <linux/smp_lock.h> | ||
20 | #include "vnode.h" | ||
21 | #include "volume.h" | ||
22 | #include <rxrpc/call.h> | ||
23 | #include "super.h" | ||
24 | #include "internal.h" | ||
25 | |||
26 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | ||
27 | struct nameidata *nd); | ||
28 | static int afs_dir_open(struct inode *inode, struct file *file); | ||
29 | static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); | ||
30 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); | ||
31 | static int afs_d_delete(struct dentry *dentry); | ||
32 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | ||
33 | loff_t fpos, ino_t ino, unsigned dtype); | ||
34 | |||
35 | struct file_operations afs_dir_file_operations = { | ||
36 | .open = afs_dir_open, | ||
37 | .readdir = afs_dir_readdir, | ||
38 | }; | ||
39 | |||
40 | struct inode_operations afs_dir_inode_operations = { | ||
41 | .lookup = afs_dir_lookup, | ||
42 | .getattr = afs_inode_getattr, | ||
43 | #if 0 /* TODO */ | ||
44 | .create = afs_dir_create, | ||
45 | .link = afs_dir_link, | ||
46 | .unlink = afs_dir_unlink, | ||
47 | .symlink = afs_dir_symlink, | ||
48 | .mkdir = afs_dir_mkdir, | ||
49 | .rmdir = afs_dir_rmdir, | ||
50 | .mknod = afs_dir_mknod, | ||
51 | .rename = afs_dir_rename, | ||
52 | #endif | ||
53 | }; | ||
54 | |||
55 | static struct dentry_operations afs_fs_dentry_operations = { | ||
56 | .d_revalidate = afs_d_revalidate, | ||
57 | .d_delete = afs_d_delete, | ||
58 | }; | ||
59 | |||
60 | #define AFS_DIR_HASHTBL_SIZE 128 | ||
61 | #define AFS_DIR_DIRENT_SIZE 32 | ||
62 | #define AFS_DIRENT_PER_BLOCK 64 | ||
63 | |||
64 | union afs_dirent { | ||
65 | struct { | ||
66 | uint8_t valid; | ||
67 | uint8_t unused[1]; | ||
68 | __be16 hash_next; | ||
69 | __be32 vnode; | ||
70 | __be32 unique; | ||
71 | uint8_t name[16]; | ||
72 | uint8_t overflow[4]; /* if any char of the name (inc | ||
73 | * NUL) reaches here, consume | ||
74 | * the next dirent too */ | ||
75 | } u; | ||
76 | uint8_t extended_name[32]; | ||
77 | }; | ||
78 | |||
79 | /* AFS directory page header (one at the beginning of every 2048-byte chunk) */ | ||
80 | struct afs_dir_pagehdr { | ||
81 | __be16 npages; | ||
82 | __be16 magic; | ||
83 | #define AFS_DIR_MAGIC htons(1234) | ||
84 | uint8_t nentries; | ||
85 | uint8_t bitmap[8]; | ||
86 | uint8_t pad[19]; | ||
87 | }; | ||
88 | |||
89 | /* directory block layout */ | ||
90 | union afs_dir_block { | ||
91 | |||
92 | struct afs_dir_pagehdr pagehdr; | ||
93 | |||
94 | struct { | ||
95 | struct afs_dir_pagehdr pagehdr; | ||
96 | uint8_t alloc_ctrs[128]; | ||
97 | /* dir hash table */ | ||
98 | uint16_t hashtable[AFS_DIR_HASHTBL_SIZE]; | ||
99 | } hdr; | ||
100 | |||
101 | union afs_dirent dirents[AFS_DIRENT_PER_BLOCK]; | ||
102 | }; | ||
103 | |||
104 | /* layout on a linux VM page */ | ||
105 | struct afs_dir_page { | ||
106 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; | ||
107 | }; | ||
108 | |||
109 | struct afs_dir_lookup_cookie { | ||
110 | struct afs_fid fid; | ||
111 | const char *name; | ||
112 | size_t nlen; | ||
113 | int found; | ||
114 | }; | ||
115 | |||
116 | /*****************************************************************************/ | ||
117 | /* | ||
118 | * check that a directory page is valid | ||
119 | */ | ||
120 | static inline void afs_dir_check_page(struct inode *dir, struct page *page) | ||
121 | { | ||
122 | struct afs_dir_page *dbuf; | ||
123 | loff_t latter; | ||
124 | int tmp, qty; | ||
125 | |||
126 | #if 0 | ||
127 | /* check the page count */ | ||
128 | qty = desc.size / sizeof(dbuf->blocks[0]); | ||
129 | if (qty == 0) | ||
130 | goto error; | ||
131 | |||
132 | if (page->index==0 && qty!=ntohs(dbuf->blocks[0].pagehdr.npages)) { | ||
133 | printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", | ||
134 | __FUNCTION__,dir->i_ino,qty,ntohs(dbuf->blocks[0].pagehdr.npages)); | ||
135 | goto error; | ||
136 | } | ||
137 | #endif | ||
138 | |||
139 | /* determine how many magic numbers there should be in this page */ | ||
140 | latter = dir->i_size - (page->index << PAGE_CACHE_SHIFT); | ||
141 | if (latter >= PAGE_SIZE) | ||
142 | qty = PAGE_SIZE; | ||
143 | else | ||
144 | qty = latter; | ||
145 | qty /= sizeof(union afs_dir_block); | ||
146 | |||
147 | /* check them */ | ||
148 | dbuf = page_address(page); | ||
149 | for (tmp = 0; tmp < qty; tmp++) { | ||
150 | if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { | ||
151 | printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", | ||
152 | __FUNCTION__, dir->i_ino, tmp, qty, | ||
153 | ntohs(dbuf->blocks[tmp].pagehdr.magic)); | ||
154 | goto error; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | SetPageChecked(page); | ||
159 | return; | ||
160 | |||
161 | error: | ||
162 | SetPageChecked(page); | ||
163 | SetPageError(page); | ||
164 | |||
165 | } /* end afs_dir_check_page() */ | ||
166 | |||
167 | /*****************************************************************************/ | ||
168 | /* | ||
169 | * discard a page cached in the pagecache | ||
170 | */ | ||
171 | static inline void afs_dir_put_page(struct page *page) | ||
172 | { | ||
173 | kunmap(page); | ||
174 | page_cache_release(page); | ||
175 | |||
176 | } /* end afs_dir_put_page() */ | ||
177 | |||
178 | /*****************************************************************************/ | ||
179 | /* | ||
180 | * get a page into the pagecache | ||
181 | */ | ||
182 | static struct page *afs_dir_get_page(struct inode *dir, unsigned long index) | ||
183 | { | ||
184 | struct page *page; | ||
185 | |||
186 | _enter("{%lu},%lu", dir->i_ino, index); | ||
187 | |||
188 | page = read_cache_page(dir->i_mapping,index, | ||
189 | (filler_t *) dir->i_mapping->a_ops->readpage, | ||
190 | NULL); | ||
191 | if (!IS_ERR(page)) { | ||
192 | wait_on_page_locked(page); | ||
193 | kmap(page); | ||
194 | if (!PageUptodate(page)) | ||
195 | goto fail; | ||
196 | if (!PageChecked(page)) | ||
197 | afs_dir_check_page(dir, page); | ||
198 | if (PageError(page)) | ||
199 | goto fail; | ||
200 | } | ||
201 | return page; | ||
202 | |||
203 | fail: | ||
204 | afs_dir_put_page(page); | ||
205 | return ERR_PTR(-EIO); | ||
206 | } /* end afs_dir_get_page() */ | ||
207 | |||
208 | /*****************************************************************************/ | ||
209 | /* | ||
210 | * open an AFS directory file | ||
211 | */ | ||
212 | static int afs_dir_open(struct inode *inode, struct file *file) | ||
213 | { | ||
214 | _enter("{%lu}", inode->i_ino); | ||
215 | |||
216 | BUG_ON(sizeof(union afs_dir_block) != 2048); | ||
217 | BUG_ON(sizeof(union afs_dirent) != 32); | ||
218 | |||
219 | if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) | ||
220 | return -ENOENT; | ||
221 | |||
222 | _leave(" = 0"); | ||
223 | return 0; | ||
224 | |||
225 | } /* end afs_dir_open() */ | ||
226 | |||
227 | /*****************************************************************************/ | ||
228 | /* | ||
229 | * deal with one block in an AFS directory | ||
230 | */ | ||
231 | static int afs_dir_iterate_block(unsigned *fpos, | ||
232 | union afs_dir_block *block, | ||
233 | unsigned blkoff, | ||
234 | void *cookie, | ||
235 | filldir_t filldir) | ||
236 | { | ||
237 | union afs_dirent *dire; | ||
238 | unsigned offset, next, curr; | ||
239 | size_t nlen; | ||
240 | int tmp, ret; | ||
241 | |||
242 | _enter("%u,%x,%p,,",*fpos,blkoff,block); | ||
243 | |||
244 | curr = (*fpos - blkoff) / sizeof(union afs_dirent); | ||
245 | |||
246 | /* walk through the block, an entry at a time */ | ||
247 | for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; | ||
248 | offset < AFS_DIRENT_PER_BLOCK; | ||
249 | offset = next | ||
250 | ) { | ||
251 | next = offset + 1; | ||
252 | |||
253 | /* skip entries marked unused in the bitmap */ | ||
254 | if (!(block->pagehdr.bitmap[offset / 8] & | ||
255 | (1 << (offset % 8)))) { | ||
256 | _debug("ENT[%Zu.%u]: unused\n", | ||
257 | blkoff / sizeof(union afs_dir_block), offset); | ||
258 | if (offset >= curr) | ||
259 | *fpos = blkoff + | ||
260 | next * sizeof(union afs_dirent); | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | /* got a valid entry */ | ||
265 | dire = &block->dirents[offset]; | ||
266 | nlen = strnlen(dire->u.name, | ||
267 | sizeof(*block) - | ||
268 | offset * sizeof(union afs_dirent)); | ||
269 | |||
270 | _debug("ENT[%Zu.%u]: %s %Zu \"%s\"\n", | ||
271 | blkoff / sizeof(union afs_dir_block), offset, | ||
272 | (offset < curr ? "skip" : "fill"), | ||
273 | nlen, dire->u.name); | ||
274 | |||
275 | /* work out where the next possible entry is */ | ||
276 | for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { | ||
277 | if (next >= AFS_DIRENT_PER_BLOCK) { | ||
278 | _debug("ENT[%Zu.%u]:" | ||
279 | " %u travelled beyond end dir block" | ||
280 | " (len %u/%Zu)\n", | ||
281 | blkoff / sizeof(union afs_dir_block), | ||
282 | offset, next, tmp, nlen); | ||
283 | return -EIO; | ||
284 | } | ||
285 | if (!(block->pagehdr.bitmap[next / 8] & | ||
286 | (1 << (next % 8)))) { | ||
287 | _debug("ENT[%Zu.%u]:" | ||
288 | " %u unmarked extension (len %u/%Zu)\n", | ||
289 | blkoff / sizeof(union afs_dir_block), | ||
290 | offset, next, tmp, nlen); | ||
291 | return -EIO; | ||
292 | } | ||
293 | |||
294 | _debug("ENT[%Zu.%u]: ext %u/%Zu\n", | ||
295 | blkoff / sizeof(union afs_dir_block), | ||
296 | next, tmp, nlen); | ||
297 | next++; | ||
298 | } | ||
299 | |||
300 | /* skip if starts before the current position */ | ||
301 | if (offset < curr) | ||
302 | continue; | ||
303 | |||
304 | /* found the next entry */ | ||
305 | ret = filldir(cookie, | ||
306 | dire->u.name, | ||
307 | nlen, | ||
308 | blkoff + offset * sizeof(union afs_dirent), | ||
309 | ntohl(dire->u.vnode), | ||
310 | filldir == afs_dir_lookup_filldir ? | ||
311 | ntohl(dire->u.unique) : DT_UNKNOWN); | ||
312 | if (ret < 0) { | ||
313 | _leave(" = 0 [full]"); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | *fpos = blkoff + next * sizeof(union afs_dirent); | ||
318 | } | ||
319 | |||
320 | _leave(" = 1 [more]"); | ||
321 | return 1; | ||
322 | } /* end afs_dir_iterate_block() */ | ||
323 | |||
324 | /*****************************************************************************/ | ||
325 | /* | ||
326 | * read an AFS directory | ||
327 | */ | ||
328 | static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, | ||
329 | filldir_t filldir) | ||
330 | { | ||
331 | union afs_dir_block *dblock; | ||
332 | struct afs_dir_page *dbuf; | ||
333 | struct page *page; | ||
334 | unsigned blkoff, limit; | ||
335 | int ret; | ||
336 | |||
337 | _enter("{%lu},%u,,", dir->i_ino, *fpos); | ||
338 | |||
339 | if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { | ||
340 | _leave(" = -ESTALE"); | ||
341 | return -ESTALE; | ||
342 | } | ||
343 | |||
344 | /* round the file position up to the next entry boundary */ | ||
345 | *fpos += sizeof(union afs_dirent) - 1; | ||
346 | *fpos &= ~(sizeof(union afs_dirent) - 1); | ||
347 | |||
348 | /* walk through the blocks in sequence */ | ||
349 | ret = 0; | ||
350 | while (*fpos < dir->i_size) { | ||
351 | blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1); | ||
352 | |||
353 | /* fetch the appropriate page from the directory */ | ||
354 | page = afs_dir_get_page(dir, blkoff / PAGE_SIZE); | ||
355 | if (IS_ERR(page)) { | ||
356 | ret = PTR_ERR(page); | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | limit = blkoff & ~(PAGE_SIZE - 1); | ||
361 | |||
362 | dbuf = page_address(page); | ||
363 | |||
364 | /* deal with the individual blocks stashed on this page */ | ||
365 | do { | ||
366 | dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / | ||
367 | sizeof(union afs_dir_block)]; | ||
368 | ret = afs_dir_iterate_block(fpos, dblock, blkoff, | ||
369 | cookie, filldir); | ||
370 | if (ret != 1) { | ||
371 | afs_dir_put_page(page); | ||
372 | goto out; | ||
373 | } | ||
374 | |||
375 | blkoff += sizeof(union afs_dir_block); | ||
376 | |||
377 | } while (*fpos < dir->i_size && blkoff < limit); | ||
378 | |||
379 | afs_dir_put_page(page); | ||
380 | ret = 0; | ||
381 | } | ||
382 | |||
383 | out: | ||
384 | _leave(" = %d", ret); | ||
385 | return ret; | ||
386 | } /* end afs_dir_iterate() */ | ||
387 | |||
388 | /*****************************************************************************/ | ||
389 | /* | ||
390 | * read an AFS directory | ||
391 | */ | ||
392 | static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | ||
393 | { | ||
394 | unsigned fpos; | ||
395 | int ret; | ||
396 | |||
397 | _enter("{%Ld,{%lu}}", file->f_pos, file->f_dentry->d_inode->i_ino); | ||
398 | |||
399 | fpos = file->f_pos; | ||
400 | ret = afs_dir_iterate(file->f_dentry->d_inode, &fpos, cookie, filldir); | ||
401 | file->f_pos = fpos; | ||
402 | |||
403 | _leave(" = %d", ret); | ||
404 | return ret; | ||
405 | } /* end afs_dir_readdir() */ | ||
406 | |||
407 | /*****************************************************************************/ | ||
408 | /* | ||
409 | * search the directory for a name | ||
410 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID | ||
411 | * uniquifier through dtype | ||
412 | */ | ||
413 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | ||
414 | loff_t fpos, ino_t ino, unsigned dtype) | ||
415 | { | ||
416 | struct afs_dir_lookup_cookie *cookie = _cookie; | ||
417 | |||
418 | _enter("{%s,%Zu},%s,%u,,%lu,%u", | ||
419 | cookie->name, cookie->nlen, name, nlen, ino, dtype); | ||
420 | |||
421 | if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { | ||
422 | _leave(" = 0 [no]"); | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | cookie->fid.vnode = ino; | ||
427 | cookie->fid.unique = dtype; | ||
428 | cookie->found = 1; | ||
429 | |||
430 | _leave(" = -1 [found]"); | ||
431 | return -1; | ||
432 | } /* end afs_dir_lookup_filldir() */ | ||
433 | |||
434 | /*****************************************************************************/ | ||
435 | /* | ||
436 | * look up an entry in a directory | ||
437 | */ | ||
438 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | ||
439 | struct nameidata *nd) | ||
440 | { | ||
441 | struct afs_dir_lookup_cookie cookie; | ||
442 | struct afs_super_info *as; | ||
443 | struct afs_vnode *vnode; | ||
444 | struct inode *inode; | ||
445 | unsigned fpos; | ||
446 | int ret; | ||
447 | |||
448 | _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); | ||
449 | |||
450 | /* insanity checks first */ | ||
451 | BUG_ON(sizeof(union afs_dir_block) != 2048); | ||
452 | BUG_ON(sizeof(union afs_dirent) != 32); | ||
453 | |||
454 | if (dentry->d_name.len > 255) { | ||
455 | _leave(" = -ENAMETOOLONG"); | ||
456 | return ERR_PTR(-ENAMETOOLONG); | ||
457 | } | ||
458 | |||
459 | vnode = AFS_FS_I(dir); | ||
460 | if (vnode->flags & AFS_VNODE_DELETED) { | ||
461 | _leave(" = -ESTALE"); | ||
462 | return ERR_PTR(-ESTALE); | ||
463 | } | ||
464 | |||
465 | as = dir->i_sb->s_fs_info; | ||
466 | |||
467 | /* search the directory */ | ||
468 | cookie.name = dentry->d_name.name; | ||
469 | cookie.nlen = dentry->d_name.len; | ||
470 | cookie.fid.vid = as->volume->vid; | ||
471 | cookie.found = 0; | ||
472 | |||
473 | fpos = 0; | ||
474 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir); | ||
475 | if (ret < 0) { | ||
476 | _leave(" = %d", ret); | ||
477 | return ERR_PTR(ret); | ||
478 | } | ||
479 | |||
480 | ret = -ENOENT; | ||
481 | if (!cookie.found) { | ||
482 | _leave(" = %d", ret); | ||
483 | return ERR_PTR(ret); | ||
484 | } | ||
485 | |||
486 | /* instantiate the dentry */ | ||
487 | ret = afs_iget(dir->i_sb, &cookie.fid, &inode); | ||
488 | if (ret < 0) { | ||
489 | _leave(" = %d", ret); | ||
490 | return ERR_PTR(ret); | ||
491 | } | ||
492 | |||
493 | dentry->d_op = &afs_fs_dentry_operations; | ||
494 | dentry->d_fsdata = (void *) (unsigned long) vnode->status.version; | ||
495 | |||
496 | d_add(dentry, inode); | ||
497 | _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", | ||
498 | cookie.fid.vnode, | ||
499 | cookie.fid.unique, | ||
500 | dentry->d_inode->i_ino, | ||
501 | dentry->d_inode->i_version); | ||
502 | |||
503 | return NULL; | ||
504 | } /* end afs_dir_lookup() */ | ||
505 | |||
506 | /*****************************************************************************/ | ||
507 | /* | ||
508 | * check that a dentry lookup hit has found a valid entry | ||
509 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an | ||
510 | * inode | ||
511 | * (derived from nfs_lookup_revalidate) | ||
512 | */ | ||
513 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
514 | { | ||
515 | struct afs_dir_lookup_cookie cookie; | ||
516 | struct dentry *parent; | ||
517 | struct inode *inode, *dir; | ||
518 | unsigned fpos; | ||
519 | int ret; | ||
520 | |||
521 | _enter("{sb=%p n=%s},", dentry->d_sb, dentry->d_name.name); | ||
522 | |||
523 | /* lock down the parent dentry so we can peer at it */ | ||
524 | parent = dget_parent(dentry->d_parent); | ||
525 | |||
526 | dir = parent->d_inode; | ||
527 | inode = dentry->d_inode; | ||
528 | |||
529 | /* handle a negative dentry */ | ||
530 | if (!inode) | ||
531 | goto out_bad; | ||
532 | |||
533 | /* handle a bad inode */ | ||
534 | if (is_bad_inode(inode)) { | ||
535 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | ||
536 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
537 | goto out_bad; | ||
538 | } | ||
539 | |||
540 | /* force a full look up if the parent directory changed since last the | ||
541 | * server was consulted | ||
542 | * - otherwise this inode must still exist, even if the inode details | ||
543 | * themselves have changed | ||
544 | */ | ||
545 | if (AFS_FS_I(dir)->flags & AFS_VNODE_CHANGED) | ||
546 | afs_vnode_fetch_status(AFS_FS_I(dir)); | ||
547 | |||
548 | if (AFS_FS_I(dir)->flags & AFS_VNODE_DELETED) { | ||
549 | _debug("%s: parent dir deleted", dentry->d_name.name); | ||
550 | goto out_bad; | ||
551 | } | ||
552 | |||
553 | if (AFS_FS_I(inode)->flags & AFS_VNODE_DELETED) { | ||
554 | _debug("%s: file already deleted", dentry->d_name.name); | ||
555 | goto out_bad; | ||
556 | } | ||
557 | |||
558 | if ((unsigned long) dentry->d_fsdata != | ||
559 | (unsigned long) AFS_FS_I(dir)->status.version) { | ||
560 | _debug("%s: parent changed %lu -> %u", | ||
561 | dentry->d_name.name, | ||
562 | (unsigned long) dentry->d_fsdata, | ||
563 | (unsigned) AFS_FS_I(dir)->status.version); | ||
564 | |||
565 | /* search the directory for this vnode */ | ||
566 | cookie.name = dentry->d_name.name; | ||
567 | cookie.nlen = dentry->d_name.len; | ||
568 | cookie.fid.vid = AFS_FS_I(inode)->volume->vid; | ||
569 | cookie.found = 0; | ||
570 | |||
571 | fpos = 0; | ||
572 | ret = afs_dir_iterate(dir, &fpos, &cookie, | ||
573 | afs_dir_lookup_filldir); | ||
574 | if (ret < 0) { | ||
575 | _debug("failed to iterate dir %s: %d", | ||
576 | parent->d_name.name, ret); | ||
577 | goto out_bad; | ||
578 | } | ||
579 | |||
580 | if (!cookie.found) { | ||
581 | _debug("%s: dirent not found", dentry->d_name.name); | ||
582 | goto not_found; | ||
583 | } | ||
584 | |||
585 | /* if the vnode ID has changed, then the dirent points to a | ||
586 | * different file */ | ||
587 | if (cookie.fid.vnode != AFS_FS_I(inode)->fid.vnode) { | ||
588 | _debug("%s: dirent changed", dentry->d_name.name); | ||
589 | goto not_found; | ||
590 | } | ||
591 | |||
592 | /* if the vnode ID uniqifier has changed, then the file has | ||
593 | * been deleted */ | ||
594 | if (cookie.fid.unique != AFS_FS_I(inode)->fid.unique) { | ||
595 | _debug("%s: file deleted (uq %u -> %u I:%lu)", | ||
596 | dentry->d_name.name, | ||
597 | cookie.fid.unique, | ||
598 | AFS_FS_I(inode)->fid.unique, | ||
599 | inode->i_version); | ||
600 | spin_lock(&AFS_FS_I(inode)->lock); | ||
601 | AFS_FS_I(inode)->flags |= AFS_VNODE_DELETED; | ||
602 | spin_unlock(&AFS_FS_I(inode)->lock); | ||
603 | invalidate_remote_inode(inode); | ||
604 | goto out_bad; | ||
605 | } | ||
606 | |||
607 | dentry->d_fsdata = | ||
608 | (void *) (unsigned long) AFS_FS_I(dir)->status.version; | ||
609 | } | ||
610 | |||
611 | out_valid: | ||
612 | dput(parent); | ||
613 | _leave(" = 1 [valid]"); | ||
614 | return 1; | ||
615 | |||
616 | /* the dirent, if it exists, now points to a different vnode */ | ||
617 | not_found: | ||
618 | spin_lock(&dentry->d_lock); | ||
619 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | ||
620 | spin_unlock(&dentry->d_lock); | ||
621 | |||
622 | out_bad: | ||
623 | if (inode) { | ||
624 | /* don't unhash if we have submounts */ | ||
625 | if (have_submounts(dentry)) | ||
626 | goto out_valid; | ||
627 | } | ||
628 | |||
629 | shrink_dcache_parent(dentry); | ||
630 | |||
631 | _debug("dropping dentry %s/%s", | ||
632 | dentry->d_parent->d_name.name, dentry->d_name.name); | ||
633 | d_drop(dentry); | ||
634 | |||
635 | dput(parent); | ||
636 | |||
637 | _leave(" = 0 [bad]"); | ||
638 | return 0; | ||
639 | } /* end afs_d_revalidate() */ | ||
640 | |||
641 | /*****************************************************************************/ | ||
642 | /* | ||
643 | * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't | ||
644 | * sleep) | ||
645 | * - called from dput() when d_count is going to 0. | ||
646 | * - return 1 to request dentry be unhashed, 0 otherwise | ||
647 | */ | ||
648 | static int afs_d_delete(struct dentry *dentry) | ||
649 | { | ||
650 | _enter("%s", dentry->d_name.name); | ||
651 | |||
652 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | ||
653 | goto zap; | ||
654 | |||
655 | if (dentry->d_inode) { | ||
656 | if (AFS_FS_I(dentry->d_inode)->flags & AFS_VNODE_DELETED) | ||
657 | goto zap; | ||
658 | } | ||
659 | |||
660 | _leave(" = 0 [keep]"); | ||
661 | return 0; | ||
662 | |||
663 | zap: | ||
664 | _leave(" = 1 [zap]"); | ||
665 | return 1; | ||
666 | } /* end afs_d_delete() */ | ||