2005-04-17 02:20:36 +04:00
/*
* file . c - part of debugfs , a tiny little debug file system
*
* Copyright ( C ) 2004 Greg Kroah - Hartman < greg @ kroah . com >
* Copyright ( C ) 2004 IBM Inc .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation .
*
* debugfs is for people to use instead of / proc or / sys .
* See Documentation / DocBook / kernel - api for more details .
*
*/
/* uncomment to get debug messages from the debug filesystem, ah the irony. */
/* #define DEBUG */
# include <linux/config.h>
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/mount.h>
# include <linux/pagemap.h>
# include <linux/init.h>
# include <linux/namei.h>
# include <linux/debugfs.h>
# define DEBUGFS_MAGIC 0x64626720
/* declared over in file.c */
extern struct file_operations debugfs_file_operations ;
static struct vfsmount * debugfs_mount ;
static int debugfs_mount_count ;
static struct inode * debugfs_get_inode ( struct super_block * sb , int mode , dev_t dev )
{
struct inode * inode = new_inode ( sb ) ;
if ( inode ) {
inode - > i_mode = mode ;
inode - > i_uid = 0 ;
inode - > i_gid = 0 ;
inode - > i_blksize = PAGE_CACHE_SIZE ;
inode - > i_blocks = 0 ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
switch ( mode & S_IFMT ) {
default :
init_special_inode ( inode , mode , dev ) ;
break ;
case S_IFREG :
inode - > i_fop = & debugfs_file_operations ;
break ;
case S_IFDIR :
inode - > i_op = & simple_dir_inode_operations ;
inode - > i_fop = & simple_dir_operations ;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inode - > i_nlink + + ;
break ;
}
}
return inode ;
}
/* SMP-safe */
static int debugfs_mknod ( struct inode * dir , struct dentry * dentry ,
int mode , dev_t dev )
{
struct inode * inode = debugfs_get_inode ( dir - > i_sb , mode , dev ) ;
int error = - EPERM ;
if ( dentry - > d_inode )
return - EEXIST ;
if ( inode ) {
d_instantiate ( dentry , inode ) ;
dget ( dentry ) ;
error = 0 ;
}
return error ;
}
static int debugfs_mkdir ( struct inode * dir , struct dentry * dentry , int mode )
{
int res ;
mode = ( mode & ( S_IRWXUGO | S_ISVTX ) ) | S_IFDIR ;
res = debugfs_mknod ( dir , dentry , mode , 0 ) ;
if ( ! res )
dir - > i_nlink + + ;
return res ;
}
static int debugfs_create ( struct inode * dir , struct dentry * dentry , int mode )
{
mode = ( mode & S_IALLUGO ) | S_IFREG ;
return debugfs_mknod ( dir , dentry , mode , 0 ) ;
}
static inline int debugfs_positive ( struct dentry * dentry )
{
return dentry - > d_inode & & ! d_unhashed ( dentry ) ;
}
static int debug_fill_super ( struct super_block * sb , void * data , int silent )
{
static struct tree_descr debug_files [ ] = { { " " } } ;
return simple_fill_super ( sb , DEBUGFS_MAGIC , debug_files ) ;
}
static struct super_block * debug_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name ,
void * data )
{
return get_sb_single ( fs_type , flags , data , debug_fill_super ) ;
}
static struct file_system_type debug_fs_type = {
. owner = THIS_MODULE ,
. name = " debugfs " ,
. get_sb = debug_get_sb ,
. kill_sb = kill_litter_super ,
} ;
static int debugfs_create_by_name ( const char * name , mode_t mode ,
struct dentry * parent ,
struct dentry * * dentry )
{
int error = 0 ;
/* 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 ) {
if ( debugfs_mount & & debugfs_mount - > mnt_sb ) {
parent = debugfs_mount - > mnt_sb - > s_root ;
}
}
if ( ! parent ) {
pr_debug ( " debugfs: Ah! can not find a parent! \n " ) ;
return - EFAULT ;
}
* dentry = NULL ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2005-06-23 11:09:12 +04:00
* dentry = lookup_one_len ( name , parent , strlen ( name ) ) ;
2005-04-17 02:20:36 +04:00
if ( ! IS_ERR ( dentry ) ) {
if ( ( mode & S_IFMT ) = = S_IFDIR )
error = debugfs_mkdir ( parent - > d_inode , * dentry , mode ) ;
else
error = debugfs_create ( parent - > d_inode , * dentry , mode ) ;
} else
error = PTR_ERR ( dentry ) ;
2006-01-10 02:59:24 +03:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
return error ;
}
/**
* debugfs_create_file - create a file in the debugfs 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 paramater is NULL , then the
* file will be created in the root of the debugfs filesystem .
* @ data : a pointer to something that the caller will want to get to later
* on . The inode . u . generic_ip 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 debugfs . It allows for a
* wide range of flexibility in createing a file , or a directory ( if you
* want to create a directory , the debugfs_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 debugfs_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 debugfs is not enabled in the kernel , the value - ENODEV will be
* returned . It is not wise to check for this value , but rather , check for
* NULL or ! NULL instead as to eliminate the need for # ifdef in the calling
* code .
*/
struct dentry * debugfs_create_file ( const char * name , mode_t mode ,
struct dentry * parent , void * data ,
2006-03-28 13:56:41 +04:00
const struct file_operations * fops )
2005-04-17 02:20:36 +04:00
{
struct dentry * dentry = NULL ;
int error ;
pr_debug ( " debugfs: creating file '%s' \n " , name ) ;
error = simple_pin_fs ( " debugfs " , & debugfs_mount , & debugfs_mount_count ) ;
if ( error )
goto exit ;
error = debugfs_create_by_name ( name , mode , parent , & dentry ) ;
if ( error ) {
dentry = NULL ;
goto exit ;
}
if ( dentry - > d_inode ) {
if ( data )
dentry - > d_inode - > u . generic_ip = data ;
if ( fops )
dentry - > d_inode - > i_fop = fops ;
}
exit :
return dentry ;
}
EXPORT_SYMBOL_GPL ( debugfs_create_file ) ;
/**
* debugfs_create_dir - create a directory in the debugfs 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 paramater is NULL , then the
* directory will be created in the root of the debugfs filesystem .
*
* This function creates a directory in debugfs with the given name .
*
* This function will return a pointer to a dentry if it succeeds . This
* pointer must be passed to the debugfs_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 debugfs is not enabled in the kernel , the value - ENODEV will be
* returned . It is not wise to check for this value , but rather , check for
* NULL or ! NULL instead as to eliminate the need for # ifdef in the calling
* code .
*/
struct dentry * debugfs_create_dir ( const char * name , struct dentry * parent )
{
return debugfs_create_file ( name ,
S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO ,
parent , NULL , NULL ) ;
}
EXPORT_SYMBOL_GPL ( debugfs_create_dir ) ;
/**
* debugfs_remove - removes a file or directory from the debugfs filesystem
*
* @ dentry : a pointer to a the dentry of the file or directory to be
* removed .
*
* This function removes a file or directory in debugfs that was previously
* created with a call to another debugfs function ( like
* debufs_create_file ( ) or variants thereof . )
*
* This function is required to be called in order for the file to be
* removed , no automatic cleanup of files will happen when a module is
* removed , you are responsible here .
*/
void debugfs_remove ( struct dentry * dentry )
{
struct dentry * parent ;
if ( ! dentry )
return ;
parent = dentry - > d_parent ;
if ( ! parent | | ! parent - > d_inode )
return ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( debugfs_positive ( dentry ) ) {
if ( dentry - > d_inode ) {
if ( S_ISDIR ( dentry - > d_inode - > i_mode ) )
simple_rmdir ( parent - > d_inode , dentry ) ;
else
simple_unlink ( parent - > d_inode , dentry ) ;
dput ( dentry ) ;
}
}
2006-01-10 02:59:24 +03:00
mutex_unlock ( & parent - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
simple_release_fs ( & debugfs_mount , & debugfs_mount_count ) ;
}
EXPORT_SYMBOL_GPL ( debugfs_remove ) ;
static decl_subsys ( debug , NULL , NULL ) ;
static int __init debugfs_init ( void )
{
int retval ;
kset_set_kset_s ( & debug_subsys , kernel_subsys ) ;
retval = subsystem_register ( & debug_subsys ) ;
if ( retval )
return retval ;
retval = register_filesystem ( & debug_fs_type ) ;
if ( retval )
subsystem_unregister ( & debug_subsys ) ;
return retval ;
}
static void __exit debugfs_exit ( void )
{
simple_release_fs ( & debugfs_mount , & debugfs_mount_count ) ;
unregister_filesystem ( & debug_fs_type ) ;
subsystem_unregister ( & debug_subsys ) ;
}
core_initcall ( debugfs_init ) ;
module_exit ( debugfs_exit ) ;
MODULE_LICENSE ( " GPL " ) ;