[PATCH 10/14] kdbus: add name registry implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Daniel Mack <daniel@xxxxxxxxxx>

This patch adds the name registry implementation.

Each bus instantiates a name registry to resolve well-known names
into unique connection IDs for message delivery. The registry will
be queried when a message is sent with kdbus_msg.dst_id set to
KDBUS_DST_ID_NAME, or when a registry dump is requested.

It's important to have this registry implemented in the kernel to
implement lookups and take-overs in a race-free way.

Signed-off-by: Daniel Mack <daniel@xxxxxxxxxx>
Signed-off-by: David Herrmann <dh.herrmann@xxxxxxxxx>
Signed-off-by: Djalal Harouni <tixxdz@xxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
---
 ipc/kdbus/names.c | 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/names.h |  74 ++++++
 2 files changed, 846 insertions(+)
 create mode 100644 ipc/kdbus/names.c
 create mode 100644 ipc/kdbus/names.h

diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c
new file mode 100644
index 000000000000..657008e1bb37
--- /dev/null
+++ b/ipc/kdbus/names.c
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
+ * Copyright (C) 2013-2015 Daniel Mack <daniel@xxxxxxxxxx>
+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@xxxxxxxxx>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@xxxxxxxxxx>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/hash.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "names.h"
+#include "notify.h"
+#include "policy.h"
+
+struct kdbus_name_pending {
+	u64 flags;
+	struct kdbus_conn *conn;
+	struct kdbus_name_entry *name;
+	struct list_head conn_entry;
+	struct list_head name_entry;
+};
+
+static int kdbus_name_pending_new(struct kdbus_name_entry *e,
+				  struct kdbus_conn *conn, u64 flags)
+{
+	struct kdbus_name_pending *p;
+
+	kdbus_conn_assert_active(conn);
+
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->flags = flags;
+	p->conn = conn;
+	p->name = e;
+	list_add_tail(&p->conn_entry, &conn->names_queue_list);
+	list_add_tail(&p->name_entry, &e->queue);
+
+	return 0;
+}
+
+static void kdbus_name_pending_free(struct kdbus_name_pending *p)
+{
+	if (!p)
+		return;
+
+	list_del(&p->name_entry);
+	list_del(&p->conn_entry);
+	kfree(p);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash, const char *name)
+{
+	struct kdbus_name_entry *e;
+	size_t namelen;
+
+	namelen = strlen(name);
+
+	e = kmalloc(sizeof(*e) + namelen + 1, GFP_KERNEL);
+	if (!e)
+		return ERR_PTR(-ENOMEM);
+
+	e->name_id = ++r->name_seq_last;
+	e->flags = 0;
+	e->conn = NULL;
+	e->activator = NULL;
+	INIT_LIST_HEAD(&e->queue);
+	INIT_LIST_HEAD(&e->conn_entry);
+	hash_add(r->entries_hash, &e->hentry, hash);
+	memcpy(e->name, name, namelen + 1);
+
+	return e;
+}
+
+static void kdbus_name_entry_free(struct kdbus_name_entry *e)
+{
+	if (!e)
+		return;
+
+	WARN_ON(!list_empty(&e->conn_entry));
+	WARN_ON(!list_empty(&e->queue));
+	WARN_ON(e->activator);
+	WARN_ON(e->conn);
+
+	hash_del(&e->hentry);
+	kfree(e);
+}
+
+static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e,
+				       struct kdbus_conn *conn, u64 flags)
+{
+	WARN_ON(e->conn);
+
+	e->conn = kdbus_conn_ref(conn);
+	e->flags = flags;
+	atomic_inc(&conn->name_count);
+	list_add_tail(&e->conn_entry, &e->conn->names_list);
+}
+
+static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e)
+{
+	WARN_ON(!e->conn);
+
+	list_del_init(&e->conn_entry);
+	atomic_dec(&e->conn->name_count);
+	e->flags = 0;
+	e->conn = kdbus_conn_unref(e->conn);
+}
+
+static void kdbus_name_entry_replace_owner(struct kdbus_name_entry *e,
+					   struct kdbus_conn *conn, u64 flags)
+{
+	if (WARN_ON(!e->conn) || WARN_ON(conn == e->conn))
+		return;
+
+	kdbus_notify_name_change(conn->ep->bus, KDBUS_ITEM_NAME_CHANGE,
+				 e->conn->id, conn->id,
+				 e->flags, flags, e->name);
+	kdbus_name_entry_remove_owner(e);
+	kdbus_name_entry_set_owner(e, conn, flags);
+}
+
+/**
+ * kdbus_name_is_valid() - check if a name is valid
+ * @p:			The name to check
+ * @allow_wildcard:	Whether or not to allow a wildcard name
+ *
+ * A name is valid if all of the following criterias are met:
+ *
+ *  - The name has two or more elements separated by a period ('.') character.
+ *  - All elements must contain at least one character.
+ *  - Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_-"
+ *    and must not begin with a digit.
+ *  - The name must not exceed KDBUS_NAME_MAX_LEN.
+ *  - If @allow_wildcard is true, the name may end on '.*'
+ */
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard)
+{
+	bool dot, found_dot = false;
+	const char *q;
+
+	for (dot = true, q = p; *q; q++) {
+		if (*q == '.') {
+			if (dot)
+				return false;
+
+			found_dot = true;
+			dot = true;
+		} else {
+			bool good;
+
+			good = isalpha(*q) || (!dot && isdigit(*q)) ||
+				*q == '_' || *q == '-' ||
+				(allow_wildcard && dot &&
+					*q == '*' && *(q + 1) == '\0');
+
+			if (!good)
+				return false;
+
+			dot = false;
+		}
+	}
+
+	if (q - p > KDBUS_NAME_MAX_LEN)
+		return false;
+
+	if (dot)
+		return false;
+
+	if (!found_dot)
+		return false;
+
+	return true;
+}
+
+/**
+ * kdbus_name_registry_new() - create a new name registry
+ *
+ * Return: a new kdbus_name_registry on success, ERR_PTR on failure.
+ */
+struct kdbus_name_registry *kdbus_name_registry_new(void)
+{
+	struct kdbus_name_registry *r;
+
+	r = kmalloc(sizeof(*r), GFP_KERNEL);
+	if (!r)
+		return ERR_PTR(-ENOMEM);
+
+	hash_init(r->entries_hash);
+	init_rwsem(&r->rwlock);
+	r->name_seq_last = 0;
+
+	return r;
+}
+
+/**
+ * kdbus_name_registry_free() - drop a name reg's reference
+ * @reg:		The name registry, may be %NULL
+ *
+ * Cleanup the name registry's internal structures.
+ */
+void kdbus_name_registry_free(struct kdbus_name_registry *reg)
+{
+	if (!reg)
+		return;
+
+	WARN_ON(!hash_empty(reg->entries_hash));
+	kfree(reg);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_find(struct kdbus_name_registry *reg, u32 hash, const char *name)
+{
+	struct kdbus_name_entry *e;
+
+	lockdep_assert_held(&reg->rwlock);
+
+	hash_for_each_possible(reg->entries_hash, e, hentry, hash)
+		if (strcmp(e->name, name) == 0)
+			return e;
+
+	return NULL;
+}
+
+/**
+ * kdbus_name_lookup_unlocked() - lookup name in registry
+ * @reg:		name registry
+ * @name:		name to lookup
+ *
+ * This looks up @name in the given name-registry and returns the
+ * kdbus_name_entry object. The caller must hold the registry-lock and must not
+ * access the returned object after releasing the lock.
+ *
+ * Return: Pointer to name-entry, or NULL if not found.
+ */
+struct kdbus_name_entry *
+kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name)
+{
+	return kdbus_name_find(reg, kdbus_strhash(name), name);
+}
+
+/**
+ * kdbus_name_acquire() - acquire a name
+ * @reg:		The name registry
+ * @conn:		The connection to pin this entry to
+ * @name:		The name to acquire
+ * @flags:		Acquisition flags (KDBUS_NAME_*)
+ * @return_flags:	Pointer to return flags for the acquired name
+ *			(KDBUS_NAME_*), may be %NULL
+ *
+ * Callers must ensure that @conn is either a privileged bus user or has
+ * sufficient privileges in the policy-db to own the well-known name @name.
+ *
+ * Return: 0 success, negative error number on failure.
+ */
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+		       struct kdbus_conn *conn, const char *name,
+		       u64 flags, u64 *return_flags)
+{
+	struct kdbus_name_entry *e;
+	u64 rflags = 0;
+	int ret = 0;
+	u32 hash;
+
+	kdbus_conn_assert_active(conn);
+
+	down_write(&reg->rwlock);
+
+	if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) {
+		ret = -EPERM;
+		goto exit_unlock;
+	}
+
+	hash = kdbus_strhash(name);
+	e = kdbus_name_find(reg, hash, name);
+	if (!e) {
+		/* claim new name */
+
+		if (conn->activator_of) {
+			ret = -EINVAL;
+			goto exit_unlock;
+		}
+
+		e = kdbus_name_entry_new(reg, hash, name);
+		if (IS_ERR(e)) {
+			ret = PTR_ERR(e);
+			goto exit_unlock;
+		}
+
+		if (kdbus_conn_is_activator(conn)) {
+			e->activator = kdbus_conn_ref(conn);
+			conn->activator_of = e;
+		}
+
+		kdbus_name_entry_set_owner(e, conn, flags);
+		kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_ADD,
+					 0, e->conn->id, 0, e->flags, e->name);
+	} else if (e->conn == conn || e == conn->activator_of) {
+		/* connection already owns that name */
+		ret = -EALREADY;
+	} else if (kdbus_conn_is_activator(conn)) {
+		/* activator claims existing name */
+
+		if (conn->activator_of) {
+			ret = -EINVAL; /* multiple names not allowed */
+		} else if (e->activator) {
+			ret = -EEXIST; /* only one activator per name */
+		} else {
+			e->activator = kdbus_conn_ref(conn);
+			conn->activator_of = e;
+		}
+	} else if (e->flags & KDBUS_NAME_ACTIVATOR) {
+		/* claim name of an activator */
+
+		kdbus_conn_move_messages(conn, e->activator, 0);
+		kdbus_name_entry_replace_owner(e, conn, flags);
+	} else if ((flags & KDBUS_NAME_REPLACE_EXISTING) &&
+		   (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) {
+		/* claim name of a previous owner */
+
+		if (e->flags & KDBUS_NAME_QUEUE) {
+			/* move owner back to queue if they asked for it */
+			ret = kdbus_name_pending_new(e, e->conn, e->flags);
+			if (ret < 0)
+				goto exit_unlock;
+		}
+
+		kdbus_name_entry_replace_owner(e, conn, flags);
+	} else if (flags & KDBUS_NAME_QUEUE) {
+		/* add to waiting-queue of the name */
+
+		ret = kdbus_name_pending_new(e, conn, flags);
+		if (ret >= 0)
+			/* tell the caller that we queued it */
+			rflags |= KDBUS_NAME_IN_QUEUE;
+	} else {
+		/* the name is busy, return a failure */
+		ret = -EEXIST;
+	}
+
+	if (ret == 0 && return_flags)
+		*return_flags = rflags;
+
+exit_unlock:
+	up_write(&reg->rwlock);
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+static void kdbus_name_release_unlocked(struct kdbus_name_registry *reg,
+					struct kdbus_name_entry *e)
+{
+	struct kdbus_name_pending *p;
+
+	lockdep_assert_held(&reg->rwlock);
+
+	p = list_first_entry_or_null(&e->queue, struct kdbus_name_pending,
+				     name_entry);
+
+	if (p) {
+		/* give it to first active waiter in the queue */
+		kdbus_name_entry_replace_owner(e, p->conn, p->flags);
+		kdbus_name_pending_free(p);
+	} else if (e->activator && e->activator != e->conn) {
+		/* hand it back to an active activator connection */
+		kdbus_conn_move_messages(e->activator, e->conn, e->name_id);
+		kdbus_name_entry_replace_owner(e, e->activator,
+					       KDBUS_NAME_ACTIVATOR);
+	} else {
+		/* release the name */
+		kdbus_notify_name_change(e->conn->ep->bus,
+					 KDBUS_ITEM_NAME_REMOVE,
+					 e->conn->id, 0, e->flags, 0, e->name);
+		kdbus_name_entry_remove_owner(e);
+		kdbus_name_entry_free(e);
+	}
+}
+
+static int kdbus_name_release(struct kdbus_name_registry *reg,
+			      struct kdbus_conn *conn,
+			      const char *name)
+{
+	struct kdbus_name_pending *p;
+	struct kdbus_name_entry *e;
+	int ret = 0;
+
+	down_write(&reg->rwlock);
+	e = kdbus_name_find(reg, kdbus_strhash(name), name);
+	if (!e) {
+		ret = -ESRCH;
+	} else if (e->conn == conn) {
+		kdbus_name_release_unlocked(reg, e);
+	} else {
+		ret = -EADDRINUSE;
+		list_for_each_entry(p, &e->queue, name_entry) {
+			if (p->conn == conn) {
+				kdbus_name_pending_free(p);
+				ret = 0;
+				break;
+			}
+		}
+	}
+	up_write(&reg->rwlock);
+
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+/**
+ * kdbus_name_release_all() - remove all name entries of a given connection
+ * @reg:		name registry
+ * @conn:		connection
+ */
+void kdbus_name_release_all(struct kdbus_name_registry *reg,
+			    struct kdbus_conn *conn)
+{
+	struct kdbus_name_pending *p;
+	struct kdbus_conn *activator = NULL;
+	struct kdbus_name_entry *e;
+
+	down_write(&reg->rwlock);
+
+	if (kdbus_conn_is_activator(conn)) {
+		activator = conn->activator_of->activator;
+		conn->activator_of->activator = NULL;
+	}
+
+	while ((p = list_first_entry_or_null(&conn->names_queue_list,
+					     struct kdbus_name_pending,
+					     conn_entry)))
+		kdbus_name_pending_free(p);
+	while ((e = list_first_entry_or_null(&conn->names_list,
+					     struct kdbus_name_entry,
+					     conn_entry)))
+		kdbus_name_release_unlocked(reg, e);
+
+	up_write(&reg->rwlock);
+
+	kdbus_conn_unref(activator);
+	kdbus_notify_flush(conn->ep->bus);
+}
+
+/**
+ * kdbus_cmd_name_acquire() - handle KDBUS_CMD_NAME_ACQUIRE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp)
+{
+	const char *item_name;
+	struct kdbus_cmd *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME, .mandatory = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_NAME_REPLACE_EXISTING |
+				 KDBUS_NAME_ALLOW_REPLACEMENT |
+				 KDBUS_NAME_QUEUE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	item_name = argv[1].item->str;
+	if (!kdbus_name_is_valid(item_name, false)) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/*
+	 * Do atomic_inc_return here to reserve our slot, then decrement
+	 * it before returning.
+	 */
+	if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) {
+		ret = -E2BIG;
+		goto exit_dec;
+	}
+
+	ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name,
+				 cmd->flags, &cmd->return_flags);
+	if (ret < 0)
+		goto exit_dec;
+
+exit_dec:
+	atomic_dec(&conn->name_count);
+exit:
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_name_release() - handle KDBUS_CMD_NAME_RELEASE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME, .mandatory = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	ret = kdbus_name_release(conn->ep->bus->name_registry, conn,
+				 argv[1].item->str);
+	return kdbus_args_clear(&args, ret);
+}
+
+static int kdbus_list_write(struct kdbus_conn *conn,
+			    struct kdbus_conn *c,
+			    struct kdbus_pool_slice *slice,
+			    size_t *pos,
+			    struct kdbus_name_entry *e,
+			    bool write)
+{
+	struct kvec kvec[4];
+	size_t cnt = 0;
+	int ret;
+
+	/* info header */
+	struct kdbus_info info = {
+		.size = 0,
+		.id = c->id,
+		.flags = c->flags,
+	};
+
+	/* fake the header of a kdbus_name item */
+	struct {
+		u64 size;
+		u64 type;
+		u64 flags;
+	} h = {};
+
+	if (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(),
+						      e->name))
+		return 0;
+
+	kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size);
+
+	/* append name */
+	if (e) {
+		size_t slen = strlen(e->name) + 1;
+
+		h.size = offsetof(struct kdbus_item, name.name) + slen;
+		h.type = KDBUS_ITEM_OWNED_NAME;
+		h.flags = e->flags;
+
+		kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size);
+		kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size);
+		cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size);
+	}
+
+	if (write) {
+		ret = kdbus_pool_slice_copy_kvec(slice, *pos, kvec,
+						 cnt, info.size);
+		if (ret < 0)
+			return ret;
+	}
+
+	*pos += info.size;
+	return 0;
+}
+
+static int kdbus_list_all(struct kdbus_conn *conn, u64 flags,
+			  struct kdbus_pool_slice *slice,
+			  size_t *pos, bool write)
+{
+	struct kdbus_conn *c;
+	size_t p = *pos;
+	int ret, i;
+
+	hash_for_each(conn->ep->bus->conn_hash, i, c, hentry) {
+		bool added = false;
+
+		/* skip monitors */
+		if (kdbus_conn_is_monitor(c))
+			continue;
+
+		/* skip activators */
+		if (!(flags & KDBUS_LIST_ACTIVATORS) &&
+		    kdbus_conn_is_activator(c))
+			continue;
+
+		/* all names the connection owns */
+		if (flags & (KDBUS_LIST_NAMES | KDBUS_LIST_ACTIVATORS)) {
+			struct kdbus_name_entry *e;
+
+			list_for_each_entry(e, &c->names_list, conn_entry) {
+				struct kdbus_conn *a = e->activator;
+
+				if ((flags & KDBUS_LIST_ACTIVATORS) &&
+				    a && a != c) {
+					ret = kdbus_list_write(conn, a, slice,
+							       &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+
+				if (flags & KDBUS_LIST_NAMES ||
+				    kdbus_conn_is_activator(c)) {
+					ret = kdbus_list_write(conn, c, slice,
+							       &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+			}
+		}
+
+		/* queue of names the connection is currently waiting for */
+		if (flags & KDBUS_LIST_QUEUED) {
+			struct kdbus_name_pending *q;
+
+			list_for_each_entry(q, &c->names_queue_list,
+					    conn_entry) {
+				ret = kdbus_list_write(conn, c, slice, &p,
+						       q->name, write);
+				if (ret < 0) {
+					mutex_unlock(&c->lock);
+					return ret;
+				}
+
+				added = true;
+			}
+		}
+
+		/* nothing added so far, just add the unique ID */
+		if (!added && flags & KDBUS_LIST_UNIQUE) {
+			ret = kdbus_list_write(conn, c, slice, &p, NULL, write);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	*pos = p;
+	return 0;
+}
+
+/**
+ * kdbus_cmd_list() - handle KDBUS_CMD_LIST
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_name_registry *reg = conn->ep->bus->name_registry;
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_cmd_list *cmd;
+	size_t pos, size;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_LIST_UNIQUE |
+				 KDBUS_LIST_NAMES |
+				 KDBUS_LIST_ACTIVATORS |
+				 KDBUS_LIST_QUEUED,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	down_read(&reg->rwlock);
+	down_read(&conn->ep->bus->conn_rwlock);
+	down_read(&conn->ep->policy_db.entries_rwlock);
+
+	/* size of records */
+	size = 0;
+	ret = kdbus_list_all(conn, cmd->flags, NULL, &size, false);
+	if (ret < 0)
+		goto exit_unlock;
+
+	if (size == 0) {
+		kdbus_pool_publish_empty(conn->pool, &cmd->offset,
+					 &cmd->list_size);
+	} else {
+		slice = kdbus_pool_slice_alloc(conn->pool, size, false);
+		if (IS_ERR(slice)) {
+			ret = PTR_ERR(slice);
+			slice = NULL;
+			goto exit_unlock;
+		}
+
+		/* copy the records */
+		pos = 0;
+		ret = kdbus_list_all(conn, cmd->flags, slice, &pos, true);
+		if (ret < 0)
+			goto exit_unlock;
+
+		WARN_ON(pos != size);
+		kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->list_size);
+	}
+
+	if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+	    kdbus_member_set_user(&cmd->list_size, argp,
+				  typeof(*cmd), list_size))
+		ret = -EFAULT;
+
+exit_unlock:
+	up_read(&conn->ep->policy_db.entries_rwlock);
+	up_read(&conn->ep->bus->conn_rwlock);
+	up_read(&reg->rwlock);
+	kdbus_pool_slice_release(slice);
+	return kdbus_args_clear(&args, ret);
+}
diff --git a/ipc/kdbus/names.h b/ipc/kdbus/names.h
new file mode 100644
index 000000000000..3dd2589293e0
--- /dev/null
+++ b/ipc/kdbus/names.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
+ * Copyright (C) 2013-2015 Daniel Mack <daniel@xxxxxxxxxx>
+ * Copyright (C) 2013-2015 David Herrmann <dh.herrmann@xxxxxxxxx>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz@xxxxxxxxxx>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __KDBUS_NAMES_H
+#define __KDBUS_NAMES_H
+
+#include <linux/hashtable.h>
+#include <linux/rwsem.h>
+
+/**
+ * struct kdbus_name_registry - names registered for a bus
+ * @entries_hash:	Map of entries
+ * @lock:		Registry data lock
+ * @name_seq_last:	Last used sequence number to assign to a name entry
+ */
+struct kdbus_name_registry {
+	DECLARE_HASHTABLE(entries_hash, 8);
+	struct rw_semaphore rwlock;
+	u64 name_seq_last;
+};
+
+/**
+ * struct kdbus_name_entry - well-know name entry
+ * @name_id:		Sequence number of name entry to be able to uniquely
+ *			identify a name over its registration lifetime
+ * @flags:		KDBUS_NAME_* flags
+ * @conn:		Connection owning the name
+ * @activator:		Connection of the activator queuing incoming messages
+ * @queue:		List of queued connections
+ * @conn_entry:		Entry in connection
+ * @hentry:		Entry in registry map
+ * @name:		The well-known name
+ */
+struct kdbus_name_entry {
+	u64 name_id;
+	u64 flags;
+	struct kdbus_conn *conn;
+	struct kdbus_conn *activator;
+	struct list_head queue;
+	struct list_head conn_entry;
+	struct hlist_node hentry;
+	char name[];
+};
+
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard);
+
+struct kdbus_name_registry *kdbus_name_registry_new(void);
+void kdbus_name_registry_free(struct kdbus_name_registry *reg);
+
+struct kdbus_name_entry *
+kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name);
+
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+		       struct kdbus_conn *conn, const char *name,
+		       u64 flags, u64 *return_flags);
+void kdbus_name_release_all(struct kdbus_name_registry *reg,
+			    struct kdbus_conn *conn);
+
+int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp);
+
+#endif
-- 
2.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux