Blame SOURCES/0081-CACHE_REQ-Implement-the-plugin-methods-that-utilize-.patch

9f2ebf
From 7482c6affd4dfa77a8d465ff0283617792847725 Mon Sep 17 00:00:00 2001
9f2ebf
From: Jakub Hrozek <jhrozek@redhat.com>
9f2ebf
Date: Mon, 6 Nov 2017 15:52:11 +0100
9f2ebf
Subject: [PATCH 81/83] CACHE_REQ: Implement the plugin methods that utilize
9f2ebf
 the domain locator API
9f2ebf
MIME-Version: 1.0
9f2ebf
Content-Type: text/plain; charset=UTF-8
9f2ebf
Content-Transfer-Encoding: 8bit
9f2ebf
9f2ebf
Mainly, this patch adds handlers for the dp_get_domain_check_fn(),
9f2ebf
dp_get_domain_send_fn() and dp_get_domain_recv_fn() functions to
9f2ebf
requests that resolve objects by ID.
9f2ebf
9f2ebf
This patch also adds domain-local negcache setter for by-id methods
9f2ebf
Previously, the by-ID methods only used global negative cache setters
9f2ebf
because the ID space is global and we always iterated over all domains.
9f2ebf
9f2ebf
However, with addition of the domain locator plugin, we want also
9f2ebf
to skip only certain domains and the easiest way to to so is to add
9f2ebf
the IDs for domains that do not contain these IDs to the negative cache
9f2ebf
with the get-account-domain request.
9f2ebf
9f2ebf
Therefore this patch also adds per-domain negative cache setters for
9f2ebf
the three plugins that search by ID.
9f2ebf
9f2ebf
Reviewed-by: Pavel Březina <pbrezina@redhat.com>
9f2ebf
Reviewed-by: Sumit Bose <sbose@redhat.com>
9f2ebf
(cherry picked from commit a6eb9c4c3ff68d134bc745e8374f182737e9696b)
9f2ebf
---
9f2ebf
 src/responder/common/cache_req/cache_req_private.h |  5 ++
9f2ebf
 .../common/cache_req/plugins/cache_req_common.c    | 17 +++++
9f2ebf
 .../cache_req/plugins/cache_req_group_by_id.c      | 62 +++++++++++++++--
9f2ebf
 .../cache_req/plugins/cache_req_object_by_id.c     | 77 ++++++++++++++++++++--
9f2ebf
 .../cache_req/plugins/cache_req_user_by_id.c       | 63 ++++++++++++++++--
9f2ebf
 src/tests/cmocka/common_mock_resp_dp.c             | 23 +++++++
9f2ebf
 6 files changed, 235 insertions(+), 12 deletions(-)
9f2ebf
9f2ebf
diff --git a/src/responder/common/cache_req/cache_req_private.h b/src/responder/common/cache_req/cache_req_private.h
9f2ebf
index 9586e3788045ff44eb2a4b626dc7fcaf11ec8028..95f24c0e5b9ab1150591d308c7288c57fe478c5d 100644
9f2ebf
--- a/src/responder/common/cache_req/cache_req_private.h
9f2ebf
+++ b/src/responder/common/cache_req/cache_req_private.h
9f2ebf
@@ -187,4 +187,9 @@ bool
9f2ebf
 cache_req_common_dp_recv(struct tevent_req *subreq,
9f2ebf
                          struct cache_req *cr);
9f2ebf
 
9f2ebf
+errno_t
9f2ebf
+cache_reg_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
9f2ebf
+                                      struct tevent_req *subreq,
9f2ebf
+                                      struct cache_req *cr,
9f2ebf
+                                      char **_domain);
9f2ebf
 #endif /* _CACHE_REQ_PRIVATE_H_ */
9f2ebf
diff --git a/src/responder/common/cache_req/plugins/cache_req_common.c b/src/responder/common/cache_req/plugins/cache_req_common.c
9f2ebf
index 1f86258bc14c7a382712959f24a4ec4c153572d4..408c91949ceb3ecaf743f270f58f4e3fcfc3ccb1 100644
9f2ebf
--- a/src/responder/common/cache_req/plugins/cache_req_common.c
9f2ebf
+++ b/src/responder/common/cache_req/plugins/cache_req_common.c
9f2ebf
@@ -147,3 +147,20 @@ done:
9f2ebf
     talloc_free(err_msg);
9f2ebf
     return bret;
9f2ebf
 }
9f2ebf
+
9f2ebf
+errno_t
9f2ebf
+cache_reg_common_get_acct_domain_recv(TALLOC_CTX *mem_ctx,
9f2ebf
+                                      struct tevent_req *subreq,
9f2ebf
+                                      struct cache_req *cr,
9f2ebf
+                                      char **_domain)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+
9f2ebf
+    ret = sss_dp_get_account_domain_recv(mem_ctx, subreq, _domain);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        CACHE_REQ_DEBUG(SSSDBG_MINOR_FAILURE, cr,
9f2ebf
+                        "Could not get account domain [%d]: %s\n",
9f2ebf
+                        ret, sss_strerror(ret));
9f2ebf
+    }
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
diff --git a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
9f2ebf
index 70381266712d2c27c95027b54efab201c5df7690..ce84b1b4458b447ff6b4b036c6e8fe8f4d7758c8 100644
9f2ebf
--- a/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
9f2ebf
+++ b/src/responder/common/cache_req/plugins/cache_req_group_by_id.c
9f2ebf
@@ -39,6 +39,15 @@ cache_req_group_by_id_ncache_check(struct sss_nc_ctx *ncache,
9f2ebf
                                    struct sss_domain_info *domain,
9f2ebf
                                    struct cache_req_data *data)
9f2ebf
 {
9f2ebf
+    errno_t ret;
9f2ebf
+
9f2ebf
+    if (domain != NULL) {
9f2ebf
+        ret = sss_ncache_check_gid(ncache, domain, data->id);
9f2ebf
+        if (ret == EEXIST) {
9f2ebf
+            return ret;
9f2ebf
+        }
9f2ebf
+    }
9f2ebf
+
9f2ebf
     return sss_ncache_check_gid(ncache, NULL, data->id);
9f2ebf
 }
9f2ebf
 
9f2ebf
@@ -57,6 +66,14 @@ cache_req_group_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
     return sss_ncache_set_gid(ncache, false, NULL, data->id);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static errno_t
9f2ebf
+cache_req_group_by_id_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
+                                 struct sss_domain_info *domain,
9f2ebf
+                                 struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    return sss_ncache_set_gid(ncache, false, domain, data->id);
9f2ebf
+}
9f2ebf
+
9f2ebf
 static errno_t
9f2ebf
 cache_req_group_by_id_lookup(TALLOC_CTX *mem_ctx,
9f2ebf
                              struct cache_req *cr,
9f2ebf
@@ -132,6 +149,43 @@ cache_req_group_by_id_dp_send(TALLOC_CTX *mem_ctx,
9f2ebf
                                    SSS_DP_GROUP, string, id, flag);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static bool
9f2ebf
+cache_req_group_by_id_get_domain_check(struct resp_ctx *rctx,
9f2ebf
+                                       struct sss_domain_info *domain,
9f2ebf
+                                       struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret == EEXIST) {
9f2ebf
+        return false;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return true;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static struct tevent_req *
9f2ebf
+cache_req_group_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
9f2ebf
+                                      struct resp_ctx *rctx,
9f2ebf
+                                      struct sss_domain_info *domain,
9f2ebf
+                                      struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE,
9f2ebf
+              "Cannot set negative cache, this might result in performance degradation\n");
9f2ebf
+        /* Not fatal */
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return sss_dp_get_account_domain_send(mem_ctx,
9f2ebf
+                                          rctx,
9f2ebf
+                                          domain,
9f2ebf
+                                          SSS_DP_GROUP,
9f2ebf
+                                          data->id);
9f2ebf
+}
9f2ebf
+
9f2ebf
 const struct cache_req_plugin cache_req_group_by_id = {
9f2ebf
     .name = "Group by ID",
9f2ebf
     .attr_expiration = SYSDB_CACHE_EXPIRE,
9f2ebf
@@ -151,14 +205,14 @@ const struct cache_req_plugin cache_req_group_by_id = {
9f2ebf
     .create_debug_name_fn = cache_req_group_by_id_create_debug_name,
9f2ebf
     .global_ncache_add_fn = cache_req_group_by_id_global_ncache_add,
9f2ebf
     .ncache_check_fn = cache_req_group_by_id_ncache_check,
9f2ebf
-    .ncache_add_fn = NULL,
9f2ebf
+    .ncache_add_fn = cache_req_group_by_id_ncache_add,
9f2ebf
     .ncache_filter_fn = cache_req_group_by_id_ncache_filter,
9f2ebf
     .lookup_fn = cache_req_group_by_id_lookup,
9f2ebf
     .dp_send_fn = cache_req_group_by_id_dp_send,
9f2ebf
     .dp_recv_fn = cache_req_common_dp_recv,
9f2ebf
-    .dp_get_domain_check_fn = NULL,
9f2ebf
-    .dp_get_domain_send_fn = NULL,
9f2ebf
-    .dp_get_domain_recv_fn = NULL,
9f2ebf
+    .dp_get_domain_check_fn = cache_req_group_by_id_get_domain_check,
9f2ebf
+    .dp_get_domain_send_fn = cache_req_group_by_id_get_domain_send,
9f2ebf
+    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
9f2ebf
 };
9f2ebf
 
9f2ebf
 struct tevent_req *
9f2ebf
diff --git a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
9f2ebf
index 2af95313cb2df0f46a61519ac962074033f34a12..1327b480c1b1b68f9826fa229c9b001f2d92b79b 100644
9f2ebf
--- a/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
9f2ebf
+++ b/src/responder/common/cache_req/plugins/cache_req_object_by_id.c
9f2ebf
@@ -83,6 +83,26 @@ cache_req_object_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
     return EOK;
9f2ebf
 }
9f2ebf
 
9f2ebf
+static errno_t
9f2ebf
+cache_req_object_by_id_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
+                                  struct sss_domain_info *domain,
9f2ebf
+                                  struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+
9f2ebf
+    ret = sss_ncache_set_uid(ncache, false, domain, data->id);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    ret = sss_ncache_set_gid(ncache, false, domain, data->id);
9f2ebf
+    if (ret != EOK) {
9f2ebf
+        return ret;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return EOK;
9f2ebf
+}
9f2ebf
+
9f2ebf
 static errno_t
9f2ebf
 cache_req_object_by_id_lookup(TALLOC_CTX *mem_ctx,
9f2ebf
                               struct cache_req *cr,
9f2ebf
@@ -106,6 +126,55 @@ cache_req_object_by_id_dp_send(TALLOC_CTX *mem_ctx,
9f2ebf
                                    cr->data->id, NULL);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static bool
9f2ebf
+cache_req_object_by_id_get_domain_check(struct resp_ctx *rctx,
9f2ebf
+                                        struct sss_domain_info *domain,
9f2ebf
+                                        struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret == EEXIST) {
9f2ebf
+        nret = sss_ncache_check_locate_gid(rctx->ncache, domain, data->id);
9f2ebf
+        if (nret == EEXIST) {
9f2ebf
+            return false;
9f2ebf
+        }
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return true;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static struct tevent_req *
9f2ebf
+cache_req_object_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
9f2ebf
+                                       struct resp_ctx *rctx,
9f2ebf
+                                       struct sss_domain_info *domain,
9f2ebf
+                                       struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE,
9f2ebf
+              "Cannot set negative cache, this might result in "
9f2ebf
+              "performance degradation\n");
9f2ebf
+        /* Not fatal */
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    nret = sss_ncache_set_locate_gid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE,
9f2ebf
+              "Cannot set negative cache, this might result in "
9f2ebf
+              "performance degradation\n");
9f2ebf
+        /* Not fatal */
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return sss_dp_get_account_domain_send(mem_ctx,
9f2ebf
+                                          rctx,
9f2ebf
+                                          domain,
9f2ebf
+                                          SSS_DP_USER_AND_GROUP,
9f2ebf
+                                          data->id);
9f2ebf
+}
9f2ebf
+
9f2ebf
 const struct cache_req_plugin cache_req_object_by_id = {
9f2ebf
     .name = "Object by ID",
9f2ebf
     .attr_expiration = SYSDB_CACHE_EXPIRE,
9f2ebf
@@ -125,14 +194,14 @@ const struct cache_req_plugin cache_req_object_by_id = {
9f2ebf
     .create_debug_name_fn = cache_req_object_by_id_create_debug_name,
9f2ebf
     .global_ncache_add_fn = cache_req_object_by_id_global_ncache_add,
9f2ebf
     .ncache_check_fn = cache_req_object_by_id_ncache_check,
9f2ebf
-    .ncache_add_fn = NULL,
9f2ebf
+    .ncache_add_fn = cache_req_object_by_id_ncache_add,
9f2ebf
     .ncache_filter_fn = cache_req_object_by_id_ncache_filter,
9f2ebf
     .lookup_fn = cache_req_object_by_id_lookup,
9f2ebf
     .dp_send_fn = cache_req_object_by_id_dp_send,
9f2ebf
     .dp_recv_fn = cache_req_common_dp_recv,
9f2ebf
-    .dp_get_domain_check_fn = NULL,
9f2ebf
-    .dp_get_domain_send_fn = NULL,
9f2ebf
-    .dp_get_domain_recv_fn = NULL,
9f2ebf
+    .dp_get_domain_check_fn = cache_req_object_by_id_get_domain_check,
9f2ebf
+    .dp_get_domain_send_fn = cache_req_object_by_id_get_domain_send,
9f2ebf
+    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
9f2ebf
 };
9f2ebf
 
9f2ebf
 struct tevent_req *
9f2ebf
diff --git a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
9f2ebf
index 254330e92cc801b84bfb5e308d6d90ac54507d77..656fa41af5f39f68c64e241aa97c4eaf3ec57395 100644
9f2ebf
--- a/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
9f2ebf
+++ b/src/responder/common/cache_req/plugins/cache_req_user_by_id.c
9f2ebf
@@ -39,6 +39,15 @@ cache_req_user_by_id_ncache_check(struct sss_nc_ctx *ncache,
9f2ebf
                                   struct sss_domain_info *domain,
9f2ebf
                                   struct cache_req_data *data)
9f2ebf
 {
9f2ebf
+    errno_t ret;
9f2ebf
+
9f2ebf
+    if (domain != NULL) {
9f2ebf
+        ret = sss_ncache_check_uid(ncache, domain, data->id);
9f2ebf
+        if (ret == EEXIST) {
9f2ebf
+            return ret;
9f2ebf
+        }
9f2ebf
+    }
9f2ebf
+
9f2ebf
     return sss_ncache_check_uid(ncache, NULL, data->id);
9f2ebf
 }
9f2ebf
 
9f2ebf
@@ -57,6 +66,14 @@ cache_req_user_by_id_global_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
     return sss_ncache_set_uid(ncache, false, NULL, data->id);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static errno_t
9f2ebf
+cache_req_user_by_id_ncache_add(struct sss_nc_ctx *ncache,
9f2ebf
+                                struct sss_domain_info *domain,
9f2ebf
+                                struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    return sss_ncache_set_uid(ncache, false, domain, data->id);
9f2ebf
+}
9f2ebf
+
9f2ebf
 static errno_t
9f2ebf
 cache_req_user_by_id_lookup(TALLOC_CTX *mem_ctx,
9f2ebf
                             struct cache_req *cr,
9f2ebf
@@ -132,6 +149,44 @@ cache_req_user_by_id_dp_send(TALLOC_CTX *mem_ctx,
9f2ebf
                                    SSS_DP_USER, string, id, flag);
9f2ebf
 }
9f2ebf
 
9f2ebf
+static bool
9f2ebf
+cache_req_user_by_id_get_domain_check(struct resp_ctx *rctx,
9f2ebf
+                                      struct sss_domain_info *domain,
9f2ebf
+                                      struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_check_locate_uid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret == EEXIST) {
9f2ebf
+        return false;
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return true;
9f2ebf
+}
9f2ebf
+
9f2ebf
+static struct tevent_req *
9f2ebf
+cache_req_user_by_id_get_domain_send(TALLOC_CTX *mem_ctx,
9f2ebf
+                                     struct resp_ctx *rctx,
9f2ebf
+                                     struct sss_domain_info *domain,
9f2ebf
+                                     struct cache_req_data *data)
9f2ebf
+{
9f2ebf
+    int nret;
9f2ebf
+
9f2ebf
+    nret = sss_ncache_set_locate_uid(rctx->ncache, domain, data->id);
9f2ebf
+    if (nret != EOK) {
9f2ebf
+        DEBUG(SSSDBG_MINOR_FAILURE,
9f2ebf
+              "Cannot set negative cache, this might result in "
9f2ebf
+              "performance degradation\n");
9f2ebf
+        /* Not fatal */
9f2ebf
+    }
9f2ebf
+
9f2ebf
+    return sss_dp_get_account_domain_send(mem_ctx,
9f2ebf
+                                          rctx,
9f2ebf
+                                          domain,
9f2ebf
+                                          SSS_DP_USER,
9f2ebf
+                                          data->id);
9f2ebf
+}
9f2ebf
+
9f2ebf
 const struct cache_req_plugin cache_req_user_by_id = {
9f2ebf
     .name = "User by ID",
9f2ebf
     .attr_expiration = SYSDB_CACHE_EXPIRE,
9f2ebf
@@ -151,14 +206,14 @@ const struct cache_req_plugin cache_req_user_by_id = {
9f2ebf
     .create_debug_name_fn = cache_req_user_by_id_create_debug_name,
9f2ebf
     .global_ncache_add_fn = cache_req_user_by_id_global_ncache_add,
9f2ebf
     .ncache_check_fn = cache_req_user_by_id_ncache_check,
9f2ebf
-    .ncache_add_fn = NULL,
9f2ebf
+    .ncache_add_fn = cache_req_user_by_id_ncache_add,
9f2ebf
     .ncache_filter_fn = cache_req_user_by_id_ncache_filter,
9f2ebf
     .lookup_fn = cache_req_user_by_id_lookup,
9f2ebf
     .dp_send_fn = cache_req_user_by_id_dp_send,
9f2ebf
     .dp_recv_fn = cache_req_common_dp_recv,
9f2ebf
-    .dp_get_domain_check_fn = NULL,
9f2ebf
-    .dp_get_domain_send_fn = NULL,
9f2ebf
-    .dp_get_domain_recv_fn = NULL,
9f2ebf
+    .dp_get_domain_check_fn = cache_req_user_by_id_get_domain_check,
9f2ebf
+    .dp_get_domain_send_fn = cache_req_user_by_id_get_domain_send,
9f2ebf
+    .dp_get_domain_recv_fn = cache_reg_common_get_acct_domain_recv,
9f2ebf
 };
9f2ebf
 
9f2ebf
 struct tevent_req *
9f2ebf
diff --git a/src/tests/cmocka/common_mock_resp_dp.c b/src/tests/cmocka/common_mock_resp_dp.c
9f2ebf
index 4b38a38e6f53499132f9fe14a0ec0af157cf85ca..f21ca53ad0d6b7f4ed28d0c1d9e491af31355d43 100644
9f2ebf
--- a/src/tests/cmocka/common_mock_resp_dp.c
9f2ebf
+++ b/src/tests/cmocka/common_mock_resp_dp.c
9f2ebf
@@ -179,3 +179,26 @@ errno_t sss_dp_get_domains_recv(struct tevent_req *req)
9f2ebf
 {
9f2ebf
     return test_request_recv(req);
9f2ebf
 }
9f2ebf
+
9f2ebf
+struct tevent_req *
9f2ebf
+sss_dp_get_account_domain_send(TALLOC_CTX *mem_ctx,
9f2ebf
+                               struct resp_ctx *rctx,
9f2ebf
+                               struct sss_domain_info *domain,
9f2ebf
+                               enum sss_dp_acct_type type,
9f2ebf
+                               uint32_t opt_id)
9f2ebf
+{
9f2ebf
+    return test_req_succeed_send(mem_ctx, rctx->ev);
9f2ebf
+}
9f2ebf
+
9f2ebf
+errno_t sss_dp_get_account_domain_recv(TALLOC_CTX *mem_ctx,
9f2ebf
+                                       struct tevent_req *req,
9f2ebf
+                                       char **_domain)
9f2ebf
+{
9f2ebf
+    errno_t ret;
9f2ebf
+
9f2ebf
+    ret = sss_mock_type(errno_t);
9f2ebf
+    if (ret == EOK) {
9f2ebf
+        *_domain = sss_mock_ptr_type(char *);
9f2ebf
+    }
9f2ebf
+    return ret;
9f2ebf
+}
9f2ebf
-- 
9f2ebf
2.14.3
9f2ebf