Files
laptops-kernel/include/linux/fs_struct.h
Christian Brauner 9a8e296958 fs: make userspace_init_fs a dynamically-initialized pointer
Change userspace_init_fs from a declared-but-unused extern struct to
a dynamically initialized pointer. Add init_userspace_fs() which is
called early in kernel_init() (PID 1) to record PID 1's fs_struct
as the canonical userspace filesystem state.

Wire up __override_init_fs() and __revert_init_fs() to actually swap
current->fs to/from userspace_init_fs. Previously these were no-ops
that stored current->fs back to itself.

Fix nullfs_userspace_init() to compare against userspace_init_fs
instead of &init_fs. When PID 1 unshares its filesystem state, revert
userspace_init_fs to init_fs's root (nullfs) so that stale filesystem
state is not silently inherited by kworkers and usermodehelpers.

At this stage PID 1's fs still points to rootfs (set by
init_mount_tree), so userspace_init_fs points to rootfs and
scoped_with_init_fs() is functionally equivalent to its previous no-op
behavior.

Link: https://patch.msgid.link/20260601-work-kthread-nullfs-v4-5-77ee053060e0@kernel.org
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-06-29 10:44:05 +02:00

85 lines
2.0 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_FS_STRUCT_H
#define _LINUX_FS_STRUCT_H
#include <linux/sched.h>
#include <linux/path.h>
#include <linux/spinlock.h>
#include <linux/seqlock.h>
#include <linux/vfsdebug.h>
struct fs_struct {
int users;
seqlock_t seq;
int umask;
int in_exec;
struct path root, pwd;
} __randomize_layout;
extern struct kmem_cache *fs_cachep;
extern struct fs_struct *userspace_init_fs;
extern void exit_fs(struct task_struct *);
extern void set_fs_root(struct fs_struct *, const struct path *);
extern void set_fs_pwd(struct fs_struct *, const struct path *);
extern struct fs_struct *copy_fs_struct(struct fs_struct *);
extern void free_fs_struct(struct fs_struct *);
extern int unshare_fs_struct(void);
static inline void get_fs_root(struct fs_struct *fs, struct path *root)
{
read_seqlock_excl(&fs->seq);
*root = fs->root;
path_get(root);
read_sequnlock_excl(&fs->seq);
}
static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd)
{
read_seqlock_excl(&fs->seq);
*pwd = fs->pwd;
path_get(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)
{
return current->fs->umask;
}
/*
* Temporarily use userspace_init_fs for path resolution in kthreads.
* Callers should use scoped_with_init_fs() which automatically
* restores the original fs_struct at scope exit.
*/
static inline struct fs_struct *__override_init_fs(void)
{
struct fs_struct *old_fs;
old_fs = current->fs;
WRITE_ONCE(current->fs, userspace_init_fs);
return old_fs;
}
static inline void __revert_init_fs(struct fs_struct *old_fs)
{
VFS_WARN_ON_ONCE(current->fs != userspace_init_fs);
WRITE_ONCE(current->fs, old_fs);
}
DEFINE_CLASS(__override_init_fs,
struct fs_struct *,
__revert_init_fs(_T),
__override_init_fs(), void)
#define scoped_with_init_fs() \
scoped_class(__override_init_fs, __UNIQUE_ID(label))
void __init init_userspace_fs(void);
#endif /* _LINUX_FS_STRUCT_H */