Blame view
fs/nsfs.c
6.11 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 6 7 8 |
#include <linux/file.h> #include <linux/fs.h> #include <linux/proc_ns.h> #include <linux/magic.h> #include <linux/ktime.h> |
75509fd88 nsfs: Add a show_... |
9 |
#include <linux/seq_file.h> |
6786741db nsfs: add ioctl t... |
10 11 |
#include <linux/user_namespace.h> #include <linux/nsfs.h> |
d95fa3c76 nsfs: Add an ioct... |
12 |
#include <linux/uaccess.h> |
e149ed2b8 take the targets ... |
13 14 |
static struct vfsmount *nsfs_mnt; |
6786741db nsfs: add ioctl t... |
15 16 |
static long ns_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg); |
e149ed2b8 take the targets ... |
17 18 |
static const struct file_operations ns_file_operations = { .llseek = no_llseek, |
6786741db nsfs: add ioctl t... |
19 |
.unlocked_ioctl = ns_ioctl, |
e149ed2b8 take the targets ... |
20 21 22 23 |
}; static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) { |
75c3cfa85 VFS: assorted wei... |
24 |
struct inode *inode = d_inode(dentry); |
e149ed2b8 take the targets ... |
25 26 27 28 29 30 31 32 |
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... |
33 |
struct inode *inode = d_inode(dentry); |
e149ed2b8 take the targets ... |
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
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); } |
6786741db nsfs: add ioctl t... |
53 |
static void *__ns_get_path(struct path *path, struct ns_common *ns) |
e149ed2b8 take the targets ... |
54 |
{ |
213b067ce nsfs: Simplify __... |
55 |
struct vfsmount *mnt = nsfs_mnt; |
e149ed2b8 take the targets ... |
56 57 |
struct dentry *dentry; struct inode *inode; |
e149ed2b8 take the targets ... |
58 |
unsigned long d; |
e149ed2b8 take the targets ... |
59 60 61 62 63 64 65 66 |
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... |
67 |
ns->ops->put(ns); |
e149ed2b8 take the targets ... |
68 |
got_it: |
213b067ce nsfs: Simplify __... |
69 |
path->mnt = mntget(mnt); |
e149ed2b8 take the targets ... |
70 71 72 73 74 75 |
path->dentry = dentry; return NULL; slow: rcu_read_unlock(); inode = new_inode_pseudo(mnt->mnt_sb); if (!inode) { |
6786741db nsfs: add ioctl t... |
76 |
ns->ops->put(ns); |
e149ed2b8 take the targets ... |
77 78 79 |
return ERR_PTR(-ENOMEM); } inode->i_ino = ns->inum; |
078cd8279 fs: Replace CURRE... |
80 |
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); |
e149ed2b8 take the targets ... |
81 82 83 84 |
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 ... |
85 |
dentry = d_alloc_anon(mnt->mnt_sb); |
e149ed2b8 take the targets ... |
86 87 |
if (!dentry) { iput(inode); |
e149ed2b8 take the targets ... |
88 89 90 |
return ERR_PTR(-ENOMEM); } d_instantiate(dentry, inode); |
6786741db nsfs: add ioctl t... |
91 |
dentry->d_fsdata = (void *)ns->ops; |
e149ed2b8 take the targets ... |
92 93 94 95 96 |
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(); |
6786741db nsfs: add ioctl t... |
97 |
return ERR_PTR(-EAGAIN); |
e149ed2b8 take the targets ... |
98 99 100 |
} goto got_it; } |
cdab6ba86 nsfs: generalize ... |
101 102 |
void *ns_get_path_cb(struct path *path, ns_get_path_helper_t *ns_get_cb, void *private_data) |
6786741db nsfs: add ioctl t... |
103 |
{ |
6786741db nsfs: add ioctl t... |
104 |
void *ret; |
357ab5b5d nsfs: unobfuscate |
105 106 107 108 109 110 111 |
do { struct ns_common *ns = ns_get_cb(private_data); if (!ns) return ERR_PTR(-ENOENT); ret = __ns_get_path(path, ns); } while (ret == ERR_PTR(-EAGAIN)); |
6786741db nsfs: add ioctl t... |
112 |
|
6786741db nsfs: add ioctl t... |
113 114 |
return ret; } |
cdab6ba86 nsfs: generalize ... |
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
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); } void *ns_get_path(struct path *path, struct task_struct *task, 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... |
137 |
int open_related_ns(struct ns_common *ns, |
6786741db nsfs: add ioctl t... |
138 139 140 141 142 143 144 145 146 147 |
struct ns_common *(*get_ns)(struct ns_common *ns)) { struct path path = {}; struct file *f; void *err; int fd; fd = get_unused_fd_flags(O_CLOEXEC); if (fd < 0) return fd; |
357ab5b5d nsfs: unobfuscate |
148 |
do { |
6786741db nsfs: add ioctl t... |
149 150 151 152 153 154 155 156 157 |
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); |
357ab5b5d nsfs: unobfuscate |
158 |
} while (err == ERR_PTR(-EAGAIN)); |
6786741db nsfs: add ioctl t... |
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
if (IS_ERR(err)) { put_unused_fd(fd); return PTR_ERR(err); } 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_... |
174 |
EXPORT_SYMBOL_GPL(open_related_ns); |
6786741db nsfs: add ioctl t... |
175 176 177 178 |
static long ns_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { |
d95fa3c76 nsfs: Add an ioct... |
179 |
struct user_namespace *user_ns; |
6786741db nsfs: add ioctl t... |
180 |
struct ns_common *ns = get_proc_ns(file_inode(filp)); |
d95fa3c76 nsfs: Add an ioct... |
181 182 |
uid_t __user *argp; uid_t uid; |
6786741db nsfs: add ioctl t... |
183 184 185 186 |
switch (ioctl) { case NS_GET_USERNS: return open_related_ns(ns, ns_get_owner); |
a7306ed8d nsfs: add ioctl t... |
187 188 189 190 |
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... |
191 192 |
case NS_GET_NSTYPE: return ns->ops->type; |
d95fa3c76 nsfs: Add an ioct... |
193 194 195 196 197 198 199 |
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... |
200 201 202 203 |
default: return -ENOTTY; } } |
e149ed2b8 take the targets ... |
204 205 206 207 208 |
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... |
209 |
const char *name; |
e149ed2b8 take the targets ... |
210 211 |
ns = ns_ops->get(task); if (ns) { |
25b14e92a ns: allow ns_entr... |
212 213 |
name = ns_ops->real_ns_name ? : ns_ops->name; res = snprintf(buf, size, "%s:[%u]", name, ns->inum); |
e149ed2b8 take the targets ... |
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
ns_ops->put(ns); } return res; } 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); } |
75509fd88 nsfs: Add a show_... |
236 237 238 239 |
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... |
240 241 |
seq_printf(seq, "%s:[%lu]", ns_ops->name, inode->i_ino); return 0; |
75509fd88 nsfs: Add a show_... |
242 |
} |
e149ed2b8 take the targets ... |
243 244 245 |
static const struct super_operations nsfs_ops = { .statfs = simple_statfs, .evict_inode = nsfs_evict, |
75509fd88 nsfs: Add a show_... |
246 |
.show_path = nsfs_show_path, |
e149ed2b8 take the targets ... |
247 |
}; |
059b20d9d vfs: Convert nsfs... |
248 249 |
static int nsfs_init_fs_context(struct fs_context *fc) |
e149ed2b8 take the targets ... |
250 |
{ |
059b20d9d vfs: Convert nsfs... |
251 252 253 254 255 256 |
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 ... |
257 |
} |
059b20d9d vfs: Convert nsfs... |
258 |
|
e149ed2b8 take the targets ... |
259 260 |
static struct file_system_type nsfs = { .name = "nsfs", |
059b20d9d vfs: Convert nsfs... |
261 |
.init_fs_context = nsfs_init_fs_context, |
e149ed2b8 take the targets ... |
262 263 264 265 266 267 268 269 270 |
.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... |
271 |
nsfs_mnt->mnt_sb->s_flags &= ~SB_NOUSER; |
e149ed2b8 take the targets ... |
272 |
} |