.. | .. |
---|
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 | |
---|