Skip to content
Snippets Groups Projects
sysctl_binary.c 4.98 KiB
Newer Older
  • Learn to ignore specific revisions
  • #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>
    #include <linux/syscalls.h>
    #include <linux/namei.h>
    #include <linux/mount.h>
    #include <linux/fs.h>
    #include <linux/nsproxy.h>
    #include <linux/pid_namespace.h>
    #include <linux/file.h>
    #include <linux/ctype.h>
    #include <linux/smp_lock.h>
    
    #ifdef CONFIG_SYSCTL_SYSCALL
    
    /* Perform the actual read/write of a sysctl table entry. */
    static int do_sysctl_strategy(struct ctl_table_root *root,
    			struct ctl_table *table,
    			void __user *oldval, size_t __user *oldlenp,
    			void __user *newval, size_t newlen)
    {
    	int op = 0, rc;
    
    	if (oldval)
    		op |= MAY_READ;
    	if (newval)
    		op |= MAY_WRITE;
    	if (sysctl_perm(root, table, op))
    		return -EPERM;
    
    	if (table->strategy) {
    		rc = table->strategy(table, oldval, oldlenp, newval, newlen);
    		if (rc < 0)
    			return rc;
    		if (rc > 0)
    			return 0;
    	}
    
    	/* If there is no strategy routine, or if the strategy returns
    	 * zero, proceed with automatic r/w */
    	if (table->data && table->maxlen) {
    		rc = sysctl_data(table, oldval, oldlenp, newval, newlen);
    		if (rc < 0)
    			return rc;
    	}
    	return 0;
    }
    
    
    static int parse_table(const int *name, int nlen,
    
    		       void __user *oldval, size_t __user *oldlenp,
    		       void __user *newval, size_t newlen,
    		       struct ctl_table_root *root,
    		       struct ctl_table *table)
    {
    	int n;
    repeat:
    	if (!nlen)
    		return -ENOTDIR;
    
    	for ( ; table->ctl_name || table->procname; table++) {
    		if (!table->ctl_name)
    			continue;
    		if (n == table->ctl_name) {
    			int error;
    			if (table->child) {
    				if (sysctl_perm(root, table, MAY_EXEC))
    					return -EPERM;
    				name++;
    				nlen--;
    				table = table->child;
    				goto repeat;
    			}
    			error = do_sysctl_strategy(root, table,
    						   oldval, oldlenp,
    						   newval, newlen);
    			return error;
    		}
    	}
    	return -ENOTDIR;
    }
    
    
    static ssize_t binary_sysctl(const int *name, int nlen,
    	void __user *oldval, size_t __user *oldlenp,
    	void __user *newval, size_t newlen)
    
    
    {
    	struct ctl_table_header *head;
    
    
    	for (head = sysctl_head_next(NULL); head;
    			head = sysctl_head_next(head)) {
    		error = parse_table(name, nlen, oldval, oldlenp, 
    					newval, newlen,
    					head->root, head->ctl_table);
    		if (error != -ENOTDIR) {
    			sysctl_head_finish(head);
    			break;
    		}
    	}
    	return error;
    }
    
    #else /* CONFIG_SYSCTL_SYSCALL */
    
    
    static ssize_t binary_sysctl(const int *ctl_name, int nlen,
    	void __user *oldval, size_t __user *oldlenp,
    	void __user *newval, size_t newlen)
    
    static void deprecated_sysctl_warning(const int *name, int nlen)
    
    {
    	static int msg_count;
    	int i;
    
    	/* Ignore accesses to kernel.version */
    
    	if ((nlen == 2) && (name[0] == CTL_KERN) && (name[1] == KERN_VERSION))
    		return;
    
    
    	if (msg_count < 5) {
    		msg_count++;
    		printk(KERN_INFO
    			"warning: process `%s' used the deprecated sysctl "
    			"system call with ", current->comm);
    
    			printk("%d.", name[i]);
    		printk("\n");
    	}
    
    static int do_sysctl(int __user *args_name, int nlen,
    
    	void __user *oldval, size_t __user *oldlenp,
    	void __user *newval, size_t newlen)
    {
    	int name[CTL_MAXNAME];
    	size_t oldlen = 0;
    	int i;
    
    	if (nlen <= 0 || nlen >= CTL_MAXNAME)
    		return -ENOTDIR;
    	if (oldval && !oldlenp)
    		return -EFAULT;
    	if (oldlenp && get_user(oldlen, oldlenp))
    		return -EFAULT;
    
    	/* Read in the sysctl name for simplicity */
    	for (i = 0; i < nlen; i++)
    		if (get_user(name[i], args_name + i))
    			return -EFAULT;
    
    	deprecated_sysctl_warning(name, nlen);
    
    	return binary_sysctl(name, nlen, oldval, oldlenp, newval, newlen);
    }
    
    
    SYSCALL_DEFINE1(sysctl, struct __sysctl_args __user *, args)
    {
    	struct __sysctl_args tmp;
    	int error;
    
    	if (copy_from_user(&tmp, args, sizeof(tmp)))
    		return -EFAULT;
    
    	lock_kernel();
    	error = do_sysctl(tmp.name, tmp.nlen, tmp.oldval, tmp.oldlenp,
    			  tmp.newval, tmp.newlen);
    	unlock_kernel();
    
    	return error;
    
    
    #ifdef CONFIG_COMPAT
    #include <asm/compat.h>
    
    struct compat_sysctl_args {
    	compat_uptr_t	name;
    	int		nlen;
    	compat_uptr_t	oldval;
    	compat_uptr_t	oldlenp;
    	compat_uptr_t	newval;
    	compat_size_t	newlen;
    	compat_ulong_t	__unused[4];
    };
    
    asmlinkage long compat_sys_sysctl(struct compat_sysctl_args __user *args)
    {
    	struct compat_sysctl_args tmp;
    	compat_size_t __user *compat_oldlenp;
    	size_t __user *oldlenp = NULL;
    	size_t oldlen = 0;
    	ssize_t result;
    
    	if (copy_from_user(&tmp, args, sizeof(tmp)))
    		return -EFAULT;
    
    	compat_oldlenp = compat_ptr(tmp.oldlenp);
    	if (compat_oldlenp) {
    		oldlenp = compat_alloc_user_space(sizeof(*compat_oldlenp));
    
    		if (get_user(oldlen, compat_oldlenp) ||
    		    put_user(oldlen, oldlenp))
    			return -EFAULT;
    	}
    
    	lock_kernel();
    	result = do_sysctl(compat_ptr(tmp.name), tmp.nlen,
    			   compat_ptr(tmp.oldval), oldlenp,
    			   compat_ptr(tmp.newval), tmp.newlen);
    	unlock_kernel();
    
    	if (oldlenp && !result) {
    		if (get_user(oldlen, oldlenp) ||
    		    put_user(oldlen, compat_oldlenp))
    			return -EFAULT;
    	}
    
    	return result;
    }
    
    #endif /* CONFIG_COMPAT */