The per-VE r/w/x permission logic (line parse, kmapset apply, access check, visibility, listing predicate, mask output) lives in fs/kernfs/ve.c and is needed verbatim by an upcoming procfs implementation. Move the filesystem agnostic parts into fs/ve_perms.c plus include/linux/ve-perms.h, operating purely on a kmapset map, key and mask, and convert fs/kernfs/ve.c to call them. The kernfs function signatures are unchanged so fs/sysfs is untouched.
ve_perms_apply() takes the node map slot as kmapset_map __rcu ** and keeps the rcu_assign_pointer publish, matching the __rcu annotation added in the previous patch. The rcu_read_lock / rcu_dereference wrapping stays in the kernfs readers. No functional change. https://virtuozzo.atlassian.net/browse/VSTOR-135286 Signed-off-by: Mirian Shilakadze <[email protected]> --- fs/Makefile | 1 + fs/kernfs/ve.c | 99 ++++++---------------------- fs/ve_perms.c | 136 +++++++++++++++++++++++++++++++++++++++ include/linux/ve-perms.h | 28 ++++++++ 4 files changed, 183 insertions(+), 81 deletions(-) create mode 100644 fs/ve_perms.c create mode 100644 include/linux/ve-perms.h diff --git a/fs/Makefile b/fs/Makefile index 73512f13e969..89242a58f1af 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -17,6 +17,7 @@ obj-y := open.o read_write.o file_table.o super.o \ fs_types.o fs_context.o fs_parser.o fsopen.o init.o \ kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o +obj-$(CONFIG_VE) += ve_perms.o obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o obj-$(CONFIG_PROC_FS) += proc_namespace.o obj-$(CONFIG_LEGACY_DIRECT_IO) += direct-io.o diff --git a/fs/kernfs/ve.c b/fs/kernfs/ve.c index 695aac1cfe9d..630ea34d1cca 100644 --- a/fs/kernfs/ve.c +++ b/fs/kernfs/ve.c @@ -18,6 +18,7 @@ #include <linux/kmapset.h> #include <linux/rcupdate.h> #include <linux/kernfs-ve.h> +#include <linux/ve-perms.h> #include "kernfs-internal.h" #include "kernfs-ve.h" @@ -62,7 +63,7 @@ int kernfs_ve_permission(struct kernfs_node *kn, struct kernfs_super_info *info, int mask) { struct kernfs_node *tmp_kn = kn; - int perm; + int ret; if (kernfs_ve_allowed(kn)) return 0; @@ -80,13 +81,10 @@ int kernfs_ve_permission(struct kernfs_node *kn, if (kernfs_type(kn) == KERNFS_LINK) kn = kn->symlink.target_kn; - perm = kmapset_get_value(rcu_dereference(kn->ve_perms_map), - kernfs_info_perms_key(info)); + ret = ve_perms_check(rcu_dereference(kn->ve_perms_map), + kernfs_info_perms_key(info), mask); rcu_read_unlock(); - if ((mask & ~perm & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) - return 0; - - return -EACCES; + return ret; } void kernfs_get_ve_perms(struct kernfs_node *kn) @@ -135,8 +133,8 @@ bool kernfs_d_visible(struct kernfs_node *kn, struct kernfs_super_info *info) if (kernfs_type(kn) == KERNFS_LINK) kn = kn->symlink.target_kn; - visible = !!kmapset_get_value(rcu_dereference(kn->ve_perms_map), - kernfs_info_perms_key(info)); + visible = ve_perms_visible(rcu_dereference(kn->ve_perms_map), + kernfs_info_perms_key(info)); rcu_read_unlock(); return visible; } @@ -172,11 +170,9 @@ static bool kernfs_perms_shown(struct ve_struct *ve, struct kernfs_node *kn, if (!map) return false; - if (ve_is_super(ve)) - return map->default_value != 0; - /* kmapset_lookup() walks an rcu list, so keep the section to the lookup. */ + /* ve_perms_shown() -> kmapset_lookup() walks an rcu list, so guard the call. */ rcu_read_lock(); - ret = kmapset_lookup(map, key) != NULL; + ret = ve_perms_shown(map, key, ve_is_super(ve)); rcu_read_unlock(); return ret; } @@ -250,19 +246,7 @@ int kernfs_perms_show(struct seq_file *m, void *v, struct kmapset_key *key) seq_commit(m, size - off); } - seq_putc(m, ' '); - - if (!mask) - seq_putc(m, '-'); - if (mask & MAY_READ) - seq_putc(m, 'r'); - if (mask & MAY_WRITE) - seq_putc(m, 'w'); - if (mask & MAY_EXEC) - seq_putc(m, 'x'); - - seq_putc(m, '\n'); - + ve_perms_emit(m, mask); return 0; } @@ -270,9 +254,8 @@ int kernfs_perms_set(char *path, struct ve_struct *ve, int mask, struct kernfs_node *root, struct kmapset_key *key) { struct kernfs_node *kn = root, *nkn; - struct kmapset_map *map = NULL, *old; char *sep = path, *dname; - int ret; + int ret = 0; kernfs_get(kn); do { @@ -295,30 +278,7 @@ int kernfs_perms_set(char *path, struct ve_struct *ve, int mask, kn = nkn; } while (sep); - ret = -ENOMEM; - /* the caller (fs/sysfs/ve.c) holds sysfs_perms_mutex vs other writers */ - map = kmapset_dup(rcu_dereference_protected(kn->ve_perms_map, true)); - if (!map) - goto out_put; - - ret = 0; - if (ve_is_super(ve)) { - kmapset_set_default(map, mask > 0 ? mask : 0); - } else if (mask < 0) { - kmapset_del_value(map, key); - } else { - ret = kmapset_set_value(map, key, mask); - } - - if (!ret) { - old = rcu_dereference_protected(kn->ve_perms_map, true); - rcu_assign_pointer(kn->ve_perms_map, kmapset_commit(map)); - kmapset_put(old); - map = NULL; - } - -out_put: - kmapset_put(map); + ret = ve_perms_apply(&kn->ve_perms_map, key, ve_is_super(ve), mask); out: kernfs_put(kn); return ret; @@ -327,35 +287,12 @@ int kernfs_perms_set(char *path, struct ve_struct *ve, int mask, static int kernfs_perms_line(struct ve_struct *ve, char *line, struct kernfs_node *root, struct kmapset_key *key) { - int mask = 0; - char *p; - - p = strpbrk(line, " \t"); - if (!p) - return -EINVAL; - *p++ = 0; - p = skip_spaces(p); - while (1) { - switch (*p++) { - case 'r': - mask |= MAY_READ; - break; - case 'w': - mask |= MAY_WRITE; - break; - case 'x': - mask |= MAY_EXEC; - break; - case '-': - mask = -1; - break; - case 0: - return kernfs_perms_set(line, ve, mask, - root, key); - default: - return -EINVAL; - } - } + int mask, ret; + + ret = ve_perms_parse(line, &mask); + if (ret) + return ret; + return kernfs_perms_set(line, ve, mask, root, key); } ssize_t kernfs_perms_write(struct kernfs_open_file *of, diff --git a/fs/ve_perms.c b/fs/ve_perms.c new file mode 100644 index 000000000000..bba08d53b205 --- /dev/null +++ b/fs/ve_perms.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Filesystem-agnostic per-VE permission helpers shared by the kernfs + * (sysfs) and procfs implementations. Every helper works purely on a + * kmapset map, key and mask and never touches a filesystem node or a + * lock, so the caller owns the tree walk and all locking. + * + * Copyright (c) 2026 Virtuozzo International GmbH. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/seq_file.h> +#include <linux/rcupdate.h> +#include <linux/kmapset.h> +#include <linux/ve-perms.h> + +/* + * Parse one "path mask" line in place. On success @line is left as the NUL + * terminated path and *@maskp holds the rwx mask, or -1 to remove the entry. + */ +int ve_perms_parse(char *line, int *maskp) +{ + int mask = 0; + char *p; + + p = strpbrk(line, " \t"); + if (!p) + return -EINVAL; + *p++ = 0; + p = skip_spaces(p); + while (1) { + switch (*p++) { + case 'r': + mask |= MAY_READ; + break; + case 'w': + mask |= MAY_WRITE; + break; + case 'x': + mask |= MAY_EXEC; + break; + case '-': + mask = -1; + break; + case 0: + *maskp = mask; + return 0; + default: + return -EINVAL; + } + } +} + +/* + * Apply @mask for @key to the node map at *@slot, copy on write. A super VE + * sets the map default, a negative mask removes the entry. The new map is + * published with rcu_assign_pointer and the old one dropped, which kmapset + * frees via RCU, so a reader that dereferences *@slot under rcu_read_lock + * cannot use it after free. + * The caller must serialise writers and may sleep here. + */ +int ve_perms_apply(struct kmapset_map __rcu **slot, struct kmapset_key *key, + bool super, int mask) +{ + struct kmapset_map *map, *old; + int ret = 0; + + map = kmapset_dup(rcu_dereference_protected(*slot, true)); + if (!map) + return -ENOMEM; + + if (super) + kmapset_set_default(map, mask > 0 ? mask : 0); + else if (mask < 0) + kmapset_del_value(map, key); + else + ret = kmapset_set_value(map, key, mask); + + if (ret) { + kmapset_put(map); + return ret; + } + + old = rcu_dereference_protected(*slot, true); + rcu_assign_pointer(*slot, kmapset_commit(map)); + kmapset_put(old); + return 0; +} + +/* + * 0 if every requested rwx bit (@mask & MAY_READ|MAY_WRITE|MAY_EXEC) is + * granted to @key by @map, else -EACCES. + */ +int ve_perms_check(struct kmapset_map *map, struct kmapset_key *key, int mask) +{ + int perm = kmapset_get_value(map, key); + + if ((mask & ~perm & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) + return 0; + return -EACCES; +} + +/* True if @key has any access on @map, used as the visibility test. */ +bool ve_perms_visible(struct kmapset_map *map, struct kmapset_key *key) +{ + return !!kmapset_get_value(map, key); +} + +/* + * True if the node should be listed when dumping @key's entries. A super VE + * lists nodes whose default is non zero. kmapset_lookup walks an rcu list, + * so the caller must hold rcu_read_lock or the kmapset set mutex. + */ +bool ve_perms_shown(struct kmapset_map *map, struct kmapset_key *key, bool super) +{ + if (super) + return map->default_value != 0; + return kmapset_lookup(map, key) != NULL; +} + +/* Emit " rwx\n" (or " -\n") after a path already written to @m. */ +void ve_perms_emit(struct seq_file *m, int mask) +{ + seq_putc(m, ' '); + if (!mask) + seq_putc(m, '-'); + if (mask & MAY_READ) + seq_putc(m, 'r'); + if (mask & MAY_WRITE) + seq_putc(m, 'w'); + if (mask & MAY_EXEC) + seq_putc(m, 'x'); + seq_putc(m, '\n'); +} diff --git a/include/linux/ve-perms.h b/include/linux/ve-perms.h new file mode 100644 index 000000000000..c9bf44b1b96b --- /dev/null +++ b/include/linux/ve-perms.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Filesystem-agnostic per-VE permission helpers, intended to be shared + * by the kernfs (sysfs) and procfs per-VE permission implementations. + * + * Copyright (c) 2026 Virtuozzo International GmbH. All rights reserved. + */ +#ifndef _LINUX_VE_PERMS_H +#define _LINUX_VE_PERMS_H + +#include <linux/types.h> + +struct kmapset_map; +struct kmapset_key; +struct seq_file; + +#ifdef CONFIG_VE +int ve_perms_parse(char *line, int *maskp); +int ve_perms_apply(struct kmapset_map __rcu **slot, struct kmapset_key *key, + bool super, int mask); +int ve_perms_check(struct kmapset_map *map, struct kmapset_key *key, int mask); +bool ve_perms_visible(struct kmapset_map *map, struct kmapset_key *key); +bool ve_perms_shown(struct kmapset_map *map, struct kmapset_key *key, + bool super); +void ve_perms_emit(struct seq_file *m, int mask); +#endif + +#endif /* _LINUX_VE_PERMS_H */ -- 2.43.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
