diff options
Diffstat (limited to 'fs/pnode.c')
| -rw-r--r-- | fs/pnode.c | 60 |
1 files changed, 57 insertions, 3 deletions
diff --git a/fs/pnode.c b/fs/pnode.c index 1d8f5447f3f7..8d5f392ec3d3 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <linux/mnt_namespace.h> | 9 | #include <linux/mnt_namespace.h> |
| 10 | #include <linux/mount.h> | 10 | #include <linux/mount.h> |
| 11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
| 12 | #include "internal.h" | ||
| 12 | #include "pnode.h" | 13 | #include "pnode.h" |
| 13 | 14 | ||
| 14 | /* return the next shared peer mount of @p */ | 15 | /* return the next shared peer mount of @p */ |
| @@ -27,6 +28,57 @@ static inline struct vfsmount *next_slave(struct vfsmount *p) | |||
| 27 | return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); | 28 | return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); |
| 28 | } | 29 | } |
| 29 | 30 | ||
| 31 | /* | ||
| 32 | * Return true if path is reachable from root | ||
| 33 | * | ||
| 34 | * namespace_sem is held, and mnt is attached | ||
| 35 | */ | ||
| 36 | static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, | ||
| 37 | const struct path *root) | ||
| 38 | { | ||
| 39 | while (mnt != root->mnt && mnt->mnt_parent != mnt) { | ||
| 40 | dentry = mnt->mnt_mountpoint; | ||
| 41 | mnt = mnt->mnt_parent; | ||
| 42 | } | ||
| 43 | return mnt == root->mnt && is_subdir(dentry, root->dentry); | ||
| 44 | } | ||
| 45 | |||
| 46 | static struct vfsmount *get_peer_under_root(struct vfsmount *mnt, | ||
| 47 | struct mnt_namespace *ns, | ||
| 48 | const struct path *root) | ||
| 49 | { | ||
| 50 | struct vfsmount *m = mnt; | ||
| 51 | |||
| 52 | do { | ||
| 53 | /* Check the namespace first for optimization */ | ||
| 54 | if (m->mnt_ns == ns && is_path_reachable(m, m->mnt_root, root)) | ||
| 55 | return m; | ||
| 56 | |||
| 57 | m = next_peer(m); | ||
| 58 | } while (m != mnt); | ||
| 59 | |||
| 60 | return NULL; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Get ID of closest dominating peer group having a representative | ||
| 65 | * under the given root. | ||
| 66 | * | ||
| 67 | * Caller must hold namespace_sem | ||
| 68 | */ | ||
| 69 | int get_dominating_id(struct vfsmount *mnt, const struct path *root) | ||
| 70 | { | ||
| 71 | struct vfsmount *m; | ||
| 72 | |||
| 73 | for (m = mnt->mnt_master; m != NULL; m = m->mnt_master) { | ||
| 74 | struct vfsmount *d = get_peer_under_root(m, mnt->mnt_ns, root); | ||
| 75 | if (d) | ||
| 76 | return d->mnt_group_id; | ||
| 77 | } | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 30 | static int do_make_slave(struct vfsmount *mnt) | 82 | static int do_make_slave(struct vfsmount *mnt) |
| 31 | { | 83 | { |
| 32 | struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; | 84 | struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; |
| @@ -45,7 +97,11 @@ static int do_make_slave(struct vfsmount *mnt) | |||
| 45 | if (peer_mnt == mnt) | 97 | if (peer_mnt == mnt) |
| 46 | peer_mnt = NULL; | 98 | peer_mnt = NULL; |
| 47 | } | 99 | } |
| 100 | if (IS_MNT_SHARED(mnt) && list_empty(&mnt->mnt_share)) | ||
| 101 | mnt_release_group_id(mnt); | ||
| 102 | |||
| 48 | list_del_init(&mnt->mnt_share); | 103 | list_del_init(&mnt->mnt_share); |
| 104 | mnt->mnt_group_id = 0; | ||
| 49 | 105 | ||
| 50 | if (peer_mnt) | 106 | if (peer_mnt) |
| 51 | master = peer_mnt; | 107 | master = peer_mnt; |
| @@ -67,7 +123,6 @@ static int do_make_slave(struct vfsmount *mnt) | |||
| 67 | } | 123 | } |
| 68 | mnt->mnt_master = master; | 124 | mnt->mnt_master = master; |
| 69 | CLEAR_MNT_SHARED(mnt); | 125 | CLEAR_MNT_SHARED(mnt); |
| 70 | INIT_LIST_HEAD(&mnt->mnt_slave_list); | ||
| 71 | return 0; | 126 | return 0; |
| 72 | } | 127 | } |
| 73 | 128 | ||
| @@ -211,8 +266,7 @@ int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry, | |||
| 211 | out: | 266 | out: |
| 212 | spin_lock(&vfsmount_lock); | 267 | spin_lock(&vfsmount_lock); |
| 213 | while (!list_empty(&tmp_list)) { | 268 | while (!list_empty(&tmp_list)) { |
| 214 | child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); | 269 | child = list_first_entry(&tmp_list, struct vfsmount, mnt_hash); |
| 215 | list_del_init(&child->mnt_hash); | ||
| 216 | umount_tree(child, 0, &umount_list); | 270 | umount_tree(child, 0, &umount_list); |
| 217 | } | 271 | } |
| 218 | spin_unlock(&vfsmount_lock); | 272 | spin_unlock(&vfsmount_lock); |
