Whamcloud - gitweb
EX-4333 sec: support supplementary groups from client
authorSebastien Buisson <sbuisson@ddn.com>
Thu, 10 Feb 2022 16:14:02 +0000 (17:14 +0100)
committerAndreas Dilger <adilger@whamcloud.com>
Thu, 31 Mar 2022 06:37:08 +0000 (06:37 +0000)
The usual way to support more than 2 supplementary groups is to
resort to the server side's identity upcall. This identity upcall
retrieves all user's credentials, including all supplementary groups,
and stores them in cache. But this access to user's credentials from
server side is not always an option.

As an alternative to the server side's identity upcall, we implement
a retry mechanism for intent locking. The client can provide at most
2 supplementary groups in the request sent to the MDS, but sometimes
it does not know which ones are useful for credentials calculation on
server side. For instance in case of lookup, the client does not have
the child inode yet when it sends the intent lock request.
Hopefully, the server can hint at the useful groups, by putting in the
request reply the target inode's GID, and also its ACL. So, in case
the server replies -EACCES, we check the user's credentials against
those, and try again the intent lock request if we find a matching
supplementary group.

On server side, we add a new identity_upcall value named "INTERNAL".
It implements a particular behavior which does not involve an actual
upcall, but instead the cache is filled with supplementary groups read
from the client request, cumulatively at each request.

A problem with the 'runas' utility was found during testing. If no
supplementary group is provided via the '-G' option, then it needs at
least to set the given GID as a supplementary group. Otherwise the
supplementary groups of the invoking user would be silently inherited.
For instance, if root user calls 'runas -u 500 -g 500 $CMD', we must
not execute $CMD with UID:GID 500:500 and supplementary group 0, as
it would make the user executing $CMD part of the superuser group.

Test-Parameters: testgroup=review-dne-part-1 env=L_GETIDENTITY=INTERNAL
Test-Parameters: testgroup=review-dne-part-2 env=L_GETIDENTITY=INTERNAL
Test-Parameters: testgroup=review-dne-selinux-ssk-part-1 env=L_GETIDENTITY=INTERNAL
Test-Parameters: testgroup=review-dne-selinux-ssk-part-2 env=L_GETIDENTITY=INTERNAL
Signed-off-by: Sebastien Buisson <sbuisson@ddn.com>
Change-Id: I4608bb766a70ca12a2142a0e2687813f3a4b9100
Reviewed-on: https://review.whamcloud.com/46493
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Lai Siyao <lai.siyao@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
13 files changed:
lustre/include/lustre_idmap.h
lustre/llite/file.c
lustre/llite/llite_internal.h
lustre/llite/namei.c
lustre/llite/xattr_cache.c
lustre/mdt/mdt_identity.c
lustre/mdt/mdt_internal.h
lustre/mdt/mdt_lib.c
lustre/obdclass/idmap.c
lustre/obdclass/upcall_cache.c
lustre/tests/runas.c
lustre/tests/sanity-pcc.sh
lustre/tests/test-framework.sh

index a8c5a21..e90dc2a 100644 (file)
@@ -62,7 +62,9 @@
 
 struct lu_ucred;
 
+extern int lustre_groups_search(struct group_info *group_info, gid_t grp);
 extern void lustre_groups_from_list(struct group_info *ginfo, gid_t *glist);
+extern void lustre_list_from_groups(gid_t *glist, struct group_info *ginfo);
 extern void lustre_groups_sort(struct group_info *group_info);
 extern int lustre_in_group_p(struct lu_ucred *mu, gid_t grp);
 
index e6dda29..6e853f5 100644 (file)
@@ -668,8 +668,8 @@ retry:
 
        OBD_FAIL_TIMEOUT(OBD_FAIL_LLITE_OPEN_DELAY, cfs_fail_val);
 
-       rc = md_intent_lock(sbi->ll_md_exp, op_data, itp, &req,
-                           &ll_md_blocking_ast, 0);
+       rc = ll_intent_lock(sbi->ll_md_exp, op_data, itp, &req,
+                           &ll_md_blocking_ast, 0, true);
        kfree(name);
        ll_finish_md_op_data(op_data);
        if (rc == -ESTALE) {
@@ -1218,7 +1218,7 @@ ll_lease_open(struct inode *inode, struct file *file, fmode_t fmode,
 
        it.it_flags = fmode | open_flags;
        it.it_flags |= MDS_OPEN_LOCK | MDS_OPEN_BY_FID | MDS_OPEN_LEASE;
-       rc = md_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
+       rc = ll_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
                            &ll_md_blocking_lease_ast,
        /* LDLM_FL_NO_LRU: To not put the lease lock into LRU list, otherwise
         * it can be cancelled which may mislead applications that the lease is
@@ -1226,7 +1226,8 @@ ll_lease_open(struct inode *inode, struct file *file, fmode_t fmode,
         * LDLM_FL_EXCL: Set this flag so that it won't be matched by normal
         * open in ll_md_blocking_ast(). Otherwise as ll_md_blocking_lease_ast
         * doesn't deal with openhandle, so normal openhandle will be leaked. */
-                           LDLM_FL_NO_LRU | LDLM_FL_EXCL);
+                           LDLM_FL_NO_LRU | LDLM_FL_EXCL,
+                           true);
        ll_finish_md_op_data(op_data);
        ptlrpc_req_finished(req);
        if (rc < 0)
@@ -5179,7 +5180,8 @@ static int ll_inode_revalidate(struct dentry *dentry, enum ldlm_intent_flags op)
        /* Call getattr by fid */
        if (exp_connect_flags2(exp) & OBD_CONNECT2_GETATTR_PFID)
                op_data->op_flags = MF_GETATTR_BY_FID;
-       rc = md_intent_lock(exp, op_data, &oit, &req, &ll_md_blocking_ast, 0);
+       rc = ll_intent_lock(exp, op_data, &oit, &req,
+                           &ll_md_blocking_ast, 0, true);
        ll_finish_md_op_data(op_data);
        if (rc < 0) {
                rc = ll_inode_revalidate_fini(inode, rc);
@@ -6054,8 +6056,8 @@ static int ll_layout_intent(struct inode *inode, struct layout_intent *intent)
        LDLM_DEBUG_NOLOCK("%s: requeue layout lock for file "DFID"(%p)",
                          sbi->ll_fsname, PFID(&lli->lli_fid), inode);
 
-       rc = md_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
-                           &ll_md_blocking_ast, 0);
+       rc = ll_intent_lock(sbi->ll_md_exp, op_data, &it, &req,
+                           &ll_md_blocking_ast, 0, true);
        if (it.it_request != NULL)
                ptlrpc_req_finished(it.it_request);
        it.it_request = NULL;
index 9e8f328..105355b 100644 (file)
@@ -1136,6 +1136,10 @@ int ll_md_blocking_ast(struct ldlm_lock *, struct ldlm_lock_desc *,
 struct dentry *ll_splice_alias(struct inode *inode, struct dentry *de);
 int ll_rmdir_entry(struct inode *dir, char *name, int namelen);
 void ll_update_times(struct ptlrpc_request *request, struct inode *inode);
+int ll_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+                  struct lookup_intent *it, struct ptlrpc_request **reqp,
+                  ldlm_blocking_callback cb_blocking, __u64 extra_lock_flags,
+                  bool tryagain);
 
 /* llite/rw.c */
 int ll_writepage(struct page *page, struct writeback_control *wbc);
index 428bee2..77211f8 100644 (file)
@@ -801,6 +801,137 @@ out:
        return rc;
 }
 
+static int get_acl_from_req(struct ptlrpc_request *req, struct posix_acl **acl)
+{
+       struct mdt_body *body;
+       void *buf;
+       int rc;
+
+       body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
+       if (!body->mbo_aclsize) {
+               *acl = NULL;
+               return 0;
+       }
+
+       buf = req_capsule_server_sized_get(&req->rq_pill, &RMF_ACL,
+                                          body->mbo_aclsize);
+       if (!buf)
+               return -EPROTO;
+
+       *acl = posix_acl_from_xattr(&init_user_ns, buf, body->mbo_aclsize);
+       if (IS_ERR_OR_NULL(*acl)) {
+               rc = *acl ? PTR_ERR(*acl) : 0;
+               CDEBUG(D_SEC, "convert xattr to acl: %d\n", rc);
+               return rc;
+       }
+
+       rc = posix_acl_valid(&init_user_ns, *acl);
+       if (rc) {
+               CDEBUG(D_SEC, "validate acl: %d\n", rc);
+               posix_acl_release(*acl);
+               return rc;
+       }
+
+       return 0;
+}
+
+static inline int accmode_from_openflags(u64 open_flags)
+{
+       unsigned int may_mask = 0;
+
+       if (open_flags & (FMODE_READ | FMODE_PREAD))
+               may_mask |= MAY_READ;
+       if (open_flags & (FMODE_WRITE | FMODE_PWRITE))
+               may_mask |= MAY_WRITE;
+       if (open_flags & FMODE_EXEC)
+               may_mask = MAY_EXEC;
+
+       return may_mask;
+}
+
+static __u32 get_uc_group_from_acl(const struct posix_acl *acl, int want)
+{
+       const struct posix_acl_entry *pa, *pe;
+
+       FOREACH_ACL_ENTRY(pa, acl, pe) {
+               switch (pa->e_tag) {
+               case ACL_GROUP_OBJ:
+               case ACL_GROUP:
+                       if (in_group_p(pa->e_gid) &&
+                           (pa->e_perm & want) == want)
+                               return (__u32)from_kgid(&init_user_ns,
+                                                       pa->e_gid);
+                       break;
+               default:
+                       /* nothing to do */
+                       break;
+               }
+       }
+
+       return (__u32)__kgid_val(INVALID_GID);
+}
+
+/* This function implements a retry mechanism on top of md_intent_lock().
+ * This is useful because the client can provide at most 2 supplementary
+ * groups in the request sent to the MDS, but sometimes it does not know
+ * which ones are useful for credentials calculation on server side. For
+ * instance in case of lookup, the client does not have the child inode yet
+ * when it sends the intent lock request.
+ * Hopefully, the server can hint at the useful groups, by putting in the
+ * request reply the target inode's GID, and also its ACL.
+ * So in case the server replies -EACCES, we check the user's credentials
+ * against those, and try again the intent lock request if we find a matching
+ * supplementary group.
+ */
+int ll_intent_lock(struct obd_export *exp, struct md_op_data *op_data,
+                  struct lookup_intent *it, struct ptlrpc_request **reqp,
+                  ldlm_blocking_callback cb_blocking, __u64 extra_lock_flags,
+                  bool tryagain)
+{
+       int rc;
+
+       ENTRY;
+
+       rc = md_intent_lock(exp, op_data, it, reqp, cb_blocking,
+                           extra_lock_flags);
+       if (rc == -EACCES && tryagain && it->it_op & IT_OPEN &&
+           it_disposition(it, DISP_OPEN_DENY) && *reqp) {
+               struct mdt_body *body;
+               __u32 new_suppgid;
+
+               body = req_capsule_server_get(&(*reqp)->rq_pill, &RMF_MDT_BODY);
+               new_suppgid = body->mbo_gid;
+               if (op_data->op_suppgids[0] == body->mbo_gid ||
+                   op_data->op_suppgids[1] == body->mbo_gid ||
+                   !in_group_p(make_kgid(&init_user_ns, body->mbo_gid))) {
+                       int accmode = accmode_from_openflags(it->it_flags);
+                       struct posix_acl *acl;
+
+                       rc = get_acl_from_req(*reqp, &acl);
+                       if (rc || !acl)
+                               GOTO(out, rc = -EACCES);
+
+                       new_suppgid = get_uc_group_from_acl(acl, accmode);
+                       posix_acl_release(acl);
+
+                       if (new_suppgid == (__u32)__kgid_val(INVALID_GID))
+                               GOTO(out, rc = -EACCES);
+               }
+
+               if (!(it->it_flags & MDS_OPEN_BY_FID))
+                       fid_zero(&op_data->op_fid2);
+               op_data->op_suppgids[1] = new_suppgid;
+               ptlrpc_req_finished(*reqp);
+               *reqp = NULL;
+               ll_intent_release(it);
+               rc = md_intent_lock(exp, op_data, it, reqp, cb_blocking,
+                                   extra_lock_flags);
+       }
+
+out:
+       RETURN(rc);
+}
+
 static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
                                   struct lookup_intent *it,
                                   void **secctx, __u32 *secctxlen,
@@ -1013,34 +1144,13 @@ inherit:
                it->it_flags |= MDS_OPEN_PCC;
        }
 
-       rc = md_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
-                           &ll_md_blocking_ast, 0);
        /* If the MDS allows the client to chgrp (CFS_SETGRP_PERM), but the
         * client does not know which suppgid should be sent to the MDS, or
         * some other(s) changed the target file's GID after this RPC sent
         * to the MDS with the suppgid as the original GID, then we should
         * try again with right suppgid. */
-       if (rc == -EACCES && it->it_op & IT_OPEN &&
-           it_disposition(it, DISP_OPEN_DENY)) {
-               struct mdt_body *body;
-
-               LASSERT(req != NULL);
-
-               body = req_capsule_server_get(&req->rq_pill, &RMF_MDT_BODY);
-               if (op_data->op_suppgids[0] == body->mbo_gid ||
-                   op_data->op_suppgids[1] == body->mbo_gid ||
-                   !in_group_p(make_kgid(&init_user_ns, body->mbo_gid)))
-                       GOTO(out, retval = ERR_PTR(-EACCES));
-
-               fid_zero(&op_data->op_fid2);
-               op_data->op_suppgids[1] = body->mbo_gid;
-               ptlrpc_req_finished(req);
-               req = NULL;
-               ll_intent_release(it);
-               rc = md_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
-                                   &ll_md_blocking_ast, 0);
-       }
-
+       rc = ll_intent_lock(ll_i2mdexp(parent), op_data, it, &req,
+                           &ll_md_blocking_ast, 0, true);
        if (rc < 0)
                GOTO(out, retval = ERR_PTR(rc));
 
index 6fef45f..011bc27 100644 (file)
@@ -401,7 +401,8 @@ static int ll_xattr_find_get_lock(struct inode *inode,
 
        op_data->op_valid = OBD_MD_FLXATTR | OBD_MD_FLXATTRLS;
 
-       rc = md_intent_lock(exp, op_data, oit, req, &ll_md_blocking_ast, 0);
+       rc = ll_intent_lock(exp, op_data, oit, req,
+                           &ll_md_blocking_ast, 0, true);
        ll_finish_md_op_data(op_data);
        *req = oit->it_request;
 
index e1bceee..e8a355b 100644 (file)
@@ -187,14 +187,16 @@ out:
        RETURN(rc);
 }
 
-struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid)
+struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid,
+                                    struct mdt_thread_info *info)
 {
        struct upcall_cache_entry *entry;
 
        if (!cache)
                return ERR_PTR(-ENOENT);
 
-       entry = upcall_cache_get_entry(cache, (__u64)uid, NULL);
+       entry = upcall_cache_get_entry(cache, (__u64)uid,
+                                      info ? mdt_ucred(info) : NULL);
        if (unlikely(!entry))
                return ERR_PTR(-ENOENT);
        if (IS_ERR(entry))
index b8cee76..0eabbe1 100644 (file)
@@ -1011,7 +1011,8 @@ static inline bool agent_req_in_final_state(enum agent_req_status ars)
 
 extern struct upcall_cache_ops mdt_identity_upcall_cache_ops;
 
-struct md_identity *mdt_identity_get(struct upcall_cache *, __u32);
+struct md_identity *mdt_identity_get(struct upcall_cache *cache, __u32 uid,
+                                    struct mdt_thread_info *info);
 
 void mdt_identity_put(struct upcall_cache *, struct md_identity *);
 
index c7fd2fd..aece627 100644 (file)
@@ -247,7 +247,7 @@ static int new_init_ucred(struct mdt_thread_info *info, ucred_init_type_t type,
                struct md_identity *identity;
 
                identity = mdt_identity_get(mdt->mdt_identity_cache,
-                                           pud->pud_uid);
+                                           pud->pud_uid, info);
                if (IS_ERR(identity)) {
                        if (unlikely(PTR_ERR(identity) == -EREMCHG)) {
                                ucred->uc_identity = NULL;
@@ -419,7 +419,8 @@ int mdt_check_ucred(struct mdt_thread_info *info)
        if (is_identity_get_disabled(mdt->mdt_identity_cache))
                RETURN(0);
 
-       identity = mdt_identity_get(mdt->mdt_identity_cache, pud->pud_uid);
+       identity = mdt_identity_get(mdt->mdt_identity_cache, pud->pud_uid,
+                                   info);
        if (IS_ERR(identity)) {
                if (unlikely(PTR_ERR(identity) == -EREMCHG)) {
                        RETURN(0);
@@ -478,7 +479,7 @@ static int old_init_ucred_common(struct mdt_thread_info *info,
 
        if (!is_identity_get_disabled(mdt->mdt_identity_cache)) {
                identity = mdt_identity_get(mdt->mdt_identity_cache,
-                                           uc->uc_fsuid);
+                                           uc->uc_fsuid, info);
                if (IS_ERR(identity)) {
                        if (unlikely(PTR_ERR(identity) == -EREMCHG ||
                                     uc->uc_cap & CFS_CAP_FS_MASK)) {
index f2d77ba..9db5497 100644 (file)
@@ -50,8 +50,7 @@
  * groups_search() is copied from linux kernel!
  * A simple bsearch.
  */
-static int lustre_groups_search(struct group_info *group_info,
-                               gid_t grp)
+int lustre_groups_search(struct group_info *group_info, gid_t grp)
 {
        int left, right;
 
@@ -74,6 +73,7 @@ static int lustre_groups_search(struct group_info *group_info,
        }
        return 0;
 }
+EXPORT_SYMBOL(lustre_groups_search);
 
 void lustre_groups_from_list(struct group_info *ginfo, gid_t *glist)
 {
@@ -96,6 +96,27 @@ void lustre_groups_from_list(struct group_info *ginfo, gid_t *glist)
 }
 EXPORT_SYMBOL(lustre_groups_from_list);
 
+void lustre_list_from_groups(gid_t *glist, struct group_info *ginfo)
+{
+#ifdef HAVE_GROUP_INFO_GID
+       memcpy(glist, ginfo->gid, ginfo->ngroups * sizeof(__u32));
+#else
+       int i;
+       int count = ginfo->ngroups;
+
+       /* fill in gid array from group_info */
+       for (i = 0; i < ginfo->nblocks && count > 0; i++) {
+               int cp_count = min(CFS_NGROUPS_PER_BLOCK, count);
+               int off = i * CFS_NGROUPS_PER_BLOCK;
+               int len = cp_count * sizeof(*glist);
+
+               memcpy(glist + off, ginfo->blocks[i], len);
+               count -= cp_count;
+       }
+#endif
+}
+EXPORT_SYMBOL(lustre_list_from_groups);
+
 /* groups_sort() is copied from linux kernel! */
 /* a simple shell-metzner sort */
 void lustre_groups_sort(struct group_info *group_info)
index 9624c0a..a6de37b 100644 (file)
 
 #include <libcfs/libcfs.h>
 #include <uapi/linux/lnet/lnet-types.h>
+#include <lustre_idmap.h>
+#include <md_object.h>
 #include <upcall_cache.h>
 
+/* The special identity_upcall value "INTERNAL" implements a particular behavior
+ * which does not involve an actual upcall. Instead, the cache is filled with
+ * supplementary groups read from the user's credentials provided as input
+ * (usually got from the client request), cumulatively at each request.
+ */
+#define IDENTITY_UPCALL_INTERNAL       "INTERNAL"
+
 static struct upcall_cache_entry *alloc_entry(struct upcall_cache *cache,
                                              __u64 key, void *args)
 {
@@ -137,8 +146,40 @@ static int check_unlink_entry(struct upcall_cache *cache,
 }
 
 static inline int refresh_entry(struct upcall_cache *cache,
-                        struct upcall_cache_entry *entry)
+                               struct upcall_cache_entry *entry,
+                               __u32 fsgid, int ngroups, gid_t *grouplist)
 {
+       if (strcmp(cache->uc_upcall, IDENTITY_UPCALL_INTERNAL) == 0) {
+               struct group_info *ginfo = NULL;
+
+               if (ngroups && grouplist) {
+                       ginfo = groups_alloc(ngroups);
+                       if (!ginfo) {
+                               CDEBUG(D_OTHER,
+                                      "failed to alloc %d groups: rc = %d\n",
+                                      ngroups, -ENOMEM);
+                               return -ENOMEM;
+                       }
+                       lustre_groups_from_list(ginfo, grouplist);
+                       lustre_groups_sort(ginfo);
+               }
+
+               spin_lock(&cache->uc_lock);
+               get_entry(entry);
+               entry->u.identity.mi_uid = entry->ue_key;
+               entry->u.identity.mi_gid = fsgid;
+               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);
+               UC_CACHE_CLEAR_ACQUIRING(entry);
+               spin_unlock(&cache->uc_lock);
+               put_entry(cache, entry);
+
+               return 0;
+       }
+
        LASSERT(cache->uc_ops->do_upcall);
        return cache->uc_ops->do_upcall(cache, entry);
 }
@@ -147,9 +188,11 @@ struct upcall_cache_entry *upcall_cache_get_entry(struct upcall_cache *cache,
                                                  __u64 key, void *args)
 {
        struct upcall_cache_entry *entry = NULL, *new = NULL, *next;
+       struct lu_ucred *uc = (struct lu_ucred *)args;
+       gid_t *grouplist = NULL, *glist_p;
        struct list_head *head;
        wait_queue_entry_t wait;
-       int rc, found;
+       int rc, found, i, ngroups = 0;
        ENTRY;
 
        LASSERT(cache);
@@ -190,12 +233,77 @@ find_again:
        }
        get_entry(entry);
 
+       spin_unlock(&cache->uc_lock);
+       if (!grouplist && !strcmp(cache->uc_upcall, IDENTITY_UPCALL_INTERNAL) &&
+           uc && uc->uc_suppgids[0] != -1) {
+               struct md_identity *identity = &entry->u.identity;
+               bool supp_in_ginfo[2] = { false, uc->uc_suppgids[1] == -1 };
+
+               if (!identity->mi_ginfo ||
+                   !identity->mi_ginfo->ngroups)
+                       ngroups = 0;
+               else
+                       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 (ngroups) {
+                               atomic_inc(&identity->mi_ginfo->usage);
+                               supp_in_ginfo[i] =
+                                       lustre_groups_search(identity->mi_ginfo,
+                                                           uc->uc_suppgids[i]);
+                               atomic_dec(&identity->mi_ginfo->usage);
+                       }
+               }
+
+               /* 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]) {
+                       ngroups += !supp_in_ginfo[0] + !supp_in_ginfo[1];
+                       CFS_ALLOC_PTR_ARRAY(grouplist, ngroups);
+                       if (grouplist == NULL)
+                               GOTO(out, entry = ERR_PTR(-ENOMEM));
+
+                       glist_p = grouplist;
+                       for (i = 0; i < 2; i++) {
+                               if (!supp_in_ginfo[i])
+                                       *(glist_p++) = uc->uc_suppgids[i];
+                       }
+
+                       if (identity->mi_ginfo && identity->mi_ginfo->ngroups) {
+                               atomic_inc(&identity->mi_ginfo->usage);
+                               lustre_list_from_groups(glist_p,
+                                                       identity->mi_ginfo);
+                               atomic_dec(&identity->mi_ginfo->usage);
+                       }
+
+                       if (!UC_CACHE_IS_NEW(entry)) {
+                               /* force refresh as an existing cache entry
+                                * cannot be modified
+                                */
+                               spin_lock(&cache->uc_lock);
+                               entry->ue_expire = ktime_get_seconds();
+                               spin_unlock(&cache->uc_lock);
+                       }
+               }
+       }
+
+       spin_lock(&cache->uc_lock);
        /* acquire for new one */
        if (UC_CACHE_IS_NEW(entry)) {
+               gid_t fsgid = (__u32)__kgid_val(INVALID_GID);
+
+               if (uc)
+                       fsgid = uc->uc_fsgid;
+
                UC_CACHE_SET_ACQUIRING(entry);
                UC_CACHE_CLEAR_NEW(entry);
                spin_unlock(&cache->uc_lock);
-               rc = refresh_entry(cache, entry);
+               rc = refresh_entry(cache, entry, fsgid, ngroups, grouplist);
                spin_lock(&cache->uc_lock);
                entry->ue_acquire_expire = ktime_get_seconds() +
                                           cache->uc_acquire_expire;
@@ -263,6 +371,8 @@ find_again:
        /* Now we know it's good */
 out:
        spin_unlock(&cache->uc_lock);
+       if (grouplist)
+               CFS_FREE_PTR_ARRAY(grouplist, ngroups);
        RETURN(entry);
 }
 EXPORT_SYMBOL(upcall_cache_get_entry);
index f43dc08..162e83c 100644 (file)
 #endif
 
 static const char usage[] =
-"Usage: %s -u user_id [-g grp_id] [-v euid] [-j egid] [-G[gid0,gid1,...]] command\n"
+"Usage: %s -u user_id [-g grp_id] [-v euid] [-j egid] [-G<gid0,gid1,...>] command\n"
 "  -u user_id           switch to UID user_id\n"
 "  -g grp_id            switch to GID grp_id\n"
 "  -v euid              switch euid to UID\n"
 "  -j egid              switch egid to GID\n"
-"  -G[gid0,gid1,...]    set supplementary groups\n";
+"  -G<gid0,gid1,...>    set supplementary groups\n";
 
 void Usage_and_abort(const char *name)
 {
@@ -145,9 +145,14 @@ int main(int argc, char **argv)
                        break;
 
                case 'G':
-                       num_supp = 0;
-                       if (!optarg || !isdigit(optarg[0]))
+                       if (!optarg || !isdigit(optarg[0])) {
+                               fprintf(stderr,
+                                       "Provided parameter '%s' for option '-G' is bad\n",
+                                       optarg);
+                               Usage_and_abort(name);
                                break;
+                       }
+                       num_supp = 0;
                        while ((grp = strsep(&optarg, ",")) != NULL) {
                                printf("adding supp group %d\n", atoi(grp));
                                supp_groups[num_supp++] = atoi(grp);
@@ -200,12 +205,17 @@ int main(int argc, char **argv)
                exit(-1);
        }
 
-       if (num_supp >= 0) {
-               status = setgroups(num_supp, supp_groups);
-               if (status == -1) {
-                       perror("setting supplementary groups");
-                       exit(-1);
-               }
+       if (num_supp == -1) {
+               /* at least one supp group needs to be provided,
+                * so take the gid
+                */
+               num_supp = 1;
+               supp_groups[0] = grp_id;
+       }
+       status = setgroups(num_supp, supp_groups);
+       if (status == -1) {
+               perror("setting supplementary groups");
+               exit(-1);
        }
 
        /* set UID */
index feb5dbf..f0d6ac3 100644 (file)
@@ -3614,6 +3614,7 @@ test_46() {
        local hsm_root="$mntpt/$tdir"
        local file=$DIR/$tfile
        local fsuuid=$($LFS getname $MOUNT | awk '{print $1}')
+       local runascmd="$RUNAS -G0"
 
        setup_loopdev client $loopfile $mntpt 60
        mkdir $hsm_root || error "mkdir $hsm_root failed"
@@ -3622,7 +3623,7 @@ test_46() {
        $LCTL pcc list $MOUNT
 
        local mode=$($LCTL get_param -n llite.$fsuuid.pcc_mode)
-       $RUNAS id
+       $runascmd id
 
        echo "Mode: $mode"
        echo "QQQQQ" > $file || error "write $file failed"
@@ -3631,44 +3632,44 @@ test_46() {
        $LCTL set_param llite.$fsuuid.pcc_mode="0" ||
                error "Set PCC mode failed"
        stack_trap "$LCTL set_param llite.$fsuuid.pcc_mode=$mode" EXIT
-       $RUNAS $LFS pcc attach -r $file &&
+       $runascmd $LFS pcc attach -r $file &&
                error "User should not attach $file"
-       $RUNAS cat $file || error "cat $file failed"
+       $runascmd cat $file || error "cat $file failed"
        check_lpcc_state $file "none" client
 
        $LCTL set_param llite.$fsuuid.pcc_mode="0400" ||
                error "Set PCC mode failed"
        stack_trap "$LCTL set_param llite.$fsuuid.pcc_mode=$mode" EXIT
-       $RUNAS $LFS pcc attach -r $file &&
+       $runascmd $LFS pcc attach -r $file &&
                error "User should not attach $file"
-       $RUNAS cat $file || error "cat $file failed"
+       $runascmd cat $file || error "cat $file failed"
        check_lpcc_state $file "none" client
 
        $LCTL set_param llite.$fsuuid.pcc_mode="0004" ||
                error "Set PCC mode failed"
-       $RUNAS cat $file || error "cat $file failed"
+       $runascmd cat $file || error "cat $file failed"
        $LFS pcc state $file
        check_lpcc_state $file "readonly" client
-       $RUNAS $LFS pcc detach $file || error "Detach $file failed"
+       $runascmd $LFS pcc detach $file || error "Detach $file failed"
 
-       $RUNAS stat $file || error "stat $file failed"
+       $runascmd stat $file || error "stat $file failed"
        $LFS pcc attach -r $file || error "failed to attach $file"
        check_lpcc_state $file "readonly" client
-       $RUNAS $LFS pcc detach $file || error "failed to detach $file"
+       $runascmd $LFS pcc detach $file || error "failed to detach $file"
 
        $LCTL set_param llite.$fsuuid.pcc_mode="0040" ||
                error "Set PCC mode failed"
        chmod 660 $file || error "chmod $file failed"
-       $RUNAS cat $file || error "cat $file failed"
+       $runascmd cat $file || error "cat $file failed"
        $LFS pcc state $file
        check_lpcc_state $file "readonly" client
-       $RUNAS $LFS pcc detach $file || error "failed to detach $file"
+       $runascmd $LFS pcc detach $file || error "failed to detach $file"
 
-       $RUNAS $LFS pcc attach -r $file || error "attach $file failed"
+       $runascmd $LFS pcc attach -r $file || error "attach $file failed"
        stat $file || error "stat $file failed"
        $LFS pcc state $file
        check_lpcc_state $file "readonly" client
-       $RUNAS $LFS pcc detach $file || error "Detach $file failed"
+       $runascmd $LFS pcc detach $file || error "Detach $file failed"
 }
 run_test 46 "Verify PCC mode setting works correctly"
 
index bb748b9..3a71f59 100755 (executable)
@@ -306,7 +306,7 @@ init_test_env() {
        export PERM_CMD=${PERM_CMD:-"$LCTL conf_param"}
 
        export L_GETIDENTITY=${L_GETIDENTITY:-"$LUSTRE/utils/l_getidentity"}
-       if [ ! -f "$L_GETIDENTITY" ]; then
+       if [ "$L_GETIDENTITY" != "INTERNAL" ] && [ ! -f "$L_GETIDENTITY" ]; then
                if `which l_getidentity > /dev/null 2>&1`; then
                        export L_GETIDENTITY=$(which l_getidentity)
                else