aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/super.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2010-04-16 16:22:52 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-05-14 15:09:28 -0400
commitce587e07ba2e25b5c9d286849885b82676661f3e (patch)
tree5bb91822d82fa9a26f7846edcc48bf01fad4411f /fs/nfs/super.c
parent6e94d62993cb79397856f3330577917ca79cffa2 (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.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 98d1ab8bf8f..50c6c282ba4 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
2676struct nfs_referral_count {
2677 struct list_head list;
2678 const struct task_struct *task;
2679 unsigned int referral_count;
2680};
2681
2682static LIST_HEAD(nfs_referral_count_list);
2683static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
2684
2685static 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
2698static 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);
2723out:
2724 return ret;
2725}
2726
2727static 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
2676static int nfs_follow_remote_path(struct vfsmount *root_mnt, 2742static 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;
2784out_put_mnt_ns:
2785 put_mnt_ns(ns_private);
2713out_mntput: 2786out_mntput:
2714 mntput(root_mnt); 2787 mntput(root_mnt);
2715out_err: 2788out_err: