Viewing: upcall_cache_internal.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2023, Whamcloud.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*/
#define DEBUG_SUBSYSTEM S_SEC
#include <lustre_idmap.h>
#include <md_object.h>
#include <upcall_cache.h>
#include "upcall_cache_internal.h"
inline void refresh_entry_internal(struct upcall_cache *cache,
struct upcall_cache_entry *entry,
__u32 fsgid, struct group_info **ginfo)
{
get_entry(entry);
entry->u.identity.mi_uid = entry->ue_key;
entry->u.identity.mi_gid = fsgid;
if (*ginfo)
entry->u.identity.mi_ginfo = *ginfo;
entry->u.identity.mi_nperms = 0;
entry->u.identity.mi_perms = NULL;
entry->ue_expire = ktime_get_seconds() + cache->uc_entry_expire;
UC_CACHE_SET_VALID(entry);
put_entry(cache, entry);
CDEBUG(D_OTHER,
"%s: INTERNAL refreshed entry for '%llu' with %d groups\n",
cache->uc_name, entry->ue_key,
*ginfo ? (*ginfo)->ngroups : 0);
*ginfo = NULL;
}
int upcall_cache_get_entry_internal(struct upcall_cache *cache,
struct upcall_cache_entry *entry,
void *args, gid_t *fsgid,
struct group_info **pginfo)
{
struct lu_ucred *uc = (struct lu_ucred *)args;
gid_t inval = (__u32)__kgid_val(INVALID_GID);
struct md_identity *identity;
bool supp_in_ginfo[2];
gid_t *groups = NULL, *glist_p;
int i, groups_num, ginfo_ngroups = 0, rc = 0;
if (*pginfo || !uc)
/* ginfo already built, or no creds provided
* => return immediately
*/
goto out;
restart:
groups_num = 0;
/* We just deal with NEW and VALID entries. Other states will
* be handled by the caller, no need to return an error.
*/
if (!UC_CACHE_IS_NEW(entry) && !UC_CACHE_IS_VALID(entry))
goto out;
identity = &entry->u.identity;
*fsgid = uc->uc_fsgid;
supp_in_ginfo[0] = (uc->uc_suppgids[0] == inval);
supp_in_ginfo[1] = (uc->uc_suppgids[1] == inval);
if (identity->mi_ginfo && identity->mi_ginfo->ngroups)
ginfo_ngroups = identity->mi_ginfo->ngroups;
/* check if provided supp groups are already in cache */
for (i = 0; i < 2 && uc->uc_suppgids[i] != inval; i++) {
if (unlikely(uc->uc_suppgids[i] == uc->uc_fsuid)) {
/* Do not place user's group ID in group list */
supp_in_ginfo[i] = true;
} else if (ginfo_ngroups) {
upcall_group_usage_inc(identity->mi_ginfo);
supp_in_ginfo[i] =
lustre_groups_search(identity->mi_ginfo,
uc->uc_suppgids[i]);
upcall_group_usage_dec(identity->mi_ginfo);
}
}
/* build new list of groups, which is a merge of provided supp
* groups and all other groups already in cache
*/
if (!supp_in_ginfo[0] || !supp_in_ginfo[1]) {
CDEBUG(D_OTHER,
"%s: INTERNAL might add suppgids %d,%d for entry '%llu'\n",
cache->uc_name, uc->uc_suppgids[0],
uc->uc_suppgids[1], entry->ue_key);
if (!supp_in_ginfo[0])
groups_num++;
if (!supp_in_ginfo[1])
groups_num++;
CFS_ALLOC_PTR_ARRAY(groups, groups_num + ginfo_ngroups);
if (groups == NULL)
GOTO(out, rc = -ENOMEM);
glist_p = groups;
for (i = 0; i < 2; i++) {
if (!supp_in_ginfo[i])
*(glist_p++) = uc->uc_suppgids[i];
}
/* An existing entry is never modified once it is marked as
* VALID. But it can change when updated from NEW to VALID,
* for instance the mi_ginfo can be set. This means the number
* of groups can only grow from 0 (mi_ginfo not set) to
* mi_ginfo->ngroups.
* So only copy mi_ginfo to the groups array if necessary space
* was allocated for it.
* In case we detect a concurrent change in mi_ginfo->ngroups,
* just start over.
*/
if (ginfo_ngroups) {
upcall_group_usage_inc(identity->mi_ginfo);
lustre_list_from_groups(glist_p, identity->mi_ginfo);
upcall_group_usage_dec(identity->mi_ginfo);
} else if (identity->mi_ginfo && identity->mi_ginfo->ngroups) {
CFS_FREE_PTR_ARRAY(groups, groups_num + ginfo_ngroups);
groups = NULL;
goto restart;
}
if (!UC_CACHE_IS_NEW(entry)) {
/* force refresh as an existing cache entry
* cannot be modified
*/
/* we are called from upcall_cache_get_entry() after
* write lock has been dropped
*/
write_lock(&cache->uc_lock);
entry->ue_expire = ktime_get_seconds();
write_unlock(&cache->uc_lock);
}
}
out:
if (groups) {
int ngroups = groups_num + ginfo_ngroups;
struct group_info *ginfo;
ginfo = groups_alloc(ngroups);
if (ginfo) {
lustre_groups_from_list(ginfo, groups);
lustre_groups_sort(ginfo);
*pginfo = ginfo;
} else {
CDEBUG(D_OTHER,
"failed to alloc %d groups: rc = %d\n",
ngroups, -ENOMEM);
rc = -ENOMEM;
}
CFS_FREE_PTR_ARRAY(groups, ngroups);
}
return rc;
}