[PATCH RFC v3 01/26] fs: add switch_fs_struct()

Christian Brauner
2026-03-11T21:56:14Z

Don't open-code the guts of replacing current's fs struct.

Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/fs_struct.c | 18 ++++++++++++++++++
include/linux/fs_struct.h | 2 ++
kernel/fork.c | 22 ++++++----------------
3 files changed, 26 insertions(+), 16 deletions(-)

diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 394875d06fd6..c441586537e7 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -147,6 +147,24 @@ int unshare_fs_struct(void)
}
EXPORT_SYMBOL_GPL(unshare_fs_struct);

+struct fs_struct *switch_fs_struct(struct fs_struct *new_fs)
+{
+ struct fs_struct *fs;
+
+ scoped_guard(task_lock, current) {
+ fs = current->fs;
+ read_seqlock_excl(&fs->seq);
+ current->fs = new_fs;
+ if (--fs->users)
+ new_fs = NULL;
+ else
+ new_fs = fs;
+ read_sequnlock_excl(&fs->seq);
+ }
+
+ return new_fs;
+}
+
/* to be mentioned only in INIT_TASK */
struct fs_struct init_fs = {
.users = 1,
diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h
index 0070764b790a..ade459383f92 100644
--- a/include/linux/fs_struct.h
+++ b/include/linux/fs_struct.h
@@ -40,6 +40,8 @@ static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd)
read_sequnlock_excl(&fs->seq);
}

+struct fs_struct *switch_fs_struct(struct fs_struct *new_fs);
+
extern bool current_chrooted(void);

static inline int current_umask(void)
diff --git a/kernel/fork.c b/kernel/fork.c
index 65113a304518..67e57ee44548 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -3123,7 +3123,7 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp
*/
int ksys_unshare(unsigned long unshare_flags)
{
- struct fs_struct *fs, *new_fs = NULL;
+ struct fs_struct *new_fs = NULL;
struct files_struct *new_fd = NULL;
struct cred *new_cred = NULL;
struct nsproxy *new_nsproxy = NULL;
@@ -3198,23 +3198,13 @@ int ksys_unshare(unsigned long unshare_flags)
if (new_nsproxy)
switch_task_namespaces(current, new_nsproxy);

- task_lock(current);
+ if (new_fs)
+ new_fs = switch_fs_struct(new_fs);

- if (new_fs) {
- fs = current->fs;
- read_seqlock_excl(&fs->seq);
- current->fs = new_fs;
- if (--fs->users)
- new_fs = NULL;
- else
- new_fs = fs;
- read_sequnlock_excl(&fs->seq);
- }
-
- if (new_fd)
+ if (new_fd) {
+ guard(task_lock)(current);
swap(current->files, new_fd);
-
- task_unlock(current);
+ }

if (new_cred) {
/* Install the new user namespace */

--
2.47.3