| @@ -4683,6 +4683,287 @@ int show_path(struct seq_file *m, struct dentry *root) · 4682 unmodified lines |
| 4683 | 4683 | return 0; |
| 4684 | 4684 | } |
| 4685 | 4685 | |
| 4686 | +static struct vfsmount *lookup_mnt_in_ns(u64 id, struct mnt_namespace *ns) |
| 4687 | +{ |
| 4688 | + struct mount *mnt = mnt_find_id_at(ns, id); |
| 4689 | + |
| 4690 | + if (!mnt || mnt->mnt_id_unique != id) |
| 4691 | + return NULL; |
| 4692 | + |
| 4693 | + return &mnt->mnt; |
| 4694 | +} |
| 4695 | + |
| 4696 | +struct kstatmount { |
| 4697 | + struct statmount __user *const buf; |
| 4698 | + size_t const bufsize; |
| 4699 | + struct vfsmount *const mnt; |
| 4700 | + u64 const mask; |
| 4701 | + struct seq_file seq; |
| 4702 | + struct path root; |
| 4703 | + struct statmount sm; |
| 4704 | + size_t pos; |
| 4705 | + int err; |
| 4706 | +}; |
| 4707 | + |
| 4708 | +typedef int (*statmount_func_t)(struct kstatmount *); |
| 4709 | + |
| 4710 | +static int statmount_string_seq(struct kstatmount *s, statmount_func_t func) |
| 4711 | +{ |
| 4712 | + size_t rem = s->bufsize - s->pos - sizeof(s->sm); |
| 4713 | + struct seq_file *seq = &s->seq; |
| 4714 | + int ret; |
| 4715 | + |
| 4716 | + seq->count = 0; |
| 4717 | + seq->size = min(seq->size, rem); |
| 4718 | + seq->buf = kvmalloc(seq->size, GFP_KERNEL_ACCOUNT); |
| 4719 | + if (!seq->buf) |
| 4720 | + return -ENOMEM; |
| 4721 | + |
| 4722 | + ret = func(s); |
| 4723 | + if (ret) |
| 4724 | + return ret; |
| 4725 | + |
| 4726 | + if (seq_has_overflowed(seq)) { |
| 4727 | + if (seq->size == rem) |
| 4728 | + return -EOVERFLOW; |
| 4729 | + seq->size *= 2; |
| 4730 | + if (seq->size > MAX_RW_COUNT) |
| 4731 | + return -ENOMEM; |
| 4732 | + kvfree(seq->buf); |
| 4733 | + return 0; |
| 4734 | + } |
| 4735 | + |
| 4736 | + /* Done */ |
| 4737 | + return 1; |
| 4738 | +} |
| 4739 | + |
| 4740 | +static void statmount_string(struct kstatmount *s, u64 mask, statmount_func_t func, |
| 4741 | + u32 *str) |
| 4742 | +{ |
| 4743 | + int ret = s->pos + sizeof(s->sm) >= s->bufsize ? -EOVERFLOW : 0; |
| 4744 | + struct statmount *sm = &s->sm; |
| 4745 | + struct seq_file *seq = &s->seq; |
| 4746 | + |
| 4747 | + if (s->err || !(s->mask & mask)) |
| 4748 | + return; |
| 4749 | + |
| 4750 | + seq->size = PAGE_SIZE; |
| 4751 | + while (!ret) |
| 4752 | + ret = statmount_string_seq(s, func); |
| 4753 | + |
| 4754 | + if (ret < 0) { |
| 4755 | + s->err = ret; |
| 4756 | + } else { |
| 4757 | + seq->buf[seq->count++] = '\0'; |
| 4758 | + if (copy_to_user(s->buf->str + s->pos, seq->buf, seq->count)) { |
| 4759 | + s->err = -EFAULT; |
| 4760 | + } else { |
| 4761 | + *str = s->pos; |
| 4762 | + s->pos += seq->count; |
| 4763 | + } |
| 4764 | + } |
| 4765 | + kvfree(seq->buf); |
| 4766 | + sm->mask |= mask; |
| 4767 | +} |
| 4768 | + |
| 4769 | +static void statmount_numeric(struct kstatmount *s, u64 mask, statmount_func_t func) |
| 4770 | +{ |
| 4771 | + if (s->err || !(s->mask & mask)) |
| 4772 | + return; |
| 4773 | + |
| 4774 | + s->err = func(s); |
| 4775 | + s->sm.mask |= mask; |
| 4776 | +} |
| 4777 | + |
| 4778 | +static u64 mnt_to_attr_flags(struct vfsmount *mnt) |
| 4779 | +{ |
| 4780 | + unsigned int mnt_flags = READ_ONCE(mnt->mnt_flags); |
| 4781 | + u64 attr_flags = 0; |
| 4782 | + |
| 4783 | + if (mnt_flags & MNT_READONLY) |
| 4784 | + attr_flags |= MOUNT_ATTR_RDONLY; |
| 4785 | + if (mnt_flags & MNT_NOSUID) |
| 4786 | + attr_flags |= MOUNT_ATTR_NOSUID; |
| 4787 | + if (mnt_flags & MNT_NODEV) |
| 4788 | + attr_flags |= MOUNT_ATTR_NODEV; |
| 4789 | + if (mnt_flags & MNT_NOEXEC) |
| 4790 | + attr_flags |= MOUNT_ATTR_NOEXEC; |
| 4791 | + if (mnt_flags & MNT_NODIRATIME) |
| 4792 | + attr_flags |= MOUNT_ATTR_NODIRATIME; |
| 4793 | + if (mnt_flags & MNT_NOSYMFOLLOW) |
| 4794 | + attr_flags |= MOUNT_ATTR_NOSYMFOLLOW; |
| 4795 | + |
| 4796 | + if (mnt_flags & MNT_NOATIME) |
| 4797 | + attr_flags |= MOUNT_ATTR_NOATIME; |
| 4798 | + else if (mnt_flags & MNT_RELATIME) |
| 4799 | + attr_flags |= MOUNT_ATTR_RELATIME; |
| 4800 | + else |
| 4801 | + attr_flags |= MOUNT_ATTR_STRICTATIME; |
| 4802 | + |
| 4803 | + if (is_idmapped_mnt(mnt)) |
| 4804 | + attr_flags |= MOUNT_ATTR_IDMAP; |
| 4805 | + |
| 4806 | + return attr_flags; |
| 4807 | +} |
| 4808 | + |
| 4809 | +static u64 mnt_to_propagation_flags(struct mount *m) |
| 4810 | +{ |
| 4811 | + u64 propagation = 0; |
| 4812 | + |
| 4813 | + if (IS_MNT_SHARED(m)) |
| 4814 | + propagation |= MS_SHARED; |
| 4815 | + if (IS_MNT_SLAVE(m)) |
| 4816 | + propagation |= MS_SLAVE; |
| 4817 | + if (IS_MNT_UNBINDABLE(m)) |
| 4818 | + propagation |= MS_UNBINDABLE; |
| 4819 | + if (!propagation) |
| 4820 | + propagation |= MS_PRIVATE; |
| 4821 | + |
| 4822 | + return propagation; |
| 4823 | +} |
| 4824 | + |
| 4825 | +static int statmount_sb_basic(struct kstatmount *s) |
| 4826 | +{ |
| 4827 | + struct super_block *sb = s->mnt->mnt_sb; |
| 4828 | + |
| 4829 | + s->sm.sb_dev_major = MAJOR(sb->s_dev); |
| 4830 | + s->sm.sb_dev_minor = MINOR(sb->s_dev); |
| 4831 | + s->sm.sb_magic = sb->s_magic; |
| 4832 | + s->sm.sb_flags = sb->s_flags & (SB_RDONLY|SB_SYNCHRONOUS|SB_DIRSYNC|SB_LAZYTIME); |
| 4833 | + |
| 4834 | + return 0; |
| 4835 | +} |
| 4836 | + |
| 4837 | +static int statmount_mnt_basic(struct kstatmount *s) |
| 4838 | +{ |
| 4839 | + struct mount *m = real_mount(s->mnt); |
| 4840 | + |
| 4841 | + s->sm.mnt_id = m->mnt_id_unique; |
| 4842 | + s->sm.mnt_parent_id = m->mnt_parent->mnt_id_unique; |
| 4843 | + s->sm.mnt_id_old = m->mnt_id; |
| 4844 | + s->sm.mnt_parent_id_old = m->mnt_parent->mnt_id; |
| 4845 | + s->sm.mnt_attr = mnt_to_attr_flags(&m->mnt); |
| 4846 | + s->sm.mnt_propagation = mnt_to_propagation_flags(m); |
| 4847 | + s->sm.mnt_peer_group = IS_MNT_SHARED(m) ? m->mnt_group_id : 0; |
| 4848 | + s->sm.mnt_master = IS_MNT_SLAVE(m) ? m->mnt_master->mnt_group_id : 0; |
| 4849 | + |
| 4850 | + return 0; |
| 4851 | +} |
| 4852 | + |
| 4853 | +static int statmount_propagate_from(struct kstatmount *s) |
| 4854 | +{ |
| 4855 | + struct mount *m = real_mount(s->mnt); |
| 4856 | + |
| 4857 | + if (!IS_MNT_SLAVE(m)) |
| 4858 | + return 0; |
| 4859 | + |
| 4860 | + s->sm.propagate_from = get_dominating_id(m, ¤t->fs->root); |
| 4861 | + |
| 4862 | + return 0; |
| 4863 | +} |
| 4864 | + |
| 4865 | +static int statmount_mnt_root(struct kstatmount *s) |
| 4866 | +{ |
| 4867 | + struct seq_file *seq = &s->seq; |
| 4868 | + int err = show_path(seq, s->mnt->mnt_root); |
| 4869 | + |
| 4870 | + if (!err && !seq_has_overflowed(seq)) { |
| 4871 | + seq->buf[seq->count] = '\0'; |
| 4872 | + seq->count = string_unescape_inplace(seq->buf, UNESCAPE_OCTAL); |
| 4873 | + } |
| 4874 | + return err; |
| 4875 | +} |
| 4876 | + |
| 4877 | +static int statmount_mnt_point(struct kstatmount *s) |
| 4878 | +{ |
| 4879 | + struct vfsmount *mnt = s->mnt; |
| 4880 | + struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
| 4881 | + int err = seq_path_root(&s->seq, &mnt_path, &s->root, ""); |
| 4882 | + |
| 4883 | + return err == SEQ_SKIP ? 0 : err; |
| 4884 | +} |
| 4885 | + |
| 4886 | +static int statmount_fs_type(struct kstatmount *s) |
| 4887 | +{ |
| 4888 | + struct seq_file *seq = &s->seq; |
| 4889 | + struct super_block *sb = s->mnt->mnt_sb; |
| 4890 | + |
| 4891 | + seq_puts(seq, sb->s_type->name); |
| 4892 | + return 0; |
| 4893 | +} |
| 4894 | + |
| 4895 | +static int do_statmount(struct kstatmount *s) |
| 4896 | +{ |
| 4897 | + struct statmount *sm = &s->sm; |
| 4898 | + struct mount *m = real_mount(s->mnt); |
| 4899 | + size_t copysize = min_t(size_t, s->bufsize, sizeof(*sm)); |
| 4900 | + int err; |
| 4901 | + |
| 4902 | + /* |
| 4903 | + * Don't trigger audit denials. We just want to determine what |
| 4904 | + * mounts to show users. |
| 4905 | + */ |
| 4906 | + if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) && |
| 4907 | + !ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) |
| 4908 | + return -EPERM; |
| 4909 | + |
| 4910 | + err = security_sb_statfs(s->mnt->mnt_root); |
| 4911 | + if (err) |
| 4912 | + return err; |
| 4913 | + |
| 4914 | + statmount_numeric(s, STATMOUNT_SB_BASIC, statmount_sb_basic); |
| 4915 | + statmount_numeric(s, STATMOUNT_MNT_BASIC, statmount_mnt_basic); |
| 4916 | + statmount_numeric(s, STATMOUNT_PROPAGATE_FROM, statmount_propagate_from); |
| 4917 | + statmount_string(s, STATMOUNT_FS_TYPE, statmount_fs_type, &sm->fs_type); |
| 4918 | + statmount_string(s, STATMOUNT_MNT_ROOT, statmount_mnt_root, &sm->mnt_root); |
| 4919 | + statmount_string(s, STATMOUNT_MNT_POINT, statmount_mnt_point, &sm->mnt_point); |
| 4920 | + |
| 4921 | + if (s->err) |
| 4922 | + return s->err; |
| 4923 | + |
| 4924 | + /* Return the number of bytes copied to the buffer */ |
| 4925 | + sm->size = copysize + s->pos; |
| 4926 | + |
| 4927 | + if (copy_to_user(s->buf, sm, copysize)) |
| 4928 | + return -EFAULT; |
| 4929 | + |
| 4930 | + return 0; |
| 4931 | +} |
| 4932 | + |
| 4933 | +SYSCALL_DEFINE4(statmount, const struct mnt_id_req __user *, req, |
| 4934 | + struct statmount __user *, buf, size_t, bufsize, |
| 4935 | + unsigned int, flags) |
| 4936 | +{ |
| 4937 | + struct vfsmount *mnt; |
| 4938 | + struct mnt_id_req kreq; |
| 4939 | + int ret; |
| 4940 | + |
| 4941 | + if (flags) |
| 4942 | + return -EINVAL; |
| 4943 | + |
| 4944 | + if (copy_from_user(&kreq, req, sizeof(kreq))) |
| 4945 | + return -EFAULT; |
| 4946 | + |
| 4947 | + down_read(&namespace_sem); |
| 4948 | + mnt = lookup_mnt_in_ns(kreq.mnt_id, current->nsproxy->mnt_ns); |
| 4949 | + ret = -ENOENT; |
| 4950 | + if (mnt) { |
| 4951 | + struct kstatmount s = { |
| 4952 | + .mask = kreq.request_mask, |
| 4953 | + .buf = buf, |
| 4954 | + .bufsize = bufsize, |
| 4955 | + .mnt = mnt, |
| 4956 | + }; |
| 4957 | + |
| 4958 | + get_fs_root(current->fs, &s.root); |
| 4959 | + ret = do_statmount(&s); |
| 4960 | + path_put(&s.root); |
| 4961 | + } |
| 4962 | + up_read(&namespace_sem); |
| 4963 | + |
| 4964 | + return ret; |
| 4965 | +} |
| 4966 | + |
| 4686 | 4967 | static void __init init_mount_tree(void) |
| 4687 | 4968 | { |
| 4688 | 4969 | struct vfsmount *mnt; |