From 938e4b22e2a7d0f6f3962e601339347b2d8e09f5 Mon Sep 17 00:00:00 2001 From: Lucas De Marchi Date: Tue, 30 Apr 2013 15:28:02 -0700 Subject: [PATCH] usermodehelper: export call_usermodehelper_exec() and call_usermodehelper_setup() call_usermodehelper_setup() + call_usermodehelper_exec() need to be called instead of call_usermodehelper_fns() when the cleanup function needs to be called even when an ENOMEM error occurs. In this case using call_usermodehelper_fns() the user can't distinguish if the cleanup function was called or not. [akpm@linux-foundation.org: export call_usermodehelper_setup() to modules] Signed-off-by: Lucas De Marchi Reviewed-by: Oleg Nesterov Cc: David Howells Cc: James Morris Cc: Al Viro Cc: Tejun Heo Cc: "Rafael J. Wysocki" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/kmod.h | 8 ++++++ kernel/kmod.c | 63 +++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 5398d5807075..7eebcf5d75f1 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -71,6 +71,14 @@ call_usermodehelper_fns(char *path, char **argv, char **envp, int wait, int (*init)(struct subprocess_info *info, struct cred *new), void (*cleanup)(struct subprocess_info *), void *data); +extern struct subprocess_info * +call_usermodehelper_setup(char *path, char **argv, char **envp, gfp_t gfp_mask, + int (*init)(struct subprocess_info *info, struct cred *new), + void (*cleanup)(struct subprocess_info *), void *data); + +extern int +call_usermodehelper_exec(struct subprocess_info *info, int wait); + static inline int call_usermodehelper(char *path, char **argv, char **envp, int wait) { diff --git a/kernel/kmod.c b/kernel/kmod.c index 56dd34976d7b..e11ea14ac011 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -502,34 +502,13 @@ static void helper_unlock(void) * @argv: arg vector for process * @envp: environment for process * @gfp_mask: gfp mask for memory allocation + * @cleanup: a cleanup function + * @init: an init function + * @data: arbitrary context sensitive data * * Returns either %NULL on allocation failure, or a subprocess_info * structure. This should be passed to call_usermodehelper_exec to * exec the process and free the structure. - */ -static -struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, - char **envp, gfp_t gfp_mask) -{ - struct subprocess_info *sub_info; - sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask); - if (!sub_info) - goto out; - - INIT_WORK(&sub_info->work, __call_usermodehelper); - sub_info->path = path; - sub_info->argv = argv; - sub_info->envp = envp; - out: - return sub_info; -} - -/** - * call_usermodehelper_setfns - set a cleanup/init function - * @info: a subprocess_info returned by call_usermodehelper_setup - * @cleanup: a cleanup function - * @init: an init function - * @data: arbitrary context sensitive data * * The init function is used to customize the helper process prior to * exec. A non-zero return code causes the process to error out, exit, @@ -540,16 +519,29 @@ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, * Function must be runnable in either a process context or the * context in which call_usermodehelper_exec is called. */ -static -void call_usermodehelper_setfns(struct subprocess_info *info, - int (*init)(struct subprocess_info *info, struct cred *new), - void (*cleanup)(struct subprocess_info *info), - void *data) +struct subprocess_info *call_usermodehelper_setup(char *path, char **argv, + char **envp, gfp_t gfp_mask, + int (*init)(struct subprocess_info *info, struct cred *new), + void (*cleanup)(struct subprocess_info *info), + void *data) { - info->cleanup = cleanup; - info->init = init; - info->data = data; + struct subprocess_info *sub_info; + sub_info = kzalloc(sizeof(struct subprocess_info), gfp_mask); + if (!sub_info) + goto out; + + INIT_WORK(&sub_info->work, __call_usermodehelper); + sub_info->path = path; + sub_info->argv = argv; + sub_info->envp = envp; + + sub_info->cleanup = cleanup; + sub_info->init = init; + sub_info->data = data; + out: + return sub_info; } +EXPORT_SYMBOL(call_usermodehelper_setup); /** * call_usermodehelper_exec - start a usermode application @@ -563,7 +555,6 @@ void call_usermodehelper_setfns(struct subprocess_info *info, * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ -static int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) { DECLARE_COMPLETION_ONSTACK(done); @@ -615,6 +606,7 @@ unlock: helper_unlock(); return retval; } +EXPORT_SYMBOL(call_usermodehelper_exec); /* * call_usermodehelper_fns() will not run the caller-provided cleanup function @@ -630,13 +622,12 @@ int call_usermodehelper_fns( struct subprocess_info *info; gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - info = call_usermodehelper_setup(path, argv, envp, gfp_mask); + info = call_usermodehelper_setup(path, argv, envp, gfp_mask, + init, cleanup, data); if (info == NULL) return -ENOMEM; - call_usermodehelper_setfns(info, init, cleanup, data); - return call_usermodehelper_exec(info, wait); } EXPORT_SYMBOL(call_usermodehelper_fns);