diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/mount.h | 9 | ||||
-rw-r--r-- | fs/namei.c | 6 | ||||
-rw-r--r-- | fs/namespace.c | 35 |
3 files changed, 49 insertions, 1 deletions
diff --git a/fs/mount.h b/fs/mount.h index 8f2a14ae38a2..8c6a2a651254 100644 --- a/fs/mount.h +++ b/fs/mount.h | |||
@@ -115,3 +115,12 @@ struct proc_mounts { | |||
115 | #define proc_mounts(p) (container_of((p), struct proc_mounts, m)) | 115 | #define proc_mounts(p) (container_of((p), struct proc_mounts, m)) |
116 | 116 | ||
117 | extern const struct seq_operations mounts_op; | 117 | extern const struct seq_operations mounts_op; |
118 | |||
119 | extern bool __is_local_mountpoint(struct dentry *dentry); | ||
120 | static inline bool is_local_mountpoint(struct dentry *dentry) | ||
121 | { | ||
122 | if (!d_mountpoint(dentry)) | ||
123 | return false; | ||
124 | |||
125 | return __is_local_mountpoint(dentry); | ||
126 | } | ||
diff --git a/fs/namei.c b/fs/namei.c index a7b05bf82d31..a3a14b033b0d 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -3565,6 +3565,8 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
3565 | mutex_lock(&dentry->d_inode->i_mutex); | 3565 | mutex_lock(&dentry->d_inode->i_mutex); |
3566 | 3566 | ||
3567 | error = -EBUSY; | 3567 | error = -EBUSY; |
3568 | if (is_local_mountpoint(dentry)) | ||
3569 | goto out; | ||
3568 | if (d_mountpoint(dentry)) | 3570 | if (d_mountpoint(dentry)) |
3569 | goto out; | 3571 | goto out; |
3570 | 3572 | ||
@@ -3681,7 +3683,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry, struct inode **delegate | |||
3681 | return -EPERM; | 3683 | return -EPERM; |
3682 | 3684 | ||
3683 | mutex_lock(&target->i_mutex); | 3685 | mutex_lock(&target->i_mutex); |
3684 | if (d_mountpoint(dentry)) | 3686 | if (is_local_mountpoint(dentry) || d_mountpoint(dentry)) |
3685 | error = -EBUSY; | 3687 | error = -EBUSY; |
3686 | else { | 3688 | else { |
3687 | error = security_inode_unlink(dir, dentry); | 3689 | error = security_inode_unlink(dir, dentry); |
@@ -4126,6 +4128,8 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
4126 | mutex_lock(&target->i_mutex); | 4128 | mutex_lock(&target->i_mutex); |
4127 | 4129 | ||
4128 | error = -EBUSY; | 4130 | error = -EBUSY; |
4131 | if (is_local_mountpoint(old_dentry) || is_local_mountpoint(new_dentry)) | ||
4132 | goto out; | ||
4129 | if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry)) | 4133 | if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry)) |
4130 | goto out; | 4134 | goto out; |
4131 | 4135 | ||
diff --git a/fs/namespace.c b/fs/namespace.c index 044134315f93..77ffdb82f63f 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -667,6 +667,41 @@ struct vfsmount *lookup_mnt(struct path *path) | |||
667 | return m; | 667 | return m; |
668 | } | 668 | } |
669 | 669 | ||
670 | /* | ||
671 | * __is_local_mountpoint - Test to see if dentry is a mountpoint in the | ||
672 | * current mount namespace. | ||
673 | * | ||
674 | * The common case is dentries are not mountpoints at all and that | ||
675 | * test is handled inline. For the slow case when we are actually | ||
676 | * dealing with a mountpoint of some kind, walk through all of the | ||
677 | * mounts in the current mount namespace and test to see if the dentry | ||
678 | * is a mountpoint. | ||
679 | * | ||
680 | * The mount_hashtable is not usable in the context because we | ||
681 | * need to identify all mounts that may be in the current mount | ||
682 | * namespace not just a mount that happens to have some specified | ||
683 | * parent mount. | ||
684 | */ | ||
685 | bool __is_local_mountpoint(struct dentry *dentry) | ||
686 | { | ||
687 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
688 | struct mount *mnt; | ||
689 | bool is_covered = false; | ||
690 | |||
691 | if (!d_mountpoint(dentry)) | ||
692 | goto out; | ||
693 | |||
694 | down_read(&namespace_sem); | ||
695 | list_for_each_entry(mnt, &ns->list, mnt_list) { | ||
696 | is_covered = (mnt->mnt_mountpoint == dentry); | ||
697 | if (is_covered) | ||
698 | break; | ||
699 | } | ||
700 | up_read(&namespace_sem); | ||
701 | out: | ||
702 | return is_covered; | ||
703 | } | ||
704 | |||
670 | static struct mountpoint *new_mountpoint(struct dentry *dentry) | 705 | static struct mountpoint *new_mountpoint(struct dentry *dentry) |
671 | { | 706 | { |
672 | struct hlist_head *chain = mp_hash(dentry); | 707 | struct hlist_head *chain = mp_hash(dentry); |