diff options
-rw-r--r-- | fs/autofs4/autofs_i.h | 2 | ||||
-rw-r--r-- | fs/autofs4/inode.c | 25 | ||||
-rw-r--r-- | fs/autofs4/root.c | 169 |
3 files changed, 156 insertions, 40 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 69b1497b0029..2dce2334737d 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h | |||
@@ -52,6 +52,7 @@ struct autofs_info { | |||
52 | 52 | ||
53 | int flags; | 53 | int flags; |
54 | 54 | ||
55 | struct list_head active; | ||
55 | struct list_head expiring; | 56 | struct list_head expiring; |
56 | 57 | ||
57 | struct autofs_sb_info *sbi; | 58 | struct autofs_sb_info *sbi; |
@@ -113,6 +114,7 @@ struct autofs_sb_info { | |||
113 | spinlock_t fs_lock; | 114 | spinlock_t fs_lock; |
114 | struct autofs_wait_queue *queues; /* Wait queue pointer */ | 115 | struct autofs_wait_queue *queues; /* Wait queue pointer */ |
115 | spinlock_t lookup_lock; | 116 | spinlock_t lookup_lock; |
117 | struct list_head active_list; | ||
116 | struct list_head expiring_list; | 118 | struct list_head expiring_list; |
117 | }; | 119 | }; |
118 | 120 | ||
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 94bfc154d7a2..e3e70994ab46 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c | |||
@@ -24,8 +24,10 @@ | |||
24 | 24 | ||
25 | static void ino_lnkfree(struct autofs_info *ino) | 25 | static void ino_lnkfree(struct autofs_info *ino) |
26 | { | 26 | { |
27 | kfree(ino->u.symlink); | 27 | if (ino->u.symlink) { |
28 | ino->u.symlink = NULL; | 28 | kfree(ino->u.symlink); |
29 | ino->u.symlink = NULL; | ||
30 | } | ||
29 | } | 31 | } |
30 | 32 | ||
31 | struct autofs_info *autofs4_init_ino(struct autofs_info *ino, | 33 | struct autofs_info *autofs4_init_ino(struct autofs_info *ino, |
@@ -41,16 +43,18 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino, | |||
41 | if (ino == NULL) | 43 | if (ino == NULL) |
42 | return NULL; | 44 | return NULL; |
43 | 45 | ||
44 | ino->flags = 0; | 46 | if (!reinit) { |
45 | ino->mode = mode; | 47 | ino->flags = 0; |
46 | ino->inode = NULL; | 48 | ino->inode = NULL; |
47 | ino->dentry = NULL; | 49 | ino->dentry = NULL; |
48 | ino->size = 0; | 50 | ino->size = 0; |
49 | 51 | INIT_LIST_HEAD(&ino->active); | |
50 | INIT_LIST_HEAD(&ino->expiring); | 52 | INIT_LIST_HEAD(&ino->expiring); |
53 | atomic_set(&ino->count, 0); | ||
54 | } | ||
51 | 55 | ||
56 | ino->mode = mode; | ||
52 | ino->last_used = jiffies; | 57 | ino->last_used = jiffies; |
53 | atomic_set(&ino->count, 0); | ||
54 | 58 | ||
55 | ino->sbi = sbi; | 59 | ino->sbi = sbi; |
56 | 60 | ||
@@ -339,6 +343,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent) | |||
339 | spin_lock_init(&sbi->fs_lock); | 343 | spin_lock_init(&sbi->fs_lock); |
340 | sbi->queues = NULL; | 344 | sbi->queues = NULL; |
341 | spin_lock_init(&sbi->lookup_lock); | 345 | spin_lock_init(&sbi->lookup_lock); |
346 | INIT_LIST_HEAD(&sbi->active_list); | ||
342 | INIT_LIST_HEAD(&sbi->expiring_list); | 347 | INIT_LIST_HEAD(&sbi->expiring_list); |
343 | s->s_blocksize = 1024; | 348 | s->s_blocksize = 1024; |
344 | s->s_blocksize_bits = 10; | 349 | s->s_blocksize_bits = 10; |
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 53dabe8d5b8b..dbb70d5a4882 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c | |||
@@ -473,6 +473,8 @@ void autofs4_dentry_release(struct dentry *de) | |||
473 | 473 | ||
474 | if (sbi) { | 474 | if (sbi) { |
475 | spin_lock(&sbi->lookup_lock); | 475 | spin_lock(&sbi->lookup_lock); |
476 | if (!list_empty(&inf->active)) | ||
477 | list_del(&inf->active); | ||
476 | if (!list_empty(&inf->expiring)) | 478 | if (!list_empty(&inf->expiring)) |
477 | list_del(&inf->expiring); | 479 | list_del(&inf->expiring); |
478 | spin_unlock(&sbi->lookup_lock); | 480 | spin_unlock(&sbi->lookup_lock); |
@@ -497,6 +499,58 @@ static struct dentry_operations autofs4_dentry_operations = { | |||
497 | .d_release = autofs4_dentry_release, | 499 | .d_release = autofs4_dentry_release, |
498 | }; | 500 | }; |
499 | 501 | ||
502 | static struct dentry *autofs4_lookup_active(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) | ||
503 | { | ||
504 | unsigned int len = name->len; | ||
505 | unsigned int hash = name->hash; | ||
506 | const unsigned char *str = name->name; | ||
507 | struct list_head *p, *head; | ||
508 | |||
509 | spin_lock(&dcache_lock); | ||
510 | spin_lock(&sbi->lookup_lock); | ||
511 | head = &sbi->active_list; | ||
512 | list_for_each(p, head) { | ||
513 | struct autofs_info *ino; | ||
514 | struct dentry *dentry; | ||
515 | struct qstr *qstr; | ||
516 | |||
517 | ino = list_entry(p, struct autofs_info, active); | ||
518 | dentry = ino->dentry; | ||
519 | |||
520 | spin_lock(&dentry->d_lock); | ||
521 | |||
522 | /* Already gone? */ | ||
523 | if (atomic_read(&dentry->d_count) == 0) | ||
524 | goto next; | ||
525 | |||
526 | qstr = &dentry->d_name; | ||
527 | |||
528 | if (dentry->d_name.hash != hash) | ||
529 | goto next; | ||
530 | if (dentry->d_parent != parent) | ||
531 | goto next; | ||
532 | |||
533 | if (qstr->len != len) | ||
534 | goto next; | ||
535 | if (memcmp(qstr->name, str, len)) | ||
536 | goto next; | ||
537 | |||
538 | if (d_unhashed(dentry)) { | ||
539 | dget(dentry); | ||
540 | spin_unlock(&dentry->d_lock); | ||
541 | spin_unlock(&sbi->lookup_lock); | ||
542 | spin_unlock(&dcache_lock); | ||
543 | return dentry; | ||
544 | } | ||
545 | next: | ||
546 | spin_unlock(&dentry->d_lock); | ||
547 | } | ||
548 | spin_unlock(&sbi->lookup_lock); | ||
549 | spin_unlock(&dcache_lock); | ||
550 | |||
551 | return NULL; | ||
552 | } | ||
553 | |||
500 | static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) | 554 | static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name) |
501 | { | 555 | { |
502 | unsigned int len = name->len; | 556 | unsigned int len = name->len; |
@@ -553,7 +607,8 @@ next: | |||
553 | static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 607 | static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
554 | { | 608 | { |
555 | struct autofs_sb_info *sbi; | 609 | struct autofs_sb_info *sbi; |
556 | struct dentry *expiring; | 610 | struct autofs_info *ino; |
611 | struct dentry *expiring, *unhashed; | ||
557 | int oz_mode; | 612 | int oz_mode; |
558 | 613 | ||
559 | DPRINTK("name = %.*s", | 614 | DPRINTK("name = %.*s", |
@@ -571,12 +626,12 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
571 | 626 | ||
572 | expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); | 627 | expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name); |
573 | if (expiring) { | 628 | if (expiring) { |
574 | struct autofs_info *ino = autofs4_dentry_ino(expiring); | ||
575 | /* | 629 | /* |
576 | * If we are racing with expire the request might not | 630 | * If we are racing with expire the request might not |
577 | * be quite complete but the directory has been removed | 631 | * be quite complete but the directory has been removed |
578 | * so it must have been successful, so just wait for it. | 632 | * so it must have been successful, so just wait for it. |
579 | */ | 633 | */ |
634 | ino = autofs4_dentry_ino(expiring); | ||
580 | while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { | 635 | while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { |
581 | DPRINTK("wait for incomplete expire %p name=%.*s", | 636 | DPRINTK("wait for incomplete expire %p name=%.*s", |
582 | expiring, expiring->d_name.len, | 637 | expiring, expiring->d_name.len, |
@@ -591,21 +646,41 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
591 | dput(expiring); | 646 | dput(expiring); |
592 | } | 647 | } |
593 | 648 | ||
594 | /* | 649 | unhashed = autofs4_lookup_active(sbi, dentry->d_parent, &dentry->d_name); |
595 | * Mark the dentry incomplete but don't hash it. We do this | 650 | if (unhashed) |
596 | * to serialize our inode creation operations (symlink and | 651 | dentry = unhashed; |
597 | * mkdir) which prevents deadlock during the callback to | 652 | else { |
598 | * the daemon. Subsequent user space lookups for the same | 653 | /* |
599 | * dentry are placed on the wait queue while the daemon | 654 | * Mark the dentry incomplete but don't hash it. We do this |
600 | * itself is allowed passage unresticted so the create | 655 | * to serialize our inode creation operations (symlink and |
601 | * operation itself can then hash the dentry. Finally, | 656 | * mkdir) which prevents deadlock during the callback to |
602 | * we check for the hashed dentry and return the newly | 657 | * the daemon. Subsequent user space lookups for the same |
603 | * hashed dentry. | 658 | * dentry are placed on the wait queue while the daemon |
604 | */ | 659 | * itself is allowed passage unresticted so the create |
605 | dentry->d_op = &autofs4_root_dentry_operations; | 660 | * operation itself can then hash the dentry. Finally, |
661 | * we check for the hashed dentry and return the newly | ||
662 | * hashed dentry. | ||
663 | */ | ||
664 | dentry->d_op = &autofs4_root_dentry_operations; | ||
665 | |||
666 | /* | ||
667 | * And we need to ensure that the same dentry is used for | ||
668 | * all following lookup calls until it is hashed so that | ||
669 | * the dentry flags are persistent throughout the request. | ||
670 | */ | ||
671 | ino = autofs4_init_ino(NULL, sbi, 0555); | ||
672 | if (!ino) | ||
673 | return ERR_PTR(-ENOMEM); | ||
674 | |||
675 | dentry->d_fsdata = ino; | ||
676 | ino->dentry = dentry; | ||
677 | |||
678 | spin_lock(&sbi->lookup_lock); | ||
679 | list_add(&ino->active, &sbi->active_list); | ||
680 | spin_unlock(&sbi->lookup_lock); | ||
606 | 681 | ||
607 | dentry->d_fsdata = NULL; | 682 | d_instantiate(dentry, NULL); |
608 | d_instantiate(dentry, NULL); | 683 | } |
609 | 684 | ||
610 | if (!oz_mode) { | 685 | if (!oz_mode) { |
611 | spin_lock(&dentry->d_lock); | 686 | spin_lock(&dentry->d_lock); |
@@ -630,12 +705,16 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
630 | if (sigismember (sigset, SIGKILL) || | 705 | if (sigismember (sigset, SIGKILL) || |
631 | sigismember (sigset, SIGQUIT) || | 706 | sigismember (sigset, SIGQUIT) || |
632 | sigismember (sigset, SIGINT)) { | 707 | sigismember (sigset, SIGINT)) { |
708 | if (unhashed) | ||
709 | dput(unhashed); | ||
633 | return ERR_PTR(-ERESTARTNOINTR); | 710 | return ERR_PTR(-ERESTARTNOINTR); |
634 | } | 711 | } |
635 | } | 712 | } |
636 | spin_lock(&dentry->d_lock); | 713 | if (!oz_mode) { |
637 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; | 714 | spin_lock(&dentry->d_lock); |
638 | spin_unlock(&dentry->d_lock); | 715 | dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; |
716 | spin_unlock(&dentry->d_lock); | ||
717 | } | ||
639 | } | 718 | } |
640 | 719 | ||
641 | /* | 720 | /* |
@@ -659,9 +738,15 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s | |||
659 | else | 738 | else |
660 | dentry = ERR_PTR(-ENOENT); | 739 | dentry = ERR_PTR(-ENOENT); |
661 | 740 | ||
741 | if (unhashed) | ||
742 | dput(unhashed); | ||
743 | |||
662 | return dentry; | 744 | return dentry; |
663 | } | 745 | } |
664 | 746 | ||
747 | if (unhashed) | ||
748 | return unhashed; | ||
749 | |||
665 | return NULL; | 750 | return NULL; |
666 | } | 751 | } |
667 | 752 | ||
@@ -682,20 +767,30 @@ static int autofs4_dir_symlink(struct inode *dir, | |||
682 | return -EACCES; | 767 | return -EACCES; |
683 | 768 | ||
684 | ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); | 769 | ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555); |
685 | if (ino == NULL) | 770 | if (!ino) |
686 | return -ENOSPC; | 771 | return -ENOMEM; |
687 | 772 | ||
688 | ino->size = strlen(symname); | 773 | spin_lock(&sbi->lookup_lock); |
689 | ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); | 774 | if (!list_empty(&ino->active)) |
775 | list_del_init(&ino->active); | ||
776 | spin_unlock(&sbi->lookup_lock); | ||
690 | 777 | ||
691 | if (cp == NULL) { | 778 | cp = kmalloc(ino->size + 1, GFP_KERNEL); |
692 | kfree(ino); | 779 | if (!cp) { |
693 | return -ENOSPC; | 780 | if (!dentry->d_fsdata) |
781 | kfree(ino); | ||
782 | return -ENOMEM; | ||
694 | } | 783 | } |
695 | 784 | ||
696 | strcpy(cp, symname); | 785 | strcpy(cp, symname); |
697 | 786 | ||
698 | inode = autofs4_get_inode(dir->i_sb, ino); | 787 | inode = autofs4_get_inode(dir->i_sb, ino); |
788 | if (!inode) { | ||
789 | kfree(cp); | ||
790 | if (!dentry->d_fsdata) | ||
791 | kfree(ino); | ||
792 | return -ENOMEM; | ||
793 | } | ||
699 | d_add(dentry, inode); | 794 | d_add(dentry, inode); |
700 | 795 | ||
701 | if (dir == dir->i_sb->s_root->d_inode) | 796 | if (dir == dir->i_sb->s_root->d_inode) |
@@ -711,6 +806,8 @@ static int autofs4_dir_symlink(struct inode *dir, | |||
711 | atomic_inc(&p_ino->count); | 806 | atomic_inc(&p_ino->count); |
712 | ino->inode = inode; | 807 | ino->inode = inode; |
713 | 808 | ||
809 | ino->size = strlen(symname); | ||
810 | ino->u.symlink = cp; | ||
714 | dir->i_mtime = CURRENT_TIME; | 811 | dir->i_mtime = CURRENT_TIME; |
715 | 812 | ||
716 | return 0; | 813 | return 0; |
@@ -755,7 +852,8 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry) | |||
755 | 852 | ||
756 | spin_lock(&dcache_lock); | 853 | spin_lock(&dcache_lock); |
757 | spin_lock(&sbi->lookup_lock); | 854 | spin_lock(&sbi->lookup_lock); |
758 | list_add(&ino->expiring, &sbi->expiring_list); | 855 | if (list_empty(&ino->expiring)) |
856 | list_add(&ino->expiring, &sbi->expiring_list); | ||
759 | spin_unlock(&sbi->lookup_lock); | 857 | spin_unlock(&sbi->lookup_lock); |
760 | spin_lock(&dentry->d_lock); | 858 | spin_lock(&dentry->d_lock); |
761 | __d_drop(dentry); | 859 | __d_drop(dentry); |
@@ -783,7 +881,8 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry) | |||
783 | return -ENOTEMPTY; | 881 | return -ENOTEMPTY; |
784 | } | 882 | } |
785 | spin_lock(&sbi->lookup_lock); | 883 | spin_lock(&sbi->lookup_lock); |
786 | list_add(&ino->expiring, &sbi->expiring_list); | 884 | if (list_empty(&ino->expiring)) |
885 | list_add(&ino->expiring, &sbi->expiring_list); | ||
787 | spin_unlock(&sbi->lookup_lock); | 886 | spin_unlock(&sbi->lookup_lock); |
788 | spin_lock(&dentry->d_lock); | 887 | spin_lock(&dentry->d_lock); |
789 | __d_drop(dentry); | 888 | __d_drop(dentry); |
@@ -819,10 +918,20 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |||
819 | dentry, dentry->d_name.len, dentry->d_name.name); | 918 | dentry, dentry->d_name.len, dentry->d_name.name); |
820 | 919 | ||
821 | ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); | 920 | ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); |
822 | if (ino == NULL) | 921 | if (!ino) |
823 | return -ENOSPC; | 922 | return -ENOMEM; |
923 | |||
924 | spin_lock(&sbi->lookup_lock); | ||
925 | if (!list_empty(&ino->active)) | ||
926 | list_del_init(&ino->active); | ||
927 | spin_unlock(&sbi->lookup_lock); | ||
824 | 928 | ||
825 | inode = autofs4_get_inode(dir->i_sb, ino); | 929 | inode = autofs4_get_inode(dir->i_sb, ino); |
930 | if (!inode) { | ||
931 | if (!dentry->d_fsdata) | ||
932 | kfree(ino); | ||
933 | return -ENOMEM; | ||
934 | } | ||
826 | d_add(dentry, inode); | 935 | d_add(dentry, inode); |
827 | 936 | ||
828 | if (dir == dir->i_sb->s_root->d_inode) | 937 | if (dir == dir->i_sb->s_root->d_inode) |