2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-06-26 10:19:14 +04:00
/* inode.c: /proc/openprom handling routines
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1996 - 1999 Jakub Jelinek ( jakub @ redhat . com )
* Copyright ( C ) 1998 Eddie C . Dost ( ecd @ skynet . be )
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/fs.h>
2019-03-25 19:38:30 +03:00
# include <linux/fs_context.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/slab.h>
2006-06-26 10:19:14 +04:00
# include <linux/seq_file.h>
2006-09-24 19:13:19 +04:00
# include <linux/magic.h>
2005-04-17 02:20:36 +04:00
# include <asm/openprom.h>
# include <asm/oplib.h>
2006-06-26 10:19:14 +04:00
# include <asm/prom.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
static DEFINE_MUTEX ( op_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
# define OPENPROM_ROOT_INO 0
2006-06-25 16:47:36 +04:00
2006-06-26 10:19:14 +04:00
enum op_inode_type {
op_inode_node ,
op_inode_prop ,
} ;
union op_inode_data {
struct device_node * node ;
struct property * prop ;
} ;
struct op_inode_info {
struct inode vfs_inode ;
enum op_inode_type type ;
union op_inode_data u ;
} ;
2008-02-07 11:15:49 +03:00
static struct inode * openprom_iget ( struct super_block * sb , ino_t ino ) ;
2006-06-26 10:19:14 +04:00
static inline struct op_inode_info * OP_I ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
return container_of ( inode , struct op_inode_info , vfs_inode ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
static int is_string ( unsigned char * p , int len )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
for ( i = 0 ; i < len ; i + + ) {
unsigned char val = p [ i ] ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
if ( ( i & & ! val ) | |
( val > = ' ' & & val < = ' ~ ' ) )
continue ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
return 1 ;
}
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
static int property_show ( struct seq_file * f , void * v )
{
struct property * prop = f - > private ;
void * pval ;
int len ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
len = prop - > length ;
pval = prop - > value ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
if ( is_string ( pval , len ) ) {
while ( len > 0 ) {
int n = strlen ( pval ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
seq_printf ( f , " %s " , ( char * ) pval ) ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
/* Skip over the NULL byte too. */
pval + = n + 1 ;
len - = n + 1 ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
if ( len > 0 )
seq_printf ( f , " + " ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
} else {
if ( len & 3 ) {
while ( len ) {
len - - ;
if ( len )
seq_printf ( f , " %02x. " ,
* ( unsigned char * ) pval ) ;
else
seq_printf ( f , " %02x " ,
* ( unsigned char * ) pval ) ;
pval + + ;
2005-04-17 02:20:36 +04:00
}
} else {
2006-06-26 10:19:14 +04:00
while ( len > = 4 ) {
len - = 4 ;
if ( len )
seq_printf ( f , " %08x. " ,
* ( unsigned int * ) pval ) ;
else
seq_printf ( f , " %08x " ,
* ( unsigned int * ) pval ) ;
pval + = 4 ;
2005-04-17 02:20:36 +04:00
}
}
}
2006-06-26 10:19:14 +04:00
seq_printf ( f , " \n " ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
static void * property_start ( struct seq_file * f , loff_t * pos )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
if ( * pos = = 0 )
return pos ;
return NULL ;
}
static void * property_next ( struct seq_file * f , void * v , loff_t * pos )
{
( * pos ) + + ;
return NULL ;
}
static void property_stop ( struct seq_file * f , void * v )
{
/* Nothing to do */
}
2008-01-23 05:29:20 +03:00
static const struct seq_operations property_op = {
2006-06-26 10:19:14 +04:00
. start = property_start ,
. next = property_next ,
. stop = property_stop ,
. show = property_show
} ;
static int property_open ( struct inode * inode , struct file * file )
{
struct op_inode_info * oi = OP_I ( inode ) ;
int ret ;
BUG_ON ( oi - > type ! = op_inode_prop ) ;
ret = seq_open ( file , & property_op ) ;
if ( ! ret ) {
struct seq_file * m = file - > private_data ;
m - > private = oi - > u . prop ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2006-03-28 13:56:42 +04:00
static const struct file_operations openpromfs_prop_ops = {
2006-06-26 10:19:14 +04:00
. open = property_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
2005-04-17 02:20:36 +04:00
} ;
2013-05-16 09:52:12 +04:00
static int openpromfs_readdir ( struct file * , struct dir_context * ) ;
2005-04-17 02:20:36 +04:00
2006-03-28 13:56:42 +04:00
static const struct file_operations openprom_operations = {
2005-04-17 02:20:36 +04:00
. read = generic_read_dir ,
2016-05-01 05:37:34 +03:00
. iterate_shared = openpromfs_readdir ,
2008-09-03 23:53:01 +04:00
. llseek = generic_file_llseek ,
2005-04-17 02:20:36 +04:00
} ;
2012-06-11 01:13:09 +04:00
static struct dentry * openpromfs_lookup ( struct inode * , struct dentry * , unsigned int ) ;
2005-04-17 02:20:36 +04:00
2007-02-12 11:55:39 +03:00
static const struct inode_operations openprom_inode_operations = {
2005-04-17 02:20:36 +04:00
. lookup = openpromfs_lookup ,
} ;
2012-06-11 01:13:09 +04:00
static struct dentry * openpromfs_lookup ( struct inode * dir , struct dentry * dentry , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
struct op_inode_info * ent_oi , * oi = OP_I ( dir ) ;
struct device_node * dp , * child ;
struct property * prop ;
enum op_inode_type ent_type ;
union op_inode_data ent_data ;
2005-04-17 02:20:36 +04:00
const char * name ;
struct inode * inode ;
2006-06-26 10:19:14 +04:00
unsigned int ino ;
int len ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
BUG_ON ( oi - > type ! = op_inode_node ) ;
dp = oi - > u . node ;
2005-04-17 02:20:36 +04:00
name = dentry - > d_name . name ;
len = dentry - > d_name . len ;
2006-06-26 10:19:14 +04:00
mutex_lock ( & op_mutex ) ;
child = dp - > child ;
while ( child ) {
2018-11-17 00:06:52 +03:00
const char * node_name = kbasename ( child - > full_name ) ;
int n = strlen ( node_name ) ;
2006-06-26 10:19:14 +04:00
if ( len = = n & &
2018-11-17 00:06:52 +03:00
! strncmp ( node_name , name , len ) ) {
2006-06-26 10:19:14 +04:00
ent_type = op_inode_node ;
ent_data . node = child ;
ino = child - > unique_id ;
goto found ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
child = child - > sibling ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
prop = dp - > properties ;
while ( prop ) {
int n = strlen ( prop - > name ) ;
if ( len = = n & & ! strncmp ( prop - > name , name , len ) ) {
ent_type = op_inode_prop ;
ent_data . prop = prop ;
ino = prop - > unique_id ;
goto found ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
prop = prop - > next ;
2005-04-17 02:20:36 +04:00
}
2006-06-26 10:19:14 +04:00
mutex_unlock ( & op_mutex ) ;
return ERR_PTR ( - ENOENT ) ;
found :
2008-02-07 11:15:49 +03:00
inode = openprom_iget ( dir - > i_sb , ino ) ;
2006-06-26 10:19:14 +04:00
mutex_unlock ( & op_mutex ) ;
2008-02-07 11:15:49 +03:00
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
2006-06-26 10:19:14 +04:00
ent_oi = OP_I ( inode ) ;
ent_oi - > type = ent_type ;
ent_oi - > u = ent_data ;
switch ( ent_type ) {
case op_inode_node :
2005-04-17 02:20:36 +04:00
inode - > i_mode = S_IFDIR | S_IRUGO | S_IXUGO ;
2006-06-26 10:19:14 +04:00
inode - > i_op = & openprom_inode_operations ;
2005-04-17 02:20:36 +04:00
inode - > i_fop = & openprom_operations ;
2011-10-28 16:13:29 +04:00
set_nlink ( inode , 2 ) ;
2005-04-17 02:20:36 +04:00
break ;
2006-06-26 10:19:14 +04:00
case op_inode_prop :
2018-11-17 00:06:53 +03:00
if ( of_node_name_eq ( dp , " options " ) & & ( len = = 17 ) & &
2006-06-26 10:19:14 +04:00
! strncmp ( name , " security-password " , 17 ) )
2005-04-17 02:20:36 +04:00
inode - > i_mode = S_IFREG | S_IRUSR | S_IWUSR ;
2006-06-26 10:19:14 +04:00
else
2005-04-17 02:20:36 +04:00
inode - > i_mode = S_IFREG | S_IRUGO ;
inode - > i_fop = & openpromfs_prop_ops ;
2011-10-28 16:13:29 +04:00
set_nlink ( inode , 1 ) ;
2006-06-26 10:19:14 +04:00
inode - > i_size = ent_oi - > u . prop - > length ;
2005-04-17 02:20:36 +04:00
break ;
}
2018-05-03 16:46:23 +03:00
return d_splice_alias ( inode , dentry ) ;
2005-04-17 02:20:36 +04:00
}
2013-05-16 09:52:12 +04:00
static int openpromfs_readdir ( struct file * file , struct dir_context * ctx )
2005-04-17 02:20:36 +04:00
{
2013-05-16 09:52:12 +04:00
struct inode * inode = file_inode ( file ) ;
2006-06-26 10:19:14 +04:00
struct op_inode_info * oi = OP_I ( inode ) ;
struct device_node * dp = oi - > u . node ;
struct device_node * child ;
struct property * prop ;
int i ;
mutex_lock ( & op_mutex ) ;
2005-04-17 02:20:36 +04:00
2013-05-16 09:52:12 +04:00
if ( ctx - > pos = = 0 ) {
if ( ! dir_emit ( ctx , " . " , 1 , inode - > i_ino , DT_DIR ) )
2006-06-26 10:19:14 +04:00
goto out ;
2013-05-16 09:52:12 +04:00
ctx - > pos = 1 ;
}
if ( ctx - > pos = = 1 ) {
if ( ! dir_emit ( ctx , " .. " , 2 ,
2006-06-26 10:19:14 +04:00
( dp - > parent = = NULL ?
OPENPROM_ROOT_INO :
2013-05-16 09:52:12 +04:00
dp - > parent - > unique_id ) , DT_DIR ) )
2005-04-17 02:20:36 +04:00
goto out ;
2013-05-16 09:52:12 +04:00
ctx - > pos = 2 ;
}
i = ctx - > pos - 2 ;
2006-06-26 10:19:14 +04:00
2013-05-16 09:52:12 +04:00
/* First, the children nodes as directories. */
child = dp - > child ;
while ( i & & child ) {
child = child - > sibling ;
i - - ;
}
while ( child ) {
if ( ! dir_emit ( ctx ,
2018-11-17 00:06:52 +03:00
kbasename ( child - > full_name ) ,
strlen ( kbasename ( child - > full_name ) ) ,
2013-05-16 09:52:12 +04:00
child - > unique_id , DT_DIR ) )
goto out ;
2006-06-26 10:19:14 +04:00
2013-05-16 09:52:12 +04:00
ctx - > pos + + ;
child = child - > sibling ;
}
/* Next, the properties as files. */
prop = dp - > properties ;
while ( i & & prop ) {
prop = prop - > next ;
i - - ;
2005-04-17 02:20:36 +04:00
}
2013-05-16 09:52:12 +04:00
while ( prop ) {
if ( ! dir_emit ( ctx , prop - > name , strlen ( prop - > name ) ,
prop - > unique_id , DT_REG ) )
goto out ;
ctx - > pos + + ;
prop = prop - > next ;
}
2005-04-17 02:20:36 +04:00
out :
2006-06-26 10:19:14 +04:00
mutex_unlock ( & op_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-12-07 07:33:20 +03:00
static struct kmem_cache * op_inode_cachep ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
static struct inode * openprom_alloc_inode ( struct super_block * sb )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
struct op_inode_info * oi ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:33:17 +03:00
oi = kmem_cache_alloc ( op_inode_cachep , GFP_KERNEL ) ;
2006-06-26 10:19:14 +04:00
if ( ! oi )
return NULL ;
2005-04-17 02:20:36 +04:00
2006-06-26 10:19:14 +04:00
return & oi - > vfs_inode ;
2005-04-17 02:20:36 +04:00
}
2019-04-16 03:27:27 +03:00
static void openprom_free_inode ( struct inode * inode )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
kmem_cache_free ( op_inode_cachep , OP_I ( inode ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:49 +03:00
static struct inode * openprom_iget ( struct super_block * sb , ino_t ino )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:49 +03:00
struct inode * inode ;
inode = iget_locked ( sb , ino ) ;
if ( ! inode )
return ERR_PTR ( - ENOMEM ) ;
if ( inode - > i_state & I_NEW ) {
2016-09-14 17:48:04 +03:00
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = current_time ( inode ) ;
2008-02-07 11:15:49 +03:00
if ( inode - > i_ino = = OPENPROM_ROOT_INO ) {
inode - > i_op = & openprom_inode_operations ;
inode - > i_fop = & openprom_operations ;
inode - > i_mode = S_IFDIR | S_IRUGO | S_IXUGO ;
}
unlock_new_inode ( inode ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:49 +03:00
return inode ;
2005-04-17 02:20:36 +04:00
}
static int openprom_remount ( struct super_block * sb , int * flags , char * data )
{
2014-03-13 18:14:33 +04:00
sync_filesystem ( sb ) ;
2017-11-28 00:05:09 +03:00
* flags | = SB_NOATIME ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-12 11:55:41 +03:00
static const struct super_operations openprom_sops = {
2006-06-26 10:19:14 +04:00
. alloc_inode = openprom_alloc_inode ,
2019-04-16 03:27:27 +03:00
. free_inode = openprom_free_inode ,
2005-04-17 02:20:36 +04:00
. statfs = simple_statfs ,
. remount_fs = openprom_remount ,
} ;
2019-03-25 19:38:30 +03:00
static int openprom_fill_super ( struct super_block * s , struct fs_context * fc )
2005-04-17 02:20:36 +04:00
{
2006-06-26 10:19:14 +04:00
struct inode * root_inode ;
struct op_inode_info * oi ;
2008-02-07 11:15:49 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2017-11-28 00:05:09 +03:00
s - > s_flags | = SB_NOATIME ;
2005-04-17 02:20:36 +04:00
s - > s_blocksize = 1024 ;
s - > s_blocksize_bits = 10 ;
s - > s_magic = OPENPROM_SUPER_MAGIC ;
s - > s_op = & openprom_sops ;
s - > s_time_gran = 1 ;
2008-02-07 11:15:49 +03:00
root_inode = openprom_iget ( s , OPENPROM_ROOT_INO ) ;
if ( IS_ERR ( root_inode ) ) {
ret = PTR_ERR ( root_inode ) ;
2005-04-17 02:20:36 +04:00
goto out_no_root ;
2008-02-07 11:15:49 +03:00
}
2006-06-26 10:19:14 +04:00
oi = OP_I ( root_inode ) ;
oi - > type = op_inode_node ;
oi - > u . node = of_find_node_by_path ( " / " ) ;
2012-01-09 07:15:13 +04:00
s - > s_root = d_make_root ( root_inode ) ;
2005-04-17 02:20:36 +04:00
if ( ! s - > s_root )
2008-02-07 11:15:49 +03:00
goto out_no_root_dentry ;
2005-04-17 02:20:36 +04:00
return 0 ;
2008-02-07 11:15:49 +03:00
out_no_root_dentry :
ret = - ENOMEM ;
2005-04-17 02:20:36 +04:00
out_no_root :
printk ( " openprom_fill_super: get root inode failed \n " ) ;
2008-02-07 11:15:49 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2019-03-25 19:38:30 +03:00
static int openpromfs_get_tree ( struct fs_context * fc )
2005-04-17 02:20:36 +04:00
{
2019-03-25 19:38:30 +03:00
return get_tree_single ( fc , openprom_fill_super ) ;
}
static const struct fs_context_operations openpromfs_context_ops = {
. get_tree = openpromfs_get_tree ,
} ;
static int openpromfs_init_fs_context ( struct fs_context * fc )
{
fc - > ops = & openpromfs_context_ops ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static struct file_system_type openprom_fs_type = {
. owner = THIS_MODULE ,
. name = " openpromfs " ,
2019-03-25 19:38:30 +03:00
. init_fs_context = openpromfs_init_fs_context ,
2005-04-17 02:20:36 +04:00
. kill_sb = kill_anon_super ,
} ;
2013-03-03 07:39:14 +04:00
MODULE_ALIAS_FS ( " openpromfs " ) ;
2005-04-17 02:20:36 +04:00
2008-07-26 06:45:34 +04:00
static void op_inode_init_once ( void * data )
2006-06-26 10:19:14 +04:00
{
struct op_inode_info * oi = ( struct op_inode_info * ) data ;
2007-05-17 09:10:57 +04:00
inode_init_once ( & oi - > vfs_inode ) ;
2006-06-26 10:19:14 +04:00
}
2005-04-17 02:20:36 +04:00
static int __init init_openprom_fs ( void )
{
2006-06-26 10:19:14 +04:00
int err ;
op_inode_cachep = kmem_cache_create ( " op_inode_cache " ,
sizeof ( struct op_inode_info ) ,
0 ,
( SLAB_RECLAIM_ACCOUNT |
2016-01-15 02:18:21 +03:00
SLAB_MEM_SPREAD | SLAB_ACCOUNT ) ,
2007-07-20 05:11:58 +04:00
op_inode_init_once ) ;
2006-06-26 10:19:14 +04:00
if ( ! op_inode_cachep )
return - ENOMEM ;
err = register_filesystem ( & openprom_fs_type ) ;
if ( err )
kmem_cache_destroy ( op_inode_cachep ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
static void __exit exit_openprom_fs ( void )
{
unregister_filesystem ( & openprom_fs_type ) ;
2012-09-26 05:33:07 +04:00
/*
* Make sure all delayed rcu free inodes are flushed before we
* destroy cache .
*/
rcu_barrier ( ) ;
2006-06-26 10:19:14 +04:00
kmem_cache_destroy ( op_inode_cachep ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( init_openprom_fs )
module_exit ( exit_openprom_fs )
MODULE_LICENSE ( " GPL " ) ;