sysctl: Factor out sysctl_data.
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 18 Oct 2007 10:05:23 +0000 (03:05 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Thu, 18 Oct 2007 21:37:22 +0000 (14:37 -0700)
There as been no easy way to wrap the default sysctl strategy routine except
for returning 0.  Which is not always what we want.  The few instances I have
seen that want different behaviour have written their own version of
sysctl_data.  While not too hard it is unnecessary code and has the potential
for extra bugs.

So to make these situations easier and make that part of sysctl more symetric
I have factord sysctl_data out of do_sysctl_strategy and exported as a
function everyone can use.

Further having sysctl_data be an explicit function makes checking for badly
formed sysctl tables much easier.

Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Cc: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/sysctl.h
kernel/sysctl.c

index f73be4c..5ca510b 100644 (file)
@@ -972,6 +972,7 @@ extern int do_sysctl_strategy (struct ctl_table *table,
                               void __user *oldval, size_t __user *oldlenp,
                               void __user *newval, size_t newlen);
 
+extern ctl_handler sysctl_data;
 extern ctl_handler sysctl_string;
 extern ctl_handler sysctl_intvec;
 extern ctl_handler sysctl_jiffies;
index 73c84b6..88c8ea8 100644 (file)
@@ -1443,7 +1443,6 @@ int do_sysctl_strategy (struct ctl_table *table,
                        void __user *newval, size_t newlen)
 {
        int op = 0, rc;
-       size_t len;
 
        if (oldval)
                op |= 004;
@@ -1464,25 +1463,10 @@ int do_sysctl_strategy (struct ctl_table *table,
        /* If there is no strategy routine, or if the strategy returns
         * zero, proceed with automatic r/w */
        if (table->data && table->maxlen) {
-               if (oldval && oldlenp) {
-                       if (get_user(len, oldlenp))
-                               return -EFAULT;
-                       if (len) {
-                               if (len > table->maxlen)
-                                       len = table->maxlen;
-                               if(copy_to_user(oldval, table->data, len))
-                                       return -EFAULT;
-                               if(put_user(len, oldlenp))
-                                       return -EFAULT;
-                       }
-               }
-               if (newval && newlen) {
-                       len = newlen;
-                       if (len > table->maxlen)
-                               len = table->maxlen;
-                       if(copy_from_user(table->data, newval, len))
-                               return -EFAULT;
-               }
+               rc = sysctl_data(table, name, nlen, oldval, oldlenp,
+                                newval, newlen);
+               if (rc < 0)
+                       return rc;
        }
        return 0;
 }
@@ -2381,6 +2365,40 @@ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write,
  * General sysctl support routines 
  */
 
+/* The generic sysctl data routine (used if no strategy routine supplied) */
+int sysctl_data(struct ctl_table *table, int __user *name, int nlen,
+               void __user *oldval, size_t __user *oldlenp,
+               void __user *newval, size_t newlen)
+{
+       size_t len;
+
+       /* Get out of I don't have a variable */
+       if (!table->data || !table->maxlen)
+               return -ENOTDIR;
+
+       if (oldval && oldlenp) {
+               if (get_user(len, oldlenp))
+                       return -EFAULT;
+               if (len) {
+                       if (len > table->maxlen)
+                               len = table->maxlen;
+                       if (copy_to_user(oldval, table->data, len))
+                               return -EFAULT;
+                       if (put_user(len, oldlenp))
+                               return -EFAULT;
+               }
+       }
+
+       if (newval && newlen) {
+               if (newlen > table->maxlen)
+                       newlen = table->maxlen;
+
+               if (copy_from_user(table->data, newval, newlen))
+                       return -EFAULT;
+       }
+       return 1;
+}
+
 /* The generic string strategy routine: */
 int sysctl_string(struct ctl_table *table, int __user *name, int nlen,
                  void __user *oldval, size_t __user *oldlenp,
@@ -2569,6 +2587,13 @@ out:
        return -ENOSYS;
 }
 
+int sysctl_data(struct ctl_table *table, int __user *name, int nlen,
+                 void __user *oldval, size_t __user *oldlenp,
+                 void __user *newval, size_t newlen)
+{
+       return -ENOSYS;
+}
+
 int sysctl_string(struct ctl_table *table, int __user *name, int nlen,
                  void __user *oldval, size_t __user *oldlenp,
                  void __user *newval, size_t newlen)
@@ -2616,4 +2641,5 @@ EXPORT_SYMBOL(sysctl_intvec);
 EXPORT_SYMBOL(sysctl_jiffies);
 EXPORT_SYMBOL(sysctl_ms_jiffies);
 EXPORT_SYMBOL(sysctl_string);
+EXPORT_SYMBOL(sysctl_data);
 EXPORT_SYMBOL(unregister_sysctl_table);