[Devel] [PATCH VZ10 1/1] fs: namespace: transform mount flags to comma separated values

Vladimir Riabchun vladimir.riabchun at virtuozzo.com
Tue Jun 2 14:05:17 MSK 2026



On 6/2/26 10:14, Vasileios Almpanis wrote:
> In legacy mount callpaths, userspace might pass mount options as
> flags. These flags escape our checks in ve_devmnt_process allowing
> devices to be mounted inside containers with options not specified in
> the allowed field. Introduce helpers that take these flags and
> already existing tables of flag -> string representation to construct
> a comma separated value string from them, and append them to userspace
> provided data. Then pass this string to parse_monolithic_mount_data
> enforcing the same checks symmetrically in both mount and fsconfig
> syscalls.
> 
> In the remount path, run legacy_merge_mount_data() before
> ve_devmnt_process() so container device mount policy sees MS_* flags
> from the legacy mount(2) API, not only the user-supplied option string.
> Keep ve_prepare_mount_options() for legacy parsers that do not use
> generic_parse_monolithic().
> 
> https://virtuozzo.atlassian.net/browse/VSTOR-132330
> Signed-off-by: Vasileios Almpanis <vasileios.almpanis at virtuozzo.com>
> 
> Feature: ve: ve generic structures
> ---
>   fs/fs_context.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++
>   fs/internal.h   |   8 ++++
>   fs/namespace.c  |  33 +++++++++++----
>   3 files changed, 139 insertions(+), 7 deletions(-)
> 
> diff --git a/fs/fs_context.c b/fs/fs_context.c
> index 76f34f3d468e..65495421fa9e 100644
> --- a/fs/fs_context.c
> +++ b/fs/fs_context.c
> @@ -81,6 +81,111 @@ static int vfs_parse_sb_flag(struct fs_context *fc, const char *key)
>   	return -ENOPARAM;
>   }
>   
> +#ifdef CONFIG_VE
> +/*
> + * Format fc->sb_flags as comma-separated mount option names (ro, sync, ...).
> + * Matches vfs_parse_sb_flag() / common_{set,clear}_sb_flag tables.
> + */
> +static int vfs_format_sb_flags(struct fs_context *fc, char *buf, size_t buflen)
> +{
> +	const struct constant_table *p;
> +	unsigned int flags = fc->sb_flags;
> +	unsigned int mask = fc->sb_flags_mask;
> +	size_t len = 0;
> +	int token;
> +
> +	for (p = common_set_sb_flag; p->name; p++) {
> +		size_t namelen = strlen(p->name);
> +
> +		token = p->value;
> +		if (!(flags & token))
> +			continue;
> +		if (len + namelen + 2 > buflen)
> +			return -ENOSPC;
> +		if (len)
> +			buf[len++] = ',';
> +		memcpy(buf + len, p->name, namelen);
> +		len += namelen;
> +	}
> +
> +	for (p = common_clear_sb_flag; p->name; p++) {
> +		size_t namelen = strlen(p->name);
> +
> +		token = p->value;
> +		if (!(mask & token) || (flags & token))
> +			continue;
> +		if (len + namelen + 2 > buflen)
> +			return -ENOSPC;
> +		if (len)
> +			buf[len++] = ',';

We do this thing three times, and there are several a bit cryptic size 
checks with constants +1 and +2. Let's have a helper instead

char* append_entry(char *dst, size_t *len, size_t cap, const char *s)
{
	size_t s_len = strlen(s);
	size_t write_chars = (s_len > 0 && *len) + s_len;
	if (*len + write_chars > cap)
		return ERR_PTR(-ENOSPC);
	if (*len)
		*dst++ = ',';
	memcpy(dst, s, s_len);
	*len += write_chars;
	return dst + s_len;
}
(I hope it works...)

This will shorten the code and unify space checks.

> +		memcpy(buf + len, p->name, namelen);
> +		len += namelen;
> +	}
> +
> +	buf[len] = '\0';
> +	return len;
> +}
> +
> +/*
> + * For legacy mount(2), MS_* mount flags are folded into fc->sb_flags and are
> + * not present in the monolithic data string.  Build a page holding those
> + * options plus any user data for ve_devmnt checks in vfs_parse_monolithic_sep.
> + *
> + * Returns @data when nothing needs to be added, a new page otherwise, or
> + * ERR_PTR() on failure.  The caller must free_page() when the result != @data.
> + */
> +void *legacy_merge_mount_data(struct fs_context *fc, void *data)
> +{
> +	struct ve_struct *ve = get_exec_env();
> +	char flags_buf[128];
> +	size_t data_len = 0;
> +	size_t total;
> +	char *page, *p;
> +	int flags_len;
> +
> +	if (ve_is_super(ve))
> +		return data;
> +
> +	if (!fc->fs_type || !(fc->fs_type->fs_flags & FS_REQUIRES_DEV))
> +		return data;
> +
> +	if (fc->fs_type->fs_flags & FS_BINARY_MOUNTDATA)
> +		return data;
> +
> +	flags_len = vfs_format_sb_flags(fc, flags_buf, sizeof(flags_buf));
> +	if (flags_len < 0)
> +		return ERR_PTR(flags_len);
> +
> +	if (!flags_len)
> +		return data;
> +
> +	if (data && *(char *)data)
> +		data_len = strlen(data);
> +
> +	total = flags_len + 1;
> +	if (data_len)
> +		total += data_len + 1;
> +
> +	if (total > PAGE_SIZE)
> +		return ERR_PTR(-EINVAL);

Inconsistent with `return -ENOSPC` from vfs_format_sb_flags.

> +
> +	page = (char *)__get_free_page(GFP_KERNEL);> +	if (!page)
> +		return ERR_PTR(-ENOMEM);
> +
> +	p = page;
> +	memcpy(p, flags_buf, flags_len);
> +	p += flags_len;
> +	if (data_len) {
> +		*p++ = ',';
> +		memcpy(p, data, data_len);
> +		p += data_len;> +	}

What if we allocate page unconditionally, save data first there and
them append new flags using vfs_format_sb_flags?

This way we avoid all these len checks, games with +1 and additional 
buffer. Also, this will simplify cleanup since we always need to free 
the pointer.

> +	*p = '\0';
> +	return page;
> +}
> +#endif /* CONFIG_VE */
> +
>   /**
>    * vfs_parse_fs_param_source - Handle setting "source" via parameter
>    * @fc: The filesystem context to modify
> diff --git a/fs/internal.h b/fs/internal.h
> index 0064345cb9d0..120c239df394 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -46,6 +46,14 @@ 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 *);
> +#ifdef CONFIG_VE
> +void *legacy_merge_mount_data(struct fs_context *fc, void *data);
> +#else
> +static inline void *legacy_merge_mount_data(struct fs_context *fc, void *data)
> +{
> +	return data;
> +}
> +#endif
>   extern void vfs_clean_context(struct fs_context *fc);
>   extern int finish_clean_context(struct fs_context *fc);
>   
> diff --git a/fs/namespace.c b/fs/namespace.c
> index acd4507e1247..27a72d4cb245 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -3295,6 +3295,7 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
>   	struct super_block *sb = path->mnt->mnt_sb;
>   	struct mount *mnt = real_mount(path->mnt);
>   	struct fs_context *fc;
> +	void *mnt_data = NULL;
>   
>   	if (!check_mnt(mnt))
>   		return -EINVAL;
> @@ -3315,13 +3316,18 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
>   	 */
>   	fc->oldapi = true;
>   
> -	err = ve_prepare_mount_options(fc, data);
> -	if (err) {
> -		put_fs_context(fc);
> -		return err;
> +	mnt_data = legacy_merge_mount_data(fc, data);
> +	if (IS_ERR(mnt_data)) {
> +		err = PTR_ERR(mnt_data);
> +		goto err;
>   	}
>   
> -	err = parse_monolithic_mount_data(fc, data);
> +	err = ve_prepare_mount_options(fc, mnt_data);
> +	if (err)
> +		goto free_mnt_data;
> +
> +	err = parse_monolithic_mount_data(fc, mnt_data);
> +
>   	if (!err) {
>   		down_write(&sb->s_umount);
>   		err = -EPERM;
> @@ -3338,6 +3344,10 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
>   
>   	mnt_warn_timestamp_expiry(path, &mnt->mnt);
>   
> +free_mnt_data:
> +	if (mnt_data && mnt_data != data)
> +		free_page((unsigned long)mnt_data);
> +err:
>   	put_fs_context(fc);
>   	return err;
>   }
> @@ -3774,8 +3784,17 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
>   					  subtype, strlen(subtype));
>   	if (!err && name)
>   		err = vfs_parse_fs_string(fc, "source", name, strlen(name));
> -	if (!err)
> -		err = parse_monolithic_mount_data(fc, data);
> +	if (!err) {
> +		void *mnt_data = legacy_merge_mount_data(fc, data);
> +
> +		if (IS_ERR(mnt_data)) {
> +			err = PTR_ERR(mnt_data);
> +		} else {
> +			err = parse_monolithic_mount_data(fc, mnt_data);
> +			if (mnt_data != data)
> +				free_page((unsigned long)mnt_data);
> +		}
> +	}
>   	if (!err && !mount_capable(fc))
>   		err = -EPERM;
>   	if (!err)

-- 
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo



More information about the Devel mailing list