AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/15 16:08 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "EROFS_FS",
    "OVERLAY_FS"
  ],
  "FocusSymbols": [
    "metafs_get_root",
    "metafs_create_root",
    "metafs_root_lookup",
    "metafs_mount_lookup",
    "metafs_options_lookup",
    "metafs_option_values_lookup",
    "metafs_get_option_values",
    "metafs_get_link",
    "metafs_readlink",
    "mnt_show_options",
    "sb_add_option",
    "sb_add_option_path",
    "ovl_get_options",
    "erofs_get_options"
  ],
  "Reasoning": "The patch introduces a new virtual filesystem `metafs` that exposes metadata about files and filesystems via the `O_ALT` flag. It also introduces a new `get_options` super operation, implemented by `erofs` and `overlayfs`. These are significant functional changes to the VFS and specific filesystems that should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/15 16:08 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 78c8d9e934ce8a9bc1ae0c994f463854bd2f9811\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Jul 15 16:08:22 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst\nindex 7c753148af8883..3dc431b6cdbb0b 100644\n--- a/Documentation/filesystems/vfs.rst\n+++ b/Documentation/filesystems/vfs.rst\n@@ -314,7 +314,7 @@ or bottom half).\n ``show_devname``\n \tOptional. Called by the VFS to show device name for\n \t/proc/\u003cpid\u003e/{mounts,mountinfo,mountstats}. If not provided then\n-\t'(struct mount).mnt_devname' will be used.\n+\t'(struct super_block).s_devname' will be used.\n \n ``show_path``\n \tOptional. Called by the VFS (for /proc/\u003cpid\u003e/mountinfo) to show\ndiff --git a/fs/Makefile b/fs/Makefile\nindex 89a8a9d207d1c5..87a4c23d5a08de 100644\n--- a/fs/Makefile\n+++ b/fs/Makefile\n@@ -58,6 +58,8 @@ obj-$(CONFIG_CONFIGFS_FS)\t+= configfs/\n obj-y\t\t\t\t+= devpts/\n \n obj-$(CONFIG_DLM)\t\t+= dlm/\n+\n+obj-y\t\t\t\t+= metafs/\n  \n # Do not add any filesystems before this line\n obj-$(CONFIG_NETFS_SUPPORT)\t+= netfs/\ndiff --git a/fs/erofs/super.c b/fs/erofs/super.c\nindex 86fa5c6a0c7087..257517c28e1d38 100644\n--- a/fs/erofs/super.c\n+++ b/fs/erofs/super.c\n@@ -1028,29 +1028,34 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)\n \treturn 0;\n }\n \n-static int erofs_show_options(struct seq_file *seq, struct dentry *root)\n+static int erofs_get_options(struct sb_opt_ctx *ctx, struct dentry *root)\n {\n \tstruct erofs_sb_info *sbi = EROFS_SB(root-\u003ed_sb);\n \tstruct erofs_mount_opts *opt = \u0026sbi-\u003eopt;\n \n+\tif (sbi-\u003edif0.file)\n+\t\tsb_add_option_path(ctx, \"source\", NULL, \u0026sbi-\u003edif0.file-\u003ef_path);\n+\n \tif (IS_ENABLED(CONFIG_EROFS_FS_XATTR))\n-\t\tseq_puts(seq, test_opt(opt, XATTR_USER) ?\n-\t\t\t\t\",user_xattr\" : \",nouser_xattr\");\n+\t\tsb_add_option(ctx, test_opt(opt, XATTR_USER) ?\n+\t\t\t      \"user_xattr\" : \"nouser_xattr\", NULL);\n \tif (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))\n-\t\tseq_puts(seq, test_opt(opt, POSIX_ACL) ? \",acl\" : \",noacl\");\n+\t\tsb_add_option(ctx, test_opt(opt, POSIX_ACL) ? \"acl\" : \"noacl\", NULL);\n \tif (IS_ENABLED(CONFIG_EROFS_FS_ZIP))\n-\t\tseq_printf(seq, \",cache_strategy=%s\",\n+\t\tsb_add_option(ctx, \"cache_strategy\",\n \t\t\t  erofs_param_cache_strategy[opt-\u003ecache_strategy].name);\n \tif (test_opt(opt, DAX_ALWAYS))\n-\t\tseq_puts(seq, \",dax=always\");\n+\t\tsb_add_option(ctx, \"dax\", \"always\");\n \tif (test_opt(opt, DAX_NEVER))\n-\t\tseq_puts(seq, \",dax=never\");\n+\t\tsb_add_option(ctx, \"dax\", \"never\");\n \tif (erofs_is_fileio_mode(sbi) \u0026\u0026 test_opt(opt, DIRECT_IO))\n-\t\tseq_puts(seq, \",directio\");\n-\tif (sbi-\u003edif0.fsoff)\n-\t\tseq_printf(seq, \",fsoffset=%llu\", sbi-\u003edif0.fsoff);\n+\t\tsb_add_option(ctx, \"directio\", NULL);\n+\tif (sbi-\u003edif0.fsoff) {\n+\t\tchar *val __free(kfree) = kasprintf(GFP_KERNEL, \"%llu\", sbi-\u003edif0.fsoff);\n+\t\tsb_add_option(ctx, \"fsoffset\", val);\n+\t}\n \tif (test_opt(opt, INODE_SHARE))\n-\t\tseq_puts(seq, \",inode_share\");\n+\t\tsb_add_option(ctx, \"inode_share\", NULL);\n \treturn 0;\n }\n \n@@ -1069,7 +1074,7 @@ const struct super_operations erofs_sops = {\n \t.free_inode = erofs_free_inode,\n \t.evict_inode = erofs_evict_inode,\n \t.statfs = erofs_statfs,\n-\t.show_options = erofs_show_options,\n+\t.get_options = erofs_get_options,\n };\n \n module_init(erofs_module_init);\ndiff --git a/fs/fcntl.c b/fs/fcntl.c\nindex c158f082f1da4f..01b765cfac8479 100644\n--- a/fs/fcntl.c\n+++ b/fs/fcntl.c\n@@ -1169,7 +1169,7 @@ static int __init fcntl_init(void)\n \t * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY\n \t * is defined as O_NONBLOCK on some platforms and not on others.\n \t */\n-\tBUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ !=\n+\tBUILD_BUG_ON(23 - 1 /* for O_RDONLY being 0 */ !=\n \t\tHWEIGHT32(\n \t\t\t(VALID_OPEN_FLAGS \u0026 ~(O_NONBLOCK | O_NDELAY)) |\n \t\t\t__FMODE_EXEC | __O_REGULAR));\ndiff --git a/fs/internal.h b/fs/internal.h\nindex 355d93f9220861..e73a5c1d5b4ee1 100644\n--- a/fs/internal.h\n+++ b/fs/internal.h\n@@ -95,6 +95,7 @@ int path_umount(const struct path *path, int flags);\n int path_pivot_root(struct path *new, struct path *old);\n \n int show_path(struct seq_file *m, struct dentry *root);\n+int mnt_show_options(struct seq_file *seq, struct vfsmount *mnt);\n \n /*\n  * fs_struct.c\ndiff --git a/fs/metafs/Makefile b/fs/metafs/Makefile\nnew file mode 100644\nindex 00000000000000..f855b48d9c2b90\n--- /dev/null\n+++ b/fs/metafs/Makefile\n@@ -0,0 +1,6 @@\n+# SPDX-License-Identifier: GPL-2.0-only\n+#\n+# Makefile for meta filesystem\n+#\n+\n+obj-y := metafs.o\ndiff --git a/fs/metafs/metafs.c b/fs/metafs/metafs.c\nnew file mode 100644\nindex 00000000000000..15ec0b6064da38\n--- /dev/null\n+++ b/fs/metafs/metafs.c\n@@ -0,0 +1,572 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * metafs - virtual filesystem for metadata access\n+ *\n+ * This provides a way to access metadata about files and filesystems\n+ * through O_ALT file descriptors.\n+ */\n+\n+#include \u003clinux/fs.h\u003e\n+#include \u003clinux/init.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/magic.h\u003e\n+#include \u003clinux/mount.h\u003e\n+#include \u003clinux/namei.h\u003e\n+#include \u003clinux/slab.h\u003e\n+#include \u003clinux/dcache.h\u003e\n+#include \u003clinux/pseudo_fs.h\u003e\n+#include \u003clinux/fs/super.h\u003e\n+#include \"metafs.h\"\n+\n+/*\n+ * metafs superblock and mount\n+ */\n+static struct vfsmount *metafs_mnt __ro_after_init;\n+\n+static const struct super_operations metafs_ops = {\n+\t.statfs\t\t= simple_statfs,\n+\t.drop_inode\t= inode_just_drop,\n+};\n+\n+static int metafs_init_fs_context(struct fs_context *fc)\n+{\n+\tstruct pseudo_fs_context *ctx = init_pseudo(fc, 0x4d455441); /* 'META' */\n+\tif (!ctx)\n+\t\treturn -ENOMEM;\n+\tctx-\u003eops = \u0026metafs_ops;\n+\treturn 0;\n+}\n+\n+static struct file_system_type metafs_type = {\n+\t.name = \"metafs\",\n+\t.init_fs_context = metafs_init_fs_context,\n+\t.kill_sb = kill_anon_super,\n+};\n+\n+/*\n+ * Mount option collection structures\n+ */\n+enum metafs_dentry_type {\n+\tMETAFS_ROOT,\n+\tMETAFS_OPTION_VALUES,\n+\tMETAFS_SYMLINK,\n+};\n+\n+struct metafs_option_entry {\n+\tchar *value;\n+\tstruct path path;\n+};\n+\n+struct metafs_dentry_info {\n+\tenum metafs_dentry_type type;\n+\tunion {\n+\t\tstruct {\n+\t\t\tstruct file *base;\n+\t\t} root;\n+\t\tstruct {\n+\t\t\tstruct metafs_option_entry *entries;\n+\t\t\tint count;\n+\t\t} opt_values;\n+\t\tstruct metafs_option_entry symlink;\n+\t};\n+};\n+\n+struct metafs_collect_ctx {\n+\tstruct sb_opt_ctx ctx;\n+\tconst char *target_name;\n+\tstruct super_block *sb;\n+\tstruct metafs_option_entry *entries;\n+\tint count;\n+\tint size;\n+\tint err;\n+\tbool is_source;\n+};\n+\n+/*\n+ * Collect specific mount option values from the base filesystem\n+ */\n+static void metafs_collect_option(struct sb_opt_ctx *ctx, const char *name,\n+\t\t\t\t  const char *value, const struct path *path)\n+{\n+\tstruct metafs_collect_ctx *collect_ctx = ctx-\u003edata;\n+\tstruct metafs_option_entry *entry;\n+\n+\tif (strcmp(name, collect_ctx-\u003etarget_name) != 0)\n+\t\treturn;\n+\n+\tif (!value \u0026\u0026 collect_ctx-\u003eis_source)\n+\t\tvalue = collect_ctx-\u003esb-\u003es_devname;\n+\n+\tif (collect_ctx-\u003ecount \u003e= collect_ctx-\u003esize) {\n+\t\tint new_size = collect_ctx-\u003esize ? collect_ctx-\u003esize * 2 : 4;\n+\t\tstruct metafs_option_entry *new_entries;\n+\n+\t\tnew_entries = krealloc_array(collect_ctx-\u003eentries, new_size, sizeof(*new_entries),\n+\t\t\t\t\t     GFP_KERNEL);\n+\t\tif (!new_entries) {\n+\t\t\tcollect_ctx-\u003eerr = -ENOMEM;\n+\t\t\treturn;\n+\t\t}\n+\t\tcollect_ctx-\u003eentries = new_entries;\n+\t\tcollect_ctx-\u003esize = new_size;\n+\t}\n+\n+\tentry = \u0026collect_ctx-\u003eentries[collect_ctx-\u003ecount];\n+\tentry-\u003evalue = value ? kstrdup(value, GFP_KERNEL) : NULL;\n+\tif (!entry-\u003evalue \u0026\u0026 value) {\n+\t\tcollect_ctx-\u003eerr = -ENOMEM;\n+\t\treturn;\n+\t}\n+\tif (path) {\n+\t\tentry-\u003epath = *path;\n+\t\tpath_get(\u0026entry-\u003epath);\n+\t} else {\n+\t\tmemset(\u0026entry-\u003epath, 0, sizeof(entry-\u003epath));\n+\t}\n+\tcollect_ctx-\u003ecount++;\n+}\n+\n+static void metafs_free_entries(struct metafs_option_entry *entries, int count)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c count; i++) {\n+\t\tkfree(entries[i].value);\n+\t\tif (entries[i].path.mnt)\n+\t\t\tpath_put(\u0026entries[i].path);\n+\t}\n+\tkfree(entries);\n+}\n+\n+static int metafs_get_option_values(struct file *base, const char *option_name,\n+\t\t\t\t    struct metafs_option_entry **entries_out,\n+\t\t\t\t    int *count_out)\n+{\n+\tconst struct path *path = \u0026base-\u003ef_path;\n+\tstruct super_block *sb = path-\u003emnt-\u003emnt_sb;\n+\tstruct metafs_collect_ctx collect_ctx = {\n+\t\t.ctx.data = \u0026collect_ctx,\n+\t\t.ctx.actor = metafs_collect_option,\n+\t\t.target_name = option_name,\n+\t\t.sb = sb,\n+\t\t.is_source = !strcmp(option_name, \"source\"),\n+\t};\n+\tint ret;\n+\n+\tif (sb-\u003es_op-\u003eget_options) {\n+\t\tret = sb-\u003es_op-\u003eget_options(\u0026collect_ctx.ctx, path-\u003emnt-\u003emnt_root);\n+\t\tif (ret || collect_ctx.err) {\n+\t\t\tmetafs_free_entries(collect_ctx.entries, collect_ctx.count);\n+\t\t\treturn ret ?: collect_ctx.err;\n+\t\t}\n+\t}\n+\n+\t/* special case: add \"source\" option if filesystem didn't add one*/\n+\tif (!collect_ctx.count \u0026\u0026 collect_ctx.is_source) {\n+\t\tstruct metafs_option_entry *ent __free(kfree) = kzalloc_obj(*ent);\n+\n+\t\tif (!ent)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tent-\u003evalue = kstrdup(sb-\u003es_devname, GFP_KERNEL);\n+\t\tif (!ent-\u003evalue)\n+\t\t\treturn -ENOMEM;\n+\n+\t\tcollect_ctx.entries = no_free_ptr(ent);\n+\t\tcollect_ctx.count = 1;\n+\t}\n+\n+\t*entries_out = collect_ctx.entries;\n+\t*count_out = collect_ctx.count;\n+\treturn 0;\n+}\n+\n+/*\n+ * metafs symlink operations\n+ */\n+static const char *metafs_get_link(struct dentry *dentry, struct inode *inode,\n+\t\t\t\t   struct delayed_call *done)\n+{\n+\tstruct metafs_dentry_info *info;\n+\n+\tif (!dentry)\n+\t\treturn ERR_PTR(-ECHILD);\n+\n+\tinfo = dentry-\u003ed_fsdata;\n+\tif (info-\u003esymlink.path.mnt) {\n+\t\tint error;\n+\n+\t\tpath_get(\u0026info-\u003esymlink.path);\n+\t\terror = nd_jump_link(\u0026info-\u003esymlink.path);\n+\t\treturn ERR_PTR(error);\n+\t}\n+\n+\treturn ERR_PTR(-ELOOP);\n+}\n+\n+static int metafs_readlink(struct dentry *dentry, char __user *buffer,\n+\t\t\t   int buflen)\n+{\n+\tstruct metafs_dentry_info *info = dentry-\u003ed_fsdata;\n+\tconst char *value = info-\u003esymlink.value;\n+\n+\tif (!value)\n+\t\tvalue = \"\";\n+\n+\treturn readlink_copy(buffer, buflen, value, strlen(value));\n+}\n+\n+static const struct inode_operations metafs_symlink_iops = {\n+\t.get_link\t= metafs_get_link,\n+\t.readlink\t= metafs_readlink,\n+};\n+\n+/*\n+ * Dentry operations - cleanup\n+ */\n+static void metafs_d_release(struct dentry *dentry)\n+{\n+\tstruct metafs_dentry_info *info = dentry-\u003ed_fsdata;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tswitch (info-\u003etype) {\n+\tcase METAFS_ROOT:\n+\t\tfput(info-\u003eroot.base);\n+\t\tbreak;\n+\tcase METAFS_OPTION_VALUES:\n+\t\tmetafs_free_entries(info-\u003eopt_values.entries,\n+\t\t\t\t    info-\u003eopt_values.count);\n+\t\tbreak;\n+\tcase METAFS_SYMLINK:\n+\t\tkfree(info-\u003esymlink.value);\n+\t\tif (info-\u003esymlink.path.mnt)\n+\t\t\tpath_put(\u0026info-\u003esymlink.path);\n+\t\tbreak;\n+\tdefault:\n+\t\tWARN_ON(1);\n+\t}\n+\tkfree(info);\n+}\n+\n+static const struct dentry_operations metafs_dentry_ops = {\n+\t.d_release = metafs_d_release,\n+};\n+\n+/*\n+ * metafs directory operations - forward declarations\n+ */\n+static struct file *metafs_get_base_file(struct dentry *dentry);\n+static struct dentry *metafs_root_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t unsigned int flags);\n+static struct dentry *metafs_mount_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t  unsigned int flags);\n+static struct dentry *metafs_options_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t    unsigned int flags);\n+static struct dentry *metafs_option_values_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t\t  unsigned int flags);\n+\n+static const struct inode_operations metafs_root_dir_iops = {\n+\t.lookup\t\t= metafs_root_lookup,\n+};\n+\n+static const struct inode_operations metafs_mount_dir_iops = {\n+\t.lookup\t\t= metafs_mount_lookup,\n+};\n+\n+static const struct inode_operations metafs_options_dir_iops = {\n+\t.lookup\t\t= metafs_options_lookup,\n+};\n+\n+static const struct inode_operations metafs_option_values_dir_iops = {\n+\t.lookup\t\t= metafs_option_values_lookup,\n+};\n+\n+/*\n+ * Create a new inode in metafs\n+ */\n+static struct inode *metafs_get_inode(struct super_block *sb, umode_t mode,\n+\t\t\t\t      const struct inode_operations *iops)\n+{\n+\tstruct inode *inode = new_inode(sb);\n+\n+\tif (!inode)\n+\t\treturn NULL;\n+\n+\tinode-\u003ei_ino = get_next_ino();\n+\tinode-\u003ei_mode = mode;\n+\tsimple_inode_init_ts(inode);\n+\n+\tif (S_ISDIR(mode)) {\n+\t\tinode-\u003ei_op = iops;\n+\t\tinc_nlink(inode);\n+\t} else if (S_ISLNK(mode)) {\n+\t\tinode-\u003ei_op = \u0026metafs_symlink_iops;\n+\t}\n+\n+\treturn inode;\n+}\n+\n+/*\n+ * Lookup in root directory (/)\n+ * Only \"mount\" is valid here\n+ */\n+static struct dentry *metafs_root_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t unsigned int flags)\n+{\n+\tconst char *name = dentry-\u003ed_name.name;\n+\tstruct inode *inode;\n+\n+\tif (strcmp(name, \"mount\") != 0)\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\tinode = metafs_get_inode(dir-\u003ei_sb, S_IFDIR | 0555, \u0026metafs_mount_dir_iops);\n+\tif (!inode)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\td_add(dentry, inode);\n+\td_mark_dontcache(inode);\n+\treturn NULL;\n+}\n+\n+/*\n+ * Lookup in /mount directory\n+ * Only \"options\" is valid here\n+ */\n+static struct dentry *metafs_mount_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t  unsigned int flags)\n+{\n+\tconst char *name = dentry-\u003ed_name.name;\n+\tstruct inode *inode;\n+\n+\tif (strcmp(name, \"options\") != 0)\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\tinode = metafs_get_inode(dir-\u003ei_sb, S_IFDIR | 0555, \u0026metafs_options_dir_iops);\n+\tif (!inode)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\td_add(dentry, inode);\n+\td_mark_dontcache(inode);\n+\treturn NULL;\n+}\n+\n+/*\n+ * Lookup in /mount/options directory\n+ * Collect values for the requested option name\n+ */\n+static struct dentry *metafs_options_lookup(struct inode *dir, struct dentry *dentry,\n+\t\t\t\t\t    unsigned int flags)\n+{\n+\tconst char *target_name = dentry-\u003ed_name.name;\n+\tstruct file *base;\n+\tstruct metafs_dentry_info *info;\n+\tstruct metafs_option_entry *entries = NULL;\n+\tint count = 0;\n+\tstruct inode *inode;\n+\tint ret;\n+\n+\tbase = metafs_get_base_file(dentry-\u003ed_parent);\n+\tif (!base)\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\tret = metafs_get_option_values(base, target_name, \u0026entries, \u0026count);\n+\tif (ret)\n+\t\treturn ERR_PTR(ret);\n+\n+\tif (count == 0) {\n+\t\tkfree(entries);\n+\t\treturn ERR_PTR(-ENOENT);\n+\t}\n+\n+\tinfo = kzalloc(sizeof(*info), GFP_KERNEL);\n+\tif (!info) {\n+\t\tmetafs_free_entries(entries, count);\n+\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n+\tinfo-\u003etype = METAFS_OPTION_VALUES;\n+\tinfo-\u003eopt_values.entries = entries;\n+\tinfo-\u003eopt_values.count = count;\n+\n+\tinode = metafs_get_inode(dir-\u003ei_sb, S_IFDIR | 0555, \u0026metafs_option_values_dir_iops);\n+\tif (!inode) {\n+\t\tmetafs_free_entries(entries, count);\n+\t\tkfree(info);\n+\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n+\tdentry-\u003ed_fsdata = info;\n+\td_add(dentry, inode);\n+\td_mark_dontcache(inode);\n+\treturn NULL;\n+}\n+\n+/*\n+ * Lookup in option-specific directory (e.g., /mount/options/lowerdir/)\n+ * Return numbered entries (0, 1, 2, ...) as symlinks\n+ */\n+static struct dentry *metafs_option_values_lookup(struct inode *dir,\n+\t\t\t\t\t\t  struct dentry *dentry,\n+\t\t\t\t\t\t  unsigned int flags)\n+{\n+\tconst char *target_name = dentry-\u003ed_name.name;\n+\tstruct metafs_dentry_info *parent_info, *info;\n+\tstruct metafs_option_entry *entry;\n+\tstruct inode *inode;\n+\tunsigned long index;\n+\tint err;\n+\n+\tparent_info = dentry-\u003ed_parent-\u003ed_fsdata;\n+\tif (WARN_ON(!parent_info || parent_info-\u003etype != METAFS_OPTION_VALUES))\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\tif (target_name[0] == '0' \u0026\u0026 target_name[1])\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\terr = kstrtoul(target_name, 10, \u0026index);\n+\tif (err || index \u003e= parent_info-\u003eopt_values.count)\n+\t\treturn ERR_PTR(-ENOENT);\n+\n+\tentry = \u0026parent_info-\u003eopt_values.entries[index];\n+\n+\tinfo = kzalloc(sizeof(*info), GFP_KERNEL);\n+\tif (!info)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tinfo-\u003etype = METAFS_SYMLINK;\n+\tif (entry-\u003evalue) {\n+\t\tinfo-\u003esymlink.value = kstrdup(entry-\u003evalue, GFP_KERNEL);\n+\t\tif (!info-\u003esymlink.value) {\n+\t\t\tkfree(info);\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\t\t}\n+\t}\n+\tif (entry-\u003epath.mnt) {\n+\t\tinfo-\u003esymlink.path = entry-\u003epath;\n+\t\tpath_get(\u0026info-\u003esymlink.path);\n+\t}\n+\n+\tinode = metafs_get_inode(dir-\u003ei_sb, S_IFLNK | 0777, NULL);\n+\tif (!inode) {\n+\t\tkfree(info-\u003esymlink.value);\n+\t\tif (info-\u003esymlink.path.mnt)\n+\t\t\tpath_put(\u0026info-\u003esymlink.path);\n+\t\tkfree(info);\n+\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n+\tdentry-\u003ed_fsdata = info;\n+\td_add(dentry, inode);\n+\td_mark_dontcache(inode);\n+\treturn NULL;\n+}\n+\n+/*\n+ * Helper to get base file from any metafs dentry\n+ */\n+static struct file *metafs_get_base_file(struct dentry *dentry)\n+{\n+\twhile (dentry) {\n+\t\tstruct metafs_dentry_info *info = dentry-\u003ed_fsdata;\n+\n+\t\tif (info \u0026\u0026 info-\u003etype == METAFS_ROOT)\n+\t\t\treturn info-\u003eroot.base;\n+\t\tif (dentry == dentry-\u003ed_parent)\n+\t\t\tbreak;\n+\t\tdentry = dentry-\u003ed_parent;\n+\t}\n+\treturn NULL;\n+}\n+\n+/*\n+ * Create the root directory structure for a metadata open\n+ */\n+static struct dentry *metafs_create_root(struct file *base)\n+{\n+\tstruct inode *inode;\n+\tstruct dentry *root;\n+\tstruct super_block *sb = metafs_mnt-\u003emnt_sb;\n+\tstruct metafs_dentry_info *info;\n+\n+\tinfo = kzalloc(sizeof(*info), GFP_KERNEL);\n+\tif (!info)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tinfo-\u003etype = METAFS_ROOT;\n+\tinfo-\u003eroot.base = get_file(base);\n+\n+\tinode = metafs_get_inode(sb, S_IFDIR | 0555, \u0026metafs_root_dir_iops);\n+\tif (!inode) {\n+\t\tfput(info-\u003eroot.base);\n+\t\tkfree(info);\n+\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n+\troot = d_alloc_anon(sb);\n+\tif (!root) {\n+\t\tiput(inode);\n+\t\tfput(info-\u003eroot.base);\n+\t\tkfree(info);\n+\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n+\t/* Store the info in d_fsdata before instantiating */\n+\troot-\u003ed_fsdata = info;\n+\td_instantiate(root, inode);\n+\td_mark_dontcache(inode);\n+\n+\treturn root;\n+}\n+\n+/*\n+ * Get metadata root path for a base file.\n+ *\n+ * This is called from path_init() when O_ALT is used.\n+ * It returns a path in the metafs that serves as the root\n+ * for the metadata lookup.\n+ */\n+int metafs_get_root(struct file *base, struct path *path)\n+{\n+\tstruct dentry *root;\n+\n+\tif (!base)\n+\t\treturn -EBADF;\n+\n+\t/* Create the metadata root for this base file */\n+\troot = metafs_create_root(base);\n+\tif (IS_ERR(root))\n+\t\treturn PTR_ERR(root);\n+\n+\t/*\n+\t * Return the path to the metadata root.\n+\t * Normal VFS lookup will take over from here.\n+\t */\n+\tpath-\u003emnt = mntget(metafs_mnt);\n+\tpath-\u003edentry = root;\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(metafs_get_root);\n+\n+static int __init metafs_init(void)\n+{\n+\tint err;\n+\n+\terr = register_filesystem(\u0026metafs_type);\n+\tif (err)\n+\t\treturn err;\n+\n+\tmetafs_mnt = kern_mount(\u0026metafs_type);\n+\tif (IS_ERR(metafs_mnt)) {\n+\t\terr = PTR_ERR(metafs_mnt);\n+\t\tunregister_filesystem(\u0026metafs_type);\n+\t\treturn err;\n+\t}\n+\tset_default_d_op(metafs_mnt-\u003emnt_sb, \u0026metafs_dentry_ops);\n+\n+\treturn 0;\n+}\n+\n+fs_initcall(metafs_init);\ndiff --git a/fs/metafs/metafs.h b/fs/metafs/metafs.h\nnew file mode 100644\nindex 00000000000000..d257714260a7fb\n--- /dev/null\n+++ b/fs/metafs/metafs.h\n@@ -0,0 +1,15 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_METAFS_H\n+#define _LINUX_METAFS_H\n+\n+struct path;\n+struct file;\n+\n+/*\n+ * Get metadata root path for a base file.\n+ * Called from path_init() when O_ALT flag is used.\n+ * Returns 0 on success with path filled in, -errno on error.\n+ */\n+int metafs_get_root(struct file *base, struct path *path);\n+\n+#endif /* _LINUX_METAFS_H */\ndiff --git a/fs/mount.h b/fs/mount.h\nindex 94fcc306d21e7d..69a953bc112b36 100644\n--- a/fs/mount.h\n+++ b/fs/mount.h\n@@ -65,7 +65,6 @@ struct mount {\n \tstruct mount * __aligned(1) *mnt_pprev_for_sb;\n \t\t\t\t\t/* except that LSB of pprev is stolen */\n #define WRITE_HOLD 1\t\t\t/* ... for use by mnt_hold_writers() */\n-\tconst char *mnt_devname;\t/* Name of device e.g. /dev/dsk/hda1 */\n \tstruct list_head mnt_list;\n \tstruct list_head mnt_expire;\t/* link in fs-specific expiry list */\n \tstruct list_head mnt_share;\t/* circular list of shared mounts */\ndiff --git a/fs/namei.c b/fs/namei.c\nindex 19ce43c9a6e662..437a14c432e67b 100644\n--- a/fs/namei.c\n+++ b/fs/namei.c\n@@ -45,6 +45,7 @@\n \n #include \"internal.h\"\n #include \"mount.h\"\n+#include \"metafs/metafs.h\"\n \n /* [Feb-1997 T. Schoebel-Theuer]\n  * Fundamental changes in the pathname lookup mechanisms (namei)\n@@ -2753,6 +2754,21 @@ static const char *path_init(struct nameidata *nd, unsigned flags)\n \t\t\t\treturn ERR_PTR(-ENOENT);\n \t\t}\n \n+\t\tif (unlikely(flags \u0026 LOOKUP_ALT)) {\n+\t\t\tif (flags \u0026 LOOKUP_RCU)\n+\t\t\t\treturn ERR_PTR(-ECHILD);\n+\n+\t\t\t/* Handle O_ALT: jump to metafs root */\n+\t\t\terror = metafs_get_root(fd_file(f), \u0026nd-\u003epath);\n+\t\t\tif (error)\n+\t\t\t\treturn ERR_PTR(error);\n+\n+\t\t\tnd-\u003einode = nd-\u003epath.dentry-\u003ed_inode;\n+\t\t\tnd-\u003estate |= ND_JUMPED;\n+\n+\t\t\treturn s;\n+\t\t}\n+\n \t\tdentry = fd_file(f)-\u003ef_path.dentry;\n \n \t\tif (*s \u0026\u0026 unlikely(!d_can_lookup(dentry)))\ndiff --git a/fs/namespace.c b/fs/namespace.c\nindex 3d5cd5bf3b0537..62cc71c401a2ac 100644\n--- a/fs/namespace.c\n+++ b/fs/namespace.c\n@@ -282,7 +282,7 @@ int mnt_get_count(struct mount *mnt)\n #endif\n }\n \n-static struct mount *alloc_vfsmnt(const char *name)\n+static struct mount *alloc_vfsmnt(void)\n {\n \tstruct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);\n \tif (mnt) {\n@@ -292,18 +292,10 @@ static struct mount *alloc_vfsmnt(const char *name)\n \t\tif (err)\n \t\t\tgoto out_free_cache;\n \n-\t\tif (name)\n-\t\t\tmnt-\u003emnt_devname = kstrdup_const(name,\n-\t\t\t\t\t\t\t GFP_KERNEL_ACCOUNT);\n-\t\telse\n-\t\t\tmnt-\u003emnt_devname = \"none\";\n-\t\tif (!mnt-\u003emnt_devname)\n-\t\t\tgoto out_free_id;\n-\n #ifdef CONFIG_SMP\n \t\tmnt-\u003emnt_pcp = alloc_percpu(struct mnt_pcp);\n \t\tif (!mnt-\u003emnt_pcp)\n-\t\t\tgoto out_free_devname;\n+\t\t\tgoto out_free_id;\n \n \t\tthis_cpu_add(mnt-\u003emnt_pcp-\u003emnt_count, 1);\n #else\n@@ -328,11 +320,9 @@ static struct mount *alloc_vfsmnt(const char *name)\n \treturn mnt;\n \n #ifdef CONFIG_SMP\n-out_free_devname:\n-\tkfree_const(mnt-\u003emnt_devname);\n-#endif\n out_free_id:\n \tmnt_free_id(mnt);\n+#endif\n out_free_cache:\n \tkmem_cache_free(mnt_cache, mnt);\n \treturn NULL;\n@@ -725,7 +715,6 @@ int sb_prepare_remount_readonly(struct super_block *sb)\n static void free_vfsmnt(struct mount *mnt)\n {\n \tmnt_idmap_put(mnt_idmap(\u0026mnt-\u003emnt));\n-\tkfree_const(mnt-\u003emnt_devname);\n #ifdef CONFIG_SMP\n \tfree_percpu(mnt-\u003emnt_pcp);\n #endif\n@@ -1180,7 +1169,7 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)\n \tif (!fc-\u003eroot)\n \t\treturn ERR_PTR(-EINVAL);\n \n-\tmnt = alloc_vfsmnt(fc-\u003esource);\n+\tmnt = alloc_vfsmnt();\n \tif (!mnt)\n \t\treturn ERR_PTR(-ENOMEM);\n \n@@ -1248,7 +1237,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,\n \tstruct mount *mnt;\n \tint err;\n \n-\tmnt = alloc_vfsmnt(old-\u003emnt_devname);\n+\tmnt = alloc_vfsmnt();\n \tif (!mnt)\n \t\treturn ERR_PTR(-ENOMEM);\n \n@@ -2957,16 +2946,12 @@ static int do_change_type(const struct path *path, int ms_flags)\n static inline bool may_copy_tree(const struct path *path)\n {\n \tstruct mount *mnt = real_mount(path-\u003emnt);\n-\tconst struct dentry_operations *d_op;\n \n \tif (check_mnt(mnt))\n \t\treturn true;\n \n-\td_op = path-\u003edentry-\u003ed_op;\n-\tif (d_op == \u0026ns_dentry_operations)\n-\t\treturn true;\n-\n-\tif (d_op == \u0026pidfs_dentry_operations)\n+\t/* Make an exception for internal mounts explicitly marked as clonable */\n+\tif (mnt-\u003emnt_ns == MNT_NS_INTERNAL \u0026\u0026 mnt-\u003emnt.mnt_flags \u0026 MNT_CLONABLE)\n \t\treturn true;\n \n \tif (!is_mounted(path-\u003emnt))\n@@ -3201,7 +3186,7 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned\n \n \tif (flags \u0026 ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE |\n \t\t      AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE |\n-\t\t      OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE))\n+\t\t      OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE | OPEN_TREE_ALT))\n \t\treturn ERR_PTR(-EINVAL);\n \n \tif ((flags \u0026 (AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE)) ==\n@@ -3215,6 +3200,8 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned\n \t\tlookup_flags \u0026= ~LOOKUP_AUTOMOUNT;\n \tif (flags \u0026 AT_SYMLINK_NOFOLLOW)\n \t\tlookup_flags \u0026= ~LOOKUP_FOLLOW;\n+\tif (flags \u0026 OPEN_TREE_ALT)\n+\t\tlookup_flags |= LOOKUP_ALT;\n \n \t/*\n \t * If we create a new mount namespace with the cloned mount tree we\n@@ -5387,7 +5374,6 @@ static void statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)\n static int statmount_sb_source(struct kstatmount *s, struct seq_file *seq)\n {\n \tstruct super_block *sb = s-\u003emnt-\u003emnt_sb;\n-\tstruct mount *r = real_mount(s-\u003emnt);\n \n \tif (sb-\u003es_op-\u003eshow_devname) {\n \t\tsize_t start = seq-\u003ecount;\n@@ -5405,7 +5391,7 @@ static int statmount_sb_source(struct kstatmount *s, struct seq_file *seq)\n \t\tseq-\u003ecount = start;\n \t\tseq_commit(seq, string_unescape_inplace(seq-\u003ebuf + start, UNESCAPE_OCTAL));\n \t} else {\n-\t\tseq_puts(seq, r-\u003emnt_devname);\n+\t\tseq_puts(seq, sb-\u003es_devname);\n \t}\n \treturn 0;\n }\n@@ -5416,6 +5402,40 @@ static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)\n \ts-\u003esm.mnt_ns_id = ns-\u003ens.ns_id;\n }\n \n+void sb_add_option(struct sb_opt_ctx *ctx, const char *name, const char *value)\n+{\n+\tsb_add_option_path(ctx, name, value, NULL);\n+}\n+\n+void sb_add_option_path(struct sb_opt_ctx *ctx, const char *name, const char *value,\n+\t\t\tconst struct path *path)\n+{\n+\tctx-\u003eactor(ctx, name, value, path);\n+}\n+\n+static void do_show_option(struct sb_opt_ctx *ctx, const char *name, const char *value,\n+\t\t\t   const struct path *path)\n+{\n+\tif (strcmp(name, \"source\") != 0)\n+\t\tseq_show_option(ctx-\u003edata, name, value);\n+}\n+\n+int mnt_show_options(struct seq_file *seq, struct vfsmount *mnt)\n+{\n+\tstruct super_block *sb = mnt-\u003emnt_sb;\n+\n+\tif (sb-\u003es_op-\u003eshow_options)\n+\t\treturn sb-\u003es_op-\u003eshow_options(seq, mnt-\u003emnt_root);\n+\n+\tif (sb-\u003es_op-\u003eget_options) {\n+\t\tstruct sb_opt_ctx ctx = { .data = seq, .actor = do_show_option };\n+\n+\t\treturn sb-\u003es_op-\u003eget_options(\u0026ctx, mnt-\u003emnt_root);\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)\n {\n \tstruct vfsmount *mnt = s-\u003emnt;\n@@ -5427,11 +5447,9 @@ static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)\n \tif (err)\n \t\treturn err;\n \n-\tif (sb-\u003es_op-\u003eshow_options) {\n-\t\terr = sb-\u003es_op-\u003eshow_options(seq, mnt-\u003emnt_root);\n-\t\tif (err)\n-\t\t\treturn err;\n-\t}\n+\terr = mnt_show_options(seq, mnt);\n+\tif (err)\n+\t\treturn err;\n \n \tif (unlikely(seq_has_overflowed(seq)))\n \t\treturn -EAGAIN;\n@@ -5479,14 +5497,10 @@ static inline int statmount_opt_process(struct seq_file *seq, size_t start)\n static int statmount_opt_array(struct kstatmount *s, struct seq_file *seq)\n {\n \tstruct vfsmount *mnt = s-\u003emnt;\n-\tstruct super_block *sb = mnt-\u003emnt_sb;\n \tsize_t start = seq-\u003ecount;\n \tint err;\n \n-\tif (!sb-\u003es_op-\u003eshow_options)\n-\t\treturn 0;\n-\n-\terr = sb-\u003es_op-\u003eshow_options(seq, mnt-\u003emnt_root);\n+\terr = mnt_show_options(seq, mnt);\n \tif (err)\n \t\treturn err;\n \ndiff --git a/fs/nsfs.c b/fs/nsfs.c\nindex c3b6ae76594a81..e110130d0760e5 100644\n--- a/fs/nsfs.c\n+++ b/fs/nsfs.c\n@@ -684,6 +684,7 @@ void __init nsfs_init(void)\n \tif (IS_ERR(nsfs_mnt))\n \t\tpanic(\"can't set nsfs up\\n\");\n \tnsfs_mnt-\u003emnt_sb-\u003es_flags \u0026= ~SB_NOUSER;\n+\tnsfs_mnt-\u003emnt_flags |= MNT_CLONABLE;\n \tnsfs_root_path.mnt = nsfs_mnt;\n \tnsfs_root_path.dentry = nsfs_mnt-\u003emnt_root;\n }\ndiff --git a/fs/open.c b/fs/open.c\nindex 408925d7bd0b77..3a081a9e306243 100644\n--- a/fs/open.c\n+++ b/fs/open.c\n@@ -1131,7 +1131,8 @@ struct file *kernel_file_open(const struct path *path, int flags,\n EXPORT_SYMBOL_GPL(kernel_file_open);\n \n #define WILL_CREATE(flags)\t(flags \u0026 (O_CREAT | __O_TMPFILE))\n-#define O_PATH_FLAGS\t\t(O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH)\n+#define O_PATH_FLAGS\t\t(O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH | \\\n+\t\t\t\t O_ALT)\n \n inline struct open_how build_open_how(int flags, umode_t mode)\n {\n@@ -1283,6 +1284,8 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)\n \t\tlookup_flags |= LOOKUP_FOLLOW;\n \tif (flags \u0026 O_EMPTYPATH)\n \t\tlookup_flags |= LOOKUP_EMPTY;\n+\tif (flags \u0026 O_ALT)\n+\t\tlookup_flags |= LOOKUP_ALT;\n \n \tif (how-\u003eresolve \u0026 RESOLVE_NO_XDEV)\n \t\tlookup_flags |= LOOKUP_NO_XDEV;\ndiff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c\nindex c93fcaa45d4a36..c5a9e858e3665e 100644\n--- a/fs/overlayfs/params.c\n+++ b/fs/overlayfs/params.c\n@@ -1046,10 +1046,11 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,\n  * Prints the mount options for a given superblock.\n  * Returns zero; does not fail.\n  */\n-int ovl_show_options(struct seq_file *m, struct dentry *dentry)\n+int ovl_get_options(struct sb_opt_ctx *ctx, struct dentry *dentry)\n {\n \tstruct super_block *sb = dentry-\u003ed_sb;\n \tstruct ovl_fs *ofs = OVL_FS(sb);\n+\tstruct path upperpath;\n \tsize_t nr, nr_merged_lower, nr_lower = 0;\n \tchar **lowerdirs = ofs-\u003econfig.lowerdirs;\n \n@@ -1062,43 +1063,47 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)\n \t * with the new lowerdir+ and datadir+ mount options.\n \t */\n \tif (lowerdirs[0]) {\n-\t\tseq_show_option(m, \"lowerdir\", lowerdirs[0]);\n+\t\tsb_add_option(ctx, \"lowerdir\", lowerdirs[0]);\n \t} else {\n \t\tnr_lower = ofs-\u003enumlayer;\n \t\tnr_merged_lower = nr_lower - ofs-\u003enumdatalayer;\n \t}\n \tfor (nr = 1; nr \u003c nr_lower; nr++) {\n+\t\tstruct path lowerpath = {\n+\t\t\t.mnt = ofs-\u003elayers[nr].mnt,\n+\t\t\t.dentry = ofs-\u003elayers[nr].mnt-\u003emnt_root,\n+\t\t};\n \t\tif (nr \u003c nr_merged_lower)\n-\t\t\tseq_show_option(m, \"lowerdir+\", lowerdirs[nr]);\n+\t\t\tsb_add_option_path(ctx, \"lowerdir+\", lowerdirs[nr],\n+\t\t\t\t\t   \u0026lowerpath);\n \t\telse\n-\t\t\tseq_show_option(m, \"datadir+\", lowerdirs[nr]);\n+\t\t\tsb_add_option_path(ctx, \"datadir+\", lowerdirs[nr],\n+\t\t\t\t\t   \u0026lowerpath);\n \t}\n \tif (ofs-\u003econfig.upperdir) {\n-\t\tseq_show_option(m, \"upperdir\", ofs-\u003econfig.upperdir);\n-\t\tseq_show_option(m, \"workdir\", ofs-\u003econfig.workdir);\n+\t\tovl_path_upper(sb-\u003es_root, \u0026upperpath);\n+\t\tsb_add_option_path(ctx, \"upperdir\", ofs-\u003econfig.upperdir, \u0026upperpath);\n+\t\tsb_add_option(ctx, \"workdir\", ofs-\u003econfig.workdir);\n \t}\n \tif (ofs-\u003econfig.default_permissions)\n-\t\tseq_puts(m, \",default_permissions\");\n+\t\tsb_add_option(ctx, \"default_permissions\", NULL);\n \tif (ofs-\u003econfig.redirect_mode != ovl_redirect_mode_def())\n-\t\tseq_printf(m, \",redirect_dir=%s\",\n-\t\t\t   ovl_redirect_mode(\u0026ofs-\u003econfig));\n+\t\tsb_add_option(ctx, \"redirect_dir\", ovl_redirect_mode(\u0026ofs-\u003econfig));\n \tif (ofs-\u003econfig.index != ovl_index_def)\n-\t\tseq_printf(m, \",index=%s\", str_on_off(ofs-\u003econfig.index));\n+\t\tsb_add_option(ctx, \"index\", str_on_off(ofs-\u003econfig.index));\n \tif (ofs-\u003econfig.uuid != ovl_uuid_def())\n-\t\tseq_printf(m, \",uuid=%s\", ovl_uuid_mode(\u0026ofs-\u003econfig));\n+\t\tsb_add_option(ctx, \"uuid\", ovl_uuid_mode(\u0026ofs-\u003econfig));\n \tif (ofs-\u003econfig.nfs_export != ovl_nfs_export_def)\n-\t\tseq_printf(m, \",nfs_export=%s\",\n-\t\t\t   str_on_off(ofs-\u003econfig.nfs_export));\n+\t\tsb_add_option(ctx, \"nfs_export\", str_on_off(ofs-\u003econfig.nfs_export));\n \tif (ofs-\u003econfig.xino != ovl_xino_def() \u0026\u0026 !ovl_same_fs(ofs))\n-\t\tseq_printf(m, \",xino=%s\", ovl_xino_mode(\u0026ofs-\u003econfig));\n+\t\tsb_add_option(ctx, \"xino\", ovl_xino_mode(\u0026ofs-\u003econfig));\n \tif (ofs-\u003econfig.metacopy != ovl_metacopy_def)\n-\t\tseq_printf(m, \",metacopy=%s\", str_on_off(ofs-\u003econfig.metacopy));\n+\t\tsb_add_option(ctx, \"metacopy\", str_on_off(ofs-\u003econfig.metacopy));\n \tif (ofs-\u003econfig.fsync_mode != ovl_fsync_mode_def())\n-\t\tseq_printf(m, \",fsync=%s\", ovl_fsync_mode(\u0026ofs-\u003econfig));\n+\t\tsb_add_option(ctx, \"fsync\", ovl_fsync_mode(\u0026ofs-\u003econfig));\n \tif (ofs-\u003econfig.userxattr)\n-\t\tseq_puts(m, \",userxattr\");\n+\t\tsb_add_option(ctx, \"userxattr\", NULL);\n \tif (ofs-\u003econfig.verity_mode != ovl_verity_mode_def())\n-\t\tseq_printf(m, \",verity=%s\",\n-\t\t\t   ovl_verity_mode(\u0026ofs-\u003econfig));\n+\t\tsb_add_option(ctx, \"verity\", ovl_verity_mode(\u0026ofs-\u003econfig));\n \treturn 0;\n }\ndiff --git a/fs/overlayfs/params.h b/fs/overlayfs/params.h\nindex ffd53cdd84827c..2cf9c4f188a9cb 100644\n--- a/fs/overlayfs/params.h\n+++ b/fs/overlayfs/params.h\n@@ -40,5 +40,5 @@ int ovl_init_fs_context(struct fs_context *fc);\n void ovl_free_fs(struct ovl_fs *ofs);\n int ovl_fs_params_verify(const struct ovl_fs_context *ctx,\n \t\t\t struct ovl_config *config);\n-int ovl_show_options(struct seq_file *m, struct dentry *dentry);\n+int ovl_get_options(struct sb_opt_ctx *ctx, struct dentry *dentry);\n const char *ovl_xino_mode(struct ovl_config *config);\ndiff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c\nindex 60f0b7ceef0a6d..4b57b2db085225 100644\n--- a/fs/overlayfs/super.c\n+++ b/fs/overlayfs/super.c\n@@ -302,7 +302,7 @@ static const struct super_operations ovl_super_operations = {\n \t.put_super\t= ovl_put_super,\n \t.sync_fs\t= ovl_sync_fs,\n \t.statfs\t\t= ovl_statfs,\n-\t.show_options\t= ovl_show_options,\n+\t.get_options\t= ovl_get_options,\n };\n \n #define OVL_WORKDIR_NAME \"work\"\n@@ -529,6 +529,7 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,\n \n \t/* Don't inherit atime flags */\n \tupper_mnt-\u003emnt_flags \u0026= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);\n+\tupper_mnt-\u003emnt_flags |= MNT_CLONABLE;\n \tupper_layer-\u003emnt = upper_mnt;\n \tupper_layer-\u003eidx = 0;\n \tupper_layer-\u003efsid = 0;\n@@ -1105,7 +1106,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,\n \t\t * Make lower layers R/O.  That way fchmod/fchown on lower file\n \t\t * will fail instead of modifying lower fs.\n \t\t */\n-\t\tmnt-\u003emnt_flags |= MNT_READONLY | MNT_NOATIME;\n+\t\tmnt-\u003emnt_flags |= MNT_READONLY | MNT_NOATIME | MNT_CLONABLE;\n \n \t\tlayers[ofs-\u003enumlayer].trap = trap;\n \t\tlayers[ofs-\u003enumlayer].mnt = mnt;\ndiff --git a/fs/pidfs.c b/fs/pidfs.c\nindex aaa609ddab0443..3843acd6594de0 100644\n--- a/fs/pidfs.c\n+++ b/fs/pidfs.c\n@@ -840,7 +840,7 @@ static char *pidfs_dname(struct dentry *dentry, char *buffer, int buflen)\n \treturn dynamic_dname(buffer, buflen, \"anon_inode:[pidfd]\");\n }\n \n-const struct dentry_operations pidfs_dentry_operations = {\n+static const struct dentry_operations pidfs_dentry_operations = {\n \t.d_dname\t= pidfs_dname,\n \t.d_prune\t= stashed_dentry_prune,\n };\n@@ -1151,6 +1151,8 @@ void __init pidfs_init(void)\n \tif (IS_ERR(pidfs_mnt))\n \t\tpanic(\"Failed to mount pidfs pseudo filesystem\");\n \n+\tpidfs_mnt-\u003emnt_flags |= MNT_CLONABLE;\n+\n \tpidfs_root_path.mnt = pidfs_mnt;\n \tpidfs_root_path.dentry = pidfs_mnt-\u003emnt_root;\n }\ndiff --git a/fs/proc_namespace.c b/fs/proc_namespace.c\nindex 5c555db68aa212..94bc4c9aa43f16 100644\n--- a/fs/proc_namespace.c\n+++ b/fs/proc_namespace.c\n@@ -98,21 +98,27 @@ static void show_type(struct seq_file *m, struct super_block *sb)\n \t}\n }\n \n+static int show_devname(struct seq_file *m, struct vfsmount *mnt)\n+{\n+\tstruct super_block *sb = mnt-\u003emnt_sb;\n+\n+\tif (sb-\u003es_op-\u003eshow_devname)\n+\t\treturn sb-\u003es_op-\u003eshow_devname(m, mnt-\u003emnt_root);\n+\n+\tmangle(m, sb-\u003es_devname);\n+\treturn 0;\n+}\n+\n static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)\n {\n \tstruct proc_mounts *p = m-\u003eprivate;\n-\tstruct mount *r = real_mount(mnt);\n \tstruct path mnt_path = { .dentry = mnt-\u003emnt_root, .mnt = mnt };\n \tstruct super_block *sb = mnt_path.dentry-\u003ed_sb;\n \tint err;\n \n-\tif (sb-\u003es_op-\u003eshow_devname) {\n-\t\terr = sb-\u003es_op-\u003eshow_devname(m, mnt_path.dentry);\n-\t\tif (err)\n-\t\t\tgoto out;\n-\t} else {\n-\t\tmangle(m, r-\u003emnt_devname);\n-\t}\n+\terr = show_devname(m, mnt);\n+\tif (err)\n+\t\tgoto out;\n \tseq_putc(m, ' ');\n \t/* mountpoints outside of chroot jail will give SEQ_SKIP on this */\n \terr = seq_path_root(m, \u0026mnt_path, \u0026p-\u003eroot, \" \\t\\n\\\\\");\n@@ -125,8 +131,7 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)\n \tif (err)\n \t\tgoto out;\n \tshow_vfsmnt_opts(m, mnt);\n-\tif (sb-\u003es_op-\u003eshow_options)\n-\t\terr = sb-\u003es_op-\u003eshow_options(m, mnt_path.dentry);\n+\terr = mnt_show_options(m, mnt);\n \tseq_puts(m, \" 0 0\\n\");\n out:\n \treturn err;\n@@ -172,19 +177,14 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)\n \tseq_puts(m, \" - \");\n \tshow_type(m, sb);\n \tseq_putc(m, ' ');\n-\tif (sb-\u003es_op-\u003eshow_devname) {\n-\t\terr = sb-\u003es_op-\u003eshow_devname(m, mnt-\u003emnt_root);\n-\t\tif (err)\n-\t\t\tgoto out;\n-\t} else {\n-\t\tmangle(m, r-\u003emnt_devname);\n-\t}\n+\terr = show_devname(m, mnt);\n+\tif (err)\n+\t\tgoto out;\n \tseq_puts(m, sb_rdonly(sb) ? \" ro\" : \" rw\");\n \terr = show_sb_opts(m, sb);\n \tif (err)\n \t\tgoto out;\n-\tif (sb-\u003es_op-\u003eshow_options)\n-\t\terr = sb-\u003es_op-\u003eshow_options(m, mnt-\u003emnt_root);\n+\terr = mnt_show_options(m, mnt);\n \tseq_putc(m, '\\n');\n out:\n \treturn err;\n@@ -193,21 +193,15 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)\n static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)\n {\n \tstruct proc_mounts *p = m-\u003eprivate;\n-\tstruct mount *r = real_mount(mnt);\n \tstruct path mnt_path = { .dentry = mnt-\u003emnt_root, .mnt = mnt };\n \tstruct super_block *sb = mnt_path.dentry-\u003ed_sb;\n \tint err;\n \n \t/* device */\n \tseq_puts(m, \"device \");\n-\tif (sb-\u003es_op-\u003eshow_devname) {\n-\t\terr = sb-\u003es_op-\u003eshow_devname(m, mnt_path.dentry);\n-\t\tif (err)\n-\t\t\tgoto out;\n-\t} else {\n-\t\tmangle(m, r-\u003emnt_devname);\n-\t}\n-\n+\terr = show_devname(m, mnt);\n+\tif (err)\n+\t\tgoto out;\n \t/* mount point */\n \tseq_puts(m, \" mounted on \");\n \t/* mountpoints outside of chroot jail will give SEQ_SKIP on this */\ndiff --git a/fs/super.c b/fs/super.c\nindex a8fd61136aafe5..e5531cb88f8917 100644\n--- a/fs/super.c\n+++ b/fs/super.c\n@@ -280,6 +280,7 @@ static void destroy_super_work(struct work_struct *work)\n \tsecurity_sb_free(s);\n \tput_user_ns(s-\u003es_user_ns);\n \tkfree(s-\u003es_subtype);\n+\tkfree_const(s-\u003es_devname);\n \tfor (int i = 0; i \u003c SB_FREEZE_LEVELS; i++)\n \t\tpercpu_free_rwsem(\u0026s-\u003es_writers.rw_sem[i]);\n \tkfree(s);\n@@ -315,7 +316,7 @@ static void destroy_unused_super(struct super_block *s)\n  *\treturns a pointer new superblock or %NULL if allocation had failed.\n  */\n static struct super_block *alloc_super(struct file_system_type *type, int flags,\n-\t\t\t\t       struct user_namespace *user_ns)\n+\t\t\t\t       struct user_namespace *user_ns, const char *name)\n {\n \tstruct super_block *s = kzalloc_obj(struct super_block);\n \tstatic const struct super_operations default_op;\n@@ -344,6 +345,13 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,\n \t */\n \tdown_write_nested(\u0026s-\u003es_umount, SINGLE_DEPTH_NESTING);\n \n+\tif (name)\n+\t\ts-\u003es_devname = kstrdup_const(name, GFP_KERNEL_ACCOUNT);\n+\telse\n+\t\ts-\u003es_devname = \"none\";\n+\tif (!s-\u003es_devname)\n+\t\tgoto fail;\n+\n \tif (security_sb_alloc(s))\n \t\tgoto fail;\n \n@@ -763,7 +771,7 @@ struct super_block *sget_fc(struct fs_context *fc,\n \t}\n \tif (!s) {\n \t\tspin_unlock(\u0026sb_lock);\n-\t\ts = alloc_super(fc-\u003efs_type, fc-\u003esb_flags, user_ns);\n+\t\ts = alloc_super(fc-\u003efs_type, fc-\u003esb_flags, user_ns, fc-\u003esource);\n \t\tif (!s)\n \t\t\treturn ERR_PTR(-ENOMEM);\n \t\tgoto retry;\ndiff --git a/include/linux/fcntl.h b/include/linux/fcntl.h\nindex 6ad6b9e7a226af..f942411c8ed7de 100644\n--- a/include/linux/fcntl.h\n+++ b/include/linux/fcntl.h\n@@ -11,7 +11,7 @@\n \t(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \\\n \t O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \\\n \t FASYNC\t| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \\\n-\t O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH)\n+\t O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH | O_ALT)\n \n /* List of all valid flags for openat2(2)'s how-\u003eflags argument. */\n #define VALID_OPENAT2_FLAGS\t(VALID_OPEN_FLAGS | OPENAT2_REGULAR)\ndiff --git a/include/linux/fs/super.h b/include/linux/fs/super.h\nindex 4056126781151e..6f0d778940f904 100644\n--- a/include/linux/fs/super.h\n+++ b/include/linux/fs/super.h\n@@ -237,4 +237,14 @@ int thaw_super(struct super_block *super, enum freeze_holder who,\n \n int sb_init_dio_done_wq(struct super_block *sb);\n \n+struct sb_opt_ctx {\n+\tvoid (*actor)(struct sb_opt_ctx *ctx, const char *name, const char *value,\n+\t\t      const struct path *path);\n+\tvoid *data;\n+};\n+\n+void sb_add_option(struct sb_opt_ctx *ctx, const char *name, const char *value);\n+void sb_add_option_path(struct sb_opt_ctx *ctx, const char *name, const char *value,\n+\t\t\tconst struct path *path);\n+\n #endif /* _LINUX_FS_SUPER_H */\ndiff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h\nindex ef7941e9dc79ac..f205759af1a633 100644\n--- a/include/linux/fs/super_types.h\n+++ b/include/linux/fs/super_types.h\n@@ -36,6 +36,7 @@ struct workqueue_struct;\n struct writeback_control;\n struct xattr_handler;\n struct fserror_event;\n+struct sb_opt_ctx;\n \n extern struct super_block *blockdev_superblock;\n \n@@ -99,6 +100,7 @@ struct super_operations {\n \tint (*statfs)(struct dentry *dentry, struct kstatfs *kstatfs);\n \tvoid (*umount_begin)(struct super_block *sb);\n \n+\tint (*get_options)(struct sb_opt_ctx *ctx, struct dentry *dentry);\n \tint (*show_options)(struct seq_file *seq, struct dentry *dentry);\n \tint (*show_devname)(struct seq_file *seq, struct dentry *dentry);\n \tint (*show_path)(struct seq_file *seq, struct dentry *dentry);\n@@ -283,6 +285,8 @@ struct super_block {\n \t */\n \tatomic_t\t\t\t\ts_isw_nr_in_flight;\n #endif\n+\t/* Name of device e.g. /dev/dsk/hda1 */\n+\tconst char \t\t\t\t*s_devname;\n } __randomize_layout;\n \n /*\ndiff --git a/include/linux/mount.h b/include/linux/mount.h\nindex acfe7ef86a1b3f..d071855ca4edec 100644\n--- a/include/linux/mount.h\n+++ b/include/linux/mount.h\n@@ -44,6 +44,7 @@ enum mount_flags {\n \tMNT_LOCKED\t\t= 0x800000,\n \tMNT_DOOMED\t\t= 0x1000000,\n \tMNT_SYNC_UMOUNT\t\t= 0x2000000,\n+\tMNT_CLONABLE\t\t= 0x4000000,\n \tMNT_UMOUNT\t\t= 0x8000000,\n \n \tMNT_USER_SETTABLE_MASK  = MNT_NOSUID | MNT_NODEV | MNT_NOEXEC\n@@ -52,7 +53,7 @@ enum mount_flags {\n \tMNT_ATIME_MASK = MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME,\n \n \tMNT_INTERNAL_FLAGS = MNT_INTERNAL | MNT_DOOMED |\n-\t\t\t     MNT_SYNC_UMOUNT | MNT_LOCKED\n+\t\t\t     MNT_SYNC_UMOUNT | MNT_LOCKED | MNT_CLONABLE\n };\n \n struct vfsmount {\ndiff --git a/include/linux/namei.h b/include/linux/namei.h\nindex ebe6e29f7e93ba..c21dc6536900cf 100644\n--- a/include/linux/namei.h\n+++ b/include/linux/namei.h\n@@ -25,7 +25,8 @@ enum { MAX_NESTED_LINKS = 8 };\n #define LOOKUP_RCU\t\tBIT(8)\t/* RCU pathwalk mode; semi-internal */\n #define LOOKUP_CACHED\t\tBIT(9) /* Only do cached lookup */\n #define LOOKUP_PARENT\t\tBIT(10)\t/* Looking up final parent in path */\n-/* 5 spare bits for pathwalk */\n+#define LOOKUP_ALT\t\tBIT(11) /* Look up in alternative namespace */\n+/* 4 spare bits for pathwalk */\n \n /* These tell filesystem methods that we are dealing with the final component... */\n #define LOOKUP_OPEN\t\tBIT(16)\t/* ... in open */\ndiff --git a/include/linux/pidfs.h b/include/linux/pidfs.h\nindex 0abf7da9ab236e..fab6f48ff20334 100644\n--- a/include/linux/pidfs.h\n+++ b/include/linux/pidfs.h\n@@ -15,7 +15,6 @@ void pidfs_exit(struct task_struct *tsk);\n #ifdef CONFIG_COREDUMP\n void pidfs_coredump(const struct coredump_params *cprm);\n #endif\n-extern const struct dentry_operations pidfs_dentry_operations;\n int pidfs_register_pid_gfp(struct pid *pid, gfp_t gfp);\n \n /**\ndiff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h\nindex 883cfd7de2effa..70dbbac1c52078 100644\n--- a/include/uapi/asm-generic/fcntl.h\n+++ b/include/uapi/asm-generic/fcntl.h\n@@ -96,6 +96,10 @@\n #define O_EMPTYPATH\t(1 \u003c\u003c 26)\t/* allow empty path */\n #endif\n \n+#ifndef O_ALT\n+#define O_ALT\t\t(1 \u003c\u003c 27)\t/* Open in alternative namespace */\n+#endif\n+\n /* a horrid kludge trying to make sure that this will fail on old kernels */\n #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)\n \ndiff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h\nindex 2204708dbf7a58..6e794d214cf983 100644\n--- a/include/uapi/linux/mount.h\n+++ b/include/uapi/linux/mount.h\n@@ -63,6 +63,7 @@\n  */\n #define OPEN_TREE_CLONE\t\t(1 \u003c\u003c 0)\t/* Clone the target tree and attach the clone */\n #define OPEN_TREE_NAMESPACE\t(1 \u003c\u003c 1)\t/* Clone the target tree into a new mount namespace */\n+#define OPEN_TREE_ALT\t\t(1 \u003c\u003c 2)\t/* Lookup in alternative namespace */\n #define OPEN_TREE_CLOEXEC\tO_CLOEXEC\t/* Close the file on execve() */\n \n /*\ndiff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py\nindex 65dd1bd129641d..5aaf2dd8db05a1 100644\n--- a/scripts/gdb/linux/proc.py\n+++ b/scripts/gdb/linux/proc.py\n@@ -174,9 +174,6 @@ values of that process namespace\"\"\"\n                   \"mount\", \"super_block\", \"devname\", \"pathname\", \"fstype\"))\n \n         for mnt in rbtree.rb_inorder_for_each_entry(namespace['mounts'], mount_ptr_type, \"mnt_node\"):\n-            devname = mnt['mnt_devname'].string()\n-            devname = devname if devname else \"none\"\n-\n             pathname = \"\"\n             parent = mnt\n             while True:\n@@ -190,6 +187,8 @@ values of that process namespace\"\"\"\n                 pathname = \"/\"\n \n             superblock = mnt['mnt']['mnt_sb']\n+            devname = superblock['s_devname'].string()\n+            devname = devname if devname else \"none\"\n             fstype = superblock['s_type']['name'].string()\n             s_flags = int(superblock['s_flags'])\n             m_flags = int(mnt['mnt']['mnt_flags'])\n"
}

2/1 2026/07/15 16:08 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "EROFS_FS",
    "OVERLAY_FS"
  ],
  "FocusSymbols": [
    "metafs_get_root",
    "metafs_create_root",
    "metafs_root_lookup",
    "metafs_mount_lookup",
    "metafs_options_lookup",
    "metafs_option_values_lookup",
    "metafs_get_option_values",
    "metafs_get_link",
    "metafs_readlink",
    "mnt_show_options",
    "sb_add_option",
    "sb_add_option_path",
    "ovl_get_options",
    "erofs_get_options"
  ],
  "Reasoning": "The patch introduces a new virtual filesystem `metafs` that exposes metadata about files and filesystems via the `O_ALT` flag. It also introduces a new `get_options` super operation, implemented by `erofs` and `overlayfs`. These are significant functional changes to the VFS and specific filesystems that should be fuzzed.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 78c8d9e934ce8a9bc1ae0c994f463854bd2f9811
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Jul 15 16:08:22 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 7c753148af8883..3dc431b6cdbb0b 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -314,7 +314,7 @@ or bottom half).
 ``show_devname``
 	Optional. Called by the VFS to show device name for
 	/proc/<pid>/{mounts,mountinfo,mountstats}. If not provided then
-	'(struct mount).mnt_devname' will be used.
+	'(struct super_block).s_devname' will be used.
 
 ``show_path``
 	Optional. Called by the VFS (for /proc/<pid>/mountinfo) to show
diff --git a/fs/Makefile b/fs/Makefile
index 89a8a9d207d1c5..87a4c23d5a08de 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -58,6 +58,8 @@ obj-$(CONFIG_CONFIGFS_FS)	+= configfs/
 obj-y				+= devpts/
 
 obj-$(CONFIG_DLM)		+= dlm/
+
+obj-y				+= metafs/
  
 # Do not add any filesystems before this line
 obj-$(CONFIG_NETFS_SUPPORT)	+= netfs/
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 86fa5c6a0c7087..257517c28e1d38 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -1028,29 +1028,34 @@ static int erofs_statfs(struct dentry *dentry, struct kstatfs *buf)
 	return 0;
 }
 
-static int erofs_show_options(struct seq_file *seq, struct dentry *root)
+static int erofs_get_options(struct sb_opt_ctx *ctx, struct dentry *root)
 {
 	struct erofs_sb_info *sbi = EROFS_SB(root->d_sb);
 	struct erofs_mount_opts *opt = &sbi->opt;
 
+	if (sbi->dif0.file)
+		sb_add_option_path(ctx, "source", NULL, &sbi->dif0.file->f_path);
+
 	if (IS_ENABLED(CONFIG_EROFS_FS_XATTR))
-		seq_puts(seq, test_opt(opt, XATTR_USER) ?
-				",user_xattr" : ",nouser_xattr");
+		sb_add_option(ctx, test_opt(opt, XATTR_USER) ?
+			      "user_xattr" : "nouser_xattr", NULL);
 	if (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))
-		seq_puts(seq, test_opt(opt, POSIX_ACL) ? ",acl" : ",noacl");
+		sb_add_option(ctx, test_opt(opt, POSIX_ACL) ? "acl" : "noacl", NULL);
 	if (IS_ENABLED(CONFIG_EROFS_FS_ZIP))
-		seq_printf(seq, ",cache_strategy=%s",
+		sb_add_option(ctx, "cache_strategy",
 			  erofs_param_cache_strategy[opt->cache_strategy].name);
 	if (test_opt(opt, DAX_ALWAYS))
-		seq_puts(seq, ",dax=always");
+		sb_add_option(ctx, "dax", "always");
 	if (test_opt(opt, DAX_NEVER))
-		seq_puts(seq, ",dax=never");
+		sb_add_option(ctx, "dax", "never");
 	if (erofs_is_fileio_mode(sbi) && test_opt(opt, DIRECT_IO))
-		seq_puts(seq, ",directio");
-	if (sbi->dif0.fsoff)
-		seq_printf(seq, ",fsoffset=%llu", sbi->dif0.fsoff);
+		sb_add_option(ctx, "directio", NULL);
+	if (sbi->dif0.fsoff) {
+		char *val __free(kfree) = kasprintf(GFP_KERNEL, "%llu", sbi->dif0.fsoff);
+		sb_add_option(ctx, "fsoffset", val);
+	}
 	if (test_opt(opt, INODE_SHARE))
-		seq_puts(seq, ",inode_share");
+		sb_add_option(ctx, "inode_share", NULL);
 	return 0;
 }
 
@@ -1069,7 +1074,7 @@ const struct super_operations erofs_sops = {
 	.free_inode = erofs_free_inode,
 	.evict_inode = erofs_evict_inode,
 	.statfs = erofs_statfs,
-	.show_options = erofs_show_options,
+	.get_options = erofs_get_options,
 };
 
 module_init(erofs_module_init);
diff --git a/fs/fcntl.c b/fs/fcntl.c
index c158f082f1da4f..01b765cfac8479 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -1169,7 +1169,7 @@ static int __init fcntl_init(void)
 	 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
 	 * is defined as O_NONBLOCK on some platforms and not on others.
 	 */
-	BUILD_BUG_ON(22 - 1 /* for O_RDONLY being 0 */ !=
+	BUILD_BUG_ON(23 - 1 /* for O_RDONLY being 0 */ !=
 		HWEIGHT32(
 			(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
 			__FMODE_EXEC | __O_REGULAR));
diff --git a/fs/internal.h b/fs/internal.h
index 355d93f9220861..e73a5c1d5b4ee1 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -95,6 +95,7 @@ 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);
+int mnt_show_options(struct seq_file *seq, struct vfsmount *mnt);
 
 /*
  * fs_struct.c
diff --git a/fs/metafs/Makefile b/fs/metafs/Makefile
new file mode 100644
index 00000000000000..f855b48d9c2b90
--- /dev/null
+++ b/fs/metafs/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for meta filesystem
+#
+
+obj-y := metafs.o
diff --git a/fs/metafs/metafs.c b/fs/metafs/metafs.c
new file mode 100644
index 00000000000000..15ec0b6064da38
--- /dev/null
+++ b/fs/metafs/metafs.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * metafs - virtual filesystem for metadata access
+ *
+ * This provides a way to access metadata about files and filesystems
+ * through O_ALT file descriptors.
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/magic.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/slab.h>
+#include <linux/dcache.h>
+#include <linux/pseudo_fs.h>
+#include <linux/fs/super.h>
+#include "metafs.h"
+
+/*
+ * metafs superblock and mount
+ */
+static struct vfsmount *metafs_mnt __ro_after_init;
+
+static const struct super_operations metafs_ops = {
+	.statfs		= simple_statfs,
+	.drop_inode	= inode_just_drop,
+};
+
+static int metafs_init_fs_context(struct fs_context *fc)
+{
+	struct pseudo_fs_context *ctx = init_pseudo(fc, 0x4d455441); /* 'META' */
+	if (!ctx)
+		return -ENOMEM;
+	ctx->ops = &metafs_ops;
+	return 0;
+}
+
+static struct file_system_type metafs_type = {
+	.name = "metafs",
+	.init_fs_context = metafs_init_fs_context,
+	.kill_sb = kill_anon_super,
+};
+
+/*
+ * Mount option collection structures
+ */
+enum metafs_dentry_type {
+	METAFS_ROOT,
+	METAFS_OPTION_VALUES,
+	METAFS_SYMLINK,
+};
+
+struct metafs_option_entry {
+	char *value;
+	struct path path;
+};
+
+struct metafs_dentry_info {
+	enum metafs_dentry_type type;
+	union {
+		struct {
+			struct file *base;
+		} root;
+		struct {
+			struct metafs_option_entry *entries;
+			int count;
+		} opt_values;
+		struct metafs_option_entry symlink;
+	};
+};
+
+struct metafs_collect_ctx {
+	struct sb_opt_ctx ctx;
+	const char *target_name;
+	struct super_block *sb;
+	struct metafs_option_entry *entries;
+	int count;
+	int size;
+	int err;
+	bool is_source;
+};
+
+/*
+ * Collect specific mount option values from the base filesystem
+ */
+static void metafs_collect_option(struct sb_opt_ctx *ctx, const char *name,
+				  const char *value, const struct path *path)
+{
+	struct metafs_collect_ctx *collect_ctx = ctx->data;
+	struct metafs_option_entry *entry;
+
+	if (strcmp(name, collect_ctx->target_name) != 0)
+		return;
+
+	if (!value && collect_ctx->is_source)
+		value = collect_ctx->sb->s_devname;
+
+	if (collect_ctx->count >= collect_ctx->size) {
+		int new_size = collect_ctx->size ? collect_ctx->size * 2 : 4;
+		struct metafs_option_entry *new_entries;
+
+		new_entries = krealloc_array(collect_ctx->entries, new_size, sizeof(*new_entries),
+					     GFP_KERNEL);
+		if (!new_entries) {
+			collect_ctx->err = -ENOMEM;
+			return;
+		}
+		collect_ctx->entries = new_entries;
+		collect_ctx->size = new_size;
+	}
+
+	entry = &collect_ctx->entries[collect_ctx->count];
+	entry->value = value ? kstrdup(value, GFP_KERNEL) : NULL;
+	if (!entry->value && value) {
+		collect_ctx->err = -ENOMEM;
+		return;
+	}
+	if (path) {
+		entry->path = *path;
+		path_get(&entry->path);
+	} else {
+		memset(&entry->path, 0, sizeof(entry->path));
+	}
+	collect_ctx->count++;
+}
+
+static void metafs_free_entries(struct metafs_option_entry *entries, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		kfree(entries[i].value);
+		if (entries[i].path.mnt)
+			path_put(&entries[i].path);
+	}
+	kfree(entries);
+}
+
+static int metafs_get_option_values(struct file *base, const char *option_name,
+				    struct metafs_option_entry **entries_out,
+				    int *count_out)
+{
+	const struct path *path = &base->f_path;
+	struct super_block *sb = path->mnt->mnt_sb;
+	struct metafs_collect_ctx collect_ctx = {
+		.ctx.data = &collect_ctx,
+		.ctx.actor = metafs_collect_option,
+		.target_name = option_name,
+		.sb = sb,
+		.is_source = !strcmp(option_name, "source"),
+	};
+	int ret;
+
+	if (sb->s_op->get_options) {
+		ret = sb->s_op->get_options(&collect_ctx.ctx, path->mnt->mnt_root);
+		if (ret || collect_ctx.err) {
+			metafs_free_entries(collect_ctx.entries, collect_ctx.count);
+			return ret ?: collect_ctx.err;
+		}
+	}
+
+	/* special case: add "source" option if filesystem didn't add one*/
+	if (!collect_ctx.count && collect_ctx.is_source) {
+		struct metafs_option_entry *ent __free(kfree) = kzalloc_obj(*ent);
+
+		if (!ent)
+			return -ENOMEM;
+
+		ent->value = kstrdup(sb->s_devname, GFP_KERNEL);
+		if (!ent->value)
+			return -ENOMEM;
+
+		collect_ctx.entries = no_free_ptr(ent);
+		collect_ctx.count = 1;
+	}
+
+	*entries_out = collect_ctx.entries;
+	*count_out = collect_ctx.count;
+	return 0;
+}
+
+/*
+ * metafs symlink operations
+ */
+static const char *metafs_get_link(struct dentry *dentry, struct inode *inode,
+				   struct delayed_call *done)
+{
+	struct metafs_dentry_info *info;
+
+	if (!dentry)
+		return ERR_PTR(-ECHILD);
+
+	info = dentry->d_fsdata;
+	if (info->symlink.path.mnt) {
+		int error;
+
+		path_get(&info->symlink.path);
+		error = nd_jump_link(&info->symlink.path);
+		return ERR_PTR(error);
+	}
+
+	return ERR_PTR(-ELOOP);
+}
+
+static int metafs_readlink(struct dentry *dentry, char __user *buffer,
+			   int buflen)
+{
+	struct metafs_dentry_info *info = dentry->d_fsdata;
+	const char *value = info->symlink.value;
+
+	if (!value)
+		value = "";
+
+	return readlink_copy(buffer, buflen, value, strlen(value));
+}
+
+static const struct inode_operations metafs_symlink_iops = {
+	.get_link	= metafs_get_link,
+	.readlink	= metafs_readlink,
+};
+
+/*
+ * Dentry operations - cleanup
+ */
+static void metafs_d_release(struct dentry *dentry)
+{
+	struct metafs_dentry_info *info = dentry->d_fsdata;
+
+	if (!info)
+		return;
+
+	switch (info->type) {
+	case METAFS_ROOT:
+		fput(info->root.base);
+		break;
+	case METAFS_OPTION_VALUES:
+		metafs_free_entries(info->opt_values.entries,
+				    info->opt_values.count);
+		break;
+	case METAFS_SYMLINK:
+		kfree(info->symlink.value);
+		if (info->symlink.path.mnt)
+			path_put(&info->symlink.path);
+		break;
+	default:
+		WARN_ON(1);
+	}
+	kfree(info);
+}
+
+static const struct dentry_operations metafs_dentry_ops = {
+	.d_release = metafs_d_release,
+};
+
+/*
+ * metafs directory operations - forward declarations
+ */
+static struct file *metafs_get_base_file(struct dentry *dentry);
+static struct dentry *metafs_root_lookup(struct inode *dir, struct dentry *dentry,
+					 unsigned int flags);
+static struct dentry *metafs_mount_lookup(struct inode *dir, struct dentry *dentry,
+					  unsigned int flags);
+static struct dentry *metafs_options_lookup(struct inode *dir, struct dentry *dentry,
+					    unsigned int flags);
+static struct dentry *metafs_option_values_lookup(struct inode *dir, struct dentry *dentry,
+						  unsigned int flags);
+
+static const struct inode_operations metafs_root_dir_iops = {
+	.lookup		= metafs_root_lookup,
+};
+
+static const struct inode_operations metafs_mount_dir_iops = {
+	.lookup		= metafs_mount_lookup,
+};
+
+static const struct inode_operations metafs_options_dir_iops = {
+	.lookup		= metafs_options_lookup,
+};
+
+static const struct inode_operations metafs_option_values_dir_iops = {
+	.lookup		= metafs_option_values_lookup,
+};
+
+/*
+ * Create a new inode in metafs
+ */
+static struct inode *metafs_get_inode(struct super_block *sb, umode_t mode,
+				      const struct inode_operations *iops)
+{
+	struct inode *inode = new_inode(sb);
+
+	if (!inode)
+		return NULL;
+
+	inode->i_ino = get_next_ino();
+	inode->i_mode = mode;
+	simple_inode_init_ts(inode);
+
+	if (S_ISDIR(mode)) {
+		inode->i_op = iops;
+		inc_nlink(inode);
+	} else if (S_ISLNK(mode)) {
+		inode->i_op = &metafs_symlink_iops;
+	}
+
+	return inode;
+}
+
+/*
+ * Lookup in root directory (/)
+ * Only "mount" is valid here
+ */
+static struct dentry *metafs_root_lookup(struct inode *dir, struct dentry *dentry,
+					 unsigned int flags)
+{
+	const char *name = dentry->d_name.name;
+	struct inode *inode;
+
+	if (strcmp(name, "mount") != 0)
+		return ERR_PTR(-ENOENT);
+
+	inode = metafs_get_inode(dir->i_sb, S_IFDIR | 0555, &metafs_mount_dir_iops);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	d_add(dentry, inode);
+	d_mark_dontcache(inode);
+	return NULL;
+}
+
+/*
+ * Lookup in /mount directory
+ * Only "options" is valid here
+ */
+static struct dentry *metafs_mount_lookup(struct inode *dir, struct dentry *dentry,
+					  unsigned int flags)
+{
+	const char *name = dentry->d_name.name;
+	struct inode *inode;
+
+	if (strcmp(name, "options") != 0)
+		return ERR_PTR(-ENOENT);
+
+	inode = metafs_get_inode(dir->i_sb, S_IFDIR | 0555, &metafs_options_dir_iops);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	d_add(dentry, inode);
+	d_mark_dontcache(inode);
+	return NULL;
+}
+
+/*
+ * Lookup in /mount/options directory
+ * Collect values for the requested option name
+ */
+static struct dentry *metafs_options_lookup(struct inode *dir, struct dentry *dentry,
+					    unsigned int flags)
+{
+	const char *target_name = dentry->d_name.name;
+	struct file *base;
+	struct metafs_dentry_info *info;
+	struct metafs_option_entry *entries = NULL;
+	int count = 0;
+	struct inode *inode;
+	int ret;
+
+	base = metafs_get_base_file(dentry->d_parent);
+	if (!base)
+		return ERR_PTR(-ENOENT);
+
+	ret = metafs_get_option_values(base, target_name, &entries, &count);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (count == 0) {
+		kfree(entries);
+		return ERR_PTR(-ENOENT);
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		metafs_free_entries(entries, count);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	info->type = METAFS_OPTION_VALUES;
+	info->opt_values.entries = entries;
+	info->opt_values.count = count;
+
+	inode = metafs_get_inode(dir->i_sb, S_IFDIR | 0555, &metafs_option_values_dir_iops);
+	if (!inode) {
+		metafs_free_entries(entries, count);
+		kfree(info);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dentry->d_fsdata = info;
+	d_add(dentry, inode);
+	d_mark_dontcache(inode);
+	return NULL;
+}
+
+/*
+ * Lookup in option-specific directory (e.g., /mount/options/lowerdir/)
+ * Return numbered entries (0, 1, 2, ...) as symlinks
+ */
+static struct dentry *metafs_option_values_lookup(struct inode *dir,
+						  struct dentry *dentry,
+						  unsigned int flags)
+{
+	const char *target_name = dentry->d_name.name;
+	struct metafs_dentry_info *parent_info, *info;
+	struct metafs_option_entry *entry;
+	struct inode *inode;
+	unsigned long index;
+	int err;
+
+	parent_info = dentry->d_parent->d_fsdata;
+	if (WARN_ON(!parent_info || parent_info->type != METAFS_OPTION_VALUES))
+		return ERR_PTR(-ENOENT);
+
+	if (target_name[0] == '0' && target_name[1])
+		return ERR_PTR(-ENOENT);
+
+	err = kstrtoul(target_name, 10, &index);
+	if (err || index >= parent_info->opt_values.count)
+		return ERR_PTR(-ENOENT);
+
+	entry = &parent_info->opt_values.entries[index];
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return ERR_PTR(-ENOMEM);
+
+	info->type = METAFS_SYMLINK;
+	if (entry->value) {
+		info->symlink.value = kstrdup(entry->value, GFP_KERNEL);
+		if (!info->symlink.value) {
+			kfree(info);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+	if (entry->path.mnt) {
+		info->symlink.path = entry->path;
+		path_get(&info->symlink.path);
+	}
+
+	inode = metafs_get_inode(dir->i_sb, S_IFLNK | 0777, NULL);
+	if (!inode) {
+		kfree(info->symlink.value);
+		if (info->symlink.path.mnt)
+			path_put(&info->symlink.path);
+		kfree(info);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dentry->d_fsdata = info;
+	d_add(dentry, inode);
+	d_mark_dontcache(inode);
+	return NULL;
+}
+
+/*
+ * Helper to get base file from any metafs dentry
+ */
+static struct file *metafs_get_base_file(struct dentry *dentry)
+{
+	while (dentry) {
+		struct metafs_dentry_info *info = dentry->d_fsdata;
+
+		if (info && info->type == METAFS_ROOT)
+			return info->root.base;
+		if (dentry == dentry->d_parent)
+			break;
+		dentry = dentry->d_parent;
+	}
+	return NULL;
+}
+
+/*
+ * Create the root directory structure for a metadata open
+ */
+static struct dentry *metafs_create_root(struct file *base)
+{
+	struct inode *inode;
+	struct dentry *root;
+	struct super_block *sb = metafs_mnt->mnt_sb;
+	struct metafs_dentry_info *info;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return ERR_PTR(-ENOMEM);
+
+	info->type = METAFS_ROOT;
+	info->root.base = get_file(base);
+
+	inode = metafs_get_inode(sb, S_IFDIR | 0555, &metafs_root_dir_iops);
+	if (!inode) {
+		fput(info->root.base);
+		kfree(info);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	root = d_alloc_anon(sb);
+	if (!root) {
+		iput(inode);
+		fput(info->root.base);
+		kfree(info);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* Store the info in d_fsdata before instantiating */
+	root->d_fsdata = info;
+	d_instantiate(root, inode);
+	d_mark_dontcache(inode);
+
+	return root;
+}
+
+/*
+ * Get metadata root path for a base file.
+ *
+ * This is called from path_init() when O_ALT is used.
+ * It returns a path in the metafs that serves as the root
+ * for the metadata lookup.
+ */
+int metafs_get_root(struct file *base, struct path *path)
+{
+	struct dentry *root;
+
+	if (!base)
+		return -EBADF;
+
+	/* Create the metadata root for this base file */
+	root = metafs_create_root(base);
+	if (IS_ERR(root))
+		return PTR_ERR(root);
+
+	/*
+	 * Return the path to the metadata root.
+	 * Normal VFS lookup will take over from here.
+	 */
+	path->mnt = mntget(metafs_mnt);
+	path->dentry = root;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(metafs_get_root);
+
+static int __init metafs_init(void)
+{
+	int err;
+
+	err = register_filesystem(&metafs_type);
+	if (err)
+		return err;
+
+	metafs_mnt = kern_mount(&metafs_type);
+	if (IS_ERR(metafs_mnt)) {
+		err = PTR_ERR(metafs_mnt);
+		unregister_filesystem(&metafs_type);
+		return err;
+	}
+	set_default_d_op(metafs_mnt->mnt_sb, &metafs_dentry_ops);
+
+	return 0;
+}
+
+fs_initcall(metafs_init);
diff --git a/fs/metafs/metafs.h b/fs/metafs/metafs.h
new file mode 100644
index 00000000000000..d257714260a7fb
--- /dev/null
+++ b/fs/metafs/metafs.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_METAFS_H
+#define _LINUX_METAFS_H
+
+struct path;
+struct file;
+
+/*
+ * Get metadata root path for a base file.
+ * Called from path_init() when O_ALT flag is used.
+ * Returns 0 on success with path filled in, -errno on error.
+ */
+int metafs_get_root(struct file *base, struct path *path);
+
+#endif /* _LINUX_METAFS_H */
diff --git a/fs/mount.h b/fs/mount.h
index 94fcc306d21e7d..69a953bc112b36 100644
--- a/fs/mount.h
+++ b/fs/mount.h
@@ -65,7 +65,6 @@ struct mount {
 	struct mount * __aligned(1) *mnt_pprev_for_sb;
 					/* except that LSB of pprev is stolen */
 #define WRITE_HOLD 1			/* ... for use by mnt_hold_writers() */
-	const char *mnt_devname;	/* Name of device e.g. /dev/dsk/hda1 */
 	struct list_head mnt_list;
 	struct list_head mnt_expire;	/* link in fs-specific expiry list */
 	struct list_head mnt_share;	/* circular list of shared mounts */
diff --git a/fs/namei.c b/fs/namei.c
index 19ce43c9a6e662..437a14c432e67b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -45,6 +45,7 @@
 
 #include "internal.h"
 #include "mount.h"
+#include "metafs/metafs.h"
 
 /* [Feb-1997 T. Schoebel-Theuer]
  * Fundamental changes in the pathname lookup mechanisms (namei)
@@ -2753,6 +2754,21 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 				return ERR_PTR(-ENOENT);
 		}
 
+		if (unlikely(flags & LOOKUP_ALT)) {
+			if (flags & LOOKUP_RCU)
+				return ERR_PTR(-ECHILD);
+
+			/* Handle O_ALT: jump to metafs root */
+			error = metafs_get_root(fd_file(f), &nd->path);
+			if (error)
+				return ERR_PTR(error);
+
+			nd->inode = nd->path.dentry->d_inode;
+			nd->state |= ND_JUMPED;
+
+			return s;
+		}
+
 		dentry = fd_file(f)->f_path.dentry;
 
 		if (*s && unlikely(!d_can_lookup(dentry)))
diff --git a/fs/namespace.c b/fs/namespace.c
index 3d5cd5bf3b0537..62cc71c401a2ac 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -282,7 +282,7 @@ int mnt_get_count(struct mount *mnt)
 #endif
 }
 
-static struct mount *alloc_vfsmnt(const char *name)
+static struct mount *alloc_vfsmnt(void)
 {
 	struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
 	if (mnt) {
@@ -292,18 +292,10 @@ static struct mount *alloc_vfsmnt(const char *name)
 		if (err)
 			goto out_free_cache;
 
-		if (name)
-			mnt->mnt_devname = kstrdup_const(name,
-							 GFP_KERNEL_ACCOUNT);
-		else
-			mnt->mnt_devname = "none";
-		if (!mnt->mnt_devname)
-			goto out_free_id;
-
 #ifdef CONFIG_SMP
 		mnt->mnt_pcp = alloc_percpu(struct mnt_pcp);
 		if (!mnt->mnt_pcp)
-			goto out_free_devname;
+			goto out_free_id;
 
 		this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
 #else
@@ -328,11 +320,9 @@ static struct mount *alloc_vfsmnt(const char *name)
 	return mnt;
 
 #ifdef CONFIG_SMP
-out_free_devname:
-	kfree_const(mnt->mnt_devname);
-#endif
 out_free_id:
 	mnt_free_id(mnt);
+#endif
 out_free_cache:
 	kmem_cache_free(mnt_cache, mnt);
 	return NULL;
@@ -725,7 +715,6 @@ int sb_prepare_remount_readonly(struct super_block *sb)
 static void free_vfsmnt(struct mount *mnt)
 {
 	mnt_idmap_put(mnt_idmap(&mnt->mnt));
-	kfree_const(mnt->mnt_devname);
 #ifdef CONFIG_SMP
 	free_percpu(mnt->mnt_pcp);
 #endif
@@ -1180,7 +1169,7 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)
 	if (!fc->root)
 		return ERR_PTR(-EINVAL);
 
-	mnt = alloc_vfsmnt(fc->source);
+	mnt = alloc_vfsmnt();
 	if (!mnt)
 		return ERR_PTR(-ENOMEM);
 
@@ -1248,7 +1237,7 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
 	struct mount *mnt;
 	int err;
 
-	mnt = alloc_vfsmnt(old->mnt_devname);
+	mnt = alloc_vfsmnt();
 	if (!mnt)
 		return ERR_PTR(-ENOMEM);
 
@@ -2957,16 +2946,12 @@ static int do_change_type(const struct path *path, int ms_flags)
 static inline bool may_copy_tree(const struct path *path)
 {
 	struct mount *mnt = real_mount(path->mnt);
-	const struct dentry_operations *d_op;
 
 	if (check_mnt(mnt))
 		return true;
 
-	d_op = path->dentry->d_op;
-	if (d_op == &ns_dentry_operations)
-		return true;
-
-	if (d_op == &pidfs_dentry_operations)
+	/* Make an exception for internal mounts explicitly marked as clonable */
+	if (mnt->mnt_ns == MNT_NS_INTERNAL && mnt->mnt.mnt_flags & MNT_CLONABLE)
 		return true;
 
 	if (!is_mounted(path->mnt))
@@ -3201,7 +3186,7 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned
 
 	if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE |
 		      AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE |
-		      OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE))
+		      OPEN_TREE_CLOEXEC | OPEN_TREE_NAMESPACE | OPEN_TREE_ALT))
 		return ERR_PTR(-EINVAL);
 
 	if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE | OPEN_TREE_NAMESPACE)) ==
@@ -3215,6 +3200,8 @@ static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned
 		lookup_flags &= ~LOOKUP_AUTOMOUNT;
 	if (flags & AT_SYMLINK_NOFOLLOW)
 		lookup_flags &= ~LOOKUP_FOLLOW;
+	if (flags & OPEN_TREE_ALT)
+		lookup_flags |= LOOKUP_ALT;
 
 	/*
 	 * If we create a new mount namespace with the cloned mount tree we
@@ -5387,7 +5374,6 @@ static void statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
 static int statmount_sb_source(struct kstatmount *s, struct seq_file *seq)
 {
 	struct super_block *sb = s->mnt->mnt_sb;
-	struct mount *r = real_mount(s->mnt);
 
 	if (sb->s_op->show_devname) {
 		size_t start = seq->count;
@@ -5405,7 +5391,7 @@ static int statmount_sb_source(struct kstatmount *s, struct seq_file *seq)
 		seq->count = start;
 		seq_commit(seq, string_unescape_inplace(seq->buf + start, UNESCAPE_OCTAL));
 	} else {
-		seq_puts(seq, r->mnt_devname);
+		seq_puts(seq, sb->s_devname);
 	}
 	return 0;
 }
@@ -5416,6 +5402,40 @@ static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
 	s->sm.mnt_ns_id = ns->ns.ns_id;
 }
 
+void sb_add_option(struct sb_opt_ctx *ctx, const char *name, const char *value)
+{
+	sb_add_option_path(ctx, name, value, NULL);
+}
+
+void sb_add_option_path(struct sb_opt_ctx *ctx, const char *name, const char *value,
+			const struct path *path)
+{
+	ctx->actor(ctx, name, value, path);
+}
+
+static void do_show_option(struct sb_opt_ctx *ctx, const char *name, const char *value,
+			   const struct path *path)
+{
+	if (strcmp(name, "source") != 0)
+		seq_show_option(ctx->data, name, value);
+}
+
+int mnt_show_options(struct seq_file *seq, struct vfsmount *mnt)
+{
+	struct super_block *sb = mnt->mnt_sb;
+
+	if (sb->s_op->show_options)
+		return sb->s_op->show_options(seq, mnt->mnt_root);
+
+	if (sb->s_op->get_options) {
+		struct sb_opt_ctx ctx = { .data = seq, .actor = do_show_option };
+
+		return sb->s_op->get_options(&ctx, mnt->mnt_root);
+	}
+
+	return 0;
+}
+
 static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
 {
 	struct vfsmount *mnt = s->mnt;
@@ -5427,11 +5447,9 @@ static int statmount_mnt_opts(struct kstatmount *s, struct seq_file *seq)
 	if (err)
 		return err;
 
-	if (sb->s_op->show_options) {
-		err = sb->s_op->show_options(seq, mnt->mnt_root);
-		if (err)
-			return err;
-	}
+	err = mnt_show_options(seq, mnt);
+	if (err)
+		return err;
 
 	if (unlikely(seq_has_overflowed(seq)))
 		return -EAGAIN;
@@ -5479,14 +5497,10 @@ static inline int statmount_opt_process(struct seq_file *seq, size_t start)
 static int statmount_opt_array(struct kstatmount *s, struct seq_file *seq)
 {
 	struct vfsmount *mnt = s->mnt;
-	struct super_block *sb = mnt->mnt_sb;
 	size_t start = seq->count;
 	int err;
 
-	if (!sb->s_op->show_options)
-		return 0;
-
-	err = sb->s_op->show_options(seq, mnt->mnt_root);
+	err = mnt_show_options(seq, mnt);
 	if (err)
 		return err;
 
diff --git a/fs/nsfs.c b/fs/nsfs.c
index c3b6ae76594a81..e110130d0760e5 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -684,6 +684,7 @@ void __init nsfs_init(void)
 	if (IS_ERR(nsfs_mnt))
 		panic("can't set nsfs up\n");
 	nsfs_mnt->mnt_sb->s_flags &= ~SB_NOUSER;
+	nsfs_mnt->mnt_flags |= MNT_CLONABLE;
 	nsfs_root_path.mnt = nsfs_mnt;
 	nsfs_root_path.dentry = nsfs_mnt->mnt_root;
 }
diff --git a/fs/open.c b/fs/open.c
index 408925d7bd0b77..3a081a9e306243 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1131,7 +1131,8 @@ struct file *kernel_file_open(const struct path *path, int flags,
 EXPORT_SYMBOL_GPL(kernel_file_open);
 
 #define WILL_CREATE(flags)	(flags & (O_CREAT | __O_TMPFILE))
-#define O_PATH_FLAGS		(O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH)
+#define O_PATH_FLAGS		(O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC | O_EMPTYPATH | \
+				 O_ALT)
 
 inline struct open_how build_open_how(int flags, umode_t mode)
 {
@@ -1283,6 +1284,8 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
 		lookup_flags |= LOOKUP_FOLLOW;
 	if (flags & O_EMPTYPATH)
 		lookup_flags |= LOOKUP_EMPTY;
+	if (flags & O_ALT)
+		lookup_flags |= LOOKUP_ALT;
 
 	if (how->resolve & RESOLVE_NO_XDEV)
 		lookup_flags |= LOOKUP_NO_XDEV;
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index c93fcaa45d4a36..c5a9e858e3665e 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -1046,10 +1046,11 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
  * Prints the mount options for a given superblock.
  * Returns zero; does not fail.
  */
-int ovl_show_options(struct seq_file *m, struct dentry *dentry)
+int ovl_get_options(struct sb_opt_ctx *ctx, struct dentry *dentry)
 {
 	struct super_block *sb = dentry->d_sb;
 	struct ovl_fs *ofs = OVL_FS(sb);
+	struct path upperpath;
 	size_t nr, nr_merged_lower, nr_lower = 0;
 	char **lowerdirs = ofs->config.lowerdirs;
 
@@ -1062,43 +1063,47 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
 	 * with the new lowerdir+ and datadir+ mount options.
 	 */
 	if (lowerdirs[0]) {
-		seq_show_option(m, "lowerdir", lowerdirs[0]);
+		sb_add_option(ctx, "lowerdir", lowerdirs[0]);
 	} else {
 		nr_lower = ofs->numlayer;
 		nr_merged_lower = nr_lower - ofs->numdatalayer;
 	}
 	for (nr = 1; nr < nr_lower; nr++) {
+		struct path lowerpath = {
+			.mnt = ofs->layers[nr].mnt,
+			.dentry = ofs->layers[nr].mnt->mnt_root,
+		};
 		if (nr < nr_merged_lower)
-			seq_show_option(m, "lowerdir+", lowerdirs[nr]);
+			sb_add_option_path(ctx, "lowerdir+", lowerdirs[nr],
+					   &lowerpath);
 		else
-			seq_show_option(m, "datadir+", lowerdirs[nr]);
+			sb_add_option_path(ctx, "datadir+", lowerdirs[nr],
+					   &lowerpath);
 	}
 	if (ofs->config.upperdir) {
-		seq_show_option(m, "upperdir", ofs->config.upperdir);
-		seq_show_option(m, "workdir", ofs->config.workdir);
+		ovl_path_upper(sb->s_root, &upperpath);
+		sb_add_option_path(ctx, "upperdir", ofs->config.upperdir, &upperpath);
+		sb_add_option(ctx, "workdir", ofs->config.workdir);
 	}
 	if (ofs->config.default_permissions)
-		seq_puts(m, ",default_permissions");
+		sb_add_option(ctx, "default_permissions", NULL);
 	if (ofs->config.redirect_mode != ovl_redirect_mode_def())
-		seq_printf(m, ",redirect_dir=%s",
-			   ovl_redirect_mode(&ofs->config));
+		sb_add_option(ctx, "redirect_dir", ovl_redirect_mode(&ofs->config));
 	if (ofs->config.index != ovl_index_def)
-		seq_printf(m, ",index=%s", str_on_off(ofs->config.index));
+		sb_add_option(ctx, "index", str_on_off(ofs->config.index));
 	if (ofs->config.uuid != ovl_uuid_def())
-		seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
+		sb_add_option(ctx, "uuid", ovl_uuid_mode(&ofs->config));
 	if (ofs->config.nfs_export != ovl_nfs_export_def)
-		seq_printf(m, ",nfs_export=%s",
-			   str_on_off(ofs->config.nfs_export));
+		sb_add_option(ctx, "nfs_export", str_on_off(ofs->config.nfs_export));
 	if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
-		seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
+		sb_add_option(ctx, "xino", ovl_xino_mode(&ofs->config));
 	if (ofs->config.metacopy != ovl_metacopy_def)
-		seq_printf(m, ",metacopy=%s", str_on_off(ofs->config.metacopy));
+		sb_add_option(ctx, "metacopy", str_on_off(ofs->config.metacopy));
 	if (ofs->config.fsync_mode != ovl_fsync_mode_def())
-		seq_printf(m, ",fsync=%s", ovl_fsync_mode(&ofs->config));
+		sb_add_option(ctx, "fsync", ovl_fsync_mode(&ofs->config));
 	if (ofs->config.userxattr)
-		seq_puts(m, ",userxattr");
+		sb_add_option(ctx, "userxattr", NULL);
 	if (ofs->config.verity_mode != ovl_verity_mode_def())
-		seq_printf(m, ",verity=%s",
-			   ovl_verity_mode(&ofs->config));
+		sb_add_option(ctx, "verity", ovl_verity_mode(&ofs->config));
 	return 0;
 }
diff --git a/fs/overlayfs/params.h b/fs/overlayfs/params.h
index ffd53cdd84827c..2cf9c4f188a9cb 100644
--- a/fs/overlayfs/params.h
+++ b/fs/overlayfs/params.h
@@ -40,5 +40,5 @@ int ovl_init_fs_context(struct fs_context *fc);
 void ovl_free_fs(struct ovl_fs *ofs);
 int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
 			 struct ovl_config *config);
-int ovl_show_options(struct seq_file *m, struct dentry *dentry);
+int ovl_get_options(struct sb_opt_ctx *ctx, struct dentry *dentry);
 const char *ovl_xino_mode(struct ovl_config *config);
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 60f0b7ceef0a6d..4b57b2db085225 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -302,7 +302,7 @@ static const struct super_operations ovl_super_operations = {
 	.put_super	= ovl_put_super,
 	.sync_fs	= ovl_sync_fs,
 	.statfs		= ovl_statfs,
-	.show_options	= ovl_show_options,
+	.get_options	= ovl_get_options,
 };
 
 #define OVL_WORKDIR_NAME "work"
@@ -529,6 +529,7 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
 
 	/* Don't inherit atime flags */
 	upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
+	upper_mnt->mnt_flags |= MNT_CLONABLE;
 	upper_layer->mnt = upper_mnt;
 	upper_layer->idx = 0;
 	upper_layer->fsid = 0;
@@ -1105,7 +1106,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
 		 * Make lower layers R/O.  That way fchmod/fchown on lower file
 		 * will fail instead of modifying lower fs.
 		 */
-		mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
+		mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME | MNT_CLONABLE;
 
 		layers[ofs->numlayer].trap = trap;
 		layers[ofs->numlayer].mnt = mnt;
diff --git a/fs/pidfs.c b/fs/pidfs.c
index aaa609ddab0443..3843acd6594de0 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -840,7 +840,7 @@ static char *pidfs_dname(struct dentry *dentry, char *buffer, int buflen)
 	return dynamic_dname(buffer, buflen, "anon_inode:[pidfd]");
 }
 
-const struct dentry_operations pidfs_dentry_operations = {
+static const struct dentry_operations pidfs_dentry_operations = {
 	.d_dname	= pidfs_dname,
 	.d_prune	= stashed_dentry_prune,
 };
@@ -1151,6 +1151,8 @@ void __init pidfs_init(void)
 	if (IS_ERR(pidfs_mnt))
 		panic("Failed to mount pidfs pseudo filesystem");
 
+	pidfs_mnt->mnt_flags |= MNT_CLONABLE;
+
 	pidfs_root_path.mnt = pidfs_mnt;
 	pidfs_root_path.dentry = pidfs_mnt->mnt_root;
 }
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 5c555db68aa212..94bc4c9aa43f16 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -98,21 +98,27 @@ static void show_type(struct seq_file *m, struct super_block *sb)
 	}
 }
 
+static int show_devname(struct seq_file *m, struct vfsmount *mnt)
+{
+	struct super_block *sb = mnt->mnt_sb;
+
+	if (sb->s_op->show_devname)
+		return sb->s_op->show_devname(m, mnt->mnt_root);
+
+	mangle(m, sb->s_devname);
+	return 0;
+}
+
 static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
 {
 	struct proc_mounts *p = m->private;
-	struct mount *r = real_mount(mnt);
 	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
 	struct super_block *sb = mnt_path.dentry->d_sb;
 	int err;
 
-	if (sb->s_op->show_devname) {
-		err = sb->s_op->show_devname(m, mnt_path.dentry);
-		if (err)
-			goto out;
-	} else {
-		mangle(m, r->mnt_devname);
-	}
+	err = show_devname(m, mnt);
+	if (err)
+		goto out;
 	seq_putc(m, ' ');
 	/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
 	err = seq_path_root(m, &mnt_path, &p->root, " \t\n\\");
@@ -125,8 +131,7 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
 	if (err)
 		goto out;
 	show_vfsmnt_opts(m, mnt);
-	if (sb->s_op->show_options)
-		err = sb->s_op->show_options(m, mnt_path.dentry);
+	err = mnt_show_options(m, mnt);
 	seq_puts(m, " 0 0\n");
 out:
 	return err;
@@ -172,19 +177,14 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
 	seq_puts(m, " - ");
 	show_type(m, sb);
 	seq_putc(m, ' ');
-	if (sb->s_op->show_devname) {
-		err = sb->s_op->show_devname(m, mnt->mnt_root);
-		if (err)
-			goto out;
-	} else {
-		mangle(m, r->mnt_devname);
-	}
+	err = show_devname(m, mnt);
+	if (err)
+		goto out;
 	seq_puts(m, sb_rdonly(sb) ? " ro" : " rw");
 	err = show_sb_opts(m, sb);
 	if (err)
 		goto out;
-	if (sb->s_op->show_options)
-		err = sb->s_op->show_options(m, mnt->mnt_root);
+	err = mnt_show_options(m, mnt);
 	seq_putc(m, '\n');
 out:
 	return err;
@@ -193,21 +193,15 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
 static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
 {
 	struct proc_mounts *p = m->private;
-	struct mount *r = real_mount(mnt);
 	struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
 	struct super_block *sb = mnt_path.dentry->d_sb;
 	int err;
 
 	/* device */
 	seq_puts(m, "device ");
-	if (sb->s_op->show_devname) {
-		err = sb->s_op->show_devname(m, mnt_path.dentry);
-		if (err)
-			goto out;
-	} else {
-		mangle(m, r->mnt_devname);
-	}
-
+	err = show_devname(m, mnt);
+	if (err)
+		goto out;
 	/* mount point */
 	seq_puts(m, " mounted on ");
 	/* mountpoints outside of chroot jail will give SEQ_SKIP on this */
diff --git a/fs/super.c b/fs/super.c
index a8fd61136aafe5..e5531cb88f8917 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -280,6 +280,7 @@ static void destroy_super_work(struct work_struct *work)
 	security_sb_free(s);
 	put_user_ns(s->s_user_ns);
 	kfree(s->s_subtype);
+	kfree_const(s->s_devname);
 	for (int i = 0; i < SB_FREEZE_LEVELS; i++)
 		percpu_free_rwsem(&s->s_writers.rw_sem[i]);
 	kfree(s);
@@ -315,7 +316,7 @@ static void destroy_unused_super(struct super_block *s)
  *	returns a pointer new superblock or %NULL if allocation had failed.
  */
 static struct super_block *alloc_super(struct file_system_type *type, int flags,
-				       struct user_namespace *user_ns)
+				       struct user_namespace *user_ns, const char *name)
 {
 	struct super_block *s = kzalloc_obj(struct super_block);
 	static const struct super_operations default_op;
@@ -344,6 +345,13 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
 	 */
 	down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
 
+	if (name)
+		s->s_devname = kstrdup_const(name, GFP_KERNEL_ACCOUNT);
+	else
+		s->s_devname = "none";
+	if (!s->s_devname)
+		goto fail;
+
 	if (security_sb_alloc(s))
 		goto fail;
 
@@ -763,7 +771,7 @@ struct super_block *sget_fc(struct fs_context *fc,
 	}
 	if (!s) {
 		spin_unlock(&sb_lock);
-		s = alloc_super(fc->fs_type, fc->sb_flags, user_ns);
+		s = alloc_super(fc->fs_type, fc->sb_flags, user_ns, fc->source);
 		if (!s)
 			return ERR_PTR(-ENOMEM);
 		goto retry;
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index 6ad6b9e7a226af..f942411c8ed7de 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -11,7 +11,7 @@
 	(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
 	 O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \
 	 FASYNC	| O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
-	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH)
+	 O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_EMPTYPATH | O_ALT)
 
 /* List of all valid flags for openat2(2)'s how->flags argument. */
 #define VALID_OPENAT2_FLAGS	(VALID_OPEN_FLAGS | OPENAT2_REGULAR)
diff --git a/include/linux/fs/super.h b/include/linux/fs/super.h
index 4056126781151e..6f0d778940f904 100644
--- a/include/linux/fs/super.h
+++ b/include/linux/fs/super.h
@@ -237,4 +237,14 @@ int thaw_super(struct super_block *super, enum freeze_holder who,
 
 int sb_init_dio_done_wq(struct super_block *sb);
 
+struct sb_opt_ctx {
+	void (*actor)(struct sb_opt_ctx *ctx, const char *name, const char *value,
+		      const struct path *path);
+	void *data;
+};
+
+void sb_add_option(struct sb_opt_ctx *ctx, const char *name, const char *value);
+void sb_add_option_path(struct sb_opt_ctx *ctx, const char *name, const char *value,
+			const struct path *path);
+
 #endif /* _LINUX_FS_SUPER_H */
diff --git a/include/linux/fs/super_types.h b/include/linux/fs/super_types.h
index ef7941e9dc79ac..f205759af1a633 100644
--- a/include/linux/fs/super_types.h
+++ b/include/linux/fs/super_types.h
@@ -36,6 +36,7 @@ struct workqueue_struct;
 struct writeback_control;
 struct xattr_handler;
 struct fserror_event;
+struct sb_opt_ctx;
 
 extern struct super_block *blockdev_superblock;
 
@@ -99,6 +100,7 @@ struct super_operations {
 	int (*statfs)(struct dentry *dentry, struct kstatfs *kstatfs);
 	void (*umount_begin)(struct super_block *sb);
 
+	int (*get_options)(struct sb_opt_ctx *ctx, struct dentry *dentry);
 	int (*show_options)(struct seq_file *seq, struct dentry *dentry);
 	int (*show_devname)(struct seq_file *seq, struct dentry *dentry);
 	int (*show_path)(struct seq_file *seq, struct dentry *dentry);
@@ -283,6 +285,8 @@ struct super_block {
 	 */
 	atomic_t				s_isw_nr_in_flight;
 #endif
+	/* Name of device e.g. /dev/dsk/hda1 */
+	const char 				*s_devname;
 } __randomize_layout;
 
 /*
diff --git a/include/linux/mount.h b/include/linux/mount.h
index acfe7ef86a1b3f..d071855ca4edec 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -44,6 +44,7 @@ enum mount_flags {
 	MNT_LOCKED		= 0x800000,
 	MNT_DOOMED		= 0x1000000,
 	MNT_SYNC_UMOUNT		= 0x2000000,
+	MNT_CLONABLE		= 0x4000000,
 	MNT_UMOUNT		= 0x8000000,
 
 	MNT_USER_SETTABLE_MASK  = MNT_NOSUID | MNT_NODEV | MNT_NOEXEC
@@ -52,7 +53,7 @@ enum mount_flags {
 	MNT_ATIME_MASK = MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME,
 
 	MNT_INTERNAL_FLAGS = MNT_INTERNAL | MNT_DOOMED |
-			     MNT_SYNC_UMOUNT | MNT_LOCKED
+			     MNT_SYNC_UMOUNT | MNT_LOCKED | MNT_CLONABLE
 };
 
 struct vfsmount {
diff --git a/include/linux/namei.h b/include/linux/namei.h
index ebe6e29f7e93ba..c21dc6536900cf 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -25,7 +25,8 @@ enum { MAX_NESTED_LINKS = 8 };
 #define LOOKUP_RCU		BIT(8)	/* RCU pathwalk mode; semi-internal */
 #define LOOKUP_CACHED		BIT(9) /* Only do cached lookup */
 #define LOOKUP_PARENT		BIT(10)	/* Looking up final parent in path */
-/* 5 spare bits for pathwalk */
+#define LOOKUP_ALT		BIT(11) /* Look up in alternative namespace */
+/* 4 spare bits for pathwalk */
 
 /* These tell filesystem methods that we are dealing with the final component... */
 #define LOOKUP_OPEN		BIT(16)	/* ... in open */
diff --git a/include/linux/pidfs.h b/include/linux/pidfs.h
index 0abf7da9ab236e..fab6f48ff20334 100644
--- a/include/linux/pidfs.h
+++ b/include/linux/pidfs.h
@@ -15,7 +15,6 @@ void pidfs_exit(struct task_struct *tsk);
 #ifdef CONFIG_COREDUMP
 void pidfs_coredump(const struct coredump_params *cprm);
 #endif
-extern const struct dentry_operations pidfs_dentry_operations;
 int pidfs_register_pid_gfp(struct pid *pid, gfp_t gfp);
 
 /**
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 883cfd7de2effa..70dbbac1c52078 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -96,6 +96,10 @@
 #define O_EMPTYPATH	(1 << 26)	/* allow empty path */
 #endif
 
+#ifndef O_ALT
+#define O_ALT		(1 << 27)	/* Open in alternative namespace */
+#endif
+
 /* a horrid kludge trying to make sure that this will fail on old kernels */
 #define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
 
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index 2204708dbf7a58..6e794d214cf983 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -63,6 +63,7 @@
  */
 #define OPEN_TREE_CLONE		(1 << 0)	/* Clone the target tree and attach the clone */
 #define OPEN_TREE_NAMESPACE	(1 << 1)	/* Clone the target tree into a new mount namespace */
+#define OPEN_TREE_ALT		(1 << 2)	/* Lookup in alternative namespace */
 #define OPEN_TREE_CLOEXEC	O_CLOEXEC	/* Close the file on execve() */
 
 /*
diff --git a/scripts/gdb/linux/proc.py b/scripts/gdb/linux/proc.py
index 65dd1bd129641d..5aaf2dd8db05a1 100644
--- a/scripts/gdb/linux/proc.py
+++ b/scripts/gdb/linux/proc.py
@@ -174,9 +174,6 @@ values of that process namespace"""
                   "mount", "super_block", "devname", "pathname", "fstype"))
 
         for mnt in rbtree.rb_inorder_for_each_entry(namespace['mounts'], mount_ptr_type, "mnt_node"):
-            devname = mnt['mnt_devname'].string()
-            devname = devname if devname else "none"
-
             pathname = ""
             parent = mnt
             while True:
@@ -190,6 +187,8 @@ values of that process namespace"""
                 pathname = "/"
 
             superblock = mnt['mnt']['mnt_sb']
+            devname = superblock['s_devname'].string()
+            devname = devname if devname else "none"
             fstype = superblock['s_type']['name'].string()
             s_flags = int(superblock['s_flags'])
             m_flags = int(mnt['mnt']['mnt_flags'])

3/2 2026/07/15 16:08 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 20035
							output: 35
							thoughts: 1379
						

Thoughts:
**Initial Assessment and Symbol Identification**

Okay, so I'm looking at this patch series. It seems we've got a pretty substantial set of changes here – a whole new virtual filesystem called `metafs` is being introduced, along with some modifications to the VFS layer to support an `O_ALT` flag. This flag is for alternative namespace lookups. They've also rejiggered how mount device names are stored, moving them from `struct mount` to `struct super_block`, and added a `get_options` to `super_operations`, which is replacing `show_options`.

First thing's first, I need to understand the scope of the changes. I'm going through the files, and it looks like:

*   We have a new filesystem being built (`metafs`).
*   The VFS layer is being tweaked for the `O_ALT` flag.
*   Mount information has been moved.
*   New `get_options` function.

Now, let's identify what the core parts of these changes are.

From a quick scan of the code changes, `metafs` looks to be the big, new thing, and will likely need fuzzing. Specifically, `metafs_init_fs_context`, `metafs_collect_option`, `metafs_get_option_values`, `metafs_get_link`, `metafs_readlink`, `metafs_d_release`, `metafs_get_inode`, `metafs_root_lookup`, `metafs_mount_lookup`, `metafs_options_lookup`, `metafs_option_values_lookup`, `metafs_get_base_file`, `metafs_create_root`, and `metafs_get_root` are the functions of this filesystem to focus on.

Also, I have to be aware of the `path_init` in `fs/namei.c`, and the `build_open_flags` in `fs/open.c`. These are both hot paths and could present opportunities for exploitation.

There are also a bunch of other changes that should be verified: `mnt_show_options`, `sb_add_option`, `sb_add_option_path`, `ovl_get_options`, and `erofs_get_options`.

**Fuzzing Strategy**

Okay, `metafs` is built-in. That's good to know. So, fuzzing `openat2` or `open` with the new `O_ALT` flag will trigger the `metafs_get_root` function, so I'll want to focus on that. Also, the same is true for `open_tree` using `OPEN_TREE_ALT`.

So the key functions to focus on for fuzzing:

*   `metafs_get_root`
*   `metafs_create_root`
*   `metafs_root_lookup`
*   `metafs_mount_lookup`
*   `metafs_options_lookup`
*   `metafs_option_values_lookup`
*   `metafs_get_option_values`
*   `metafs_get_link`
*   `metafs_readlink`
*   `mnt_show_options`
*   `sb_add_option`
*   `sb_add_option_path`
*   `ovl_get_options`
*   `erofs_get_options`

I should scan `fs/metafs/metafs.c` a bit closer and make sure I haven't missed any potential targets.



4/2 2026/07/15 16:08 tool grepper
0m Args:
{
  "Expression": "^static.* metafs_",
  "PathPrefix": "fs/metafs/metafs.c"
}

Results:
{
  "Output": "fs/metafs/metafs.c=24=static struct vfsmount *metafs_mnt __ro_after_init;\nfs/metafs/metafs.c-25-\nfs/metafs/metafs.c:26:static const struct super_operations metafs_ops = {\nfs/metafs/metafs.c-27-\t.statfs\t\t= simple_statfs,\n--\nfs/metafs/metafs.c-30-\nfs/metafs/metafs.c:31:static int metafs_init_fs_context(struct fs_context *fc)\nfs/metafs/metafs.c-32-{\n--\nfs/metafs/metafs.c-39-\nfs/metafs/metafs.c:40:static struct file_system_type metafs_type = {\nfs/metafs/metafs.c-41-\t.name = \"metafs\",\n--\nfs/metafs/metafs.c=74=struct metafs_collect_ctx {\n--\nfs/metafs/metafs.c-87- */\nfs/metafs/metafs.c:88:static void metafs_collect_option(struct sb_opt_ctx *ctx, const char *name,\nfs/metafs/metafs.c-89-\t\t\t\t  const char *value, const struct path *path)\n--\nfs/metafs/metafs.c-128-\nfs/metafs/metafs.c:129:static void metafs_free_entries(struct metafs_option_entry *entries, int count)\nfs/metafs/metafs.c-130-{\n--\nfs/metafs/metafs.c-140-\nfs/metafs/metafs.c:141:static int metafs_get_option_values(struct file *base, const char *option_name,\nfs/metafs/metafs.c-142-\t\t\t\t    struct metafs_option_entry **entries_out,\n--\nfs/metafs/metafs.c=187=static const char *metafs_get_link(struct dentry *dentry, struct inode *inode,\n--\nfs/metafs/metafs.c-206-\nfs/metafs/metafs.c:207:static int metafs_readlink(struct dentry *dentry, char __user *buffer,\nfs/metafs/metafs.c-208-\t\t\t   int buflen)\n--\nfs/metafs/metafs.c-218-\nfs/metafs/metafs.c:219:static const struct inode_operations metafs_symlink_iops = {\nfs/metafs/metafs.c-220-\t.get_link\t= metafs_get_link,\n--\nfs/metafs/metafs.c-226- */\nfs/metafs/metafs.c:227:static void metafs_d_release(struct dentry *dentry)\nfs/metafs/metafs.c-228-{\n--\nfs/metafs/metafs.c-252-\nfs/metafs/metafs.c:253:static const struct dentry_operations metafs_dentry_ops = {\nfs/metafs/metafs.c-254-\t.d_release = metafs_d_release,\n--\nfs/metafs/metafs.c=267=static struct dentry *metafs_option_values_lookup(struct inode *dir, struct dentry *dentry,\n--\nfs/metafs/metafs.c-269-\nfs/metafs/metafs.c:270:static const struct inode_operations metafs_root_dir_iops = {\nfs/metafs/metafs.c-271-\t.lookup\t\t= metafs_root_lookup,\n--\nfs/metafs/metafs.c-273-\nfs/metafs/metafs.c:274:static const struct inode_operations metafs_mount_dir_iops = {\nfs/metafs/metafs.c-275-\t.lookup\t\t= metafs_mount_lookup,\n--\nfs/metafs/metafs.c-277-\nfs/metafs/metafs.c:278:static const struct inode_operations metafs_options_dir_iops = {\nfs/metafs/metafs.c-279-\t.lookup\t\t= metafs_options_lookup,\n--\nfs/metafs/metafs.c-281-\nfs/metafs/metafs.c:282:static const struct inode_operations metafs_option_values_dir_iops = {\nfs/metafs/metafs.c-283-\t.lookup\t\t= metafs_option_values_lookup,\n--\nfs/metafs/metafs.c=551=EXPORT_SYMBOL_GPL(metafs_get_root);\nfs/metafs/metafs.c-552-\nfs/metafs/metafs.c:553:static int __init metafs_init(void)\nfs/metafs/metafs.c-554-{\n"
}

5/2 2026/07/15 16:08 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 23496
							output: 67
							thoughts: 165
						

Thoughts:
**Analyzing the Kernel Filesystem Changes**

Okay, so this patch introduces this new `metafs` virtual filesystem – interesting. It sounds like it's designed to give us a direct way to access metadata about files and filesystems, which is useful. I see it uses `O_ALT` file descriptors, which is a good choice for this type of access, keeps things clean. Plus, it touches `erofs` and `overlayfs`, modifying them to support `get_options`. That's not entirely surprising, given the relationship between filesystems.

The `metafs` filesystem itself is always built-in, no configuration needed, just `obj-y += metafs/`. That simplifies things on the user side. However, I need to make sure `EROFS_FS` and `OVERLAY_FS` are enabled to test the `erofs` and `overlayfs` changes properly, as it impacts them.

Now, let's just confirm. I want to be thorough. I'll take a quick look at `fs/erofs/Kconfig` and `fs/overlayfs/Kconfig`. I'll confirm I'm not missing anything obvious in the configuration space, and double check the exact config names for those.



6/2 2026/07/15 16:09 tool grepper
0m Args:
{
  "Expression": "^config EROFS_FS",
  "PathPrefix": "fs/erofs/Kconfig"
}

Results:
{
  "Output": "fs/erofs/Kconfig-2-\nfs/erofs/Kconfig:3:config EROFS_FS\nfs/erofs/Kconfig-4-\ttristate \"EROFS filesystem support\"\n--\nfs/erofs/Kconfig-49-\nfs/erofs/Kconfig:50:config EROFS_FS_DEBUG\nfs/erofs/Kconfig-51-\tbool \"EROFS debugging feature\"\n--\nfs/erofs/Kconfig-59-\nfs/erofs/Kconfig:60:config EROFS_FS_XATTR\nfs/erofs/Kconfig-61-\tbool \"EROFS extended attributes\"\n--\nfs/erofs/Kconfig-70-\nfs/erofs/Kconfig:71:config EROFS_FS_POSIX_ACL\nfs/erofs/Kconfig-72-\tbool \"EROFS Access Control Lists\"\n--\nfs/erofs/Kconfig-84-\nfs/erofs/Kconfig:85:config EROFS_FS_SECURITY\nfs/erofs/Kconfig-86-\tbool \"EROFS Security Labels\"\n--\nfs/erofs/Kconfig-97-\nfs/erofs/Kconfig:98:config EROFS_FS_BACKED_BY_FILE\nfs/erofs/Kconfig-99-\tbool \"File-backed EROFS filesystem support\"\n--\nfs/erofs/Kconfig-111-\nfs/erofs/Kconfig:112:config EROFS_FS_ZIP\nfs/erofs/Kconfig-113-\tbool \"EROFS Data Compression Support\"\n--\nfs/erofs/Kconfig-121-\nfs/erofs/Kconfig:122:config EROFS_FS_ZIP_LZMA\nfs/erofs/Kconfig-123-\tbool \"EROFS LZMA compressed data support\"\n--\nfs/erofs/Kconfig-133-\nfs/erofs/Kconfig:134:config EROFS_FS_ZIP_DEFLATE\nfs/erofs/Kconfig-135-\tbool \"EROFS DEFLATE compressed data support\"\n--\nfs/erofs/Kconfig-144-\nfs/erofs/Kconfig:145:config EROFS_FS_ZIP_ZSTD\nfs/erofs/Kconfig-146-\tbool \"EROFS Zstandard compressed data support\"\n--\nfs/erofs/Kconfig-155-\nfs/erofs/Kconfig:156:config EROFS_FS_ZIP_ACCEL\nfs/erofs/Kconfig-157-\tbool \"EROFS hardware decompression support\"\n--\nfs/erofs/Kconfig-169-\nfs/erofs/Kconfig:170:config EROFS_FS_PCPU_KTHREAD\nfs/erofs/Kconfig-171-\tbool \"EROFS per-cpu decompression kthread workers\"\n--\nfs/erofs/Kconfig-178-\nfs/erofs/Kconfig:179:config EROFS_FS_PCPU_KTHREAD_HIPRI\nfs/erofs/Kconfig-180-\tbool \"EROFS high priority per-CPU kthread workers\"\n--\nfs/erofs/Kconfig-188-\nfs/erofs/Kconfig:189:config EROFS_FS_PAGE_CACHE_SHARE\nfs/erofs/Kconfig-190-\tbool \"EROFS page cache share support (experimental)\"\n"
}

7/2 2026/07/15 16:09 tool grepper
0m Args:
{
  "Expression": "^config OVERLAY_FS",
  "PathPrefix": "fs/overlayfs/Kconfig"
}

Results:
{
  "Output": "fs/overlayfs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/overlayfs/Kconfig:2:config OVERLAY_FS\nfs/overlayfs/Kconfig-3-\ttristate \"Overlay filesystem support\"\n--\nfs/overlayfs/Kconfig-14-\nfs/overlayfs/Kconfig:15:config OVERLAY_FS_REDIRECT_DIR\nfs/overlayfs/Kconfig-16-\tbool \"Overlayfs: turn on redirect directory feature by default\"\n--\nfs/overlayfs/Kconfig-30-\nfs/overlayfs/Kconfig:31:config OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW\nfs/overlayfs/Kconfig-32-\tbool \"Overlayfs: follow redirects even if redirects are turned off\"\n--\nfs/overlayfs/Kconfig-45-\nfs/overlayfs/Kconfig:46:config OVERLAY_FS_INDEX\nfs/overlayfs/Kconfig-47-\tbool \"Overlayfs: turn on inodes index feature by default\"\n--\nfs/overlayfs/Kconfig-64-\nfs/overlayfs/Kconfig:65:config OVERLAY_FS_NFS_EXPORT\nfs/overlayfs/Kconfig-66-\tbool \"Overlayfs: turn on NFS export feature by default\"\n--\nfs/overlayfs/Kconfig-92-\nfs/overlayfs/Kconfig:93:config OVERLAY_FS_XINO_AUTO\nfs/overlayfs/Kconfig-94-\tbool \"Overlayfs: auto enable inode number mapping\"\n--\nfs/overlayfs/Kconfig-110-\nfs/overlayfs/Kconfig:111:config OVERLAY_FS_METACOPY\nfs/overlayfs/Kconfig-112-\tbool \"Overlayfs: turn on metadata only copy up feature by default\"\n--\nfs/overlayfs/Kconfig-128-\nfs/overlayfs/Kconfig:129:config OVERLAY_FS_DEBUG\nfs/overlayfs/Kconfig-130-\tbool \"Overlayfs: turn on extra debugging checks\"\n"
}

8/2 2026/07/15 16:09 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25346
							output: 243
							thoughts: 568
						

Thoughts:
**My Analysis of the MetaFS Implementation**

Okay, so I've been digging into this `metafs` filesystem code, and here's what's bubbling to the surface. It seems the key entry points are through the `O_ALT` flag within `openat2` or `open` calls, and the `OPEN_TREE_ALT` flag within the `open_tree` function. My focus needs to be on the functions defined in `fs/metafs/metafs.c`, and the new `get_options` functions sprinkled within `fs/erofs/super.c` and `fs/overlayfs/params.c`. That's where the action is.

Specifically, I'm thinking I should scrutinize these symbols: `metafs_get_root`, `metafs_create_root`, `metafs_root_lookup`, `metafs_mount_lookup`, `metafs_options_lookup`, `metafs_option_values_lookup`, `metafs_get_option_values`, `metafs_get_link`, `metafs_readlink`, alongside `mnt_show_options`, `sb_add_option`, `sb_add_option_path`, `ovl_get_options`, and `erofs_get_options`. Those seem to be the critical pieces.

I need to make sure I have the right kernel configs enabled. It looks like `EROFS_FS` and `OVERLAY_FS` are essential. I should probably double-check if there are any other dependencies, just to be thorough. Right now, it looks like `metafs` itself is built-in. I also noted that the `O_ALT` flag is added to `O_PATH_FLAGS`, and `OPEN_TREE_ALT` flag is added to `open_tree`. The `MNT_CLONABLE` flag is added to `nsfs`, `pidfs`, and `overlayfs` layers.

Now, an important question is if any other filesystems are utilizing that `get_options` function. The patch mentions changes to `erofs` and `overlayfs`, but I should verify that's the extent of the impact.

Okay, let's verify that `metafs_get_root` is indeed reachable. Looking at `fs/namei.c`, in `path_init`, there's a conditional: if `flags & LOOKUP_ALT` is set, and it jumps to `metafs_get_root`. Okay, perfect. The `LOOKUP_ALT` flag is set in `build_open_flags` if `O_ALT` is present. And `O_ALT` is equal to `1 << 27`. So, an `openat(..., O_ALT, ...)` call will absolutely trigger `metafs_get_root`. That makes it testable and reachable. Excellent! I think I have a clear picture.

Now, I think I'm ready to start preparing the results and the test strategy.



9/2 2026/07/15 16:09 tool set-results
0m Args:
{
  "EnableConfigs": [
    "EROFS_FS",
    "OVERLAY_FS"
  ],
  "FocusSymbols": [
    "metafs_get_root",
    "metafs_create_root",
    "metafs_root_lookup",
    "metafs_mount_lookup",
    "metafs_options_lookup",
    "metafs_option_values_lookup",
    "metafs_get_option_values",
    "metafs_get_link",
    "metafs_readlink",
    "mnt_show_options",
    "sb_add_option",
    "sb_add_option_path",
    "ovl_get_options",
    "erofs_get_options"
  ],
  "Reasoning": "The patch introduces a new virtual filesystem `metafs` that exposes metadata about files and filesystems via the `O_ALT` flag. It also introduces a new `get_options` super operation, implemented by `erofs` and `overlayfs`. These are significant functional changes to the VFS and specific filesystems that should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "EROFS_FS",
    "OVERLAY_FS"
  ],
  "FocusSymbols": [
    "metafs_get_root",
    "metafs_create_root",
    "metafs_root_lookup",
    "metafs_mount_lookup",
    "metafs_options_lookup",
    "metafs_option_values_lookup",
    "metafs_get_option_values",
    "metafs_get_link",
    "metafs_readlink",
    "mnt_show_options",
    "sb_add_option",
    "sb_add_option_path",
    "ovl_get_options",
    "erofs_get_options"
  ],
  "Reasoning": "The patch introduces a new virtual filesystem `metafs` that exposes metadata about files and filesystems via the `O_ALT` flag. It also introduces a new `get_options` super operation, implemented by `erofs` and `overlayfs`. These are significant functional changes to the VFS and specific filesystems that should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)