Commit | Line | Data |
---|---|---|
1da177e4 LT |
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> | |
1da177e4 LT |
15 | #include <linux/slab.h> |
16 | #include <linux/fs.h> | |
17 | #include <linux/pagemap.h> | |
1da177e4 LT |
18 | #include "internal.h" |
19 | ||
20 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |
21 | struct nameidata *nd); | |
22 | static int afs_dir_open(struct inode *inode, struct file *file); | |
23 | static int afs_dir_readdir(struct file *file, void *dirent, filldir_t filldir); | |
24 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); | |
25 | static int afs_d_delete(struct dentry *dentry); | |
26 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | |
afefdbb2 | 27 | loff_t fpos, u64 ino, unsigned dtype); |
1da177e4 | 28 | |
4b6f5d20 | 29 | const struct file_operations afs_dir_file_operations = { |
1da177e4 LT |
30 | .open = afs_dir_open, |
31 | .readdir = afs_dir_readdir, | |
32 | }; | |
33 | ||
754661f1 | 34 | const struct inode_operations afs_dir_inode_operations = { |
1da177e4 LT |
35 | .lookup = afs_dir_lookup, |
36 | .getattr = afs_inode_getattr, | |
37 | #if 0 /* TODO */ | |
38 | .create = afs_dir_create, | |
39 | .link = afs_dir_link, | |
40 | .unlink = afs_dir_unlink, | |
41 | .symlink = afs_dir_symlink, | |
42 | .mkdir = afs_dir_mkdir, | |
43 | .rmdir = afs_dir_rmdir, | |
44 | .mknod = afs_dir_mknod, | |
45 | .rename = afs_dir_rename, | |
46 | #endif | |
47 | }; | |
48 | ||
49 | static struct dentry_operations afs_fs_dentry_operations = { | |
50 | .d_revalidate = afs_d_revalidate, | |
51 | .d_delete = afs_d_delete, | |
52 | }; | |
53 | ||
54 | #define AFS_DIR_HASHTBL_SIZE 128 | |
55 | #define AFS_DIR_DIRENT_SIZE 32 | |
56 | #define AFS_DIRENT_PER_BLOCK 64 | |
57 | ||
58 | union afs_dirent { | |
59 | struct { | |
60 | uint8_t valid; | |
61 | uint8_t unused[1]; | |
62 | __be16 hash_next; | |
63 | __be32 vnode; | |
64 | __be32 unique; | |
65 | uint8_t name[16]; | |
66 | uint8_t overflow[4]; /* if any char of the name (inc | |
67 | * NUL) reaches here, consume | |
68 | * the next dirent too */ | |
69 | } u; | |
70 | uint8_t extended_name[32]; | |
71 | }; | |
72 | ||
73 | /* AFS directory page header (one at the beginning of every 2048-byte chunk) */ | |
74 | struct afs_dir_pagehdr { | |
75 | __be16 npages; | |
76 | __be16 magic; | |
77 | #define AFS_DIR_MAGIC htons(1234) | |
78 | uint8_t nentries; | |
79 | uint8_t bitmap[8]; | |
80 | uint8_t pad[19]; | |
81 | }; | |
82 | ||
83 | /* directory block layout */ | |
84 | union afs_dir_block { | |
85 | ||
86 | struct afs_dir_pagehdr pagehdr; | |
87 | ||
88 | struct { | |
89 | struct afs_dir_pagehdr pagehdr; | |
90 | uint8_t alloc_ctrs[128]; | |
91 | /* dir hash table */ | |
92 | uint16_t hashtable[AFS_DIR_HASHTBL_SIZE]; | |
93 | } hdr; | |
94 | ||
95 | union afs_dirent dirents[AFS_DIRENT_PER_BLOCK]; | |
96 | }; | |
97 | ||
98 | /* layout on a linux VM page */ | |
99 | struct afs_dir_page { | |
100 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; | |
101 | }; | |
102 | ||
103 | struct afs_dir_lookup_cookie { | |
104 | struct afs_fid fid; | |
105 | const char *name; | |
106 | size_t nlen; | |
107 | int found; | |
108 | }; | |
109 | ||
1da177e4 LT |
110 | /* |
111 | * check that a directory page is valid | |
112 | */ | |
113 | static inline void afs_dir_check_page(struct inode *dir, struct page *page) | |
114 | { | |
115 | struct afs_dir_page *dbuf; | |
116 | loff_t latter; | |
117 | int tmp, qty; | |
118 | ||
119 | #if 0 | |
120 | /* check the page count */ | |
121 | qty = desc.size / sizeof(dbuf->blocks[0]); | |
122 | if (qty == 0) | |
123 | goto error; | |
124 | ||
08e0e7c8 | 125 | if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) { |
1da177e4 | 126 | printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n", |
08e0e7c8 DH |
127 | __FUNCTION__, dir->i_ino, qty, |
128 | ntohs(dbuf->blocks[0].pagehdr.npages)); | |
1da177e4 LT |
129 | goto error; |
130 | } | |
131 | #endif | |
132 | ||
133 | /* determine how many magic numbers there should be in this page */ | |
54b21a79 | 134 | latter = dir->i_size - page_offset(page); |
1da177e4 LT |
135 | if (latter >= PAGE_SIZE) |
136 | qty = PAGE_SIZE; | |
137 | else | |
138 | qty = latter; | |
139 | qty /= sizeof(union afs_dir_block); | |
140 | ||
141 | /* check them */ | |
142 | dbuf = page_address(page); | |
143 | for (tmp = 0; tmp < qty; tmp++) { | |
144 | if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { | |
145 | printk("kAFS: %s(%lu): bad magic %d/%d is %04hx\n", | |
146 | __FUNCTION__, dir->i_ino, tmp, qty, | |
147 | ntohs(dbuf->blocks[tmp].pagehdr.magic)); | |
148 | goto error; | |
149 | } | |
150 | } | |
151 | ||
152 | SetPageChecked(page); | |
153 | return; | |
154 | ||
ec26815a | 155 | error: |
1da177e4 LT |
156 | SetPageChecked(page); |
157 | SetPageError(page); | |
ec26815a | 158 | } |
1da177e4 | 159 | |
1da177e4 LT |
160 | /* |
161 | * discard a page cached in the pagecache | |
162 | */ | |
163 | static inline void afs_dir_put_page(struct page *page) | |
164 | { | |
165 | kunmap(page); | |
166 | page_cache_release(page); | |
ec26815a | 167 | } |
1da177e4 | 168 | |
1da177e4 LT |
169 | /* |
170 | * get a page into the pagecache | |
171 | */ | |
172 | static struct page *afs_dir_get_page(struct inode *dir, unsigned long index) | |
173 | { | |
174 | struct page *page; | |
175 | ||
176 | _enter("{%lu},%lu", dir->i_ino, index); | |
177 | ||
090d2b18 | 178 | page = read_mapping_page(dir->i_mapping, index, NULL); |
1da177e4 LT |
179 | if (!IS_ERR(page)) { |
180 | wait_on_page_locked(page); | |
181 | kmap(page); | |
182 | if (!PageUptodate(page)) | |
183 | goto fail; | |
184 | if (!PageChecked(page)) | |
185 | afs_dir_check_page(dir, page); | |
186 | if (PageError(page)) | |
187 | goto fail; | |
188 | } | |
189 | return page; | |
190 | ||
ec26815a | 191 | fail: |
1da177e4 | 192 | afs_dir_put_page(page); |
08e0e7c8 | 193 | _leave(" = -EIO"); |
1da177e4 | 194 | return ERR_PTR(-EIO); |
ec26815a | 195 | } |
1da177e4 | 196 | |
1da177e4 LT |
197 | /* |
198 | * open an AFS directory file | |
199 | */ | |
200 | static int afs_dir_open(struct inode *inode, struct file *file) | |
201 | { | |
202 | _enter("{%lu}", inode->i_ino); | |
203 | ||
2ecd05ae AD |
204 | BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); |
205 | BUILD_BUG_ON(sizeof(union afs_dirent) != 32); | |
1da177e4 | 206 | |
08e0e7c8 | 207 | if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags)) |
1da177e4 LT |
208 | return -ENOENT; |
209 | ||
210 | _leave(" = 0"); | |
211 | return 0; | |
ec26815a | 212 | } |
1da177e4 | 213 | |
1da177e4 LT |
214 | /* |
215 | * deal with one block in an AFS directory | |
216 | */ | |
217 | static int afs_dir_iterate_block(unsigned *fpos, | |
218 | union afs_dir_block *block, | |
219 | unsigned blkoff, | |
220 | void *cookie, | |
221 | filldir_t filldir) | |
222 | { | |
223 | union afs_dirent *dire; | |
224 | unsigned offset, next, curr; | |
225 | size_t nlen; | |
226 | int tmp, ret; | |
227 | ||
228 | _enter("%u,%x,%p,,",*fpos,blkoff,block); | |
229 | ||
230 | curr = (*fpos - blkoff) / sizeof(union afs_dirent); | |
231 | ||
232 | /* walk through the block, an entry at a time */ | |
233 | for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; | |
234 | offset < AFS_DIRENT_PER_BLOCK; | |
235 | offset = next | |
236 | ) { | |
237 | next = offset + 1; | |
238 | ||
239 | /* skip entries marked unused in the bitmap */ | |
240 | if (!(block->pagehdr.bitmap[offset / 8] & | |
241 | (1 << (offset % 8)))) { | |
08e0e7c8 | 242 | _debug("ENT[%Zu.%u]: unused", |
1da177e4 LT |
243 | blkoff / sizeof(union afs_dir_block), offset); |
244 | if (offset >= curr) | |
245 | *fpos = blkoff + | |
246 | next * sizeof(union afs_dirent); | |
247 | continue; | |
248 | } | |
249 | ||
250 | /* got a valid entry */ | |
251 | dire = &block->dirents[offset]; | |
252 | nlen = strnlen(dire->u.name, | |
253 | sizeof(*block) - | |
254 | offset * sizeof(union afs_dirent)); | |
255 | ||
08e0e7c8 | 256 | _debug("ENT[%Zu.%u]: %s %Zu \"%s\"", |
1da177e4 LT |
257 | blkoff / sizeof(union afs_dir_block), offset, |
258 | (offset < curr ? "skip" : "fill"), | |
259 | nlen, dire->u.name); | |
260 | ||
261 | /* work out where the next possible entry is */ | |
262 | for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { | |
263 | if (next >= AFS_DIRENT_PER_BLOCK) { | |
264 | _debug("ENT[%Zu.%u]:" | |
265 | " %u travelled beyond end dir block" | |
08e0e7c8 | 266 | " (len %u/%Zu)", |
1da177e4 LT |
267 | blkoff / sizeof(union afs_dir_block), |
268 | offset, next, tmp, nlen); | |
269 | return -EIO; | |
270 | } | |
271 | if (!(block->pagehdr.bitmap[next / 8] & | |
272 | (1 << (next % 8)))) { | |
273 | _debug("ENT[%Zu.%u]:" | |
08e0e7c8 | 274 | " %u unmarked extension (len %u/%Zu)", |
1da177e4 LT |
275 | blkoff / sizeof(union afs_dir_block), |
276 | offset, next, tmp, nlen); | |
277 | return -EIO; | |
278 | } | |
279 | ||
08e0e7c8 | 280 | _debug("ENT[%Zu.%u]: ext %u/%Zu", |
1da177e4 LT |
281 | blkoff / sizeof(union afs_dir_block), |
282 | next, tmp, nlen); | |
283 | next++; | |
284 | } | |
285 | ||
286 | /* skip if starts before the current position */ | |
287 | if (offset < curr) | |
288 | continue; | |
289 | ||
290 | /* found the next entry */ | |
291 | ret = filldir(cookie, | |
292 | dire->u.name, | |
293 | nlen, | |
294 | blkoff + offset * sizeof(union afs_dirent), | |
295 | ntohl(dire->u.vnode), | |
296 | filldir == afs_dir_lookup_filldir ? | |
297 | ntohl(dire->u.unique) : DT_UNKNOWN); | |
298 | if (ret < 0) { | |
299 | _leave(" = 0 [full]"); | |
300 | return 0; | |
301 | } | |
302 | ||
303 | *fpos = blkoff + next * sizeof(union afs_dirent); | |
304 | } | |
305 | ||
306 | _leave(" = 1 [more]"); | |
307 | return 1; | |
ec26815a | 308 | } |
1da177e4 | 309 | |
1da177e4 | 310 | /* |
08e0e7c8 | 311 | * iterate through the data blob that lists the contents of an AFS directory |
1da177e4 LT |
312 | */ |
313 | static int afs_dir_iterate(struct inode *dir, unsigned *fpos, void *cookie, | |
314 | filldir_t filldir) | |
315 | { | |
08e0e7c8 | 316 | union afs_dir_block *dblock; |
1da177e4 LT |
317 | struct afs_dir_page *dbuf; |
318 | struct page *page; | |
319 | unsigned blkoff, limit; | |
320 | int ret; | |
321 | ||
322 | _enter("{%lu},%u,,", dir->i_ino, *fpos); | |
323 | ||
08e0e7c8 | 324 | if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { |
1da177e4 LT |
325 | _leave(" = -ESTALE"); |
326 | return -ESTALE; | |
327 | } | |
328 | ||
329 | /* round the file position up to the next entry boundary */ | |
330 | *fpos += sizeof(union afs_dirent) - 1; | |
331 | *fpos &= ~(sizeof(union afs_dirent) - 1); | |
332 | ||
333 | /* walk through the blocks in sequence */ | |
334 | ret = 0; | |
335 | while (*fpos < dir->i_size) { | |
336 | blkoff = *fpos & ~(sizeof(union afs_dir_block) - 1); | |
337 | ||
338 | /* fetch the appropriate page from the directory */ | |
339 | page = afs_dir_get_page(dir, blkoff / PAGE_SIZE); | |
340 | if (IS_ERR(page)) { | |
341 | ret = PTR_ERR(page); | |
342 | break; | |
343 | } | |
344 | ||
345 | limit = blkoff & ~(PAGE_SIZE - 1); | |
346 | ||
347 | dbuf = page_address(page); | |
348 | ||
349 | /* deal with the individual blocks stashed on this page */ | |
350 | do { | |
351 | dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / | |
352 | sizeof(union afs_dir_block)]; | |
353 | ret = afs_dir_iterate_block(fpos, dblock, blkoff, | |
354 | cookie, filldir); | |
355 | if (ret != 1) { | |
356 | afs_dir_put_page(page); | |
357 | goto out; | |
358 | } | |
359 | ||
360 | blkoff += sizeof(union afs_dir_block); | |
361 | ||
362 | } while (*fpos < dir->i_size && blkoff < limit); | |
363 | ||
364 | afs_dir_put_page(page); | |
365 | ret = 0; | |
366 | } | |
367 | ||
ec26815a | 368 | out: |
1da177e4 LT |
369 | _leave(" = %d", ret); |
370 | return ret; | |
ec26815a | 371 | } |
1da177e4 | 372 | |
1da177e4 LT |
373 | /* |
374 | * read an AFS directory | |
375 | */ | |
376 | static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | |
377 | { | |
378 | unsigned fpos; | |
379 | int ret; | |
380 | ||
08e0e7c8 DH |
381 | _enter("{%Ld,{%lu}}", |
382 | file->f_pos, file->f_path.dentry->d_inode->i_ino); | |
1da177e4 LT |
383 | |
384 | fpos = file->f_pos; | |
08e0e7c8 DH |
385 | ret = afs_dir_iterate(file->f_path.dentry->d_inode, &fpos, |
386 | cookie, filldir); | |
1da177e4 LT |
387 | file->f_pos = fpos; |
388 | ||
389 | _leave(" = %d", ret); | |
390 | return ret; | |
ec26815a | 391 | } |
1da177e4 | 392 | |
1da177e4 LT |
393 | /* |
394 | * search the directory for a name | |
395 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID | |
396 | * uniquifier through dtype | |
397 | */ | |
398 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | |
afefdbb2 | 399 | loff_t fpos, u64 ino, unsigned dtype) |
1da177e4 LT |
400 | { |
401 | struct afs_dir_lookup_cookie *cookie = _cookie; | |
402 | ||
08e0e7c8 | 403 | _enter("{%s,%Zu},%s,%u,,%llu,%u", |
1da177e4 LT |
404 | cookie->name, cookie->nlen, name, nlen, ino, dtype); |
405 | ||
08e0e7c8 DH |
406 | /* insanity checks first */ |
407 | BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); | |
408 | BUILD_BUG_ON(sizeof(union afs_dirent) != 32); | |
409 | ||
1da177e4 LT |
410 | if (cookie->nlen != nlen || memcmp(cookie->name, name, nlen) != 0) { |
411 | _leave(" = 0 [no]"); | |
412 | return 0; | |
413 | } | |
414 | ||
415 | cookie->fid.vnode = ino; | |
416 | cookie->fid.unique = dtype; | |
417 | cookie->found = 1; | |
418 | ||
419 | _leave(" = -1 [found]"); | |
420 | return -1; | |
ec26815a | 421 | } |
1da177e4 | 422 | |
1da177e4 | 423 | /* |
08e0e7c8 | 424 | * do a lookup in a directory |
1da177e4 | 425 | */ |
08e0e7c8 DH |
426 | static int afs_do_lookup(struct inode *dir, struct dentry *dentry, |
427 | struct afs_fid *fid) | |
1da177e4 LT |
428 | { |
429 | struct afs_dir_lookup_cookie cookie; | |
430 | struct afs_super_info *as; | |
1da177e4 LT |
431 | unsigned fpos; |
432 | int ret; | |
433 | ||
08e0e7c8 | 434 | _enter("{%lu},%p{%s},", dir->i_ino, dentry, dentry->d_name.name); |
1da177e4 LT |
435 | |
436 | as = dir->i_sb->s_fs_info; | |
437 | ||
438 | /* search the directory */ | |
439 | cookie.name = dentry->d_name.name; | |
440 | cookie.nlen = dentry->d_name.len; | |
441 | cookie.fid.vid = as->volume->vid; | |
442 | cookie.found = 0; | |
443 | ||
444 | fpos = 0; | |
445 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir); | |
446 | if (ret < 0) { | |
08e0e7c8 DH |
447 | _leave(" = %d [iter]", ret); |
448 | return ret; | |
1da177e4 LT |
449 | } |
450 | ||
451 | ret = -ENOENT; | |
452 | if (!cookie.found) { | |
08e0e7c8 DH |
453 | _leave(" = -ENOENT [not found]"); |
454 | return -ENOENT; | |
1da177e4 LT |
455 | } |
456 | ||
08e0e7c8 DH |
457 | *fid = cookie.fid; |
458 | _leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique); | |
459 | return 0; | |
460 | } | |
461 | ||
462 | /* | |
463 | * look up an entry in a directory | |
464 | */ | |
465 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |
466 | struct nameidata *nd) | |
467 | { | |
468 | struct afs_vnode *vnode; | |
469 | struct afs_fid fid; | |
470 | struct inode *inode; | |
471 | int ret; | |
472 | ||
473 | _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); | |
474 | ||
475 | if (dentry->d_name.len > 255) { | |
476 | _leave(" = -ENAMETOOLONG"); | |
477 | return ERR_PTR(-ENAMETOOLONG); | |
478 | } | |
479 | ||
480 | vnode = AFS_FS_I(dir); | |
481 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | |
482 | _leave(" = -ESTALE"); | |
483 | return ERR_PTR(-ESTALE); | |
484 | } | |
485 | ||
486 | ret = afs_do_lookup(dir, dentry, &fid); | |
1da177e4 | 487 | if (ret < 0) { |
08e0e7c8 | 488 | _leave(" = %d [do]", ret); |
1da177e4 LT |
489 | return ERR_PTR(ret); |
490 | } | |
491 | ||
08e0e7c8 DH |
492 | /* instantiate the dentry */ |
493 | inode = afs_iget(dir->i_sb, &fid); | |
494 | if (IS_ERR(inode)) { | |
495 | _leave(" = %ld", PTR_ERR(inode)); | |
496 | return ERR_PTR(PTR_ERR(inode)); | |
497 | } | |
498 | ||
1da177e4 | 499 | dentry->d_op = &afs_fs_dentry_operations; |
1da177e4 LT |
500 | |
501 | d_add(dentry, inode); | |
502 | _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%lu }", | |
08e0e7c8 DH |
503 | fid.vnode, |
504 | fid.unique, | |
1da177e4 LT |
505 | dentry->d_inode->i_ino, |
506 | dentry->d_inode->i_version); | |
507 | ||
508 | return NULL; | |
ec26815a | 509 | } |
1da177e4 | 510 | |
08e0e7c8 DH |
511 | /* |
512 | * propagate changed and modified flags on a directory to all the children of | |
513 | * that directory as they may indicate that the ACL on the dir has changed, | |
514 | * potentially rendering the child inaccessible or that a file has been deleted | |
515 | * or renamed | |
516 | */ | |
517 | static void afs_propagate_dir_changes(struct dentry *dir) | |
518 | { | |
519 | struct dentry *child; | |
520 | bool c, m; | |
521 | ||
522 | c = test_bit(AFS_VNODE_CHANGED, &AFS_FS_I(dir->d_inode)->flags); | |
523 | m = test_bit(AFS_VNODE_MODIFIED, &AFS_FS_I(dir->d_inode)->flags); | |
524 | ||
525 | _enter("{%d,%d}", c, m); | |
526 | ||
527 | spin_lock(&dir->d_lock); | |
528 | ||
529 | list_for_each_entry(child, &dir->d_subdirs, d_u.d_child) { | |
530 | if (child->d_inode) { | |
531 | struct afs_vnode *vnode; | |
532 | ||
533 | _debug("tag %s", child->d_name.name); | |
534 | vnode = AFS_FS_I(child->d_inode); | |
535 | if (c) | |
536 | set_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags); | |
537 | if (m) | |
538 | set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags); | |
539 | } | |
540 | } | |
541 | ||
542 | spin_unlock(&dir->d_lock); | |
543 | } | |
544 | ||
1da177e4 LT |
545 | /* |
546 | * check that a dentry lookup hit has found a valid entry | |
547 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an | |
548 | * inode | |
08e0e7c8 DH |
549 | * - there are several things we need to check |
550 | * - parent dir data changes (rm, rmdir, rename, mkdir, create, link, | |
551 | * symlink) | |
552 | * - parent dir metadata changed (security changes) | |
553 | * - dentry data changed (write, truncate) | |
554 | * - dentry metadata changed (security changes) | |
1da177e4 LT |
555 | */ |
556 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | |
557 | { | |
08e0e7c8 DH |
558 | struct afs_vnode *vnode; |
559 | struct afs_fid fid; | |
1da177e4 LT |
560 | struct dentry *parent; |
561 | struct inode *inode, *dir; | |
1da177e4 LT |
562 | int ret; |
563 | ||
08e0e7c8 DH |
564 | vnode = AFS_FS_I(dentry->d_inode); |
565 | ||
566 | _enter("{sb=%p n=%s fl=%lx},", | |
567 | dentry->d_sb, dentry->d_name.name, vnode->flags); | |
1da177e4 LT |
568 | |
569 | /* lock down the parent dentry so we can peer at it */ | |
08e0e7c8 | 570 | parent = dget_parent(dentry); |
1da177e4 LT |
571 | |
572 | dir = parent->d_inode; | |
573 | inode = dentry->d_inode; | |
574 | ||
575 | /* handle a negative dentry */ | |
576 | if (!inode) | |
577 | goto out_bad; | |
578 | ||
579 | /* handle a bad inode */ | |
580 | if (is_bad_inode(inode)) { | |
581 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | |
08e0e7c8 | 582 | parent->d_name.name, dentry->d_name.name); |
1da177e4 LT |
583 | goto out_bad; |
584 | } | |
585 | ||
08e0e7c8 DH |
586 | /* check that this dirent still exists if the directory's contents were |
587 | * modified */ | |
588 | if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { | |
1da177e4 LT |
589 | _debug("%s: parent dir deleted", dentry->d_name.name); |
590 | goto out_bad; | |
591 | } | |
592 | ||
08e0e7c8 DH |
593 | if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) { |
594 | /* rm/rmdir/rename may have occurred */ | |
595 | _debug("dir modified"); | |
1da177e4 LT |
596 | |
597 | /* search the directory for this vnode */ | |
08e0e7c8 DH |
598 | ret = afs_do_lookup(dir, dentry, &fid); |
599 | if (ret == -ENOENT) { | |
600 | _debug("%s: dirent not found", dentry->d_name.name); | |
601 | goto not_found; | |
602 | } | |
1da177e4 LT |
603 | if (ret < 0) { |
604 | _debug("failed to iterate dir %s: %d", | |
605 | parent->d_name.name, ret); | |
606 | goto out_bad; | |
607 | } | |
608 | ||
1da177e4 LT |
609 | /* if the vnode ID has changed, then the dirent points to a |
610 | * different file */ | |
08e0e7c8 DH |
611 | if (fid.vnode != vnode->fid.vnode) { |
612 | _debug("%s: dirent changed [%u != %u]", | |
613 | dentry->d_name.name, fid.vnode, | |
614 | vnode->fid.vnode); | |
1da177e4 LT |
615 | goto not_found; |
616 | } | |
617 | ||
618 | /* if the vnode ID uniqifier has changed, then the file has | |
619 | * been deleted */ | |
08e0e7c8 | 620 | if (fid.unique != vnode->fid.unique) { |
1da177e4 | 621 | _debug("%s: file deleted (uq %u -> %u I:%lu)", |
08e0e7c8 DH |
622 | dentry->d_name.name, fid.unique, |
623 | vnode->fid.unique, inode->i_version); | |
624 | spin_lock(&vnode->lock); | |
625 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | |
626 | spin_unlock(&vnode->lock); | |
1da177e4 LT |
627 | invalidate_remote_inode(inode); |
628 | goto out_bad; | |
629 | } | |
08e0e7c8 DH |
630 | } |
631 | ||
632 | /* if the directory's metadata were changed then the security may be | |
633 | * different and we may no longer have access */ | |
634 | mutex_lock(&vnode->cb_broken_lock); | |
1da177e4 | 635 | |
08e0e7c8 DH |
636 | if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) || |
637 | test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | |
638 | _debug("%s: changed", dentry->d_name.name); | |
639 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | |
640 | if (afs_vnode_fetch_status(vnode) < 0) { | |
641 | mutex_unlock(&vnode->cb_broken_lock); | |
642 | goto out_bad; | |
643 | } | |
1da177e4 LT |
644 | } |
645 | ||
08e0e7c8 DH |
646 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { |
647 | _debug("%s: file already deleted", dentry->d_name.name); | |
648 | mutex_unlock(&vnode->cb_broken_lock); | |
649 | goto out_bad; | |
650 | } | |
651 | ||
652 | /* if the vnode's data version number changed then its contents are | |
653 | * different */ | |
654 | if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) { | |
655 | _debug("zap data"); | |
656 | invalidate_remote_inode(inode); | |
657 | } | |
658 | ||
659 | if (S_ISDIR(inode->i_mode) && | |
660 | (test_bit(AFS_VNODE_CHANGED, &vnode->flags) || | |
661 | test_bit(AFS_VNODE_MODIFIED, &vnode->flags))) | |
662 | afs_propagate_dir_changes(dentry); | |
663 | ||
664 | clear_bit(AFS_VNODE_CHANGED, &vnode->flags); | |
665 | clear_bit(AFS_VNODE_MODIFIED, &vnode->flags); | |
666 | mutex_unlock(&vnode->cb_broken_lock); | |
667 | ||
ec26815a | 668 | out_valid: |
1da177e4 LT |
669 | dput(parent); |
670 | _leave(" = 1 [valid]"); | |
671 | return 1; | |
672 | ||
673 | /* the dirent, if it exists, now points to a different vnode */ | |
ec26815a | 674 | not_found: |
1da177e4 LT |
675 | spin_lock(&dentry->d_lock); |
676 | dentry->d_flags |= DCACHE_NFSFS_RENAMED; | |
677 | spin_unlock(&dentry->d_lock); | |
678 | ||
ec26815a | 679 | out_bad: |
1da177e4 LT |
680 | if (inode) { |
681 | /* don't unhash if we have submounts */ | |
682 | if (have_submounts(dentry)) | |
683 | goto out_valid; | |
684 | } | |
685 | ||
1da177e4 | 686 | _debug("dropping dentry %s/%s", |
08e0e7c8 DH |
687 | parent->d_name.name, dentry->d_name.name); |
688 | shrink_dcache_parent(dentry); | |
1da177e4 | 689 | d_drop(dentry); |
1da177e4 LT |
690 | dput(parent); |
691 | ||
692 | _leave(" = 0 [bad]"); | |
693 | return 0; | |
ec26815a | 694 | } |
1da177e4 | 695 | |
1da177e4 LT |
696 | /* |
697 | * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't | |
698 | * sleep) | |
699 | * - called from dput() when d_count is going to 0. | |
700 | * - return 1 to request dentry be unhashed, 0 otherwise | |
701 | */ | |
702 | static int afs_d_delete(struct dentry *dentry) | |
703 | { | |
704 | _enter("%s", dentry->d_name.name); | |
705 | ||
706 | if (dentry->d_flags & DCACHE_NFSFS_RENAMED) | |
707 | goto zap; | |
708 | ||
08e0e7c8 DH |
709 | if (dentry->d_inode && |
710 | test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags)) | |
1da177e4 | 711 | goto zap; |
1da177e4 LT |
712 | |
713 | _leave(" = 0 [keep]"); | |
714 | return 0; | |
715 | ||
ec26815a | 716 | zap: |
1da177e4 LT |
717 | _leave(" = 1 [zap]"); |
718 | return 1; | |
ec26815a | 719 | } |