LCOV - code coverage report
Current view: top level - fs - nsfs.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 11 117 9.4 %
Date: 2023-08-24 13:40:31 Functions: 2 15 13.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : #include <linux/mount.h>
       3             : #include <linux/pseudo_fs.h>
       4             : #include <linux/file.h>
       5             : #include <linux/fs.h>
       6             : #include <linux/proc_fs.h>
       7             : #include <linux/proc_ns.h>
       8             : #include <linux/magic.h>
       9             : #include <linux/ktime.h>
      10             : #include <linux/seq_file.h>
      11             : #include <linux/user_namespace.h>
      12             : #include <linux/nsfs.h>
      13             : #include <linux/uaccess.h>
      14             : 
      15             : #include "internal.h"
      16             : 
      17             : static struct vfsmount *nsfs_mnt;
      18             : 
      19             : static long ns_ioctl(struct file *filp, unsigned int ioctl,
      20             :                         unsigned long arg);
      21             : static const struct file_operations ns_file_operations = {
      22             :         .llseek         = no_llseek,
      23             :         .unlocked_ioctl = ns_ioctl,
      24             :         .compat_ioctl   = compat_ptr_ioctl,
      25             : };
      26             : 
      27           0 : static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
      28             : {
      29           0 :         struct inode *inode = d_inode(dentry);
      30           0 :         const struct proc_ns_operations *ns_ops = dentry->d_fsdata;
      31             : 
      32           0 :         return dynamic_dname(buffer, buflen, "%s:[%lu]",
      33             :                 ns_ops->name, inode->i_ino);
      34             : }
      35             : 
      36           0 : static void ns_prune_dentry(struct dentry *dentry)
      37             : {
      38           0 :         struct inode *inode = d_inode(dentry);
      39           0 :         if (inode) {
      40           0 :                 struct ns_common *ns = inode->i_private;
      41           0 :                 atomic_long_set(&ns->stashed, 0);
      42             :         }
      43           0 : }
      44             : 
      45             : const struct dentry_operations ns_dentry_operations =
      46             : {
      47             :         .d_prune        = ns_prune_dentry,
      48             :         .d_delete       = always_delete_dentry,
      49             :         .d_dname        = ns_dname,
      50             : };
      51             : 
      52           0 : static void nsfs_evict(struct inode *inode)
      53             : {
      54           0 :         struct ns_common *ns = inode->i_private;
      55           0 :         clear_inode(inode);
      56           0 :         ns->ops->put(ns);
      57           0 : }
      58             : 
      59           0 : static int __ns_get_path(struct path *path, struct ns_common *ns)
      60             : {
      61           0 :         struct vfsmount *mnt = nsfs_mnt;
      62             :         struct dentry *dentry;
      63             :         struct inode *inode;
      64             :         unsigned long d;
      65             : 
      66             :         rcu_read_lock();
      67           0 :         d = atomic_long_read(&ns->stashed);
      68           0 :         if (!d)
      69             :                 goto slow;
      70           0 :         dentry = (struct dentry *)d;
      71           0 :         if (!lockref_get_not_dead(&dentry->d_lockref))
      72             :                 goto slow;
      73             :         rcu_read_unlock();
      74           0 :         ns->ops->put(ns);
      75             : got_it:
      76           0 :         path->mnt = mntget(mnt);
      77           0 :         path->dentry = dentry;
      78           0 :         return 0;
      79             : slow:
      80             :         rcu_read_unlock();
      81           0 :         inode = new_inode_pseudo(mnt->mnt_sb);
      82           0 :         if (!inode) {
      83           0 :                 ns->ops->put(ns);
      84           0 :                 return -ENOMEM;
      85             :         }
      86           0 :         inode->i_ino = ns->inum;
      87           0 :         inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
      88           0 :         inode->i_flags |= S_IMMUTABLE;
      89           0 :         inode->i_mode = S_IFREG | S_IRUGO;
      90           0 :         inode->i_fop = &ns_file_operations;
      91           0 :         inode->i_private = ns;
      92             : 
      93           0 :         dentry = d_alloc_anon(mnt->mnt_sb);
      94           0 :         if (!dentry) {
      95           0 :                 iput(inode);
      96           0 :                 return -ENOMEM;
      97             :         }
      98           0 :         d_instantiate(dentry, inode);
      99           0 :         dentry->d_fsdata = (void *)ns->ops;
     100           0 :         d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
     101           0 :         if (d) {
     102           0 :                 d_delete(dentry);       /* make sure ->d_prune() does nothing */
     103           0 :                 dput(dentry);
     104             :                 cpu_relax();
     105           0 :                 return -EAGAIN;
     106             :         }
     107             :         goto got_it;
     108             : }
     109             : 
     110           0 : int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb,
     111             :                      void *private_data)
     112             : {
     113             :         int ret;
     114             : 
     115             :         do {
     116           0 :                 struct ns_common *ns = ns_get_cb(private_data);
     117           0 :                 if (!ns)
     118             :                         return -ENOENT;
     119           0 :                 ret = __ns_get_path(path, ns);
     120           0 :         } while (ret == -EAGAIN);
     121             : 
     122             :         return ret;
     123             : }
     124             : 
     125             : struct ns_get_path_task_args {
     126             :         const struct proc_ns_operations *ns_ops;
     127             :         struct task_struct *task;
     128             : };
     129             : 
     130           0 : static struct ns_common *ns_get_path_task(void *private_data)
     131             : {
     132           0 :         struct ns_get_path_task_args *args = private_data;
     133             : 
     134           0 :         return args->ns_ops->get(args->task);
     135             : }
     136             : 
     137           0 : int ns_get_path(struct path *path, struct task_struct *task,
     138             :                   const struct proc_ns_operations *ns_ops)
     139             : {
     140           0 :         struct ns_get_path_task_args args = {
     141             :                 .ns_ops = ns_ops,
     142             :                 .task   = task,
     143             :         };
     144             : 
     145           0 :         return ns_get_path_cb(path, ns_get_path_task, &args);
     146             : }
     147             : 
     148           0 : int open_related_ns(struct ns_common *ns,
     149             :                    struct ns_common *(*get_ns)(struct ns_common *ns))
     150             : {
     151           0 :         struct path path = {};
     152             :         struct file *f;
     153             :         int err;
     154             :         int fd;
     155             : 
     156           0 :         fd = get_unused_fd_flags(O_CLOEXEC);
     157           0 :         if (fd < 0)
     158             :                 return fd;
     159             : 
     160             :         do {
     161             :                 struct ns_common *relative;
     162             : 
     163           0 :                 relative = get_ns(ns);
     164           0 :                 if (IS_ERR(relative)) {
     165           0 :                         put_unused_fd(fd);
     166           0 :                         return PTR_ERR(relative);
     167             :                 }
     168             : 
     169           0 :                 err = __ns_get_path(&path, relative);
     170           0 :         } while (err == -EAGAIN);
     171             : 
     172           0 :         if (err) {
     173           0 :                 put_unused_fd(fd);
     174           0 :                 return err;
     175             :         }
     176             : 
     177           0 :         f = dentry_open(&path, O_RDONLY, current_cred());
     178           0 :         path_put(&path);
     179           0 :         if (IS_ERR(f)) {
     180           0 :                 put_unused_fd(fd);
     181           0 :                 fd = PTR_ERR(f);
     182             :         } else
     183           0 :                 fd_install(fd, f);
     184             : 
     185             :         return fd;
     186             : }
     187             : EXPORT_SYMBOL_GPL(open_related_ns);
     188             : 
     189           0 : static long ns_ioctl(struct file *filp, unsigned int ioctl,
     190             :                         unsigned long arg)
     191             : {
     192             :         struct user_namespace *user_ns;
     193           0 :         struct ns_common *ns = get_proc_ns(file_inode(filp));
     194             :         uid_t __user *argp;
     195             :         uid_t uid;
     196             : 
     197           0 :         switch (ioctl) {
     198             :         case NS_GET_USERNS:
     199           0 :                 return open_related_ns(ns, ns_get_owner);
     200             :         case NS_GET_PARENT:
     201           0 :                 if (!ns->ops->get_parent)
     202             :                         return -EINVAL;
     203           0 :                 return open_related_ns(ns, ns->ops->get_parent);
     204             :         case NS_GET_NSTYPE:
     205           0 :                 return ns->ops->type;
     206             :         case NS_GET_OWNER_UID:
     207           0 :                 if (ns->ops->type != CLONE_NEWUSER)
     208             :                         return -EINVAL;
     209           0 :                 user_ns = container_of(ns, struct user_namespace, ns);
     210           0 :                 argp = (uid_t __user *) arg;
     211           0 :                 uid = from_kuid_munged(current_user_ns(), user_ns->owner);
     212           0 :                 return put_user(uid, argp);
     213             :         default:
     214             :                 return -ENOTTY;
     215             :         }
     216             : }
     217             : 
     218           0 : int ns_get_name(char *buf, size_t size, struct task_struct *task,
     219             :                         const struct proc_ns_operations *ns_ops)
     220             : {
     221             :         struct ns_common *ns;
     222           0 :         int res = -ENOENT;
     223             :         const char *name;
     224           0 :         ns = ns_ops->get(task);
     225           0 :         if (ns) {
     226           0 :                 name = ns_ops->real_ns_name ? : ns_ops->name;
     227           0 :                 res = snprintf(buf, size, "%s:[%u]", name, ns->inum);
     228           0 :                 ns_ops->put(ns);
     229             :         }
     230           0 :         return res;
     231             : }
     232             : 
     233           0 : bool proc_ns_file(const struct file *file)
     234             : {
     235           0 :         return file->f_op == &ns_file_operations;
     236             : }
     237             : 
     238             : /**
     239             :  * ns_match() - Returns true if current namespace matches dev/ino provided.
     240             :  * @ns: current namespace
     241             :  * @dev: dev_t from nsfs that will be matched against current nsfs
     242             :  * @ino: ino_t from nsfs that will be matched against current nsfs
     243             :  *
     244             :  * Return: true if dev and ino matches the current nsfs.
     245             :  */
     246           0 : bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino)
     247             : {
     248           0 :         return (ns->inum == ino) && (nsfs_mnt->mnt_sb->s_dev == dev);
     249             : }
     250             : 
     251             : 
     252           0 : static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry)
     253             : {
     254           0 :         struct inode *inode = d_inode(dentry);
     255           0 :         const struct proc_ns_operations *ns_ops = dentry->d_fsdata;
     256             : 
     257           0 :         seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino);
     258           0 :         return 0;
     259             : }
     260             : 
     261             : static const struct super_operations nsfs_ops = {
     262             :         .statfs = simple_statfs,
     263             :         .evict_inode = nsfs_evict,
     264             :         .show_path = nsfs_show_path,
     265             : };
     266             : 
     267           1 : static int nsfs_init_fs_context(struct fs_context *fc)
     268             : {
     269           1 :         struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC);
     270           1 :         if (!ctx)
     271             :                 return -ENOMEM;
     272           1 :         ctx->ops = &nsfs_ops;
     273           1 :         ctx->dops = &ns_dentry_operations;
     274           1 :         return 0;
     275             : }
     276             : 
     277             : static struct file_system_type nsfs = {
     278             :         .name = "nsfs",
     279             :         .init_fs_context = nsfs_init_fs_context,
     280             :         .kill_sb = kill_anon_super,
     281             : };
     282             : 
     283           1 : void __init nsfs_init(void)
     284             : {
     285           1 :         nsfs_mnt = kern_mount(&nsfs);
     286           2 :         if (IS_ERR(nsfs_mnt))
     287           0 :                 panic("can't set nsfs up\n");
     288           1 :         nsfs_mnt->mnt_sb->s_flags &= ~SB_NOUSER;
     289           1 : }

Generated by: LCOV version 1.14