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