2007-10-18 14:05:54 +04:00
# include <linux/stat.h>
# include <linux/sysctl.h>
# include "../fs/xfs/linux-2.6/xfs_sysctl.h"
# include <linux/sunrpc/debug.h>
# include <linux/string.h>
# include <net/ip_vs.h>
static int sysctl_depth ( struct ctl_table * table )
{
struct ctl_table * tmp ;
int depth ;
depth = 0 ;
for ( tmp = table ; tmp - > parent ; tmp = tmp - > parent )
depth + + ;
return depth ;
}
static struct ctl_table * sysctl_parent ( struct ctl_table * table , int n )
{
int i ;
for ( i = 0 ; table & & i < n ; i + + )
table = table - > parent ;
return table ;
}
static void sysctl_print_path ( struct ctl_table * table )
{
struct ctl_table * tmp ;
int depth , i ;
depth = sysctl_depth ( table ) ;
if ( table - > procname ) {
for ( i = depth ; i > = 0 ; i - - ) {
tmp = sysctl_parent ( table , i ) ;
printk ( " /%s " , tmp - > procname ? tmp - > procname : " " ) ;
}
}
printk ( " " ) ;
}
2007-11-30 15:54:00 +03:00
static struct ctl_table * sysctl_check_lookup ( struct nsproxy * namespaces ,
struct ctl_table * table )
2007-10-18 14:05:54 +04:00
{
struct ctl_table_header * head ;
struct ctl_table * ref , * test ;
int depth , cur_depth ;
depth = sysctl_depth ( table ) ;
2007-11-30 15:54:00 +03:00
for ( head = __sysctl_head_next ( namespaces , NULL ) ; head ;
head = __sysctl_head_next ( namespaces , head ) ) {
2007-10-18 14:05:54 +04:00
cur_depth = depth ;
ref = head - > ctl_table ;
repeat :
test = sysctl_parent ( table , cur_depth ) ;
2009-04-03 13:22:26 +04:00
for ( ; ref - > procname ; ref + + ) {
2007-10-18 14:05:54 +04:00
int match = 0 ;
if ( cur_depth & & ! ref - > child )
continue ;
if ( test - > procname & & ref - > procname & &
( strcmp ( test - > procname , ref - > procname ) = = 0 ) )
match + + ;
if ( match ) {
if ( cur_depth ! = 0 ) {
cur_depth - - ;
ref = ref - > child ;
goto repeat ;
}
goto out ;
}
}
}
ref = NULL ;
out :
sysctl_head_finish ( head ) ;
return ref ;
}
static void set_fail ( const char * * fail , struct ctl_table * table , const char * str )
{
if ( * fail ) {
printk ( KERN_ERR " sysctl table check failed: " ) ;
sysctl_print_path ( table ) ;
printk ( " %s \n " , * fail ) ;
2007-11-06 01:50:52 +03:00
dump_stack ( ) ;
2007-10-18 14:05:54 +04:00
}
* fail = str ;
}
2007-11-30 15:54:00 +03:00
static void sysctl_check_leaf ( struct nsproxy * namespaces ,
struct ctl_table * table , const char * * fail )
2007-10-18 14:05:54 +04:00
{
struct ctl_table * ref ;
2007-11-30 15:54:00 +03:00
ref = sysctl_check_lookup ( namespaces , table ) ;
2007-10-18 14:05:54 +04:00
if ( ref & & ( ref ! = table ) )
set_fail ( fail , table , " Sysctl already exists " ) ;
}
2007-11-30 15:54:00 +03:00
int sysctl_check_table ( struct nsproxy * namespaces , struct ctl_table * table )
2007-10-18 14:05:54 +04:00
{
int error = 0 ;
2009-04-03 13:22:26 +04:00
for ( ; table - > procname ; table + + ) {
2007-10-18 14:05:54 +04:00
const char * fail = NULL ;
if ( table - > parent ) {
if ( table - > procname & & ! table - > parent - > procname )
set_fail ( & fail , table , " Parent without procname " ) ;
}
if ( ! table - > procname )
set_fail ( & fail , table , " No procname " ) ;
if ( table - > child ) {
if ( table - > data )
set_fail ( & fail , table , " Directory with data? " ) ;
if ( table - > maxlen )
set_fail ( & fail , table , " Directory with maxlen? " ) ;
if ( ( table - > mode & ( S_IRUGO | S_IXUGO ) ) ! = table - > mode )
set_fail ( & fail , table , " Writable sysctl directory " ) ;
if ( table - > proc_handler )
set_fail ( & fail , table , " Directory with proc_handler " ) ;
if ( table - > extra1 )
set_fail ( & fail , table , " Directory with extra1 " ) ;
if ( table - > extra2 )
set_fail ( & fail , table , " Directory with extra2 " ) ;
} else {
2009-04-03 13:22:26 +04:00
if ( ( table - > proc_handler = = proc_dostring ) | |
2007-10-18 14:05:54 +04:00
( table - > proc_handler = = proc_dointvec ) | |
( table - > proc_handler = = proc_dointvec_minmax ) | |
( table - > proc_handler = = proc_dointvec_jiffies ) | |
( table - > proc_handler = = proc_dointvec_userhz_jiffies ) | |
( table - > proc_handler = = proc_dointvec_ms_jiffies ) | |
( table - > proc_handler = = proc_doulongvec_minmax ) | |
( table - > proc_handler = = proc_doulongvec_ms_jiffies_minmax ) ) {
if ( ! table - > data )
set_fail ( & fail , table , " No data " ) ;
if ( ! table - > maxlen )
set_fail ( & fail , table , " No maxlen " ) ;
}
2007-10-18 14:05:57 +04:00
if ( ( table - > proc_handler = = proc_doulongvec_minmax ) | |
2007-10-18 14:05:54 +04:00
( table - > proc_handler = = proc_doulongvec_ms_jiffies_minmax ) ) {
2007-10-18 14:05:57 +04:00
if ( table - > maxlen > sizeof ( unsigned long ) ) {
if ( ! table - > extra1 )
set_fail ( & fail , table , " No min " ) ;
if ( ! table - > extra2 )
set_fail ( & fail , table , " No max " ) ;
}
2007-10-18 14:05:54 +04:00
}
2009-10-27 02:50:07 +03:00
# ifdef CONFIG_PROC_SYSCTL
2007-10-18 14:05:54 +04:00
if ( table - > procname & & ! table - > proc_handler )
set_fail ( & fail , table , " No proc_handler " ) ;
2007-10-18 14:05:57 +04:00
# endif
2007-10-18 14:05:54 +04:00
#if 0
if ( ! table - > procname & & table - > proc_handler )
set_fail ( & fail , table , " proc_handler without procname " ) ;
# endif
2007-11-30 15:54:00 +03:00
sysctl_check_leaf ( namespaces , table , & fail ) ;
2007-10-18 14:05:54 +04:00
}
2008-07-25 12:48:31 +04:00
if ( table - > mode > 0777 )
set_fail ( & fail , table , " bogus .mode " ) ;
2007-10-18 14:05:54 +04:00
if ( fail ) {
set_fail ( & fail , table , NULL ) ;
error = - EINVAL ;
}
if ( table - > child )
2007-11-30 15:54:00 +03:00
error | = sysctl_check_table ( namespaces , table - > child ) ;
2007-10-18 14:05:54 +04:00
}
return error ;
}