diff --git a/fs/exec.c b/fs/exec.c index 475543002f13..b152029f18f6 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1269,19 +1269,13 @@ int do_execve(char * filename, struct linux_binprm *bprm; struct file *file; unsigned long env_p; - struct files_struct *files; + struct files_struct *displaced; int retval; - files = current->files; - retval = unshare_files(); + retval = unshare_files(&displaced); if (retval) goto out_ret; - if (files == current->files) { - put_files_struct(files); - files = NULL; - } - retval = -ENOMEM; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) @@ -1340,8 +1334,8 @@ int do_execve(char * filename, security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); - if (files) - put_files_struct(files); + if (displaced) + put_files_struct(displaced); return retval; } @@ -1363,8 +1357,8 @@ out_kfree: kfree(bprm); out_files: - if (files) - reset_files_struct(current, files); + if (displaced) + reset_files_struct(displaced); out_ret: return retval; } diff --git a/include/linux/file.h b/include/linux/file.h index 653477021e4c..69baf5a4f0a5 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -117,7 +117,8 @@ struct task_struct; struct files_struct *get_files_struct(struct task_struct *); void put_files_struct(struct files_struct *fs); -void reset_files_struct(struct task_struct *, struct files_struct *); +void reset_files_struct(struct files_struct *); +int unshare_files(struct files_struct **); extern struct kmem_cache *files_cachep; diff --git a/include/linux/fs.h b/include/linux/fs.h index ad41d0bbcb4d..e057438a05ad 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2033,9 +2033,6 @@ static inline ino_t parent_ino(struct dentry *dentry) return res; } -/* kernel/fork.c */ -extern int unshare_files(void); - /* Transaction based IO helpers */ /* diff --git a/kernel/exit.c b/kernel/exit.c index 3d320003cc03..97f609f574b1 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -507,8 +507,9 @@ void put_files_struct(struct files_struct *files) } } -void reset_files_struct(struct task_struct *tsk, struct files_struct *files) +void reset_files_struct(struct files_struct *files) { + struct task_struct *tsk = current; struct files_struct *old; old = tsk->files; diff --git a/kernel/fork.c b/kernel/fork.c index 2fc11f2e2b21..efb618fc8ffe 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -840,36 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk) return 0; } -/* - * Helper to unshare the files of the current task. - * We don't want to expose copy_files internals to - * the exec layer of the kernel. - */ - -int unshare_files(void) -{ - struct files_struct *files = current->files; - struct files_struct *newf; - int error = 0; - - BUG_ON(!files); - - /* This can race but the race causes us to copy when we don't - need to and drop the copy */ - if(atomic_read(&files->count) == 1) - { - atomic_inc(&files->count); - return 0; - } - newf = dup_fd(files, &error); - if (newf) { - task_lock(current); - current->files = newf; - task_unlock(current); - } - return error; -} - static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig; @@ -1807,3 +1777,27 @@ bad_unshare_cleanup_thread: bad_unshare_out: return err; } + +/* + * Helper to unshare the files of the current task. + * We don't want to expose copy_files internals to + * the exec layer of the kernel. + */ + +int unshare_files(struct files_struct **displaced) +{ + struct task_struct *task = current; + struct files_struct *copy; + int error; + + error = unshare_fd(CLONE_FILES, ©); + if (error || !copy) { + *displaced = NULL; + return error; + } + *displaced = task->files; + task_lock(task); + task->files = copy; + task_unlock(task); + return 0; +}