b7d4d7
From 87b7689f7727a542c5afa22bdebd3781dd650a2f Mon Sep 17 00:00:00 2001
b7d4d7
From: Csaba Henk <csaba@redhat.com>
b7d4d7
Date: Fri, 17 Jul 2020 11:33:36 +0200
b7d4d7
Subject: [PATCH 508/511] fuse: fetch arbitrary number of groups from
b7d4d7
 /proc/[pid]/status
b7d4d7
b7d4d7
Glusterfs so far constrained itself with an arbitrary limit (32)
b7d4d7
for the number of groups read from /proc/[pid]/status (this was
b7d4d7
the number of groups shown there prior to Linux commit
b7d4d7
v3.7-9553-g8d238027b87e (v3.8-rc1~74^2~59); since this commit, all
b7d4d7
groups are shown).
b7d4d7
b7d4d7
With this change we'll read groups up to the number Glusterfs
b7d4d7
supports in general (64k).
b7d4d7
b7d4d7
Note: the actual number of groups that are made use of in a
b7d4d7
regular Glusterfs setup shall still be capped at ~93 due to limitations
b7d4d7
of the RPC transport. To be able to handle more groups than that,
b7d4d7
brick side gid resolution (server.manage-gids option) can be used along
b7d4d7
with NIS, LDAP or other such networked directory service (see
b7d4d7
https://github.com/gluster/glusterdocs/blob/5ba15a2/docs/Administrator%20Guide/Handling-of-users-with-many-groups.md#limit-in-the-glusterfs-protocol
b7d4d7
).
b7d4d7
b7d4d7
Also adding some diagnostic messages to frame_fill_groups().
b7d4d7
b7d4d7
Upstream:
b7d4d7
> Reviewed-on: https://review.gluster.org/c/glusterfs/+/24721
b7d4d7
> Change-Id: I271f3dc3e6d3c44d6d989c7a2073ea5f16c26ee0
b7d4d7
> fixes: #1075
b7d4d7
> Signed-off-by: Csaba Henk <csaba@redhat.com>
b7d4d7
b7d4d7
BUG: 1749304
b7d4d7
Change-Id: I80bf99d34087fb95768bf2259d8c4774d9f5d0c5
b7d4d7
Signed-off-by: Csaba Henk <csaba@redhat.com>
b7d4d7
Reviewed-on: https://code.engineering.redhat.com/gerrit/220920
b7d4d7
Tested-by: RHGS Build Bot <nigelb@redhat.com>
b7d4d7
Reviewed-by: Sunil Kumar Heggodu Gopala Acharya <sheggodu@redhat.com>
b7d4d7
---
b7d4d7
 libglusterfs/src/glusterfs/stack.h    |  7 ++++
b7d4d7
 tests/bugs/fuse/many-groups-for-acl.t | 13 ++++++-
b7d4d7
 xlators/mount/fuse/src/fuse-helpers.c | 71 +++++++++++++++++++++++------------
b7d4d7
 3 files changed, 65 insertions(+), 26 deletions(-)
b7d4d7
b7d4d7
diff --git a/libglusterfs/src/glusterfs/stack.h b/libglusterfs/src/glusterfs/stack.h
b7d4d7
index 1758550..bd466d8 100644
b7d4d7
--- a/libglusterfs/src/glusterfs/stack.h
b7d4d7
+++ b/libglusterfs/src/glusterfs/stack.h
b7d4d7
@@ -429,6 +429,7 @@ call_stack_alloc_groups(call_stack_t *stack, int ngrps)
b7d4d7
     if (ngrps <= SMALL_GROUP_COUNT) {
b7d4d7
         stack->groups = stack->groups_small;
b7d4d7
     } else {
b7d4d7
+        GF_FREE(stack->groups_large);
b7d4d7
         stack->groups_large = GF_CALLOC(ngrps, sizeof(gid_t),
b7d4d7
                                         gf_common_mt_groups_t);
b7d4d7
         if (!stack->groups_large)
b7d4d7
@@ -442,6 +443,12 @@ call_stack_alloc_groups(call_stack_t *stack, int ngrps)
b7d4d7
 }
b7d4d7
 
b7d4d7
 static inline int
b7d4d7
+call_stack_groups_capacity(call_stack_t *stack)
b7d4d7
+{
b7d4d7
+    return max(stack->ngrps, SMALL_GROUP_COUNT);
b7d4d7
+}
b7d4d7
+
b7d4d7
+static inline int
b7d4d7
 call_frames_count(call_stack_t *call_stack)
b7d4d7
 {
b7d4d7
     call_frame_t *pos;
b7d4d7
diff --git a/tests/bugs/fuse/many-groups-for-acl.t b/tests/bugs/fuse/many-groups-for-acl.t
b7d4d7
index d959f75..a51b1bc 100755
b7d4d7
--- a/tests/bugs/fuse/many-groups-for-acl.t
b7d4d7
+++ b/tests/bugs/fuse/many-groups-for-acl.t
b7d4d7
@@ -38,6 +38,13 @@ do
b7d4d7
 done
b7d4d7
 TEST useradd -o -M -u ${NEW_UID} -g ${NEW_GID} -G ${NEW_USER}-${NEW_GIDS} ${NEW_USER}
b7d4d7
 
b7d4d7
+# Linux < 3.8 exports only first 32 gids of pid to userspace
b7d4d7
+kernel_exports_few_gids=0
b7d4d7
+if [ "$OSTYPE" = Linux ] && \
b7d4d7
+   su -m ${NEW_USER} -c "grep ^Groups: /proc/self/status | wc -w | xargs -I@ expr @ - 1 '<' $LAST_GID - $NEW_GID + 1" > /dev/null; then
b7d4d7
+       kernel_exports_few_gids=1
b7d4d7
+fi
b7d4d7
+
b7d4d7
 # preparation done, start the tests
b7d4d7
 
b7d4d7
 TEST glusterd
b7d4d7
@@ -48,6 +55,8 @@ TEST $CLI volume set $V0 nfs.disable off
b7d4d7
 TEST $CLI volume set ${V0} server.manage-gids off
b7d4d7
 TEST $CLI volume start ${V0}
b7d4d7
 
b7d4d7
+# This is just a synchronization hack to make sure the bricks are
b7d4d7
+# up before going on.
b7d4d7
 EXPECT_WITHIN ${NFS_EXPORT_TIMEOUT} "1" is_nfs_export_available
b7d4d7
 
b7d4d7
 # mount the volume with POSIX ACL support, without --resolve-gids
b7d4d7
@@ -69,8 +78,8 @@ TEST [ $? -eq 0 ]
b7d4d7
 su -m ${NEW_USER} -c "touch ${M0}/first-32-gids-2/success > /dev/null"
b7d4d7
 TEST [ $? -eq 0 ]
b7d4d7
 
b7d4d7
-su -m ${NEW_USER} -c "touch ${M0}/gid-64/failure > /dev/null"
b7d4d7
-TEST [ $? -ne 0 ]
b7d4d7
+su -m ${NEW_USER} -c "touch ${M0}/gid-64/success--if-all-gids-exported > /dev/null"
b7d4d7
+TEST [ $? -eq $kernel_exports_few_gids ]
b7d4d7
 
b7d4d7
 su -m ${NEW_USER} -c "touch ${M0}/gid-120/failure > /dev/null"
b7d4d7
 TEST [ $? -ne 0 ]
b7d4d7
diff --git a/xlators/mount/fuse/src/fuse-helpers.c b/xlators/mount/fuse/src/fuse-helpers.c
b7d4d7
index 5bfc40c..6e04cd4 100644
b7d4d7
--- a/xlators/mount/fuse/src/fuse-helpers.c
b7d4d7
+++ b/xlators/mount/fuse/src/fuse-helpers.c
b7d4d7
@@ -139,8 +139,6 @@ get_fuse_state(xlator_t *this, fuse_in_header_t *finh)
b7d4d7
     return state;
b7d4d7
 }
b7d4d7
 
b7d4d7
-#define FUSE_MAX_AUX_GROUPS                                                    \
b7d4d7
-    32 /* We can get only up to 32 aux groups from /proc */
b7d4d7
 void
b7d4d7
 frame_fill_groups(call_frame_t *frame)
b7d4d7
 {
b7d4d7
@@ -150,8 +148,6 @@ frame_fill_groups(call_frame_t *frame)
b7d4d7
     char filename[32];
b7d4d7
     char line[4096];
b7d4d7
     char *ptr = NULL;
b7d4d7
-    FILE *fp = NULL;
b7d4d7
-    int idx = 0;
b7d4d7
     long int id = 0;
b7d4d7
     char *saveptr = NULL;
b7d4d7
     char *endptr = NULL;
b7d4d7
@@ -191,45 +187,72 @@ frame_fill_groups(call_frame_t *frame)
b7d4d7
 
b7d4d7
         call_stack_set_groups(frame->root, ngroups, &mygroups);
b7d4d7
     } else {
b7d4d7
+        FILE *fp = NULL;
b7d4d7
+
b7d4d7
         ret = snprintf(filename, sizeof filename, "/proc/%d/status",
b7d4d7
                        frame->root->pid);
b7d4d7
-        if (ret >= sizeof filename)
b7d4d7
+        if (ret >= sizeof filename) {
b7d4d7
+            gf_log(this->name, GF_LOG_ERROR, "procfs path exceeds buffer size");
b7d4d7
             goto out;
b7d4d7
+        }
b7d4d7
 
b7d4d7
         fp = fopen(filename, "r");
b7d4d7
-        if (!fp)
b7d4d7
+        if (!fp) {
b7d4d7
+            gf_log(this->name, GF_LOG_ERROR, "failed to open %s: %s", filename,
b7d4d7
+                   strerror(errno));
b7d4d7
             goto out;
b7d4d7
+        }
b7d4d7
 
b7d4d7
-        if (call_stack_alloc_groups(frame->root, ngroups) != 0)
b7d4d7
-            goto out;
b7d4d7
+        for (;;) {
b7d4d7
+            gf_boolean_t found_groups = _gf_false;
b7d4d7
+            int idx = 0;
b7d4d7
 
b7d4d7
-        while ((ptr = fgets(line, sizeof line, fp))) {
b7d4d7
-            if (strncmp(ptr, "Groups:", 7) != 0)
b7d4d7
-                continue;
b7d4d7
+            if (call_stack_alloc_groups(frame->root, ngroups) != 0) {
b7d4d7
+                gf_log(this->name, GF_LOG_ERROR,
b7d4d7
+                       "failed to allocate gid buffer");
b7d4d7
+                goto out;
b7d4d7
+            }
b7d4d7
 
b7d4d7
+            while ((ptr = fgets(line, sizeof line, fp))) {
b7d4d7
+                if (strncmp(ptr, "Groups:", 7) == 0) {
b7d4d7
+                    found_groups = _gf_true;
b7d4d7
+                    break;
b7d4d7
+                }
b7d4d7
+            }
b7d4d7
+            if (!found_groups) {
b7d4d7
+                gf_log(this->name, GF_LOG_ERROR, "cannot find gid list in %s",
b7d4d7
+                       filename);
b7d4d7
+                break;
b7d4d7
+            }
b7d4d7
             ptr = line + 8;
b7d4d7
 
b7d4d7
             for (ptr = strtok_r(ptr, " \t\r\n", &saveptr); ptr;
b7d4d7
                  ptr = strtok_r(NULL, " \t\r\n", &saveptr)) {
b7d4d7
                 errno = 0;
b7d4d7
                 id = strtol(ptr, &endptr, 0);
b7d4d7
-                if (errno == ERANGE)
b7d4d7
-                    break;
b7d4d7
-                if (!endptr || *endptr)
b7d4d7
+                if (errno == ERANGE || !endptr || *endptr) {
b7d4d7
+                    gf_log(this->name, GF_LOG_ERROR, "failed to parse %s",
b7d4d7
+                           filename);
b7d4d7
                     break;
b7d4d7
-                frame->root->groups[idx++] = id;
b7d4d7
-                if (idx == FUSE_MAX_AUX_GROUPS)
b7d4d7
+                }
b7d4d7
+                if (idx < call_stack_groups_capacity(frame->root))
b7d4d7
+                    frame->root->groups[idx] = id;
b7d4d7
+                idx++;
b7d4d7
+                if (idx == GF_MAX_AUX_GROUPS)
b7d4d7
                     break;
b7d4d7
             }
b7d4d7
-
b7d4d7
-            frame->root->ngrps = idx;
b7d4d7
-            break;
b7d4d7
+            if (idx > call_stack_groups_capacity(frame->root)) {
b7d4d7
+                ngroups = idx;
b7d4d7
+                rewind(fp);
b7d4d7
+            } else {
b7d4d7
+                frame->root->ngrps = idx;
b7d4d7
+                break;
b7d4d7
+            }
b7d4d7
         }
b7d4d7
+    out:
b7d4d7
+        if (fp)
b7d4d7
+            fclose(fp);
b7d4d7
     }
b7d4d7
-
b7d4d7
-out:
b7d4d7
-    if (fp)
b7d4d7
-        fclose(fp);
b7d4d7
 #elif defined(GF_SOLARIS_HOST_OS)
b7d4d7
     char filename[32];
b7d4d7
     char scratch[128];
b7d4d7
@@ -245,7 +268,7 @@ out:
b7d4d7
         fp = fopen(filename, "r");
b7d4d7
         if (fp != NULL) {
b7d4d7
             if (fgets(scratch, sizeof scratch, fp) != NULL) {
b7d4d7
-                ngrps = MIN(prcred->pr_ngroups, FUSE_MAX_AUX_GROUPS);
b7d4d7
+                ngrps = MIN(prcred->pr_ngroups, GF_MAX_AUX_GROUPS);
b7d4d7
                 if (call_stack_alloc_groups(frame->root, ngrps) != 0) {
b7d4d7
                     fclose(fp);
b7d4d7
                     return;
b7d4d7
-- 
b7d4d7
1.8.3.1
b7d4d7