aboutsummaryrefslogtreecommitdiffstats
path: root/fs/pnode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/pnode.c')
-rw-r--r--fs/pnode.c81
1 files changed, 72 insertions, 9 deletions
diff --git a/fs/pnode.c b/fs/pnode.c
index f73eba24f1d1..3e266c5a3071 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -17,6 +17,16 @@ static inline struct vfsmount *next_peer(struct vfsmount *p)
17 return list_entry(p->mnt_share.next, struct vfsmount, mnt_share); 17 return list_entry(p->mnt_share.next, struct vfsmount, mnt_share);
18} 18}
19 19
20static inline struct vfsmount *first_slave(struct vfsmount *p)
21{
22 return list_entry(p->mnt_slave_list.next, struct vfsmount, mnt_slave);
23}
24
25static inline struct vfsmount *next_slave(struct vfsmount *p)
26{
27 return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave);
28}
29
20static int do_make_slave(struct vfsmount *mnt) 30static int do_make_slave(struct vfsmount *mnt)
21{ 31{
22 struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; 32 struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master;
@@ -83,10 +93,64 @@ void change_mnt_propagation(struct vfsmount *mnt, int type)
83static struct vfsmount *propagation_next(struct vfsmount *m, 93static struct vfsmount *propagation_next(struct vfsmount *m,
84 struct vfsmount *origin) 94 struct vfsmount *origin)
85{ 95{
86 m = next_peer(m); 96 /* are there any slaves of this mount? */
87 if (m == origin) 97 if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
88 return NULL; 98 return first_slave(m);
89 return m; 99
100 while (1) {
101 struct vfsmount *next;
102 struct vfsmount *master = m->mnt_master;
103
104 if ( master == origin->mnt_master ) {
105 next = next_peer(m);
106 return ((next == origin) ? NULL : next);
107 } else if (m->mnt_slave.next != &master->mnt_slave_list)
108 return next_slave(m);
109
110 /* back at master */
111 m = master;
112 }
113}
114
115/*
116 * return the source mount to be used for cloning
117 *
118 * @dest the current destination mount
119 * @last_dest the last seen destination mount
120 * @last_src the last seen source mount
121 * @type return CL_SLAVE if the new mount has to be
122 * cloned as a slave.
123 */
124static struct vfsmount *get_source(struct vfsmount *dest,
125 struct vfsmount *last_dest,
126 struct vfsmount *last_src,
127 int *type)
128{
129 struct vfsmount *p_last_src = NULL;
130 struct vfsmount *p_last_dest = NULL;
131 *type = CL_PROPAGATION;;
132
133 if (IS_MNT_SHARED(dest))
134 *type |= CL_MAKE_SHARED;
135
136 while (last_dest != dest->mnt_master) {
137 p_last_dest = last_dest;
138 p_last_src = last_src;
139 last_dest = last_dest->mnt_master;
140 last_src = last_src->mnt_master;
141 }
142
143 if (p_last_dest) {
144 do {
145 p_last_dest = next_peer(p_last_dest);
146 } while (IS_MNT_NEW(p_last_dest));
147 }
148
149 if (dest != p_last_dest) {
150 *type |= CL_SLAVE;
151 return last_src;
152 } else
153 return p_last_src;
90} 154}
91 155
92/* 156/*
@@ -114,16 +178,15 @@ int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry,
114 178
115 for (m = propagation_next(dest_mnt, dest_mnt); m; 179 for (m = propagation_next(dest_mnt, dest_mnt); m;
116 m = propagation_next(m, dest_mnt)) { 180 m = propagation_next(m, dest_mnt)) {
117 int type = CL_PROPAGATION; 181 int type;
182 struct vfsmount *source;
118 183
119 if (IS_MNT_NEW(m)) 184 if (IS_MNT_NEW(m))
120 continue; 185 continue;
121 186
122 if (IS_MNT_SHARED(m)) 187 source = get_source(m, prev_dest_mnt, prev_src_mnt, &type);
123 type |= CL_MAKE_SHARED;
124 188
125 if (!(child = copy_tree(source_mnt, source_mnt->mnt_root, 189 if (!(child = copy_tree(source, source->mnt_root, type))) {
126 type))) {
127 ret = -ENOMEM; 190 ret = -ENOMEM;
128 list_splice(tree_list, tmp_list.prev); 191 list_splice(tree_list, tmp_list.prev);
129 goto out; 192 goto out;