2005-04-16 15:20:36 -07:00
/* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $
* openpromfs . c : / proc / openprom handling routines
*
* 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>
# include <linux/openprom_fs.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/smp_lock.h>
# include <asm/openprom.h>
# include <asm/oplib.h>
# include <asm/uaccess.h>
# define ALIASES_NNODES 64
typedef struct {
u16 parent ;
u16 next ;
u16 child ;
u16 first_prop ;
u32 node ;
} openpromfs_node ;
typedef struct {
# define OPP_STRING 0x10
# define OPP_STRINGLIST 0x20
# define OPP_BINARY 0x40
# define OPP_HEXSTRING 0x80
# define OPP_DIRTY 0x01
# define OPP_QUOTED 0x02
# define OPP_NOTQUOTED 0x04
# define OPP_ASCIIZ 0x08
u32 flag ;
u32 alloclen ;
u32 len ;
char * value ;
char name [ 8 ] ;
} openprom_property ;
static openpromfs_node * nodes ;
static int alloced ;
static u16 last_node ;
static u16 first_prop ;
static u16 options = 0xffff ;
static u16 aliases = 0xffff ;
static int aliases_nodes ;
static char * alias_names [ ALIASES_NNODES ] ;
# define OPENPROM_ROOT_INO 16
# define OPENPROM_FIRST_INO OPENPROM_ROOT_INO
# define NODE(ino) nodes[ino - OPENPROM_FIRST_INO]
# define NODE2INO(node) (node + OPENPROM_FIRST_INO)
# define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node)
static int openpromfs_create ( struct inode * , struct dentry * , int , struct nameidata * ) ;
static int openpromfs_readdir ( struct file * , void * , filldir_t ) ;
static struct dentry * openpromfs_lookup ( struct inode * , struct dentry * dentry , struct nameidata * nd ) ;
static int openpromfs_unlink ( struct inode * , struct dentry * dentry ) ;
static ssize_t nodenum_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
struct inode * inode = file - > f_dentry - > d_inode ;
char buffer [ 10 ] ;
if ( count < 0 | | ! inode - > u . generic_ip )
return - EINVAL ;
sprintf ( buffer , " %8.8x \n " , ( u32 ) ( long ) ( inode - > u . generic_ip ) ) ;
if ( file - > f_pos > = 9 )
return 0 ;
if ( count > 9 - file - > f_pos )
count = 9 - file - > f_pos ;
if ( copy_to_user ( buf , buffer + file - > f_pos , count ) )
return - EFAULT ;
* ppos + = count ;
return count ;
}
static ssize_t property_read ( struct file * filp , char __user * buf ,
size_t count , loff_t * ppos )
{
struct inode * inode = filp - > f_dentry - > d_inode ;
int i , j , k ;
u32 node ;
char * p , * s ;
u32 * q ;
openprom_property * op ;
char buffer [ 64 ] ;
if ( ! filp - > private_data ) {
node = nodes [ ( u16 ) ( ( long ) inode - > u . generic_ip ) ] . node ;
i = ( ( u32 ) ( long ) inode - > u . generic_ip ) > > 16 ;
if ( ( u16 ) ( ( long ) inode - > u . generic_ip ) = = aliases ) {
if ( i > = aliases_nodes )
p = NULL ;
else
p = alias_names [ i ] ;
} else
for ( p = prom_firstprop ( node , buffer ) ;
i & & p & & * p ;
p = prom_nextprop ( node , p , buffer ) , i - - )
/* nothing */ ;
if ( ! p | | ! * p )
return - EIO ;
i = prom_getproplen ( node , p ) ;
if ( i < 0 ) {
if ( ( u16 ) ( ( long ) inode - > u . generic_ip ) = = aliases )
i = 0 ;
else
return - EIO ;
}
k = i ;
if ( i < 64 ) i = 64 ;
filp - > private_data = kmalloc ( sizeof ( openprom_property )
+ ( j = strlen ( p ) ) + 2 * i ,
GFP_KERNEL ) ;
if ( ! filp - > private_data )
return - ENOMEM ;
op = ( openprom_property * ) filp - > private_data ;
op - > flag = 0 ;
op - > alloclen = 2 * i ;
strcpy ( op - > name , p ) ;
op - > value = ( char * ) ( ( ( unsigned long ) ( op - > name + j + 4 ) ) & ~ 3 ) ;
op - > len = k ;
if ( k & & prom_getproperty ( node , p , op - > value , i ) < 0 )
return - EIO ;
op - > value [ k ] = 0 ;
if ( k ) {
for ( s = NULL , p = op - > value ; p < op - > value + k ; p + + ) {
if ( ( * p > = ' ' & & * p < = ' ~ ' ) | | * p = = ' \n ' ) {
op - > flag | = OPP_STRING ;
s = p ;
continue ;
}
if ( p > op - > value & & ! * p & & s = = p - 1 ) {
if ( p < op - > value + k - 1 )
op - > flag | = OPP_STRINGLIST ;
else
op - > flag | = OPP_ASCIIZ ;
continue ;
}
if ( k = = 1 & & ! * p ) {
op - > flag | = ( OPP_STRING | OPP_ASCIIZ ) ;
break ;
}
op - > flag & = ~ ( OPP_STRING | OPP_STRINGLIST ) ;
if ( k & 3 )
op - > flag | = OPP_HEXSTRING ;
else
op - > flag | = OPP_BINARY ;
break ;
}
if ( op - > flag & OPP_STRINGLIST )
op - > flag & = ~ ( OPP_STRING ) ;
if ( op - > flag & OPP_ASCIIZ )
op - > len - - ;
}
} else
op = ( openprom_property * ) filp - > private_data ;
if ( ! count | | ! ( op - > len | | ( op - > flag & OPP_ASCIIZ ) ) )
return 0 ;
if ( * ppos > = 0xffffff | | count > = 0xffffff )
return - EINVAL ;
if ( op - > flag & OPP_STRINGLIST ) {
for ( k = 0 , p = op - > value ; p < op - > value + op - > len ; p + + )
if ( ! * p )
k + + ;
i = op - > len + 4 * k + 3 ;
} else if ( op - > flag & OPP_STRING ) {
i = op - > len + 3 ;
} else if ( op - > flag & OPP_BINARY ) {
i = ( op - > len * 9 ) > > 2 ;
} else {
i = ( op - > len < < 1 ) + 1 ;
}
k = * ppos ;
if ( k > = i ) return 0 ;
if ( count > i - k ) count = i - k ;
if ( op - > flag & OPP_STRING ) {
if ( ! k ) {
if ( put_user ( ' \' ' , buf ) )
return - EFAULT ;
k + + ;
count - - ;
}
if ( k + count > = i - 2 )
j = i - 2 - k ;
else
j = count ;
if ( j > = 0 ) {
if ( copy_to_user ( buf + k - * ppos ,
op - > value + k - 1 , j ) )
return - EFAULT ;
count - = j ;
k + = j ;
}
if ( count ) {
if ( put_user ( ' \' ' , & buf [ k + + - * ppos ] ) )
return - EFAULT ;
}
if ( count > 1 ) {
if ( put_user ( ' \n ' , & buf [ k + + - * ppos ] ) )
return - EFAULT ;
}
} else if ( op - > flag & OPP_STRINGLIST ) {
char * tmp ;
tmp = kmalloc ( i , GFP_KERNEL ) ;
if ( ! tmp )
return - ENOMEM ;
s = tmp ;
* s + + = ' \' ' ;
for ( p = op - > value ; p < op - > value + op - > len ; p + + ) {
if ( ! * p ) {
strcpy ( s , " ' + ' " ) ;
s + = 5 ;
continue ;
}
* s + + = * p ;
}
strcpy ( s , " ' \n " ) ;
if ( copy_to_user ( buf , tmp + k , count ) )
return - EFAULT ;
kfree ( tmp ) ;
k + = count ;
} else if ( op - > flag & OPP_BINARY ) {
char buffer [ 10 ] ;
u32 * first , * last ;
int first_off , last_cnt ;
first = ( ( u32 * ) op - > value ) + k / 9 ;
first_off = k % 9 ;
last = ( ( u32 * ) op - > value ) + ( k + count - 1 ) / 9 ;
last_cnt = ( k + count ) % 9 ;
if ( ! last_cnt ) last_cnt = 9 ;
if ( first = = last ) {
sprintf ( buffer , " %08x. " , * first ) ;
if ( copy_to_user ( buf , buffer + first_off ,
last_cnt - first_off ) )
return - EFAULT ;
buf + = last_cnt - first_off ;
} else {
for ( q = first ; q < = last ; q + + ) {
sprintf ( buffer , " %08x. " , * q ) ;
if ( q = = first ) {
if ( copy_to_user ( buf , buffer + first_off ,
9 - first_off ) )
return - EFAULT ;
buf + = 9 - first_off ;
} else if ( q = = last ) {
if ( copy_to_user ( buf , buffer , last_cnt ) )
return - EFAULT ;
buf + = last_cnt ;
} else {
if ( copy_to_user ( buf , buffer , 9 ) )
return - EFAULT ;
buf + = 9 ;
}
}
}
if ( last = = ( u32 * ) ( op - > value + op - > len - 4 ) & & last_cnt = = 9 ) {
if ( put_user ( ' \n ' , ( buf - 1 ) ) )
return - EFAULT ;
}
k + = count ;
} else if ( op - > flag & OPP_HEXSTRING ) {
char buffer [ 3 ] ;
if ( ( k < i - 1 ) & & ( k & 1 ) ) {
sprintf ( buffer , " %02x " ,
( unsigned char ) * ( op - > value + ( k > > 1 ) ) & 0xff ) ;
if ( put_user ( buffer [ 1 ] , & buf [ k + + - * ppos ] ) )
return - EFAULT ;
count - - ;
}
for ( ; ( count > 1 ) & & ( k < i - 1 ) ; k + = 2 ) {
sprintf ( buffer , " %02x " ,
( unsigned char ) * ( op - > value + ( k > > 1 ) ) & 0xff ) ;
if ( copy_to_user ( buf + k - * ppos , buffer , 2 ) )
return - EFAULT ;
count - = 2 ;
}
if ( count & & ( k < i - 1 ) ) {
sprintf ( buffer , " %02x " ,
( unsigned char ) * ( op - > value + ( k > > 1 ) ) & 0xff ) ;
if ( put_user ( buffer [ 0 ] , & buf [ k + + - * ppos ] ) )
return - EFAULT ;
count - - ;
}
if ( count ) {
if ( put_user ( ' \n ' , & buf [ k + + - * ppos ] ) )
return - EFAULT ;
}
}
count = k - * ppos ;
* ppos = k ;
return count ;
}
static ssize_t property_write ( struct file * filp , const char __user * buf ,
size_t count , loff_t * ppos )
{
int i , j , k ;
char * p ;
u32 * q ;
void * b ;
openprom_property * op ;
if ( * ppos > = 0xffffff | | count > = 0xffffff )
return - EINVAL ;
if ( ! filp - > private_data ) {
i = property_read ( filp , NULL , 0 , NULL ) ;
if ( i )
return i ;
}
k = * ppos ;
op = ( openprom_property * ) filp - > private_data ;
if ( ! ( op - > flag & OPP_STRING ) ) {
u32 * first , * last ;
int first_off , last_cnt ;
u32 mask , mask2 ;
char tmp [ 9 ] ;
int forcelen = 0 ;
j = k % 9 ;
for ( i = 0 ; i < count ; i + + , j + + ) {
if ( j = = 9 ) j = 0 ;
if ( ! j ) {
char ctmp ;
if ( get_user ( ctmp , & buf [ i ] ) )
return - EFAULT ;
if ( ctmp ! = ' . ' ) {
if ( ctmp ! = ' \n ' ) {
if ( op - > flag & OPP_BINARY )
return - EINVAL ;
else
goto write_try_string ;
} else {
count = i + 1 ;
forcelen = 1 ;
break ;
}
}
} else {
char ctmp ;
if ( get_user ( ctmp , & buf [ i ] ) )
return - EFAULT ;
if ( ctmp < ' 0 ' | |
( ctmp > ' 9 ' & & ctmp < ' A ' ) | |
( ctmp > ' F ' & & ctmp < ' a ' ) | |
ctmp > ' f ' ) {
if ( op - > flag & OPP_BINARY )
return - EINVAL ;
else
goto write_try_string ;
}
}
}
op - > flag | = OPP_BINARY ;
tmp [ 8 ] = 0 ;
i = ( ( count + k + 8 ) / 9 ) < < 2 ;
if ( op - > alloclen < = i ) {
b = kmalloc ( sizeof ( openprom_property ) + 2 * i ,
GFP_KERNEL ) ;
if ( ! b )
return - ENOMEM ;
memcpy ( b , filp - > private_data ,
sizeof ( openprom_property )
+ strlen ( op - > name ) + op - > alloclen ) ;
memset ( ( ( char * ) b ) + sizeof ( openprom_property )
+ strlen ( op - > name ) + op - > alloclen ,
0 , 2 * i - op - > alloclen ) ;
op = ( openprom_property * ) b ;
op - > alloclen = 2 * i ;
b = filp - > private_data ;
filp - > private_data = ( void * ) op ;
kfree ( b ) ;
}
first = ( ( u32 * ) op - > value ) + ( k / 9 ) ;
first_off = k % 9 ;
last = ( u32 * ) ( op - > value + i ) ;
last_cnt = ( k + count ) % 9 ;
if ( first + 1 = = last ) {
memset ( tmp , ' 0 ' , 8 ) ;
if ( copy_from_user ( tmp + first_off , buf ,
( count + first_off > 8 ) ?
8 - first_off : count ) )
return - EFAULT ;
mask = 0xffffffff ;
mask2 = 0xffffffff ;
for ( j = 0 ; j < first_off ; j + + )
mask > > = 1 ;
for ( j = 8 - count - first_off ; j > 0 ; j - - )
mask2 < < = 1 ;
mask & = mask2 ;
if ( mask ) {
* first & = ~ mask ;
* first | = simple_strtoul ( tmp , NULL , 16 ) ;
op - > flag | = OPP_DIRTY ;
}
} else {
op - > flag | = OPP_DIRTY ;
for ( q = first ; q < last ; q + + ) {
if ( q = = first ) {
if ( first_off < 8 ) {
memset ( tmp , ' 0 ' , 8 ) ;
if ( copy_from_user ( tmp + first_off ,
buf ,
8 - first_off ) )
return - EFAULT ;
mask = 0xffffffff ;
for ( j = 0 ; j < first_off ; j + + )
mask > > = 1 ;
* q & = ~ mask ;
* q | = simple_strtoul ( tmp , NULL , 16 ) ;
}
buf + = 9 ;
} else if ( ( q = = last - 1 ) & & last_cnt
& & ( last_cnt < 8 ) ) {
memset ( tmp , ' 0 ' , 8 ) ;
if ( copy_from_user ( tmp , buf , last_cnt ) )
return - EFAULT ;
mask = 0xffffffff ;
for ( j = 0 ; j < 8 - last_cnt ; j + + )
mask < < = 1 ;
* q & = ~ mask ;
* q | = simple_strtoul ( tmp , NULL , 16 ) ;
buf + = last_cnt ;
} else {
char tchars [ 17 ] ; /* XXX yuck... */
if ( copy_from_user ( tchars , buf , 16 ) )
return - EFAULT ;
* q = simple_strtoul ( tchars , NULL , 16 ) ;
buf + = 9 ;
}
}
}
if ( ! forcelen ) {
if ( op - > len < i )
op - > len = i ;
} else
op - > len = i ;
* ppos + = count ;
}
write_try_string :
if ( ! ( op - > flag & OPP_BINARY ) ) {
if ( ! ( op - > flag & ( OPP_QUOTED | OPP_NOTQUOTED ) ) ) {
char ctmp ;
/* No way, if somebody starts writing from the middle,
* we don ' t know whether he uses quotes around or not
*/
if ( k > 0 )
return - EINVAL ;
if ( get_user ( ctmp , buf ) )
return - EFAULT ;
if ( ctmp = = ' \' ' ) {
op - > flag | = OPP_QUOTED ;
buf + + ;
count - - ;
( * ppos ) + + ;
if ( ! count ) {
op - > flag | = OPP_STRING ;
return 1 ;
}
} else
op - > flag | = OPP_NOTQUOTED ;
}
op - > flag | = OPP_STRING ;
if ( op - > alloclen < = count + * ppos ) {
b = kmalloc ( sizeof ( openprom_property )
+ 2 * ( count + * ppos ) , GFP_KERNEL ) ;
if ( ! b )
return - ENOMEM ;
memcpy ( b , filp - > private_data ,
sizeof ( openprom_property )
+ strlen ( op - > name ) + op - > alloclen ) ;
memset ( ( ( char * ) b ) + sizeof ( openprom_property )
+ strlen ( op - > name ) + op - > alloclen ,
0 , 2 * ( count - * ppos ) - op - > alloclen ) ;
op = ( openprom_property * ) b ;
op - > alloclen = 2 * ( count + * ppos ) ;
b = filp - > private_data ;
filp - > private_data = ( void * ) op ;
kfree ( b ) ;
}
p = op - > value + * ppos - ( ( op - > flag & OPP_QUOTED ) ? 1 : 0 ) ;
if ( copy_from_user ( p , buf , count ) )
return - EFAULT ;
op - > flag | = OPP_DIRTY ;
for ( i = 0 ; i < count ; i + + , p + + )
if ( * p = = ' \n ' ) {
* p = 0 ;
break ;
}
if ( i < count ) {
op - > len = p - op - > value ;
* ppos + = i + 1 ;
if ( ( p > op - > value ) & & ( op - > flag & OPP_QUOTED )
& & ( * ( p - 1 ) = = ' \' ' ) )
op - > len - - ;
} else {
if ( p - op - > value > op - > len )
op - > len = p - op - > value ;
* ppos + = count ;
}
}
return * ppos - k ;
}
int property_release ( struct inode * inode , struct file * filp )
{
openprom_property * op = ( openprom_property * ) filp - > private_data ;
int error ;
u32 node ;
if ( ! op )
return 0 ;
lock_kernel ( ) ;
node = nodes [ ( u16 ) ( ( long ) inode - > u . generic_ip ) ] . node ;
if ( ( u16 ) ( ( long ) inode - > u . generic_ip ) = = aliases ) {
if ( ( op - > flag & OPP_DIRTY ) & & ( op - > flag & OPP_STRING ) ) {
char * p = op - > name ;
int i = ( op - > value - op - > name ) - strlen ( op - > name ) - 1 ;
op - > value [ op - > len ] = 0 ;
* ( op - > value - 1 ) = ' ' ;
if ( i ) {
for ( p = op - > value - i - 2 ; p > = op - > name ; p - - )
p [ i ] = * p ;
p = op - > name + i ;
}
memcpy ( p - 8 , " nvalias " , 8 ) ;
prom_feval ( p - 8 ) ;
}
} else if ( op - > flag & OPP_DIRTY ) {
if ( op - > flag & OPP_STRING ) {
op - > value [ op - > len ] = 0 ;
error = prom_setprop ( node , op - > name ,
op - > value , op - > len + 1 ) ;
if ( error < = 0 )
printk ( KERN_WARNING " openpromfs: "
" Couldn't write property %s \n " ,
op - > name ) ;
} else if ( ( op - > flag & OPP_BINARY ) | | ! op - > len ) {
error = prom_setprop ( node , op - > name ,
op - > value , op - > len ) ;
if ( error < = 0 )
printk ( KERN_WARNING " openpromfs: "
" Couldn't write property %s \n " ,
op - > name ) ;
} else {
printk ( KERN_WARNING " openpromfs: "
" Unknown property type of %s \n " ,
op - > name ) ;
}
}
unlock_kernel ( ) ;
kfree ( filp - > private_data ) ;
return 0 ;
}
static struct file_operations openpromfs_prop_ops = {
. read = property_read ,
. write = property_write ,
. release = property_release ,
} ;
static struct file_operations openpromfs_nodenum_ops = {
. read = nodenum_read ,
} ;
static struct file_operations openprom_operations = {
. read = generic_read_dir ,
. readdir = openpromfs_readdir ,
} ;
static struct inode_operations openprom_alias_inode_operations = {
. create = openpromfs_create ,
. lookup = openpromfs_lookup ,
. unlink = openpromfs_unlink ,
} ;
static struct inode_operations openprom_inode_operations = {
. lookup = openpromfs_lookup ,
} ;
static int lookup_children ( u16 n , const char * name , int len )
{
int ret ;
u16 node ;
for ( ; n ! = 0xffff ; n = nodes [ n ] . next ) {
node = nodes [ n ] . child ;
if ( node ! = 0xffff ) {
char buffer [ 128 ] ;
int i ;
char * p ;
while ( node ! = 0xffff ) {
if ( prom_getname ( nodes [ node ] . node ,
buffer , 128 ) > = 0 ) {
i = strlen ( buffer ) ;
if ( ( len = = i )
& & ! strncmp ( buffer , name , len ) )
return NODE2INO ( node ) ;
p = strchr ( buffer , ' @ ' ) ;
if ( p & & ( len = = p - buffer )
& & ! strncmp ( buffer , name , len ) )
return NODE2INO ( node ) ;
}
node = nodes [ node ] . next ;
}
} else
continue ;
ret = lookup_children ( nodes [ n ] . child , name , len ) ;
if ( ret ) return ret ;
}
return 0 ;
}
static struct dentry * openpromfs_lookup ( struct inode * dir , struct dentry * dentry , struct nameidata * nd )
{
int ino = 0 ;
# define OPFSL_DIR 0
# define OPFSL_PROPERTY 1
# define OPFSL_NODENUM 2
int type = 0 ;
char buffer [ 128 ] ;
char * p ;
const char * name ;
u32 n ;
u16 dirnode ;
unsigned int len ;
int i ;
struct inode * inode ;
char buffer2 [ 64 ] ;
inode = NULL ;
name = dentry - > d_name . name ;
len = dentry - > d_name . len ;
lock_kernel ( ) ;
if ( name [ 0 ] = = ' . ' & & len = = 5 & & ! strncmp ( name + 1 , " node " , 4 ) ) {
ino = NODEP2INO ( NODE ( dir - > i_ino ) . first_prop ) ;
type = OPFSL_NODENUM ;
}
if ( ! ino ) {
u16 node = NODE ( dir - > i_ino ) . child ;
while ( node ! = 0xffff ) {
if ( prom_getname ( nodes [ node ] . node , buffer , 128 ) > = 0 ) {
i = strlen ( buffer ) ;
if ( len = = i & & ! strncmp ( buffer , name , len ) ) {
ino = NODE2INO ( node ) ;
type = OPFSL_DIR ;
break ;
}
p = strchr ( buffer , ' @ ' ) ;
if ( p & & ( len = = p - buffer )
& & ! strncmp ( buffer , name , len ) ) {
ino = NODE2INO ( node ) ;
type = OPFSL_DIR ;
break ;
}
}
node = nodes [ node ] . next ;
}
}
n = NODE ( dir - > i_ino ) . node ;
dirnode = dir - > i_ino - OPENPROM_FIRST_INO ;
if ( ! ino ) {
int j = NODEP2INO ( NODE ( dir - > i_ino ) . first_prop ) ;
if ( dirnode ! = aliases ) {
for ( p = prom_firstprop ( n , buffer2 ) ;
p & & * p ;
p = prom_nextprop ( n , p , buffer2 ) ) {
j + + ;
if ( ( len = = strlen ( p ) )
& & ! strncmp ( p , name , len ) ) {
ino = j ;
type = OPFSL_PROPERTY ;
break ;
}
}
} else {
int k ;
for ( k = 0 ; k < aliases_nodes ; k + + ) {
j + + ;
if ( alias_names [ k ]
& & ( len = = strlen ( alias_names [ k ] ) )
& & ! strncmp ( alias_names [ k ] , name , len ) ) {
ino = j ;
type = OPFSL_PROPERTY ;
break ;
}
}
}
}
if ( ! ino ) {
ino = lookup_children ( NODE ( dir - > i_ino ) . child , name , len ) ;
if ( ino )
type = OPFSL_DIR ;
else {
unlock_kernel ( ) ;
return ERR_PTR ( - ENOENT ) ;
}
}
inode = iget ( dir - > i_sb , ino ) ;
unlock_kernel ( ) ;
if ( ! inode )
return ERR_PTR ( - EINVAL ) ;
switch ( type ) {
case OPFSL_DIR :
inode - > i_mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( ino = = OPENPROM_FIRST_INO + aliases ) {
inode - > i_mode | = S_IWUSR ;
inode - > i_op = & openprom_alias_inode_operations ;
} else
inode - > i_op = & openprom_inode_operations ;
inode - > i_fop = & openprom_operations ;
inode - > i_nlink = 2 ;
break ;
case OPFSL_NODENUM :
inode - > i_mode = S_IFREG | S_IRUGO ;
inode - > i_fop = & openpromfs_nodenum_ops ;
inode - > i_nlink = 1 ;
inode - > u . generic_ip = ( void * ) ( long ) ( n ) ;
break ;
case OPFSL_PROPERTY :
if ( ( dirnode = = options ) & & ( len = = 17 )
& & ! strncmp ( name , " security-password " , 17 ) )
inode - > i_mode = S_IFREG | S_IRUSR | S_IWUSR ;
else {
inode - > i_mode = S_IFREG | S_IRUGO ;
if ( dirnode = = options | | dirnode = = aliases ) {
if ( len ! = 4 | | strncmp ( name , " name " , 4 ) )
inode - > i_mode | = S_IWUSR ;
}
}
inode - > i_fop = & openpromfs_prop_ops ;
inode - > i_nlink = 1 ;
if ( inode - > i_size < 0 )
inode - > i_size = 0 ;
inode - > u . generic_ip = ( void * ) ( long ) ( ( ( u16 ) dirnode ) |
( ( ( u16 ) ( ino - NODEP2INO ( NODE ( dir - > i_ino ) . first_prop ) - 1 ) ) < < 16 ) ) ;
break ;
}
inode - > i_gid = 0 ;
inode - > i_uid = 0 ;
d_add ( dentry , inode ) ;
return NULL ;
}
static int openpromfs_readdir ( struct file * filp , void * dirent , filldir_t filldir )
{
struct inode * inode = filp - > f_dentry - > d_inode ;
unsigned int ino ;
u32 n ;
int i , j ;
char buffer [ 128 ] ;
u16 node ;
char * p ;
char buffer2 [ 64 ] ;
lock_kernel ( ) ;
ino = inode - > i_ino ;
i = filp - > f_pos ;
switch ( i ) {
case 0 :
if ( filldir ( dirent , " . " , 1 , i , ino , DT_DIR ) < 0 ) goto out ;
i + + ;
filp - > f_pos + + ;
/* fall thru */
case 1 :
if ( filldir ( dirent , " .. " , 2 , i ,
( NODE ( ino ) . parent = = 0xffff ) ?
OPENPROM_ROOT_INO : NODE2INO ( NODE ( ino ) . parent ) , DT_DIR ) < 0 )
goto out ;
i + + ;
filp - > f_pos + + ;
/* fall thru */
default :
i - = 2 ;
node = NODE ( ino ) . child ;
while ( i & & node ! = 0xffff ) {
node = nodes [ node ] . next ;
i - - ;
}
while ( node ! = 0xffff ) {
if ( prom_getname ( nodes [ node ] . node , buffer , 128 ) < 0 )
goto out ;
if ( filldir ( dirent , buffer , strlen ( buffer ) ,
filp - > f_pos , NODE2INO ( node ) , DT_DIR ) < 0 )
goto out ;
filp - > f_pos + + ;
node = nodes [ node ] . next ;
}
j = NODEP2INO ( NODE ( ino ) . first_prop ) ;
if ( ! i ) {
if ( filldir ( dirent , " .node " , 5 , filp - > f_pos , j , DT_REG ) < 0 )
goto out ;
filp - > f_pos + + ;
} else
i - - ;
n = NODE ( ino ) . node ;
if ( ino = = OPENPROM_FIRST_INO + aliases ) {
for ( j + + ; i < aliases_nodes ; i + + , j + + ) {
if ( alias_names [ i ] ) {
if ( filldir ( dirent , alias_names [ i ] ,
strlen ( alias_names [ i ] ) ,
filp - > f_pos , j , DT_REG ) < 0 ) goto out ;
filp - > f_pos + + ;
}
}
} else {
for ( p = prom_firstprop ( n , buffer2 ) ;
p & & * p ;
p = prom_nextprop ( n , p , buffer2 ) ) {
j + + ;
if ( i ) i - - ;
else {
if ( filldir ( dirent , p , strlen ( p ) ,
filp - > f_pos , j , DT_REG ) < 0 )
goto out ;
filp - > f_pos + + ;
}
}
}
}
out :
unlock_kernel ( ) ;
return 0 ;
}
static int openpromfs_create ( struct inode * dir , struct dentry * dentry , int mode ,
struct nameidata * nd )
{
char * p ;
struct inode * inode ;
if ( ! dir )
return - ENOENT ;
if ( dentry - > d_name . len > 256 )
return - EINVAL ;
p = kmalloc ( dentry - > d_name . len + 1 , GFP_KERNEL ) ;
if ( ! p )
return - ENOMEM ;
strncpy ( p , dentry - > d_name . name , dentry - > d_name . len ) ;
p [ dentry - > d_name . len ] = 0 ;
lock_kernel ( ) ;
if ( aliases_nodes = = ALIASES_NNODES ) {
kfree ( p ) ;
unlock_kernel ( ) ;
return - EIO ;
}
alias_names [ aliases_nodes + + ] = p ;
inode = iget ( dir - > i_sb ,
NODEP2INO ( NODE ( dir - > i_ino ) . first_prop ) + aliases_nodes ) ;
if ( ! inode ) {
unlock_kernel ( ) ;
return - EINVAL ;
}
inode - > i_mode = S_IFREG | S_IRUGO | S_IWUSR ;
inode - > i_fop = & openpromfs_prop_ops ;
inode - > i_nlink = 1 ;
if ( inode - > i_size < 0 ) inode - > i_size = 0 ;
inode - > u . generic_ip = ( void * ) ( long ) ( ( ( u16 ) aliases ) |
( ( ( u16 ) ( aliases_nodes - 1 ) ) < < 16 ) ) ;
unlock_kernel ( ) ;
d_instantiate ( dentry , inode ) ;
return 0 ;
}
static int openpromfs_unlink ( struct inode * dir , struct dentry * dentry )
{
unsigned int len ;
char * p ;
const char * name ;
int i ;
name = dentry - > d_name . name ;
len = dentry - > d_name . len ;
lock_kernel ( ) ;
for ( i = 0 ; i < aliases_nodes ; i + + )
if ( ( strlen ( alias_names [ i ] ) = = len )
& & ! strncmp ( name , alias_names [ i ] , len ) ) {
char buffer [ 512 ] ;
p = alias_names [ i ] ;
alias_names [ i ] = NULL ;
kfree ( p ) ;
strcpy ( buffer , " nvunalias " ) ;
memcpy ( buffer + 10 , name , len ) ;
buffer [ 10 + len ] = 0 ;
prom_feval ( buffer ) ;
}
unlock_kernel ( ) ;
return 0 ;
}
/* {{{ init section */
static int __init check_space ( u16 n )
{
unsigned long pages ;
if ( ( 1 < < alloced ) * PAGE_SIZE < ( n + 2 ) * sizeof ( openpromfs_node ) ) {
pages = __get_free_pages ( GFP_KERNEL , alloced + 1 ) ;
if ( ! pages )
return - 1 ;
if ( nodes ) {
memcpy ( ( char * ) pages , ( char * ) nodes ,
( 1 < < alloced ) * PAGE_SIZE ) ;
free_pages ( ( unsigned long ) nodes , alloced ) ;
}
alloced + + ;
nodes = ( openpromfs_node * ) pages ;
}
return 0 ;
}
static u16 __init get_nodes ( u16 parent , u32 node )
{
char * p ;
u16 n = last_node + + , i ;
char buffer [ 64 ] ;
if ( check_space ( n ) < 0 )
return 0xffff ;
nodes [ n ] . parent = parent ;
nodes [ n ] . node = node ;
nodes [ n ] . next = 0xffff ;
nodes [ n ] . child = 0xffff ;
nodes [ n ] . first_prop = first_prop + + ;
if ( ! parent ) {
char buffer [ 8 ] ;
int j ;
if ( ( j = prom_getproperty ( node , " name " , buffer , 8 ) ) > = 0 ) {
buffer [ j ] = 0 ;
if ( ! strcmp ( buffer , " options " ) )
options = n ;
else if ( ! strcmp ( buffer , " aliases " ) )
aliases = n ;
}
}
if ( n ! = aliases )
for ( p = prom_firstprop ( node , buffer ) ;
p & & p ! = ( char * ) - 1 & & * p ;
p = prom_nextprop ( node , p , buffer ) )
first_prop + + ;
else {
char * q ;
for ( p = prom_firstprop ( node , buffer ) ;
p & & p ! = ( char * ) - 1 & & * p ;
p = prom_nextprop ( node , p , buffer ) ) {
if ( aliases_nodes = = ALIASES_NNODES )
break ;
for ( i = 0 ; i < aliases_nodes ; i + + )
if ( ! strcmp ( p , alias_names [ i ] ) )
break ;
if ( i < aliases_nodes )
continue ;
q = kmalloc ( strlen ( p ) + 1 , GFP_KERNEL ) ;
if ( ! q )
return 0xffff ;
strcpy ( q , p ) ;
alias_names [ aliases_nodes + + ] = q ;
}
first_prop + = ALIASES_NNODES ;
}
node = prom_getchild ( node ) ;
if ( node ) {
parent = get_nodes ( n , node ) ;
if ( parent = = 0xffff )
return 0xffff ;
nodes [ n ] . child = parent ;
while ( ( node = prom_getsibling ( node ) ) ! = 0 ) {
i = get_nodes ( n , node ) ;
if ( i = = 0xffff )
return 0xffff ;
nodes [ parent ] . next = i ;
parent = i ;
}
}
return n ;
}
static void openprom_read_inode ( struct inode * inode )
{
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
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 ;
}
}
static int openprom_remount ( struct super_block * sb , int * flags , char * data )
{
* flags | = MS_NOATIME ;
return 0 ;
}
static struct super_operations openprom_sops = {
. read_inode = openprom_read_inode ,
. statfs = simple_statfs ,
. remount_fs = openprom_remount ,
} ;
static int openprom_fill_super ( struct super_block * s , void * data , int silent )
{
struct inode * root_inode ;
s - > s_flags | = MS_NOATIME ;
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 ;
root_inode = iget ( s , OPENPROM_ROOT_INO ) ;
if ( ! root_inode )
goto out_no_root ;
s - > s_root = d_alloc_root ( root_inode ) ;
if ( ! s - > s_root )
goto out_no_root ;
return 0 ;
out_no_root :
printk ( " openprom_fill_super: get root inode failed \n " ) ;
iput ( root_inode ) ;
return - ENOMEM ;
}
static struct super_block * openprom_get_sb ( struct file_system_type * fs_type ,
int flags , const char * dev_name , void * data )
{
return get_sb_single ( fs_type , flags , data , openprom_fill_super ) ;
}
static struct file_system_type openprom_fs_type = {
. owner = THIS_MODULE ,
. name = " openpromfs " ,
. get_sb = openprom_get_sb ,
. kill_sb = kill_anon_super ,
} ;
static int __init init_openprom_fs ( void )
{
nodes = ( openpromfs_node * ) __get_free_pages ( GFP_KERNEL , 0 ) ;
if ( ! nodes ) {
printk ( KERN_WARNING " openpromfs: can't get free page \n " ) ;
return - EIO ;
}
if ( get_nodes ( 0xffff , prom_root_node ) = = 0xffff ) {
printk ( KERN_WARNING " openpromfs: couldn't setup tree \n " ) ;
return - EIO ;
}
nodes [ last_node ] . first_prop = first_prop ;
return register_filesystem ( & openprom_fs_type ) ;
}
static void __exit exit_openprom_fs ( void )
{
int i ;
unregister_filesystem ( & openprom_fs_type ) ;
free_pages ( ( unsigned long ) nodes , alloced ) ;
for ( i = 0 ; i < aliases_nodes ; i + + )
2005-11-07 01:01:34 -08:00
kfree ( alias_names [ i ] ) ;
2005-04-16 15:20:36 -07:00
nodes = NULL ;
}
module_init ( init_openprom_fs )
module_exit ( exit_openprom_fs )
MODULE_LICENSE ( " GPL " ) ;