#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
+#include <linux/capability.h>
#include <linux/ctype.h>
#include <linux/utsname.h>
#include <linux/capability.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/net.h>
#include <linux/sysrq.h>
#include <linux/highuid.h>
#include <linux/writeback.h>
#include <linux/initrd.h>
#include <linux/times.h>
#include <linux/limits.h>
+#include <linux/dcache.h>
+#include <linux/syscalls.h>
+#include <linux/nfs_fs.h>
+#include <linux/acpi.h>
+#include <linux/vserver/cvirt.h>
+
#include <asm/uaccess.h>
+#include <asm/processor.h>
-#ifdef CONFIG_ROOT_NFS
-#include <linux/nfs_fs.h>
-#endif
+extern int proc_nr_files(ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos);
#if defined(CONFIG_SYSCTL)
/* External variables not in a header file. */
-extern int panic_timeout;
extern int C_A_D;
extern int sysctl_overcommit_memory;
extern int sysctl_overcommit_ratio;
extern int max_threads;
-extern atomic_t nr_queued_signals;
-extern int max_queued_signals;
extern int sysrq_enabled;
extern int core_uses_pid;
+extern int suid_dumpable;
extern char core_pattern[];
extern int cad_pid;
extern int pid_max;
-extern int sysctl_lower_zone_protection;
extern int min_free_kbytes;
extern int printk_ratelimit_jiffies;
extern int printk_ratelimit_burst;
-
-extern unsigned int vdso_enabled;
-
-int exec_shield = 1;
-int exec_shield_randomize = 1;
-
-static int __init setup_exec_shield(char *str)
-{
- get_option (&str, &exec_shield);
-
- return 1;
-}
-
-__setup("exec-shield=", setup_exec_shield);
-
-static int __init setup_exec_shield_randomize(char *str)
-{
- get_option (&str, &exec_shield_randomize);
-
- return 1;
-}
-
-__setup("exec-shield-randomize=", setup_exec_shield_randomize);
+extern int pid_max_min, pid_max_max;
+extern int sysctl_drop_caches;
+extern int percpu_pagelist_fraction;
+
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
+int unknown_nmi_panic;
+extern int proc_unknown_nmi_panic(ctl_table *, int, struct file *,
+ void __user *, size_t *, loff_t *);
+#endif
/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
static int maxolduid = 65535;
static int minolduid;
+static int min_percpu_pagelist_fract = 8;
static int ngroups_max = NGROUPS_MAX;
#ifdef CONFIG_KMOD
extern char modprobe_path[];
#endif
-#ifdef CONFIG_HOTPLUG
-extern char hotplug_path[];
-#endif
+extern char vshelper_path[];
#ifdef CONFIG_CHR_DEV_SG
extern int sg_big_buff;
#endif
extern int unaligned_enabled;
#endif
-#ifdef CONFIG_ARCH_S390
+#ifdef CONFIG_S390
#ifdef CONFIG_MATHEMU
extern int sysctl_ieee_emulation_warnings;
#endif
extern int sysctl_userprocess_debug;
+extern int spin_retry;
#endif
extern int sysctl_hz_timer;
-#if defined(CONFIG_PPC32) && defined(CONFIG_6xx)
-extern unsigned long powersave_nap;
-int proc_dol2crvec(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp);
-#endif
-
#ifdef CONFIG_BSD_PROCESS_ACCT
extern int acct_parm[];
#endif
+#ifdef CONFIG_IA64
+extern int no_unaligned_warning;
+#endif
+
static int parse_table(int __user *, int, void __user *, size_t __user *, void __user *, size_t,
ctl_table *, void **);
static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp);
+ void __user *buffer, size_t *lenp, loff_t *ppos);
static ctl_table root_table[];
static struct ctl_table_header root_table_header =
static ctl_table kern_table[];
static ctl_table vm_table[];
-#ifdef CONFIG_NET
-extern ctl_table net_table[];
-#endif
static ctl_table proc_table[];
static ctl_table fs_table[];
static ctl_table debug_table[];
#ifdef CONFIG_UNIX98_PTYS
extern ctl_table pty_table[];
#endif
+#ifdef CONFIG_INOTIFY
+extern ctl_table inotify_table[];
+#endif
+
+#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
+int sysctl_legacy_va_layout;
+#endif
/* /proc declarations: */
extern struct proc_dir_entry *proc_sys_root;
-static void register_proc_table(ctl_table *, struct proc_dir_entry *);
+static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *);
static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
#endif
.mode = 0555,
.child = dev_table,
},
+
{ .ctl_name = 0 }
};
.ctl_name = KERN_OSTYPE,
.procname = "ostype",
.data = system_utsname.sysname,
- .maxlen = 64,
+ .maxlen = sizeof(system_utsname.sysname),
.mode = 0444,
.proc_handler = &proc_doutsstring,
+ .virt_handler = &vx_uts_virt_handler,
.strategy = &sysctl_string,
},
{
.ctl_name = KERN_OSRELEASE,
.procname = "osrelease",
.data = system_utsname.release,
- .maxlen = 64,
+ .maxlen = sizeof(system_utsname.release),
.mode = 0444,
.proc_handler = &proc_doutsstring,
+ .virt_handler = &vx_uts_virt_handler,
.strategy = &sysctl_string,
},
{
.ctl_name = KERN_VERSION,
.procname = "version",
.data = system_utsname.version,
- .maxlen = 64,
+ .maxlen = sizeof(system_utsname.version),
.mode = 0444,
.proc_handler = &proc_doutsstring,
+ .virt_handler = &vx_uts_virt_handler,
.strategy = &sysctl_string,
},
{
.ctl_name = KERN_NODENAME,
.procname = "hostname",
.data = system_utsname.nodename,
- .maxlen = 64,
+ .maxlen = sizeof(system_utsname.nodename),
.mode = 0644,
.proc_handler = &proc_doutsstring,
+ .virt_handler = &vx_uts_virt_handler,
.strategy = &sysctl_string,
},
{
.ctl_name = KERN_DOMAINNAME,
.procname = "domainname",
.data = system_utsname.domainname,
- .maxlen = 64,
+ .maxlen = sizeof(system_utsname.domainname),
.mode = 0644,
.proc_handler = &proc_doutsstring,
+ .virt_handler = &vx_uts_virt_handler,
.strategy = &sysctl_string,
},
{
.mode = 0644,
.proc_handler = &proc_dointvec,
},
- {
- .ctl_name = KERN_PANIC,
- .procname = "exec-shield",
- .data = &exec_shield,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
- {
- .ctl_name = KERN_PANIC,
- .procname = "exec-shield-randomize",
- .data = &exec_shield_randomize,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
- {
- .ctl_name = KERN_PANIC,
- .procname = "print-fatal-signals",
- .data = &print_fatal_signals,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
-#if __i386__
- {
- .ctl_name = KERN_PANIC,
- .procname = "vdso",
- .data = &vdso_enabled,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
-#endif
{
.ctl_name = KERN_CORE_USES_PID,
.procname = "core_uses_pid",
.procname = "tainted",
.data = &tainted,
.maxlen = sizeof(int),
- .mode = 0644,
+ .mode = 0444,
.proc_handler = &proc_dointvec,
},
{
.mode = 0644,
.proc_handler = &proc_dointvec,
},
-#endif
-#if defined(CONFIG_PPC32) && defined(CONFIG_6xx)
- {
- .ctl_name = KERN_PPC_POWERSAVE_NAP,
- .procname = "powersave-nap",
- .data = &powersave_nap,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
- {
- .ctl_name = KERN_PPC_L2CR,
- .procname = "l2cr",
- .mode = 0644,
- .proc_handler = &proc_dol2crvec,
- },
#endif
{
.ctl_name = KERN_CTLALTDEL,
.ctl_name = KERN_MODPROBE,
.procname = "modprobe",
.data = &modprobe_path,
- .maxlen = 256,
+ .maxlen = KMOD_PATH_LEN,
.mode = 0644,
.proc_handler = &proc_dostring,
.strategy = &sysctl_string,
{
.ctl_name = KERN_HOTPLUG,
.procname = "hotplug",
- .data = &hotplug_path,
- .maxlen = 256,
+ .data = &uevent_helper,
+ .maxlen = UEVENT_HELPER_PATH_LEN,
.mode = 0644,
.proc_handler = &proc_dostring,
.strategy = &sysctl_string,
},
#endif
+ {
+ .ctl_name = KERN_VSHELPER,
+ .procname = "vshelper",
+ .data = &vshelper_path,
+ .maxlen = 256,
+ .mode = 0644,
+ .proc_handler = &proc_dostring,
+ .strategy = &sysctl_string,
+ },
#ifdef CONFIG_CHR_DEV_SG
{
.ctl_name = KERN_SG_BIG_BUFF,
.proc_handler = &proc_dointvec,
},
#endif
- {
- .ctl_name = KERN_RTSIGNR,
- .procname = "rtsig-nr",
- .data = &nr_queued_signals,
- .maxlen = sizeof(int),
- .mode = 0444,
- .proc_handler = &proc_dointvec,
- },
- {
- .ctl_name = KERN_RTSIGMAX,
- .procname = "rtsig-max",
- .data = &max_queued_signals,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = &proc_dointvec,
- },
#ifdef CONFIG_SYSVIPC
{
.ctl_name = KERN_SHMMAX,
.extra1 = &minolduid,
.extra2 = &maxolduid,
},
-#ifdef CONFIG_ARCH_S390
+#ifdef CONFIG_S390
#ifdef CONFIG_MATHEMU
{
.ctl_name = KERN_IEEE_EMULATION_WARNINGS,
.data = &pid_max,
.maxlen = sizeof (int),
.mode = 0644,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_dointvec_minmax,
+ .strategy = sysctl_intvec,
+ .extra1 = &pid_max_min,
+ .extra2 = &pid_max_max,
},
{
.ctl_name = KERN_PANIC_ON_OOPS,
.mode = 0444,
.proc_handler = &proc_dointvec,
},
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86)
+ {
+ .ctl_name = KERN_UNKNOWN_NMI_PANIC,
+ .procname = "unknown_nmi_panic",
+ .data = &unknown_nmi_panic,
+ .maxlen = sizeof (int),
+ .mode = 0644,
+ .proc_handler = &proc_unknown_nmi_panic,
+ },
+#endif
+#if defined(CONFIG_X86)
+ {
+ .ctl_name = KERN_BOOTLOADER_TYPE,
+ .procname = "bootloader_type",
+ .data = &bootloader_type,
+ .maxlen = sizeof (int),
+ .mode = 0444,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
+#if defined(CONFIG_MMU)
+ {
+ .ctl_name = KERN_RANDOMIZE,
+ .procname = "randomize_va_space",
+ .data = &randomize_va_space,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
+#if defined(CONFIG_S390) && defined(CONFIG_SMP)
+ {
+ .ctl_name = KERN_SPIN_RETRY,
+ .procname = "spin_retry",
+ .data = &spin_retry,
+ .maxlen = sizeof (int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
+#ifdef CONFIG_ACPI_SLEEP
+ {
+ .ctl_name = KERN_ACPI_VIDEO_FLAGS,
+ .procname = "acpi_video_flags",
+ .data = &acpi_video_flags,
+ .maxlen = sizeof (unsigned long),
+ .mode = 0644,
+ .proc_handler = &proc_doulongvec_minmax,
+ },
+#endif
+#ifdef CONFIG_IA64
+ {
+ .ctl_name = KERN_IA64_UNALIGNED,
+ .procname = "ignore-unaligned-usertrap",
+ .data = &no_unaligned_warning,
+ .maxlen = sizeof (int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
+#endif
{ .ctl_name = 0 }
};
.extra1 = (void *)&hugetlb_zero,
.extra2 = (void *)&hugetlb_infinity,
},
+ {
+ .ctl_name = VM_HUGETLB_GROUP,
+ .procname = "hugetlb_shm_group",
+ .data = &sysctl_hugetlb_shm_group,
+ .maxlen = sizeof(gid_t),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
#endif
{
- .ctl_name = VM_LOWER_ZONE_PROTECTION,
- .procname = "lower_zone_protection",
- .data = &sysctl_lower_zone_protection,
- .maxlen = sizeof(sysctl_lower_zone_protection),
+ .ctl_name = VM_LOWMEM_RESERVE_RATIO,
+ .procname = "lowmem_reserve_ratio",
+ .data = &sysctl_lowmem_reserve_ratio,
+ .maxlen = sizeof(sysctl_lowmem_reserve_ratio),
.mode = 0644,
- .proc_handler = &lower_zone_protection_sysctl_handler,
+ .proc_handler = &lowmem_reserve_ratio_sysctl_handler,
+ .strategy = &sysctl_intvec,
+ },
+ {
+ .ctl_name = VM_DROP_PAGECACHE,
+ .procname = "drop_caches",
+ .data = &sysctl_drop_caches,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = drop_caches_sysctl_handler,
.strategy = &sysctl_intvec,
- .extra1 = &zero,
},
{
.ctl_name = VM_MIN_FREE_KBYTES,
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
+ {
+ .ctl_name = VM_PERCPU_PAGELIST_FRACTION,
+ .procname = "percpu_pagelist_fraction",
+ .data = &percpu_pagelist_fraction,
+ .maxlen = sizeof(percpu_pagelist_fraction),
+ .mode = 0644,
+ .proc_handler = &percpu_pagelist_fraction_sysctl_handler,
+ .strategy = &sysctl_intvec,
+ .extra1 = &min_percpu_pagelist_fract,
+ },
+#ifdef CONFIG_MMU
{
.ctl_name = VM_MAX_MAP_COUNT,
.procname = "max_map_count",
.mode = 0644,
.proc_handler = &proc_dointvec
},
+#endif
{
.ctl_name = VM_LAPTOP_MODE,
.procname = "laptop_mode",
.strategy = &sysctl_intvec,
.extra1 = &zero,
},
+ {
+ .ctl_name = VM_VFS_CACHE_PRESSURE,
+ .procname = "vfs_cache_pressure",
+ .data = &sysctl_vfs_cache_pressure,
+ .maxlen = sizeof(sysctl_vfs_cache_pressure),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ },
+#ifdef HAVE_ARCH_PICK_MMAP_LAYOUT
+ {
+ .ctl_name = VM_LEGACY_VA_LAYOUT,
+ .procname = "legacy_va_layout",
+ .data = &sysctl_legacy_va_layout,
+ .maxlen = sizeof(sysctl_legacy_va_layout),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ },
+#endif
+#ifdef CONFIG_SWAP
+ {
+ .ctl_name = VM_SWAP_TOKEN_TIMEOUT,
+ .procname = "swap_token_timeout",
+ .data = &swap_token_default_timeout,
+ .maxlen = sizeof(swap_token_default_timeout),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_jiffies,
+ .strategy = &sysctl_jiffies,
+ },
+#endif
+#ifdef CONFIG_NUMA
+ {
+ .ctl_name = VM_ZONE_RECLAIM_MODE,
+ .procname = "zone_reclaim_mode",
+ .data = &zone_reclaim_mode,
+ .maxlen = sizeof(zone_reclaim_mode),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ },
+ {
+ .ctl_name = VM_ZONE_RECLAIM_INTERVAL,
+ .procname = "zone_reclaim_interval",
+ .data = &zone_reclaim_interval,
+ .maxlen = sizeof(zone_reclaim_interval),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec_jiffies,
+ .strategy = &sysctl_jiffies,
+ },
+#endif
{ .ctl_name = 0 }
};
.data = &files_stat,
.maxlen = 3*sizeof(int),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_nr_files,
},
{
.ctl_name = FS_MAXFILE,
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+#ifdef CONFIG_DNOTIFY
{
.ctl_name = FS_DIR_NOTIFY,
.procname = "dir-notify-enable",
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+#endif
+#ifdef CONFIG_MMU
{
.ctl_name = FS_LEASE_TIME,
.procname = "lease-break-time",
.data = &aio_nr,
.maxlen = sizeof(aio_nr),
.mode = 0444,
- .proc_handler = &proc_dointvec,
+ .proc_handler = &proc_doulongvec_minmax,
},
{
.ctl_name = FS_AIO_MAX_NR,
.data = &aio_max_nr,
.maxlen = sizeof(aio_max_nr),
.mode = 0644,
+ .proc_handler = &proc_doulongvec_minmax,
+ },
+#ifdef CONFIG_INOTIFY
+ {
+ .ctl_name = FS_INOTIFY,
+ .procname = "inotify",
+ .mode = 0555,
+ .child = inotify_table,
+ },
+#endif
+#endif
+ {
+ .ctl_name = KERN_SETUID_DUMPABLE,
+ .procname = "suid_dumpable",
+ .data = &suid_dumpable,
+ .maxlen = sizeof(int),
+ .mode = 0644,
.proc_handler = &proc_dointvec,
},
{ .ctl_name = 0 }
static ctl_table dev_table[] = {
{ .ctl_name = 0 }
-};
+};
extern void init_irq_proc (void);
+static DEFINE_SPINLOCK(sysctl_lock);
+
+/* called under sysctl_lock */
+static int use_table(struct ctl_table_header *p)
+{
+ if (unlikely(p->unregistering))
+ return 0;
+ p->used++;
+ return 1;
+}
+
+/* called under sysctl_lock */
+static void unuse_table(struct ctl_table_header *p)
+{
+ if (!--p->used)
+ if (unlikely(p->unregistering))
+ complete(p->unregistering);
+}
+
+/* called under sysctl_lock, will reacquire if has to wait */
+static void start_unregistering(struct ctl_table_header *p)
+{
+ /*
+ * if p->used is 0, nobody will ever touch that entry again;
+ * we'll eliminate all paths to it before dropping sysctl_lock
+ */
+ if (unlikely(p->used)) {
+ struct completion wait;
+ init_completion(&wait);
+ p->unregistering = &wait;
+ spin_unlock(&sysctl_lock);
+ wait_for_completion(&wait);
+ spin_lock(&sysctl_lock);
+ }
+ /*
+ * do not remove from the list until nobody holds it; walking the
+ * list in do_sysctl() relies on that.
+ */
+ list_del_init(&p->ctl_entry);
+}
+
void __init sysctl_init(void)
{
#ifdef CONFIG_PROC_FS
- register_proc_table(root_table, proc_sys_root);
+ register_proc_table(root_table, proc_sys_root, &root_table_header);
init_irq_proc();
#endif
}
void __user *newval, size_t newlen)
{
struct list_head *tmp;
+ int error = -ENOTDIR;
if (nlen <= 0 || nlen >= CTL_MAXNAME)
return -ENOTDIR;
if (!oldlenp || get_user(old_len, oldlenp))
return -EFAULT;
}
+ spin_lock(&sysctl_lock);
tmp = &root_table_header.ctl_entry;
do {
struct ctl_table_header *head =
list_entry(tmp, struct ctl_table_header, ctl_entry);
void *context = NULL;
- int error = parse_table(name, nlen, oldval, oldlenp,
+
+ if (!use_table(head))
+ continue;
+
+ spin_unlock(&sysctl_lock);
+
+ error = parse_table(name, nlen, oldval, oldlenp,
newval, newlen, head->ctl_table,
&context);
- if (context)
- kfree(context);
+ kfree(context);
+
+ spin_lock(&sysctl_lock);
+ unuse_table(head);
if (error != -ENOTDIR)
- return error;
- tmp = tmp->next;
- } while (tmp != &root_table_header.ctl_entry);
- return -ENOTDIR;
+ break;
+ } while ((tmp = tmp->next) != &root_table_header.ctl_entry);
+ spin_unlock(&sysctl_lock);
+ return error;
}
asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
return NULL;
tmp->ctl_table = table;
INIT_LIST_HEAD(&tmp->ctl_entry);
+ tmp->used = 0;
+ tmp->unregistering = NULL;
+ spin_lock(&sysctl_lock);
if (insert_at_head)
list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
else
list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
+ spin_unlock(&sysctl_lock);
#ifdef CONFIG_PROC_FS
- register_proc_table(table, proc_sys_root);
+ register_proc_table(table, proc_sys_root, tmp);
#endif
return tmp;
}
*/
void unregister_sysctl_table(struct ctl_table_header * header)
{
- list_del(&header->ctl_entry);
+ might_sleep();
+ spin_lock(&sysctl_lock);
+ start_unregistering(header);
#ifdef CONFIG_PROC_FS
unregister_proc_table(header->ctl_table, proc_sys_root);
#endif
+ spin_unlock(&sysctl_lock);
kfree(header);
}
#ifdef CONFIG_PROC_FS
/* Scan the sysctl entries in table and add them all into /proc */
-static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
+static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set)
{
struct proc_dir_entry *de;
int len;
de = create_proc_entry(table->procname, mode, root);
if (!de)
continue;
+ de->set = set;
de->data = (void *) table;
if (table->proc_handler)
de->proc_fops = &proc_sys_file_operations;
}
table->de = de;
if (de->mode & S_IFDIR)
- register_proc_table(table->child, de);
+ register_proc_table(table->child, de, set);
}
}
continue;
}
+ /*
+ * In any case, mark the entry as goner; we'll keep it
+ * around if it's busy, but we'll know to do nothing with
+ * its fields. We are under sysctl_lock here.
+ */
+ de->data = NULL;
+
/* Don't unregister proc entries that are still being used.. */
if (atomic_read(&de->count))
continue;
size_t count, loff_t *ppos)
{
int op;
- struct proc_dir_entry *de;
+ struct proc_dir_entry *de = PDE(file->f_dentry->d_inode);
struct ctl_table *table;
size_t res;
- ssize_t error;
-
- de = PDE(file->f_dentry->d_inode);
- if (!de || !de->data)
- return -ENOTDIR;
- table = (struct ctl_table *) de->data;
- if (!table || !table->proc_handler)
- return -ENOTDIR;
- op = (write ? 002 : 004);
- if (ctl_perm(table, op))
- return -EPERM;
+ ssize_t error = -ENOTDIR;
- res = count;
-
- /*
- * FIXME: we need to pass on ppos to the handler.
- */
-
- error = (*table->proc_handler) (table, write, file, buf, &res);
- if (error)
- return error;
- return res;
+ spin_lock(&sysctl_lock);
+ if (de && de->data && use_table(de->set)) {
+ /*
+ * at that point we know that sysctl was not unregistered
+ * and won't be until we finish
+ */
+ spin_unlock(&sysctl_lock);
+ table = (struct ctl_table *) de->data;
+ if (!table || !table->proc_handler)
+ goto out;
+ error = -EPERM;
+ op = (write ? 002 : 004);
+ if (ctl_perm(table, op))
+ goto out;
+
+ /* careful: calling conventions are nasty here */
+ res = count;
+ error = (*table->proc_handler)(table, write, file,
+ buf, &res, ppos);
+ if (!error)
+ error = res;
+ out:
+ spin_lock(&sysctl_lock);
+ unuse_table(de->set);
+ }
+ spin_unlock(&sysctl_lock);
+ return error;
}
static int proc_opensys(struct inode *inode, struct file *file)
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes a string from/to the user buffer. If the kernel
* buffer provided is not large enough to hold the string, the
* Returns 0 on success.
*/
int proc_dostring(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- size_t len;
+ size_t len, maxlen;
char __user *p;
char c;
+ void *data;
+
+ data = table->data;
+ maxlen = table->maxlen;
+
+ if (!data || !maxlen || !*lenp || (*ppos && !write))
+ return (*lenp = 0);
- if (!table->data || !table->maxlen || !*lenp ||
- (filp->f_pos && !write)) {
- *lenp = 0;
- return 0;
- }
-
+ if (table->virt_handler)
+ table->virt_handler(table, write, filp->f_xid, &data, &maxlen);
+
if (write) {
len = 0;
p = buffer;
break;
len++;
}
- if (len >= table->maxlen)
- len = table->maxlen-1;
- if(copy_from_user(table->data, buffer, len))
+ if (len >= maxlen)
+ len = maxlen-1;
+ if(copy_from_user(data, buffer, len))
return -EFAULT;
- ((char *) table->data)[len] = 0;
- filp->f_pos += *lenp;
+ ((char *) data)[len] = 0;
+ *ppos += *lenp;
} else {
- len = strlen(table->data);
- if (len > table->maxlen)
- len = table->maxlen;
+ len = strlen(data);
+ if (len > maxlen)
+ len = maxlen;
if (len > *lenp)
len = *lenp;
if (len)
- if(copy_to_user(buffer, table->data, len))
+ if(copy_to_user(buffer, data, len))
return -EFAULT;
if (len < *lenp) {
if(put_user('\n', ((char __user *) buffer) + len))
len++;
}
*lenp = len;
- filp->f_pos += len;
+ *ppos += len;
}
return 0;
}
*/
static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int r;
if (!write) {
down_read(&uts_sem);
- r=proc_dostring(table,0,filp,buffer,lenp);
+ r=proc_dostring(table,0,filp,buffer,lenp, ppos);
up_read(&uts_sem);
} else {
down_write(&uts_sem);
- r=proc_dostring(table,1,filp,buffer,lenp);
+ r=proc_dostring(table,1,filp,buffer,lenp, ppos);
up_write(&uts_sem);
}
return r;
}
static int do_proc_dointvec(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp,
+ void __user *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(int *negp, unsigned long *lvalp, int *valp,
int write, void *data),
void *data)
{
-#define TMPBUFLEN 20
+#define TMPBUFLEN 21
int *i, vleft, first=1, neg, val;
unsigned long lval;
size_t left, len;
char __user *s = buffer;
if (!table->data || !table->maxlen || !*lenp ||
- (filp->f_pos && !write)) {
+ (*ppos && !write)) {
*lenp = 0;
return 0;
}
if (write && first)
return -EINVAL;
*lenp -= left;
- filp->f_pos += *lenp;
+ *ppos += *lenp;
return 0;
#undef TMPBUFLEN
}
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* Returns 0 on success.
*/
int proc_dointvec(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table,write,filp,buffer,lenp,
+ return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
NULL,NULL);
}
*/
int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
int op;
}
op = (current->pid == 1) ? OP_SET : OP_AND;
- return do_proc_dointvec(table,write,filp,buffer,lenp,
+ return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
do_proc_dointvec_bset_conv,&op);
}
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* Returns 0 on success.
*/
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct do_proc_dointvec_minmax_conv_param param = {
.min = (int *) table->extra1,
.max = (int *) table->extra2,
};
- return do_proc_dointvec(table, write, filp, buffer, lenp,
+ return do_proc_dointvec(table, write, filp, buffer, lenp, ppos,
do_proc_dointvec_minmax_conv, ¶m);
}
static int do_proc_doulongvec_minmax(ctl_table *table, int write,
struct file *filp,
- void __user *buffer, size_t *lenp,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos,
unsigned long convmul,
unsigned long convdiv)
{
-#define TMPBUFLEN 20
+#define TMPBUFLEN 21
unsigned long *i, *min, *max, val;
int vleft, first=1, neg;
size_t len, left;
char __user *s = buffer;
if (!table->data || !table->maxlen || !*lenp ||
- (filp->f_pos && !write)) {
+ (*ppos && !write)) {
*lenp = 0;
return 0;
}
if (write && first)
return -EINVAL;
*lenp -= left;
- filp->f_pos += *lenp;
+ *ppos += *lenp;
return 0;
#undef TMPBUFLEN
}
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long
* values from/to the user buffer, treated as an ASCII string.
* Returns 0 on success.
*/
int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_doulongvec_minmax(table, write, filp, buffer, lenp, 1l, 1l);
+ return do_proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos, 1l, 1l);
}
/**
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned long) unsigned long
* values from/to the user buffer, treated as an ASCII string. The values
*/
int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
return do_proc_doulongvec_minmax(table, write, filp, buffer,
- lenp, HZ, 1000l);
+ lenp, ppos, HZ, 1000l);
}
return 0;
}
+static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp,
+ int *valp,
+ int write, void *data)
+{
+ if (write) {
+ *valp = msecs_to_jiffies(*negp ? -*lvalp : *lvalp);
+ } else {
+ int val = *valp;
+ unsigned long lval;
+ if (val < 0) {
+ *negp = -1;
+ lval = (unsigned long)-val;
+ } else {
+ *negp = 0;
+ lval = (unsigned long)val;
+ }
+ *lvalp = jiffies_to_msecs(lval);
+ }
+ return 0;
+}
+
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
* @table: the sysctl table
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* Returns 0 on success.
*/
int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table,write,filp,buffer,lenp,
+ return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
do_proc_dointvec_jiffies_conv,NULL);
}
* @filp: the file structure
* @buffer: the user buffer
* @lenp: the size of the user buffer
+ * @ppos: pointer to the file position
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) integer
* values from/to the user buffer, treated as an ASCII string.
* Returns 0 on success.
*/
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
- return do_proc_dointvec(table,write,filp,buffer,lenp,
+ return do_proc_dointvec(table,write,filp,buffer,lenp,ppos,
do_proc_dointvec_userhz_jiffies_conv,NULL);
}
+/**
+ * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds
+ * @table: the sysctl table
+ * @write: %TRUE if this is a write to the sysctl file
+ * @filp: the file structure
+ * @buffer: the user buffer
+ * @lenp: the size of the user buffer
+ * @ppos: file position
+ * @ppos: the current position in the file
+ *
+ * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
+ * values from/to the user buffer, treated as an ASCII string.
+ * The values read are assumed to be in 1/1000 seconds, and
+ * are converted into jiffies.
+ *
+ * Returns 0 on success.
+ */
+int proc_dointvec_ms_jiffies(ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return do_proc_dointvec(table, write, filp, buffer, lenp, ppos,
+ do_proc_dointvec_ms_jiffies_conv, NULL);
+}
+
#else /* CONFIG_PROC_FS */
int proc_dostring(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
static int proc_doutsstring(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return -ENOSYS;
+}
+
+int proc_dointvec_ms_jiffies(ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen, void **context)
{
- size_t l, len;
-
if (!table->data || !table->maxlen)
return -ENOTDIR;
if (oldval && oldlenp) {
- if (get_user(len, oldlenp))
+ size_t bufsize;
+ if (get_user(bufsize, oldlenp))
return -EFAULT;
- if (len) {
- l = strlen(table->data);
- if (len > l) len = l;
- if (len >= table->maxlen)
+ if (bufsize) {
+ size_t len = strlen(table->data), copied;
+
+ /* This shouldn't trigger for a well-formed sysctl */
+ if (len > table->maxlen)
len = table->maxlen;
- if(copy_to_user(oldval, table->data, len))
- return -EFAULT;
- if(put_user(0, ((char __user *) oldval) + len))
+
+ /* Copy up to a max of bufsize-1 bytes of the string */
+ copied = (len >= bufsize) ? bufsize - 1 : len;
+
+ if (copy_to_user(oldval, table->data, copied) ||
+ put_user(0, (char __user *)(oldval + copied)))
return -EFAULT;
- if(put_user(len, oldlenp))
+ if (put_user(len, oldlenp))
return -EFAULT;
}
}
if (newval && newlen) {
- len = newlen;
+ size_t len = newlen;
if (len > table->maxlen)
len = table->maxlen;
if(copy_from_user(table->data, newval, len))
len--;
((char *) table->data)[len] = 0;
}
- return 0;
+ return 1;
}
/*
return 1;
}
+/* Strategy function to convert jiffies to seconds */
+int sysctl_ms_jiffies(ctl_table *table, int __user *name, int nlen,
+ void __user *oldval, size_t __user *oldlenp,
+ void __user *newval, size_t newlen, void **context)
+{
+ if (oldval) {
+ size_t olen;
+ if (oldlenp) {
+ if (get_user(olen, oldlenp))
+ return -EFAULT;
+ if (olen!=sizeof(int))
+ return -EINVAL;
+ }
+ if (put_user(jiffies_to_msecs(*(int *)(table->data)), (int __user *)oldval) ||
+ (oldlenp && put_user(sizeof(int),oldlenp)))
+ return -EFAULT;
+ }
+ if (newval && newlen) {
+ int new;
+ if (newlen != sizeof(int))
+ return -EINVAL;
+ if (get_user(new, (int __user *)newval))
+ return -EFAULT;
+ *(int *)(table->data) = msecs_to_jiffies(new);
+ }
+ return 1;
+}
#else /* CONFIG_SYSCTL */
return -ENOSYS;
}
+int sysctl_ms_jiffies(ctl_table *table, int __user *name, int nlen,
+ void __user *oldval, size_t __user *oldlenp,
+ void __user *newval, size_t newlen, void **context)
+{
+ return -ENOSYS;
+}
+
int proc_dostring(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_bset(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_jiffies(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_dointvec_userhz_jiffies(ctl_table *table, int write, struct file *filp,
- void *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ return -ENOSYS;
+}
+
+int proc_dointvec_ms_jiffies(ctl_table *table, int write, struct file *filp,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_minmax(ctl_table *table, int write, struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer, size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
int proc_doulongvec_ms_jiffies_minmax(ctl_table *table, int write,
struct file *filp,
- void __user *buffer, size_t *lenp)
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
{
return -ENOSYS;
}
struct ctl_table_header * register_sysctl_table(ctl_table * table,
int insert_at_head)
{
- return 0;
+ return NULL;
}
void unregister_sysctl_table(struct ctl_table_header * table)
EXPORT_SYMBOL(proc_dointvec_jiffies);
EXPORT_SYMBOL(proc_dointvec_minmax);
EXPORT_SYMBOL(proc_dointvec_userhz_jiffies);
+EXPORT_SYMBOL(proc_dointvec_ms_jiffies);
EXPORT_SYMBOL(proc_dostring);
EXPORT_SYMBOL(proc_doulongvec_minmax);
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
EXPORT_SYMBOL(register_sysctl_table);
EXPORT_SYMBOL(sysctl_intvec);
EXPORT_SYMBOL(sysctl_jiffies);
+EXPORT_SYMBOL(sysctl_ms_jiffies);
EXPORT_SYMBOL(sysctl_string);
EXPORT_SYMBOL(unregister_sysctl_table);