ipc: Store ipc sysctls in the ipc namespace
[linux-2.6-microblaze.git] / ipc / ipc_sysctl.c
index f101c17..15210ac 100644 (file)
 #include <linux/capability.h>
 #include <linux/ipc_namespace.h>
 #include <linux/msg.h>
+#include <linux/slab.h>
 #include "util.h"
 
-static void *get_ipc(struct ctl_table *table)
-{
-       char *which = table->data;
-       struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
-       which = (which - (char *)&init_ipc_ns) + (char *)ipc_ns;
-       return which;
-}
-
-static int proc_ipc_dointvec(struct ctl_table *table, int write,
-               void *buffer, size_t *lenp, loff_t *ppos)
-{
-       struct ctl_table ipc_table;
-
-       memcpy(&ipc_table, table, sizeof(ipc_table));
-       ipc_table.data = get_ipc(table);
-
-       return proc_dointvec(&ipc_table, write, buffer, lenp, ppos);
-}
-
-static int proc_ipc_dointvec_minmax(struct ctl_table *table, int write,
+static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
                void *buffer, size_t *lenp, loff_t *ppos)
 {
+       struct ipc_namespace *ns = table->extra1;
        struct ctl_table ipc_table;
+       int err;
 
        memcpy(&ipc_table, table, sizeof(ipc_table));
-       ipc_table.data = get_ipc(table);
 
-       return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
-}
+       ipc_table.extra1 = SYSCTL_ZERO;
+       ipc_table.extra2 = SYSCTL_ONE;
 
-static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
-               void *buffer, size_t *lenp, loff_t *ppos)
-{
-       struct ipc_namespace *ns = current->nsproxy->ipc_ns;
-       int err = proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       err = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 
        if (err < 0)
                return err;
@@ -58,17 +37,6 @@ static int proc_ipc_dointvec_minmax_orphans(struct ctl_table *table, int write,
        return err;
 }
 
-static int proc_ipc_doulongvec_minmax(struct ctl_table *table, int write,
-               void *buffer, size_t *lenp, loff_t *ppos)
-{
-       struct ctl_table ipc_table;
-       memcpy(&ipc_table, table, sizeof(ipc_table));
-       ipc_table.data = get_ipc(table);
-
-       return proc_doulongvec_minmax(&ipc_table, write, buffer,
-                                       lenp, ppos);
-}
-
 static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
                void *buffer, size_t *lenp, loff_t *ppos)
 {
@@ -87,11 +55,17 @@ static int proc_ipc_auto_msgmni(struct ctl_table *table, int write,
 static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
        void *buffer, size_t *lenp, loff_t *ppos)
 {
+       struct ipc_namespace *ns = table->extra1;
+       struct ctl_table ipc_table;
        int ret, semmni;
-       struct ipc_namespace *ns = current->nsproxy->ipc_ns;
+
+       memcpy(&ipc_table, table, sizeof(ipc_table));
+
+       ipc_table.extra1 = NULL;
+       ipc_table.extra2 = NULL;
 
        semmni = ns->sem_ctls[3];
-       ret = proc_ipc_dointvec(table, write, buffer, lenp, ppos);
+       ret = proc_dointvec(table, write, buffer, lenp, ppos);
 
        if (!ret)
                ret = sem_check_semmni(current->nsproxy->ipc_ns);
@@ -108,12 +82,18 @@ static int proc_ipc_sem_dointvec(struct ctl_table *table, int write,
 static int proc_ipc_dointvec_minmax_checkpoint_restore(struct ctl_table *table,
                int write, void *buffer, size_t *lenp, loff_t *ppos)
 {
-       struct user_namespace *user_ns = current->nsproxy->ipc_ns->user_ns;
+       struct ipc_namespace *ns = table->extra1;
+       struct ctl_table ipc_table;
 
-       if (write && !checkpoint_restore_ns_capable(user_ns))
+       if (write && !checkpoint_restore_ns_capable(ns->user_ns))
                return -EPERM;
 
-       return proc_ipc_dointvec_minmax(table, write, buffer, lenp, ppos);
+       memcpy(&ipc_table, table, sizeof(ipc_table));
+
+       ipc_table.extra1 = SYSCTL_ZERO;
+       ipc_table.extra2 = SYSCTL_INT_MAX;
+
+       return proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos);
 }
 #endif
 
@@ -121,27 +101,27 @@ int ipc_mni = IPCMNI;
 int ipc_mni_shift = IPCMNI_SHIFT;
 int ipc_min_cycle = RADIX_TREE_MAP_SIZE;
 
-static struct ctl_table ipc_kern_table[] = {
+static struct ctl_table ipc_sysctls[] = {
        {
                .procname       = "shmmax",
                .data           = &init_ipc_ns.shm_ctlmax,
                .maxlen         = sizeof(init_ipc_ns.shm_ctlmax),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_doulongvec_minmax,
+               .proc_handler   = proc_doulongvec_minmax,
        },
        {
                .procname       = "shmall",
                .data           = &init_ipc_ns.shm_ctlall,
                .maxlen         = sizeof(init_ipc_ns.shm_ctlall),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_doulongvec_minmax,
+               .proc_handler   = proc_doulongvec_minmax,
        },
        {
                .procname       = "shmmni",
                .data           = &init_ipc_ns.shm_ctlmni,
                .maxlen         = sizeof(init_ipc_ns.shm_ctlmni),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = SYSCTL_ZERO,
                .extra2         = &ipc_mni,
        },
@@ -151,15 +131,13 @@ static struct ctl_table ipc_kern_table[] = {
                .maxlen         = sizeof(init_ipc_ns.shm_rmid_forced),
                .mode           = 0644,
                .proc_handler   = proc_ipc_dointvec_minmax_orphans,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_ONE,
        },
        {
                .procname       = "msgmax",
                .data           = &init_ipc_ns.msg_ctlmax,
                .maxlen         = sizeof(init_ipc_ns.msg_ctlmax),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_INT_MAX,
        },
@@ -168,7 +146,7 @@ static struct ctl_table ipc_kern_table[] = {
                .data           = &init_ipc_ns.msg_ctlmni,
                .maxlen         = sizeof(init_ipc_ns.msg_ctlmni),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = SYSCTL_ZERO,
                .extra2         = &ipc_mni,
        },
@@ -186,7 +164,7 @@ static struct ctl_table ipc_kern_table[] = {
                .data           = &init_ipc_ns.msg_ctlmnb,
                .maxlen         = sizeof(init_ipc_ns.msg_ctlmnb),
                .mode           = 0644,
-               .proc_handler   = proc_ipc_dointvec_minmax,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = SYSCTL_ZERO,
                .extra2         = SYSCTL_INT_MAX,
        },
@@ -204,8 +182,6 @@ static struct ctl_table ipc_kern_table[] = {
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id),
                .mode           = 0666,
                .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_INT_MAX,
        },
        {
                .procname       = "msg_next_id",
@@ -213,8 +189,6 @@ static struct ctl_table ipc_kern_table[] = {
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id),
                .mode           = 0666,
                .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_INT_MAX,
        },
        {
                .procname       = "shm_next_id",
@@ -222,25 +196,106 @@ static struct ctl_table ipc_kern_table[] = {
                .maxlen         = sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id),
                .mode           = 0666,
                .proc_handler   = proc_ipc_dointvec_minmax_checkpoint_restore,
-               .extra1         = SYSCTL_ZERO,
-               .extra2         = SYSCTL_INT_MAX,
        },
 #endif
        {}
 };
 
-static struct ctl_table ipc_root_table[] = {
-       {
-               .procname       = "kernel",
-               .mode           = 0555,
-               .child          = ipc_kern_table,
-       },
-       {}
+static struct ctl_table_set *set_lookup(struct ctl_table_root *root)
+{
+       return &current->nsproxy->ipc_ns->ipc_set;
+}
+
+static int set_is_seen(struct ctl_table_set *set)
+{
+       return &current->nsproxy->ipc_ns->ipc_set == set;
+}
+
+static struct ctl_table_root set_root = {
+       .lookup = set_lookup,
 };
 
+bool setup_ipc_sysctls(struct ipc_namespace *ns)
+{
+       struct ctl_table *tbl;
+
+       setup_sysctl_set(&ns->ipc_set, &set_root, set_is_seen);
+
+       tbl = kmemdup(ipc_sysctls, sizeof(ipc_sysctls), GFP_KERNEL);
+       if (tbl) {
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(ipc_sysctls); i++) {
+                       if (tbl[i].data == &init_ipc_ns.shm_ctlmax) {
+                               tbl[i].data = &ns->shm_ctlmax;
+
+                       } else if (tbl[i].data == &init_ipc_ns.shm_ctlall) {
+                               tbl[i].data = &ns->shm_ctlall;
+
+                       } else if (tbl[i].data == &init_ipc_ns.shm_ctlmni) {
+                               tbl[i].data = &ns->shm_ctlmni;
+
+                       } else if (tbl[i].data == &init_ipc_ns.shm_rmid_forced) {
+                               tbl[i].data = &ns->shm_rmid_forced;
+                               tbl[i].extra1 = ns;
+
+                       } else if (tbl[i].data == &init_ipc_ns.msg_ctlmax) {
+                               tbl[i].data = &ns->msg_ctlmax;
+
+                       } else if (tbl[i].data == &init_ipc_ns.msg_ctlmni) {
+                               tbl[i].data = &ns->msg_ctlmni;
+
+                       } else if (tbl[i].data == &init_ipc_ns.msg_ctlmnb) {
+                               tbl[i].data = &ns->msg_ctlmnb;
+
+                       } else if (tbl[i].data == &init_ipc_ns.sem_ctls) {
+                               tbl[i].data = &ns->sem_ctls;
+                               tbl[i].extra1 = ns;
+#ifdef CONFIG_CHECKPOINT_RESTORE
+                       } else if (tbl[i].data == &init_ipc_ns.ids[IPC_SEM_IDS].next_id) {
+                               tbl[i].data = &ns->ids[IPC_SEM_IDS].next_id;
+                               tbl[i].extra1 = ns;
+
+                       } else if (tbl[i].data == &init_ipc_ns.ids[IPC_MSG_IDS].next_id) {
+                               tbl[i].data = &ns->ids[IPC_MSG_IDS].next_id;
+                               tbl[i].extra1 = ns;
+
+                       } else if (tbl[i].data == &init_ipc_ns.ids[IPC_SHM_IDS].next_id) {
+                               tbl[i].data = &ns->ids[IPC_SHM_IDS].next_id;
+                               tbl[i].extra1 = ns;
+#endif
+                       } else {
+                               tbl[i].data = NULL;
+                       }
+               }
+
+               ns->ipc_sysctls = __register_sysctl_table(&ns->ipc_set, "kernel", tbl);
+       }
+       if (!ns->ipc_sysctls) {
+               kfree(tbl);
+               retire_sysctl_set(&ns->ipc_set);
+               return false;
+       }
+
+       return true;
+}
+
+void retire_ipc_sysctls(struct ipc_namespace *ns)
+{
+       struct ctl_table *tbl;
+
+       tbl = ns->ipc_sysctls->ctl_table_arg;
+       unregister_sysctl_table(ns->ipc_sysctls);
+       retire_sysctl_set(&ns->ipc_set);
+       kfree(tbl);
+}
+
 static int __init ipc_sysctl_init(void)
 {
-       register_sysctl_table(ipc_root_table);
+       if (!setup_ipc_sysctls(&init_ipc_ns)) {
+               pr_warn("ipc sysctl registration failed\n");
+               return -ENOMEM;
+       }
        return 0;
 }