[Devel] [PATCH RH9 04/13] ve/kernfs: add new interface to control per-VE nodes visibility

Pavel Tikhomirov ptikhomirov at virtuozzo.com
Tue Sep 21 19:04:22 MSK 2021


From: Stanislav Kinsburskiy <skinsbursky at virtuozzo.com>

This patch allows to set kernfs nodes visibility via kernfs_perms_* helpers.
There is an internal kernel interface. I.e. it should to be wrapped by actual
file operations for a desired file system.
These four should be used for sequentialpremissions  reading:

  void *kernfs_perms_start(struct seq_file *m, loff_t *ppos,
                           struct kernfs_node *root, struct kmapset_key *key);

  void *kernfs_perms_next(struct seq_file *m, void *v, loff_t *ppos,
                          struct kmapset_key *key);

  void kernfs_perms_stop(struct seq_file *m, void *v);

  int kernfs_perms_show(struct seq_file *m, void *v, struct kmapset_key *key);

And this one for permissions writing:

  ssize_t kernfs_perms_write(struct kernfs_open_file *of,
                             char *buf, size_t nbytes, loff_t off,
                             struct kernfs_node *root, struct kmapset_key *key);

They do all the job. All is needed is to pass file system root and pointer
per-ve key.

Permissions are controlled via human-readable interface with the following
format:
    <path> [r][w][x][-]

Helpers accepts multi-line input, so they are 'cat' compatible.

An example of default config for sysfs:

/ rx
block rx
class rx
class/block rx
class/net rx
class/tty rx
class/mem rx
devices rx
devices/virtual rx
devices/virtual/tty rx
devices/virtual/tty/tty rx
devices/virtual/tty/tty/uevent rw
devices/virtual/tty/tty/dev r
devices/virtual/tty/console rx
devices/virtual/tty/console/uevent rw
devices/virtual/tty/console/dev r
devices/virtual/tty/ptmx rx
devices/virtual/tty/ptmx/uevent rw
devices/virtual/tty/ptmx/dev r
devices/virtual/tty/tty0 rx
devices/virtual/tty/tty0/uevent rw
devices/virtual/tty/tty0/dev r
devices/virtual/tty/tty1 rx
devices/virtual/tty/tty1/uevent rw
devices/virtual/tty/tty1/dev r
devices/virtual/tty/tty2 rx
devices/virtual/tty/tty2/uevent rw
devices/virtual/tty/tty2/dev r
devices/virtual/mem rx
devices/virtual/mem/null rx
devices/virtual/mem/null/uevent rw
devices/virtual/mem/null/dev r
devices/virtual/mem/zero rx
devices/virtual/mem/zero/uevent rw
devices/virtual/mem/zero/dev r
devices/virtual/mem/full rx
devices/virtual/mem/full/uevent rw
devices/virtual/mem/full/dev r
devices/virtual/mem/random rx
devices/virtual/mem/random/uevent rw
devices/virtual/mem/random/dev r
devices/virtual/mem/urandom rx
devices/virtual/mem/urandom/uevent rw
devices/virtual/mem/urandom/dev r
devices/virtual/net rx
devices/system rx
devices/system/cpu rx
devices/system/cpu/cpu0 rx
devices/system/cpu/cpu1 rx
dev rx
dev/block rx
dev/char rx
fs rx
fs/cgroup rx
kernel rx
kernel/uevent_seqnum r

Signed-off-by: Konstantin Khlebnikov <khlebnikov at openvz.org>
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
Signed-off-by: Stanislav Kinsburskiy <skinsbursky at virtuozzo.com>

(cherry-picked from vz8 commit 294d1d1c97258748b13e7663ca470f97d4b90328)
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
---
 fs/kernfs/ve.c            | 226 ++++++++++++++++++++++++++++++++++++++
 include/linux/kernfs-ve.h |  13 +++
 2 files changed, 239 insertions(+)

diff --git a/fs/kernfs/ve.c b/fs/kernfs/ve.c
index 383724fb2dae..8abb020de40f 100644
--- a/fs/kernfs/ve.c
+++ b/fs/kernfs/ve.c
@@ -126,3 +126,229 @@ bool kernfs_d_visible(struct kernfs_node *kn, struct kernfs_super_info *info)
 	return !!kmapset_get_value(kn->ve_perms_map,
 				   kernfs_info_perms_key(info));
 }
+
+#define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb)
+
+static struct kernfs_node *kernfs_next_recursive(struct kernfs_node *kn)
+{
+	struct rb_node *node;
+
+	if (kernfs_type(kn) == KERNFS_DIR &&
+			!RB_EMPTY_ROOT(&kn->dir.children)) {
+		return rb_to_kn(rb_first(&kn->dir.children));
+	}
+
+	do {
+		node = rb_next(&kn->rb);
+		if (node)
+			return rb_to_kn(node);
+		kn = kn->parent;
+	} while (kn);
+
+	return kn;
+}
+
+static bool kernfs_perms_shown(struct ve_struct *ve, struct kernfs_node *kn,
+			       struct kmapset_key *key)
+{
+	if (ve_is_super(ve))
+		return kn->ve_perms_map->default_value != 0;
+	return kmapset_lookup(kn->ve_perms_map, key) != NULL;
+}
+
+void *kernfs_perms_start(struct seq_file *m, loff_t *ppos,
+			 struct kernfs_node *root, struct kmapset_key *key)
+{
+	struct ve_struct *ve = css_to_ve(seq_css(m));
+	struct kernfs_node *kn;
+	loff_t pos = *ppos;
+
+	mutex_lock(&kernfs_mutex);
+	for (kn = root; kn; kn = kernfs_next_recursive(kn)) {
+		if (kernfs_perms_shown(ve, kn, key) && !pos--)
+			break;
+	};
+	return kn;
+}
+
+void *kernfs_perms_next(struct seq_file *m, void *v, loff_t *ppos,
+			      struct kmapset_key *key)
+{
+	struct ve_struct *ve = css_to_ve(seq_css(m));
+	struct kernfs_node *kn = v;
+
+	(*ppos)++;
+	while ((kn = kernfs_next_recursive(kn))) {
+		if (kernfs_perms_shown(ve, kn, key))
+			break;
+	};
+	return kn;
+}
+
+void kernfs_perms_stop(struct seq_file *m, void *v)
+{
+	mutex_unlock(&kernfs_mutex);
+}
+
+int kernfs_perms_show(struct seq_file *m, void *v, struct kmapset_key *key)
+{
+	struct ve_struct *ve = css_to_ve(seq_css(m));
+	struct kernfs_node *kn = v;
+	char *buf;
+	size_t size, len, off;
+	int mask;
+
+	if (ve_is_super(ve))
+		mask = kn->ve_perms_map->default_value;
+	else
+		mask = kmapset_get_value(kn->ve_perms_map, key);
+
+	size = seq_get_buf(m, &buf);
+	if (size) {
+		off = size;
+		do {
+			len = strlen(kn->name);
+			if (len >= off) {
+				seq_commit(m, -1);
+				return 0;
+			}
+			if (kernfs_type(kn) == KERNFS_DIR)
+				buf[--off] = '/';
+			off -= len;
+			memcpy(buf + off, kn->name, len);
+			kn = kn->parent;
+		} while (kn && kn != kernfs_root(kn)->kn);
+		memmove(buf, buf + off, size - off);
+		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');
+
+	return 0;
+}
+
+static 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;
+	char *sep = path, *dname;
+	int ret;
+
+	kernfs_get(kn);
+	do {
+		dname = sep;
+
+		sep = strchr(sep, '/');
+		if (sep)
+			*sep++ = 0;
+
+		if (!*dname)
+			break;
+
+		nkn = kernfs_find_and_get(kn, dname);
+		if (!nkn) {
+			ret = -ENOENT;
+			goto out;
+		}
+
+		kernfs_put(kn);
+		kn = nkn;
+	} while (sep);
+
+	ret = -ENOMEM;
+	map = kmapset_dup(kn->ve_perms_map);
+	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) {
+		map = kmapset_commit(map);
+		swap(map, kn->ve_perms_map);
+	}
+
+out_put:
+	kmapset_put(map);
+out:
+	kernfs_put(kn);
+	return ret;
+}
+
+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;
+		}
+	}
+}
+
+ssize_t kernfs_perms_write(struct kernfs_open_file *of,
+			   char *buf, size_t nbytes, loff_t off,
+			   struct kernfs_node *root, struct kmapset_key *key)
+{
+	struct ve_struct *ve = css_to_ve(of_css(of));
+	char *line, *next = buf;
+	int ret = -EINVAL;
+
+	do {
+		line = skip_spaces(next);
+		if (!*line)
+			break;
+
+		next = strchr(line, '\n');
+		if (next)
+			*next++ = '\0';
+
+		if (*line != '#') {
+			ret = kernfs_perms_line(ve, line, root, key);
+			if (ret)
+				break;
+		}
+	} while (next);
+	return ret ? ret : nbytes;
+}
diff --git a/include/linux/kernfs-ve.h b/include/linux/kernfs-ve.h
index 791d6fb2e8ad..bdef9b5ada70 100644
--- a/include/linux/kernfs-ve.h
+++ b/include/linux/kernfs-ve.h
@@ -18,6 +18,19 @@ struct dentry;
 int kernfs_init_ve_perms(struct kernfs_root *root,
 			 struct kmapset_set *perms_set);
 void kernfs_set_ve_perms(struct dentry *root, off_t key_off);
+
+void *kernfs_perms_start(struct seq_file *m, loff_t *ppos,
+			 struct kernfs_node *root, struct kmapset_key *key);
+void *kernfs_perms_next(struct seq_file *m, void *v, loff_t *ppos,
+			      struct kmapset_key *key);
+void kernfs_perms_stop(struct seq_file *m, void *v);
+
+int kernfs_perms_show(struct seq_file *m, void *v, struct kmapset_key *key);
+
+ssize_t kernfs_perms_write(struct kernfs_open_file *of,
+			   char *buf, size_t nbytes, loff_t off,
+			   struct kernfs_node *root, struct kmapset_key *key);
+
 #else   /* CONFIG_VE */
 static inline int kernfs_init_ve_perms(struct kernfs_root *root,
 				       struct kmapset_set *perms_set)
-- 
2.31.1



More information about the Devel mailing list