struct md_identity *identity;
bool supp_in_ginfo[2];
gid_t *groups = NULL, *glist_p;
- int groups_num, i, rc = 0;
+ int i, groups_num, ginfo_ngroups = 0, rc = 0;
if (*grouplist || !uc || uc->uc_suppgids[0] == -1)
/* grouplist already built, or no supp groups
*/
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] = false;
supp_in_ginfo[1] = (uc->uc_suppgids[1] == -1);
-
- if (!identity->mi_ginfo || !identity->mi_ginfo->ngroups)
- groups_num = 0;
- else
- groups_num = identity->mi_ginfo->ngroups;
+ 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] != -1; 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 (groups_num) {
+ } else if (ginfo_ngroups) {
atomic_inc(&identity->mi_ginfo->usage);
supp_in_ginfo[i] =
lustre_groups_search(identity->mi_ginfo,
groups_num++;
if (!supp_in_ginfo[1])
groups_num++;
- CFS_ALLOC_PTR_ARRAY(groups, groups_num);
+ CFS_ALLOC_PTR_ARRAY(groups, groups_num + ginfo_ngroups);
if (groups == NULL)
GOTO(out, rc = -ENOMEM);
*(glist_p++) = uc->uc_suppgids[i];
}
- if (identity->mi_ginfo && identity->mi_ginfo->ngroups) {
+ /* 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) {
atomic_inc(&identity->mi_ginfo->usage);
lustre_list_from_groups(glist_p, identity->mi_ginfo);
atomic_dec(&identity->mi_ginfo->usage);
+ } 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)) {
out:
if (groups) {
*grouplist = groups;
- *ngroups = groups_num;
+ *ngroups = groups_num + ginfo_ngroups;
}
return rc;
}