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