[Devel] [PATCH v7 VZ10] fs: enforce container device-mount policy in the common mount path

Konstantin Khorenko khorenko at virtuozzo.com
Fri Jul 31 17:31:35 MSK 2026


Problem: a trap on the legacy mount path.

For filesystems still on the legacy API the two halves of the devmnt
policy no longer meet. Here is the whole picture as a call stack:

  do_new_mount()
    parse_monolithic_mount_data(fc, data)
      legacy_parse_monolithic()
          /*
           * Legacy fs: the option string is only stashed away into
           * ctx->legacy_data for the fs to parse later. Unlike
           * generic_parse_monolithic() -> vfs_parse_monolithic_sep(),
           * neither ve_devmnt_process() runs here nor is
           * fc->ve_final_opts populated - it stays NULL.
           */
    vfs_get_tree(fc)
      fc->ops->get_tree(fc)                       /* legacy_get_tree() */
        fc->fs_type->mount()                      /* e.g. ext2_mount() */
          mount_bdev()
            ve_devmnt_process(ve, dev, &data, 0)  /* fs/super.c:1767 */
                /*
                 * The "old" enforcement: allowed_options are checked
                 * and hidden_options are inserted into @data...
                 */
            fill_super(s, data, ...)
                /*
                 * ...and the fs does parse them. So far so good.
                 */
      ve_devmnt_verify_fc(fc, true)               /* fs/super.c:1933 */
          /*
           * The "new" enforcement. It builds the option string to vet
           * from fc->ve_final_opts plus the synthesized ro/rw flag
           * token. ve_final_opts is NULL on this path (see above), so
           * opts ends up being just "rw".
           */
        ve_devmnt_verify(ve, dev, opts = "rw", new_mount = true, ...)
          ve_devmnt_options_subset(hidden, opts, NULL)
              /*
               * On a new mount every hidden option must be present in
               * opts: hidden = "balloon_ino=42" vs opts = "rw" ->
               * -EPERM. The mount is refused even though mount_bdev()
               * inserted the hidden options and the fs consumed them
               * just fine. Any listed device with non-empty
               * hidden_options is unmountable through a legacy fs,
               * unconditionally.
               */

In practice this looks unreachable today: every FS_VIRTUALIZED
block-device filesystem in 6.12 has been converted to fs_context,
and the typical hidden options (e.g. balloon_ino=...) target ext4,
which is on the new API. But if a legacy filesystem ever ends up in
the VE whitelist, this will be painful to debug: the mount fails with
a ratelimited "mount options not permitted" message that points at
the options, while the actual culprit is the missing ve_final_opts
stash on this path.

At a minimum, please add a comment at the mount_bdev() hook stating
that the ve_devmnt policy verification in vfs_get_tree() will refuse
any listed device with hidden_options mounted through this path.
Alternatively, declare the legacy path unsupported and drop the hook
there altogether, so the failure mode is explicit rather than
accidental.

#########################################



On 7/23/26 15:49, Vasileios Almpanis wrote:
...
> diff --git a/fs/internal.h b/fs/internal.h
> index 3647ce69b2c7..e33d3ae70cce 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -46,6 +46,7 @@ extern void __init chrdev_init(void);
>   */
>  extern const struct fs_context_operations legacy_fs_context_ops;
>  extern int parse_monolithic_mount_data(struct fs_context *, void *);
> +extern int ve_devmnt_verify_fc(struct fs_context *fc, bool new_mount);

+#ifdef CONFIG_VE
+extern int ve_devmnt_verify_fc(struct fs_context *fc, bool new_mount);
+#else
+static inline int ve_devmnt_verify_fc(struct fs_context *fc, bool new_mount)
+{
+       return 0;
+}
+#endif

>  extern void vfs_clean_context(struct fs_context *fc);
>  extern int finish_clean_context(struct fs_context *fc);
>  


More information about the Devel mailing list