diff options
author | Steve French <sfrench@us.ibm.com> | 2008-04-24 11:26:50 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-04-24 11:26:50 -0400 |
commit | 36d99df2fb474222ab47fbe8ae7385661033223b (patch) | |
tree | 962e068491b752a944f61c454fad3f8619a1ea3f /fs/namespace.c | |
parent | 076d8423a98659a92837b07aa494cb74bfefe77c (diff) | |
parent | 3dc5063786b273f1aee545844f6bd4e9651ebffe (diff) |
Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 647 |
1 files changed, 579 insertions, 68 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 94f026ec990a..0505fb61aa74 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/quotaops.h> | 17 | #include <linux/quotaops.h> |
18 | #include <linux/acct.h> | 18 | #include <linux/acct.h> |
19 | #include <linux/capability.h> | 19 | #include <linux/capability.h> |
20 | #include <linux/cpumask.h> | ||
20 | #include <linux/module.h> | 21 | #include <linux/module.h> |
21 | #include <linux/sysfs.h> | 22 | #include <linux/sysfs.h> |
22 | #include <linux/seq_file.h> | 23 | #include <linux/seq_file.h> |
@@ -26,6 +27,7 @@ | |||
26 | #include <linux/mount.h> | 27 | #include <linux/mount.h> |
27 | #include <linux/ramfs.h> | 28 | #include <linux/ramfs.h> |
28 | #include <linux/log2.h> | 29 | #include <linux/log2.h> |
30 | #include <linux/idr.h> | ||
29 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
30 | #include <asm/unistd.h> | 32 | #include <asm/unistd.h> |
31 | #include "pnode.h" | 33 | #include "pnode.h" |
@@ -38,6 +40,8 @@ | |||
38 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); | 40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); |
39 | 41 | ||
40 | static int event; | 42 | static int event; |
43 | static DEFINE_IDA(mnt_id_ida); | ||
44 | static DEFINE_IDA(mnt_group_ida); | ||
41 | 45 | ||
42 | static struct list_head *mount_hashtable __read_mostly; | 46 | static struct list_head *mount_hashtable __read_mostly; |
43 | static struct kmem_cache *mnt_cache __read_mostly; | 47 | static struct kmem_cache *mnt_cache __read_mostly; |
@@ -55,10 +59,65 @@ static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |||
55 | return tmp & (HASH_SIZE - 1); | 59 | return tmp & (HASH_SIZE - 1); |
56 | } | 60 | } |
57 | 61 | ||
62 | #define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) | ||
63 | |||
64 | /* allocation is serialized by namespace_sem */ | ||
65 | static int mnt_alloc_id(struct vfsmount *mnt) | ||
66 | { | ||
67 | int res; | ||
68 | |||
69 | retry: | ||
70 | ida_pre_get(&mnt_id_ida, GFP_KERNEL); | ||
71 | spin_lock(&vfsmount_lock); | ||
72 | res = ida_get_new(&mnt_id_ida, &mnt->mnt_id); | ||
73 | spin_unlock(&vfsmount_lock); | ||
74 | if (res == -EAGAIN) | ||
75 | goto retry; | ||
76 | |||
77 | return res; | ||
78 | } | ||
79 | |||
80 | static void mnt_free_id(struct vfsmount *mnt) | ||
81 | { | ||
82 | spin_lock(&vfsmount_lock); | ||
83 | ida_remove(&mnt_id_ida, mnt->mnt_id); | ||
84 | spin_unlock(&vfsmount_lock); | ||
85 | } | ||
86 | |||
87 | /* | ||
88 | * Allocate a new peer group ID | ||
89 | * | ||
90 | * mnt_group_ida is protected by namespace_sem | ||
91 | */ | ||
92 | static int mnt_alloc_group_id(struct vfsmount *mnt) | ||
93 | { | ||
94 | if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL)) | ||
95 | return -ENOMEM; | ||
96 | |||
97 | return ida_get_new_above(&mnt_group_ida, 1, &mnt->mnt_group_id); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * Release a peer group ID | ||
102 | */ | ||
103 | void mnt_release_group_id(struct vfsmount *mnt) | ||
104 | { | ||
105 | ida_remove(&mnt_group_ida, mnt->mnt_group_id); | ||
106 | mnt->mnt_group_id = 0; | ||
107 | } | ||
108 | |||
58 | struct vfsmount *alloc_vfsmnt(const char *name) | 109 | struct vfsmount *alloc_vfsmnt(const char *name) |
59 | { | 110 | { |
60 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); | 111 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); |
61 | if (mnt) { | 112 | if (mnt) { |
113 | int err; | ||
114 | |||
115 | err = mnt_alloc_id(mnt); | ||
116 | if (err) { | ||
117 | kmem_cache_free(mnt_cache, mnt); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
62 | atomic_set(&mnt->mnt_count, 1); | 121 | atomic_set(&mnt->mnt_count, 1); |
63 | INIT_LIST_HEAD(&mnt->mnt_hash); | 122 | INIT_LIST_HEAD(&mnt->mnt_hash); |
64 | INIT_LIST_HEAD(&mnt->mnt_child); | 123 | INIT_LIST_HEAD(&mnt->mnt_child); |
@@ -68,6 +127,7 @@ struct vfsmount *alloc_vfsmnt(const char *name) | |||
68 | INIT_LIST_HEAD(&mnt->mnt_share); | 127 | INIT_LIST_HEAD(&mnt->mnt_share); |
69 | INIT_LIST_HEAD(&mnt->mnt_slave_list); | 128 | INIT_LIST_HEAD(&mnt->mnt_slave_list); |
70 | INIT_LIST_HEAD(&mnt->mnt_slave); | 129 | INIT_LIST_HEAD(&mnt->mnt_slave); |
130 | atomic_set(&mnt->__mnt_writers, 0); | ||
71 | if (name) { | 131 | if (name) { |
72 | int size = strlen(name) + 1; | 132 | int size = strlen(name) + 1; |
73 | char *newname = kmalloc(size, GFP_KERNEL); | 133 | char *newname = kmalloc(size, GFP_KERNEL); |
@@ -80,6 +140,263 @@ struct vfsmount *alloc_vfsmnt(const char *name) | |||
80 | return mnt; | 140 | return mnt; |
81 | } | 141 | } |
82 | 142 | ||
143 | /* | ||
144 | * Most r/o checks on a fs are for operations that take | ||
145 | * discrete amounts of time, like a write() or unlink(). | ||
146 | * We must keep track of when those operations start | ||
147 | * (for permission checks) and when they end, so that | ||
148 | * we can determine when writes are able to occur to | ||
149 | * a filesystem. | ||
150 | */ | ||
151 | /* | ||
152 | * __mnt_is_readonly: check whether a mount is read-only | ||
153 | * @mnt: the mount to check for its write status | ||
154 | * | ||
155 | * This shouldn't be used directly ouside of the VFS. | ||
156 | * It does not guarantee that the filesystem will stay | ||
157 | * r/w, just that it is right *now*. This can not and | ||
158 | * should not be used in place of IS_RDONLY(inode). | ||
159 | * mnt_want/drop_write() will _keep_ the filesystem | ||
160 | * r/w. | ||
161 | */ | ||
162 | int __mnt_is_readonly(struct vfsmount *mnt) | ||
163 | { | ||
164 | if (mnt->mnt_flags & MNT_READONLY) | ||
165 | return 1; | ||
166 | if (mnt->mnt_sb->s_flags & MS_RDONLY) | ||
167 | return 1; | ||
168 | return 0; | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(__mnt_is_readonly); | ||
171 | |||
172 | struct mnt_writer { | ||
173 | /* | ||
174 | * If holding multiple instances of this lock, they | ||
175 | * must be ordered by cpu number. | ||
176 | */ | ||
177 | spinlock_t lock; | ||
178 | struct lock_class_key lock_class; /* compiles out with !lockdep */ | ||
179 | unsigned long count; | ||
180 | struct vfsmount *mnt; | ||
181 | } ____cacheline_aligned_in_smp; | ||
182 | static DEFINE_PER_CPU(struct mnt_writer, mnt_writers); | ||
183 | |||
184 | static int __init init_mnt_writers(void) | ||
185 | { | ||
186 | int cpu; | ||
187 | for_each_possible_cpu(cpu) { | ||
188 | struct mnt_writer *writer = &per_cpu(mnt_writers, cpu); | ||
189 | spin_lock_init(&writer->lock); | ||
190 | lockdep_set_class(&writer->lock, &writer->lock_class); | ||
191 | writer->count = 0; | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | fs_initcall(init_mnt_writers); | ||
196 | |||
197 | static void unlock_mnt_writers(void) | ||
198 | { | ||
199 | int cpu; | ||
200 | struct mnt_writer *cpu_writer; | ||
201 | |||
202 | for_each_possible_cpu(cpu) { | ||
203 | cpu_writer = &per_cpu(mnt_writers, cpu); | ||
204 | spin_unlock(&cpu_writer->lock); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | static inline void __clear_mnt_count(struct mnt_writer *cpu_writer) | ||
209 | { | ||
210 | if (!cpu_writer->mnt) | ||
211 | return; | ||
212 | /* | ||
213 | * This is in case anyone ever leaves an invalid, | ||
214 | * old ->mnt and a count of 0. | ||
215 | */ | ||
216 | if (!cpu_writer->count) | ||
217 | return; | ||
218 | atomic_add(cpu_writer->count, &cpu_writer->mnt->__mnt_writers); | ||
219 | cpu_writer->count = 0; | ||
220 | } | ||
221 | /* | ||
222 | * must hold cpu_writer->lock | ||
223 | */ | ||
224 | static inline void use_cpu_writer_for_mount(struct mnt_writer *cpu_writer, | ||
225 | struct vfsmount *mnt) | ||
226 | { | ||
227 | if (cpu_writer->mnt == mnt) | ||
228 | return; | ||
229 | __clear_mnt_count(cpu_writer); | ||
230 | cpu_writer->mnt = mnt; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * Most r/o checks on a fs are for operations that take | ||
235 | * discrete amounts of time, like a write() or unlink(). | ||
236 | * We must keep track of when those operations start | ||
237 | * (for permission checks) and when they end, so that | ||
238 | * we can determine when writes are able to occur to | ||
239 | * a filesystem. | ||
240 | */ | ||
241 | /** | ||
242 | * mnt_want_write - get write access to a mount | ||
243 | * @mnt: the mount on which to take a write | ||
244 | * | ||
245 | * This tells the low-level filesystem that a write is | ||
246 | * about to be performed to it, and makes sure that | ||
247 | * writes are allowed before returning success. When | ||
248 | * the write operation is finished, mnt_drop_write() | ||
249 | * must be called. This is effectively a refcount. | ||
250 | */ | ||
251 | int mnt_want_write(struct vfsmount *mnt) | ||
252 | { | ||
253 | int ret = 0; | ||
254 | struct mnt_writer *cpu_writer; | ||
255 | |||
256 | cpu_writer = &get_cpu_var(mnt_writers); | ||
257 | spin_lock(&cpu_writer->lock); | ||
258 | if (__mnt_is_readonly(mnt)) { | ||
259 | ret = -EROFS; | ||
260 | goto out; | ||
261 | } | ||
262 | use_cpu_writer_for_mount(cpu_writer, mnt); | ||
263 | cpu_writer->count++; | ||
264 | out: | ||
265 | spin_unlock(&cpu_writer->lock); | ||
266 | put_cpu_var(mnt_writers); | ||
267 | return ret; | ||
268 | } | ||
269 | EXPORT_SYMBOL_GPL(mnt_want_write); | ||
270 | |||
271 | static void lock_mnt_writers(void) | ||
272 | { | ||
273 | int cpu; | ||
274 | struct mnt_writer *cpu_writer; | ||
275 | |||
276 | for_each_possible_cpu(cpu) { | ||
277 | cpu_writer = &per_cpu(mnt_writers, cpu); | ||
278 | spin_lock(&cpu_writer->lock); | ||
279 | __clear_mnt_count(cpu_writer); | ||
280 | cpu_writer->mnt = NULL; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | * These per-cpu write counts are not guaranteed to have | ||
286 | * matched increments and decrements on any given cpu. | ||
287 | * A file open()ed for write on one cpu and close()d on | ||
288 | * another cpu will imbalance this count. Make sure it | ||
289 | * does not get too far out of whack. | ||
290 | */ | ||
291 | static void handle_write_count_underflow(struct vfsmount *mnt) | ||
292 | { | ||
293 | if (atomic_read(&mnt->__mnt_writers) >= | ||
294 | MNT_WRITER_UNDERFLOW_LIMIT) | ||
295 | return; | ||
296 | /* | ||
297 | * It isn't necessary to hold all of the locks | ||
298 | * at the same time, but doing it this way makes | ||
299 | * us share a lot more code. | ||
300 | */ | ||
301 | lock_mnt_writers(); | ||
302 | /* | ||
303 | * vfsmount_lock is for mnt_flags. | ||
304 | */ | ||
305 | spin_lock(&vfsmount_lock); | ||
306 | /* | ||
307 | * If coalescing the per-cpu writer counts did not | ||
308 | * get us back to a positive writer count, we have | ||
309 | * a bug. | ||
310 | */ | ||
311 | if ((atomic_read(&mnt->__mnt_writers) < 0) && | ||
312 | !(mnt->mnt_flags & MNT_IMBALANCED_WRITE_COUNT)) { | ||
313 | printk(KERN_DEBUG "leak detected on mount(%p) writers " | ||
314 | "count: %d\n", | ||
315 | mnt, atomic_read(&mnt->__mnt_writers)); | ||
316 | WARN_ON(1); | ||
317 | /* use the flag to keep the dmesg spam down */ | ||
318 | mnt->mnt_flags |= MNT_IMBALANCED_WRITE_COUNT; | ||
319 | } | ||
320 | spin_unlock(&vfsmount_lock); | ||
321 | unlock_mnt_writers(); | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * mnt_drop_write - give up write access to a mount | ||
326 | * @mnt: the mount on which to give up write access | ||
327 | * | ||
328 | * Tells the low-level filesystem that we are done | ||
329 | * performing writes to it. Must be matched with | ||
330 | * mnt_want_write() call above. | ||
331 | */ | ||
332 | void mnt_drop_write(struct vfsmount *mnt) | ||
333 | { | ||
334 | int must_check_underflow = 0; | ||
335 | struct mnt_writer *cpu_writer; | ||
336 | |||
337 | cpu_writer = &get_cpu_var(mnt_writers); | ||
338 | spin_lock(&cpu_writer->lock); | ||
339 | |||
340 | use_cpu_writer_for_mount(cpu_writer, mnt); | ||
341 | if (cpu_writer->count > 0) { | ||
342 | cpu_writer->count--; | ||
343 | } else { | ||
344 | must_check_underflow = 1; | ||
345 | atomic_dec(&mnt->__mnt_writers); | ||
346 | } | ||
347 | |||
348 | spin_unlock(&cpu_writer->lock); | ||
349 | /* | ||
350 | * Logically, we could call this each time, | ||
351 | * but the __mnt_writers cacheline tends to | ||
352 | * be cold, and makes this expensive. | ||
353 | */ | ||
354 | if (must_check_underflow) | ||
355 | handle_write_count_underflow(mnt); | ||
356 | /* | ||
357 | * This could be done right after the spinlock | ||
358 | * is taken because the spinlock keeps us on | ||
359 | * the cpu, and disables preemption. However, | ||
360 | * putting it here bounds the amount that | ||
361 | * __mnt_writers can underflow. Without it, | ||
362 | * we could theoretically wrap __mnt_writers. | ||
363 | */ | ||
364 | put_cpu_var(mnt_writers); | ||
365 | } | ||
366 | EXPORT_SYMBOL_GPL(mnt_drop_write); | ||
367 | |||
368 | static int mnt_make_readonly(struct vfsmount *mnt) | ||
369 | { | ||
370 | int ret = 0; | ||
371 | |||
372 | lock_mnt_writers(); | ||
373 | /* | ||
374 | * With all the locks held, this value is stable | ||
375 | */ | ||
376 | if (atomic_read(&mnt->__mnt_writers) > 0) { | ||
377 | ret = -EBUSY; | ||
378 | goto out; | ||
379 | } | ||
380 | /* | ||
381 | * nobody can do a successful mnt_want_write() with all | ||
382 | * of the counts in MNT_DENIED_WRITE and the locks held. | ||
383 | */ | ||
384 | spin_lock(&vfsmount_lock); | ||
385 | if (!ret) | ||
386 | mnt->mnt_flags |= MNT_READONLY; | ||
387 | spin_unlock(&vfsmount_lock); | ||
388 | out: | ||
389 | unlock_mnt_writers(); | ||
390 | return ret; | ||
391 | } | ||
392 | |||
393 | static void __mnt_unmake_readonly(struct vfsmount *mnt) | ||
394 | { | ||
395 | spin_lock(&vfsmount_lock); | ||
396 | mnt->mnt_flags &= ~MNT_READONLY; | ||
397 | spin_unlock(&vfsmount_lock); | ||
398 | } | ||
399 | |||
83 | int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) | 400 | int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) |
84 | { | 401 | { |
85 | mnt->mnt_sb = sb; | 402 | mnt->mnt_sb = sb; |
@@ -92,6 +409,7 @@ EXPORT_SYMBOL(simple_set_mnt); | |||
92 | void free_vfsmnt(struct vfsmount *mnt) | 409 | void free_vfsmnt(struct vfsmount *mnt) |
93 | { | 410 | { |
94 | kfree(mnt->mnt_devname); | 411 | kfree(mnt->mnt_devname); |
412 | mnt_free_id(mnt); | ||
95 | kmem_cache_free(mnt_cache, mnt); | 413 | kmem_cache_free(mnt_cache, mnt); |
96 | } | 414 | } |
97 | 415 | ||
@@ -238,6 +556,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
238 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); | 556 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); |
239 | 557 | ||
240 | if (mnt) { | 558 | if (mnt) { |
559 | if (flag & (CL_SLAVE | CL_PRIVATE)) | ||
560 | mnt->mnt_group_id = 0; /* not a peer of original */ | ||
561 | else | ||
562 | mnt->mnt_group_id = old->mnt_group_id; | ||
563 | |||
564 | if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) { | ||
565 | int err = mnt_alloc_group_id(mnt); | ||
566 | if (err) | ||
567 | goto out_free; | ||
568 | } | ||
569 | |||
241 | mnt->mnt_flags = old->mnt_flags; | 570 | mnt->mnt_flags = old->mnt_flags; |
242 | atomic_inc(&sb->s_active); | 571 | atomic_inc(&sb->s_active); |
243 | mnt->mnt_sb = sb; | 572 | mnt->mnt_sb = sb; |
@@ -267,11 +596,44 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
267 | } | 596 | } |
268 | } | 597 | } |
269 | return mnt; | 598 | return mnt; |
599 | |||
600 | out_free: | ||
601 | free_vfsmnt(mnt); | ||
602 | return NULL; | ||
270 | } | 603 | } |
271 | 604 | ||
272 | static inline void __mntput(struct vfsmount *mnt) | 605 | static inline void __mntput(struct vfsmount *mnt) |
273 | { | 606 | { |
607 | int cpu; | ||
274 | struct super_block *sb = mnt->mnt_sb; | 608 | struct super_block *sb = mnt->mnt_sb; |
609 | /* | ||
610 | * We don't have to hold all of the locks at the | ||
611 | * same time here because we know that we're the | ||
612 | * last reference to mnt and that no new writers | ||
613 | * can come in. | ||
614 | */ | ||
615 | for_each_possible_cpu(cpu) { | ||
616 | struct mnt_writer *cpu_writer = &per_cpu(mnt_writers, cpu); | ||
617 | if (cpu_writer->mnt != mnt) | ||
618 | continue; | ||
619 | spin_lock(&cpu_writer->lock); | ||
620 | atomic_add(cpu_writer->count, &mnt->__mnt_writers); | ||
621 | cpu_writer->count = 0; | ||
622 | /* | ||
623 | * Might as well do this so that no one | ||
624 | * ever sees the pointer and expects | ||
625 | * it to be valid. | ||
626 | */ | ||
627 | cpu_writer->mnt = NULL; | ||
628 | spin_unlock(&cpu_writer->lock); | ||
629 | } | ||
630 | /* | ||
631 | * This probably indicates that somebody messed | ||
632 | * up a mnt_want/drop_write() pair. If this | ||
633 | * happens, the filesystem was probably unable | ||
634 | * to make r/w->r/o transitions. | ||
635 | */ | ||
636 | WARN_ON(atomic_read(&mnt->__mnt_writers)); | ||
275 | dput(mnt->mnt_root); | 637 | dput(mnt->mnt_root); |
276 | free_vfsmnt(mnt); | 638 | free_vfsmnt(mnt); |
277 | deactivate_super(sb); | 639 | deactivate_super(sb); |
@@ -362,20 +724,21 @@ void save_mount_options(struct super_block *sb, char *options) | |||
362 | } | 724 | } |
363 | EXPORT_SYMBOL(save_mount_options); | 725 | EXPORT_SYMBOL(save_mount_options); |
364 | 726 | ||
727 | #ifdef CONFIG_PROC_FS | ||
365 | /* iterator */ | 728 | /* iterator */ |
366 | static void *m_start(struct seq_file *m, loff_t *pos) | 729 | static void *m_start(struct seq_file *m, loff_t *pos) |
367 | { | 730 | { |
368 | struct mnt_namespace *n = m->private; | 731 | struct proc_mounts *p = m->private; |
369 | 732 | ||
370 | down_read(&namespace_sem); | 733 | down_read(&namespace_sem); |
371 | return seq_list_start(&n->list, *pos); | 734 | return seq_list_start(&p->ns->list, *pos); |
372 | } | 735 | } |
373 | 736 | ||
374 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) | 737 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) |
375 | { | 738 | { |
376 | struct mnt_namespace *n = m->private; | 739 | struct proc_mounts *p = m->private; |
377 | 740 | ||
378 | return seq_list_next(v, &n->list, pos); | 741 | return seq_list_next(v, &p->ns->list, pos); |
379 | } | 742 | } |
380 | 743 | ||
381 | static void m_stop(struct seq_file *m, void *v) | 744 | static void m_stop(struct seq_file *m, void *v) |
@@ -383,20 +746,30 @@ static void m_stop(struct seq_file *m, void *v) | |||
383 | up_read(&namespace_sem); | 746 | up_read(&namespace_sem); |
384 | } | 747 | } |
385 | 748 | ||
386 | static int show_vfsmnt(struct seq_file *m, void *v) | 749 | struct proc_fs_info { |
750 | int flag; | ||
751 | const char *str; | ||
752 | }; | ||
753 | |||
754 | static void show_sb_opts(struct seq_file *m, struct super_block *sb) | ||
387 | { | 755 | { |
388 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 756 | static const struct proc_fs_info fs_info[] = { |
389 | int err = 0; | ||
390 | static struct proc_fs_info { | ||
391 | int flag; | ||
392 | char *str; | ||
393 | } fs_info[] = { | ||
394 | { MS_SYNCHRONOUS, ",sync" }, | 757 | { MS_SYNCHRONOUS, ",sync" }, |
395 | { MS_DIRSYNC, ",dirsync" }, | 758 | { MS_DIRSYNC, ",dirsync" }, |
396 | { MS_MANDLOCK, ",mand" }, | 759 | { MS_MANDLOCK, ",mand" }, |
397 | { 0, NULL } | 760 | { 0, NULL } |
398 | }; | 761 | }; |
399 | static struct proc_fs_info mnt_info[] = { | 762 | const struct proc_fs_info *fs_infop; |
763 | |||
764 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | ||
765 | if (sb->s_flags & fs_infop->flag) | ||
766 | seq_puts(m, fs_infop->str); | ||
767 | } | ||
768 | } | ||
769 | |||
770 | static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) | ||
771 | { | ||
772 | static const struct proc_fs_info mnt_info[] = { | ||
400 | { MNT_NOSUID, ",nosuid" }, | 773 | { MNT_NOSUID, ",nosuid" }, |
401 | { MNT_NODEV, ",nodev" }, | 774 | { MNT_NODEV, ",nodev" }, |
402 | { MNT_NOEXEC, ",noexec" }, | 775 | { MNT_NOEXEC, ",noexec" }, |
@@ -405,40 +778,108 @@ static int show_vfsmnt(struct seq_file *m, void *v) | |||
405 | { MNT_RELATIME, ",relatime" }, | 778 | { MNT_RELATIME, ",relatime" }, |
406 | { 0, NULL } | 779 | { 0, NULL } |
407 | }; | 780 | }; |
408 | struct proc_fs_info *fs_infop; | 781 | const struct proc_fs_info *fs_infop; |
782 | |||
783 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
784 | if (mnt->mnt_flags & fs_infop->flag) | ||
785 | seq_puts(m, fs_infop->str); | ||
786 | } | ||
787 | } | ||
788 | |||
789 | static void show_type(struct seq_file *m, struct super_block *sb) | ||
790 | { | ||
791 | mangle(m, sb->s_type->name); | ||
792 | if (sb->s_subtype && sb->s_subtype[0]) { | ||
793 | seq_putc(m, '.'); | ||
794 | mangle(m, sb->s_subtype); | ||
795 | } | ||
796 | } | ||
797 | |||
798 | static int show_vfsmnt(struct seq_file *m, void *v) | ||
799 | { | ||
800 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
801 | int err = 0; | ||
409 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | 802 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
410 | 803 | ||
411 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | 804 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); |
412 | seq_putc(m, ' '); | 805 | seq_putc(m, ' '); |
413 | seq_path(m, &mnt_path, " \t\n\\"); | 806 | seq_path(m, &mnt_path, " \t\n\\"); |
414 | seq_putc(m, ' '); | 807 | seq_putc(m, ' '); |
415 | mangle(m, mnt->mnt_sb->s_type->name); | 808 | show_type(m, mnt->mnt_sb); |
416 | if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) { | 809 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); |
417 | seq_putc(m, '.'); | 810 | show_sb_opts(m, mnt->mnt_sb); |
418 | mangle(m, mnt->mnt_sb->s_subtype); | 811 | show_mnt_opts(m, mnt); |
419 | } | ||
420 | seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? " ro" : " rw"); | ||
421 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | ||
422 | if (mnt->mnt_sb->s_flags & fs_infop->flag) | ||
423 | seq_puts(m, fs_infop->str); | ||
424 | } | ||
425 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
426 | if (mnt->mnt_flags & fs_infop->flag) | ||
427 | seq_puts(m, fs_infop->str); | ||
428 | } | ||
429 | if (mnt->mnt_sb->s_op->show_options) | 812 | if (mnt->mnt_sb->s_op->show_options) |
430 | err = mnt->mnt_sb->s_op->show_options(m, mnt); | 813 | err = mnt->mnt_sb->s_op->show_options(m, mnt); |
431 | seq_puts(m, " 0 0\n"); | 814 | seq_puts(m, " 0 0\n"); |
432 | return err; | 815 | return err; |
433 | } | 816 | } |
434 | 817 | ||
435 | struct seq_operations mounts_op = { | 818 | const struct seq_operations mounts_op = { |
436 | .start = m_start, | 819 | .start = m_start, |
437 | .next = m_next, | 820 | .next = m_next, |
438 | .stop = m_stop, | 821 | .stop = m_stop, |
439 | .show = show_vfsmnt | 822 | .show = show_vfsmnt |
440 | }; | 823 | }; |
441 | 824 | ||
825 | static int show_mountinfo(struct seq_file *m, void *v) | ||
826 | { | ||
827 | struct proc_mounts *p = m->private; | ||
828 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
829 | struct super_block *sb = mnt->mnt_sb; | ||
830 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | ||
831 | struct path root = p->root; | ||
832 | int err = 0; | ||
833 | |||
834 | seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, | ||
835 | MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
836 | seq_dentry(m, mnt->mnt_root, " \t\n\\"); | ||
837 | seq_putc(m, ' '); | ||
838 | seq_path_root(m, &mnt_path, &root, " \t\n\\"); | ||
839 | if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { | ||
840 | /* | ||
841 | * Mountpoint is outside root, discard that one. Ugly, | ||
842 | * but less so than trying to do that in iterator in a | ||
843 | * race-free way (due to renames). | ||
844 | */ | ||
845 | return SEQ_SKIP; | ||
846 | } | ||
847 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); | ||
848 | show_mnt_opts(m, mnt); | ||
849 | |||
850 | /* Tagged fields ("foo:X" or "bar") */ | ||
851 | if (IS_MNT_SHARED(mnt)) | ||
852 | seq_printf(m, " shared:%i", mnt->mnt_group_id); | ||
853 | if (IS_MNT_SLAVE(mnt)) { | ||
854 | int master = mnt->mnt_master->mnt_group_id; | ||
855 | int dom = get_dominating_id(mnt, &p->root); | ||
856 | seq_printf(m, " master:%i", master); | ||
857 | if (dom && dom != master) | ||
858 | seq_printf(m, " propagate_from:%i", dom); | ||
859 | } | ||
860 | if (IS_MNT_UNBINDABLE(mnt)) | ||
861 | seq_puts(m, " unbindable"); | ||
862 | |||
863 | /* Filesystem specific data */ | ||
864 | seq_puts(m, " - "); | ||
865 | show_type(m, sb); | ||
866 | seq_putc(m, ' '); | ||
867 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | ||
868 | seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); | ||
869 | show_sb_opts(m, sb); | ||
870 | if (sb->s_op->show_options) | ||
871 | err = sb->s_op->show_options(m, mnt); | ||
872 | seq_putc(m, '\n'); | ||
873 | return err; | ||
874 | } | ||
875 | |||
876 | const struct seq_operations mountinfo_op = { | ||
877 | .start = m_start, | ||
878 | .next = m_next, | ||
879 | .stop = m_stop, | ||
880 | .show = show_mountinfo, | ||
881 | }; | ||
882 | |||
442 | static int show_vfsstat(struct seq_file *m, void *v) | 883 | static int show_vfsstat(struct seq_file *m, void *v) |
443 | { | 884 | { |
444 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 885 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); |
@@ -459,7 +900,7 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
459 | 900 | ||
460 | /* file system type */ | 901 | /* file system type */ |
461 | seq_puts(m, "with fstype "); | 902 | seq_puts(m, "with fstype "); |
462 | mangle(m, mnt->mnt_sb->s_type->name); | 903 | show_type(m, mnt->mnt_sb); |
463 | 904 | ||
464 | /* optional statistics */ | 905 | /* optional statistics */ |
465 | if (mnt->mnt_sb->s_op->show_stats) { | 906 | if (mnt->mnt_sb->s_op->show_stats) { |
@@ -471,12 +912,13 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
471 | return err; | 912 | return err; |
472 | } | 913 | } |
473 | 914 | ||
474 | struct seq_operations mountstats_op = { | 915 | const struct seq_operations mountstats_op = { |
475 | .start = m_start, | 916 | .start = m_start, |
476 | .next = m_next, | 917 | .next = m_next, |
477 | .stop = m_stop, | 918 | .stop = m_stop, |
478 | .show = show_vfsstat, | 919 | .show = show_vfsstat, |
479 | }; | 920 | }; |
921 | #endif /* CONFIG_PROC_FS */ | ||
480 | 922 | ||
481 | /** | 923 | /** |
482 | * may_umount_tree - check if a mount tree is busy | 924 | * may_umount_tree - check if a mount tree is busy |
@@ -801,23 +1243,50 @@ Enomem: | |||
801 | struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) | 1243 | struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) |
802 | { | 1244 | { |
803 | struct vfsmount *tree; | 1245 | struct vfsmount *tree; |
804 | down_read(&namespace_sem); | 1246 | down_write(&namespace_sem); |
805 | tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); | 1247 | tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); |
806 | up_read(&namespace_sem); | 1248 | up_write(&namespace_sem); |
807 | return tree; | 1249 | return tree; |
808 | } | 1250 | } |
809 | 1251 | ||
810 | void drop_collected_mounts(struct vfsmount *mnt) | 1252 | void drop_collected_mounts(struct vfsmount *mnt) |
811 | { | 1253 | { |
812 | LIST_HEAD(umount_list); | 1254 | LIST_HEAD(umount_list); |
813 | down_read(&namespace_sem); | 1255 | down_write(&namespace_sem); |
814 | spin_lock(&vfsmount_lock); | 1256 | spin_lock(&vfsmount_lock); |
815 | umount_tree(mnt, 0, &umount_list); | 1257 | umount_tree(mnt, 0, &umount_list); |
816 | spin_unlock(&vfsmount_lock); | 1258 | spin_unlock(&vfsmount_lock); |
817 | up_read(&namespace_sem); | 1259 | up_write(&namespace_sem); |
818 | release_mounts(&umount_list); | 1260 | release_mounts(&umount_list); |
819 | } | 1261 | } |
820 | 1262 | ||
1263 | static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) | ||
1264 | { | ||
1265 | struct vfsmount *p; | ||
1266 | |||
1267 | for (p = mnt; p != end; p = next_mnt(p, mnt)) { | ||
1268 | if (p->mnt_group_id && !IS_MNT_SHARED(p)) | ||
1269 | mnt_release_group_id(p); | ||
1270 | } | ||
1271 | } | ||
1272 | |||
1273 | static int invent_group_ids(struct vfsmount *mnt, bool recurse) | ||
1274 | { | ||
1275 | struct vfsmount *p; | ||
1276 | |||
1277 | for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { | ||
1278 | if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { | ||
1279 | int err = mnt_alloc_group_id(p); | ||
1280 | if (err) { | ||
1281 | cleanup_group_ids(mnt, p); | ||
1282 | return err; | ||
1283 | } | ||
1284 | } | ||
1285 | } | ||
1286 | |||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
821 | /* | 1290 | /* |
822 | * @source_mnt : mount tree to be attached | 1291 | * @source_mnt : mount tree to be attached |
823 | * @nd : place the mount tree @source_mnt is attached | 1292 | * @nd : place the mount tree @source_mnt is attached |
@@ -888,9 +1357,16 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
888 | struct vfsmount *dest_mnt = path->mnt; | 1357 | struct vfsmount *dest_mnt = path->mnt; |
889 | struct dentry *dest_dentry = path->dentry; | 1358 | struct dentry *dest_dentry = path->dentry; |
890 | struct vfsmount *child, *p; | 1359 | struct vfsmount *child, *p; |
1360 | int err; | ||
891 | 1361 | ||
892 | if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) | 1362 | if (IS_MNT_SHARED(dest_mnt)) { |
893 | return -EINVAL; | 1363 | err = invent_group_ids(source_mnt, true); |
1364 | if (err) | ||
1365 | goto out; | ||
1366 | } | ||
1367 | err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); | ||
1368 | if (err) | ||
1369 | goto out_cleanup_ids; | ||
894 | 1370 | ||
895 | if (IS_MNT_SHARED(dest_mnt)) { | 1371 | if (IS_MNT_SHARED(dest_mnt)) { |
896 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) | 1372 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) |
@@ -913,34 +1389,40 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
913 | } | 1389 | } |
914 | spin_unlock(&vfsmount_lock); | 1390 | spin_unlock(&vfsmount_lock); |
915 | return 0; | 1391 | return 0; |
1392 | |||
1393 | out_cleanup_ids: | ||
1394 | if (IS_MNT_SHARED(dest_mnt)) | ||
1395 | cleanup_group_ids(source_mnt, NULL); | ||
1396 | out: | ||
1397 | return err; | ||
916 | } | 1398 | } |
917 | 1399 | ||
918 | static int graft_tree(struct vfsmount *mnt, struct nameidata *nd) | 1400 | static int graft_tree(struct vfsmount *mnt, struct path *path) |
919 | { | 1401 | { |
920 | int err; | 1402 | int err; |
921 | if (mnt->mnt_sb->s_flags & MS_NOUSER) | 1403 | if (mnt->mnt_sb->s_flags & MS_NOUSER) |
922 | return -EINVAL; | 1404 | return -EINVAL; |
923 | 1405 | ||
924 | if (S_ISDIR(nd->path.dentry->d_inode->i_mode) != | 1406 | if (S_ISDIR(path->dentry->d_inode->i_mode) != |
925 | S_ISDIR(mnt->mnt_root->d_inode->i_mode)) | 1407 | S_ISDIR(mnt->mnt_root->d_inode->i_mode)) |
926 | return -ENOTDIR; | 1408 | return -ENOTDIR; |
927 | 1409 | ||
928 | err = -ENOENT; | 1410 | err = -ENOENT; |
929 | mutex_lock(&nd->path.dentry->d_inode->i_mutex); | 1411 | mutex_lock(&path->dentry->d_inode->i_mutex); |
930 | if (IS_DEADDIR(nd->path.dentry->d_inode)) | 1412 | if (IS_DEADDIR(path->dentry->d_inode)) |
931 | goto out_unlock; | 1413 | goto out_unlock; |
932 | 1414 | ||
933 | err = security_sb_check_sb(mnt, nd); | 1415 | err = security_sb_check_sb(mnt, path); |
934 | if (err) | 1416 | if (err) |
935 | goto out_unlock; | 1417 | goto out_unlock; |
936 | 1418 | ||
937 | err = -ENOENT; | 1419 | err = -ENOENT; |
938 | if (IS_ROOT(nd->path.dentry) || !d_unhashed(nd->path.dentry)) | 1420 | if (IS_ROOT(path->dentry) || !d_unhashed(path->dentry)) |
939 | err = attach_recursive_mnt(mnt, &nd->path, NULL); | 1421 | err = attach_recursive_mnt(mnt, path, NULL); |
940 | out_unlock: | 1422 | out_unlock: |
941 | mutex_unlock(&nd->path.dentry->d_inode->i_mutex); | 1423 | mutex_unlock(&path->dentry->d_inode->i_mutex); |
942 | if (!err) | 1424 | if (!err) |
943 | security_sb_post_addmount(mnt, nd); | 1425 | security_sb_post_addmount(mnt, path); |
944 | return err; | 1426 | return err; |
945 | } | 1427 | } |
946 | 1428 | ||
@@ -953,6 +1435,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
953 | struct vfsmount *m, *mnt = nd->path.mnt; | 1435 | struct vfsmount *m, *mnt = nd->path.mnt; |
954 | int recurse = flag & MS_REC; | 1436 | int recurse = flag & MS_REC; |
955 | int type = flag & ~MS_REC; | 1437 | int type = flag & ~MS_REC; |
1438 | int err = 0; | ||
956 | 1439 | ||
957 | if (!capable(CAP_SYS_ADMIN)) | 1440 | if (!capable(CAP_SYS_ADMIN)) |
958 | return -EPERM; | 1441 | return -EPERM; |
@@ -961,12 +1444,20 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
961 | return -EINVAL; | 1444 | return -EINVAL; |
962 | 1445 | ||
963 | down_write(&namespace_sem); | 1446 | down_write(&namespace_sem); |
1447 | if (type == MS_SHARED) { | ||
1448 | err = invent_group_ids(mnt, recurse); | ||
1449 | if (err) | ||
1450 | goto out_unlock; | ||
1451 | } | ||
1452 | |||
964 | spin_lock(&vfsmount_lock); | 1453 | spin_lock(&vfsmount_lock); |
965 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) | 1454 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) |
966 | change_mnt_propagation(m, type); | 1455 | change_mnt_propagation(m, type); |
967 | spin_unlock(&vfsmount_lock); | 1456 | spin_unlock(&vfsmount_lock); |
1457 | |||
1458 | out_unlock: | ||
968 | up_write(&namespace_sem); | 1459 | up_write(&namespace_sem); |
969 | return 0; | 1460 | return err; |
970 | } | 1461 | } |
971 | 1462 | ||
972 | /* | 1463 | /* |
@@ -1004,7 +1495,7 @@ static noinline int do_loopback(struct nameidata *nd, char *old_name, | |||
1004 | if (!mnt) | 1495 | if (!mnt) |
1005 | goto out; | 1496 | goto out; |
1006 | 1497 | ||
1007 | err = graft_tree(mnt, nd); | 1498 | err = graft_tree(mnt, &nd->path); |
1008 | if (err) { | 1499 | if (err) { |
1009 | LIST_HEAD(umount_list); | 1500 | LIST_HEAD(umount_list); |
1010 | spin_lock(&vfsmount_lock); | 1501 | spin_lock(&vfsmount_lock); |
@@ -1019,6 +1510,23 @@ out: | |||
1019 | return err; | 1510 | return err; |
1020 | } | 1511 | } |
1021 | 1512 | ||
1513 | static int change_mount_flags(struct vfsmount *mnt, int ms_flags) | ||
1514 | { | ||
1515 | int error = 0; | ||
1516 | int readonly_request = 0; | ||
1517 | |||
1518 | if (ms_flags & MS_RDONLY) | ||
1519 | readonly_request = 1; | ||
1520 | if (readonly_request == __mnt_is_readonly(mnt)) | ||
1521 | return 0; | ||
1522 | |||
1523 | if (readonly_request) | ||
1524 | error = mnt_make_readonly(mnt); | ||
1525 | else | ||
1526 | __mnt_unmake_readonly(mnt); | ||
1527 | return error; | ||
1528 | } | ||
1529 | |||
1022 | /* | 1530 | /* |
1023 | * change filesystem flags. dir should be a physical root of filesystem. | 1531 | * change filesystem flags. dir should be a physical root of filesystem. |
1024 | * If you've mounted a non-root directory somewhere and want to do remount | 1532 | * If you've mounted a non-root directory somewhere and want to do remount |
@@ -1041,7 +1549,10 @@ static noinline int do_remount(struct nameidata *nd, int flags, int mnt_flags, | |||
1041 | return -EINVAL; | 1549 | return -EINVAL; |
1042 | 1550 | ||
1043 | down_write(&sb->s_umount); | 1551 | down_write(&sb->s_umount); |
1044 | err = do_remount_sb(sb, flags, data, 0); | 1552 | if (flags & MS_BIND) |
1553 | err = change_mount_flags(nd->path.mnt, flags); | ||
1554 | else | ||
1555 | err = do_remount_sb(sb, flags, data, 0); | ||
1045 | if (!err) | 1556 | if (!err) |
1046 | nd->path.mnt->mnt_flags = mnt_flags; | 1557 | nd->path.mnt->mnt_flags = mnt_flags; |
1047 | up_write(&sb->s_umount); | 1558 | up_write(&sb->s_umount); |
@@ -1191,7 +1702,7 @@ int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, | |||
1191 | goto unlock; | 1702 | goto unlock; |
1192 | 1703 | ||
1193 | newmnt->mnt_flags = mnt_flags; | 1704 | newmnt->mnt_flags = mnt_flags; |
1194 | if ((err = graft_tree(newmnt, nd))) | 1705 | if ((err = graft_tree(newmnt, &nd->path))) |
1195 | goto unlock; | 1706 | goto unlock; |
1196 | 1707 | ||
1197 | if (fslist) /* add to the specified expiration list */ | 1708 | if (fslist) /* add to the specified expiration list */ |
@@ -1425,6 +1936,8 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, | |||
1425 | mnt_flags |= MNT_NODIRATIME; | 1936 | mnt_flags |= MNT_NODIRATIME; |
1426 | if (flags & MS_RELATIME) | 1937 | if (flags & MS_RELATIME) |
1427 | mnt_flags |= MNT_RELATIME; | 1938 | mnt_flags |= MNT_RELATIME; |
1939 | if (flags & MS_RDONLY) | ||
1940 | mnt_flags |= MNT_READONLY; | ||
1428 | 1941 | ||
1429 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | | 1942 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | |
1430 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT); | 1943 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT); |
@@ -1434,7 +1947,8 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, | |||
1434 | if (retval) | 1947 | if (retval) |
1435 | return retval; | 1948 | return retval; |
1436 | 1949 | ||
1437 | retval = security_sb_mount(dev_name, &nd, type_page, flags, data_page); | 1950 | retval = security_sb_mount(dev_name, &nd.path, |
1951 | type_page, flags, data_page); | ||
1438 | if (retval) | 1952 | if (retval) |
1439 | goto dput_out; | 1953 | goto dput_out; |
1440 | 1954 | ||
@@ -1674,15 +2188,13 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1674 | const char __user * put_old) | 2188 | const char __user * put_old) |
1675 | { | 2189 | { |
1676 | struct vfsmount *tmp; | 2190 | struct vfsmount *tmp; |
1677 | struct nameidata new_nd, old_nd, user_nd; | 2191 | struct nameidata new_nd, old_nd; |
1678 | struct path parent_path, root_parent; | 2192 | struct path parent_path, root_parent, root; |
1679 | int error; | 2193 | int error; |
1680 | 2194 | ||
1681 | if (!capable(CAP_SYS_ADMIN)) | 2195 | if (!capable(CAP_SYS_ADMIN)) |
1682 | return -EPERM; | 2196 | return -EPERM; |
1683 | 2197 | ||
1684 | lock_kernel(); | ||
1685 | |||
1686 | error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, | 2198 | error = __user_walk(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, |
1687 | &new_nd); | 2199 | &new_nd); |
1688 | if (error) | 2200 | if (error) |
@@ -1695,14 +2207,14 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1695 | if (error) | 2207 | if (error) |
1696 | goto out1; | 2208 | goto out1; |
1697 | 2209 | ||
1698 | error = security_sb_pivotroot(&old_nd, &new_nd); | 2210 | error = security_sb_pivotroot(&old_nd.path, &new_nd.path); |
1699 | if (error) { | 2211 | if (error) { |
1700 | path_put(&old_nd.path); | 2212 | path_put(&old_nd.path); |
1701 | goto out1; | 2213 | goto out1; |
1702 | } | 2214 | } |
1703 | 2215 | ||
1704 | read_lock(¤t->fs->lock); | 2216 | read_lock(¤t->fs->lock); |
1705 | user_nd.path = current->fs->root; | 2217 | root = current->fs->root; |
1706 | path_get(¤t->fs->root); | 2218 | path_get(¤t->fs->root); |
1707 | read_unlock(¤t->fs->lock); | 2219 | read_unlock(¤t->fs->lock); |
1708 | down_write(&namespace_sem); | 2220 | down_write(&namespace_sem); |
@@ -1710,9 +2222,9 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1710 | error = -EINVAL; | 2222 | error = -EINVAL; |
1711 | if (IS_MNT_SHARED(old_nd.path.mnt) || | 2223 | if (IS_MNT_SHARED(old_nd.path.mnt) || |
1712 | IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || | 2224 | IS_MNT_SHARED(new_nd.path.mnt->mnt_parent) || |
1713 | IS_MNT_SHARED(user_nd.path.mnt->mnt_parent)) | 2225 | IS_MNT_SHARED(root.mnt->mnt_parent)) |
1714 | goto out2; | 2226 | goto out2; |
1715 | if (!check_mnt(user_nd.path.mnt)) | 2227 | if (!check_mnt(root.mnt)) |
1716 | goto out2; | 2228 | goto out2; |
1717 | error = -ENOENT; | 2229 | error = -ENOENT; |
1718 | if (IS_DEADDIR(new_nd.path.dentry->d_inode)) | 2230 | if (IS_DEADDIR(new_nd.path.dentry->d_inode)) |
@@ -1722,13 +2234,13 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1722 | if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) | 2234 | if (d_unhashed(old_nd.path.dentry) && !IS_ROOT(old_nd.path.dentry)) |
1723 | goto out2; | 2235 | goto out2; |
1724 | error = -EBUSY; | 2236 | error = -EBUSY; |
1725 | if (new_nd.path.mnt == user_nd.path.mnt || | 2237 | if (new_nd.path.mnt == root.mnt || |
1726 | old_nd.path.mnt == user_nd.path.mnt) | 2238 | old_nd.path.mnt == root.mnt) |
1727 | goto out2; /* loop, on the same file system */ | 2239 | goto out2; /* loop, on the same file system */ |
1728 | error = -EINVAL; | 2240 | error = -EINVAL; |
1729 | if (user_nd.path.mnt->mnt_root != user_nd.path.dentry) | 2241 | if (root.mnt->mnt_root != root.dentry) |
1730 | goto out2; /* not a mountpoint */ | 2242 | goto out2; /* not a mountpoint */ |
1731 | if (user_nd.path.mnt->mnt_parent == user_nd.path.mnt) | 2243 | if (root.mnt->mnt_parent == root.mnt) |
1732 | goto out2; /* not attached */ | 2244 | goto out2; /* not attached */ |
1733 | if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) | 2245 | if (new_nd.path.mnt->mnt_root != new_nd.path.dentry) |
1734 | goto out2; /* not a mountpoint */ | 2246 | goto out2; /* not a mountpoint */ |
@@ -1750,27 +2262,26 @@ asmlinkage long sys_pivot_root(const char __user * new_root, | |||
1750 | } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) | 2262 | } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry)) |
1751 | goto out3; | 2263 | goto out3; |
1752 | detach_mnt(new_nd.path.mnt, &parent_path); | 2264 | detach_mnt(new_nd.path.mnt, &parent_path); |
1753 | detach_mnt(user_nd.path.mnt, &root_parent); | 2265 | detach_mnt(root.mnt, &root_parent); |
1754 | /* mount old root on put_old */ | 2266 | /* mount old root on put_old */ |
1755 | attach_mnt(user_nd.path.mnt, &old_nd.path); | 2267 | attach_mnt(root.mnt, &old_nd.path); |
1756 | /* mount new_root on / */ | 2268 | /* mount new_root on / */ |
1757 | attach_mnt(new_nd.path.mnt, &root_parent); | 2269 | attach_mnt(new_nd.path.mnt, &root_parent); |
1758 | touch_mnt_namespace(current->nsproxy->mnt_ns); | 2270 | touch_mnt_namespace(current->nsproxy->mnt_ns); |
1759 | spin_unlock(&vfsmount_lock); | 2271 | spin_unlock(&vfsmount_lock); |
1760 | chroot_fs_refs(&user_nd.path, &new_nd.path); | 2272 | chroot_fs_refs(&root, &new_nd.path); |
1761 | security_sb_post_pivotroot(&user_nd, &new_nd); | 2273 | security_sb_post_pivotroot(&root, &new_nd.path); |
1762 | error = 0; | 2274 | error = 0; |
1763 | path_put(&root_parent); | 2275 | path_put(&root_parent); |
1764 | path_put(&parent_path); | 2276 | path_put(&parent_path); |
1765 | out2: | 2277 | out2: |
1766 | mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); | 2278 | mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex); |
1767 | up_write(&namespace_sem); | 2279 | up_write(&namespace_sem); |
1768 | path_put(&user_nd.path); | 2280 | path_put(&root); |
1769 | path_put(&old_nd.path); | 2281 | path_put(&old_nd.path); |
1770 | out1: | 2282 | out1: |
1771 | path_put(&new_nd.path); | 2283 | path_put(&new_nd.path); |
1772 | out0: | 2284 | out0: |
1773 | unlock_kernel(); | ||
1774 | return error; | 2285 | return error; |
1775 | out3: | 2286 | out3: |
1776 | spin_unlock(&vfsmount_lock); | 2287 | spin_unlock(&vfsmount_lock); |