diff options
Diffstat (limited to 'fs/pnode.c')
-rw-r--r-- | fs/pnode.c | 198 |
1 files changed, 119 insertions, 79 deletions
diff --git a/fs/pnode.c b/fs/pnode.c index 88396df725b4..302bf22c4a30 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
@@ -164,46 +164,94 @@ static struct mount *propagation_next(struct mount *m, | |||
164 | } | 164 | } |
165 | } | 165 | } |
166 | 166 | ||
167 | /* | 167 | static struct mount *next_group(struct mount *m, struct mount *origin) |
168 | * return the source mount to be used for cloning | ||
169 | * | ||
170 | * @dest the current destination mount | ||
171 | * @last_dest the last seen destination mount | ||
172 | * @last_src the last seen source mount | ||
173 | * @type return CL_SLAVE if the new mount has to be | ||
174 | * cloned as a slave. | ||
175 | */ | ||
176 | static struct mount *get_source(struct mount *dest, | ||
177 | struct mount *last_dest, | ||
178 | struct mount *last_src, | ||
179 | int *type) | ||
180 | { | 168 | { |
181 | struct mount *p_last_src = NULL; | 169 | while (1) { |
182 | struct mount *p_last_dest = NULL; | 170 | while (1) { |
183 | 171 | struct mount *next; | |
184 | while (last_dest != dest->mnt_master) { | 172 | if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list)) |
185 | p_last_dest = last_dest; | 173 | return first_slave(m); |
186 | p_last_src = last_src; | 174 | next = next_peer(m); |
187 | last_dest = last_dest->mnt_master; | 175 | if (m->mnt_group_id == origin->mnt_group_id) { |
188 | last_src = last_src->mnt_master; | 176 | if (next == origin) |
177 | return NULL; | ||
178 | } else if (m->mnt_slave.next != &next->mnt_slave) | ||
179 | break; | ||
180 | m = next; | ||
181 | } | ||
182 | /* m is the last peer */ | ||
183 | while (1) { | ||
184 | struct mount *master = m->mnt_master; | ||
185 | if (m->mnt_slave.next != &master->mnt_slave_list) | ||
186 | return next_slave(m); | ||
187 | m = next_peer(master); | ||
188 | if (master->mnt_group_id == origin->mnt_group_id) | ||
189 | break; | ||
190 | if (master->mnt_slave.next == &m->mnt_slave) | ||
191 | break; | ||
192 | m = master; | ||
193 | } | ||
194 | if (m == origin) | ||
195 | return NULL; | ||
189 | } | 196 | } |
197 | } | ||
190 | 198 | ||
191 | if (p_last_dest) { | 199 | /* all accesses are serialized by namespace_sem */ |
192 | do { | 200 | static struct user_namespace *user_ns; |
193 | p_last_dest = next_peer(p_last_dest); | 201 | static struct mount *last_dest, *last_source, *dest_master; |
194 | } while (IS_MNT_NEW(p_last_dest)); | 202 | static struct mountpoint *mp; |
195 | /* is that a peer of the earlier? */ | 203 | static struct hlist_head *list; |
196 | if (dest == p_last_dest) { | 204 | |
197 | *type = CL_MAKE_SHARED; | 205 | static int propagate_one(struct mount *m) |
198 | return p_last_src; | 206 | { |
207 | struct mount *child; | ||
208 | int type; | ||
209 | /* skip ones added by this propagate_mnt() */ | ||
210 | if (IS_MNT_NEW(m)) | ||
211 | return 0; | ||
212 | /* skip if mountpoint isn't covered by it */ | ||
213 | if (!is_subdir(mp->m_dentry, m->mnt.mnt_root)) | ||
214 | return 0; | ||
215 | if (m->mnt_group_id == last_dest->mnt_group_id) { | ||
216 | type = CL_MAKE_SHARED; | ||
217 | } else { | ||
218 | struct mount *n, *p; | ||
219 | for (n = m; ; n = p) { | ||
220 | p = n->mnt_master; | ||
221 | if (p == dest_master || IS_MNT_MARKED(p)) { | ||
222 | while (last_dest->mnt_master != p) { | ||
223 | last_source = last_source->mnt_master; | ||
224 | last_dest = last_source->mnt_parent; | ||
225 | } | ||
226 | if (n->mnt_group_id != last_dest->mnt_group_id) { | ||
227 | last_source = last_source->mnt_master; | ||
228 | last_dest = last_source->mnt_parent; | ||
229 | } | ||
230 | break; | ||
231 | } | ||
199 | } | 232 | } |
233 | type = CL_SLAVE; | ||
234 | /* beginning of peer group among the slaves? */ | ||
235 | if (IS_MNT_SHARED(m)) | ||
236 | type |= CL_MAKE_SHARED; | ||
200 | } | 237 | } |
201 | /* slave of the earlier, then */ | 238 | |
202 | *type = CL_SLAVE; | 239 | /* Notice when we are propagating across user namespaces */ |
203 | /* beginning of peer group among the slaves? */ | 240 | if (m->mnt_ns->user_ns != user_ns) |
204 | if (IS_MNT_SHARED(dest)) | 241 | type |= CL_UNPRIVILEGED; |
205 | *type |= CL_MAKE_SHARED; | 242 | child = copy_tree(last_source, last_source->mnt.mnt_root, type); |
206 | return last_src; | 243 | if (IS_ERR(child)) |
244 | return PTR_ERR(child); | ||
245 | mnt_set_mountpoint(m, mp, child); | ||
246 | last_dest = m; | ||
247 | last_source = child; | ||
248 | if (m->mnt_master != dest_master) { | ||
249 | read_seqlock_excl(&mount_lock); | ||
250 | SET_MNT_MARK(m->mnt_master); | ||
251 | read_sequnlock_excl(&mount_lock); | ||
252 | } | ||
253 | hlist_add_head(&child->mnt_hash, list); | ||
254 | return 0; | ||
207 | } | 255 | } |
208 | 256 | ||
209 | /* | 257 | /* |
@@ -222,56 +270,48 @@ static struct mount *get_source(struct mount *dest, | |||
222 | int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp, | 270 | int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp, |
223 | struct mount *source_mnt, struct hlist_head *tree_list) | 271 | struct mount *source_mnt, struct hlist_head *tree_list) |
224 | { | 272 | { |
225 | struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; | 273 | struct mount *m, *n; |
226 | struct mount *m, *child; | ||
227 | int ret = 0; | 274 | int ret = 0; |
228 | struct mount *prev_dest_mnt = dest_mnt; | 275 | |
229 | struct mount *prev_src_mnt = source_mnt; | 276 | /* |
230 | HLIST_HEAD(tmp_list); | 277 | * we don't want to bother passing tons of arguments to |
231 | 278 | * propagate_one(); everything is serialized by namespace_sem, | |
232 | for (m = propagation_next(dest_mnt, dest_mnt); m; | 279 | * so globals will do just fine. |
233 | m = propagation_next(m, dest_mnt)) { | 280 | */ |
234 | int type; | 281 | user_ns = current->nsproxy->mnt_ns->user_ns; |
235 | struct mount *source; | 282 | last_dest = dest_mnt; |
236 | 283 | last_source = source_mnt; | |
237 | if (IS_MNT_NEW(m)) | 284 | mp = dest_mp; |
238 | continue; | 285 | list = tree_list; |
239 | 286 | dest_master = dest_mnt->mnt_master; | |
240 | source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); | 287 | |
241 | 288 | /* all peers of dest_mnt, except dest_mnt itself */ | |
242 | /* Notice when we are propagating across user namespaces */ | 289 | for (n = next_peer(dest_mnt); n != dest_mnt; n = next_peer(n)) { |
243 | if (m->mnt_ns->user_ns != user_ns) | 290 | ret = propagate_one(n); |
244 | type |= CL_UNPRIVILEGED; | 291 | if (ret) |
245 | |||
246 | child = copy_tree(source, source->mnt.mnt_root, type); | ||
247 | if (IS_ERR(child)) { | ||
248 | ret = PTR_ERR(child); | ||
249 | tmp_list = *tree_list; | ||
250 | tmp_list.first->pprev = &tmp_list.first; | ||
251 | INIT_HLIST_HEAD(tree_list); | ||
252 | goto out; | 292 | goto out; |
253 | } | 293 | } |
254 | 294 | ||
255 | if (is_subdir(dest_mp->m_dentry, m->mnt.mnt_root)) { | 295 | /* all slave groups */ |
256 | mnt_set_mountpoint(m, dest_mp, child); | 296 | for (m = next_group(dest_mnt, dest_mnt); m; |
257 | hlist_add_head(&child->mnt_hash, tree_list); | 297 | m = next_group(m, dest_mnt)) { |
258 | } else { | 298 | /* everything in that slave group */ |
259 | /* | 299 | n = m; |
260 | * This can happen if the parent mount was bind mounted | 300 | do { |
261 | * on some subdirectory of a shared/slave mount. | 301 | ret = propagate_one(n); |
262 | */ | 302 | if (ret) |
263 | hlist_add_head(&child->mnt_hash, &tmp_list); | 303 | goto out; |
264 | } | 304 | n = next_peer(n); |
265 | prev_dest_mnt = m; | 305 | } while (n != m); |
266 | prev_src_mnt = child; | ||
267 | } | 306 | } |
268 | out: | 307 | out: |
269 | lock_mount_hash(); | 308 | read_seqlock_excl(&mount_lock); |
270 | while (!hlist_empty(&tmp_list)) { | 309 | hlist_for_each_entry(n, tree_list, mnt_hash) { |
271 | child = hlist_entry(tmp_list.first, struct mount, mnt_hash); | 310 | m = n->mnt_parent; |
272 | umount_tree(child, 0); | 311 | if (m->mnt_master != dest_mnt->mnt_master) |
312 | CLEAR_MNT_MARK(m->mnt_master); | ||
273 | } | 313 | } |
274 | unlock_mount_hash(); | 314 | read_sequnlock_excl(&mount_lock); |
275 | return ret; | 315 | return ret; |
276 | } | 316 | } |
277 | 317 | ||