2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-01-20 19:36:55 +03:00
/*
* inode . c - part of tracefs , a pseudo file system for activating tracing
*
* Based on debugfs by : Greg Kroah - Hartman < greg @ kroah . com >
*
* Copyright ( C ) 2014 Red Hat Inc , author : Steven Rostedt < srostedt @ redhat . com >
*
* tracefs is the file system that is used by the tracing infrastructure .
*/
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/mount.h>
2015-01-21 19:28:23 +03:00
# include <linux/kobject.h>
2015-01-20 19:36:55 +03:00
# include <linux/namei.h>
# include <linux/tracefs.h>
# include <linux/fsnotify.h>
2019-10-12 03:41:41 +03:00
# include <linux/security.h>
2015-01-20 19:36:55 +03:00
# include <linux/seq_file.h>
# include <linux/parser.h>
# include <linux/magic.h>
# include <linux/slab.h>
# define TRACEFS_DEFAULT_MODE 0700
static struct vfsmount * tracefs_mount ;
static int tracefs_mount_count ;
static bool tracefs_registered ;
static ssize_t default_read_file ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
return 0 ;
}
static ssize_t default_write_file ( struct file * file , const char __user * buf ,
size_t count , loff_t * ppos )
{
return count ;
}
static const struct file_operations tracefs_file_operations = {
. read = default_read_file ,
. write = default_write_file ,
. open = simple_open ,
. llseek = noop_llseek ,
} ;
2015-01-21 18:01:39 +03:00
static struct tracefs_dir_ops {
int ( * mkdir ) ( const char * name ) ;
int ( * rmdir ) ( const char * name ) ;
2018-07-25 20:19:01 +03:00
} tracefs_ops __ro_after_init ;
2015-01-21 18:01:39 +03:00
static char * get_dname ( struct dentry * dentry )
{
const char * dname ;
char * name ;
int len = dentry - > d_name . len ;
dname = dentry - > d_name . name ;
name = kmalloc ( len + 1 , GFP_KERNEL ) ;
if ( ! name )
return NULL ;
memcpy ( name , dname , len ) ;
name [ len ] = 0 ;
return name ;
}
2023-01-13 14:49:15 +03:00
static int tracefs_syscall_mkdir ( struct mnt_idmap * idmap ,
2021-01-21 16:19:43 +03:00
struct inode * inode , struct dentry * dentry ,
umode_t mode )
2015-01-21 18:01:39 +03:00
{
char * name ;
int ret ;
name = get_dname ( dentry ) ;
if ( ! name )
return - ENOMEM ;
/*
* The mkdir call can call the generic functions that create
* the files within the tracefs system . It is up to the individual
* mkdir routine to handle races .
*/
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2015-01-21 18:01:39 +03:00
ret = tracefs_ops . mkdir ( name ) ;
2016-01-22 23:40:57 +03:00
inode_lock ( inode ) ;
2015-01-21 18:01:39 +03:00
kfree ( name ) ;
return ret ;
}
static int tracefs_syscall_rmdir ( struct inode * inode , struct dentry * dentry )
{
char * name ;
int ret ;
name = get_dname ( dentry ) ;
if ( ! name )
return - ENOMEM ;
/*
* The rmdir call can call the generic functions that create
* the files within the tracefs system . It is up to the individual
* rmdir routine to handle races .
* This time we need to unlock not only the parent ( inode ) but
* also the directory that is being deleted .
*/
2016-01-22 23:40:57 +03:00
inode_unlock ( inode ) ;
2021-12-08 18:27:31 +03:00
inode_unlock ( d_inode ( dentry ) ) ;
2015-01-21 18:01:39 +03:00
ret = tracefs_ops . rmdir ( name ) ;
2016-01-22 23:40:57 +03:00
inode_lock_nested ( inode , I_MUTEX_PARENT ) ;
2021-12-08 18:27:31 +03:00
inode_lock ( d_inode ( dentry ) ) ;
2015-01-21 18:01:39 +03:00
kfree ( name ) ;
return ret ;
}
static const struct inode_operations tracefs_dir_inode_operations = {
. lookup = simple_lookup ,
. mkdir = tracefs_syscall_mkdir ,
. rmdir = tracefs_syscall_rmdir ,
} ;
2015-01-20 19:36:55 +03:00
static struct inode * tracefs_get_inode ( struct super_block * sb )
{
struct inode * inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_ino = get_next_ino ( ) ;
2016-09-14 17:48:04 +03:00
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = current_time ( inode ) ;
2015-01-20 19:36:55 +03:00
}
return inode ;
}
struct tracefs_mount_opts {
kuid_t uid ;
kgid_t gid ;
umode_t mode ;
2022-08-27 03:44:17 +03:00
/* Opt_* bitfield. */
unsigned int opts ;
2015-01-20 19:36:55 +03:00
} ;
enum {
Opt_uid ,
Opt_gid ,
Opt_mode ,
Opt_err
} ;
static const match_table_t tokens = {
{ Opt_uid , " uid=%u " } ,
{ Opt_gid , " gid=%u " } ,
{ Opt_mode , " mode=%o " } ,
{ Opt_err , NULL }
} ;
struct tracefs_fs_info {
struct tracefs_mount_opts mount_opts ;
} ;
2021-12-08 01:17:29 +03:00
static void change_gid ( struct dentry * dentry , kgid_t gid )
{
if ( ! dentry - > d_inode )
return ;
dentry - > d_inode - > i_gid = gid ;
}
/*
* Taken from d_walk , but without he need for handling renames .
* Nothing can be renamed while walking the list , as tracefs
* does not support renames . This is only called when mounting
* or remounting the file system , to set all the files to
* the given gid .
*/
static void set_gid ( struct dentry * parent , kgid_t gid )
{
struct dentry * this_parent ;
struct list_head * next ;
this_parent = parent ;
spin_lock ( & this_parent - > d_lock ) ;
change_gid ( this_parent , gid ) ;
repeat :
next = this_parent - > d_subdirs . next ;
resume :
while ( next ! = & this_parent - > d_subdirs ) {
struct list_head * tmp = next ;
struct dentry * dentry = list_entry ( tmp , struct dentry , d_child ) ;
next = tmp - > next ;
spin_lock_nested ( & dentry - > d_lock , DENTRY_D_LOCK_NESTED ) ;
change_gid ( dentry , gid ) ;
if ( ! list_empty ( & dentry - > d_subdirs ) ) {
spin_unlock ( & this_parent - > d_lock ) ;
spin_release ( & dentry - > d_lock . dep_map , _RET_IP_ ) ;
this_parent = dentry ;
spin_acquire ( & this_parent - > d_lock . dep_map , 0 , 1 , _RET_IP_ ) ;
goto repeat ;
}
spin_unlock ( & dentry - > d_lock ) ;
}
/*
* All done at this level . . . ascend and resume the search .
*/
rcu_read_lock ( ) ;
ascend :
if ( this_parent ! = parent ) {
struct dentry * child = this_parent ;
this_parent = child - > d_parent ;
spin_unlock ( & child - > d_lock ) ;
spin_lock ( & this_parent - > d_lock ) ;
/* go into the first sibling still alive */
do {
next = child - > d_child . next ;
if ( next = = & this_parent - > d_subdirs )
goto ascend ;
child = list_entry ( next , struct dentry , d_child ) ;
} while ( unlikely ( child - > d_flags & DCACHE_DENTRY_KILLED ) ) ;
rcu_read_unlock ( ) ;
goto resume ;
}
rcu_read_unlock ( ) ;
spin_unlock ( & this_parent - > d_lock ) ;
return ;
}
2015-01-20 19:36:55 +03:00
static int tracefs_parse_options ( char * data , struct tracefs_mount_opts * opts )
{
substring_t args [ MAX_OPT_ARGS ] ;
int option ;
int token ;
kuid_t uid ;
kgid_t gid ;
char * p ;
2022-08-27 03:44:17 +03:00
opts - > opts = 0 ;
2015-01-20 19:36:55 +03:00
opts - > mode = TRACEFS_DEFAULT_MODE ;
while ( ( p = strsep ( & data , " , " ) ) ! = NULL ) {
if ( ! * p )
continue ;
token = match_token ( p , tokens , args ) ;
switch ( token ) {
case Opt_uid :
if ( match_int ( & args [ 0 ] , & option ) )
return - EINVAL ;
uid = make_kuid ( current_user_ns ( ) , option ) ;
if ( ! uid_valid ( uid ) )
return - EINVAL ;
opts - > uid = uid ;
break ;
case Opt_gid :
if ( match_int ( & args [ 0 ] , & option ) )
return - EINVAL ;
gid = make_kgid ( current_user_ns ( ) , option ) ;
if ( ! gid_valid ( gid ) )
return - EINVAL ;
opts - > gid = gid ;
break ;
case Opt_mode :
if ( match_octal ( & args [ 0 ] , & option ) )
return - EINVAL ;
opts - > mode = option & S_IALLUGO ;
break ;
/*
* We might like to report bad mount options here ;
* but traditionally tracefs has ignored all mount options
*/
}
2022-08-27 03:44:17 +03:00
opts - > opts | = BIT ( token ) ;
2015-01-20 19:36:55 +03:00
}
return 0 ;
}
2022-08-27 03:44:17 +03:00
static int tracefs_apply_options ( struct super_block * sb , bool remount )
2015-01-20 19:36:55 +03:00
{
struct tracefs_fs_info * fsi = sb - > s_fs_info ;
2021-12-08 18:27:31 +03:00
struct inode * inode = d_inode ( sb - > s_root ) ;
2015-01-20 19:36:55 +03:00
struct tracefs_mount_opts * opts = & fsi - > mount_opts ;
2022-08-27 03:44:17 +03:00
/*
* On remount , only reset mode / uid / gid if they were provided as mount
* options .
*/
if ( ! remount | | opts - > opts & BIT ( Opt_mode ) ) {
inode - > i_mode & = ~ S_IALLUGO ;
inode - > i_mode | = opts - > mode ;
}
2015-01-20 19:36:55 +03:00
2022-08-27 03:44:17 +03:00
if ( ! remount | | opts - > opts & BIT ( Opt_uid ) )
inode - > i_uid = opts - > uid ;
2022-02-25 23:34:26 +03:00
2022-08-27 03:44:17 +03:00
if ( ! remount | | opts - > opts & BIT ( Opt_gid ) ) {
/* Set all the group ids to the mount option */
set_gid ( sb - > s_root , opts - > gid ) ;
}
2015-01-20 19:36:55 +03:00
return 0 ;
}
static int tracefs_remount ( struct super_block * sb , int * flags , char * data )
{
int err ;
struct tracefs_fs_info * fsi = sb - > s_fs_info ;
sync_filesystem ( sb ) ;
err = tracefs_parse_options ( data , & fsi - > mount_opts ) ;
if ( err )
goto fail ;
2022-08-27 03:44:17 +03:00
tracefs_apply_options ( sb , true ) ;
2015-01-20 19:36:55 +03:00
fail :
return err ;
}
static int tracefs_show_options ( struct seq_file * m , struct dentry * root )
{
struct tracefs_fs_info * fsi = root - > d_sb - > s_fs_info ;
struct tracefs_mount_opts * opts = & fsi - > mount_opts ;
if ( ! uid_eq ( opts - > uid , GLOBAL_ROOT_UID ) )
seq_printf ( m , " ,uid=%u " ,
from_kuid_munged ( & init_user_ns , opts - > uid ) ) ;
if ( ! gid_eq ( opts - > gid , GLOBAL_ROOT_GID ) )
seq_printf ( m , " ,gid=%u " ,
from_kgid_munged ( & init_user_ns , opts - > gid ) ) ;
if ( opts - > mode ! = TRACEFS_DEFAULT_MODE )
seq_printf ( m , " ,mode=%o " , opts - > mode ) ;
return 0 ;
}
static const struct super_operations tracefs_super_operations = {
. statfs = simple_statfs ,
. remount_fs = tracefs_remount ,
. show_options = tracefs_show_options ,
} ;
static int trace_fill_super ( struct super_block * sb , void * data , int silent )
{
2017-03-26 07:15:37 +03:00
static const struct tree_descr trace_files [ ] = { { " " } } ;
2015-01-20 19:36:55 +03:00
struct tracefs_fs_info * fsi ;
int err ;
fsi = kzalloc ( sizeof ( struct tracefs_fs_info ) , GFP_KERNEL ) ;
sb - > s_fs_info = fsi ;
if ( ! fsi ) {
err = - ENOMEM ;
goto fail ;
}
err = tracefs_parse_options ( data , & fsi - > mount_opts ) ;
if ( err )
goto fail ;
err = simple_fill_super ( sb , TRACEFS_MAGIC , trace_files ) ;
if ( err )
goto fail ;
sb - > s_op = & tracefs_super_operations ;
2022-08-27 03:44:17 +03:00
tracefs_apply_options ( sb , false ) ;
2015-01-20 19:36:55 +03:00
return 0 ;
fail :
kfree ( fsi ) ;
sb - > s_fs_info = NULL ;
return err ;
}
static struct dentry * trace_mount ( struct file_system_type * fs_type ,
int flags , const char * dev_name ,
void * data )
{
return mount_single ( fs_type , flags , data , trace_fill_super ) ;
}
static struct file_system_type trace_fs_type = {
. owner = THIS_MODULE ,
. name = " tracefs " ,
. mount = trace_mount ,
. kill_sb = kill_litter_super ,
} ;
MODULE_ALIAS_FS ( " tracefs " ) ;
static struct dentry * start_creating ( const char * name , struct dentry * parent )
{
struct dentry * dentry ;
int error ;
pr_debug ( " tracefs: creating file '%s' \n " , name ) ;
error = simple_pin_fs ( & trace_fs_type , & tracefs_mount ,
& tracefs_mount_count ) ;
if ( error )
return ERR_PTR ( error ) ;
/* If the parent is not specified, we create it in the root.
* We need the root dentry to do this , which is in the super
* block . A pointer to that is in the struct vfsmount that we
* have around .
*/
if ( ! parent )
parent = tracefs_mount - > mnt_root ;
2021-12-08 18:27:31 +03:00
inode_lock ( d_inode ( parent ) ) ;
if ( unlikely ( IS_DEADDIR ( d_inode ( parent ) ) ) )
2019-11-18 17:43:10 +03:00
dentry = ERR_PTR ( - ENOENT ) ;
else
dentry = lookup_one_len ( name , parent , strlen ( name ) ) ;
2021-12-08 18:27:31 +03:00
if ( ! IS_ERR ( dentry ) & & d_inode ( dentry ) ) {
2015-01-20 19:36:55 +03:00
dput ( dentry ) ;
dentry = ERR_PTR ( - EEXIST ) ;
}
2015-11-05 01:33:17 +03:00
if ( IS_ERR ( dentry ) ) {
2021-12-08 18:27:31 +03:00
inode_unlock ( d_inode ( parent ) ) ;
2015-11-05 01:33:17 +03:00
simple_release_fs ( & tracefs_mount , & tracefs_mount_count ) ;
}
2015-01-20 19:36:55 +03:00
return dentry ;
}
static struct dentry * failed_creating ( struct dentry * dentry )
{
2021-12-08 18:27:31 +03:00
inode_unlock ( d_inode ( dentry - > d_parent ) ) ;
2015-01-20 19:36:55 +03:00
dput ( dentry ) ;
simple_release_fs ( & tracefs_mount , & tracefs_mount_count ) ;
return NULL ;
}
static struct dentry * end_creating ( struct dentry * dentry )
{
2021-12-08 18:27:31 +03:00
inode_unlock ( d_inode ( dentry - > d_parent ) ) ;
2015-01-20 19:36:55 +03:00
return dentry ;
}
/**
* tracefs_create_file - create a file in the tracefs filesystem
* @ name : a pointer to a string containing the name of the file to create .
* @ mode : the permission that the file should have .
* @ parent : a pointer to the parent dentry for this file . This should be a
* directory dentry if set . If this parameter is NULL , then the
* file will be created in the root of the tracefs filesystem .
* @ data : a pointer to something that the caller will want to get to later
* on . The inode . i_private pointer will point to this value on
* the open ( ) call .
* @ fops : a pointer to a struct file_operations that should be used for
* this file .
*
* This is the basic " create a file " function for tracefs . It allows for a
* wide range of flexibility in creating a file , or a directory ( if you want
* to create a directory , the tracefs_create_dir ( ) function is
* recommended to be used instead . )
*
* This function will return a pointer to a dentry if it succeeds . This
* pointer must be passed to the tracefs_remove ( ) function when the file is
* to be removed ( no automatic cleanup happens if your module is unloaded ,
* you are responsible here . ) If an error occurs , % NULL will be returned .
*
* If tracefs is not enabled in the kernel , the value - % ENODEV will be
* returned .
*/
struct dentry * tracefs_create_file ( const char * name , umode_t mode ,
struct dentry * parent , void * data ,
const struct file_operations * fops )
{
struct dentry * dentry ;
struct inode * inode ;
2019-10-12 03:41:41 +03:00
if ( security_locked_down ( LOCKDOWN_TRACEFS ) )
return NULL ;
2015-01-20 19:36:55 +03:00
if ( ! ( mode & S_IFMT ) )
mode | = S_IFREG ;
BUG_ON ( ! S_ISREG ( mode ) ) ;
dentry = start_creating ( name , parent ) ;
if ( IS_ERR ( dentry ) )
return NULL ;
inode = tracefs_get_inode ( dentry - > d_sb ) ;
if ( unlikely ( ! inode ) )
return failed_creating ( dentry ) ;
inode - > i_mode = mode ;
2019-10-11 20:54:58 +03:00
inode - > i_fop = fops ? fops : & tracefs_file_operations ;
2015-01-20 19:36:55 +03:00
inode - > i_private = data ;
2021-12-08 15:57:20 +03:00
inode - > i_uid = d_inode ( dentry - > d_parent ) - > i_uid ;
inode - > i_gid = d_inode ( dentry - > d_parent ) - > i_gid ;
2015-01-20 19:36:55 +03:00
d_instantiate ( dentry , inode ) ;
2021-12-08 18:27:31 +03:00
fsnotify_create ( d_inode ( dentry - > d_parent ) , dentry ) ;
2015-01-20 19:36:55 +03:00
return end_creating ( dentry ) ;
}
2015-01-21 18:01:39 +03:00
static struct dentry * __create_dir ( const char * name , struct dentry * parent ,
const struct inode_operations * ops )
{
struct dentry * dentry = start_creating ( name , parent ) ;
struct inode * inode ;
if ( IS_ERR ( dentry ) )
return NULL ;
inode = tracefs_get_inode ( dentry - > d_sb ) ;
if ( unlikely ( ! inode ) )
return failed_creating ( dentry ) ;
2021-08-18 18:24:50 +03:00
/* Do not set bits for OTH */
inode - > i_mode = S_IFDIR | S_IRWXU | S_IRUSR | S_IRGRP | S_IXUSR | S_IXGRP ;
2015-01-21 18:01:39 +03:00
inode - > i_op = ops ;
inode - > i_fop = & simple_dir_operations ;
2021-12-08 15:57:20 +03:00
inode - > i_uid = d_inode ( dentry - > d_parent ) - > i_uid ;
inode - > i_gid = d_inode ( dentry - > d_parent ) - > i_gid ;
2015-01-21 18:01:39 +03:00
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink ( inode ) ;
d_instantiate ( dentry , inode ) ;
2021-12-08 18:27:31 +03:00
inc_nlink ( d_inode ( dentry - > d_parent ) ) ;
fsnotify_mkdir ( d_inode ( dentry - > d_parent ) , dentry ) ;
2015-01-21 18:01:39 +03:00
return end_creating ( dentry ) ;
}
2015-01-20 19:36:55 +03:00
/**
* tracefs_create_dir - create a directory in the tracefs filesystem
* @ name : a pointer to a string containing the name of the directory to
* create .
* @ parent : a pointer to the parent dentry for this file . This should be a
* directory dentry if set . If this parameter is NULL , then the
* directory will be created in the root of the tracefs filesystem .
*
* This function creates a directory in tracefs with the given name .
*
* This function will return a pointer to a dentry if it succeeds . This
* pointer must be passed to the tracefs_remove ( ) function when the file is
* to be removed . If an error occurs , % NULL will be returned .
*
* If tracing is not enabled in the kernel , the value - % ENODEV will be
* returned .
*/
struct dentry * tracefs_create_dir ( const char * name , struct dentry * parent )
{
2015-01-21 18:01:39 +03:00
return __create_dir ( name , parent , & simple_dir_inode_operations ) ;
}
2015-01-20 19:36:55 +03:00
2015-01-21 18:01:39 +03:00
/**
* tracefs_create_instance_dir - create the tracing instances directory
* @ name : The name of the instances directory to create
* @ parent : The parent directory that the instances directory will exist
* @ mkdir : The function to call when a mkdir is performed .
* @ rmdir : The function to call when a rmdir is performed .
*
* Only one instances directory is allowed .
*
2022-06-05 12:27:29 +03:00
* The instances directory is special as it allows for mkdir and rmdir
2015-01-21 18:01:39 +03:00
* to be done by userspace . When a mkdir or rmdir is performed , the inode
2021-03-23 20:49:35 +03:00
* locks are released and the methods passed in ( @ mkdir and @ rmdir ) are
2015-01-21 18:01:39 +03:00
* called without locks and with the name of the directory being created
* within the instances directory .
*
* Returns the dentry of the instances directory .
*/
2018-07-25 20:19:01 +03:00
__init struct dentry * tracefs_create_instance_dir ( const char * name ,
struct dentry * parent ,
2015-01-21 18:01:39 +03:00
int ( * mkdir ) ( const char * name ) ,
int ( * rmdir ) ( const char * name ) )
{
struct dentry * dentry ;
/* Only allow one instance of the instances directory. */
if ( WARN_ON ( tracefs_ops . mkdir | | tracefs_ops . rmdir ) )
2015-01-20 19:36:55 +03:00
return NULL ;
2015-01-21 18:01:39 +03:00
dentry = __create_dir ( name , parent , & tracefs_dir_inode_operations ) ;
if ( ! dentry )
return NULL ;
2015-01-20 19:36:55 +03:00
2015-01-21 18:01:39 +03:00
tracefs_ops . mkdir = mkdir ;
tracefs_ops . rmdir = rmdir ;
2015-01-20 19:36:55 +03:00
2015-01-21 18:01:39 +03:00
return dentry ;
2015-01-20 19:36:55 +03:00
}
2019-11-18 17:43:10 +03:00
static void remove_one ( struct dentry * victim )
2015-01-20 19:36:55 +03:00
{
2019-11-18 17:43:10 +03:00
simple_release_fs ( & tracefs_mount , & tracefs_mount_count ) ;
2015-01-20 19:36:55 +03:00
}
/**
2019-11-18 17:43:10 +03:00
* tracefs_remove - recursively removes a directory
2015-01-20 19:36:55 +03:00
* @ dentry : a pointer to a the dentry of the directory to be removed .
*
* This function recursively removes a directory tree in tracefs that
* was previously created with a call to another tracefs function
* ( like tracefs_create_file ( ) or variants thereof . )
*/
2019-11-18 17:43:10 +03:00
void tracefs_remove ( struct dentry * dentry )
2015-01-20 19:36:55 +03:00
{
if ( IS_ERR_OR_NULL ( dentry ) )
return ;
2019-11-18 17:43:10 +03:00
simple_pin_fs ( & trace_fs_type , & tracefs_mount , & tracefs_mount_count ) ;
simple_recursive_removal ( dentry , remove_one ) ;
simple_release_fs ( & tracefs_mount , & tracefs_mount_count ) ;
2015-01-20 19:36:55 +03:00
}
/**
* tracefs_initialized - Tells whether tracefs has been registered
*/
bool tracefs_initialized ( void )
{
return tracefs_registered ;
}
static int __init tracefs_init ( void )
{
int retval ;
2015-05-14 01:35:41 +03:00
retval = sysfs_create_mount_point ( kernel_kobj , " tracing " ) ;
if ( retval )
2015-01-21 19:28:23 +03:00
return - EINVAL ;
2015-01-20 19:36:55 +03:00
retval = register_filesystem ( & trace_fs_type ) ;
if ( ! retval )
tracefs_registered = true ;
return retval ;
}
core_initcall ( tracefs_init ) ;