Blob Blame History Raw
From ab02d88f50c3cd92e432be26faa5bd77438068c7 Mon Sep 17 00:00:00 2001
From: Sanoj Unnikrishnan <sunnikri@redhat.com>
Date: Sun, 18 Dec 2016 17:29:47 +0530
Subject: [PATCH 252/257] Fixes GlusterFS process crashes on deep directory
 hierarchy

alloca() calls to recusive function posix_make_ancestryfromgfid
consumes the entire stack leading to segfault.
Changed the function to  iterative and reused the same linkname
buffer across iterations.

>Reviewed-on: http://review.gluster.org/16192
>Smoke: Gluster Build System <jenkins@build.gluster.org>
>NetBSD-regression: NetBSD Build System <jenkins@build.gluster.org>
>Reviewed-by: Raghavendra G <rgowdapp@redhat.com>
>CentOS-regression: Gluster Build System <jenkins@build.gluster.org>

Change-Id: If065c8b9f7f85219bdd2d23259a115fe66f5f60d
BUG: 1404633
Signed-off-by: Sanoj Unnikrishnan <sunnikri@redhat.com>
Reviewed-on: https://code.engineering.redhat.com/gerrit/93563
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
---
 xlators/storage/posix/src/posix-handle.c   | 142 ++++++++++++++++-------------
 xlators/storage/posix/src/posix-messages.h |  10 ++
 2 files changed, 87 insertions(+), 65 deletions(-)

diff --git a/xlators/storage/posix/src/posix-handle.c b/xlators/storage/posix/src/posix-handle.c
index ddafb0d..c8524a6 100644
--- a/xlators/storage/posix/src/posix-handle.c
+++ b/xlators/storage/posix/src/posix-handle.c
@@ -124,7 +124,6 @@ posix_make_ancestryfromgfid (xlator_t *this, char *path, int pathsize,
         char        *linkname   = NULL; /* "../../<gfid[0]>/<gfid[1]/"
                                          "<gfidstr>/<NAME_MAX>" */
         char        *dir_handle = NULL;
-        char        *dir_name   = NULL;
         char        *pgfidstr   = NULL;
         char        *saveptr    = NULL;
         ssize_t       len        = 0;
@@ -132,93 +131,106 @@ posix_make_ancestryfromgfid (xlator_t *this, char *path, int pathsize,
         struct iatt  iabuf      = {0, };
         int          ret        = -1;
         uuid_t       tmp_gfid   = {0, };
+        char        *dir_stack[PATH_MAX/2]; /* Since PATH_MAX/2 also gives
+                                            an upper bound on depth of
+                                            directories tree */
+        uuid_t       gfid_stack[PATH_MAX/2];
+
+        char        *dir_name   = NULL;
+        char        *saved_dir  = NULL;
+        int          top = -1;
 
         if (!path || !parent || !priv_base_path || gf_uuid_is_null (gfid)) {
                 *op_errno = EINVAL;
                 goto out;
         }
 
-        if (__is_root_gfid (gfid)) {
-                if (parent) {
-                        if (*parent) {
-                                inode_unref (*parent);
-                        }
+        dir_handle = alloca (handle_size);
+        linkname   = alloca (PATH_MAX);
+        gf_uuid_copy(tmp_gfid, gfid);
+
+        while (top < PATH_MAX/2) {
+
+                gf_uuid_copy(gfid_stack[++top], tmp_gfid);
+                if (__is_root_gfid (tmp_gfid)) {
 
                         *parent = inode_ref (itable->root);
-                }
 
+                        saved_dir = alloca(strlen("/") + 1);
+                        strcpy(saved_dir, "/");
+                        dir_stack[top] = saved_dir;
+                        break;
+                } else {
+                        snprintf (dir_handle, handle_size, "%s/%s/%02x/%02x/%s",
+                                  priv_base_path, GF_HIDDEN_PATH, tmp_gfid[0],
+                                  tmp_gfid[1], uuid_utoa (tmp_gfid));
+
+                        len = sys_readlink (dir_handle, linkname, PATH_MAX);
+                        if (len < 0) {
+                                *op_errno = errno;
+                                gf_msg (this->name, (errno == ENOENT ||
+                                                      errno == ESTALE)
+                                        ? GF_LOG_DEBUG:GF_LOG_ERROR, errno,
+                                        P_MSG_READLINK_FAILED, "could not read"
+                                        " the link from the gfid handle %s ",
+                                         dir_handle);
+                                ret = -1;
+                                goto out;
+                        }
 
-                inode = posix_resolve (this, itable, *parent, "/", &iabuf);
-                if (!inode) {
-                        gf_msg (this->name, GF_LOG_ERROR,
-                                P_MSG_INODE_RESOLVE_FAILED, 0,
-                                "posix resolve on the root inode %s failed",
-                                uuid_utoa (gfid));
-                        *op_errno = ESTALE;
-                        goto out;
-                }
+                        linkname[len] = '\0';
 
-                ret = posix_make_ancestral_node (priv_base_path, path, pathsize,
-                                                 head, "/", &iabuf, inode, type,
-                                                 xdata);
-                if (ret < 0)
-                        *op_errno = ENOMEM;
-                return ret;
+                        pgfidstr = strtok_r (linkname + SLEN("../../00/00/"),
+                                              "/", &saveptr);
+                        dir_name = strtok_r (NULL, "/", &saveptr);
+                        saved_dir = alloca(strlen(dir_name) + 1);
+                        gf_uuid_parse (pgfidstr, tmp_gfid);
+                        strcpy(saved_dir, dir_name);
+                        dir_stack[top] = saved_dir;
+                }
         }
-
-        dir_handle = alloca (handle_size);
-        linkname   = alloca (PATH_MAX);
-        snprintf (dir_handle, handle_size, "%s/%s/%02x/%02x/%s",
-                  priv_base_path, GF_HIDDEN_PATH, gfid[0], gfid[1],
-                  uuid_utoa (gfid));
-
-        len = sys_readlink (dir_handle, linkname, PATH_MAX);
-        if (len < 0) {
-                gf_msg (this->name, (errno == ENOENT || errno == ESTALE)
-                        ? GF_LOG_DEBUG:GF_LOG_ERROR, errno,
-                        P_MSG_READLINK_FAILED, "could not read the link from "
-                        "the gfid handle %s ", dir_handle);
+        if (top == PATH_MAX/2) {
+                gf_msg (this->name, GF_LOG_ERROR,
+                        P_MSG_ANCESTORY_FAILED,
+                        0, "build ancestory failed due to "
+                        "deep directory hierarchy, depth: %d.", top);
+                *op_errno = EINVAL;
                 ret = -1;
-                *op_errno = errno;
                 goto out;
         }
 
-        linkname[len] = '\0';
-
-        pgfidstr = strtok_r (linkname + SLEN("../../00/00/"), "/", &saveptr);
-        dir_name = strtok_r (NULL, "/", &saveptr);
-
-        gf_uuid_parse (pgfidstr, tmp_gfid);
+        while (top >= 0) {
 
-        ret = posix_make_ancestryfromgfid (this, path, pathsize, head, type,
-                                           tmp_gfid, handle_size,
-                                           priv_base_path, itable, parent,
-                                           xdata, op_errno);
-        if (ret < 0) {
-                goto out;
-        }
+                memset (&iabuf, 0, sizeof (iabuf));
+                inode = posix_resolve (this, itable, *parent,
+                                       dir_stack[top], &iabuf);
+                if (inode == NULL) {
+                        gf_msg (this->name, GF_LOG_ERROR,
+                                 P_MSG_INODE_RESOLVE_FAILED,
+                                0, "posix resolve on the inode %s failed",
+                                uuid_utoa (gfid_stack[top]));
+                        *op_errno = ESTALE;
+                        ret = -1;
+                        goto out;
+                }
 
-        memset (&iabuf, 0, sizeof (iabuf));
+                ret = posix_make_ancestral_node (priv_base_path, path,
+                                                 pathsize, head,
+                                                 dir_stack[top], &iabuf,
+                                                 inode, type, xdata);
+                if (ret < 0) {
+                        *op_errno = ENOMEM;
+                        goto out;
+                }
 
-        inode = posix_resolve (this, itable, *parent, dir_name, &iabuf);
-        if (inode == NULL) {
-                gf_msg (this->name, GF_LOG_ERROR, P_MSG_INODE_RESOLVE_FAILED,
-                        0, "posix resolve on the root inode %s failed",
-                        uuid_utoa (gfid));
-                *op_errno = ESTALE;
-                ret = -1;
-                goto out;
+                inode_unref (*parent);
+                *parent = inode_ref(inode);
+                top--;
         }
-
-        ret = posix_make_ancestral_node (priv_base_path, path, pathsize, head,
-                                         dir_name, &iabuf, inode, type, xdata);
+out:
         if (*parent != NULL) {
                 inode_unref (*parent);
         }
-
-        *parent = inode;
-
-out:
         return ret;
 }
 
diff --git a/xlators/storage/posix/src/posix-messages.h b/xlators/storage/posix/src/posix-messages.h
index ba6bf2c..ee06d6f 100644
--- a/xlators/storage/posix/src/posix-messages.h
+++ b/xlators/storage/posix/src/posix-messages.h
@@ -945,6 +945,16 @@
  *
  */
 
+#define P_MSG_ANCESTORY_FAILED                    (POSIX_COMP_BASE + 111)
+
+/*!
+ * @messageid
+ * @diagnosis
+ * @recommendedaction
+ *
+ */
+
+
 /*------------*/
 #define glfs_msg_end_x GLFS_MSGID_END, "Invalid: End of messages"
 
-- 
2.9.3