diff options
author | David Howells <dhowells@redhat.com> | 2007-04-26 18:59:35 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-04-26 18:59:35 -0400 |
commit | 260a980317dac80182dd76140cf67c6e81d6d3dd (patch) | |
tree | 84f3e919fd33be56aad4fc57f5cb844df1a6b952 /fs/afs/dir.c | |
parent | c35eccb1f614954b10cba3f74b7c301993b2f42e (diff) |
[AFS]: Add "directory write" support.
Add support for the create, link, symlink, unlink, mkdir, rmdir and
rename VFS operations to the in-kernel AFS filesystem.
Also:
(1) Fix dentry and inode revalidation. d_revalidate should only look at
state of the dentry. Revalidation of the contents of an inode pointed to
by a dentry is now separate.
(2) Fix afs_lookup() to hash negative dentries as well as positive ones.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'fs/afs/dir.c')
-rw-r--r-- | fs/afs/dir.c | 676 |
1 files changed, 536 insertions, 140 deletions
diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 87368417e4d3..dbbe75d6023b 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c | |||
@@ -18,40 +18,50 @@ | |||
18 | #include <linux/ctype.h> | 18 | #include <linux/ctype.h> |
19 | #include "internal.h" | 19 | #include "internal.h" |
20 | 20 | ||
21 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | 21 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
22 | struct nameidata *nd); | 22 | struct nameidata *nd); |
23 | static int afs_dir_open(struct inode *inode, struct file *file); | 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); | 24 | static int afs_readdir(struct file *file, void *dirent, filldir_t filldir); |
25 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); | 25 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd); |
26 | static int afs_d_delete(struct dentry *dentry); | 26 | static int afs_d_delete(struct dentry *dentry); |
27 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | 27 | static void afs_d_release(struct dentry *dentry); |
28 | static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, | ||
28 | loff_t fpos, u64 ino, unsigned dtype); | 29 | loff_t fpos, u64 ino, unsigned dtype); |
30 | static int afs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
31 | struct nameidata *nd); | ||
32 | static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode); | ||
33 | static int afs_rmdir(struct inode *dir, struct dentry *dentry); | ||
34 | static int afs_unlink(struct inode *dir, struct dentry *dentry); | ||
35 | static int afs_link(struct dentry *from, struct inode *dir, | ||
36 | struct dentry *dentry); | ||
37 | static int afs_symlink(struct inode *dir, struct dentry *dentry, | ||
38 | const char *content); | ||
39 | static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
40 | struct inode *new_dir, struct dentry *new_dentry); | ||
29 | 41 | ||
30 | const struct file_operations afs_dir_file_operations = { | 42 | const struct file_operations afs_dir_file_operations = { |
31 | .open = afs_dir_open, | 43 | .open = afs_dir_open, |
32 | .release = afs_release, | 44 | .release = afs_release, |
33 | .readdir = afs_dir_readdir, | 45 | .readdir = afs_readdir, |
34 | }; | 46 | }; |
35 | 47 | ||
36 | const struct inode_operations afs_dir_inode_operations = { | 48 | const struct inode_operations afs_dir_inode_operations = { |
37 | .lookup = afs_dir_lookup, | 49 | .create = afs_create, |
50 | .lookup = afs_lookup, | ||
51 | .link = afs_link, | ||
52 | .unlink = afs_unlink, | ||
53 | .symlink = afs_symlink, | ||
54 | .mkdir = afs_mkdir, | ||
55 | .rmdir = afs_rmdir, | ||
56 | .rename = afs_rename, | ||
38 | .permission = afs_permission, | 57 | .permission = afs_permission, |
39 | .getattr = afs_inode_getattr, | 58 | .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 | }; | 59 | }; |
51 | 60 | ||
52 | static struct dentry_operations afs_fs_dentry_operations = { | 61 | static struct dentry_operations afs_fs_dentry_operations = { |
53 | .d_revalidate = afs_d_revalidate, | 62 | .d_revalidate = afs_d_revalidate, |
54 | .d_delete = afs_d_delete, | 63 | .d_delete = afs_d_delete, |
64 | .d_release = afs_d_release, | ||
55 | }; | 65 | }; |
56 | 66 | ||
57 | #define AFS_DIR_HASHTBL_SIZE 128 | 67 | #define AFS_DIR_HASHTBL_SIZE 128 |
@@ -103,7 +113,7 @@ struct afs_dir_page { | |||
103 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; | 113 | union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)]; |
104 | }; | 114 | }; |
105 | 115 | ||
106 | struct afs_dir_lookup_cookie { | 116 | struct afs_lookup_cookie { |
107 | struct afs_fid fid; | 117 | struct afs_fid fid; |
108 | const char *name; | 118 | const char *name; |
109 | size_t nlen; | 119 | size_t nlen; |
@@ -299,7 +309,7 @@ static int afs_dir_iterate_block(unsigned *fpos, | |||
299 | nlen, | 309 | nlen, |
300 | blkoff + offset * sizeof(union afs_dirent), | 310 | blkoff + offset * sizeof(union afs_dirent), |
301 | ntohl(dire->u.vnode), | 311 | ntohl(dire->u.vnode), |
302 | filldir == afs_dir_lookup_filldir ? | 312 | filldir == afs_lookup_filldir ? |
303 | ntohl(dire->u.unique) : DT_UNKNOWN); | 313 | ntohl(dire->u.unique) : DT_UNKNOWN); |
304 | if (ret < 0) { | 314 | if (ret < 0) { |
305 | _leave(" = 0 [full]"); | 315 | _leave(" = 0 [full]"); |
@@ -379,7 +389,7 @@ out: | |||
379 | /* | 389 | /* |
380 | * read an AFS directory | 390 | * read an AFS directory |
381 | */ | 391 | */ |
382 | static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | 392 | static int afs_readdir(struct file *file, void *cookie, filldir_t filldir) |
383 | { | 393 | { |
384 | unsigned fpos; | 394 | unsigned fpos; |
385 | int ret; | 395 | int ret; |
@@ -403,10 +413,10 @@ static int afs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) | |||
403 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID | 413 | * - if afs_dir_iterate_block() spots this function, it'll pass the FID |
404 | * uniquifier through dtype | 414 | * uniquifier through dtype |
405 | */ | 415 | */ |
406 | static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | 416 | static int afs_lookup_filldir(void *_cookie, const char *name, int nlen, |
407 | loff_t fpos, u64 ino, unsigned dtype) | 417 | loff_t fpos, u64 ino, unsigned dtype) |
408 | { | 418 | { |
409 | struct afs_dir_lookup_cookie *cookie = _cookie; | 419 | struct afs_lookup_cookie *cookie = _cookie; |
410 | 420 | ||
411 | _enter("{%s,%Zu},%s,%u,,%llu,%u", | 421 | _enter("{%s,%Zu},%s,%u,,%llu,%u", |
412 | cookie->name, cookie->nlen, name, nlen, ino, dtype); | 422 | cookie->name, cookie->nlen, name, nlen, ino, dtype); |
@@ -430,11 +440,12 @@ static int afs_dir_lookup_filldir(void *_cookie, const char *name, int nlen, | |||
430 | 440 | ||
431 | /* | 441 | /* |
432 | * do a lookup in a directory | 442 | * do a lookup in a directory |
443 | * - just returns the FID the dentry name maps to if found | ||
433 | */ | 444 | */ |
434 | static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | 445 | static int afs_do_lookup(struct inode *dir, struct dentry *dentry, |
435 | struct afs_fid *fid, struct key *key) | 446 | struct afs_fid *fid, struct key *key) |
436 | { | 447 | { |
437 | struct afs_dir_lookup_cookie cookie; | 448 | struct afs_lookup_cookie cookie; |
438 | struct afs_super_info *as; | 449 | struct afs_super_info *as; |
439 | unsigned fpos; | 450 | unsigned fpos; |
440 | int ret; | 451 | int ret; |
@@ -450,7 +461,7 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
450 | cookie.found = 0; | 461 | cookie.found = 0; |
451 | 462 | ||
452 | fpos = 0; | 463 | fpos = 0; |
453 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_dir_lookup_filldir, | 464 | ret = afs_dir_iterate(dir, &fpos, &cookie, afs_lookup_filldir, |
454 | key); | 465 | key); |
455 | if (ret < 0) { | 466 | if (ret < 0) { |
456 | _leave(" = %d [iter]", ret); | 467 | _leave(" = %d [iter]", ret); |
@@ -471,8 +482,8 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, | |||
471 | /* | 482 | /* |
472 | * look up an entry in a directory | 483 | * look up an entry in a directory |
473 | */ | 484 | */ |
474 | static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | 485 | static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
475 | struct nameidata *nd) | 486 | struct nameidata *nd) |
476 | { | 487 | { |
477 | struct afs_vnode *vnode; | 488 | struct afs_vnode *vnode; |
478 | struct afs_fid fid; | 489 | struct afs_fid fid; |
@@ -480,14 +491,18 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
480 | struct key *key; | 491 | struct key *key; |
481 | int ret; | 492 | int ret; |
482 | 493 | ||
483 | _enter("{%lu},%p{%s}", dir->i_ino, dentry, dentry->d_name.name); | 494 | vnode = AFS_FS_I(dir); |
495 | |||
496 | _enter("{%x:%d},%p{%s},", | ||
497 | vnode->fid.vid, vnode->fid.vnode, dentry, dentry->d_name.name); | ||
498 | |||
499 | ASSERTCMP(dentry->d_inode, ==, NULL); | ||
484 | 500 | ||
485 | if (dentry->d_name.len > 255) { | 501 | if (dentry->d_name.len > 255) { |
486 | _leave(" = -ENAMETOOLONG"); | 502 | _leave(" = -ENAMETOOLONG"); |
487 | return ERR_PTR(-ENAMETOOLONG); | 503 | return ERR_PTR(-ENAMETOOLONG); |
488 | } | 504 | } |
489 | 505 | ||
490 | vnode = AFS_FS_I(dir); | ||
491 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | 506 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { |
492 | _leave(" = -ESTALE"); | 507 | _leave(" = -ESTALE"); |
493 | return ERR_PTR(-ESTALE); | 508 | return ERR_PTR(-ESTALE); |
@@ -499,15 +514,28 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
499 | return ERR_PTR(PTR_ERR(key)); | 514 | return ERR_PTR(PTR_ERR(key)); |
500 | } | 515 | } |
501 | 516 | ||
517 | ret = afs_validate(vnode, key); | ||
518 | if (ret < 0) { | ||
519 | key_put(key); | ||
520 | _leave(" = %d [val]", ret); | ||
521 | return ERR_PTR(ret); | ||
522 | } | ||
523 | |||
502 | ret = afs_do_lookup(dir, dentry, &fid, key); | 524 | ret = afs_do_lookup(dir, dentry, &fid, key); |
503 | if (ret < 0) { | 525 | if (ret < 0) { |
504 | key_put(key); | 526 | key_put(key); |
527 | if (ret == -ENOENT) { | ||
528 | d_add(dentry, NULL); | ||
529 | _leave(" = NULL [negative]"); | ||
530 | return NULL; | ||
531 | } | ||
505 | _leave(" = %d [do]", ret); | 532 | _leave(" = %d [do]", ret); |
506 | return ERR_PTR(ret); | 533 | return ERR_PTR(ret); |
507 | } | 534 | } |
535 | dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; | ||
508 | 536 | ||
509 | /* instantiate the dentry */ | 537 | /* instantiate the dentry */ |
510 | inode = afs_iget(dir->i_sb, key, &fid); | 538 | inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); |
511 | key_put(key); | 539 | key_put(key); |
512 | if (IS_ERR(inode)) { | 540 | if (IS_ERR(inode)) { |
513 | _leave(" = %ld", PTR_ERR(inode)); | 541 | _leave(" = %ld", PTR_ERR(inode)); |
@@ -527,105 +555,64 @@ static struct dentry *afs_dir_lookup(struct inode *dir, struct dentry *dentry, | |||
527 | } | 555 | } |
528 | 556 | ||
529 | /* | 557 | /* |
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 | |||
563 | /* | ||
564 | * check that a dentry lookup hit has found a valid entry | 558 | * 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 | 559 | * - NOTE! the hit can be a negative hit too, so we can't assume we have an |
566 | * inode | 560 | * inode |
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) | ||
573 | */ | 561 | */ |
574 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | 562 | static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
575 | { | 563 | { |
576 | struct afs_vnode *vnode; | 564 | struct afs_vnode *vnode, *dir; |
577 | struct afs_fid fid; | 565 | struct afs_fid fid; |
578 | struct dentry *parent; | 566 | struct dentry *parent; |
579 | struct inode *inode, *dir; | ||
580 | struct key *key; | 567 | struct key *key; |
568 | void *dir_version; | ||
581 | int ret; | 569 | int ret; |
582 | 570 | ||
583 | vnode = AFS_FS_I(dentry->d_inode); | 571 | vnode = AFS_FS_I(dentry->d_inode); |
584 | 572 | ||
585 | _enter("{sb=%p n=%s fl=%lx},", | 573 | if (dentry->d_inode) |
586 | dentry->d_sb, dentry->d_name.name, vnode->flags); | 574 | _enter("{v={%x:%u} n=%s fl=%lx},", |
575 | vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, | ||
576 | vnode->flags); | ||
577 | else | ||
578 | _enter("{neg n=%s}", dentry->d_name.name); | ||
587 | 579 | ||
588 | key = afs_request_key(vnode->volume->cell); | 580 | key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); |
589 | if (IS_ERR(key)) | 581 | if (IS_ERR(key)) |
590 | key = NULL; | 582 | key = NULL; |
591 | 583 | ||
592 | /* lock down the parent dentry so we can peer at it */ | 584 | /* lock down the parent dentry so we can peer at it */ |
593 | parent = dget_parent(dentry); | 585 | parent = dget_parent(dentry); |
594 | 586 | if (!parent->d_inode) | |
595 | dir = parent->d_inode; | ||
596 | inode = dentry->d_inode; | ||
597 | |||
598 | /* handle a negative dentry */ | ||
599 | if (!inode) | ||
600 | goto out_bad; | 587 | goto out_bad; |
601 | 588 | ||
602 | /* handle a bad inode */ | 589 | dir = AFS_FS_I(parent->d_inode); |
603 | if (is_bad_inode(inode)) { | ||
604 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | ||
605 | parent->d_name.name, dentry->d_name.name); | ||
606 | goto out_bad; | ||
607 | } | ||
608 | 590 | ||
609 | /* check that this dirent still exists if the directory's contents were | 591 | /* validate the parent directory */ |
610 | * modified */ | 592 | if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) |
611 | if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dir)->flags)) { | 593 | afs_validate(dir, key); |
594 | |||
595 | if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { | ||
612 | _debug("%s: parent dir deleted", dentry->d_name.name); | 596 | _debug("%s: parent dir deleted", dentry->d_name.name); |
613 | goto out_bad; | 597 | goto out_bad; |
614 | } | 598 | } |
615 | 599 | ||
616 | if (test_and_clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags)) { | 600 | dir_version = (void *) (unsigned long) dir->status.data_version; |
617 | /* rm/rmdir/rename may have occurred */ | 601 | if (dentry->d_fsdata == dir_version) |
618 | _debug("dir modified"); | 602 | goto out_valid; /* the dir contents are unchanged */ |
619 | 603 | ||
620 | /* search the directory for this vnode */ | 604 | _debug("dir modified"); |
621 | ret = afs_do_lookup(dir, dentry, &fid, key); | 605 | |
622 | if (ret == -ENOENT) { | 606 | /* search the directory for this vnode */ |
623 | _debug("%s: dirent not found", dentry->d_name.name); | 607 | ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); |
624 | goto not_found; | 608 | switch (ret) { |
625 | } | 609 | case 0: |
626 | if (ret < 0) { | 610 | /* the filename maps to something */ |
627 | _debug("failed to iterate dir %s: %d", | 611 | if (!dentry->d_inode) |
628 | parent->d_name.name, ret); | 612 | goto out_bad; |
613 | if (is_bad_inode(dentry->d_inode)) { | ||
614 | printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", | ||
615 | parent->d_name.name, dentry->d_name.name); | ||
629 | goto out_bad; | 616 | goto out_bad; |
630 | } | 617 | } |
631 | 618 | ||
@@ -639,56 +626,35 @@ static int afs_d_revalidate(struct dentry *dentry, struct nameidata *nd) | |||
639 | } | 626 | } |
640 | 627 | ||
641 | /* if the vnode ID uniqifier has changed, then the file has | 628 | /* if the vnode ID uniqifier has changed, then the file has |
642 | * been deleted */ | 629 | * been deleted and replaced, and the original vnode ID has |
630 | * been reused */ | ||
643 | if (fid.unique != vnode->fid.unique) { | 631 | if (fid.unique != vnode->fid.unique) { |
644 | _debug("%s: file deleted (uq %u -> %u I:%lu)", | 632 | _debug("%s: file deleted (uq %u -> %u I:%lu)", |
645 | dentry->d_name.name, fid.unique, | 633 | dentry->d_name.name, fid.unique, |
646 | vnode->fid.unique, inode->i_version); | 634 | vnode->fid.unique, dentry->d_inode->i_version); |
647 | spin_lock(&vnode->lock); | 635 | spin_lock(&vnode->lock); |
648 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | 636 | set_bit(AFS_VNODE_DELETED, &vnode->flags); |
649 | spin_unlock(&vnode->lock); | 637 | spin_unlock(&vnode->lock); |
650 | invalidate_remote_inode(inode); | 638 | goto not_found; |
651 | goto out_bad; | ||
652 | } | 639 | } |
653 | } | 640 | goto out_valid; |
654 | 641 | ||
655 | /* if the directory's metadata were changed then the security may be | 642 | case -ENOENT: |
656 | * different and we may no longer have access */ | 643 | /* the filename is unknown */ |
657 | mutex_lock(&vnode->cb_broken_lock); | 644 | _debug("%s: dirent not found", dentry->d_name.name); |
658 | 645 | if (dentry->d_inode) | |
659 | if (test_and_clear_bit(AFS_VNODE_DIR_CHANGED, &vnode->flags) || | 646 | goto not_found; |
660 | test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) { | 647 | goto out_valid; |
661 | _debug("%s: changed", dentry->d_name.name); | ||
662 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
663 | if (afs_vnode_fetch_status(vnode, NULL, key) < 0) { | ||
664 | mutex_unlock(&vnode->cb_broken_lock); | ||
665 | goto out_bad; | ||
666 | } | ||
667 | } | ||
668 | 648 | ||
669 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { | 649 | default: |
670 | _debug("%s: file already deleted", dentry->d_name.name); | 650 | _debug("failed to iterate dir %s: %d", |
671 | mutex_unlock(&vnode->cb_broken_lock); | 651 | parent->d_name.name, ret); |
672 | goto out_bad; | 652 | goto out_bad; |
673 | } | 653 | } |
674 | 654 | ||
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 | |||
691 | out_valid: | 655 | out_valid: |
656 | dentry->d_fsdata = dir_version; | ||
657 | out_skip: | ||
692 | dput(parent); | 658 | dput(parent); |
693 | key_put(key); | 659 | key_put(key); |
694 | _leave(" = 1 [valid]"); | 660 | _leave(" = 1 [valid]"); |
@@ -701,10 +667,10 @@ not_found: | |||
701 | spin_unlock(&dentry->d_lock); | 667 | spin_unlock(&dentry->d_lock); |
702 | 668 | ||
703 | out_bad: | 669 | out_bad: |
704 | if (inode) { | 670 | if (dentry->d_inode) { |
705 | /* don't unhash if we have submounts */ | 671 | /* don't unhash if we have submounts */ |
706 | if (have_submounts(dentry)) | 672 | if (have_submounts(dentry)) |
707 | goto out_valid; | 673 | goto out_skip; |
708 | } | 674 | } |
709 | 675 | ||
710 | _debug("dropping dentry %s/%s", | 676 | _debug("dropping dentry %s/%s", |
@@ -742,3 +708,433 @@ zap: | |||
742 | _leave(" = 1 [zap]"); | 708 | _leave(" = 1 [zap]"); |
743 | return 1; | 709 | return 1; |
744 | } | 710 | } |
711 | |||
712 | /* | ||
713 | * handle dentry release | ||
714 | */ | ||
715 | static void afs_d_release(struct dentry *dentry) | ||
716 | { | ||
717 | _enter("%s", dentry->d_name.name); | ||
718 | } | ||
719 | |||
720 | /* | ||
721 | * create a directory on an AFS filesystem | ||
722 | */ | ||
723 | static int afs_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
724 | { | ||
725 | struct afs_file_status status; | ||
726 | struct afs_callback cb; | ||
727 | struct afs_server *server; | ||
728 | struct afs_vnode *dvnode, *vnode; | ||
729 | struct afs_fid fid; | ||
730 | struct inode *inode; | ||
731 | struct key *key; | ||
732 | int ret; | ||
733 | |||
734 | dvnode = AFS_FS_I(dir); | ||
735 | |||
736 | _enter("{%x:%d},{%s},%o", | ||
737 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); | ||
738 | |||
739 | ret = -ENAMETOOLONG; | ||
740 | if (dentry->d_name.len > 255) | ||
741 | goto error; | ||
742 | |||
743 | key = afs_request_key(dvnode->volume->cell); | ||
744 | if (IS_ERR(key)) { | ||
745 | ret = PTR_ERR(key); | ||
746 | goto error; | ||
747 | } | ||
748 | |||
749 | mode |= S_IFDIR; | ||
750 | ret = afs_vnode_create(dvnode, key, dentry->d_name.name, | ||
751 | mode, &fid, &status, &cb, &server); | ||
752 | if (ret < 0) | ||
753 | goto mkdir_error; | ||
754 | |||
755 | inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); | ||
756 | if (IS_ERR(inode)) { | ||
757 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
758 | * directory on the server */ | ||
759 | ret = PTR_ERR(inode); | ||
760 | goto iget_error; | ||
761 | } | ||
762 | |||
763 | /* apply the status report we've got for the new vnode */ | ||
764 | vnode = AFS_FS_I(inode); | ||
765 | spin_lock(&vnode->lock); | ||
766 | vnode->update_cnt++; | ||
767 | spin_unlock(&vnode->lock); | ||
768 | afs_vnode_finalise_status_update(vnode, server); | ||
769 | afs_put_server(server); | ||
770 | |||
771 | d_instantiate(dentry, inode); | ||
772 | if (d_unhashed(dentry)) { | ||
773 | _debug("not hashed"); | ||
774 | d_rehash(dentry); | ||
775 | } | ||
776 | key_put(key); | ||
777 | _leave(" = 0"); | ||
778 | return 0; | ||
779 | |||
780 | iget_error: | ||
781 | afs_put_server(server); | ||
782 | mkdir_error: | ||
783 | key_put(key); | ||
784 | error: | ||
785 | d_drop(dentry); | ||
786 | _leave(" = %d", ret); | ||
787 | return ret; | ||
788 | } | ||
789 | |||
790 | /* | ||
791 | * remove a directory from an AFS filesystem | ||
792 | */ | ||
793 | static int afs_rmdir(struct inode *dir, struct dentry *dentry) | ||
794 | { | ||
795 | struct afs_vnode *dvnode, *vnode; | ||
796 | struct key *key; | ||
797 | int ret; | ||
798 | |||
799 | dvnode = AFS_FS_I(dir); | ||
800 | |||
801 | _enter("{%x:%d},{%s}", | ||
802 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); | ||
803 | |||
804 | ret = -ENAMETOOLONG; | ||
805 | if (dentry->d_name.len > 255) | ||
806 | goto error; | ||
807 | |||
808 | key = afs_request_key(dvnode->volume->cell); | ||
809 | if (IS_ERR(key)) { | ||
810 | ret = PTR_ERR(key); | ||
811 | goto error; | ||
812 | } | ||
813 | |||
814 | ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true); | ||
815 | if (ret < 0) | ||
816 | goto rmdir_error; | ||
817 | |||
818 | if (dentry->d_inode) { | ||
819 | vnode = AFS_FS_I(dentry->d_inode); | ||
820 | clear_nlink(&vnode->vfs_inode); | ||
821 | set_bit(AFS_VNODE_DELETED, &vnode->flags); | ||
822 | afs_discard_callback_on_delete(vnode); | ||
823 | } | ||
824 | |||
825 | key_put(key); | ||
826 | _leave(" = 0"); | ||
827 | return 0; | ||
828 | |||
829 | rmdir_error: | ||
830 | key_put(key); | ||
831 | error: | ||
832 | _leave(" = %d", ret); | ||
833 | return ret; | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * remove a file from an AFS filesystem | ||
838 | */ | ||
839 | static int afs_unlink(struct inode *dir, struct dentry *dentry) | ||
840 | { | ||
841 | struct afs_vnode *dvnode, *vnode; | ||
842 | struct key *key; | ||
843 | int ret; | ||
844 | |||
845 | dvnode = AFS_FS_I(dir); | ||
846 | |||
847 | _enter("{%x:%d},{%s}", | ||
848 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name); | ||
849 | |||
850 | ret = -ENAMETOOLONG; | ||
851 | if (dentry->d_name.len > 255) | ||
852 | goto error; | ||
853 | |||
854 | key = afs_request_key(dvnode->volume->cell); | ||
855 | if (IS_ERR(key)) { | ||
856 | ret = PTR_ERR(key); | ||
857 | goto error; | ||
858 | } | ||
859 | |||
860 | if (dentry->d_inode) { | ||
861 | vnode = AFS_FS_I(dentry->d_inode); | ||
862 | |||
863 | /* make sure we have a callback promise on the victim */ | ||
864 | ret = afs_validate(vnode, key); | ||
865 | if (ret < 0) | ||
866 | goto error; | ||
867 | } | ||
868 | |||
869 | ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); | ||
870 | if (ret < 0) | ||
871 | goto remove_error; | ||
872 | |||
873 | if (dentry->d_inode) { | ||
874 | /* if the file wasn't deleted due to excess hard links, the | ||
875 | * fileserver will break the callback promise on the file - if | ||
876 | * it had one - before it returns to us, and if it was deleted, | ||
877 | * it won't | ||
878 | * | ||
879 | * however, if we didn't have a callback promise outstanding, | ||
880 | * or it was outstanding on a different server, then it won't | ||
881 | * break it either... | ||
882 | */ | ||
883 | vnode = AFS_FS_I(dentry->d_inode); | ||
884 | if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) | ||
885 | _debug("AFS_VNODE_DELETED"); | ||
886 | if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) | ||
887 | _debug("AFS_VNODE_CB_BROKEN"); | ||
888 | set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); | ||
889 | ret = afs_validate(vnode, key); | ||
890 | _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); | ||
891 | } | ||
892 | |||
893 | key_put(key); | ||
894 | _leave(" = 0"); | ||
895 | return 0; | ||
896 | |||
897 | remove_error: | ||
898 | key_put(key); | ||
899 | error: | ||
900 | _leave(" = %d", ret); | ||
901 | return ret; | ||
902 | } | ||
903 | |||
904 | /* | ||
905 | * create a regular file on an AFS filesystem | ||
906 | */ | ||
907 | static int afs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
908 | struct nameidata *nd) | ||
909 | { | ||
910 | struct afs_file_status status; | ||
911 | struct afs_callback cb; | ||
912 | struct afs_server *server; | ||
913 | struct afs_vnode *dvnode, *vnode; | ||
914 | struct afs_fid fid; | ||
915 | struct inode *inode; | ||
916 | struct key *key; | ||
917 | int ret; | ||
918 | |||
919 | dvnode = AFS_FS_I(dir); | ||
920 | |||
921 | _enter("{%x:%d},{%s},%o,", | ||
922 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, mode); | ||
923 | |||
924 | ret = -ENAMETOOLONG; | ||
925 | if (dentry->d_name.len > 255) | ||
926 | goto error; | ||
927 | |||
928 | key = afs_request_key(dvnode->volume->cell); | ||
929 | if (IS_ERR(key)) { | ||
930 | ret = PTR_ERR(key); | ||
931 | goto error; | ||
932 | } | ||
933 | |||
934 | mode |= S_IFREG; | ||
935 | ret = afs_vnode_create(dvnode, key, dentry->d_name.name, | ||
936 | mode, &fid, &status, &cb, &server); | ||
937 | if (ret < 0) | ||
938 | goto create_error; | ||
939 | |||
940 | inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); | ||
941 | if (IS_ERR(inode)) { | ||
942 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
943 | * directory on the server */ | ||
944 | ret = PTR_ERR(inode); | ||
945 | goto iget_error; | ||
946 | } | ||
947 | |||
948 | /* apply the status report we've got for the new vnode */ | ||
949 | vnode = AFS_FS_I(inode); | ||
950 | spin_lock(&vnode->lock); | ||
951 | vnode->update_cnt++; | ||
952 | spin_unlock(&vnode->lock); | ||
953 | afs_vnode_finalise_status_update(vnode, server); | ||
954 | afs_put_server(server); | ||
955 | |||
956 | d_instantiate(dentry, inode); | ||
957 | if (d_unhashed(dentry)) { | ||
958 | _debug("not hashed"); | ||
959 | d_rehash(dentry); | ||
960 | } | ||
961 | key_put(key); | ||
962 | _leave(" = 0"); | ||
963 | return 0; | ||
964 | |||
965 | iget_error: | ||
966 | afs_put_server(server); | ||
967 | create_error: | ||
968 | key_put(key); | ||
969 | error: | ||
970 | d_drop(dentry); | ||
971 | _leave(" = %d", ret); | ||
972 | return ret; | ||
973 | } | ||
974 | |||
975 | /* | ||
976 | * create a hard link between files in an AFS filesystem | ||
977 | */ | ||
978 | static int afs_link(struct dentry *from, struct inode *dir, | ||
979 | struct dentry *dentry) | ||
980 | { | ||
981 | struct afs_vnode *dvnode, *vnode; | ||
982 | struct key *key; | ||
983 | int ret; | ||
984 | |||
985 | vnode = AFS_FS_I(from->d_inode); | ||
986 | dvnode = AFS_FS_I(dir); | ||
987 | |||
988 | _enter("{%x:%d},{%x:%d},{%s}", | ||
989 | vnode->fid.vid, vnode->fid.vnode, | ||
990 | dvnode->fid.vid, dvnode->fid.vnode, | ||
991 | dentry->d_name.name); | ||
992 | |||
993 | ret = -ENAMETOOLONG; | ||
994 | if (dentry->d_name.len > 255) | ||
995 | goto error; | ||
996 | |||
997 | key = afs_request_key(dvnode->volume->cell); | ||
998 | if (IS_ERR(key)) { | ||
999 | ret = PTR_ERR(key); | ||
1000 | goto error; | ||
1001 | } | ||
1002 | |||
1003 | ret = afs_vnode_link(dvnode, vnode, key, dentry->d_name.name); | ||
1004 | if (ret < 0) | ||
1005 | goto link_error; | ||
1006 | |||
1007 | atomic_inc(&vnode->vfs_inode.i_count); | ||
1008 | d_instantiate(dentry, &vnode->vfs_inode); | ||
1009 | key_put(key); | ||
1010 | _leave(" = 0"); | ||
1011 | return 0; | ||
1012 | |||
1013 | link_error: | ||
1014 | key_put(key); | ||
1015 | error: | ||
1016 | d_drop(dentry); | ||
1017 | _leave(" = %d", ret); | ||
1018 | return ret; | ||
1019 | } | ||
1020 | |||
1021 | /* | ||
1022 | * create a symlink in an AFS filesystem | ||
1023 | */ | ||
1024 | static int afs_symlink(struct inode *dir, struct dentry *dentry, | ||
1025 | const char *content) | ||
1026 | { | ||
1027 | struct afs_file_status status; | ||
1028 | struct afs_server *server; | ||
1029 | struct afs_vnode *dvnode, *vnode; | ||
1030 | struct afs_fid fid; | ||
1031 | struct inode *inode; | ||
1032 | struct key *key; | ||
1033 | int ret; | ||
1034 | |||
1035 | dvnode = AFS_FS_I(dir); | ||
1036 | |||
1037 | _enter("{%x:%d},{%s},%s", | ||
1038 | dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, | ||
1039 | content); | ||
1040 | |||
1041 | ret = -ENAMETOOLONG; | ||
1042 | if (dentry->d_name.len > 255) | ||
1043 | goto error; | ||
1044 | |||
1045 | ret = -EINVAL; | ||
1046 | if (strlen(content) > 1023) | ||
1047 | goto error; | ||
1048 | |||
1049 | key = afs_request_key(dvnode->volume->cell); | ||
1050 | if (IS_ERR(key)) { | ||
1051 | ret = PTR_ERR(key); | ||
1052 | goto error; | ||
1053 | } | ||
1054 | |||
1055 | ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content, | ||
1056 | &fid, &status, &server); | ||
1057 | if (ret < 0) | ||
1058 | goto create_error; | ||
1059 | |||
1060 | inode = afs_iget(dir->i_sb, key, &fid, &status, NULL); | ||
1061 | if (IS_ERR(inode)) { | ||
1062 | /* ENOMEM at a really inconvenient time - just abandon the new | ||
1063 | * directory on the server */ | ||
1064 | ret = PTR_ERR(inode); | ||
1065 | goto iget_error; | ||
1066 | } | ||
1067 | |||
1068 | /* apply the status report we've got for the new vnode */ | ||
1069 | vnode = AFS_FS_I(inode); | ||
1070 | spin_lock(&vnode->lock); | ||
1071 | vnode->update_cnt++; | ||
1072 | spin_unlock(&vnode->lock); | ||
1073 | afs_vnode_finalise_status_update(vnode, server); | ||
1074 | afs_put_server(server); | ||
1075 | |||
1076 | d_instantiate(dentry, inode); | ||
1077 | if (d_unhashed(dentry)) { | ||
1078 | _debug("not hashed"); | ||
1079 | d_rehash(dentry); | ||
1080 | } | ||
1081 | key_put(key); | ||
1082 | _leave(" = 0"); | ||
1083 | return 0; | ||
1084 | |||
1085 | iget_error: | ||
1086 | afs_put_server(server); | ||
1087 | create_error: | ||
1088 | key_put(key); | ||
1089 | error: | ||
1090 | d_drop(dentry); | ||
1091 | _leave(" = %d", ret); | ||
1092 | return ret; | ||
1093 | } | ||
1094 | |||
1095 | /* | ||
1096 | * rename a file in an AFS filesystem and/or move it between directories | ||
1097 | */ | ||
1098 | static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
1099 | struct inode *new_dir, struct dentry *new_dentry) | ||
1100 | { | ||
1101 | struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; | ||
1102 | struct key *key; | ||
1103 | int ret; | ||
1104 | |||
1105 | vnode = AFS_FS_I(old_dentry->d_inode); | ||
1106 | orig_dvnode = AFS_FS_I(old_dir); | ||
1107 | new_dvnode = AFS_FS_I(new_dir); | ||
1108 | |||
1109 | _enter("{%x:%d},{%x:%d},{%x:%d},{%s}", | ||
1110 | orig_dvnode->fid.vid, orig_dvnode->fid.vnode, | ||
1111 | vnode->fid.vid, vnode->fid.vnode, | ||
1112 | new_dvnode->fid.vid, new_dvnode->fid.vnode, | ||
1113 | new_dentry->d_name.name); | ||
1114 | |||
1115 | ret = -ENAMETOOLONG; | ||
1116 | if (new_dentry->d_name.len > 255) | ||
1117 | goto error; | ||
1118 | |||
1119 | key = afs_request_key(orig_dvnode->volume->cell); | ||
1120 | if (IS_ERR(key)) { | ||
1121 | ret = PTR_ERR(key); | ||
1122 | goto error; | ||
1123 | } | ||
1124 | |||
1125 | ret = afs_vnode_rename(orig_dvnode, new_dvnode, key, | ||
1126 | old_dentry->d_name.name, | ||
1127 | new_dentry->d_name.name); | ||
1128 | if (ret < 0) | ||
1129 | goto rename_error; | ||
1130 | key_put(key); | ||
1131 | _leave(" = 0"); | ||
1132 | return 0; | ||
1133 | |||
1134 | rename_error: | ||
1135 | key_put(key); | ||
1136 | error: | ||
1137 | d_drop(new_dentry); | ||
1138 | _leave(" = %d", ret); | ||
1139 | return ret; | ||
1140 | } | ||