2005-04-17 02:20:36 +04:00
/*
* inode . c - basic inode and dentry operations .
*
* sysfs is Copyright ( c ) 2001 - 3 Patrick Mochel
*
* Please see Documentation / filesystems / sysfs . txt for more information .
*/
# undef DEBUG
# include <linux/pagemap.h>
# include <linux/namei.h>
# include <linux/backing-dev.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2006-07-11 10:05:25 +04:00
# include <linux/errno.h>
2005-04-17 02:20:36 +04:00
# include "sysfs.h"
extern struct super_block * sysfs_sb ;
2006-06-28 15:26:44 +04:00
static const struct address_space_operations sysfs_aops = {
2005-04-17 02:20:36 +04:00
. readpage = simple_readpage ,
. prepare_write = simple_prepare_write ,
. commit_write = simple_commit_write
} ;
static struct backing_dev_info sysfs_backing_dev_info = {
. ra_pages = 0 , /* No readahead */
. capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK ,
} ;
2005-05-31 09:09:14 +04:00
static struct inode_operations sysfs_inode_operations = {
. setattr = sysfs_setattr ,
} ;
int sysfs_setattr ( struct dentry * dentry , struct iattr * iattr )
{
struct inode * inode = dentry - > d_inode ;
struct sysfs_dirent * sd = dentry - > d_fsdata ;
struct iattr * sd_iattr ;
unsigned int ia_valid = iattr - > ia_valid ;
int error ;
if ( ! sd )
return - EINVAL ;
sd_iattr = sd - > s_iattr ;
error = inode_change_ok ( inode , iattr ) ;
if ( error )
return error ;
error = inode_setattr ( inode , iattr ) ;
if ( error )
return error ;
if ( ! sd_iattr ) {
/* setting attributes for the first time, allocate now */
2006-02-22 13:18:15 +03:00
sd_iattr = kzalloc ( sizeof ( struct iattr ) , GFP_KERNEL ) ;
2005-05-31 09:09:14 +04:00
if ( ! sd_iattr )
return - ENOMEM ;
/* assign default attributes */
sd_iattr - > ia_mode = sd - > s_mode ;
sd_iattr - > ia_uid = 0 ;
sd_iattr - > ia_gid = 0 ;
sd_iattr - > ia_atime = sd_iattr - > ia_mtime = sd_iattr - > ia_ctime = CURRENT_TIME ;
sd - > s_iattr = sd_iattr ;
}
/* attributes were changed atleast once in past */
if ( ia_valid & ATTR_UID )
sd_iattr - > ia_uid = iattr - > ia_uid ;
if ( ia_valid & ATTR_GID )
sd_iattr - > ia_gid = iattr - > ia_gid ;
if ( ia_valid & ATTR_ATIME )
sd_iattr - > ia_atime = timespec_trunc ( iattr - > ia_atime ,
inode - > i_sb - > s_time_gran ) ;
if ( ia_valid & ATTR_MTIME )
sd_iattr - > ia_mtime = timespec_trunc ( iattr - > ia_mtime ,
inode - > i_sb - > s_time_gran ) ;
if ( ia_valid & ATTR_CTIME )
sd_iattr - > ia_ctime = timespec_trunc ( iattr - > ia_ctime ,
inode - > i_sb - > s_time_gran ) ;
if ( ia_valid & ATTR_MODE ) {
umode_t mode = iattr - > ia_mode ;
if ( ! in_group_p ( inode - > i_gid ) & & ! capable ( CAP_FSETID ) )
mode & = ~ S_ISGID ;
2005-07-29 23:14:19 +04:00
sd_iattr - > ia_mode = sd - > s_mode = mode ;
2005-05-31 09:09:14 +04:00
}
return error ;
}
2005-05-31 09:09:52 +04:00
static inline void set_default_inode_attr ( struct inode * inode , mode_t mode )
{
inode - > i_mode = mode ;
inode - > i_uid = 0 ;
inode - > i_gid = 0 ;
inode - > i_atime = inode - > i_mtime = inode - > i_ctime = CURRENT_TIME ;
}
static inline void set_inode_attr ( struct inode * inode , struct iattr * iattr )
{
inode - > i_mode = iattr - > ia_mode ;
inode - > i_uid = iattr - > ia_uid ;
inode - > i_gid = iattr - > ia_gid ;
inode - > i_atime = iattr - > ia_atime ;
inode - > i_mtime = iattr - > ia_mtime ;
inode - > i_ctime = iattr - > ia_ctime ;
}
2006-07-12 20:03:06 +04:00
/*
* sysfs has a different i_mutex lock order behavior for i_mutex than other
* filesystems ; sysfs i_mutex is called in many places with subsystem locks
* held . At the same time , many of the VFS locking rules do not apply to
* sysfs at all ( cross directory rename for example ) . To untangle this mess
* ( which gives false positives in lockdep ) , we ' re giving sysfs inodes their
* own class for i_mutex .
*/
static struct lock_class_key sysfs_inode_imutex_key ;
2005-05-31 09:09:52 +04:00
struct inode * sysfs_new_inode ( mode_t mode , struct sysfs_dirent * sd )
2005-04-17 02:20:36 +04:00
{
struct inode * inode = new_inode ( sysfs_sb ) ;
if ( inode ) {
inode - > i_blocks = 0 ;
inode - > i_mapping - > a_ops = & sysfs_aops ;
inode - > i_mapping - > backing_dev_info = & sysfs_backing_dev_info ;
2005-05-31 09:09:52 +04:00
inode - > i_op = & sysfs_inode_operations ;
2006-07-12 20:03:06 +04:00
lockdep_set_class ( & inode - > i_mutex , & sysfs_inode_imutex_key ) ;
2005-05-31 09:09:52 +04:00
if ( sd - > s_iattr ) {
/* sysfs_dirent has non-default attributes
* get them for the new inode from persistent copy
* in sysfs_dirent
*/
set_inode_attr ( inode , sd - > s_iattr ) ;
} else
set_default_inode_attr ( inode , mode ) ;
2005-04-17 02:20:36 +04:00
}
return inode ;
}
int sysfs_create ( struct dentry * dentry , int mode , int ( * init ) ( struct inode * ) )
{
int error = 0 ;
struct inode * inode = NULL ;
if ( dentry ) {
if ( ! dentry - > d_inode ) {
2005-05-31 09:09:52 +04:00
struct sysfs_dirent * sd = dentry - > d_fsdata ;
if ( ( inode = sysfs_new_inode ( mode , sd ) ) ) {
2005-04-17 02:20:36 +04:00
if ( dentry - > d_parent & & dentry - > d_parent - > d_inode ) {
struct inode * p_inode = dentry - > d_parent - > d_inode ;
p_inode - > i_mtime = p_inode - > i_ctime = CURRENT_TIME ;
}
goto Proceed ;
}
else
error = - ENOMEM ;
} else
error = - EEXIST ;
} else
error = - ENOENT ;
goto Done ;
Proceed :
if ( init )
error = init ( inode ) ;
if ( ! error ) {
d_instantiate ( dentry , inode ) ;
if ( S_ISDIR ( mode ) )
dget ( dentry ) ; /* pin only directory dentry in core */
} else
iput ( inode ) ;
Done :
return error ;
}
/*
* Get the name for corresponding element represented by the given sysfs_dirent
*/
const unsigned char * sysfs_get_name ( struct sysfs_dirent * sd )
{
struct attribute * attr ;
struct bin_attribute * bin_attr ;
struct sysfs_symlink * sl ;
2006-04-01 03:18:38 +04:00
BUG_ON ( ! sd | | ! sd - > s_element ) ;
2005-04-17 02:20:36 +04:00
switch ( sd - > s_type ) {
case SYSFS_DIR :
/* Always have a dentry so use that */
return sd - > s_dentry - > d_name . name ;
case SYSFS_KOBJ_ATTR :
attr = sd - > s_element ;
return attr - > name ;
case SYSFS_KOBJ_BIN_ATTR :
bin_attr = sd - > s_element ;
return bin_attr - > attr . name ;
case SYSFS_KOBJ_LINK :
sl = sd - > s_element ;
return sl - > link_name ;
}
return NULL ;
}
/*
* Unhashes the dentry corresponding to given sysfs_dirent
2006-01-10 02:59:24 +03:00
* Called with parent inode ' s i_mutex held .
2005-04-17 02:20:36 +04:00
*/
void sysfs_drop_dentry ( struct sysfs_dirent * sd , struct dentry * parent )
{
struct dentry * dentry = sd - > s_dentry ;
if ( dentry ) {
spin_lock ( & dcache_lock ) ;
spin_lock ( & dentry - > d_lock ) ;
if ( ! ( d_unhashed ( dentry ) & & dentry - > d_inode ) ) {
dget_locked ( dentry ) ;
__d_drop ( dentry ) ;
spin_unlock ( & dentry - > d_lock ) ;
spin_unlock ( & dcache_lock ) ;
simple_unlink ( parent - > d_inode , dentry ) ;
} else {
spin_unlock ( & dentry - > d_lock ) ;
spin_unlock ( & dcache_lock ) ;
}
}
}
2006-07-11 10:05:25 +04:00
int sysfs_hash_and_remove ( struct dentry * dir , const char * name )
2005-04-17 02:20:36 +04:00
{
struct sysfs_dirent * sd ;
2006-03-17 02:44:26 +03:00
struct sysfs_dirent * parent_sd ;
2006-07-11 10:05:25 +04:00
int found = 0 ;
2006-03-17 02:44:26 +03:00
if ( ! dir )
2006-07-11 10:05:25 +04:00
return - ENOENT ;
2005-04-17 02:20:36 +04:00
2005-08-27 05:34:17 +04:00
if ( dir - > d_inode = = NULL )
/* no inode means this hasn't been made visible yet */
2006-07-11 10:05:25 +04:00
return - ENOENT ;
2005-08-27 05:34:17 +04:00
2006-03-17 02:44:26 +03:00
parent_sd = dir - > d_fsdata ;
2006-01-10 02:59:24 +03:00
mutex_lock ( & dir - > d_inode - > i_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each_entry ( sd , & parent_sd - > s_children , s_sibling ) {
if ( ! sd - > s_element )
continue ;
if ( ! strcmp ( sysfs_get_name ( sd ) , name ) ) {
list_del_init ( & sd - > s_sibling ) ;
sysfs_drop_dentry ( sd , dir ) ;
sysfs_put ( sd ) ;
2006-07-11 10:05:25 +04:00
found = 1 ;
2005-04-17 02:20:36 +04:00
break ;
}
}
2006-01-10 02:59:24 +03:00
mutex_unlock ( & dir - > d_inode - > i_mutex ) ;
2006-07-11 10:05:25 +04:00
return found ? 0 : - ENOENT ;
2005-04-17 02:20:36 +04:00
}