Add switch_files_struct() to install another table on a task. It consumes the reference to the new table and hands the old one back for the caller to put. Convert every place that switches a descriptor table. No functional changes. Signed-off-by: Christian Brauner (Amutable) --- fs/file.c | 28 ++++++++++++++++------------ include/linux/fdtable.h | 2 ++ kernel/fork.c | 6 ++---- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/fs/file.c b/fs/file.c index 88ede1262b3e..aec2adf59031 100644 --- a/fs/file.c +++ b/fs/file.c @@ -515,16 +515,23 @@ void put_files_struct(struct files_struct *files) } } -void exit_files(struct task_struct *tsk) +/* Install @files on @tsk, consuming the reference; returns the old table. */ +struct files_struct *switch_files_struct(struct task_struct *tsk, + struct files_struct *files) { - struct files_struct * files = tsk->files; + struct files_struct *old; - if (files) { - task_lock(tsk); - tsk->files = NULL; - task_unlock(tsk); - put_files_struct(files); - } + task_lock(tsk); + old = tsk->files; + tsk->files = files; + task_unlock(tsk); + return old; +} + +void exit_files(struct task_struct *tsk) +{ + if (tsk->files) + put_files_struct(switch_files_struct(tsk, NULL)); } struct files_struct init_files = { @@ -855,10 +862,7 @@ SYSCALL_DEFINE3(close_range, unsigned int, fd, unsigned int, max_fd, * We're done closing the files we were supposed to. Time to install * the new file descriptor table and drop the old one. */ - task_lock(me); - me->files = cur_fds; - task_unlock(me); - put_files_struct(fds); + put_files_struct(switch_files_struct(me, cur_fds)); } return 0; diff --git a/include/linux/fdtable.h b/include/linux/fdtable.h index 050f4a04157e..80f192412a5a 100644 --- a/include/linux/fdtable.h +++ b/include/linux/fdtable.h @@ -100,6 +100,8 @@ static inline bool close_on_exec(unsigned int fd, const struct files_struct *fil struct task_struct; void put_files_struct(struct files_struct *fs); +struct files_struct *switch_files_struct(struct task_struct *tsk, + struct files_struct *files); int unshare_files(void); struct fd_range { unsigned int from, to; diff --git a/kernel/fork.c b/kernel/fork.c index 416758c8a3d4..b959191edf52 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -3320,10 +3320,8 @@ int ksys_unshare(unsigned long unshare_flags) if (new_fs) new_fs = switch_fs_struct(new_fs); - if (new_fd) { - guard(task_lock)(current); - swap(current->files, new_fd); - } + if (new_fd) + new_fd = switch_files_struct(current, new_fd); if (new_cred) { /* Install the new user namespace */ -- 2.53.0