Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 113 additions & 13 deletions sql/auth/sql_auth_cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@

#include <algorithm>
#include <functional>
#include <unordered_map>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -140,6 +141,14 @@ malloc_unordered_map<std::string, unique_ptr_my_free<acl_entry>> db_cache{
key_memory_acl_cache};
collation_unordered_map<std::string, ACL_USER *> *acl_check_hosts = nullptr;

typedef Acl_cache_allocator<std::pair<const std::string, Acl_user_ptr_list>>
Name_to_userlist_pair_allocator;
typedef std::unordered_map<std::string, Acl_user_ptr_list,
std::hash<std::string>, std::equal_to<std::string>,
Name_to_userlist_pair_allocator>
Name_to_userlist;
Name_to_userlist *name_to_userlist = nullptr;

bool initialized = 0;
bool acl_cache_initialized = false;
bool allow_all_hosts = 1;
Expand Down Expand Up @@ -798,6 +807,88 @@ bool GRANT_TABLE::init(TABLE *col_privs) {
return false;
}

namespace {

class ACL_compare : public std::binary_function<ACL_ACCESS, ACL_ACCESS, bool> {
public:
bool operator()(const ACL_ACCESS &a, const ACL_ACCESS &b) {
return a.sort > b.sort;
}
bool operator()(const ACL_ACCESS *a, const ACL_ACCESS *b) {
return a->sort > b->sort;
}
};

} // namespace

/*
Build the lists of ACL_USERs which share name or have no name
*/

void rebuild_cached_acl_users_for_name(void) {
DBUG_ENTER("rebuild_cached_acl_users_for_name");
DBUG_PRINT("enter", ("acl_users size: %lu", acl_users->size()));

DBUG_ASSERT(!current_thd || assert_acl_cache_read_lock(current_thd));

if (name_to_userlist) {
name_to_userlist->clear();
} else {
size_t size = sizeof(Name_to_userlist);
myf_t flags = MYF(MY_WME | ME_FATALERROR);
void *bytes = my_malloc(key_memory_acl_cache, size, flags);
name_to_userlist = new (bytes) Name_to_userlist();
}

std::list<ACL_USER *> anons;

/* first build each named list */
for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
++acl_user) {
std::string name = acl_user->user ? acl_user->user : "";
(*name_to_userlist)[name].push_back(acl_user);

/* keep track of anonymous acl_users */
if (!name.compare("")) anons.push_back(acl_user);
}

/* add the anonymous acl_users to each non-anon list */
for (auto it = name_to_userlist->begin(); it != name_to_userlist->end();
++it) {
std::string name = it->first;
if (!name.compare("")) continue;

auto *list = &it->second;
for (auto it2 = anons.begin(); it2 != anons.end(); ++it2) {
list->push_back(*it2);
}

list->sort(ACL_compare());
}
DBUG_VOID_RETURN;
}

/*
Fetch the list of ACL_USERs which share name or have no name
*/

Acl_user_ptr_list *cached_acl_users_for_name(const char *name) {
DBUG_ENTER("cached_acl_users_for_name");
DBUG_PRINT("enter", ("name: '%s'", name));

DBUG_ASSERT(!current_thd || assert_acl_cache_read_lock(current_thd));

std::string user_name = name ? name : "";

auto it = name_to_userlist->find(user_name);
if (it != name_to_userlist->end()) DBUG_RETURN(&it->second);

it = name_to_userlist->find("");
if (it != name_to_userlist->end()) DBUG_RETURN(&it->second);

DBUG_RETURN(NULL);
}

/*
Find first entry that matches the current user
*/
Expand All @@ -809,8 +900,13 @@ ACL_USER *find_acl_user(const char *host, const char *user, bool exact) {
DBUG_ASSERT(assert_acl_cache_read_lock(current_thd));

if (likely(acl_users)) {
for (ACL_USER *acl_user = acl_users->begin(); acl_user != acl_users->end();
++acl_user) {
Acl_user_ptr_list *list = cached_acl_users_for_name(user);
if (!list) {
DBUG_RETURN(0);
}

for (auto it = list->begin(); it != list->end(); ++it) {
ACL_USER *acl_user = (*it);
DBUG_PRINT("info",
("strcmp('%s','%s'), compare_hostname('%s','%s'),", user,
acl_user->user ? acl_user->user : "", host,
Expand Down Expand Up @@ -1211,17 +1307,6 @@ bool acl_getroot(THD *thd, Security_context *sctx, char *user, char *host,
DBUG_RETURN(res);
}

namespace {

class ACL_compare : public std::binary_function<ACL_ACCESS, ACL_ACCESS, bool> {
public:
bool operator()(const ACL_ACCESS &a, const ACL_ACCESS &b) {
return a.sort > b.sort;
}
};

} // namespace

/**
Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
Expand Down Expand Up @@ -1477,6 +1562,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) {
1, false))
goto end;
table->use_all_columns();
if (name_to_userlist) name_to_userlist->clear();
acl_users->clear();
/*
We need to check whether we are working with old database layout. This
Expand Down Expand Up @@ -1840,12 +1926,14 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) {
allow_all_hosts = 1; // Anyone can connect
}
} // END while reading records from the mysql.user table
rebuild_cached_acl_users_for_name();

end_read_record(&read_record_info);
if (read_rec_errcode > 0) goto end;

std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
acl_users->shrink_to_fit();
rebuild_cached_acl_users_for_name();

if (super_users_with_empty_plugin) {
LogErr(WARNING_LEVEL, ER_NO_SUPER_WITHOUT_USER_PLUGIN);
Expand Down Expand Up @@ -1957,8 +2045,17 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) {
DBUG_RETURN(return_val);
}

void free_name_to_userlist() {
if (!name_to_userlist) return;

name_to_userlist->~unordered_map();
my_free(name_to_userlist);
name_to_userlist = nullptr;
}

void acl_free(bool end /*= false*/) {
free_root(&global_acl_memory, MYF(0));
free_name_to_userlist();
delete acl_users;
acl_users = NULL;
delete acl_dbs;
Expand Down Expand Up @@ -2260,6 +2357,7 @@ bool acl_reload(THD *thd, bool locked) {
// Delete the old role caches
delete_old_role_cache();
}
rebuild_cached_acl_users_for_name();

end:
commit_and_close_mysql_tables(thd);
Expand Down Expand Up @@ -2788,6 +2886,7 @@ void acl_update_user(const char *user, const char *host, enum SSL_type ssl_type,
}
}
}
rebuild_cached_acl_users_for_name();
DBUG_VOID_RETURN;
}

Expand Down Expand Up @@ -2868,6 +2967,7 @@ void acl_insert_user(THD *thd MY_ATTRIBUTE((unused)), const char *user,
if (acl_user.host.check_allow_all_hosts())
allow_all_hosts = 1; // Anyone can connect /* purecov: tested */
std::sort(acl_users->begin(), acl_users->end(), ACL_compare());
rebuild_cached_acl_users_for_name();

/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
Expand Down
30 changes: 30 additions & 0 deletions sql/auth/sql_auth_cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,36 @@ class GRANT_TABLE : public GRANT_NAME {
bool ok() { return privs != 0 || cols != 0; }
};

/*
* A default/no-arg constructor is useful with containers-of-containers
* situations in which a two-allocator scoped_allocator_adapter is not enough.
* This custom allocator provides a Malloc_allocator with a no-arg constructor
* by hard-coding the key_memory_acl_cache constructor argument.
* This "solution" lacks beauty, yet is pragmatic.
*/
template <class T>
class Acl_cache_allocator : public Malloc_allocator<T> {
public:
Acl_cache_allocator() : Malloc_allocator<T>(key_memory_acl_cache) {}
template <class U>
struct rebind {
typedef Acl_cache_allocator<U> other;
};

template <class U>
Acl_cache_allocator(const Acl_cache_allocator<U> &other
__attribute__((unused)))
: Malloc_allocator<T>(key_memory_acl_cache) {}

template <class U>
Acl_cache_allocator &operator=(const Acl_cache_allocator<U> &other
__attribute__((unused))) {}
};
typedef Acl_cache_allocator<ACL_USER *> Acl_user_ptr_allocator;
typedef std::list<ACL_USER *, Acl_user_ptr_allocator> Acl_user_ptr_list;
Acl_user_ptr_list *cached_acl_users_for_name(const char *name);
void rebuild_cached_acl_users_for_name(void);

/* Data Structures */

extern MEM_ROOT global_acl_memory;
Expand Down
16 changes: 10 additions & 6 deletions sql/auth/sql_authentication.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1685,17 +1685,21 @@ static bool find_mpvio_user(THD *thd, MPVIO_EXT *mpvio) {
DBUG_PRINT("info", ("entry: %s", mpvio->auth_info.user_name));
DBUG_ASSERT(mpvio->acl_user == 0);

Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
if (!acl_cache_lock.lock(false)) DBUG_RETURN(true);

Acl_user_ptr_list *list = nullptr;
if (likely(acl_users)) {
Acl_cache_lock_guard acl_cache_lock(thd, Acl_cache_lock_mode::READ_MODE);
if (!acl_cache_lock.lock(false)) DBUG_RETURN(true);
list = cached_acl_users_for_name(mpvio->auth_info.user_name);
}
if (list) {
for (auto it = list->begin(); it != list->end(); ++it) {
ACL_USER *acl_user_tmp = (*it);

for (ACL_USER *acl_user_tmp = acl_users->begin();
acl_user_tmp != acl_users->end(); ++acl_user_tmp) {
if ((!acl_user_tmp->user ||
!strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
acl_user_tmp->host.compare_hostname(mpvio->host, mpvio->ip)) {
mpvio->acl_user = acl_user_tmp->copy(mpvio->mem_root);

/*
When setting mpvio->acl_user_plugin we can save memory allocation if
this is a built in plugin.
Expand All @@ -1709,8 +1713,8 @@ static bool find_mpvio_user(THD *thd, MPVIO_EXT *mpvio) {
break;
}
}
acl_cache_lock.unlock();
}
acl_cache_lock.unlock();

if (!mpvio->acl_user) {
/*
Expand Down
4 changes: 4 additions & 0 deletions sql/auth/sql_user.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,

if (drop) {
acl_users->erase(idx);
rebuild_cached_acl_users_for_name();
/*
- If we are iterating through an array then we just have moved all
elements after the current element one position closer to its head.
Expand All @@ -1341,6 +1342,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
acl_user->user = strdup_root(&global_acl_memory, user_to->user.str);
acl_user->host.update_hostname(
strdup_root(&global_acl_memory, user_to->host.str));
rebuild_cached_acl_users_for_name();
} else {
/* If search is requested, we do not need to search further. */
break;
Expand Down Expand Up @@ -1931,6 +1933,7 @@ bool mysql_drop_user(THD *thd, List<LEX_USER> &list, bool if_exists) {

/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
rebuild_cached_acl_users_for_name();

if (result && !thd->is_error()) {
String operation_str;
Expand Down Expand Up @@ -2077,6 +2080,7 @@ bool mysql_rename_user(THD *thd, List<LEX_USER> &list) {

/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
rebuild_cached_acl_users_for_name();

if (result && !thd->is_error())
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
Expand Down