summaryrefslogtreecommitdiffstats
path: root/lib/dbwrap/dbwrap_util.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /lib/dbwrap/dbwrap_util.c
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/dbwrap/dbwrap_util.c')
-rw-r--r--lib/dbwrap/dbwrap_util.c756
1 files changed, 756 insertions, 0 deletions
diff --git a/lib/dbwrap/dbwrap_util.c b/lib/dbwrap/dbwrap_util.c
new file mode 100644
index 0000000..312fb41
--- /dev/null
+++ b/lib/dbwrap/dbwrap_util.c
@@ -0,0 +1,756 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility functions for the dbwrap API
+ Copyright (C) Volker Lendecke 2007
+ Copyright (C) Michael Adam 2009
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2006
+
+ Major code contributions from Aleksey Fedoseev (fedoseev@ru.ibm.com)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "dbwrap.h"
+#include "lib/util/util_tdb.h"
+
+struct dbwrap_fetch_int32_state {
+ NTSTATUS status;
+ int32_t result;
+};
+
+static void dbwrap_fetch_int32_parser(TDB_DATA key, TDB_DATA data,
+ void *private_data)
+{
+ struct dbwrap_fetch_int32_state *state =
+ (struct dbwrap_fetch_int32_state *)private_data;
+
+ if (data.dsize != sizeof(state->result)) {
+ state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ return;
+ }
+ state->result = IVAL(data.dptr, 0);
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS dbwrap_fetch_int32(struct db_context *db, TDB_DATA key,
+ int32_t *result)
+{
+ struct dbwrap_fetch_int32_state state;
+ NTSTATUS status;
+
+ if (result == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ state.status = NT_STATUS_INTERNAL_ERROR;
+
+ status = dbwrap_parse_record(db, key, dbwrap_fetch_int32_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (NT_STATUS_IS_OK(state.status)) {
+ *result = state.result;
+ }
+ return state.status;
+}
+
+NTSTATUS dbwrap_fetch_int32_bystring(struct db_context *db, const char *keystr,
+ int32_t *result)
+{
+ return dbwrap_fetch_int32(db, string_term_tdb_data(keystr), result);
+}
+
+NTSTATUS dbwrap_store_int32_bystring(struct db_context *db, const char *keystr,
+ int32_t v)
+{
+ uint8_t v_store[sizeof(int32_t)];
+ TDB_DATA data = { .dptr = v_store, .dsize = sizeof(v_store) };
+ NTSTATUS status;
+
+ SIVAL(v_store, 0, v);
+
+ status = dbwrap_store(db, string_term_tdb_data(keystr), data,
+ TDB_REPLACE);
+ return status;
+}
+
+struct dbwrap_fetch_uint32_state {
+ NTSTATUS status;
+ uint32_t result;
+};
+
+static void dbwrap_fetch_uint32_parser(TDB_DATA key, TDB_DATA data,
+ void *private_data)
+{
+ struct dbwrap_fetch_uint32_state *state =
+ (struct dbwrap_fetch_uint32_state *)private_data;
+
+ if (data.dsize != sizeof(state->result)) {
+ state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ return;
+ }
+ state->result = IVAL(data.dptr, 0);
+ state->status = NT_STATUS_OK;
+}
+
+NTSTATUS dbwrap_fetch_uint32_bystring(struct db_context *db,
+ const char *keystr, uint32_t *val)
+{
+ struct dbwrap_fetch_uint32_state state;
+ NTSTATUS status;
+
+ if (val == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ state.status = NT_STATUS_INTERNAL_ERROR;
+
+ status = dbwrap_parse_record(db, string_term_tdb_data(keystr),
+ dbwrap_fetch_uint32_parser, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (NT_STATUS_IS_OK(state.status)) {
+ *val = state.result;
+ }
+ return state.status;
+}
+
+NTSTATUS dbwrap_store_uint32_bystring(struct db_context *db,
+ const char *keystr, uint32_t v)
+{
+ uint8_t v_store[sizeof(uint32_t)];
+ TDB_DATA data = { .dptr = v_store, .dsize = sizeof(v_store) };
+ NTSTATUS status;
+
+ SIVAL(v_store, 0, v);
+
+ status = dbwrap_store(db, string_term_tdb_data(keystr), data,
+ TDB_REPLACE);
+ return status;
+}
+
+/**
+ * Atomic unsigned integer change (addition):
+ *
+ * if value does not exist yet in the db, use *oldval as initial old value.
+ * return old value in *oldval.
+ * store *oldval + change_val to db.
+ */
+
+struct dbwrap_change_uint32_atomic_context {
+ const char *keystr;
+ uint32_t *oldval;
+ uint32_t change_val;
+ NTSTATUS status;
+};
+
+static void dbwrap_change_uint32_atomic_action_fn(struct db_record *rec,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct dbwrap_change_uint32_atomic_context *state = private_data;
+ uint8_t v_store[4];
+ TDB_DATA data = {
+ .dptr = v_store,
+ .dsize = sizeof(v_store),
+ };
+ uint32_t val = UINT32_MAX;
+
+ if (value.dptr == NULL) {
+ val = *(state->oldval);
+ } else if (value.dsize == sizeof(val)) {
+ val = PULL_LE_U32(value.dptr, 0);
+ *(state->oldval) = val;
+ } else {
+ state->status = NT_STATUS_UNSUCCESSFUL;
+ return;
+ }
+
+ val += state->change_val;
+ PUSH_LE_U32(v_store, 0, val);
+
+ state->status = dbwrap_record_store(rec, data, TDB_REPLACE);
+}
+
+static NTSTATUS dbwrap_change_uint32_atomic_action(struct db_context *db,
+ void *private_data)
+{
+ struct dbwrap_change_uint32_atomic_context *state = private_data;
+ NTSTATUS status;
+
+ status = dbwrap_do_locked(db,
+ string_term_tdb_data(state->keystr),
+ dbwrap_change_uint32_atomic_action_fn,
+ state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dbwrap_do_locked() failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(state->status)) {
+ DBG_DEBUG("dbwrap_change_uint32_atomic_action_fn() "
+ "failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dbwrap_change_uint32_atomic_bystring(struct db_context *db,
+ const char *keystr,
+ uint32_t *oldval,
+ uint32_t change_val)
+{
+ NTSTATUS ret;
+ struct dbwrap_change_uint32_atomic_context state;
+
+ state.keystr = keystr;
+ state.oldval = oldval;
+ state.change_val = change_val;
+
+ ret = dbwrap_change_uint32_atomic_action(db, &state);
+
+ return ret;
+}
+
+NTSTATUS dbwrap_trans_change_uint32_atomic_bystring(struct db_context *db,
+ const char *keystr,
+ uint32_t *oldval,
+ uint32_t change_val)
+{
+ NTSTATUS ret;
+ struct dbwrap_change_uint32_atomic_context state;
+
+ state.keystr = keystr;
+ state.oldval = oldval;
+ state.change_val = change_val;
+
+ ret = dbwrap_trans_do(db, dbwrap_change_uint32_atomic_action, &state);
+
+ return ret;
+}
+
+/**
+ * Atomic integer change (addition):
+ *
+ * if value does not exist yet in the db, use *oldval as initial old value.
+ * return old value in *oldval.
+ * store *oldval + change_val to db.
+ */
+
+struct dbwrap_change_int32_atomic_context {
+ TDB_DATA key;
+ int32_t *oldval;
+ int32_t change_val;
+ NTSTATUS status;
+};
+
+static void dbwrap_change_int32_atomic_action_fn(struct db_record *rec,
+ TDB_DATA value,
+ void *private_data)
+{
+ struct dbwrap_change_int32_atomic_context *state = private_data;
+ uint8_t v_store[4];
+ TDB_DATA data = {
+ .dptr = v_store,
+ .dsize = sizeof(v_store),
+ };
+ int32_t val = -1;
+
+ if (value.dptr == NULL) {
+ val = *(state->oldval);
+ } else if (value.dsize == sizeof(val)) {
+ val = PULL_LE_U32(value.dptr, 0);
+ *(state->oldval) = val;
+ } else {
+ state->status = NT_STATUS_UNSUCCESSFUL;
+ return;
+ }
+
+ val += state->change_val;
+ PUSH_LE_U32(v_store, 0, val);
+
+ state->status = dbwrap_record_store(rec, data, TDB_REPLACE);
+}
+
+static NTSTATUS dbwrap_change_int32_atomic_action(struct db_context *db,
+ void *private_data)
+{
+ struct dbwrap_change_int32_atomic_context *state = private_data;
+ NTSTATUS status;
+
+ status = dbwrap_do_locked(db,
+ state->key,
+ dbwrap_change_int32_atomic_action_fn,
+ state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dbwrap_do_locked() failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(state->status)) {
+ DBG_DEBUG("dbwrap_change_int32_atomic_action_fn() "
+ "failed: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+ return NT_STATUS_OK;
+}
+
+NTSTATUS dbwrap_change_int32_atomic(struct db_context *db,
+ TDB_DATA key,
+ int32_t *oldval,
+ int32_t change_val)
+{
+ NTSTATUS ret;
+ struct dbwrap_change_int32_atomic_context state;
+
+ state.key = key;
+ state.oldval = oldval;
+ state.change_val = change_val;
+
+ ret = dbwrap_change_int32_atomic_action(db, &state);
+
+ return ret;
+}
+
+NTSTATUS dbwrap_change_int32_atomic_bystring(struct db_context *db,
+ const char *keystr,
+ int32_t *oldval,
+ int32_t change_val)
+{
+ return dbwrap_change_int32_atomic(db, string_term_tdb_data(keystr),
+ oldval, change_val);
+}
+
+NTSTATUS dbwrap_trans_change_int32_atomic_bystring(struct db_context *db,
+ const char *keystr,
+ int32_t *oldval,
+ int32_t change_val)
+{
+ NTSTATUS ret;
+ struct dbwrap_change_int32_atomic_context state;
+
+ state.key = string_term_tdb_data(keystr);
+ state.oldval = oldval;
+ state.change_val = change_val;
+
+ ret = dbwrap_trans_do(db, dbwrap_change_int32_atomic_action, &state);
+
+ return ret;
+}
+
+struct dbwrap_store_context {
+ TDB_DATA *key;
+ TDB_DATA *dbuf;
+ int flag;
+};
+
+static NTSTATUS dbwrap_store_action(struct db_context *db, void *private_data)
+{
+ NTSTATUS status;
+ struct dbwrap_store_context *store_ctx;
+
+ store_ctx = (struct dbwrap_store_context *)private_data;
+
+ status = dbwrap_store(db, *(store_ctx->key), *(store_ctx->dbuf),
+ store_ctx->flag);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(5, ("store returned %s\n", nt_errstr(status)));
+ }
+
+ return status;
+}
+
+NTSTATUS dbwrap_trans_store(struct db_context *db, TDB_DATA key, TDB_DATA dbuf,
+ int flag)
+{
+ NTSTATUS status;
+ struct dbwrap_store_context store_ctx;
+
+ store_ctx.key = &key;
+ store_ctx.dbuf = &dbuf;
+ store_ctx.flag = flag;
+
+ status = dbwrap_trans_do(db, dbwrap_store_action, &store_ctx);
+
+ return status;
+}
+
+static NTSTATUS dbwrap_delete_action(struct db_context * db, void *private_data)
+{
+ NTSTATUS status;
+ TDB_DATA *key = (TDB_DATA *)private_data;
+
+ status = dbwrap_delete(db, *key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_INFO("dbwrap_record_delete returned %s\n",
+ nt_errstr(status));
+ }
+ return status;
+}
+
+NTSTATUS dbwrap_trans_delete(struct db_context *db, TDB_DATA key)
+{
+ NTSTATUS status;
+
+ status = dbwrap_trans_do(db, dbwrap_delete_action, &key);
+
+ return status;
+}
+
+NTSTATUS dbwrap_trans_store_int32_bystring(struct db_context *db,
+ const char *keystr,
+ int32_t v)
+{
+ int32_t v_store;
+
+ SIVAL(&v_store, 0, v);
+
+ return dbwrap_trans_store(db, string_term_tdb_data(keystr),
+ make_tdb_data((const uint8_t *)&v_store,
+ sizeof(v_store)),
+ TDB_REPLACE);
+}
+
+NTSTATUS dbwrap_trans_store_uint32_bystring(struct db_context *db,
+ const char *keystr,
+ uint32_t v)
+{
+ uint32_t v_store;
+
+ SIVAL(&v_store, 0, v);
+
+ return dbwrap_trans_store(db, string_term_tdb_data(keystr),
+ make_tdb_data((const uint8_t *)&v_store,
+ sizeof(v_store)),
+ TDB_REPLACE);
+}
+
+NTSTATUS dbwrap_trans_store_bystring(struct db_context *db, const char *key,
+ TDB_DATA data, int flags)
+{
+ return dbwrap_trans_store(db, string_term_tdb_data(key), data, flags);
+}
+
+NTSTATUS dbwrap_trans_delete_bystring(struct db_context *db, const char *key)
+{
+ return dbwrap_trans_delete(db, string_term_tdb_data(key));
+}
+
+/**
+ * Wrap db action(s) into a transaction.
+ */
+NTSTATUS dbwrap_trans_do(struct db_context *db,
+ NTSTATUS (*action)(struct db_context *, void *),
+ void *private_data)
+{
+ int res;
+ NTSTATUS status;
+
+ res = dbwrap_transaction_start(db);
+ if (res != 0) {
+ DEBUG(5, ("transaction_start failed\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = action(db, private_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (dbwrap_transaction_cancel(db) != 0) {
+ smb_panic("Cancelling transaction failed");
+ }
+ return status;
+ }
+
+ res = dbwrap_transaction_commit(db);
+ if (res == 0) {
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(2, ("transaction_commit failed\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+}
+
+struct dbwrap_trans_traverse_action_ctx {
+ int (*f)(struct db_record* rec, void* private_data);
+ void* private_data;
+};
+
+
+static NTSTATUS dbwrap_trans_traverse_action(struct db_context* db, void* private_data)
+{
+ struct dbwrap_trans_traverse_action_ctx* ctx =
+ (struct dbwrap_trans_traverse_action_ctx*)private_data;
+
+ NTSTATUS status = dbwrap_traverse(db, ctx->f, ctx->private_data, NULL);
+
+ return status;
+}
+
+NTSTATUS dbwrap_trans_traverse(struct db_context *db,
+ int (*f)(struct db_record*, void*),
+ void *private_data)
+{
+ struct dbwrap_trans_traverse_action_ctx ctx = {
+ .f = f,
+ .private_data = private_data,
+ };
+ return dbwrap_trans_do(db, dbwrap_trans_traverse_action, &ctx);
+}
+
+NTSTATUS dbwrap_purge(struct db_context *db, TDB_DATA key)
+{
+ NTSTATUS status;
+
+ status = dbwrap_delete(db, key);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+NTSTATUS dbwrap_purge_bystring(struct db_context *db, const char *key)
+{
+ return dbwrap_purge(db, string_term_tdb_data(key));
+}
+
+NTSTATUS dbwrap_delete_bystring(struct db_context *db, const char *key)
+{
+ return dbwrap_delete(db, string_term_tdb_data(key));
+}
+
+NTSTATUS dbwrap_store_bystring(struct db_context *db, const char *key,
+ TDB_DATA data, int flags)
+{
+ return dbwrap_store(db, string_term_tdb_data(key), data, flags);
+}
+
+NTSTATUS dbwrap_fetch_bystring(struct db_context *db, TALLOC_CTX *mem_ctx,
+ const char *key, TDB_DATA *value)
+{
+ return dbwrap_fetch(db, mem_ctx, string_term_tdb_data(key), value);
+}
+
+
+
+NTSTATUS dbwrap_delete_bystring_upper(struct db_context *db, const char *key)
+{
+ char *key_upper;
+ NTSTATUS status;
+
+ key_upper = talloc_strdup_upper(talloc_tos(), key);
+ if (key_upper == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_delete_bystring(db, key_upper);
+
+ talloc_free(key_upper);
+ return status;
+}
+
+NTSTATUS dbwrap_store_bystring_upper(struct db_context *db, const char *key,
+ TDB_DATA data, int flags)
+{
+ char *key_upper;
+ NTSTATUS status;
+
+ key_upper = talloc_strdup_upper(talloc_tos(), key);
+ if (key_upper == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_store_bystring(db, key_upper, data, flags);
+
+ talloc_free(key_upper);
+ return status;
+}
+
+NTSTATUS dbwrap_fetch_bystring_upper(struct db_context *db, TALLOC_CTX *mem_ctx,
+ const char *key, TDB_DATA *value)
+{
+ char *key_upper;
+ NTSTATUS status;
+
+ key_upper = talloc_strdup_upper(talloc_tos(), key);
+ if (key_upper == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dbwrap_fetch_bystring(db, mem_ctx, key_upper, value);
+
+ talloc_free(key_upper);
+ return status;
+}
+
+struct dbwrap_marshall_state {
+ uint8_t *buf;
+ size_t bufsize;
+ size_t dbsize;
+};
+
+static int dbwrap_marshall_fn(struct db_record *rec, void *private_data)
+{
+ struct dbwrap_marshall_state *state = private_data;
+ TDB_DATA key, value;
+ size_t new_dbsize;
+
+ key = dbwrap_record_get_key(rec);
+ value = dbwrap_record_get_value(rec);
+
+ new_dbsize = state->dbsize;
+ new_dbsize += 8 + key.dsize;
+ new_dbsize += 8 + value.dsize;
+
+ if (new_dbsize <= state->bufsize) {
+ uint8_t *p = state->buf + state->dbsize;
+
+ SBVAL(p, 0, key.dsize);
+ p += 8;
+ memcpy(p, key.dptr, key.dsize);
+ p += key.dsize;
+
+ SBVAL(p, 0, value.dsize);
+ p += 8;
+ memcpy(p, value.dptr, value.dsize);
+ }
+ state->dbsize = new_dbsize;
+ return 0;
+}
+
+size_t dbwrap_marshall(struct db_context *db, uint8_t *buf, size_t bufsize)
+{
+ struct dbwrap_marshall_state state;
+
+ state.bufsize = bufsize;
+ state.buf = buf;
+ state.dbsize = 0;
+
+ dbwrap_traverse_read(db, dbwrap_marshall_fn, &state, NULL);
+
+ return state.dbsize;
+}
+
+static ssize_t dbwrap_unmarshall_get_data(const uint8_t *buf, size_t buflen,
+ size_t ofs, TDB_DATA *pdata)
+{
+ uint64_t space, len;
+ const uint8_t *p;
+
+ if (ofs == buflen) {
+ return 0;
+ }
+ if (ofs > buflen) {
+ return -1;
+ }
+
+ space = buflen - ofs;
+ if (space < 8) {
+ return -1;
+ }
+
+ p = buf + ofs;
+ len = BVAL(p, 0);
+
+ p += 8;
+ space -= 8;
+
+ if (len > space) {
+ return -1;
+ }
+
+ *pdata = (TDB_DATA) { .dptr = discard_const_p(uint8_t, p),
+ .dsize = len };
+ return len + 8;
+}
+
+NTSTATUS dbwrap_parse_marshall_buf(const uint8_t *buf, size_t buflen,
+ bool (*fn)(TDB_DATA key, TDB_DATA value,
+ void *private_data),
+ void *private_data)
+{
+ size_t ofs = 0;
+
+ while (true) {
+ ssize_t len;
+ TDB_DATA key, value;
+ bool ok;
+
+ len = dbwrap_unmarshall_get_data(buf, buflen, ofs, &key);
+ if (len == 0) {
+ break;
+ }
+ if (len == -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs += len;
+
+ len = dbwrap_unmarshall_get_data(buf, buflen, ofs, &value);
+ if (len == 0) {
+ break;
+ }
+ if (len == -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ofs += len;
+
+ ok = fn(key, value, private_data);
+ if (!ok) {
+ break;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct dbwrap_unmarshall_state {
+ struct db_context *db;
+ NTSTATUS ret;
+};
+
+static bool dbwrap_unmarshall_fn(TDB_DATA key, TDB_DATA value,
+ void *private_data)
+{
+ struct dbwrap_unmarshall_state *state = private_data;
+ NTSTATUS status;
+
+ status = dbwrap_store(state->db, key, value, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dbwrap_record_store failed: %s\n",
+ nt_errstr(status));
+ state->ret = status;
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS dbwrap_unmarshall(struct db_context *db, const uint8_t *buf,
+ size_t buflen)
+{
+ struct dbwrap_unmarshall_state state = { .db = db };
+ NTSTATUS status;
+
+ status = dbwrap_parse_marshall_buf(buf, buflen,
+ dbwrap_unmarshall_fn, &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return state.ret;
+}