From e5fd91f1ef340da553f7a79da9540c3db711c937 Mon Sep 17 00:00:00 2001 From: AndrĂ© Fabian Silva Delgado Date: Tue, 8 Sep 2015 01:01:14 -0300 Subject: Linux-libre 4.2-gnu --- ipc/kdbus/names.c | 762 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 423 insertions(+), 339 deletions(-) (limited to 'ipc/kdbus/names.c') diff --git a/ipc/kdbus/names.c b/ipc/kdbus/names.c index 057f8061c..bf44ca3f1 100644 --- a/ipc/kdbus/names.c +++ b/ipc/kdbus/names.c @@ -34,167 +34,128 @@ #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; -}; +#define KDBUS_NAME_SAVED_MASK (KDBUS_NAME_ALLOW_REPLACEMENT | \ + KDBUS_NAME_QUEUE) -static int kdbus_name_pending_new(struct kdbus_name_entry *e, - struct kdbus_conn *conn, u64 flags) +static bool kdbus_name_owner_is_used(struct kdbus_name_owner *owner) { - 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; + return !list_empty(&owner->name_entry) || + owner == owner->name->activator; } -static void kdbus_name_pending_free(struct kdbus_name_pending *p) +static struct kdbus_name_owner * +kdbus_name_owner_new(struct kdbus_conn *conn, struct kdbus_name_entry *name, + u64 flags) { - if (!p) - return; + struct kdbus_name_owner *owner; - 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; + kdbus_conn_assert_active(conn); - namelen = strlen(name); + if (conn->name_count >= KDBUS_CONN_MAX_NAMES) + return ERR_PTR(-E2BIG); - e = kmalloc(sizeof(*e) + namelen + 1, GFP_KERNEL); - if (!e) + owner = kmalloc(sizeof(*owner), GFP_KERNEL); + if (!owner) 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); + owner->flags = flags & KDBUS_NAME_SAVED_MASK; + owner->conn = conn; + owner->name = name; + list_add_tail(&owner->conn_entry, &conn->names_list); + INIT_LIST_HEAD(&owner->name_entry); - return e; + ++conn->name_count; + return owner; } -static void kdbus_name_entry_free(struct kdbus_name_entry *e) +static void kdbus_name_owner_free(struct kdbus_name_owner *owner) { - if (!e) + if (!owner) 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); + WARN_ON(kdbus_name_owner_is_used(owner)); + --owner->conn->name_count; + list_del(&owner->conn_entry); + kfree(owner); } -static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e, - struct kdbus_conn *conn, u64 flags) +static struct kdbus_name_owner * +kdbus_name_owner_find(struct kdbus_name_entry *name, struct kdbus_conn *conn) { - WARN_ON(e->conn); + struct kdbus_name_owner *owner; - e->conn = kdbus_conn_ref(conn); - e->flags = flags; - atomic_inc(&conn->name_count); - list_add_tail(&e->conn_entry, &e->conn->names_list); + /* + * Use conn->names_list over name->queue to make sure boundaries of + * this linear search are controlled by the connection itself. + * Furthermore, this will find normal owners as well as activators + * without any additional code. + */ + list_for_each_entry(owner, &conn->names_list, conn_entry) + if (owner->name == name) + return owner; + + return NULL; } -static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e) +static bool kdbus_name_entry_is_used(struct kdbus_name_entry *name) { - 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); + return !list_empty(&name->queue) || name->activator; } -static void kdbus_name_entry_replace_owner(struct kdbus_name_entry *e, - struct kdbus_conn *conn, u64 flags) +static struct kdbus_name_owner * +kdbus_name_entry_first(struct kdbus_name_entry *name) { - 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); + return list_first_entry_or_null(&name->queue, struct kdbus_name_owner, + name_entry); } -/** - * 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) +static struct kdbus_name_entry * +kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash, + const char *name_str) { - bool dot, found_dot = false; - const char *q; + struct kdbus_name_entry *name; + size_t namelen; - for (dot = true, q = p; *q; q++) { - if (*q == '.') { - if (dot) - return false; + lockdep_assert_held(&r->rwlock); - found_dot = true; - dot = true; - } else { - bool good; + namelen = strlen(name_str); - good = isalpha(*q) || (!dot && isdigit(*q)) || - *q == '_' || *q == '-' || - (allow_wildcard && dot && - *q == '*' && *(q + 1) == '\0'); + name = kmalloc(sizeof(*name) + namelen + 1, GFP_KERNEL); + if (!name) + return ERR_PTR(-ENOMEM); - if (!good) - return false; + name->name_id = ++r->name_seq_last; + name->activator = NULL; + INIT_LIST_HEAD(&name->queue); + hash_add(r->entries_hash, &name->hentry, hash); + memcpy(name->name, name_str, namelen + 1); - dot = false; - } - } + return name; +} - if (q - p > KDBUS_NAME_MAX_LEN) - return false; +static void kdbus_name_entry_free(struct kdbus_name_entry *name) +{ + if (!name) + return; - if (dot) - return false; + WARN_ON(kdbus_name_entry_is_used(name)); + hash_del(&name->hentry); + kfree(name); +} - if (!found_dot) - return false; +static struct kdbus_name_entry * +kdbus_name_entry_find(struct kdbus_name_registry *r, u32 hash, + const char *name_str) +{ + struct kdbus_name_entry *name; - return true; + lockdep_assert_held(&r->rwlock); + + hash_for_each_possible(r->entries_hash, name, hentry, hash) + if (!strcmp(name->name, name_str)) + return name; + + return NULL; } /** @@ -218,32 +179,19 @@ struct kdbus_name_registry *kdbus_name_registry_new(void) } /** - * kdbus_name_registry_free() - drop a name reg's reference - * @reg: The name registry, may be %NULL + * kdbus_name_registry_free() - free name registry + * @r: name registry to free, or NULL * - * Cleanup the name registry's internal structures. + * Free a name registry and cleanup all internal objects. This is a no-op if + * you pass NULL as registry. */ -void kdbus_name_registry_free(struct kdbus_name_registry *reg) +void kdbus_name_registry_free(struct kdbus_name_registry *r) { - if (!reg) + if (!r) 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(®->rwlock); - - hash_for_each_possible(reg->entries_hash, e, hentry, hash) - if (strcmp(e->name, name) == 0) - return e; - - return NULL; + WARN_ON(!hash_empty(r->entries_hash)); + kfree(r); } /** @@ -260,169 +208,286 @@ kdbus_name_find(struct kdbus_name_registry *reg, u32 hash, const char *name) 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); + return kdbus_name_entry_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) +static int kdbus_name_become_activator(struct kdbus_name_owner *owner, + u64 *return_flags) { - struct kdbus_name_entry *e; - u64 rflags = 0; - int ret = 0; - u32 hash; + if (kdbus_name_owner_is_used(owner)) + return -EALREADY; + if (owner->name->activator) + return -EEXIST; - kdbus_conn_assert_active(conn); + owner->name->activator = owner; + owner->flags |= KDBUS_NAME_ACTIVATOR; - down_write(®->rwlock); - - if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) { - ret = -EPERM; - goto exit_unlock; + if (kdbus_name_entry_first(owner->name)) { + owner->flags |= KDBUS_NAME_IN_QUEUE; + } else { + owner->flags |= KDBUS_NAME_PRIMARY; + kdbus_notify_name_change(owner->conn->ep->bus, + KDBUS_ITEM_NAME_ADD, + 0, owner->conn->id, + 0, owner->flags, + owner->name->name); } - hash = kdbus_strhash(name); - e = kdbus_name_find(reg, hash, name); - if (!e) { - /* claim new name */ + if (return_flags) + *return_flags = owner->flags | KDBUS_NAME_ACQUIRED; - if (conn->activator_of) { - ret = -EINVAL; - goto exit_unlock; - } + return 0; +} - e = kdbus_name_entry_new(reg, hash, name); - if (IS_ERR(e)) { - ret = PTR_ERR(e); - goto exit_unlock; - } +static int kdbus_name_update(struct kdbus_name_owner *owner, u64 flags, + u64 *return_flags) +{ + struct kdbus_name_owner *primary, *activator; + struct kdbus_name_entry *name; + struct kdbus_bus *bus; + u64 nflags = 0; + int ret = 0; - if (kdbus_conn_is_activator(conn)) { - e->activator = kdbus_conn_ref(conn); - conn->activator_of = e; + name = owner->name; + bus = owner->conn->ep->bus; + primary = kdbus_name_entry_first(name); + activator = name->activator; + + /* cannot be activator and acquire a name */ + if (owner == activator) + return -EUCLEAN; + + /* update saved flags */ + owner->flags = flags & KDBUS_NAME_SAVED_MASK; + + if (!primary) { + /* + * No primary owner (but maybe an activator). Take over the + * name. + */ + + list_add(&owner->name_entry, &name->queue); + owner->flags |= KDBUS_NAME_PRIMARY; + nflags |= KDBUS_NAME_ACQUIRED; + + /* move messages to new owner on activation */ + if (activator) { + kdbus_conn_move_messages(owner->conn, activator->conn, + name->name_id); + kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_CHANGE, + activator->conn->id, owner->conn->id, + activator->flags, owner->flags, + name->name); + activator->flags &= ~KDBUS_NAME_PRIMARY; + activator->flags |= KDBUS_NAME_IN_QUEUE; + } else { + kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_ADD, + 0, owner->conn->id, + 0, owner->flags, + name->name); } - 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 if (owner == primary) { + /* + * Already the primary owner of the name, flags were already + * updated. Nothing to do. + */ + + owner->flags |= KDBUS_NAME_PRIMARY; + + } else if ((primary->flags & KDBUS_NAME_ALLOW_REPLACEMENT) && + (flags & KDBUS_NAME_REPLACE_EXISTING)) { + /* + * We're not the primary owner but can replace it. Move us + * ahead of the primary owner and acquire the name (possibly + * skipping queued owners ahead of us). + */ + + list_del_init(&owner->name_entry); + list_add(&owner->name_entry, &name->queue); + owner->flags |= KDBUS_NAME_PRIMARY; + nflags |= KDBUS_NAME_ACQUIRED; + + kdbus_notify_name_change(bus, KDBUS_ITEM_NAME_CHANGE, + primary->conn->id, owner->conn->id, + primary->flags, owner->flags, + name->name); + + /* requeue old primary, or drop if queueing not wanted */ + if (primary->flags & KDBUS_NAME_QUEUE) { + primary->flags &= ~KDBUS_NAME_PRIMARY; + primary->flags |= KDBUS_NAME_IN_QUEUE; } 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; + list_del_init(&primary->name_entry); + kdbus_name_owner_free(primary); } - 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; + /* + * Name is already occupied and we cannot take it over, but + * queuing is allowed. Put us silently on the queue, if not + * already there. + */ + + owner->flags |= KDBUS_NAME_IN_QUEUE; + if (!kdbus_name_owner_is_used(owner)) { + list_add_tail(&owner->name_entry, &name->queue); + nflags |= KDBUS_NAME_ACQUIRED; + } + } else if (kdbus_name_owner_is_used(owner)) { + /* + * Already queued on name, but re-queueing was not requested. + * Make sure to unlink it from the name, the caller is + * responsible for releasing it. + */ + + list_del_init(&owner->name_entry); } else { - /* the name is busy, return a failure */ + /* + * Name is already claimed and queueing is not requested. + * Return error to the caller. + */ + ret = -EEXIST; } - if (ret == 0 && return_flags) - *return_flags = rflags; + if (return_flags) + *return_flags = owner->flags | nflags; -exit_unlock: + return ret; +} + +int kdbus_name_acquire(struct kdbus_name_registry *reg, + struct kdbus_conn *conn, const char *name_str, + u64 flags, u64 *return_flags) +{ + struct kdbus_name_entry *name = NULL; + struct kdbus_name_owner *owner = NULL; + u32 hash; + int ret; + + kdbus_conn_assert_active(conn); + + down_write(®->rwlock); + + /* + * Verify the connection has access to the name. Do this before testing + * for double-acquisitions and other errors to make sure we do not leak + * information about this name through possible custom endpoints. + */ + if (!kdbus_conn_policy_own_name(conn, current_cred(), name_str)) { + ret = -EPERM; + goto exit; + } + + /* + * Lookup the name entry. If it already exists, search for an owner + * entry as we might already own that name. If either does not exist, + * we will allocate a fresh one. + */ + hash = kdbus_strhash(name_str); + name = kdbus_name_entry_find(reg, hash, name_str); + if (name) { + owner = kdbus_name_owner_find(name, conn); + } else { + name = kdbus_name_entry_new(reg, hash, name_str); + if (IS_ERR(name)) { + ret = PTR_ERR(name); + name = NULL; + goto exit; + } + } + + /* create name owner object if not already queued */ + if (!owner) { + owner = kdbus_name_owner_new(conn, name, flags); + if (IS_ERR(owner)) { + ret = PTR_ERR(owner); + owner = NULL; + goto exit; + } + } + + if (flags & KDBUS_NAME_ACTIVATOR) + ret = kdbus_name_become_activator(owner, return_flags); + else + ret = kdbus_name_update(owner, flags, return_flags); + if (ret < 0) + goto exit; + +exit: + if (owner && !kdbus_name_owner_is_used(owner)) + kdbus_name_owner_free(owner); + if (name && !kdbus_name_entry_is_used(name)) + kdbus_name_entry_free(name); up_write(®->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) +static void kdbus_name_release_unlocked(struct kdbus_name_owner *owner) { - struct kdbus_name_pending *p; - - lockdep_assert_held(®->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); + struct kdbus_name_owner *primary, *next; + struct kdbus_name_entry *name; + + name = owner->name; + primary = kdbus_name_entry_first(name); + + list_del_init(&owner->name_entry); + if (owner == name->activator) + name->activator = NULL; + + if (!primary || owner == primary) { + next = kdbus_name_entry_first(name); + if (!next) + next = name->activator; + + if (next) { + /* hand to next in queue */ + next->flags &= ~KDBUS_NAME_IN_QUEUE; + next->flags |= KDBUS_NAME_PRIMARY; + if (next == name->activator) + kdbus_conn_move_messages(next->conn, + owner->conn, + name->name_id); + + kdbus_notify_name_change(owner->conn->ep->bus, + KDBUS_ITEM_NAME_CHANGE, + owner->conn->id, next->conn->id, + owner->flags, next->flags, + name->name); + } else { + kdbus_notify_name_change(owner->conn->ep->bus, + KDBUS_ITEM_NAME_REMOVE, + owner->conn->id, 0, + owner->flags, 0, + name->name); + } } + + kdbus_name_owner_free(owner); + if (!kdbus_name_entry_is_used(name)) + kdbus_name_entry_free(name); } static int kdbus_name_release(struct kdbus_name_registry *reg, struct kdbus_conn *conn, - const char *name) + const char *name_str) { - struct kdbus_name_pending *p; - struct kdbus_name_entry *e; + struct kdbus_name_owner *owner; + struct kdbus_name_entry *name; int ret = 0; down_write(®->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); + name = kdbus_name_entry_find(reg, kdbus_strhash(name_str), name_str); + if (name) { + owner = kdbus_name_owner_find(name, conn); + if (owner) + kdbus_name_release_unlocked(owner); + else + ret = -EADDRINUSE; } else { - ret = -EADDRINUSE; - list_for_each_entry(p, &e->queue, name_entry) { - if (p->conn == conn) { - kdbus_name_pending_free(p); - ret = 0; - break; - } - } + ret = -ESRCH; } up_write(®->rwlock); @@ -438,32 +503,73 @@ static int kdbus_name_release(struct kdbus_name_registry *reg, 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; + struct kdbus_name_owner *owner; down_write(®->rwlock); - if (conn->activator_of) { - 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); + while ((owner = list_first_entry_or_null(&conn->names_list, + struct kdbus_name_owner, + conn_entry))) + kdbus_name_release_unlocked(owner); up_write(®->rwlock); - kdbus_conn_unref(activator); kdbus_notify_flush(conn->ep->bus); } +/** + * 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_cmd_name_acquire() - handle KDBUS_CMD_NAME_ACQUIRE * @conn: connection to operate on @@ -503,20 +609,9 @@ int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp) 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); -exit_dec: - atomic_dec(&conn->name_count); exit: return kdbus_args_clear(&args, ret); } @@ -559,7 +654,7 @@ 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, + struct kdbus_name_owner *o, bool write) { struct kvec kvec[4]; @@ -580,22 +675,22 @@ static int kdbus_list_write(struct kdbus_conn *conn, u64 flags; } h = {}; - if (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(), - e->name)) + if (o && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(), + o->name->name)) return 0; kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size); /* append name */ - if (e) { - size_t slen = strlen(e->name) + 1; + if (o) { + size_t slen = strlen(o->name->name) + 1; h.size = offsetof(struct kdbus_item, name.name) + slen; h.type = KDBUS_ITEM_OWNED_NAME; - h.flags = e->flags; + h.flags = o->flags; kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size); - kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size); + kdbus_kvec_set(&kvec[cnt++], o->name->name, slen, &info.size); cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size); } @@ -625,63 +720,52 @@ static int kdbus_list_all(struct kdbus_conn *conn, u64 flags, 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; + if (flags & (KDBUS_LIST_NAMES | + KDBUS_LIST_ACTIVATORS | + KDBUS_LIST_QUEUED)) { + struct kdbus_name_owner *o; - list_for_each_entry(e, &c->names_list, conn_entry) { - struct kdbus_conn *a = e->activator; + list_for_each_entry(o, &c->names_list, conn_entry) { + if (o->flags & KDBUS_NAME_ACTIVATOR) { + if (!(flags & KDBUS_LIST_ACTIVATORS)) + continue; - if ((flags & KDBUS_LIST_ACTIVATORS) && - a && a != c) { - ret = kdbus_list_write(conn, a, slice, - &p, e, write); + ret = kdbus_list_write(conn, c, slice, + &p, o, write); if (ret < 0) { mutex_unlock(&c->lock); return ret; } added = true; - } + } else if (o->flags & KDBUS_NAME_IN_QUEUE) { + if (!(flags & KDBUS_LIST_QUEUED)) + continue; - if (flags & KDBUS_LIST_NAMES || - kdbus_conn_is_activator(c)) { ret = kdbus_list_write(conn, c, slice, - &p, e, write); + &p, o, write); if (ret < 0) { mutex_unlock(&c->lock); return ret; } added = true; - } - } - } + } else if (flags & KDBUS_LIST_NAMES) { + ret = kdbus_list_write(conn, c, slice, + &p, o, write); + if (ret < 0) { + mutex_unlock(&c->lock); + return ret; + } - /* 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; } - - added = true; } } /* nothing added so far, just add the unique ID */ - if (!added && flags & KDBUS_LIST_UNIQUE) { + if (!added && (flags & KDBUS_LIST_UNIQUE)) { ret = kdbus_list_write(conn, c, slice, &p, NULL, write); if (ret < 0) return ret; -- cgit v1.2.3-54-g00ecf