| .. | .. |
|---|
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
|---|
| 2 | 2 | #include <linux/mount.h> |
|---|
| 3 | +#include <linux/pseudo_fs.h> |
|---|
| 3 | 4 | #include <linux/file.h> |
|---|
| 4 | 5 | #include <linux/fs.h> |
|---|
| 6 | +#include <linux/proc_fs.h> |
|---|
| 5 | 7 | #include <linux/proc_ns.h> |
|---|
| 6 | 8 | #include <linux/magic.h> |
|---|
| 7 | 9 | #include <linux/ktime.h> |
|---|
| .. | .. |
|---|
| 9 | 11 | #include <linux/user_namespace.h> |
|---|
| 10 | 12 | #include <linux/nsfs.h> |
|---|
| 11 | 13 | #include <linux/uaccess.h> |
|---|
| 14 | + |
|---|
| 15 | +#include "internal.h" |
|---|
| 12 | 16 | |
|---|
| 13 | 17 | static struct vfsmount *nsfs_mnt; |
|---|
| 14 | 18 | |
|---|
| .. | .. |
|---|
| 51 | 55 | ns->ops->put(ns); |
|---|
| 52 | 56 | } |
|---|
| 53 | 57 | |
|---|
| 54 | | -static void *__ns_get_path(struct path *path, struct ns_common *ns) |
|---|
| 58 | +static int __ns_get_path(struct path *path, struct ns_common *ns) |
|---|
| 55 | 59 | { |
|---|
| 56 | 60 | struct vfsmount *mnt = nsfs_mnt; |
|---|
| 57 | 61 | struct dentry *dentry; |
|---|
| .. | .. |
|---|
| 70 | 74 | got_it: |
|---|
| 71 | 75 | path->mnt = mntget(mnt); |
|---|
| 72 | 76 | path->dentry = dentry; |
|---|
| 73 | | - return NULL; |
|---|
| 77 | + return 0; |
|---|
| 74 | 78 | slow: |
|---|
| 75 | 79 | rcu_read_unlock(); |
|---|
| 76 | 80 | inode = new_inode_pseudo(mnt->mnt_sb); |
|---|
| 77 | 81 | if (!inode) { |
|---|
| 78 | 82 | ns->ops->put(ns); |
|---|
| 79 | | - return ERR_PTR(-ENOMEM); |
|---|
| 83 | + return -ENOMEM; |
|---|
| 80 | 84 | } |
|---|
| 81 | 85 | inode->i_ino = ns->inum; |
|---|
| 82 | 86 | inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); |
|---|
| .. | .. |
|---|
| 88 | 92 | dentry = d_alloc_anon(mnt->mnt_sb); |
|---|
| 89 | 93 | if (!dentry) { |
|---|
| 90 | 94 | iput(inode); |
|---|
| 91 | | - return ERR_PTR(-ENOMEM); |
|---|
| 95 | + return -ENOMEM; |
|---|
| 92 | 96 | } |
|---|
| 93 | 97 | d_instantiate(dentry, inode); |
|---|
| 94 | 98 | dentry->d_fsdata = (void *)ns->ops; |
|---|
| .. | .. |
|---|
| 97 | 101 | d_delete(dentry); /* make sure ->d_prune() does nothing */ |
|---|
| 98 | 102 | dput(dentry); |
|---|
| 99 | 103 | cpu_relax(); |
|---|
| 100 | | - return ERR_PTR(-EAGAIN); |
|---|
| 104 | + return -EAGAIN; |
|---|
| 101 | 105 | } |
|---|
| 102 | 106 | goto got_it; |
|---|
| 103 | 107 | } |
|---|
| 104 | 108 | |
|---|
| 105 | | -void *ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb, |
|---|
| 109 | +int ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb, |
|---|
| 106 | 110 | void *private_data) |
|---|
| 107 | 111 | { |
|---|
| 108 | | - struct ns_common *ns; |
|---|
| 109 | | - void *ret; |
|---|
| 112 | + int ret; |
|---|
| 110 | 113 | |
|---|
| 111 | | -again: |
|---|
| 112 | | - ns = ns_get_cb(private_data); |
|---|
| 113 | | - if (!ns) |
|---|
| 114 | | - return ERR_PTR(-ENOENT); |
|---|
| 114 | + do { |
|---|
| 115 | + struct ns_common *ns = ns_get_cb(private_data); |
|---|
| 116 | + if (!ns) |
|---|
| 117 | + return -ENOENT; |
|---|
| 118 | + ret = __ns_get_path(path, ns); |
|---|
| 119 | + } while (ret == -EAGAIN); |
|---|
| 115 | 120 | |
|---|
| 116 | | - ret = __ns_get_path(path, ns); |
|---|
| 117 | | - if (IS_ERR(ret) && PTR_ERR(ret) == -EAGAIN) |
|---|
| 118 | | - goto again; |
|---|
| 119 | 121 | return ret; |
|---|
| 120 | 122 | } |
|---|
| 121 | 123 | |
|---|
| .. | .. |
|---|
| 131 | 133 | return args->ns_ops->get(args->task); |
|---|
| 132 | 134 | } |
|---|
| 133 | 135 | |
|---|
| 134 | | -void *ns_get_path(struct path *path, struct task_struct *task, |
|---|
| 136 | +int ns_get_path(struct path *path, struct task_struct *task, |
|---|
| 135 | 137 | const struct proc_ns_operations *ns_ops) |
|---|
| 136 | 138 | { |
|---|
| 137 | 139 | struct ns_get_path_task_args args = { |
|---|
| .. | .. |
|---|
| 147 | 149 | { |
|---|
| 148 | 150 | struct path path = {}; |
|---|
| 149 | 151 | struct file *f; |
|---|
| 150 | | - void *err; |
|---|
| 152 | + int err; |
|---|
| 151 | 153 | int fd; |
|---|
| 152 | 154 | |
|---|
| 153 | 155 | fd = get_unused_fd_flags(O_CLOEXEC); |
|---|
| 154 | 156 | if (fd < 0) |
|---|
| 155 | 157 | return fd; |
|---|
| 156 | 158 | |
|---|
| 157 | | - while (1) { |
|---|
| 159 | + do { |
|---|
| 158 | 160 | struct ns_common *relative; |
|---|
| 159 | 161 | |
|---|
| 160 | 162 | relative = get_ns(ns); |
|---|
| .. | .. |
|---|
| 164 | 166 | } |
|---|
| 165 | 167 | |
|---|
| 166 | 168 | err = __ns_get_path(&path, relative); |
|---|
| 167 | | - if (IS_ERR(err) && PTR_ERR(err) == -EAGAIN) |
|---|
| 168 | | - continue; |
|---|
| 169 | | - break; |
|---|
| 170 | | - } |
|---|
| 171 | | - if (IS_ERR(err)) { |
|---|
| 169 | + } while (err == -EAGAIN); |
|---|
| 170 | + |
|---|
| 171 | + if (err) { |
|---|
| 172 | 172 | put_unused_fd(fd); |
|---|
| 173 | | - return PTR_ERR(err); |
|---|
| 173 | + return err; |
|---|
| 174 | 174 | } |
|---|
| 175 | 175 | |
|---|
| 176 | 176 | f = dentry_open(&path, O_RDONLY, current_cred()); |
|---|
| .. | .. |
|---|
| 229 | 229 | return res; |
|---|
| 230 | 230 | } |
|---|
| 231 | 231 | |
|---|
| 232 | +bool proc_ns_file(const struct file *file) |
|---|
| 233 | +{ |
|---|
| 234 | + return file->f_op == &ns_file_operations; |
|---|
| 235 | +} |
|---|
| 236 | + |
|---|
| 232 | 237 | struct file *proc_ns_fget(int fd) |
|---|
| 233 | 238 | { |
|---|
| 234 | 239 | struct file *file; |
|---|
| .. | .. |
|---|
| 247 | 252 | return ERR_PTR(-EINVAL); |
|---|
| 248 | 253 | } |
|---|
| 249 | 254 | |
|---|
| 255 | +/** |
|---|
| 256 | + * ns_match() - Returns true if current namespace matches dev/ino provided. |
|---|
| 257 | + * @ns_common: current ns |
|---|
| 258 | + * @dev: dev_t from nsfs that will be matched against current nsfs |
|---|
| 259 | + * @ino: ino_t from nsfs that will be matched against current nsfs |
|---|
| 260 | + * |
|---|
| 261 | + * Return: true if dev and ino matches the current nsfs. |
|---|
| 262 | + */ |
|---|
| 263 | +bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino) |
|---|
| 264 | +{ |
|---|
| 265 | + return (ns->inum == ino) && (nsfs_mnt->mnt_sb->s_dev == dev); |
|---|
| 266 | +} |
|---|
| 267 | + |
|---|
| 268 | + |
|---|
| 250 | 269 | static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry) |
|---|
| 251 | 270 | { |
|---|
| 252 | 271 | struct inode *inode = d_inode(dentry); |
|---|
| .. | .. |
|---|
| 261 | 280 | .evict_inode = nsfs_evict, |
|---|
| 262 | 281 | .show_path = nsfs_show_path, |
|---|
| 263 | 282 | }; |
|---|
| 264 | | -static struct dentry *nsfs_mount(struct file_system_type *fs_type, |
|---|
| 265 | | - int flags, const char *dev_name, void *data) |
|---|
| 283 | + |
|---|
| 284 | +static int nsfs_init_fs_context(struct fs_context *fc) |
|---|
| 266 | 285 | { |
|---|
| 267 | | - return mount_pseudo(fs_type, "nsfs:", &nsfs_ops, |
|---|
| 268 | | - &ns_dentry_operations, NSFS_MAGIC); |
|---|
| 286 | + struct pseudo_fs_context *ctx = init_pseudo(fc, NSFS_MAGIC); |
|---|
| 287 | + if (!ctx) |
|---|
| 288 | + return -ENOMEM; |
|---|
| 289 | + ctx->ops = &nsfs_ops; |
|---|
| 290 | + ctx->dops = &ns_dentry_operations; |
|---|
| 291 | + return 0; |
|---|
| 269 | 292 | } |
|---|
| 293 | + |
|---|
| 270 | 294 | static struct file_system_type nsfs = { |
|---|
| 271 | 295 | .name = "nsfs", |
|---|
| 272 | | - .mount = nsfs_mount, |
|---|
| 296 | + .init_fs_context = nsfs_init_fs_context, |
|---|
| 273 | 297 | .kill_sb = kill_anon_super, |
|---|
| 274 | 298 | }; |
|---|
| 275 | 299 | |
|---|