nfsd: nfsd should drop CAP_MKNOD for non-root
[safe/jmp/linux-2.6] / lib / kobject_uevent.c
index 5ccda46..318328d 100644 (file)
  */
 
 #include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+
 #include <linux/socket.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
-#include <linux/string.h>
-#include <linux/kobject.h>
 #include <net/sock.h>
 
 
-/* the strings here must match the enum in include/linux/kobject.h */
-const char *kobject_actions[] = {
-       "add",
-       "remove",
-       "change",
-       "move",
-       "online",
-       "offline",
-};
-
-#if defined(CONFIG_HOTPLUG)
 u64 uevent_seqnum;
 char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
 static DEFINE_SPINLOCK(sequence_lock);
@@ -41,10 +32,54 @@ static DEFINE_SPINLOCK(sequence_lock);
 static struct sock *uevent_sock;
 #endif
 
+/* the strings here must match the enum in include/linux/kobject.h */
+static const char *kobject_actions[] = {
+       [KOBJ_ADD] =            "add",
+       [KOBJ_REMOVE] =         "remove",
+       [KOBJ_CHANGE] =         "change",
+       [KOBJ_MOVE] =           "move",
+       [KOBJ_ONLINE] =         "online",
+       [KOBJ_OFFLINE] =        "offline",
+};
+
+/**
+ * kobject_action_type - translate action string to numeric type
+ *
+ * @buf: buffer containing the action string, newline is ignored
+ * @len: length of buffer
+ * @type: pointer to the location to store the action type
+ *
+ * Returns 0 if the action string was recognized.
+ */
+int kobject_action_type(const char *buf, size_t count,
+                       enum kobject_action *type)
+{
+       enum kobject_action action;
+       int ret = -EINVAL;
+
+       if (count && (buf[count-1] == '\n' || buf[count-1] == '\0'))
+               count--;
+
+       if (!count)
+               goto out;
+
+       for (action = 0; action < ARRAY_SIZE(kobject_actions); action++) {
+               if (strncmp(kobject_actions[action], buf, count) != 0)
+                       continue;
+               if (kobject_actions[action][count] != '\0')
+                       continue;
+               *type = action;
+               ret = 0;
+               break;
+       }
+out:
+       return ret;
+}
+
 /**
  * kobject_uevent_env - send an uevent with environmental data
  *
- * @action: action that is happening (usually KOBJ_MOVE)
+ * @action: action that is happening
  * @kobj: struct kobject that the action is happening to
  * @envp_ext: pointer to environmental data
  *
@@ -65,15 +100,18 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        int i = 0;
        int retval = 0;
 
-       pr_debug("%s\n", __FUNCTION__);
+       pr_debug("kobject: '%s' (%p): %s\n",
+                kobject_name(kobj), kobj, __func__);
 
        /* search the kset we belong to */
        top_kobj = kobj;
-       while (!top_kobj->kset && top_kobj->parent) {
+       while (!top_kobj->kset && top_kobj->parent)
                top_kobj = top_kobj->parent;
-       }
+
        if (!top_kobj->kset) {
-               pr_debug("kobject attempted to send uevent without kset!\n");
+               pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
+                        "without kset!\n", kobject_name(kobj), kobj,
+                        __func__);
                return -EINVAL;
        }
 
@@ -83,7 +121,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        /* skip the event, if the filter returns zero. */
        if (uevent_ops && uevent_ops->filter)
                if (!uevent_ops->filter(kset, kobj)) {
-                       pr_debug("kobject filter function caused the event to drop!\n");
+                       pr_debug("kobject: '%s' (%p): %s: filter function "
+                                "caused the event to drop!\n",
+                                kobject_name(kobj), kobj, __func__);
                        return 0;
                }
 
@@ -93,7 +133,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        else
                subsystem = kobject_name(&kset->kobj);
        if (!subsystem) {
-               pr_debug("unset subsytem caused the event to drop!\n");
+               pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
+                        "event to drop!\n", kobject_name(kobj), kobj,
+                        __func__);
                return 0;
        }
 
@@ -123,7 +165,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        /* keys passed in from the caller */
        if (envp_ext) {
                for (i = 0; envp_ext[i]; i++) {
-                       retval = add_uevent_var(env, envp_ext[i]);
+                       retval = add_uevent_var(env, "%s", envp_ext[i]);
                        if (retval)
                                goto exit;
                }
@@ -133,12 +175,24 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
        if (uevent_ops && uevent_ops->uevent) {
                retval = uevent_ops->uevent(kset, kobj, env);
                if (retval) {
-                       pr_debug ("%s - uevent() returned %d\n",
-                                 __FUNCTION__, retval);
+                       pr_debug("kobject: '%s' (%p): %s: uevent() returned "
+                                "%d\n", kobject_name(kobj), kobj,
+                                __func__, retval);
                        goto exit;
                }
        }
 
+       /*
+        * Mark "add" and "remove" events in the object to ensure proper
+        * events to userspace during automatic cleanup. If the object did
+        * send an "add" event, "remove" will automatically generated by
+        * the core, if not already done by the caller.
+        */
+       if (action == KOBJ_ADD)
+               kobj->state_add_uevent_sent = 1;
+       else if (action == KOBJ_REMOVE)
+               kobj->state_remove_uevent_sent = 1;
+
        /* we will send an event, so request a new sequence number */
        spin_lock(&sequence_lock);
        seq = ++uevent_seqnum;
@@ -171,8 +225,10 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                        }
 
                        NETLINK_CB(skb).dst_group = 1;
-                       netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
-               }
+                       retval = netlink_broadcast(uevent_sock, skb, 0, 1,
+                                                  GFP_KERNEL);
+               } else
+                       retval = -ENOMEM;
        }
 #endif
 
@@ -186,11 +242,13 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                retval = add_uevent_var(env, "HOME=/");
                if (retval)
                        goto exit;
-               retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
+               retval = add_uevent_var(env,
+                                       "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
                if (retval)
                        goto exit;
 
-               call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
+               retval = call_usermodehelper(argv[0], argv,
+                                            env->envp, UMH_WAIT_EXEC);
        }
 
 exit:
@@ -198,13 +256,12 @@ exit:
        kfree(env);
        return retval;
 }
-
 EXPORT_SYMBOL_GPL(kobject_uevent_env);
 
 /**
  * kobject_uevent - notify userspace by ending an uevent
  *
- * @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE)
+ * @action: action that is happening
  * @kobj: struct kobject that the action is happening to
  *
  * Returns 0 if kobject_uevent() is completed with success or the
@@ -214,7 +271,6 @@ int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 {
        return kobject_uevent_env(kobj, action, NULL);
 }
-
 EXPORT_SYMBOL_GPL(kobject_uevent);
 
 /**
@@ -231,8 +287,7 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
        int len;
 
        if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
-               printk(KERN_ERR "add_uevent_var: too many keys\n");
-               WARN_ON(1);
+               WARN(1, KERN_ERR "add_uevent_var: too many keys\n");
                return -ENOMEM;
        }
 
@@ -243,8 +298,7 @@ int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
        va_end(args);
 
        if (len >= (sizeof(env->buf) - env->buflen)) {
-               printk(KERN_ERR "add_uevent_var: buffer size too small\n");
-               WARN_ON(1);
+               WARN(1, KERN_ERR "add_uevent_var: buffer size too small\n");
                return -ENOMEM;
        }
 
@@ -270,5 +324,3 @@ static int __init kobject_uevent_init(void)
 
 postcore_initcall(kobject_uevent_init);
 #endif
-
-#endif /* CONFIG_HOTPLUG */