Driver core: change add_uevent_var to use a struct

This changes the uevent buffer functions to use a struct instead of a
long list of parameters. It does no longer require the caller to do the
proper buffer termination and size accounting, which is currently wrong
in some places. It fixes a known bug where parts of the uevent
environment are overwritten because of wrong index calculations.

Many thanks to Mathieu Desnoyers for finding bugs and improving the
error handling.

Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


diff --git a/include/linux/device.h b/include/linux/device.h
index 5d97ca6..2e15822 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -66,8 +66,7 @@
 	struct driver_attribute	* drv_attrs;
 
 	int		(*match)(struct device * dev, struct device_driver * drv);
-	int		(*uevent)(struct device *dev, char **envp,
-				  int num_envp, char *buffer, int buffer_size);
+	int		(*uevent)(struct device *dev, struct kobj_uevent_env *env);
 	int		(*probe)(struct device * dev);
 	int		(*remove)(struct device * dev);
 	void		(*shutdown)(struct device * dev);
@@ -187,10 +186,8 @@
 	struct class_device_attribute	* class_dev_attrs;
 	struct device_attribute		* dev_attrs;
 
-	int	(*uevent)(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
-	int	(*dev_uevent)(struct device *dev, char **envp, int num_envp,
-				char *buffer, int buffer_size);
+	int	(*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
+	int	(*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
 
 	void	(*release)(struct class_device *dev);
 	void	(*class_release)(struct class *class);
@@ -266,8 +263,7 @@
 	struct attribute_group  ** groups;	/* optional groups */
 
 	void	(*release)(struct class_device *dev);
-	int	(*uevent)(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
+	int	(*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
 	char	class_id[BUS_ID_SIZE];	/* unique to this class */
 };
 
@@ -335,8 +331,7 @@
 struct device_type {
 	const char *name;
 	struct attribute_group **groups;
-	int (*uevent)(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size);
+	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 	void (*release)(struct device *dev);
 	int (*suspend)(struct device * dev, pm_message_t state);
 	int (*resume)(struct device * dev);
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 949706c..626bdd3 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -29,6 +29,8 @@
 
 #define KOBJ_NAME_LEN			20
 #define UEVENT_HELPER_PATH_LEN		256
+#define UEVENT_NUM_ENVP			32	/* number of env pointers */
+#define UEVENT_BUFFER_SIZE		2048	/* buffer for the variables */
 
 /* path to the userspace helper executed on an event */
 extern char uevent_helper[];
@@ -111,11 +113,18 @@
 	struct attribute	** default_attrs;
 };
 
+struct kobj_uevent_env {
+	char *envp[UEVENT_NUM_ENVP];
+	int envp_idx;
+	char buf[UEVENT_BUFFER_SIZE];
+	int buflen;
+};
+
 struct kset_uevent_ops {
 	int (*filter)(struct kset *kset, struct kobject *kobj);
 	const char *(*name)(struct kset *kset, struct kobject *kobj);
-	int (*uevent)(struct kset *kset, struct kobject *kobj, char **envp,
-			int num_envp, char *buffer, int buffer_size);
+	int (*uevent)(struct kset *kset, struct kobject *kobj,
+		      struct kobj_uevent_env *env);
 };
 
 /*
@@ -275,10 +284,8 @@
 int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
 			char *envp[]);
 
-int add_uevent_var(char **envp, int num_envp, int *cur_index,
-			char *buffer, int buffer_size, int *cur_len,
-			const char *format, ...)
-	__attribute__((format (printf, 7, 8)));
+int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
+	__attribute__((format (printf, 2, 3)));
 #else
 static inline int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 { return 0; }
@@ -287,9 +294,7 @@
 				      char *envp[])
 { return 0; }
 
-static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
-				      char *buffer, int buffer_size, int *cur_len, 
-				      const char *format, ...)
+static inline int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 { return 0; }
 #endif