diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-04-16 16:22:52 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-14 15:09:28 -0400 |
commit | ce587e07ba2e25b5c9d286849885b82676661f3e (patch) | |
tree | 5bb91822d82fa9a26f7846edcc48bf01fad4411f /fs/nfs/super.c | |
parent | 6e94d62993cb79397856f3330577917ca79cffa2 (diff) |
NFS: Prevent the mount code from looping forever on broken exports
Keep a global count of how many referrals that the current task has
traversed on a path lookup. Return ELOOP if the count exceeds
MAX_NESTED_LINKS.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/super.c')
-rw-r--r-- | fs/nfs/super.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 98d1ab8bf8f2..50c6c282ba40 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c | |||
@@ -2673,6 +2673,72 @@ out_freepage: | |||
2673 | free_page((unsigned long)page); | 2673 | free_page((unsigned long)page); |
2674 | } | 2674 | } |
2675 | 2675 | ||
2676 | struct nfs_referral_count { | ||
2677 | struct list_head list; | ||
2678 | const struct task_struct *task; | ||
2679 | unsigned int referral_count; | ||
2680 | }; | ||
2681 | |||
2682 | static LIST_HEAD(nfs_referral_count_list); | ||
2683 | static DEFINE_SPINLOCK(nfs_referral_count_list_lock); | ||
2684 | |||
2685 | static struct nfs_referral_count *nfs_find_referral_count(void) | ||
2686 | { | ||
2687 | struct nfs_referral_count *p; | ||
2688 | |||
2689 | list_for_each_entry(p, &nfs_referral_count_list, list) { | ||
2690 | if (p->task == current) | ||
2691 | return p; | ||
2692 | } | ||
2693 | return NULL; | ||
2694 | } | ||
2695 | |||
2696 | #define NFS_MAX_NESTED_REFERRALS 2 | ||
2697 | |||
2698 | static int nfs_referral_loop_protect(void) | ||
2699 | { | ||
2700 | struct nfs_referral_count *p, *new; | ||
2701 | int ret = -ENOMEM; | ||
2702 | |||
2703 | new = kmalloc(sizeof(*new), GFP_KERNEL); | ||
2704 | if (!new) | ||
2705 | goto out; | ||
2706 | new->task = current; | ||
2707 | new->referral_count = 1; | ||
2708 | |||
2709 | ret = 0; | ||
2710 | spin_lock(&nfs_referral_count_list_lock); | ||
2711 | p = nfs_find_referral_count(); | ||
2712 | if (p != NULL) { | ||
2713 | if (p->referral_count >= NFS_MAX_NESTED_REFERRALS) | ||
2714 | ret = -ELOOP; | ||
2715 | else | ||
2716 | p->referral_count++; | ||
2717 | } else { | ||
2718 | list_add(&new->list, &nfs_referral_count_list); | ||
2719 | new = NULL; | ||
2720 | } | ||
2721 | spin_unlock(&nfs_referral_count_list_lock); | ||
2722 | kfree(new); | ||
2723 | out: | ||
2724 | return ret; | ||
2725 | } | ||
2726 | |||
2727 | static void nfs_referral_loop_unprotect(void) | ||
2728 | { | ||
2729 | struct nfs_referral_count *p; | ||
2730 | |||
2731 | spin_lock(&nfs_referral_count_list_lock); | ||
2732 | p = nfs_find_referral_count(); | ||
2733 | p->referral_count--; | ||
2734 | if (p->referral_count == 0) | ||
2735 | list_del(&p->list); | ||
2736 | else | ||
2737 | p = NULL; | ||
2738 | spin_unlock(&nfs_referral_count_list_lock); | ||
2739 | kfree(p); | ||
2740 | } | ||
2741 | |||
2676 | static int nfs_follow_remote_path(struct vfsmount *root_mnt, | 2742 | static int nfs_follow_remote_path(struct vfsmount *root_mnt, |
2677 | const char *export_path, struct vfsmount *mnt_target) | 2743 | const char *export_path, struct vfsmount *mnt_target) |
2678 | { | 2744 | { |
@@ -2690,9 +2756,14 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt, | |||
2690 | if (IS_ERR(ns_private)) | 2756 | if (IS_ERR(ns_private)) |
2691 | goto out_mntput; | 2757 | goto out_mntput; |
2692 | 2758 | ||
2759 | ret = nfs_referral_loop_protect(); | ||
2760 | if (ret != 0) | ||
2761 | goto out_put_mnt_ns; | ||
2762 | |||
2693 | ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, | 2763 | ret = vfs_path_lookup(root_mnt->mnt_root, root_mnt, |
2694 | export_path, LOOKUP_FOLLOW, nd); | 2764 | export_path, LOOKUP_FOLLOW, nd); |
2695 | 2765 | ||
2766 | nfs_referral_loop_unprotect(); | ||
2696 | put_mnt_ns(ns_private); | 2767 | put_mnt_ns(ns_private); |
2697 | 2768 | ||
2698 | if (ret != 0) | 2769 | if (ret != 0) |
@@ -2710,6 +2781,8 @@ static int nfs_follow_remote_path(struct vfsmount *root_mnt, | |||
2710 | kfree(nd); | 2781 | kfree(nd); |
2711 | down_write(&s->s_umount); | 2782 | down_write(&s->s_umount); |
2712 | return 0; | 2783 | return 0; |
2784 | out_put_mnt_ns: | ||
2785 | put_mnt_ns(ns_private); | ||
2713 | out_mntput: | 2786 | out_mntput: |
2714 | mntput(root_mnt); | 2787 | mntput(root_mnt); |
2715 | out_err: | 2788 | out_err: |