#include #include #include "../fs/xfs/linux-2.6/xfs_sysctl.h" #include #include #include #include #include #include #include #include #include #include #include #include #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; n = *name; 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; ssize_t error = -ENOTDIR; 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) { return -ENOSYS; } #endif /* CONFIG_SYSCTL_SYSCALL */ 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); for (i = 0; i < nlen; i++) printk("%d.", name[i]); printk("\n"); } return; } 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 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 */