diff options
-rw-r--r-- | fs/namespace.c | 14 | ||||
-rw-r--r-- | fs/proc/base.c | 108 | ||||
-rw-r--r-- | include/linux/mnt_namespace.h | 11 |
3 files changed, 70 insertions, 63 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index cefa1d9939b0..dfdf51e81c1c 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -724,20 +724,21 @@ void save_mount_options(struct super_block *sb, char *options) | |||
724 | } | 724 | } |
725 | EXPORT_SYMBOL(save_mount_options); | 725 | EXPORT_SYMBOL(save_mount_options); |
726 | 726 | ||
727 | #ifdef CONFIG_PROC_FS | ||
727 | /* iterator */ | 728 | /* iterator */ |
728 | static void *m_start(struct seq_file *m, loff_t *pos) | 729 | static void *m_start(struct seq_file *m, loff_t *pos) |
729 | { | 730 | { |
730 | struct mnt_namespace *n = m->private; | 731 | struct proc_mounts *p = m->private; |
731 | 732 | ||
732 | down_read(&namespace_sem); | 733 | down_read(&namespace_sem); |
733 | return seq_list_start(&n->list, *pos); | 734 | return seq_list_start(&p->ns->list, *pos); |
734 | } | 735 | } |
735 | 736 | ||
736 | 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) |
737 | { | 738 | { |
738 | struct mnt_namespace *n = m->private; | 739 | struct proc_mounts *p = m->private; |
739 | 740 | ||
740 | return seq_list_next(v, &n->list, pos); | 741 | return seq_list_next(v, &p->ns->list, pos); |
741 | } | 742 | } |
742 | 743 | ||
743 | static void m_stop(struct seq_file *m, void *v) | 744 | static void m_stop(struct seq_file *m, void *v) |
@@ -794,7 +795,7 @@ static int show_vfsmnt(struct seq_file *m, void *v) | |||
794 | return err; | 795 | return err; |
795 | } | 796 | } |
796 | 797 | ||
797 | struct seq_operations mounts_op = { | 798 | const struct seq_operations mounts_op = { |
798 | .start = m_start, | 799 | .start = m_start, |
799 | .next = m_next, | 800 | .next = m_next, |
800 | .stop = m_stop, | 801 | .stop = m_stop, |
@@ -833,12 +834,13 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
833 | return err; | 834 | return err; |
834 | } | 835 | } |
835 | 836 | ||
836 | struct seq_operations mountstats_op = { | 837 | const struct seq_operations mountstats_op = { |
837 | .start = m_start, | 838 | .start = m_start, |
838 | .next = m_next, | 839 | .next = m_next, |
839 | .stop = m_stop, | 840 | .stop = m_stop, |
840 | .show = show_vfsstat, | 841 | .show = show_vfsstat, |
841 | }; | 842 | }; |
843 | #endif /* CONFIG_PROC_FS */ | ||
842 | 844 | ||
843 | /** | 845 | /** |
844 | * may_umount_tree - check if a mount tree is busy | 846 | * may_umount_tree - check if a mount tree is busy |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 7313c62e3e9d..a04b3db7a296 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -502,17 +502,14 @@ static const struct inode_operations proc_def_inode_operations = { | |||
502 | .setattr = proc_setattr, | 502 | .setattr = proc_setattr, |
503 | }; | 503 | }; |
504 | 504 | ||
505 | extern const struct seq_operations mounts_op; | 505 | static int mounts_open_common(struct inode *inode, struct file *file, |
506 | struct proc_mounts { | 506 | const struct seq_operations *op) |
507 | struct seq_file m; | ||
508 | int event; | ||
509 | }; | ||
510 | |||
511 | static int mounts_open(struct inode *inode, struct file *file) | ||
512 | { | 507 | { |
513 | struct task_struct *task = get_proc_task(inode); | 508 | struct task_struct *task = get_proc_task(inode); |
514 | struct nsproxy *nsp; | 509 | struct nsproxy *nsp; |
515 | struct mnt_namespace *ns = NULL; | 510 | struct mnt_namespace *ns = NULL; |
511 | struct fs_struct *fs = NULL; | ||
512 | struct path root; | ||
516 | struct proc_mounts *p; | 513 | struct proc_mounts *p; |
517 | int ret = -EINVAL; | 514 | int ret = -EINVAL; |
518 | 515 | ||
@@ -525,40 +522,61 @@ static int mounts_open(struct inode *inode, struct file *file) | |||
525 | get_mnt_ns(ns); | 522 | get_mnt_ns(ns); |
526 | } | 523 | } |
527 | rcu_read_unlock(); | 524 | rcu_read_unlock(); |
528 | 525 | if (ns) | |
526 | fs = get_fs_struct(task); | ||
529 | put_task_struct(task); | 527 | put_task_struct(task); |
530 | } | 528 | } |
531 | 529 | ||
532 | if (ns) { | 530 | if (!ns) |
533 | ret = -ENOMEM; | 531 | goto err; |
534 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); | 532 | if (!fs) |
535 | if (p) { | 533 | goto err_put_ns; |
536 | file->private_data = &p->m; | 534 | |
537 | ret = seq_open(file, &mounts_op); | 535 | read_lock(&fs->lock); |
538 | if (!ret) { | 536 | root = fs->root; |
539 | p->m.private = ns; | 537 | path_get(&root); |
540 | p->event = ns->event; | 538 | read_unlock(&fs->lock); |
541 | return 0; | 539 | put_fs_struct(fs); |
542 | } | 540 | |
543 | kfree(p); | 541 | ret = -ENOMEM; |
544 | } | 542 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); |
545 | put_mnt_ns(ns); | 543 | if (!p) |
546 | } | 544 | goto err_put_path; |
545 | |||
546 | file->private_data = &p->m; | ||
547 | ret = seq_open(file, op); | ||
548 | if (ret) | ||
549 | goto err_free; | ||
550 | |||
551 | p->m.private = p; | ||
552 | p->ns = ns; | ||
553 | p->root = root; | ||
554 | p->event = ns->event; | ||
555 | |||
556 | return 0; | ||
557 | |||
558 | err_free: | ||
559 | kfree(p); | ||
560 | err_put_path: | ||
561 | path_put(&root); | ||
562 | err_put_ns: | ||
563 | put_mnt_ns(ns); | ||
564 | err: | ||
547 | return ret; | 565 | return ret; |
548 | } | 566 | } |
549 | 567 | ||
550 | static int mounts_release(struct inode *inode, struct file *file) | 568 | static int mounts_release(struct inode *inode, struct file *file) |
551 | { | 569 | { |
552 | struct seq_file *m = file->private_data; | 570 | struct proc_mounts *p = file->private_data; |
553 | struct mnt_namespace *ns = m->private; | 571 | path_put(&p->root); |
554 | put_mnt_ns(ns); | 572 | put_mnt_ns(p->ns); |
555 | return seq_release(inode, file); | 573 | return seq_release(inode, file); |
556 | } | 574 | } |
557 | 575 | ||
558 | static unsigned mounts_poll(struct file *file, poll_table *wait) | 576 | static unsigned mounts_poll(struct file *file, poll_table *wait) |
559 | { | 577 | { |
560 | struct proc_mounts *p = file->private_data; | 578 | struct proc_mounts *p = file->private_data; |
561 | struct mnt_namespace *ns = p->m.private; | 579 | struct mnt_namespace *ns = p->ns; |
562 | unsigned res = 0; | 580 | unsigned res = 0; |
563 | 581 | ||
564 | poll_wait(file, &ns->poll, wait); | 582 | poll_wait(file, &ns->poll, wait); |
@@ -573,6 +591,11 @@ static unsigned mounts_poll(struct file *file, poll_table *wait) | |||
573 | return res; | 591 | return res; |
574 | } | 592 | } |
575 | 593 | ||
594 | static int mounts_open(struct inode *inode, struct file *file) | ||
595 | { | ||
596 | return mounts_open_common(inode, file, &mounts_op); | ||
597 | } | ||
598 | |||
576 | static const struct file_operations proc_mounts_operations = { | 599 | static const struct file_operations proc_mounts_operations = { |
577 | .open = mounts_open, | 600 | .open = mounts_open, |
578 | .read = seq_read, | 601 | .read = seq_read, |
@@ -581,38 +604,9 @@ static const struct file_operations proc_mounts_operations = { | |||
581 | .poll = mounts_poll, | 604 | .poll = mounts_poll, |
582 | }; | 605 | }; |
583 | 606 | ||
584 | extern const struct seq_operations mountstats_op; | ||
585 | static int mountstats_open(struct inode *inode, struct file *file) | 607 | static int mountstats_open(struct inode *inode, struct file *file) |
586 | { | 608 | { |
587 | int ret = seq_open(file, &mountstats_op); | 609 | return mounts_open_common(inode, file, &mountstats_op); |
588 | |||
589 | if (!ret) { | ||
590 | struct seq_file *m = file->private_data; | ||
591 | struct nsproxy *nsp; | ||
592 | struct mnt_namespace *mnt_ns = NULL; | ||
593 | struct task_struct *task = get_proc_task(inode); | ||
594 | |||
595 | if (task) { | ||
596 | rcu_read_lock(); | ||
597 | nsp = task_nsproxy(task); | ||
598 | if (nsp) { | ||
599 | mnt_ns = nsp->mnt_ns; | ||
600 | if (mnt_ns) | ||
601 | get_mnt_ns(mnt_ns); | ||
602 | } | ||
603 | rcu_read_unlock(); | ||
604 | |||
605 | put_task_struct(task); | ||
606 | } | ||
607 | |||
608 | if (mnt_ns) | ||
609 | m->private = mnt_ns; | ||
610 | else { | ||
611 | seq_release(inode, file); | ||
612 | ret = -EINVAL; | ||
613 | } | ||
614 | } | ||
615 | return ret; | ||
616 | } | 610 | } |
617 | 611 | ||
618 | static const struct file_operations proc_mountstats_operations = { | 612 | static const struct file_operations proc_mountstats_operations = { |
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h index 8eed44f8ca73..c078aacc8116 100644 --- a/include/linux/mnt_namespace.h +++ b/include/linux/mnt_namespace.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/mount.h> | 5 | #include <linux/mount.h> |
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/nsproxy.h> | 7 | #include <linux/nsproxy.h> |
8 | #include <linux/seq_file.h> | ||
8 | 9 | ||
9 | struct mnt_namespace { | 10 | struct mnt_namespace { |
10 | atomic_t count; | 11 | atomic_t count; |
@@ -14,6 +15,13 @@ struct mnt_namespace { | |||
14 | int event; | 15 | int event; |
15 | }; | 16 | }; |
16 | 17 | ||
18 | struct proc_mounts { | ||
19 | struct seq_file m; /* must be the first element */ | ||
20 | struct mnt_namespace *ns; | ||
21 | struct path root; | ||
22 | int event; | ||
23 | }; | ||
24 | |||
17 | extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, | 25 | extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, |
18 | struct fs_struct *); | 26 | struct fs_struct *); |
19 | extern void __put_mnt_ns(struct mnt_namespace *ns); | 27 | extern void __put_mnt_ns(struct mnt_namespace *ns); |
@@ -37,5 +45,8 @@ static inline void get_mnt_ns(struct mnt_namespace *ns) | |||
37 | atomic_inc(&ns->count); | 45 | atomic_inc(&ns->count); |
38 | } | 46 | } |
39 | 47 | ||
48 | extern const struct seq_operations mounts_op; | ||
49 | extern const struct seq_operations mountstats_op; | ||
50 | |||
40 | #endif | 51 | #endif |
41 | #endif | 52 | #endif |