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

Reply via email to