* [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
@ 2026-01-12 15:47 ` Christian Brauner
2026-01-12 19:37 ` Jeff Layton
2026-01-12 15:47 ` [PATCH v2 2/4] fs: add init_pivot_root() Christian Brauner
` (8 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Christian Brauner @ 2026-01-12 15:47 UTC (permalink / raw)
To: linux-fsdevel
Cc: Alexander Viro, Jan Kara, Jeff Layton, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik,
Christian Brauner, stable
and the rootfs get mount id one as it always has. Before we actually
mount the rootfs we create an internal tmpfs mount which has mount id
zero but is never exposed anywhere. Continue that "tradition".
Fixes: 7f9bfafc5f49 ("fs: use xarray for old mount id")
Cc: <stable@vger.kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/namespace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index c58674a20cad..8b082b1de7f3 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -221,7 +221,7 @@ static int mnt_alloc_id(struct mount *mnt)
int res;
xa_lock(&mnt_id_xa);
- res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, XA_LIMIT(1, INT_MAX), GFP_KERNEL);
+ res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, xa_limit_31b, GFP_KERNEL);
if (!res)
mnt->mnt_id_unique = ++mnt_id_ctr;
xa_unlock(&mnt_id_xa);
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero
2026-01-12 15:47 ` [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero Christian Brauner
@ 2026-01-12 19:37 ` Jeff Layton
0 siblings, 0 replies; 17+ messages in thread
From: Jeff Layton @ 2026-01-12 19:37 UTC (permalink / raw)
To: Christian Brauner, linux-fsdevel
Cc: Alexander Viro, Jan Kara, Amir Goldstein, Lennart Poettering,
Zbigniew Jędrzejewski-Szmek, Josef Bacik, stable
On Mon, 2026-01-12 at 16:47 +0100, Christian Brauner wrote:
> and the rootfs get mount id one as it always has. Before we actually
> mount the rootfs we create an internal tmpfs mount which has mount id
> zero but is never exposed anywhere. Continue that "tradition".
>
> Fixes: 7f9bfafc5f49 ("fs: use xarray for old mount id")
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> fs/namespace.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/namespace.c b/fs/namespace.c
> index c58674a20cad..8b082b1de7f3 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -221,7 +221,7 @@ static int mnt_alloc_id(struct mount *mnt)
> int res;
>
> xa_lock(&mnt_id_xa);
> - res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, XA_LIMIT(1, INT_MAX), GFP_KERNEL);
> + res = __xa_alloc(&mnt_id_xa, &mnt->mnt_id, mnt, xa_limit_31b, GFP_KERNEL);
> if (!res)
> mnt->mnt_id_unique = ++mnt_id_ctr;
> xa_unlock(&mnt_id_xa);
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 2/4] fs: add init_pivot_root()
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
2026-01-12 15:47 ` [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero Christian Brauner
@ 2026-01-12 15:47 ` Christian Brauner
2026-01-12 15:47 ` [PATCH v2 3/4] fs: add immutable rootfs Christian Brauner
` (7 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Christian Brauner @ 2026-01-12 15:47 UTC (permalink / raw)
To: linux-fsdevel
Cc: Alexander Viro, Jan Kara, Jeff Layton, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik,
Christian Brauner
We will soon be able to pivot_root() with the introduction of the
immutable rootfs. Add a wrapper for kernel internal usage.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/init.c | 17 +++++++
fs/internal.h | 1 +
fs/namespace.c | 101 ++++++++++++++++++++++--------------------
include/linux/init_syscalls.h | 1 +
4 files changed, 73 insertions(+), 47 deletions(-)
diff --git a/fs/init.c b/fs/init.c
index e0f5429c0a49..e33b2690d851 100644
--- a/fs/init.c
+++ b/fs/init.c
@@ -13,6 +13,23 @@
#include <linux/security.h>
#include "internal.h"
+int __init init_pivot_root(const char *new_root, const char *put_old)
+{
+ struct path new_path __free(path_put) = {};
+ struct path old_path __free(path_put) = {};
+ int ret;
+
+ ret = kern_path(new_root, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new_path);
+ if (ret)
+ return ret;
+
+ ret = kern_path(put_old, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old_path);
+ if (ret)
+ return ret;
+
+ return path_pivot_root(&new_path, &old_path);
+}
+
int __init init_mount(const char *dev_name, const char *dir_name,
const char *type_page, unsigned long flags, void *data_page)
{
diff --git a/fs/internal.h b/fs/internal.h
index ab638d41ab81..4b27a4b0fdef 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -90,6 +90,7 @@ extern bool may_mount(void);
int path_mount(const char *dev_name, const struct path *path,
const char *type_page, unsigned long flags, void *data_page);
int path_umount(const struct path *path, int flags);
+int path_pivot_root(struct path *new, struct path *old);
int show_path(struct seq_file *m, struct dentry *root);
diff --git a/fs/namespace.c b/fs/namespace.c
index 8b082b1de7f3..9261f56ccc81 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4498,36 +4498,8 @@ bool path_is_under(const struct path *path1, const struct path *path2)
}
EXPORT_SYMBOL(path_is_under);
-/*
- * pivot_root Semantics:
- * Moves the root file system of the current process to the directory put_old,
- * makes new_root as the new root file system of the current process, and sets
- * root/cwd of all processes which had them on the current root to new_root.
- *
- * Restrictions:
- * The new_root and put_old must be directories, and must not be on the
- * same file system as the current process root. The put_old must be
- * underneath new_root, i.e. adding a non-zero number of /.. to the string
- * pointed to by put_old must yield the same directory as new_root. No other
- * file system may be mounted on put_old. After all, new_root is a mountpoint.
- *
- * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
- * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
- * in this situation.
- *
- * Notes:
- * - we don't move root/cwd if they are not at the root (reason: if something
- * cared enough to change them, it's probably wrong to force them elsewhere)
- * - it's okay to pick a root that isn't the root of a file system, e.g.
- * /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
- * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
- * first.
- */
-SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
- const char __user *, put_old)
+int path_pivot_root(struct path *new, struct path *old)
{
- struct path new __free(path_put) = {};
- struct path old __free(path_put) = {};
struct path root __free(path_put) = {};
struct mount *new_mnt, *root_mnt, *old_mnt, *root_parent, *ex_parent;
int error;
@@ -4535,28 +4507,18 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
if (!may_mount())
return -EPERM;
- error = user_path_at(AT_FDCWD, new_root,
- LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
- if (error)
- return error;
-
- error = user_path_at(AT_FDCWD, put_old,
- LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
- if (error)
- return error;
-
- error = security_sb_pivotroot(&old, &new);
+ error = security_sb_pivotroot(old, new);
if (error)
return error;
get_fs_root(current->fs, &root);
- LOCK_MOUNT(old_mp, &old);
+ LOCK_MOUNT(old_mp, old);
old_mnt = old_mp.parent;
if (IS_ERR(old_mnt))
return PTR_ERR(old_mnt);
- new_mnt = real_mount(new.mnt);
+ new_mnt = real_mount(new->mnt);
root_mnt = real_mount(root.mnt);
ex_parent = new_mnt->mnt_parent;
root_parent = root_mnt->mnt_parent;
@@ -4568,7 +4530,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
return -EINVAL;
if (new_mnt->mnt.mnt_flags & MNT_LOCKED)
return -EINVAL;
- if (d_unlinked(new.dentry))
+ if (d_unlinked(new->dentry))
return -ENOENT;
if (new_mnt == root_mnt || old_mnt == root_mnt)
return -EBUSY; /* loop, on the same file system */
@@ -4576,15 +4538,15 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
return -EINVAL; /* not a mountpoint */
if (!mnt_has_parent(root_mnt))
return -EINVAL; /* absolute root */
- if (!path_mounted(&new))
+ if (!path_mounted(new))
return -EINVAL; /* not a mountpoint */
if (!mnt_has_parent(new_mnt))
return -EINVAL; /* absolute root */
/* make sure we can reach put_old from new_root */
- if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, &new))
+ if (!is_path_reachable(old_mnt, old_mp.mp->m_dentry, new))
return -EINVAL;
/* make certain new is below the root */
- if (!is_path_reachable(new_mnt, new.dentry, &root))
+ if (!is_path_reachable(new_mnt, new->dentry, &root))
return -EINVAL;
lock_mount_hash();
umount_mnt(new_mnt);
@@ -4603,10 +4565,55 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
unlock_mount_hash();
mnt_notify_add(root_mnt);
mnt_notify_add(new_mnt);
- chroot_fs_refs(&root, &new);
+ chroot_fs_refs(&root, new);
return 0;
}
+/*
+ * pivot_root Semantics:
+ * Moves the root file system of the current process to the directory put_old,
+ * makes new_root as the new root file system of the current process, and sets
+ * root/cwd of all processes which had them on the current root to new_root.
+ *
+ * Restrictions:
+ * The new_root and put_old must be directories, and must not be on the
+ * same file system as the current process root. The put_old must be
+ * underneath new_root, i.e. adding a non-zero number of /.. to the string
+ * pointed to by put_old must yield the same directory as new_root. No other
+ * file system may be mounted on put_old. After all, new_root is a mountpoint.
+ *
+ * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
+ * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
+ * in this situation.
+ *
+ * Notes:
+ * - we don't move root/cwd if they are not at the root (reason: if something
+ * cared enough to change them, it's probably wrong to force them elsewhere)
+ * - it's okay to pick a root that isn't the root of a file system, e.g.
+ * /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
+ * though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
+ * first.
+ */
+SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
+ const char __user *, put_old)
+{
+ struct path new __free(path_put) = {};
+ struct path old __free(path_put) = {};
+ int error;
+
+ error = user_path_at(AT_FDCWD, new_root,
+ LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &new);
+ if (error)
+ return error;
+
+ error = user_path_at(AT_FDCWD, put_old,
+ LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &old);
+ if (error)
+ return error;
+
+ return path_pivot_root(&new, &old);
+}
+
static unsigned int recalc_flags(struct mount_kattr *kattr, struct mount *mnt)
{
unsigned int flags = mnt->mnt.mnt_flags;
diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h
index 92045d18cbfc..28776ee28d8e 100644
--- a/include/linux/init_syscalls.h
+++ b/include/linux/init_syscalls.h
@@ -17,3 +17,4 @@ int __init init_mkdir(const char *pathname, umode_t mode);
int __init init_rmdir(const char *pathname);
int __init init_utimes(char *filename, struct timespec64 *ts);
int __init init_dup(struct file *file);
+int __init init_pivot_root(const char *new_root, const char *put_old);
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v2 3/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
2026-01-12 15:47 ` [PATCH v2 1/4] fs: ensure that internal tmpfs mount gets mount id zero Christian Brauner
2026-01-12 15:47 ` [PATCH v2 2/4] fs: add init_pivot_root() Christian Brauner
@ 2026-01-12 15:47 ` Christian Brauner
2026-01-12 15:47 ` [PATCH v2 4/4] docs: mention nullfs Christian Brauner
` (6 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Christian Brauner @ 2026-01-12 15:47 UTC (permalink / raw)
To: linux-fsdevel
Cc: Alexander Viro, Jan Kara, Jeff Layton, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik,
Christian Brauner
Currently pivot_root() doesn't work on the real rootfs because it
cannot be unmounted. Userspace has to do a recursive removal of the
initramfs contents manually before continuing the boot.
Really all we want from the real rootfs is to serve as the parent mount
for anything that is actually useful such as the tmpfs or ramfs for
initramfs unpacking or the rootfs itself. There's no need for the real
rootfs to actually be anything meaningful or useful. Add a immutable
rootfs called "nullfs" that can be selected via the "nullfs_rootfs"
kernel command line option.
The kernel will mount a tmpfs/ramfs on top of it, unpack the initramfs
and fire up userspace which mounts the rootfs and can then just do:
chdir(rootfs);
pivot_root(".", ".");
umount2(".", MNT_DETACH);
and be done with it. (Ofc, userspace can also choose to retain the
initramfs contents by using something like pivot_root(".", "/initramfs")
without unmounting it.)
Technically this also means that the rootfs mount in unprivileged
namespaces doesn't need to become MNT_LOCKED anymore as it's guaranteed
that the immutable rootfs remains permanently empty so there cannot be
anything revealed by unmounting the covering mount.
In the future this will also allow us to create completely empty mount
namespaces without risking to leak anything.
systemd already handles this all correctly as it tries to pivot_root()
first and falls back to MS_MOVE only when that fails.
This goes back to various discussion in previous years and a LPC 2024
presentation about this very topic.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/Makefile | 2 +-
fs/mount.h | 1 +
fs/namespace.c | 82 +++++++++++++++++++++++++++++++++++++++-------
fs/nullfs.c | 70 +++++++++++++++++++++++++++++++++++++++
include/uapi/linux/magic.h | 1 +
init/do_mounts.c | 14 ++++++++
init/do_mounts.h | 1 +
7 files changed, 159 insertions(+), 12 deletions(-)
diff --git a/fs/Makefile b/fs/Makefile
index a04274a3c854..becf133e4791 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -16,7 +16,7 @@ obj-y := open.o read_write.o file_table.o super.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
fs_dirent.o fs_context.o fs_parser.o fsopen.o init.o \
kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
- file_attr.o
+ file_attr.o nullfs.o
obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
obj-$(CONFIG_PROC_FS) += proc_namespace.o
diff --git a/fs/mount.h b/fs/mount.h
index 2d28ef2a3aed..e0816c11a198 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -5,6 +5,7 @@
#include <linux/ns_common.h>
#include <linux/fs_pin.h>
+extern struct file_system_type nullfs_fs_type;
extern struct list_head notify_list;
struct mnt_namespace {
diff --git a/fs/namespace.c b/fs/namespace.c
index 9261f56ccc81..a44ebb2f1161 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -75,6 +75,17 @@ static int __init initramfs_options_setup(char *str)
__setup("initramfs_options=", initramfs_options_setup);
+bool nullfs_rootfs = false;
+
+static int __init nullfs_rootfs_setup(char *str)
+{
+ if (*str)
+ return 0;
+ nullfs_rootfs = true;
+ return 1;
+}
+__setup("nullfs_rootfs", nullfs_rootfs_setup);
+
static u64 event;
static DEFINE_XARRAY_FLAGS(mnt_id_xa, XA_FLAGS_ALLOC);
static DEFINE_IDA(mnt_group_ida);
@@ -4582,8 +4593,9 @@ int path_pivot_root(struct path *new, struct path *old)
* pointed to by put_old must yield the same directory as new_root. No other
* file system may be mounted on put_old. After all, new_root is a mountpoint.
*
- * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
- * See Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
+ * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem
+ * unless the kernel was booted with "nullfs_rootfs". See
+ * Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
* in this situation.
*
* Notes:
@@ -5976,24 +5988,72 @@ struct mnt_namespace init_mnt_ns = {
static void __init init_mount_tree(void)
{
- struct vfsmount *mnt;
- struct mount *m;
+ struct vfsmount *mnt, *nullfs_mnt;
+ struct mount *mnt_root;
struct path root;
+ /*
+ * When nullfs is used, we create two mounts:
+ *
+ * (1) nullfs with mount id 1
+ * (2) mutable rootfs with mount id 2
+ *
+ * with (2) mounted on top of (1).
+ */
+ if (nullfs_rootfs) {
+ nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL);
+ if (IS_ERR(nullfs_mnt))
+ panic("VFS: Failed to create nullfs");
+ }
+
mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options);
if (IS_ERR(mnt))
panic("Can't create rootfs");
- m = real_mount(mnt);
- init_mnt_ns.root = m;
- init_mnt_ns.nr_mounts = 1;
- mnt_add_to_ns(&init_mnt_ns, m);
+ if (nullfs_rootfs) {
+ VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1);
+ VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2);
+
+ /* The namespace root is the nullfs mnt. */
+ mnt_root = real_mount(nullfs_mnt);
+ init_mnt_ns.root = mnt_root;
+
+ /* Mount mutable rootfs on top of nullfs. */
+ root.mnt = nullfs_mnt;
+ root.dentry = nullfs_mnt->mnt_root;
+
+ LOCK_MOUNT_EXACT(mp, &root);
+ if (unlikely(IS_ERR(mp.parent)))
+ panic("VFS: Failed to mount rootfs on nullfs");
+ scoped_guard(mount_writer)
+ attach_mnt(real_mount(mnt), mp.parent, mp.mp);
+
+ pr_info("VFS: Finished mounting rootfs on nullfs\n");
+ } else {
+ VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 1);
+
+ /* The namespace root is the mutable rootfs. */
+ mnt_root = real_mount(mnt);
+ init_mnt_ns.root = mnt_root;
+ }
+
+ /*
+ * We've dropped all locks here but that's fine. Not just are we
+ * the only task that's running, there's no other mount
+ * namespace in existence and the initial mount namespace is
+ * completely empty until we add the mounts we just created.
+ */
+ for (struct mount *p = mnt_root; p; p = next_mnt(p, mnt_root)) {
+ mnt_add_to_ns(&init_mnt_ns, p);
+ init_mnt_ns.nr_mounts++;
+ }
+
init_task.nsproxy->mnt_ns = &init_mnt_ns;
get_mnt_ns(&init_mnt_ns);
- root.mnt = mnt;
- root.dentry = mnt->mnt_root;
-
+ /* The root and pwd always point to the mutable rootfs. */
+ root.mnt = mnt;
+ root.dentry = mnt->mnt_root;
set_fs_pwd(current->fs, &root);
set_fs_root(current->fs, &root);
diff --git a/fs/nullfs.c b/fs/nullfs.c
new file mode 100644
index 000000000000..fdbd3e5d3d71
--- /dev/null
+++ b/fs/nullfs.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Christian Brauner <brauner@kernel.org> */
+#include <linux/fs/super_types.h>
+#include <linux/fs_context.h>
+#include <linux/magic.h>
+
+static const struct super_operations nullfs_super_operations = {
+ .statfs = simple_statfs,
+};
+
+static int nullfs_fs_fill_super(struct super_block *s, struct fs_context *fc)
+{
+ struct inode *inode;
+
+ s->s_maxbytes = MAX_LFS_FILESIZE;
+ s->s_blocksize = PAGE_SIZE;
+ s->s_blocksize_bits = PAGE_SHIFT;
+ s->s_magic = NULL_FS_MAGIC;
+ s->s_op = &nullfs_super_operations;
+ s->s_export_op = NULL;
+ s->s_xattr = NULL;
+ s->s_time_gran = 1;
+ s->s_d_flags = 0;
+
+ inode = new_inode(s);
+ if (!inode)
+ return -ENOMEM;
+
+ /* nullfs is permanently empty... */
+ make_empty_dir_inode(inode);
+ simple_inode_init_ts(inode);
+ inode->i_ino = 1;
+ /* ... and immutable. */
+ inode->i_flags |= S_IMMUTABLE;
+
+ s->s_root = d_make_root(inode);
+ if (!s->s_root)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/*
+ * For now this is a single global instance. If needed we can make it
+ * mountable by userspace at which point we will need to make it
+ * multi-instance.
+ */
+static int nullfs_fs_get_tree(struct fs_context *fc)
+{
+ return get_tree_single(fc, nullfs_fs_fill_super);
+}
+
+static const struct fs_context_operations nullfs_fs_context_ops = {
+ .get_tree = nullfs_fs_get_tree,
+};
+
+static int nullfs_init_fs_context(struct fs_context *fc)
+{
+ fc->ops = &nullfs_fs_context_ops;
+ fc->global = true;
+ fc->sb_flags = SB_NOUSER;
+ fc->s_iflags = SB_I_NOEXEC | SB_I_NODEV;
+ return 0;
+}
+
+struct file_system_type nullfs_fs_type = {
+ .name = "nullfs",
+ .init_fs_context = nullfs_init_fs_context,
+ .kill_sb = kill_anon_super,
+};
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 638ca21b7a90..4f2da935a76c 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -104,5 +104,6 @@
#define SECRETMEM_MAGIC 0x5345434d /* "SECM" */
#define PID_FS_MAGIC 0x50494446 /* "PIDF" */
#define GUEST_MEMFD_MAGIC 0x474d454d /* "GMEM" */
+#define NULL_FS_MAGIC 0x4E554C4C /* "NULL" */
#endif /* __LINUX_MAGIC_H__ */
diff --git a/init/do_mounts.c b/init/do_mounts.c
index defbbf1d55f7..675397c8a7a4 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -492,6 +492,20 @@ void __init prepare_namespace(void)
mount_root(saved_root_name);
out:
devtmpfs_mount();
+
+ if (nullfs_rootfs) {
+ if (init_pivot_root(".", ".")) {
+ pr_err("VFS: Failed to pivot into new rootfs\n");
+ return;
+ }
+ if (init_umount(".", MNT_DETACH)) {
+ pr_err("VFS: Failed to unmount old rootfs\n");
+ return;
+ }
+ pr_info("VFS: Pivoted into new rootfs\n");
+ return;
+ }
+
init_mount(".", "/", NULL, MS_MOVE, NULL);
init_chroot(".");
}
diff --git a/init/do_mounts.h b/init/do_mounts.h
index 6069ea3eb80d..fbfee810aa89 100644
--- a/init/do_mounts.h
+++ b/init/do_mounts.h
@@ -15,6 +15,7 @@
void mount_root_generic(char *name, char *pretty_name, int flags);
void mount_root(char *root_device_name);
extern int root_mountflags;
+extern bool nullfs_rootfs;
static inline __init int create_dev(char *name, dev_t dev)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread* [PATCH v2 4/4] docs: mention nullfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (2 preceding siblings ...)
2026-01-12 15:47 ` [PATCH v2 3/4] fs: add immutable rootfs Christian Brauner
@ 2026-01-12 15:47 ` Christian Brauner
2026-01-13 14:17 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
` (5 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Christian Brauner @ 2026-01-12 15:47 UTC (permalink / raw)
To: linux-fsdevel
Cc: Alexander Viro, Jan Kara, Jeff Layton, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik,
Christian Brauner
Add a section about nullfs and how it enables pivot_root() to work.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
.../filesystems/ramfs-rootfs-initramfs.rst | 32 ++++++++++++++++------
1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
index a9d271e171c3..a8899f849e90 100644
--- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst
+++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
@@ -76,10 +76,15 @@ What is rootfs?
---------------
Rootfs is a special instance of ramfs (or tmpfs, if that's enabled), which is
-always present in 2.6 systems. You can't unmount rootfs for approximately the
-same reason you can't kill the init process; rather than having special code
-to check for and handle an empty list, it's smaller and simpler for the kernel
-to just make sure certain lists can't become empty.
+always present in 2.6 systems. Traditionally, you can't unmount rootfs for
+approximately the same reason you can't kill the init process; rather than
+having special code to check for and handle an empty list, it's smaller and
+simpler for the kernel to just make sure certain lists can't become empty.
+
+However, if the kernel is booted with "nullfs_rootfs", an immutable empty
+filesystem called nullfs is used as the true root, with the mutable rootfs
+(tmpfs/ramfs) mounted on top of it. This allows pivot_root() and unmounting
+of the initramfs to work normally.
Most systems just mount another filesystem over rootfs and ignore it. The
amount of space an empty instance of ramfs takes up is tiny.
@@ -121,17 +126,26 @@ All this differs from the old initrd in several ways:
program. See the switch_root utility, below.)
- When switching another root device, initrd would pivot_root and then
- umount the ramdisk. But initramfs is rootfs: you can neither pivot_root
- rootfs, nor unmount it. Instead delete everything out of rootfs to
- free up the space (find -xdev / -exec rm '{}' ';'), overmount rootfs
- with the new root (cd /newmount; mount --move . /; chroot .), attach
- stdin/stdout/stderr to the new /dev/console, and exec the new init.
+ umount the ramdisk. Traditionally, initramfs is rootfs: you can neither
+ pivot_root rootfs, nor unmount it. Instead delete everything out of
+ rootfs to free up the space (find -xdev / -exec rm '{}' ';'), overmount
+ rootfs with the new root (cd /newmount; mount --move . /; chroot .),
+ attach stdin/stdout/stderr to the new /dev/console, and exec the new init.
Since this is a remarkably persnickety process (and involves deleting
commands before you can run them), the klibc package introduced a helper
program (utils/run_init.c) to do all this for you. Most other packages
(such as busybox) have named this command "switch_root".
+ However, if the kernel is booted with "nullfs_rootfs", pivot_root() works
+ normally from the initramfs. Userspace can simply do::
+
+ chdir(new_root);
+ pivot_root(".", ".");
+ umount2(".", MNT_DETACH);
+
+ This is the preferred method when nullfs_rootfs is enabled.
+
Populating initramfs:
---------------------
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (3 preceding siblings ...)
2026-01-12 15:47 ` [PATCH v2 4/4] docs: mention nullfs Christian Brauner
@ 2026-01-13 14:17 ` Jeff Layton
2026-01-14 8:58 ` Christian Brauner
2026-01-14 11:58 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
` (4 subsequent siblings)
9 siblings, 1 reply; 17+ messages in thread
From: Jeff Layton @ 2026-01-13 14:17 UTC (permalink / raw)
To: Christian Brauner, linux-fsdevel
Cc: Alexander Viro, Jan Kara, Amir Goldstein, Lennart Poettering,
Zbigniew Jędrzejewski-Szmek, Josef Bacik, stable
On Mon, 2026-01-12 at 16:47 +0100, Christian Brauner wrote:
> Currently pivot_root() doesn't work on the real rootfs because it
> cannot be unmounted. Userspace has to do a recursive removal of the
> initramfs contents manually before continuing the boot.
>
> Really all we want from the real rootfs is to serve as the parent mount
> for anything that is actually useful such as the tmpfs or ramfs for
> initramfs unpacking or the rootfs itself. There's no need for the real
> rootfs to actually be anything meaningful or useful. Add a immutable
> rootfs called "nullfs" that can be selected via the "nullfs_rootfs"
> kernel command line option.
>
> The kernel will mount a tmpfs/ramfs on top of it, unpack the initramfs
> and fire up userspace which mounts the rootfs and can then just do:
>
> chdir(rootfs);
> pivot_root(".", ".");
> umount2(".", MNT_DETACH);
>
> and be done with it. (Ofc, userspace can also choose to retain the
> initramfs contents by using something like pivot_root(".", "/initramfs")
> without unmounting it.)
>
> Technically this also means that the rootfs mount in unprivileged
> namespaces doesn't need to become MNT_LOCKED anymore as it's guaranteed
> that the immutable rootfs remains permanently empty so there cannot be
> anything revealed by unmounting the covering mount.
>
> In the future this will also allow us to create completely empty mount
> namespaces without risking to leak anything.
>
> systemd already handles this all correctly as it tries to pivot_root()
> first and falls back to MS_MOVE only when that fails.
>
> This goes back to various discussion in previous years and a LPC 2024
> presentation about this very topic.
>
> Now in vfs-7.0.nullfs.
>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> Changes in v2:
> - Rename to "nullfs".
> - Update documentation.
> - Link to v1: https://patch.msgid.link/20260102-work-immutable-rootfs-v1-0-f2073b2d1602@kernel.org
>
> ---
> Christian Brauner (4):
> fs: ensure that internal tmpfs mount gets mount id zero
> fs: add init_pivot_root()
> fs: add immutable rootfs
> docs: mention nullfs
>
> .../filesystems/ramfs-rootfs-initramfs.rst | 32 +++-
> fs/Makefile | 2 +-
> fs/init.c | 17 ++
> fs/internal.h | 1 +
> fs/mount.h | 1 +
> fs/namespace.c | 181 ++++++++++++++-------
> fs/nullfs.c | 70 ++++++++
> include/linux/init_syscalls.h | 1 +
> include/uapi/linux/magic.h | 1 +
> init/do_mounts.c | 14 ++
> init/do_mounts.h | 1 +
> 11 files changed, 254 insertions(+), 67 deletions(-)
> ---
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> change-id: 20260102-work-immutable-rootfs-b5f23e0f5a27
I like the set overall. My only real gripe is the new command-line
option. Won't that make distro adoption hard?
IIUC, with this new method, you're just adding a new nullfs at the
bottom of the stack and then mounting the traditional ramfs as the
mutable rootfs on top of it.
Would there be any harm in just always doing this (and dropping the
command-line option)?
You would end up "leaking" both the nullfs and ramfs in the case of
traditional userspace that was unaware of the extra mount, but that
seems like it should be something we could live with.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-13 14:17 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
@ 2026-01-14 8:58 ` Christian Brauner
2026-01-14 10:32 ` [RFC PATCH 5/4] fs: use nullfs unconditionally as the real rootfs Christian Brauner
0 siblings, 1 reply; 17+ messages in thread
From: Christian Brauner @ 2026-01-14 8:58 UTC (permalink / raw)
To: Jeff Layton
Cc: linux-fsdevel, Alexander Viro, Jan Kara, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik,
stable
On Tue, Jan 13, 2026 at 09:17:05AM -0500, Jeff Layton wrote:
> On Mon, 2026-01-12 at 16:47 +0100, Christian Brauner wrote:
> > Currently pivot_root() doesn't work on the real rootfs because it
> > cannot be unmounted. Userspace has to do a recursive removal of the
> > initramfs contents manually before continuing the boot.
> >
> > Really all we want from the real rootfs is to serve as the parent mount
> > for anything that is actually useful such as the tmpfs or ramfs for
> > initramfs unpacking or the rootfs itself. There's no need for the real
> > rootfs to actually be anything meaningful or useful. Add a immutable
> > rootfs called "nullfs" that can be selected via the "nullfs_rootfs"
> > kernel command line option.
> >
> > The kernel will mount a tmpfs/ramfs on top of it, unpack the initramfs
> > and fire up userspace which mounts the rootfs and can then just do:
> >
> > chdir(rootfs);
> > pivot_root(".", ".");
> > umount2(".", MNT_DETACH);
> >
> > and be done with it. (Ofc, userspace can also choose to retain the
> > initramfs contents by using something like pivot_root(".", "/initramfs")
> > without unmounting it.)
> >
> > Technically this also means that the rootfs mount in unprivileged
> > namespaces doesn't need to become MNT_LOCKED anymore as it's guaranteed
> > that the immutable rootfs remains permanently empty so there cannot be
> > anything revealed by unmounting the covering mount.
> >
> > In the future this will also allow us to create completely empty mount
> > namespaces without risking to leak anything.
> >
> > systemd already handles this all correctly as it tries to pivot_root()
> > first and falls back to MS_MOVE only when that fails.
> >
> > This goes back to various discussion in previous years and a LPC 2024
> > presentation about this very topic.
> >
> > Now in vfs-7.0.nullfs.
> >
> > Signed-off-by: Christian Brauner <brauner@kernel.org>
> > ---
> > Changes in v2:
> > - Rename to "nullfs".
> > - Update documentation.
> > - Link to v1: https://patch.msgid.link/20260102-work-immutable-rootfs-v1-0-f2073b2d1602@kernel.org
> >
> > ---
> > Christian Brauner (4):
> > fs: ensure that internal tmpfs mount gets mount id zero
> > fs: add init_pivot_root()
> > fs: add immutable rootfs
> > docs: mention nullfs
> >
> > .../filesystems/ramfs-rootfs-initramfs.rst | 32 +++-
> > fs/Makefile | 2 +-
> > fs/init.c | 17 ++
> > fs/internal.h | 1 +
> > fs/mount.h | 1 +
> > fs/namespace.c | 181 ++++++++++++++-------
> > fs/nullfs.c | 70 ++++++++
> > include/linux/init_syscalls.h | 1 +
> > include/uapi/linux/magic.h | 1 +
> > init/do_mounts.c | 14 ++
> > init/do_mounts.h | 1 +
> > 11 files changed, 254 insertions(+), 67 deletions(-)
> > ---
> > base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> > change-id: 20260102-work-immutable-rootfs-b5f23e0f5a27
>
> I like the set overall. My only real gripe is the new command-line
> option. Won't that make distro adoption hard?
>
> IIUC, with this new method, you're just adding a new nullfs at the
> bottom of the stack and then mounting the traditional ramfs as the
> mutable rootfs on top of it.
>
> Would there be any harm in just always doing this (and dropping the
> command-line option)?
>
> You would end up "leaking" both the nullfs and ramfs in the case of
> traditional userspace that was unaware of the extra mount, but that
> seems like it should be something we could live with.
I would be open to trying to make it unconditional. IOW, a patch on top
of the current set that removes the "nullfs_rootfs" command line so that
we can simply revert if it turns out that is an issue.
^ permalink raw reply [flat|nested] 17+ messages in thread* [RFC PATCH 5/4] fs: use nullfs unconditionally as the real rootfs
2026-01-14 8:58 ` Christian Brauner
@ 2026-01-14 10:32 ` Christian Brauner
2026-01-14 12:05 ` Jeff Layton
0 siblings, 1 reply; 17+ messages in thread
From: Christian Brauner @ 2026-01-14 10:32 UTC (permalink / raw)
To: Jeff Layton
Cc: Christian Brauner, linux-fsdevel, Alexander Viro, Jan Kara,
Amir Goldstein, Lennart Poettering,
Zbigniew Jędrzejewski-Szmek, Josef Bacik
Remove the "nullfs_rootfs" boot parameter and try to simply always use
nullfs. The mutable rootfs will be mounted on top of it. Systems that
don't use pivot_root() to pivot away from the real rootfs will have an
additional mount stick around but that shouldn't be a problem at all. If
it is we'll rever this commit.
This also simplifies the boot process and removes the need for the
traditional switch_root workarounds.
Suggested-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
.../filesystems/ramfs-rootfs-initramfs.rst | 24 ++-----
fs/namespace.c | 64 ++++++-------------
init/do_mounts.c | 20 ++----
init/do_mounts.h | 1 -
4 files changed, 32 insertions(+), 77 deletions(-)
diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
index a8899f849e90..165117a721ce 100644
--- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst
+++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
@@ -76,13 +76,8 @@ What is rootfs?
---------------
Rootfs is a special instance of ramfs (or tmpfs, if that's enabled), which is
-always present in 2.6 systems. Traditionally, you can't unmount rootfs for
-approximately the same reason you can't kill the init process; rather than
-having special code to check for and handle an empty list, it's smaller and
-simpler for the kernel to just make sure certain lists can't become empty.
-
-However, if the kernel is booted with "nullfs_rootfs", an immutable empty
-filesystem called nullfs is used as the true root, with the mutable rootfs
+always present in Linux systems. The kernel uses an immutable empty filesystem
+called nullfs as the true root of the VFS hierarchy, with the mutable rootfs
(tmpfs/ramfs) mounted on top of it. This allows pivot_root() and unmounting
of the initramfs to work normally.
@@ -126,25 +121,14 @@ All this differs from the old initrd in several ways:
program. See the switch_root utility, below.)
- When switching another root device, initrd would pivot_root and then
- umount the ramdisk. Traditionally, initramfs is rootfs: you can neither
- pivot_root rootfs, nor unmount it. Instead delete everything out of
- rootfs to free up the space (find -xdev / -exec rm '{}' ';'), overmount
- rootfs with the new root (cd /newmount; mount --move . /; chroot .),
- attach stdin/stdout/stderr to the new /dev/console, and exec the new init.
-
- Since this is a remarkably persnickety process (and involves deleting
- commands before you can run them), the klibc package introduced a helper
- program (utils/run_init.c) to do all this for you. Most other packages
- (such as busybox) have named this command "switch_root".
-
- However, if the kernel is booted with "nullfs_rootfs", pivot_root() works
+ umount the ramdisk. With nullfs as the true root, pivot_root() works
normally from the initramfs. Userspace can simply do::
chdir(new_root);
pivot_root(".", ".");
umount2(".", MNT_DETACH);
- This is the preferred method when nullfs_rootfs is enabled.
+ This is the preferred method for switching root filesystems.
Populating initramfs:
---------------------
diff --git a/fs/namespace.c b/fs/namespace.c
index a44ebb2f1161..53d1055c1825 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -75,17 +75,6 @@ static int __init initramfs_options_setup(char *str)
__setup("initramfs_options=", initramfs_options_setup);
-bool nullfs_rootfs = false;
-
-static int __init nullfs_rootfs_setup(char *str)
-{
- if (*str)
- return 0;
- nullfs_rootfs = true;
- return 1;
-}
-__setup("nullfs_rootfs", nullfs_rootfs_setup);
-
static u64 event;
static DEFINE_XARRAY_FLAGS(mnt_id_xa, XA_FLAGS_ALLOC);
static DEFINE_IDA(mnt_group_ida);
@@ -4593,10 +4582,9 @@ int path_pivot_root(struct path *new, struct path *old)
* pointed to by put_old must yield the same directory as new_root. No other
* file system may be mounted on put_old. After all, new_root is a mountpoint.
*
- * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem
- * unless the kernel was booted with "nullfs_rootfs". See
- * Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
- * in this situation.
+ * The immutable nullfs filesystem is mounted as the true root of the VFS
+ * hierarchy. The mutable rootfs (tmpfs/ramfs) is layered on top of this,
+ * allowing pivot_root() to work normally from initramfs.
*
* Notes:
* - we don't move root/cwd if they are not at the root (reason: if something
@@ -5993,49 +5981,39 @@ static void __init init_mount_tree(void)
struct path root;
/*
- * When nullfs is used, we create two mounts:
+ * We create two mounts:
*
* (1) nullfs with mount id 1
* (2) mutable rootfs with mount id 2
*
* with (2) mounted on top of (1).
*/
- if (nullfs_rootfs) {
- nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL);
- if (IS_ERR(nullfs_mnt))
- panic("VFS: Failed to create nullfs");
- }
+ nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL);
+ if (IS_ERR(nullfs_mnt))
+ panic("VFS: Failed to create nullfs");
mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options);
if (IS_ERR(mnt))
panic("Can't create rootfs");
- if (nullfs_rootfs) {
- VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1);
- VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2);
+ VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1);
+ VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2);
- /* The namespace root is the nullfs mnt. */
- mnt_root = real_mount(nullfs_mnt);
- init_mnt_ns.root = mnt_root;
+ /* The namespace root is the nullfs mnt. */
+ mnt_root = real_mount(nullfs_mnt);
+ init_mnt_ns.root = mnt_root;
- /* Mount mutable rootfs on top of nullfs. */
- root.mnt = nullfs_mnt;
- root.dentry = nullfs_mnt->mnt_root;
+ /* Mount mutable rootfs on top of nullfs. */
+ root.mnt = nullfs_mnt;
+ root.dentry = nullfs_mnt->mnt_root;
- LOCK_MOUNT_EXACT(mp, &root);
- if (unlikely(IS_ERR(mp.parent)))
- panic("VFS: Failed to mount rootfs on nullfs");
- scoped_guard(mount_writer)
- attach_mnt(real_mount(mnt), mp.parent, mp.mp);
+ LOCK_MOUNT_EXACT(mp, &root);
+ if (unlikely(IS_ERR(mp.parent)))
+ panic("VFS: Failed to mount rootfs on nullfs");
+ scoped_guard(mount_writer)
+ attach_mnt(real_mount(mnt), mp.parent, mp.mp);
- pr_info("VFS: Finished mounting rootfs on nullfs\n");
- } else {
- VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 1);
-
- /* The namespace root is the mutable rootfs. */
- mnt_root = real_mount(mnt);
- init_mnt_ns.root = mnt_root;
- }
+ pr_info("VFS: Finished mounting rootfs on nullfs\n");
/*
* We've dropped all locks here but that's fine. Not just are we
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 675397c8a7a4..df6847bcf1f2 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -493,21 +493,15 @@ void __init prepare_namespace(void)
out:
devtmpfs_mount();
- if (nullfs_rootfs) {
- if (init_pivot_root(".", ".")) {
- pr_err("VFS: Failed to pivot into new rootfs\n");
- return;
- }
- if (init_umount(".", MNT_DETACH)) {
- pr_err("VFS: Failed to unmount old rootfs\n");
- return;
- }
- pr_info("VFS: Pivoted into new rootfs\n");
+ if (init_pivot_root(".", ".")) {
+ pr_err("VFS: Failed to pivot into new rootfs\n");
return;
}
-
- init_mount(".", "/", NULL, MS_MOVE, NULL);
- init_chroot(".");
+ if (init_umount(".", MNT_DETACH)) {
+ pr_err("VFS: Failed to unmount old rootfs\n");
+ return;
+ }
+ pr_info("VFS: Pivoted into new rootfs\n");
}
static bool is_tmpfs;
diff --git a/init/do_mounts.h b/init/do_mounts.h
index fbfee810aa89..6069ea3eb80d 100644
--- a/init/do_mounts.h
+++ b/init/do_mounts.h
@@ -15,7 +15,6 @@
void mount_root_generic(char *name, char *pretty_name, int flags);
void mount_root(char *root_device_name);
extern int root_mountflags;
-extern bool nullfs_rootfs;
static inline __init int create_dev(char *name, dev_t dev)
{
--
2.47.3
^ permalink raw reply related [flat|nested] 17+ messages in thread* Re: [RFC PATCH 5/4] fs: use nullfs unconditionally as the real rootfs
2026-01-14 10:32 ` [RFC PATCH 5/4] fs: use nullfs unconditionally as the real rootfs Christian Brauner
@ 2026-01-14 12:05 ` Jeff Layton
0 siblings, 0 replies; 17+ messages in thread
From: Jeff Layton @ 2026-01-14 12:05 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Alexander Viro, Jan Kara, Amir Goldstein,
Lennart Poettering, Zbigniew Jędrzejewski-Szmek, Josef Bacik
On Wed, 2026-01-14 at 11:32 +0100, Christian Brauner wrote:
> Remove the "nullfs_rootfs" boot parameter and try to simply always use
> nullfs. The mutable rootfs will be mounted on top of it. Systems that
> don't use pivot_root() to pivot away from the real rootfs will have an
> additional mount stick around but that shouldn't be a problem at all. If
> it is we'll rever this commit.
>
> This also simplifies the boot process and removes the need for the
> traditional switch_root workarounds.
>
> Suggested-by: Jeff Layton <jlayton@kernel.org>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> .../filesystems/ramfs-rootfs-initramfs.rst | 24 ++-----
> fs/namespace.c | 64 ++++++-------------
> init/do_mounts.c | 20 ++----
> init/do_mounts.h | 1 -
> 4 files changed, 32 insertions(+), 77 deletions(-)
>
> diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
> index a8899f849e90..165117a721ce 100644
> --- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst
> +++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
> @@ -76,13 +76,8 @@ What is rootfs?
> ---------------
>
> Rootfs is a special instance of ramfs (or tmpfs, if that's enabled), which is
> -always present in 2.6 systems. Traditionally, you can't unmount rootfs for
> -approximately the same reason you can't kill the init process; rather than
> -having special code to check for and handle an empty list, it's smaller and
> -simpler for the kernel to just make sure certain lists can't become empty.
> -
> -However, if the kernel is booted with "nullfs_rootfs", an immutable empty
> -filesystem called nullfs is used as the true root, with the mutable rootfs
> +always present in Linux systems. The kernel uses an immutable empty filesystem
> +called nullfs as the true root of the VFS hierarchy, with the mutable rootfs
> (tmpfs/ramfs) mounted on top of it. This allows pivot_root() and unmounting
> of the initramfs to work normally.
>
> @@ -126,25 +121,14 @@ All this differs from the old initrd in several ways:
> program. See the switch_root utility, below.)
>
> - When switching another root device, initrd would pivot_root and then
> - umount the ramdisk. Traditionally, initramfs is rootfs: you can neither
> - pivot_root rootfs, nor unmount it. Instead delete everything out of
> - rootfs to free up the space (find -xdev / -exec rm '{}' ';'), overmount
> - rootfs with the new root (cd /newmount; mount --move . /; chroot .),
> - attach stdin/stdout/stderr to the new /dev/console, and exec the new init.
> -
> - Since this is a remarkably persnickety process (and involves deleting
> - commands before you can run them), the klibc package introduced a helper
> - program (utils/run_init.c) to do all this for you. Most other packages
> - (such as busybox) have named this command "switch_root".
> -
> - However, if the kernel is booted with "nullfs_rootfs", pivot_root() works
> + umount the ramdisk. With nullfs as the true root, pivot_root() works
> normally from the initramfs. Userspace can simply do::
>
> chdir(new_root);
> pivot_root(".", ".");
> umount2(".", MNT_DETACH);
>
> - This is the preferred method when nullfs_rootfs is enabled.
> + This is the preferred method for switching root filesystems.
>
> Populating initramfs:
> ---------------------
> diff --git a/fs/namespace.c b/fs/namespace.c
> index a44ebb2f1161..53d1055c1825 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -75,17 +75,6 @@ static int __init initramfs_options_setup(char *str)
>
> __setup("initramfs_options=", initramfs_options_setup);
>
> -bool nullfs_rootfs = false;
> -
> -static int __init nullfs_rootfs_setup(char *str)
> -{
> - if (*str)
> - return 0;
> - nullfs_rootfs = true;
> - return 1;
> -}
> -__setup("nullfs_rootfs", nullfs_rootfs_setup);
> -
> static u64 event;
> static DEFINE_XARRAY_FLAGS(mnt_id_xa, XA_FLAGS_ALLOC);
> static DEFINE_IDA(mnt_group_ida);
> @@ -4593,10 +4582,9 @@ int path_pivot_root(struct path *new, struct path *old)
> * pointed to by put_old must yield the same directory as new_root. No other
> * file system may be mounted on put_old. After all, new_root is a mountpoint.
> *
> - * Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem
> - * unless the kernel was booted with "nullfs_rootfs". See
> - * Documentation/filesystems/ramfs-rootfs-initramfs.rst for alternatives
> - * in this situation.
> + * The immutable nullfs filesystem is mounted as the true root of the VFS
> + * hierarchy. The mutable rootfs (tmpfs/ramfs) is layered on top of this,
> + * allowing pivot_root() to work normally from initramfs.
> *
> * Notes:
> * - we don't move root/cwd if they are not at the root (reason: if something
> @@ -5993,49 +5981,39 @@ static void __init init_mount_tree(void)
> struct path root;
>
> /*
> - * When nullfs is used, we create two mounts:
> + * We create two mounts:
> *
> * (1) nullfs with mount id 1
> * (2) mutable rootfs with mount id 2
> *
> * with (2) mounted on top of (1).
> */
> - if (nullfs_rootfs) {
> - nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL);
> - if (IS_ERR(nullfs_mnt))
> - panic("VFS: Failed to create nullfs");
> - }
> + nullfs_mnt = vfs_kern_mount(&nullfs_fs_type, 0, "nullfs", NULL);
> + if (IS_ERR(nullfs_mnt))
> + panic("VFS: Failed to create nullfs");
>
> mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options);
> if (IS_ERR(mnt))
> panic("Can't create rootfs");
>
> - if (nullfs_rootfs) {
> - VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1);
> - VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2);
> + VFS_WARN_ON_ONCE(real_mount(nullfs_mnt)->mnt_id != 1);
> + VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 2);
>
> - /* The namespace root is the nullfs mnt. */
> - mnt_root = real_mount(nullfs_mnt);
> - init_mnt_ns.root = mnt_root;
> + /* The namespace root is the nullfs mnt. */
> + mnt_root = real_mount(nullfs_mnt);
> + init_mnt_ns.root = mnt_root;
>
> - /* Mount mutable rootfs on top of nullfs. */
> - root.mnt = nullfs_mnt;
> - root.dentry = nullfs_mnt->mnt_root;
> + /* Mount mutable rootfs on top of nullfs. */
> + root.mnt = nullfs_mnt;
> + root.dentry = nullfs_mnt->mnt_root;
>
> - LOCK_MOUNT_EXACT(mp, &root);
> - if (unlikely(IS_ERR(mp.parent)))
> - panic("VFS: Failed to mount rootfs on nullfs");
> - scoped_guard(mount_writer)
> - attach_mnt(real_mount(mnt), mp.parent, mp.mp);
> + LOCK_MOUNT_EXACT(mp, &root);
> + if (unlikely(IS_ERR(mp.parent)))
> + panic("VFS: Failed to mount rootfs on nullfs");
> + scoped_guard(mount_writer)
> + attach_mnt(real_mount(mnt), mp.parent, mp.mp);
>
> - pr_info("VFS: Finished mounting rootfs on nullfs\n");
> - } else {
> - VFS_WARN_ON_ONCE(real_mount(mnt)->mnt_id != 1);
> -
> - /* The namespace root is the mutable rootfs. */
> - mnt_root = real_mount(mnt);
> - init_mnt_ns.root = mnt_root;
> - }
> + pr_info("VFS: Finished mounting rootfs on nullfs\n");
>
> /*
> * We've dropped all locks here but that's fine. Not just are we
> diff --git a/init/do_mounts.c b/init/do_mounts.c
> index 675397c8a7a4..df6847bcf1f2 100644
> --- a/init/do_mounts.c
> +++ b/init/do_mounts.c
> @@ -493,21 +493,15 @@ void __init prepare_namespace(void)
> out:
> devtmpfs_mount();
>
> - if (nullfs_rootfs) {
> - if (init_pivot_root(".", ".")) {
> - pr_err("VFS: Failed to pivot into new rootfs\n");
> - return;
> - }
> - if (init_umount(".", MNT_DETACH)) {
> - pr_err("VFS: Failed to unmount old rootfs\n");
> - return;
> - }
> - pr_info("VFS: Pivoted into new rootfs\n");
> + if (init_pivot_root(".", ".")) {
> + pr_err("VFS: Failed to pivot into new rootfs\n");
> return;
> }
> -
> - init_mount(".", "/", NULL, MS_MOVE, NULL);
> - init_chroot(".");
> + if (init_umount(".", MNT_DETACH)) {
> + pr_err("VFS: Failed to unmount old rootfs\n");
> + return;
> + }
> + pr_info("VFS: Pivoted into new rootfs\n");
> }
>
> static bool is_tmpfs;
> diff --git a/init/do_mounts.h b/init/do_mounts.h
> index fbfee810aa89..6069ea3eb80d 100644
> --- a/init/do_mounts.h
> +++ b/init/do_mounts.h
> @@ -15,7 +15,6 @@
> void mount_root_generic(char *name, char *pretty_name, int flags);
> void mount_root(char *root_device_name);
> extern int root_mountflags;
> -extern bool nullfs_rootfs;
>
> static inline __init int create_dev(char *name, dev_t dev)
> {
I like the idea of getting rid of the command-line option. Anything we
can do to make this more automatic seems like a good idea.
If you're worried about it, you could flip the command-line option
around: do the new method by default, but allow a command-line option
that makes it set up the rootfs the old way.
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (4 preceding siblings ...)
2026-01-13 14:17 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
@ 2026-01-14 11:58 ` Jeff Layton
2026-01-25 19:18 ` Askar Safin
` (3 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Jeff Layton @ 2026-01-14 11:58 UTC (permalink / raw)
To: Christian Brauner, linux-fsdevel
Cc: Alexander Viro, Jan Kara, Amir Goldstein, Lennart Poettering,
Zbigniew Jędrzejewski-Szmek, Josef Bacik, stable
On Mon, 2026-01-12 at 16:47 +0100, Christian Brauner wrote:
> Currently pivot_root() doesn't work on the real rootfs because it
> cannot be unmounted. Userspace has to do a recursive removal of the
> initramfs contents manually before continuing the boot.
>
> Really all we want from the real rootfs is to serve as the parent mount
> for anything that is actually useful such as the tmpfs or ramfs for
> initramfs unpacking or the rootfs itself. There's no need for the real
> rootfs to actually be anything meaningful or useful. Add a immutable
> rootfs called "nullfs" that can be selected via the "nullfs_rootfs"
> kernel command line option.
>
> The kernel will mount a tmpfs/ramfs on top of it, unpack the initramfs
> and fire up userspace which mounts the rootfs and can then just do:
>
> chdir(rootfs);
> pivot_root(".", ".");
> umount2(".", MNT_DETACH);
>
> and be done with it. (Ofc, userspace can also choose to retain the
> initramfs contents by using something like pivot_root(".", "/initramfs")
> without unmounting it.)
>
> Technically this also means that the rootfs mount in unprivileged
> namespaces doesn't need to become MNT_LOCKED anymore as it's guaranteed
> that the immutable rootfs remains permanently empty so there cannot be
> anything revealed by unmounting the covering mount.
>
> In the future this will also allow us to create completely empty mount
> namespaces without risking to leak anything.
>
> systemd already handles this all correctly as it tries to pivot_root()
> first and falls back to MS_MOVE only when that fails.
>
> This goes back to various discussion in previous years and a LPC 2024
> presentation about this very topic.
>
> Now in vfs-7.0.nullfs.
>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> Changes in v2:
> - Rename to "nullfs".
> - Update documentation.
> - Link to v1: https://patch.msgid.link/20260102-work-immutable-rootfs-v1-0-f2073b2d1602@kernel.org
>
> ---
> Christian Brauner (4):
> fs: ensure that internal tmpfs mount gets mount id zero
> fs: add init_pivot_root()
> fs: add immutable rootfs
> docs: mention nullfs
>
> .../filesystems/ramfs-rootfs-initramfs.rst | 32 +++-
> fs/Makefile | 2 +-
> fs/init.c | 17 ++
> fs/internal.h | 1 +
> fs/mount.h | 1 +
> fs/namespace.c | 181 ++++++++++++++-------
> fs/nullfs.c | 70 ++++++++
> include/linux/init_syscalls.h | 1 +
> include/uapi/linux/magic.h | 1 +
> init/do_mounts.c | 14 ++
> init/do_mounts.h | 1 +
> 11 files changed, 254 insertions(+), 67 deletions(-)
> ---
> base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
> change-id: 20260102-work-immutable-rootfs-b5f23e0f5a27
Reviewed-by: Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (5 preceding siblings ...)
2026-01-14 11:58 ` [PATCH v2 0/4] fs: add immutable rootfs Jeff Layton
@ 2026-01-25 19:18 ` Askar Safin
2026-02-01 19:55 ` Askar Safin
` (2 subsequent siblings)
9 siblings, 0 replies; 17+ messages in thread
From: Askar Safin @ 2026-01-25 19:18 UTC (permalink / raw)
To: brauner
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek
Christian Brauner <brauner@kernel.org>:
> Currently pivot_root() doesn't work on the real rootfs because it
> cannot be unmounted. Userspace has to do a recursive removal of the
> initramfs contents manually before continuing the boot.
>
> Really all we want from the real rootfs is to serve as the parent mount
Note: this *is* possible to get access to nullfs.
In the end of this email you will find code, which proves this. I tested it
on current vfs.all. The program will print "done", and this will prove my
statement.
I think this is not a bug. I just want to make sure you are aware of this.
=====
#define GNU_SOURCE
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <sched.h>
#include <sys/wait.h>
#define OPEN_TREE_NAMESPACE (1 << 1) /* Clone the target tree into a new mount namespace */
/* Get information about namespace. */
#define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info)
struct mnt_ns_info {
__u32 size;
__u32 nr_mounts;
__u64 mnt_ns_id;
};
int
main (void)
{
mkdir ("/foo", 0777);
if (mount ("tmpfsfoo", "/foo", "tmpfs", 0, NULL) == -1)
{
fprintf (stderr, "mount\n");
return 1;
}
int ns = open_tree (-EBADFD, "/foo", OPEN_TREE_NAMESPACE);
if (ns == -1)
{
fprintf (stderr, "open_tree failed\n");
return 1;
}
if (fork () == 0)
{
if (setns (ns, CLONE_NEWNS) == -1)
{
abort ();
}
if (umount2 ("/", MNT_DETACH) == -1) // This umount2 will succeed
{
abort ();
}
_exit (0);
}
{
int status;
if (wait (&status) == -1)
{
abort ();
}
if (status != 0)
{
abort ();
}
}
if (fork () == 0)
{
if (setns (ns, CLONE_NEWNS) == -1)
{
abort ();
}
if (umount2 ("/", MNT_DETACH) == 0) // This umount2 will fail, because we got to nullfs
{
abort ();
}
_exit (0);
}
{
int status;
if (wait (&status) == -1)
{
abort ();
}
if (status != 0)
{
abort ();
}
}
printf ("done\n");
return 0;
}
--
Askar Safin
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (6 preceding siblings ...)
2026-01-25 19:18 ` Askar Safin
@ 2026-02-01 19:55 ` Askar Safin
2026-02-04 13:00 ` Christian Brauner
2026-02-02 14:27 ` Askar Safin
2026-02-02 16:22 ` Askar Safin
9 siblings, 1 reply; 17+ messages in thread
From: Askar Safin @ 2026-02-01 19:55 UTC (permalink / raw)
To: brauner
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek
Christian, important! Your patchset breaks userspace! (And I tested this.)
Christian Brauner <brauner@kernel.org>:
> Currently pivot_root() doesn't work on the real rootfs because it
> cannot be unmounted. Userspace has to do a recursive removal of the
> initramfs contents manually before continuing the boot.
listmount is buggy (see details below), but currently on a typical
Linux distro the bug is hidden. Your new nullfs patchset exposes the bug,
and now typical Linux configuration becomes buggy!
Look at this loop:
https://elixir.bootlin.com/linux/v6.19-rc5/source/fs/namespace.c#L5518 .
This loop is executed, when we call listmount on non-our namespace with
LSMT_ROOT.
As you can see, this loop finds mount, which is exactly one layer above
initial root mount. But why? What if our initial root mount was
overmounted multiple times?
What (in my opinion) is actually needed here is to find topmost overmount
of initial root mount. We know how to do this, we do this here:
https://elixir.bootlin.com/linux/v6.19-rc5/source/fs/namespace.c#L6096 .
Fortunately, current listmount code works in a typical configuration.
Usually we indeed overmount initramfs exactly one time.
So, listmount usually does the right thing.
But this nullfs patchset breaks listmount. Now on a typical distro
(we assume that initramfs implementations (i. e. dracut, etc) are not
yet changed to take advantage of nullfs) initial root mount (i. e. nullfs)
will be overmounted two times: initramfs and actual disk root fs.
So listmount with LSMT_ROOT in somebody's else namespace will return
initramfs instead of actual topmost root fs.
I think this listmount bug should be fixed before nullfs is applied to
mainline.
I tested listmount behavior I'm talking about. On both vfs.all (i. e. with
nullfs patches applied) and on some older vfs.git commit (without nullfs).
--
Askar Safin
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-02-01 19:55 ` Askar Safin
@ 2026-02-04 13:00 ` Christian Brauner
2026-02-04 14:48 ` Askar Safin
0 siblings, 1 reply; 17+ messages in thread
From: Christian Brauner @ 2026-02-04 13:00 UTC (permalink / raw)
To: Askar Safin
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek
On Sun, Feb 01, 2026 at 10:55:31PM +0300, Askar Safin wrote:
> Christian, important! Your patchset breaks userspace! (And I tested this.)
If a bug is found in a piece of code we _calmly_ point it out and fix it.
> I tested listmount behavior I'm talking about. On both vfs.all (i. e. with
> nullfs patches applied) and on some older vfs.git commit (without nullfs).
Looking at a foreign mount namespace over which the caller is privileged
intentionally lists all mounts on top of the namespace root. In contrast
to mountinfo which always looks at another mount namespace from the
perspective of the process that is located within that mount namespace
listmount() on a foreing mount namespace looks at the namespace itself
and aims to list all mounts in that namespace. Since it is a new api
there can be no regressions.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-02-04 13:00 ` Christian Brauner
@ 2026-02-04 14:48 ` Askar Safin
0 siblings, 0 replies; 17+ messages in thread
From: Askar Safin @ 2026-02-04 14:48 UTC (permalink / raw)
To: Christian Brauner
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek
On Wed, Feb 4, 2026 at 4:01 PM Christian Brauner <brauner@kernel.org> wrote:
>
> On Sun, Feb 01, 2026 at 10:55:31PM +0300, Askar Safin wrote:
> > Christian, important! Your patchset breaks userspace! (And I tested this.)
>
> If a bug is found in a piece of code we _calmly_ point it out and fix it.
Okay, I will be calmer next time.
> > I tested listmount behavior I'm talking about. On both vfs.all (i. e. with
> > nullfs patches applied) and on some older vfs.git commit (without nullfs).
>
> Looking at a foreign mount namespace over which the caller is privileged
> intentionally lists all mounts on top of the namespace root. In contrast
> to mountinfo which always looks at another mount namespace from the
> perspective of the process that is located within that mount namespace
> listmount() on a foreing mount namespace looks at the namespace itself
> and aims to list all mounts in that namespace. Since it is a new api
> there can be no regressions.
Okay. Thank you for your explanation.
But then instead of a loop (
https://elixir.bootlin.com/linux/v6.19-rc5/source/fs/namespace.c#L5518
)
I suggest simply using ns->root->overmount.
--
Askar Safin
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (7 preceding siblings ...)
2026-02-01 19:55 ` Askar Safin
@ 2026-02-02 14:27 ` Askar Safin
2026-02-02 16:22 ` Askar Safin
9 siblings, 0 replies; 17+ messages in thread
From: Askar Safin @ 2026-02-02 14:27 UTC (permalink / raw)
To: brauner
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek, Menglong Dong, Zhang Yunkai, cgel.zte,
Menglong Dong
Christian Brauner <brauner@kernel.org>:
> Currently pivot_root() doesn't work on the real rootfs because it
> cannot be unmounted. Userspace has to do a recursive removal of the
> initramfs contents manually before continuing the boot.
I want to point out at yet another benefit of this nullfs patchset:
it prevents data loss on external devices.
Here is why:
https://lore.kernel.org/all/20210522113155.244796-3-dong.menglong@zte.com.cn/
(search for phrase "data lose arise").
--
Askar Safin
^ permalink raw reply [flat|nested] 17+ messages in thread* Re: [PATCH v2 0/4] fs: add immutable rootfs
2026-01-12 15:47 [PATCH v2 0/4] fs: add immutable rootfs Christian Brauner
` (8 preceding siblings ...)
2026-02-02 14:27 ` Askar Safin
@ 2026-02-02 16:22 ` Askar Safin
9 siblings, 0 replies; 17+ messages in thread
From: Askar Safin @ 2026-02-02 16:22 UTC (permalink / raw)
To: brauner
Cc: amir73il, jack, jlayton, josef, lennart, linux-fsdevel, stable,
viro, zbyszek
Christian Brauner <brauner@kernel.org>:
> Add a immutable
> rootfs called "nullfs"
Why not fix pivot_root instead?
I. e. why not make sure pivot_root will work on true root of VFS?
--
Askar Safin
^ permalink raw reply [flat|nested] 17+ messages in thread