| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | *  inode.c - part of tracefs, a pseudo file system for activating tracing | 
|---|
| 4 | * | 
|---|
| 5 | * Based on debugfs by: Greg Kroah-Hartman <greg@kroah.com> | 
|---|
| 6 | * | 
|---|
| 7 | *  Copyright (C) 2014 Red Hat Inc, author: Steven Rostedt <srostedt@redhat.com> | 
|---|
| 8 | * | 
|---|
| 9 | * tracefs is the file system that is used by the tracing infrastructure. | 
|---|
| 10 | */ | 
|---|
| 11 |  | 
|---|
| 12 | #include <linux/module.h> | 
|---|
| 13 | #include <linux/fs.h> | 
|---|
| 14 | #include <linux/fs_context.h> | 
|---|
| 15 | #include <linux/fs_parser.h> | 
|---|
| 16 | #include <linux/kobject.h> | 
|---|
| 17 | #include <linux/namei.h> | 
|---|
| 18 | #include <linux/tracefs.h> | 
|---|
| 19 | #include <linux/fsnotify.h> | 
|---|
| 20 | #include <linux/security.h> | 
|---|
| 21 | #include <linux/seq_file.h> | 
|---|
| 22 | #include <linux/magic.h> | 
|---|
| 23 | #include <linux/slab.h> | 
|---|
| 24 | #include "internal.h" | 
|---|
| 25 |  | 
|---|
| 26 | #define TRACEFS_DEFAULT_MODE	0700 | 
|---|
| 27 | static struct kmem_cache *tracefs_inode_cachep __ro_after_init; | 
|---|
| 28 |  | 
|---|
| 29 | static struct vfsmount *tracefs_mount; | 
|---|
| 30 | static int tracefs_mount_count; | 
|---|
| 31 | static bool tracefs_registered; | 
|---|
| 32 |  | 
|---|
| 33 | /* | 
|---|
| 34 | * Keep track of all tracefs_inodes in order to update their | 
|---|
| 35 | * flags if necessary on a remount. | 
|---|
| 36 | */ | 
|---|
| 37 | static DEFINE_SPINLOCK(tracefs_inode_lock); | 
|---|
| 38 | static LIST_HEAD(tracefs_inodes); | 
|---|
| 39 |  | 
|---|
| 40 | static struct inode *tracefs_alloc_inode(struct super_block *sb) | 
|---|
| 41 | { | 
|---|
| 42 | struct tracefs_inode *ti; | 
|---|
| 43 | unsigned long flags; | 
|---|
| 44 |  | 
|---|
| 45 | ti = alloc_inode_sb(sb, tracefs_inode_cachep, GFP_KERNEL); | 
|---|
| 46 | if (!ti) | 
|---|
| 47 | return NULL; | 
|---|
| 48 |  | 
|---|
| 49 | spin_lock_irqsave(&tracefs_inode_lock, flags); | 
|---|
| 50 | list_add_rcu(new: &ti->list, head: &tracefs_inodes); | 
|---|
| 51 | spin_unlock_irqrestore(lock: &tracefs_inode_lock, flags); | 
|---|
| 52 |  | 
|---|
| 53 | return &ti->vfs_inode; | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | static void tracefs_free_inode(struct inode *inode) | 
|---|
| 57 | { | 
|---|
| 58 | struct tracefs_inode *ti = get_tracefs(inode); | 
|---|
| 59 |  | 
|---|
| 60 | kmem_cache_free(s: tracefs_inode_cachep, objp: ti); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | static void tracefs_destroy_inode(struct inode *inode) | 
|---|
| 64 | { | 
|---|
| 65 | struct tracefs_inode *ti = get_tracefs(inode); | 
|---|
| 66 | unsigned long flags; | 
|---|
| 67 |  | 
|---|
| 68 | spin_lock_irqsave(&tracefs_inode_lock, flags); | 
|---|
| 69 | list_del_rcu(entry: &ti->list); | 
|---|
| 70 | spin_unlock_irqrestore(lock: &tracefs_inode_lock, flags); | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | static ssize_t default_read_file(struct file *file, char __user *buf, | 
|---|
| 74 | size_t count, loff_t *ppos) | 
|---|
| 75 | { | 
|---|
| 76 | return 0; | 
|---|
| 77 | } | 
|---|
| 78 |  | 
|---|
| 79 | static ssize_t default_write_file(struct file *file, const char __user *buf, | 
|---|
| 80 | size_t count, loff_t *ppos) | 
|---|
| 81 | { | 
|---|
| 82 | return count; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | static const struct file_operations tracefs_file_operations = { | 
|---|
| 86 | .read =		default_read_file, | 
|---|
| 87 | .write =	default_write_file, | 
|---|
| 88 | .open =		simple_open, | 
|---|
| 89 | .llseek =	noop_llseek, | 
|---|
| 90 | }; | 
|---|
| 91 |  | 
|---|
| 92 | static struct tracefs_dir_ops { | 
|---|
| 93 | int (*mkdir)(const char *name); | 
|---|
| 94 | int (*rmdir)(const char *name); | 
|---|
| 95 | } tracefs_ops __ro_after_init; | 
|---|
| 96 |  | 
|---|
| 97 | static char *get_dname(struct dentry *dentry) | 
|---|
| 98 | { | 
|---|
| 99 | const char *dname; | 
|---|
| 100 | char *name; | 
|---|
| 101 | int len = dentry->d_name.len; | 
|---|
| 102 |  | 
|---|
| 103 | dname = dentry->d_name.name; | 
|---|
| 104 | name = kmalloc(len + 1, GFP_KERNEL); | 
|---|
| 105 | if (!name) | 
|---|
| 106 | return NULL; | 
|---|
| 107 | memcpy(to: name, from: dname, len); | 
|---|
| 108 | name[len] = 0; | 
|---|
| 109 | return name; | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | static struct dentry *tracefs_syscall_mkdir(struct mnt_idmap *idmap, | 
|---|
| 113 | struct inode *inode, struct dentry *dentry, | 
|---|
| 114 | umode_t mode) | 
|---|
| 115 | { | 
|---|
| 116 | struct tracefs_inode *ti; | 
|---|
| 117 | char *name; | 
|---|
| 118 | int ret; | 
|---|
| 119 |  | 
|---|
| 120 | name = get_dname(dentry); | 
|---|
| 121 | if (!name) | 
|---|
| 122 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 123 |  | 
|---|
| 124 | /* | 
|---|
| 125 | * This is a new directory that does not take the default of | 
|---|
| 126 | * the rootfs. It becomes the default permissions for all the | 
|---|
| 127 | * files and directories underneath it. | 
|---|
| 128 | */ | 
|---|
| 129 | ti = get_tracefs(inode); | 
|---|
| 130 | ti->flags |= TRACEFS_INSTANCE_INODE; | 
|---|
| 131 | ti->private = inode; | 
|---|
| 132 |  | 
|---|
| 133 | /* | 
|---|
| 134 | * The mkdir call can call the generic functions that create | 
|---|
| 135 | * the files within the tracefs system. It is up to the individual | 
|---|
| 136 | * mkdir routine to handle races. | 
|---|
| 137 | */ | 
|---|
| 138 | inode_unlock(inode); | 
|---|
| 139 | ret = tracefs_ops.mkdir(name); | 
|---|
| 140 | inode_lock(inode); | 
|---|
| 141 |  | 
|---|
| 142 | kfree(objp: name); | 
|---|
| 143 |  | 
|---|
| 144 | return ERR_PTR(error: ret); | 
|---|
| 145 | } | 
|---|
| 146 |  | 
|---|
| 147 | static int tracefs_syscall_rmdir(struct inode *inode, struct dentry *dentry) | 
|---|
| 148 | { | 
|---|
| 149 | char *name; | 
|---|
| 150 | int ret; | 
|---|
| 151 |  | 
|---|
| 152 | name = get_dname(dentry); | 
|---|
| 153 | if (!name) | 
|---|
| 154 | return -ENOMEM; | 
|---|
| 155 |  | 
|---|
| 156 | /* | 
|---|
| 157 | * The rmdir call can call the generic functions that create | 
|---|
| 158 | * the files within the tracefs system. It is up to the individual | 
|---|
| 159 | * rmdir routine to handle races. | 
|---|
| 160 | * This time we need to unlock not only the parent (inode) but | 
|---|
| 161 | * also the directory that is being deleted. | 
|---|
| 162 | */ | 
|---|
| 163 | inode_unlock(inode); | 
|---|
| 164 | inode_unlock(inode: d_inode(dentry)); | 
|---|
| 165 |  | 
|---|
| 166 | ret = tracefs_ops.rmdir(name); | 
|---|
| 167 |  | 
|---|
| 168 | inode_lock_nested(inode, subclass: I_MUTEX_PARENT); | 
|---|
| 169 | inode_lock(inode: d_inode(dentry)); | 
|---|
| 170 |  | 
|---|
| 171 | kfree(objp: name); | 
|---|
| 172 |  | 
|---|
| 173 | return ret; | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | static void set_tracefs_inode_owner(struct inode *inode) | 
|---|
| 177 | { | 
|---|
| 178 | struct tracefs_inode *ti = get_tracefs(inode); | 
|---|
| 179 | struct inode *root_inode = ti->private; | 
|---|
| 180 | kuid_t uid; | 
|---|
| 181 | kgid_t gid; | 
|---|
| 182 |  | 
|---|
| 183 | uid = root_inode->i_uid; | 
|---|
| 184 | gid = root_inode->i_gid; | 
|---|
| 185 |  | 
|---|
| 186 | /* | 
|---|
| 187 | * If the root is not the mount point, then check the root's | 
|---|
| 188 | * permissions. If it was never set, then default to the | 
|---|
| 189 | * mount point. | 
|---|
| 190 | */ | 
|---|
| 191 | if (root_inode != d_inode(dentry: root_inode->i_sb->s_root)) { | 
|---|
| 192 | struct tracefs_inode *rti; | 
|---|
| 193 |  | 
|---|
| 194 | rti = get_tracefs(inode: root_inode); | 
|---|
| 195 | root_inode = d_inode(dentry: root_inode->i_sb->s_root); | 
|---|
| 196 |  | 
|---|
| 197 | if (!(rti->flags & TRACEFS_UID_PERM_SET)) | 
|---|
| 198 | uid = root_inode->i_uid; | 
|---|
| 199 |  | 
|---|
| 200 | if (!(rti->flags & TRACEFS_GID_PERM_SET)) | 
|---|
| 201 | gid = root_inode->i_gid; | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | /* | 
|---|
| 205 | * If this inode has never been referenced, then update | 
|---|
| 206 | * the permissions to the superblock. | 
|---|
| 207 | */ | 
|---|
| 208 | if (!(ti->flags & TRACEFS_UID_PERM_SET)) | 
|---|
| 209 | inode->i_uid = uid; | 
|---|
| 210 |  | 
|---|
| 211 | if (!(ti->flags & TRACEFS_GID_PERM_SET)) | 
|---|
| 212 | inode->i_gid = gid; | 
|---|
| 213 | } | 
|---|
| 214 |  | 
|---|
| 215 | static int tracefs_permission(struct mnt_idmap *idmap, | 
|---|
| 216 | struct inode *inode, int mask) | 
|---|
| 217 | { | 
|---|
| 218 | set_tracefs_inode_owner(inode); | 
|---|
| 219 | return generic_permission(idmap, inode, mask); | 
|---|
| 220 | } | 
|---|
| 221 |  | 
|---|
| 222 | static int tracefs_getattr(struct mnt_idmap *idmap, | 
|---|
| 223 | const struct path *path, struct kstat *stat, | 
|---|
| 224 | u32 request_mask, unsigned int flags) | 
|---|
| 225 | { | 
|---|
| 226 | struct inode *inode = d_backing_inode(upper: path->dentry); | 
|---|
| 227 |  | 
|---|
| 228 | set_tracefs_inode_owner(inode); | 
|---|
| 229 | generic_fillattr(idmap, request_mask, inode, stat); | 
|---|
| 230 | return 0; | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | static int tracefs_setattr(struct mnt_idmap *idmap, struct dentry *dentry, | 
|---|
| 234 | struct iattr *attr) | 
|---|
| 235 | { | 
|---|
| 236 | unsigned int ia_valid = attr->ia_valid; | 
|---|
| 237 | struct inode *inode = d_inode(dentry); | 
|---|
| 238 | struct tracefs_inode *ti = get_tracefs(inode); | 
|---|
| 239 |  | 
|---|
| 240 | if (ia_valid & ATTR_UID) | 
|---|
| 241 | ti->flags |= TRACEFS_UID_PERM_SET; | 
|---|
| 242 |  | 
|---|
| 243 | if (ia_valid & ATTR_GID) | 
|---|
| 244 | ti->flags |= TRACEFS_GID_PERM_SET; | 
|---|
| 245 |  | 
|---|
| 246 | return simple_setattr(idmap, dentry, attr); | 
|---|
| 247 | } | 
|---|
| 248 |  | 
|---|
| 249 | static const struct inode_operations tracefs_instance_dir_inode_operations = { | 
|---|
| 250 | .lookup		= simple_lookup, | 
|---|
| 251 | .mkdir		= tracefs_syscall_mkdir, | 
|---|
| 252 | .rmdir		= tracefs_syscall_rmdir, | 
|---|
| 253 | .permission	= tracefs_permission, | 
|---|
| 254 | .getattr	= tracefs_getattr, | 
|---|
| 255 | .setattr	= tracefs_setattr, | 
|---|
| 256 | }; | 
|---|
| 257 |  | 
|---|
| 258 | static const struct inode_operations tracefs_dir_inode_operations = { | 
|---|
| 259 | .lookup		= simple_lookup, | 
|---|
| 260 | .permission	= tracefs_permission, | 
|---|
| 261 | .getattr	= tracefs_getattr, | 
|---|
| 262 | .setattr	= tracefs_setattr, | 
|---|
| 263 | }; | 
|---|
| 264 |  | 
|---|
| 265 | static const struct inode_operations tracefs_file_inode_operations = { | 
|---|
| 266 | .permission	= tracefs_permission, | 
|---|
| 267 | .getattr	= tracefs_getattr, | 
|---|
| 268 | .setattr	= tracefs_setattr, | 
|---|
| 269 | }; | 
|---|
| 270 |  | 
|---|
| 271 | struct inode *tracefs_get_inode(struct super_block *sb) | 
|---|
| 272 | { | 
|---|
| 273 | struct inode *inode = new_inode(sb); | 
|---|
| 274 | if (inode) { | 
|---|
| 275 | inode->i_ino = get_next_ino(); | 
|---|
| 276 | simple_inode_init_ts(inode); | 
|---|
| 277 | } | 
|---|
| 278 | return inode; | 
|---|
| 279 | } | 
|---|
| 280 |  | 
|---|
| 281 | struct tracefs_fs_info { | 
|---|
| 282 | kuid_t uid; | 
|---|
| 283 | kgid_t gid; | 
|---|
| 284 | umode_t mode; | 
|---|
| 285 | /* Opt_* bitfield. */ | 
|---|
| 286 | unsigned int opts; | 
|---|
| 287 | }; | 
|---|
| 288 |  | 
|---|
| 289 | enum { | 
|---|
| 290 | Opt_uid, | 
|---|
| 291 | Opt_gid, | 
|---|
| 292 | Opt_mode, | 
|---|
| 293 | }; | 
|---|
| 294 |  | 
|---|
| 295 | static const struct fs_parameter_spec tracefs_param_specs[] = { | 
|---|
| 296 | fsparam_gid	( "gid",		Opt_gid), | 
|---|
| 297 | fsparam_u32oct	( "mode",	Opt_mode), | 
|---|
| 298 | fsparam_uid	( "uid",		Opt_uid), | 
|---|
| 299 | {} | 
|---|
| 300 | }; | 
|---|
| 301 |  | 
|---|
| 302 | static int tracefs_parse_param(struct fs_context *fc, struct fs_parameter *param) | 
|---|
| 303 | { | 
|---|
| 304 | struct tracefs_fs_info *opts = fc->s_fs_info; | 
|---|
| 305 | struct fs_parse_result result; | 
|---|
| 306 | int opt; | 
|---|
| 307 |  | 
|---|
| 308 | opt = fs_parse(fc, desc: tracefs_param_specs, param, result: &result); | 
|---|
| 309 | if (opt < 0) | 
|---|
| 310 | return opt; | 
|---|
| 311 |  | 
|---|
| 312 | switch (opt) { | 
|---|
| 313 | case Opt_uid: | 
|---|
| 314 | opts->uid = result.uid; | 
|---|
| 315 | break; | 
|---|
| 316 | case Opt_gid: | 
|---|
| 317 | opts->gid = result.gid; | 
|---|
| 318 | break; | 
|---|
| 319 | case Opt_mode: | 
|---|
| 320 | opts->mode = result.uint_32 & S_IALLUGO; | 
|---|
| 321 | break; | 
|---|
| 322 | /* | 
|---|
| 323 | * We might like to report bad mount options here; | 
|---|
| 324 | * but traditionally tracefs has ignored all mount options | 
|---|
| 325 | */ | 
|---|
| 326 | } | 
|---|
| 327 |  | 
|---|
| 328 | opts->opts |= BIT(opt); | 
|---|
| 329 |  | 
|---|
| 330 | return 0; | 
|---|
| 331 | } | 
|---|
| 332 |  | 
|---|
| 333 | static int tracefs_apply_options(struct super_block *sb, bool remount) | 
|---|
| 334 | { | 
|---|
| 335 | struct tracefs_fs_info *fsi = sb->s_fs_info; | 
|---|
| 336 | struct inode *inode = d_inode(dentry: sb->s_root); | 
|---|
| 337 | struct tracefs_inode *ti; | 
|---|
| 338 | bool update_uid, update_gid; | 
|---|
| 339 | umode_t tmp_mode; | 
|---|
| 340 |  | 
|---|
| 341 | /* | 
|---|
| 342 | * On remount, only reset mode/uid/gid if they were provided as mount | 
|---|
| 343 | * options. | 
|---|
| 344 | */ | 
|---|
| 345 |  | 
|---|
| 346 | if (!remount || fsi->opts & BIT(Opt_mode)) { | 
|---|
| 347 | tmp_mode = READ_ONCE(inode->i_mode) & ~S_IALLUGO; | 
|---|
| 348 | tmp_mode |= fsi->mode; | 
|---|
| 349 | WRITE_ONCE(inode->i_mode, tmp_mode); | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | if (!remount || fsi->opts & BIT(Opt_uid)) | 
|---|
| 353 | inode->i_uid = fsi->uid; | 
|---|
| 354 |  | 
|---|
| 355 | if (!remount || fsi->opts & BIT(Opt_gid)) | 
|---|
| 356 | inode->i_gid = fsi->gid; | 
|---|
| 357 |  | 
|---|
| 358 | if (remount && (fsi->opts & BIT(Opt_uid) || fsi->opts & BIT(Opt_gid))) { | 
|---|
| 359 |  | 
|---|
| 360 | update_uid = fsi->opts & BIT(Opt_uid); | 
|---|
| 361 | update_gid = fsi->opts & BIT(Opt_gid); | 
|---|
| 362 |  | 
|---|
| 363 | rcu_read_lock(); | 
|---|
| 364 | list_for_each_entry_rcu(ti, &tracefs_inodes, list) { | 
|---|
| 365 | if (update_uid) { | 
|---|
| 366 | ti->flags &= ~TRACEFS_UID_PERM_SET; | 
|---|
| 367 | ti->vfs_inode.i_uid = fsi->uid; | 
|---|
| 368 | } | 
|---|
| 369 |  | 
|---|
| 370 | if (update_gid) { | 
|---|
| 371 | ti->flags &= ~TRACEFS_GID_PERM_SET; | 
|---|
| 372 | ti->vfs_inode.i_gid = fsi->gid; | 
|---|
| 373 | } | 
|---|
| 374 |  | 
|---|
| 375 | /* | 
|---|
| 376 | * Note, the above ti->vfs_inode updates are | 
|---|
| 377 | * used in eventfs_remount() so they must come | 
|---|
| 378 | * before calling it. | 
|---|
| 379 | */ | 
|---|
| 380 | if (ti->flags & TRACEFS_EVENT_INODE) | 
|---|
| 381 | eventfs_remount(ti, update_uid, update_gid); | 
|---|
| 382 | } | 
|---|
| 383 | rcu_read_unlock(); | 
|---|
| 384 | } | 
|---|
| 385 |  | 
|---|
| 386 | return 0; | 
|---|
| 387 | } | 
|---|
| 388 |  | 
|---|
| 389 | static int tracefs_reconfigure(struct fs_context *fc) | 
|---|
| 390 | { | 
|---|
| 391 | struct super_block *sb = fc->root->d_sb; | 
|---|
| 392 | struct tracefs_fs_info *sb_opts = sb->s_fs_info; | 
|---|
| 393 | struct tracefs_fs_info *new_opts = fc->s_fs_info; | 
|---|
| 394 |  | 
|---|
| 395 | if (!new_opts) | 
|---|
| 396 | return 0; | 
|---|
| 397 |  | 
|---|
| 398 | sync_filesystem(sb); | 
|---|
| 399 | /* structure copy of new mount options to sb */ | 
|---|
| 400 | *sb_opts = *new_opts; | 
|---|
| 401 |  | 
|---|
| 402 | return tracefs_apply_options(sb, remount: true); | 
|---|
| 403 | } | 
|---|
| 404 |  | 
|---|
| 405 | static int tracefs_show_options(struct seq_file *m, struct dentry *root) | 
|---|
| 406 | { | 
|---|
| 407 | struct tracefs_fs_info *fsi = root->d_sb->s_fs_info; | 
|---|
| 408 |  | 
|---|
| 409 | if (!uid_eq(left: fsi->uid, GLOBAL_ROOT_UID)) | 
|---|
| 410 | seq_printf(m, ",uid=%u", | 
|---|
| 411 | from_kuid_munged(&init_user_ns, fsi->uid)); | 
|---|
| 412 | if (!gid_eq(fsi->gid, GLOBAL_ROOT_GID)) | 
|---|
| 413 | seq_printf(m, ",gid=%u", | 
|---|
| 414 | from_kgid_munged(&init_user_ns, fsi->gid)); | 
|---|
| 415 | if (fsi->mode != TRACEFS_DEFAULT_MODE) | 
|---|
| 416 | seq_printf(m, ",mode=%o", fsi->mode); | 
|---|
| 417 |  | 
|---|
| 418 | return 0; | 
|---|
| 419 | } | 
|---|
| 420 |  | 
|---|
| 421 | static int tracefs_drop_inode(struct inode *inode) | 
|---|
| 422 | { | 
|---|
| 423 | struct tracefs_inode *ti = get_tracefs(inode); | 
|---|
| 424 |  | 
|---|
| 425 | /* | 
|---|
| 426 | * This inode is being freed and cannot be used for | 
|---|
| 427 | * eventfs. Clear the flag so that it doesn't call into | 
|---|
| 428 | * eventfs during the remount flag updates. The eventfs_inode | 
|---|
| 429 | * gets freed after an RCU cycle, so the content will still | 
|---|
| 430 | * be safe if the iteration is going on now. | 
|---|
| 431 | */ | 
|---|
| 432 | ti->flags &= ~TRACEFS_EVENT_INODE; | 
|---|
| 433 |  | 
|---|
| 434 | return 1; | 
|---|
| 435 | } | 
|---|
| 436 |  | 
|---|
| 437 | static const struct super_operations tracefs_super_operations = { | 
|---|
| 438 | .alloc_inode    = tracefs_alloc_inode, | 
|---|
| 439 | .free_inode     = tracefs_free_inode, | 
|---|
| 440 | .destroy_inode  = tracefs_destroy_inode, | 
|---|
| 441 | .drop_inode     = tracefs_drop_inode, | 
|---|
| 442 | .statfs		= simple_statfs, | 
|---|
| 443 | .show_options	= tracefs_show_options, | 
|---|
| 444 | }; | 
|---|
| 445 |  | 
|---|
| 446 | /* | 
|---|
| 447 | * It would be cleaner if eventfs had its own dentry ops. | 
|---|
| 448 | * | 
|---|
| 449 | * Note that d_revalidate is called potentially under RCU, | 
|---|
| 450 | * so it can't take the eventfs mutex etc. It's fine - if | 
|---|
| 451 | * we open a file just as it's marked dead, things will | 
|---|
| 452 | * still work just fine, and just see the old stale case. | 
|---|
| 453 | */ | 
|---|
| 454 | static void tracefs_d_release(struct dentry *dentry) | 
|---|
| 455 | { | 
|---|
| 456 | if (dentry->d_fsdata) | 
|---|
| 457 | eventfs_d_release(dentry); | 
|---|
| 458 | } | 
|---|
| 459 |  | 
|---|
| 460 | static int tracefs_d_revalidate(struct inode *inode, const struct qstr *name, | 
|---|
| 461 | struct dentry *dentry, unsigned int flags) | 
|---|
| 462 | { | 
|---|
| 463 | struct eventfs_inode *ei = dentry->d_fsdata; | 
|---|
| 464 |  | 
|---|
| 465 | return !(ei && ei->is_freed); | 
|---|
| 466 | } | 
|---|
| 467 |  | 
|---|
| 468 | static int tracefs_d_delete(const struct dentry *dentry) | 
|---|
| 469 | { | 
|---|
| 470 | /* | 
|---|
| 471 | * We want to keep eventfs dentries around but not tracefs | 
|---|
| 472 | * ones. eventfs dentries have content in d_fsdata. | 
|---|
| 473 | * Use d_fsdata to determine if it's a eventfs dentry or not. | 
|---|
| 474 | */ | 
|---|
| 475 | return dentry->d_fsdata == NULL; | 
|---|
| 476 | } | 
|---|
| 477 |  | 
|---|
| 478 | static const struct dentry_operations tracefs_dentry_operations = { | 
|---|
| 479 | .d_revalidate = tracefs_d_revalidate, | 
|---|
| 480 | .d_release = tracefs_d_release, | 
|---|
| 481 | .d_delete = tracefs_d_delete, | 
|---|
| 482 | }; | 
|---|
| 483 |  | 
|---|
| 484 | static int tracefs_fill_super(struct super_block *sb, struct fs_context *fc) | 
|---|
| 485 | { | 
|---|
| 486 | static const struct tree_descr trace_files[] = {{ ""}}; | 
|---|
| 487 | int err; | 
|---|
| 488 |  | 
|---|
| 489 | err = simple_fill_super(sb, TRACEFS_MAGIC, trace_files); | 
|---|
| 490 | if (err) | 
|---|
| 491 | return err; | 
|---|
| 492 |  | 
|---|
| 493 | sb->s_op = &tracefs_super_operations; | 
|---|
| 494 | set_default_d_op(sb, &tracefs_dentry_operations); | 
|---|
| 495 |  | 
|---|
| 496 | return 0; | 
|---|
| 497 | } | 
|---|
| 498 |  | 
|---|
| 499 | static int tracefs_get_tree(struct fs_context *fc) | 
|---|
| 500 | { | 
|---|
| 501 | int err = get_tree_single(fc, fill_super: tracefs_fill_super); | 
|---|
| 502 |  | 
|---|
| 503 | if (err) | 
|---|
| 504 | return err; | 
|---|
| 505 |  | 
|---|
| 506 | return tracefs_reconfigure(fc); | 
|---|
| 507 | } | 
|---|
| 508 |  | 
|---|
| 509 | static void tracefs_free_fc(struct fs_context *fc) | 
|---|
| 510 | { | 
|---|
| 511 | kfree(objp: fc->s_fs_info); | 
|---|
| 512 | } | 
|---|
| 513 |  | 
|---|
| 514 | static const struct fs_context_operations tracefs_context_ops = { | 
|---|
| 515 | .free		= tracefs_free_fc, | 
|---|
| 516 | .parse_param	= tracefs_parse_param, | 
|---|
| 517 | .get_tree	= tracefs_get_tree, | 
|---|
| 518 | .reconfigure	= tracefs_reconfigure, | 
|---|
| 519 | }; | 
|---|
| 520 |  | 
|---|
| 521 | static int tracefs_init_fs_context(struct fs_context *fc) | 
|---|
| 522 | { | 
|---|
| 523 | struct tracefs_fs_info *fsi; | 
|---|
| 524 |  | 
|---|
| 525 | fsi = kzalloc(sizeof(struct tracefs_fs_info), GFP_KERNEL); | 
|---|
| 526 | if (!fsi) | 
|---|
| 527 | return -ENOMEM; | 
|---|
| 528 |  | 
|---|
| 529 | fsi->mode = TRACEFS_DEFAULT_MODE; | 
|---|
| 530 |  | 
|---|
| 531 | fc->s_fs_info = fsi; | 
|---|
| 532 | fc->ops = &tracefs_context_ops; | 
|---|
| 533 | return 0; | 
|---|
| 534 | } | 
|---|
| 535 |  | 
|---|
| 536 | static struct file_system_type trace_fs_type = { | 
|---|
| 537 | .owner =	THIS_MODULE, | 
|---|
| 538 | .name = "tracefs", | 
|---|
| 539 | .init_fs_context = tracefs_init_fs_context, | 
|---|
| 540 | .parameters	= tracefs_param_specs, | 
|---|
| 541 | .kill_sb =	kill_litter_super, | 
|---|
| 542 | }; | 
|---|
| 543 | MODULE_ALIAS_FS( "tracefs"); | 
|---|
| 544 |  | 
|---|
| 545 | struct dentry *tracefs_start_creating(const char *name, struct dentry *parent) | 
|---|
| 546 | { | 
|---|
| 547 | struct dentry *dentry; | 
|---|
| 548 | int error; | 
|---|
| 549 |  | 
|---|
| 550 | pr_debug( "tracefs: creating file '%s'\n",name); | 
|---|
| 551 |  | 
|---|
| 552 | error = simple_pin_fs(&trace_fs_type, mount: &tracefs_mount, | 
|---|
| 553 | count: &tracefs_mount_count); | 
|---|
| 554 | if (error) | 
|---|
| 555 | return ERR_PTR(error); | 
|---|
| 556 |  | 
|---|
| 557 | /* If the parent is not specified, we create it in the root. | 
|---|
| 558 | * We need the root dentry to do this, which is in the super | 
|---|
| 559 | * block. A pointer to that is in the struct vfsmount that we | 
|---|
| 560 | * have around. | 
|---|
| 561 | */ | 
|---|
| 562 | if (!parent) | 
|---|
| 563 | parent = tracefs_mount->mnt_root; | 
|---|
| 564 |  | 
|---|
| 565 | dentry = simple_start_creating(parent, name); | 
|---|
| 566 | if (IS_ERR(ptr: dentry)) | 
|---|
| 567 | simple_release_fs(mount: &tracefs_mount, count: &tracefs_mount_count); | 
|---|
| 568 |  | 
|---|
| 569 | return dentry; | 
|---|
| 570 | } | 
|---|
| 571 |  | 
|---|
| 572 | struct dentry *tracefs_failed_creating(struct dentry *dentry) | 
|---|
| 573 | { | 
|---|
| 574 | inode_unlock(inode: d_inode(dentry: dentry->d_parent)); | 
|---|
| 575 | dput(dentry); | 
|---|
| 576 | simple_release_fs(mount: &tracefs_mount, count: &tracefs_mount_count); | 
|---|
| 577 | return NULL; | 
|---|
| 578 | } | 
|---|
| 579 |  | 
|---|
| 580 | struct dentry *tracefs_end_creating(struct dentry *dentry) | 
|---|
| 581 | { | 
|---|
| 582 | inode_unlock(inode: d_inode(dentry: dentry->d_parent)); | 
|---|
| 583 | return dentry; | 
|---|
| 584 | } | 
|---|
| 585 |  | 
|---|
| 586 | /* Find the inode that this will use for default */ | 
|---|
| 587 | static struct inode *instance_inode(struct dentry *parent, struct inode *inode) | 
|---|
| 588 | { | 
|---|
| 589 | struct tracefs_inode *ti; | 
|---|
| 590 |  | 
|---|
| 591 | /* If parent is NULL then use root inode */ | 
|---|
| 592 | if (!parent) | 
|---|
| 593 | return d_inode(dentry: inode->i_sb->s_root); | 
|---|
| 594 |  | 
|---|
| 595 | /* Find the inode that is flagged as an instance or the root inode */ | 
|---|
| 596 | while (!IS_ROOT(parent)) { | 
|---|
| 597 | ti = get_tracefs(inode: d_inode(dentry: parent)); | 
|---|
| 598 | if (ti->flags & TRACEFS_INSTANCE_INODE) | 
|---|
| 599 | break; | 
|---|
| 600 | parent = parent->d_parent; | 
|---|
| 601 | } | 
|---|
| 602 |  | 
|---|
| 603 | return d_inode(dentry: parent); | 
|---|
| 604 | } | 
|---|
| 605 |  | 
|---|
| 606 | /** | 
|---|
| 607 | * tracefs_create_file - create a file in the tracefs filesystem | 
|---|
| 608 | * @name: a pointer to a string containing the name of the file to create. | 
|---|
| 609 | * @mode: the permission that the file should have. | 
|---|
| 610 | * @parent: a pointer to the parent dentry for this file.  This should be a | 
|---|
| 611 | *          directory dentry if set.  If this parameter is NULL, then the | 
|---|
| 612 | *          file will be created in the root of the tracefs filesystem. | 
|---|
| 613 | * @data: a pointer to something that the caller will want to get to later | 
|---|
| 614 | *        on.  The inode.i_private pointer will point to this value on | 
|---|
| 615 | *        the open() call. | 
|---|
| 616 | * @fops: a pointer to a struct file_operations that should be used for | 
|---|
| 617 | *        this file. | 
|---|
| 618 | * | 
|---|
| 619 | * This is the basic "create a file" function for tracefs.  It allows for a | 
|---|
| 620 | * wide range of flexibility in creating a file, or a directory (if you want | 
|---|
| 621 | * to create a directory, the tracefs_create_dir() function is | 
|---|
| 622 | * recommended to be used instead.) | 
|---|
| 623 | * | 
|---|
| 624 | * This function will return a pointer to a dentry if it succeeds.  This | 
|---|
| 625 | * pointer must be passed to the tracefs_remove() function when the file is | 
|---|
| 626 | * to be removed (no automatic cleanup happens if your module is unloaded, | 
|---|
| 627 | * you are responsible here.)  If an error occurs, %NULL will be returned. | 
|---|
| 628 | * | 
|---|
| 629 | * If tracefs is not enabled in the kernel, the value -%ENODEV will be | 
|---|
| 630 | * returned. | 
|---|
| 631 | */ | 
|---|
| 632 | struct dentry *tracefs_create_file(const char *name, umode_t mode, | 
|---|
| 633 | struct dentry *parent, void *data, | 
|---|
| 634 | const struct file_operations *fops) | 
|---|
| 635 | { | 
|---|
| 636 | struct tracefs_inode *ti; | 
|---|
| 637 | struct dentry *dentry; | 
|---|
| 638 | struct inode *inode; | 
|---|
| 639 |  | 
|---|
| 640 | if (security_locked_down(what: LOCKDOWN_TRACEFS)) | 
|---|
| 641 | return NULL; | 
|---|
| 642 |  | 
|---|
| 643 | if (!(mode & S_IFMT)) | 
|---|
| 644 | mode |= S_IFREG; | 
|---|
| 645 | BUG_ON(!S_ISREG(mode)); | 
|---|
| 646 | dentry = tracefs_start_creating(name, parent); | 
|---|
| 647 |  | 
|---|
| 648 | if (IS_ERR(ptr: dentry)) | 
|---|
| 649 | return NULL; | 
|---|
| 650 |  | 
|---|
| 651 | inode = tracefs_get_inode(sb: dentry->d_sb); | 
|---|
| 652 | if (unlikely(!inode)) | 
|---|
| 653 | return tracefs_failed_creating(dentry); | 
|---|
| 654 |  | 
|---|
| 655 | ti = get_tracefs(inode); | 
|---|
| 656 | ti->private = instance_inode(parent, inode); | 
|---|
| 657 |  | 
|---|
| 658 | inode->i_mode = mode; | 
|---|
| 659 | inode->i_op = &tracefs_file_inode_operations; | 
|---|
| 660 | inode->i_fop = fops ? fops : &tracefs_file_operations; | 
|---|
| 661 | inode->i_private = data; | 
|---|
| 662 | inode->i_uid = d_inode(dentry: dentry->d_parent)->i_uid; | 
|---|
| 663 | inode->i_gid = d_inode(dentry: dentry->d_parent)->i_gid; | 
|---|
| 664 | d_instantiate(dentry, inode); | 
|---|
| 665 | fsnotify_create(dir: d_inode(dentry: dentry->d_parent), dentry); | 
|---|
| 666 | return tracefs_end_creating(dentry); | 
|---|
| 667 | } | 
|---|
| 668 |  | 
|---|
| 669 | static struct dentry *__create_dir(const char *name, struct dentry *parent, | 
|---|
| 670 | const struct inode_operations *ops) | 
|---|
| 671 | { | 
|---|
| 672 | struct tracefs_inode *ti; | 
|---|
| 673 | struct dentry *dentry = tracefs_start_creating(name, parent); | 
|---|
| 674 | struct inode *inode; | 
|---|
| 675 |  | 
|---|
| 676 | if (IS_ERR(ptr: dentry)) | 
|---|
| 677 | return NULL; | 
|---|
| 678 |  | 
|---|
| 679 | inode = tracefs_get_inode(sb: dentry->d_sb); | 
|---|
| 680 | if (unlikely(!inode)) | 
|---|
| 681 | return tracefs_failed_creating(dentry); | 
|---|
| 682 |  | 
|---|
| 683 | /* Do not set bits for OTH */ | 
|---|
| 684 | inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP; | 
|---|
| 685 | inode->i_op = ops; | 
|---|
| 686 | inode->i_fop = &simple_dir_operations; | 
|---|
| 687 | inode->i_uid = d_inode(dentry: dentry->d_parent)->i_uid; | 
|---|
| 688 | inode->i_gid = d_inode(dentry: dentry->d_parent)->i_gid; | 
|---|
| 689 |  | 
|---|
| 690 | ti = get_tracefs(inode); | 
|---|
| 691 | ti->private = instance_inode(parent, inode); | 
|---|
| 692 |  | 
|---|
| 693 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | 
|---|
| 694 | inc_nlink(inode); | 
|---|
| 695 | d_instantiate(dentry, inode); | 
|---|
| 696 | inc_nlink(inode: d_inode(dentry: dentry->d_parent)); | 
|---|
| 697 | fsnotify_mkdir(dir: d_inode(dentry: dentry->d_parent), dentry); | 
|---|
| 698 | return tracefs_end_creating(dentry); | 
|---|
| 699 | } | 
|---|
| 700 |  | 
|---|
| 701 | /** | 
|---|
| 702 | * tracefs_create_dir - create a directory in the tracefs filesystem | 
|---|
| 703 | * @name: a pointer to a string containing the name of the directory to | 
|---|
| 704 | *        create. | 
|---|
| 705 | * @parent: a pointer to the parent dentry for this file.  This should be a | 
|---|
| 706 | *          directory dentry if set.  If this parameter is NULL, then the | 
|---|
| 707 | *          directory will be created in the root of the tracefs filesystem. | 
|---|
| 708 | * | 
|---|
| 709 | * This function creates a directory in tracefs with the given name. | 
|---|
| 710 | * | 
|---|
| 711 | * This function will return a pointer to a dentry if it succeeds.  This | 
|---|
| 712 | * pointer must be passed to the tracefs_remove() function when the file is | 
|---|
| 713 | * to be removed. If an error occurs, %NULL will be returned. | 
|---|
| 714 | * | 
|---|
| 715 | * If tracing is not enabled in the kernel, the value -%ENODEV will be | 
|---|
| 716 | * returned. | 
|---|
| 717 | */ | 
|---|
| 718 | struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) | 
|---|
| 719 | { | 
|---|
| 720 | if (security_locked_down(what: LOCKDOWN_TRACEFS)) | 
|---|
| 721 | return NULL; | 
|---|
| 722 |  | 
|---|
| 723 | return __create_dir(name, parent, ops: &tracefs_dir_inode_operations); | 
|---|
| 724 | } | 
|---|
| 725 |  | 
|---|
| 726 | /** | 
|---|
| 727 | * tracefs_create_instance_dir - create the tracing instances directory | 
|---|
| 728 | * @name: The name of the instances directory to create | 
|---|
| 729 | * @parent: The parent directory that the instances directory will exist | 
|---|
| 730 | * @mkdir: The function to call when a mkdir is performed. | 
|---|
| 731 | * @rmdir: The function to call when a rmdir is performed. | 
|---|
| 732 | * | 
|---|
| 733 | * Only one instances directory is allowed. | 
|---|
| 734 | * | 
|---|
| 735 | * The instances directory is special as it allows for mkdir and rmdir | 
|---|
| 736 | * to be done by userspace. When a mkdir or rmdir is performed, the inode | 
|---|
| 737 | * locks are released and the methods passed in (@mkdir and @rmdir) are | 
|---|
| 738 | * called without locks and with the name of the directory being created | 
|---|
| 739 | * within the instances directory. | 
|---|
| 740 | * | 
|---|
| 741 | * Returns the dentry of the instances directory. | 
|---|
| 742 | */ | 
|---|
| 743 | __init struct dentry *tracefs_create_instance_dir(const char *name, | 
|---|
| 744 | struct dentry *parent, | 
|---|
| 745 | int (*mkdir)(const char *name), | 
|---|
| 746 | int (*rmdir)(const char *name)) | 
|---|
| 747 | { | 
|---|
| 748 | struct dentry *dentry; | 
|---|
| 749 |  | 
|---|
| 750 | /* Only allow one instance of the instances directory. */ | 
|---|
| 751 | if (WARN_ON(tracefs_ops.mkdir || tracefs_ops.rmdir)) | 
|---|
| 752 | return NULL; | 
|---|
| 753 |  | 
|---|
| 754 | dentry = __create_dir(name, parent, ops: &tracefs_instance_dir_inode_operations); | 
|---|
| 755 | if (!dentry) | 
|---|
| 756 | return NULL; | 
|---|
| 757 |  | 
|---|
| 758 | tracefs_ops.mkdir = mkdir; | 
|---|
| 759 | tracefs_ops.rmdir = rmdir; | 
|---|
| 760 |  | 
|---|
| 761 | return dentry; | 
|---|
| 762 | } | 
|---|
| 763 |  | 
|---|
| 764 | static void remove_one(struct dentry *victim) | 
|---|
| 765 | { | 
|---|
| 766 | simple_release_fs(mount: &tracefs_mount, count: &tracefs_mount_count); | 
|---|
| 767 | } | 
|---|
| 768 |  | 
|---|
| 769 | /** | 
|---|
| 770 | * tracefs_remove - recursively removes a directory | 
|---|
| 771 | * @dentry: a pointer to a the dentry of the directory to be removed. | 
|---|
| 772 | * | 
|---|
| 773 | * This function recursively removes a directory tree in tracefs that | 
|---|
| 774 | * was previously created with a call to another tracefs function | 
|---|
| 775 | * (like tracefs_create_file() or variants thereof.) | 
|---|
| 776 | */ | 
|---|
| 777 | void tracefs_remove(struct dentry *dentry) | 
|---|
| 778 | { | 
|---|
| 779 | if (IS_ERR_OR_NULL(ptr: dentry)) | 
|---|
| 780 | return; | 
|---|
| 781 |  | 
|---|
| 782 | simple_pin_fs(&trace_fs_type, mount: &tracefs_mount, count: &tracefs_mount_count); | 
|---|
| 783 | simple_recursive_removal(dentry, callback: remove_one); | 
|---|
| 784 | simple_release_fs(mount: &tracefs_mount, count: &tracefs_mount_count); | 
|---|
| 785 | } | 
|---|
| 786 |  | 
|---|
| 787 | /** | 
|---|
| 788 | * tracefs_initialized - Tells whether tracefs has been registered | 
|---|
| 789 | */ | 
|---|
| 790 | bool tracefs_initialized(void) | 
|---|
| 791 | { | 
|---|
| 792 | return tracefs_registered; | 
|---|
| 793 | } | 
|---|
| 794 |  | 
|---|
| 795 | static void init_once(void *foo) | 
|---|
| 796 | { | 
|---|
| 797 | struct tracefs_inode *ti = (struct tracefs_inode *) foo; | 
|---|
| 798 |  | 
|---|
| 799 | /* inode_init_once() calls memset() on the vfs_inode portion */ | 
|---|
| 800 | inode_init_once(&ti->vfs_inode); | 
|---|
| 801 |  | 
|---|
| 802 | /* Zero out the rest */ | 
|---|
| 803 | memset_after(ti, 0, vfs_inode); | 
|---|
| 804 | } | 
|---|
| 805 |  | 
|---|
| 806 | static int __init tracefs_init(void) | 
|---|
| 807 | { | 
|---|
| 808 | int retval; | 
|---|
| 809 |  | 
|---|
| 810 | tracefs_inode_cachep = kmem_cache_create( "tracefs_inode_cache", | 
|---|
| 811 | sizeof(struct tracefs_inode), | 
|---|
| 812 | 0, (SLAB_RECLAIM_ACCOUNT| | 
|---|
| 813 | SLAB_ACCOUNT), | 
|---|
| 814 | init_once); | 
|---|
| 815 | if (!tracefs_inode_cachep) | 
|---|
| 816 | return -ENOMEM; | 
|---|
| 817 |  | 
|---|
| 818 | retval = sysfs_create_mount_point(parent_kobj: kernel_kobj, name: "tracing"); | 
|---|
| 819 | if (retval) | 
|---|
| 820 | return -EINVAL; | 
|---|
| 821 |  | 
|---|
| 822 | retval = register_filesystem(&trace_fs_type); | 
|---|
| 823 | if (!retval) | 
|---|
| 824 | tracefs_registered = true; | 
|---|
| 825 |  | 
|---|
| 826 | return retval; | 
|---|
| 827 | } | 
|---|
| 828 | core_initcall(tracefs_init); | 
|---|
| 829 |  | 
|---|